cli: error on unknown subcommand in multi-fn files#320
Merged
Conversation
Running `ilo file.ilo wibble x` on a multi-fn file used to silently route to the first declared function with `["wibble", "x"]` as args, producing a misleading arity error (`helper: expected 1 args, got 2`) far from the cause. Default-engine dispatch now checks the leading positional against the known function names and, if it doesn't match AND looks like an identifier (ASCII alphabetic / underscore start, ident-shaped tail), emits a friendly "no such function 'wibble' in <file>" error with the available-function listing instead. The ident-shape guard preserves the existing pass-through for numeric, quoted-string, and bracketed-list leading args (they're clearly data, not a typoed subcommand). Reserved literal words (`true`, `false`, `nil`) are also exempt since `parse_cli_arg` maps them to Bool/Nil values and passing them through to the entry function is the long-standing intent. Synthetic `__lit_*` lambda-lifted decls remain filtered out of the listing (already covered by #307). Single-function files and inline programs are unchanged: unknown leading tokens still pass through to the sole/entry function per the SKILL.md auto-run rule.
Five new CLI integration tests pin the new shape-aware unknown-subcommand behaviour: - unknown ident-shaped subcommand on a multi-fn file errors with "no such function" + available-functions listing, does not leak the pre-fix first-fn arity error - known subcommand still routes correctly on the same multi-fn file - synthetic __lit_N decls are filtered out of the listing - single-fn file treats an unknown leading token as a literal arg (SKILL.md auto-run rule) - inline program treats an unknown leading token as a literal arg - multi-fn file with a numeric leading arg still passes through to the entry function (pins the shape guard that prevents the unwrap_*_inline tests in tests/eval_inline.rs from regressing) - multi-fn file with a quoted-string leading arg passes through - multi-fn file with a bracketed-list leading arg passes through
Multi-fn file with `main` plus two helpers, pinning the happy-path contracts the unknown-subcommand guard sits next to: - `ilo file.ilo` auto-runs `main` - `ilo file.ilo dbl 21` dispatches to the named function - `ilo file.ilo trp 4` dispatches to the named function The CLI-level "no such function" error path is covered by tests/regression_cli_default.rs; the examples harness only exercises happy-path dispatch across every engine.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
danieljohnmorris
added a commit
that referenced
this pull request
May 16, 2026
eight fixes since 0.11.4, all from rerun5 personas: bare-bang silent-nil regression (#324), Cranelift AArch64 panic catch_unwind fallback (#319), multi-line body span drift (#318), HOF tree-bridge error parity on Cranelift (#321), bool-ternary brace sugar (#323), single-line body diagnostic with brace-block bodies (#322), unknown-subcommand error in multi-fn files (#320), window perf cliff fused flt/map (#325).
6 tasks
danieljohnmorris
added a commit
that referenced
this pull request
May 17, 2026
PR #320's looks_like_subcommand_name rejected anything containing -, so hyphenated idents like list-orders / wibble-x fell through to the old greedy first-fn dispatch and produced misleading arity errors. Per SPEC the canonical ilo ident shape is [a-z][a-z0-9]*(-[a-z0-9]+)*, so - is legal mid-ident; the helper now matches that. Trailing dash, doubled dash, and leading dash remain rejected as data so --flag, -1, foo-, foo--bar still pass through unchanged. File-shaped args (data.csv, path/to/file) are still rejected because a dot or slash is not a legal ident char. Interactive-cli rerun6 surfaced this on a multi-fn file with a zero-arg load fn; ilo file.ilo list-orders used to produce 'load: expected 0 args' rather than the friendly no-such-function listing #320 was meant to deliver.
danieljohnmorris
added a commit
that referenced
this pull request
May 17, 2026
Eight new integration tests covering the two PR #320 follow-up bugs: Hyphenated unknown subcommand (interactive-cli rerun6): - list-orders typo → no-such-function listing, not first-fn dispatch - wibble-x tail-hyphenated form → same listing shape - foo- trailing-dash → falls through as data (not an ident shape) Non-ident positional with main (gis-analyst rerun6, devops-sre rerun6): - top200.csv → routes to main, not to first-declared hav - data.csv out.txt → both positionals flow to main in order - numeric 21 with main defined → routes to main - /tmp/inc.json with named-helper i:_ first → routes to main - 41 on a no-main file → legacy passthrough preserved - explicit fn name still overrides main routing Each test pins the diagnostic absence too — pre-fix symptoms (load / hav / gs arity or type errors) must not appear.
danieljohnmorris
added a commit
that referenced
this pull request
May 17, 2026
PR #320's looks_like_subcommand_name rejected anything containing -, so hyphenated idents like list-orders / wibble-x fell through to the old greedy first-fn dispatch and produced misleading arity errors. Per SPEC the canonical ilo ident shape is [a-z][a-z0-9]*(-[a-z0-9]+)*, so - is legal mid-ident; the helper now matches that. Trailing dash, doubled dash, and leading dash remain rejected as data so --flag, -1, foo-, foo--bar still pass through unchanged. File-shaped args (data.csv, path/to/file) are still rejected because a dot or slash is not a legal ident char. Interactive-cli rerun6 surfaced this on a multi-fn file with a zero-arg load fn; ilo file.ilo list-orders used to produce 'load: expected 0 args' rather than the friendly no-such-function listing #320 was meant to deliver.
danieljohnmorris
added a commit
that referenced
this pull request
May 17, 2026
Eight new integration tests covering the two PR #320 follow-up bugs: Hyphenated unknown subcommand (interactive-cli rerun6): - list-orders typo → no-such-function listing, not first-fn dispatch - wibble-x tail-hyphenated form → same listing shape - foo- trailing-dash → falls through as data (not an ident shape) Non-ident positional with main (gis-analyst rerun6, devops-sre rerun6): - top200.csv → routes to main, not to first-declared hav - data.csv out.txt → both positionals flow to main in order - numeric 21 with main defined → routes to main - /tmp/inc.json with named-helper i:_ first → routes to main - 41 on a no-main file → legacy passthrough preserved - explicit fn name still overrides main routing Each test pins the diagnostic absence too — pre-fix symptoms (load / hav / gs arity or type errors) must not appear.
danieljohnmorris
added a commit
that referenced
this pull request
May 17, 2026
…onident cli: hyphenated subcommands + non-ident positionals route to main (#320 follow-up)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ilo file.ilo wibble xon a multi-function file used to silently route to the FIRST declared function with["wibble", "x"]as positional args, producing a misleading arity error likehelper: expected 1 args, got 2far from the cause. Filed as interactive-cli rerun5 P1.Default-engine dispatch now checks the leading positional against the known function names and, when it doesn't match AND looks like an identifier, emits a friendly diagnostic listing the available functions instead.
The token-cost framing: silently routing an unknown subcommand wastes follow-up turns on an arity error the agent then has to chase back through the file. A direct "no such function 'wibble'; available: main, helper, ..." closes the loop in one read.
Repro
Before:
After:
What's in the diff
looks_like_subcommand_name(ASCII ident-shape check, exemptstrue/false/nil) and a new branch in the Default-engine dispatch that fires when the file has 2+ user functions and the leading positional is ident-shaped but unknown. Synthetic__lit_*decls are already filtered out of the listing per cli: restore documented auto-run for main and inline programs #307.tests/regression_cli_default.rscovering ident-shaped unknown subcommand, known subcommand happy path,__lit_*filtering, single-fn and inline pass-through, and numeric / quoted-string / bracketed-list leading-arg pass-through (the last three pin the shape guard sotests/eval_inline.rsunwrap_*_inline don't regress).examples/unknown-subcommand-listing.ilowithmain+ two helpers and-- run: / -- out:annotations, pinning the happy-path dispatch contracts across every engine via the examples harness.Three new unit tests in
src/main.rsalso coverlooks_like_subcommand_namedirectly.Test plan
cargo test --release --features cranelift— 138 test binaries pass, 0 failurescargo clippy --release --features cranelift --tests -- -D warningscargo fmt --checkcleanilo /tmp/repro.ilo wibble xproduces the new error,ilo /tmp/repro.ilo helper 5still works,ilo /tmp/repro.ilostill auto-runs mainilo dbl.ilo 21still pass-throughs as beforeilo 'dbl x:n>n;*x 2' 21still pass-throughsFollow-ups
--vm/--tree/--cranelift) still emit their backend-native "undefined function" error rather than the friendly listing. Default-engine covers the agent default-path which is the common case; lifting the guard to all engines would be a small follow-up if it surfaces.