Skip to content

fix: persona-diagnostic batch 2 (cross-language slips)#300

Merged
danieljohnmorris merged 5 commits into
mainfrom
fix/persona-diagnostic-batch-2
May 16, 2026
Merged

fix: persona-diagnostic batch 2 (cross-language slips)#300
danieljohnmorris merged 5 commits into
mainfrom
fix/persona-diagnostic-batch-2

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

Six diagnostic-only improvements addressing persona-friction patterns from rerun3 logs. Each is a cross-language slip that previously surfaced as a bare or misleading parse error; each now emits a friendly hint pointing at the canonical ilo form, so the agent's first retry should be the right one.

Manifesto framing: every friendly hint shaves the cost of one wrong follow-up message. The cumulative savings across these six shapes is meaningful because they're concentrated in early-program-writing turns where retries are expensive.

What's in the diff

Five commits, one logical change each:

  • lexer: friendly hint for AND/OR/NOT from other languages — forward-scan of consecutive uppercase letters at fresh-expression position; emits ILO-L001 with a suggestion pointing at & / | / !. Type position (f x:OR n n>nO R n n) is left untouched via a prev-token guard.
  • parser: friendly hints for cross-language syntax slips — four hints bundled because they live across the same file and helper site:
    • =<a b / =>a b compound-prefix → suggest <= / >= single token (ILO-P003).
    • fn p:t>r;body at expression position → suggest parenthesised inline lambda (p:t>r;body) (ILO-P009).
    • main:>n;body no-param signature → suggest main>n;body (no : before >) (ILO-P003).
    • expected LBrace, got Semi multi-line-body confusion → hint about ilo's single-line ;-separated body rule.
  • main: AST-aware hint for discarded-result braced conditionalcond{^\"err\"} runs and discards the body value silently. Added a post-parse AST walker to collect_hints_with_program that detects guard bodies whose tail expression is Expr::Err(_) / Expr::Ok(_), recursing through nested ForEach / Match-arm / While bodies. Hint points at the braceless cond ^\"err\" early-return form. Also fixes a pre-existing false positive in warn_cross_language_syntax: comments mentioning -> / && / || / // illustratively no longer trigger the warning.
  • example: persona-diagnostic-batch-2 covers canonical forms — five run/out pairs pinning the corrected forms across tree, VM, and Cranelift via the examples_engines harness.
  • docs: cross-language gotchas section + SKILL common-mistakes — new SPEC.md subsection with a six-row table mapping each slip to canonical form and error code. Regenerated into ai.txt via build.rs. Six new entries in skills/ilo/SKILL.md Common Mistakes. Site docs deliberately untouched to avoid collision with the in-flight doc-audit PR docs: SPEC drift fix for v0.11.2 + load-bearing SKILL.md #290.

Items dropped and deferred

  • Item 1 (sin=42 builtin shadow) dropped: already covered by the in-flight fix/builtin-shadow-flat-freq worktree (extends Builtin::is_builtin gating to parse_let and parse_decl). The fix there is broader and more correct (gates against ALL builtin names, not just trig/log/exp).
  • Item 8 (prefix - parse-error column off-by-one) parked: no clean repro from the original persona log entry. Filed in ilo_assessment_feedback.md Parked section for revisit when a concrete failing snippet shows up.

Repro before/after

Item 2 — AND a b:

before:  ILO-L001 unexpected token 'A'  Unexpected character(s): 'A'
after:   ILO-L001 unexpected token 'AND'  `AND` is not an ilo keyword. ilo uses `&` (single `&` for logical and)

Item 5 — =<d 0{...}:

before:  ILO-P009 expected expression, got LBrace
after:   ILO-P003 `=<` is not an ilo operator; compound prefix forms don't compose
         hint: use `<=` (single token) instead, e.g. `<=a b` for the comparison

Item 6 — f=fn x:n>n;+x 1:

before:  ILO-P009 expected expression, got KwFn
after:   ILO-P009 `fn` is a reserved word and cannot start an expression
         hint: ilo's inline lambda syntax is `(p:t>r;body)`, e.g. `map (x:n>n;+x 1) xs`. For a named function use `name params>return;body` at the top level.

Item 7 — main:>n;42:

before:  ILO-P003 expected Greater, got Colon
after:   ILO-P003 unexpected `:>` after `main` — ilo signatures don't put a colon before the return-type separator
         hint: write `main>return;body` for a no-param function, or `main p:t>return;body` if params were intended

Item 9 — @k xs;+k 1:

before:  ILO-P003 expected LBrace, got Semi  (no hint)
after:   ILO-P003 expected LBrace, got Semi
         hint: ilo bodies are single-line, `;`-separated. If you broke the body across lines, collapse it onto one line or wrap with `{ ... }`. For example `@k kws{body};...` not `@k kws;body`.

Item 4 — <x 0{^\"err\"};~x (runs cleanly, silently discards ^\"err\"):

before:  (no diagnostic; agent thinks early-return happened, downstream logic breaks)
after:   hint: `cond{^\"err\"}` and `cond{~v}` discard the body expression. For early-return use the braceless form `cond ^\"err\"` or wrap the body in `{ret ^\"err\"}`.

Test plan

  • 7 new lexer tests (AND/OR/NOT positive, post-dot OR, mid-ident OR, OR-as-type position negative cases)
  • 7 new parser tests (compound prefix Eq+Less, Eq+Greater, Bang+Less is-negated-guard, fn/def lambda hint, fn-decl colon-before-greater, params-present no-hint, LBrace-got-Semi, LBrace-got-other-token)
  • 6 new collect_hints tests (discarded Err/Ok, braceless-guard no-hint, explicit-ret no-hint, foreach-body, match-arm-body)
  • 5 example run/out pairs exercising the canonical forms end-to-end across tree/VM/Cranelift via examples_engines harness
  • cargo test --release --features cranelift — all 3013 lib + 199 example + integration tests pass, 0 failures
  • cargo fmt --check clean
  • cargo clippy --release --features cranelift --all-targets clean

Follow-ups

@codecov
Copy link
Copy Markdown

codecov Bot commented May 16, 2026

Codecov Report

❌ Patch coverage is 99.19786% with 3 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/main.rs 98.29% 2 Missing ⚠️
src/lexer/mod.rs 99.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@danieljohnmorris danieljohnmorris force-pushed the fix/persona-diagnostic-batch-2 branch 2 times, most recently from 039d46f to 5c8f599 Compare May 16, 2026 10:17
`AND a b` lex-fails at the leading capital with a bare `unexpected
token 'A'` and `OR a b` cascades into a downstream parse error
because `O` lexes as the OptType sigil. Personas (qa-tester,
scientific-researcher rerun3) hit this when reaching for boolean
operators from Python/JavaScript/Pascal.

Add a forward-scan of consecutive uppercase letters at fresh-expression
position; when the resulting word matches a known logical keyword
(`AND`, `OR`, `NOT`), emit ILO-L001 with a suggestion pointing at
the canonical `&` / `|` / `!` prefix forms.

Type position is left alone: `f x:OR n n>n` is the compact form of
`O R n n` (Optional of Result), which is valid ilo. The guard checks
the previous token for `:`, `>`, or a sigil before firing, so
intentional compact-type composition keeps working.

The new path is also gated on fresh-position (no preceding flush ident
and no post-dot context) so `fooAND` stays an L003 mid-ident camel
error and `r.OR` stays a JSON post-dot field access.
Four diagnostic improvements at the parser level, each addressing
persona-friction patterns from rerun3 logs.

`=<a b` / `=>a b` compound-prefix (qa-tester): the parser used to
consume `=` as its own prefix-eq then fail at the second operand with
a misleading "expected expression, got LBrace" deep in the
expression. Detect `Eq` followed immediately by `Less`/`Greater`
in parse_prefix_binop and surface ILO-P003 with a suggestion pointing
at the single-token `<=` / `>=` form. `!<` / `!>` are
deliberately excluded; `!cond{body}` is a valid negated guard.

`fn p:t>r;body` lambda at expression position (qa-tester,
scientific-researcher): KwFn used to fall through to a bare
"expected expression, got KwFn". Add a lambda_keyword_message
helper covering `fn` and `def`, called from parse_atom's fallback
arm. Hint points at ilo's parenthesised inline-lambda form
`(x:n>n;+x 1)`.

`main:>n;body` no-param signature shape (qa-tester): the colon-then-
greater pattern after the fn name used to surface as
"expected Greater, got Colon". Add an explicit check in parse_fn_decl
after register_user_fn when params is empty, with a hint pointing at
`name>return;body`.

Multi-line function/loop/guard body (qa-tester, devops-sre, pdf-analyst,
html-scraper, all rerun3): newlines collapse to `;` at the lexer, so
`@k kws\n  body` becomes `@k kws;body` and the foreach's required
`{` lands on a `;`. Extend the generic expect() helper so that
`expected LBrace, got Semi` carries a hint about single-line bodies
and braced blocks. Doesn't change any existing error code or message
text, only adds the hint.
Personas (qa-tester, scientific-researcher rerun3) write
`cond{^"err"}` expecting early-return semantics, but a braced
conditional in ilo runs the body and discards its value. The shape
parses cleanly and even runs successfully, so the failure mode is
silent: the program proceeds with whatever follows, never returning the
intended error.

Extend collect_hints to accept the parsed Program and walk it
recursively, looking for a `Stmt::Guard { braceless: false, .. }`
whose body's tail statement is a `Stmt::Expr(Expr::Err(_) |
Expr::Ok(_))`. Recurses through Guard else_body, ForEach/ForRange/
While bodies, and Match arm bodies. Emits a single hint per run pointing
at the canonical braceless guard form `cond ^"err"` or the explicit
`{ret ^"err"}` wrapper.

Test coverage: positive cases for Err and Ok in guard tails, negative
cases for braceless guards (which already return) and explicit `ret`
forms (which already propagate), plus nested-walker coverage for the
ForEach and Match-arm body shapes.

Also strip `--` line-comment contents in warn_cross_language_syntax,
not just string literals. Without this, comments mentioning `->` /
`&&` / `||` / `//` illustratively (e.g. examples that explain
ilo's non-use of these tokens) triggered a false-positive
cross-language warning. Reuses the existing
strip_string_and_comment_contents helper.
Pins the corrected forms our new parser hints suggest, so the
examples_engines harness exercises them across tree, VM, and Cranelift
as cross-engine regression coverage.

Five end-to-end run/out pairs:
- main: combines sum-plus-one (foreach with braced body), forty-two
  (no-param signature), plus-one (inline lambda)
- abs-check 7: braceless guard returns ok value
- abs-check -3: braceless guard returns err and exits 1
- lte 3 5 / gte 3 5: single-token comparisons

Header comment documents each cross-language slip alongside the
canonical ilo form, so an agent dumping the file gets the friction
map for free.
SPEC.md gets a new "Cross-language gotchas" subsection under
Identifier syntax, with a six-row table mapping each known slip
(AND/OR/NOT, compound prefix, fn lambda, main:> sig, multi-line body,
discarded braced-cond) to its canonical ilo form and the error code
that surfaces. build.rs regenerates ai.txt from SPEC.md, so the same
table lands in the compact spec.

skills/ilo/SKILL.md gets six new entries appended to Common Mistakes,
mirroring the SPEC table at the load-bearing summary level so an agent
dumping the skill prompt gets the friction map before writing code.

Site docs deliberately untouched: the in-flight doc-audit PR #290
already covers reference/error-codes.md and friends, and a parallel
edit here would create avoidable merge conflict.
@danieljohnmorris danieljohnmorris force-pushed the fix/persona-diagnostic-batch-2 branch from 5c8f599 to 2902281 Compare May 16, 2026 11:43
@danieljohnmorris danieljohnmorris merged commit 398395a into main May 16, 2026
4 checks passed
@danieljohnmorris danieljohnmorris deleted the fix/persona-diagnostic-batch-2 branch May 16, 2026 11:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant