Skip to content

fix(modules): root flags before a reserved subcommand under an alias - #24

Merged
bpamiri merged 3 commits into
wheelsfrom
peter/alias-root-flags
Oct 6, 2026
Merged

bpamiri merged 3 commits into
wheelsfrom
peter/alias-root-flags

Conversation

@bpamiri

@bpamiri bpamiri commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator

Fork-only: follows FORK.md row 2, the reserved-token rule (upstream PR cybersonic#124, still open). Found in the 2026-10-05 CLI docs sweep (C2).

What

Under an aliased binary, a root flag before a root subcommand handed the whole command to the module. For example, wheels --timing cfml 'writeOutput(1)' and wheels --verbose cfml '…' printed the module's help (Wheels) or failed with "no function main", and exited 0 without running the expression. Without the flag (wheels cfml '…') it works. Upstream v0.6.1 and main, which don't have row 2, run it.

Why

rewriteModuleHelpOrReserved (row 2) treats args[1] as the leading position, and reserved root-subcommand tokens later than that as module positionals. With a root flag first, args[1] is --timing, so cfml at index 2 counted as a module positional, and the args were rewritten to modules run wheels --timing cfml ….

How

  • leadingPositionAfterRootOptions(args) skips root options, and the separate value of --env/-e/--envfile/--timeout (ROOT_OPTIONS_WITH_VALUE). It stops at --help/-h, which keep their existing handling.
  • A reserved token at that leading position is a root subcommand the user invoked directly, so nothing is rewritten. Reserved tokens after a module subcommand are still rewritten as before.

Tests

  • New in LuCLIModuleHelpPreprocessTest: rootFlagBeforeReservedSubcommand…, rootOptionWithSeparateValueBefore…, rootOptionWithInlineValueBefore…, rootFlagBeforeModuleSubcommand_reservedPositionalStillRewritten (regression guard) and leadingPositionSkipsRootOptionsAndTheirValues.
  • RED on wheels @ 8942048 (behaviour tests only, separate worktree): the 3 "not handed to the module" cases failed, and the regression guard passed.
  • GREEN: mvn test gave 774 tests, 0 failures, 0 errors, 6 skipped. BATS not run locally; please rely on CI.
  • End to end: aliased binary -Dlucli.binary.name=envprobe (a probe module, isolated home), --timing cfml 'writeOutput(1)'. v0.6.2.2: Component [modules.envprobe.Module] has no function with name [main]. This branch: 1 plus the timing report.

Fork bookkeeping

CHANGELOG ## Unreleased entry; FORK.md "Next queue" row 12 (marked fork-only, following row 2).

Upstream

No separate upstream issue: upstream doesn't have the row-2 rule yet. If upstream takes cybersonic#124, this fix should go with it; I can offer it on cybersonic#124 then.

🤖 Generated with Claude Code

bpamiri and others added 2 commits October 6, 2026 04:41
The reserved-token rule (row 2) decides whether a reserved root
subcommand is the leading token or a module positional by looking at
args[1]. With a root flag first, 'wheels --timing cfml x' had args[1] =
--timing, so 'cfml' was treated as a module positional and the whole
command went to the module: the expression never ran.

The leading position now skips root options and the separate value of
--env/-e/--envfile/--timeout; a reserved token there is a root
subcommand the user invoked directly. Reserved tokens later in a module
command are still rewritten through 'modules run'.

Fork-only: follows row 2 (upstream PR cybersonic#124).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
@bpamiri

bpamiri commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

rev1-r3 review @ d35a1cc: code APPROVE (final pending PR CI: no checks are reported on this head yet)

RED before, reproduced. I copied the updated LuCLIModuleHelpPreprocessTest onto wheels @ 8942048, without leadingPositionSkipsRootOptionsAndTheirValues, since that helper doesn't exist on base. Result: 12 tests and exactly the 3 "is not handed to the module" cases fail; the regression guard rootFlagBeforeModuleSubcommand_reservedPositionalStillRewritten passes. GREEN on the branch: 13/0/0, with a sandboxed user.home / lucli.home.

End to end: aliased binary -Dlucli.binary.name=envprobe, isolated home, a probe module with show().

Invocation base this branch
--timing cfml 'writeOutput(1)' …has no function with name [main] 1 + the timing report
--env prod cfml 'writeOutput(2)' (separate value) same error 2
cfml 'writeOutput(3)' (no flag) 3 3
--verbose show (a module subcommand) runs the module runs the module (unchanged)

Code: leadingPositionAfterRootOptions skips root options and the separate value of each option in ROOT_OPTIONS_WITH_VALUE. I checked that set against the root @Options in LuCLI.java: the only value-taking root options are --timeout, --env/-e and --envfile, so it's complete. It stops at --help / -h, and Math.min keeps the index in bounds when only flags follow the module name. If a value-taking root option is added later and missed from the set, the result falls back to the old behaviour, which is safe. Consider a comment next to the @Options pointing at the set.

The CHANGELOG entry and FORK.md row 12 (fork-only) are fine.

PR CI: gh pr checks reports no checks on this branch yet. I'll post the final once the Build/Unit/Integration jobs have run and passed.

@bpamiri

bpamiri commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

rev1-r3 review @ a0e5058: APPROVE (final)

The only change since the approved d35a1cc is a0e5058, docs(fork): link row 12 to #24: a one-line FORK.md edit with no code or test change. The CI workflow didn't run on this docs-only head, which is why no checks are listed. The full CI run on d35a1cc (run 37458145904) passed: Build ×3, Unit ×3 and Integration ×2, with APT smoke skipped. The Java sources and tests are byte-identical between the two heads, so that run covers this one.

Ready to merge.

@bpamiri

bpamiri commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

rev1-r3 correction @ a0e5058: this head conflicts with wheels (mergeStateStatus DIRTY), and that's why no checks are listed. My final above is withdrawn until wheels is merged in. Once that merge head is pushed, I'll check that the delta is only the merge plus the FORK.md row-12 link, read that head's CI, and then post the final. The code approval at d35a1cc stands.

Signed-off-by: Peter Amiri <peter@alurium.com>

# Conflicts:
#	CHANGELOG.md
#	FORK.md
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown

Unit Tests (ubuntu-latest, Java 21)

 63 files   63 suites   2m 27s ⏱️
778 tests 772 ✅ 6 💤 0 ❌
783 runs  777 ✅ 6 💤 0 ❌

Results for commit 9cf3fd8.

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown

Integration Tests (ubuntu-latest, Java 21)

78 tests   78 ✅  2m 3s ⏱️
14 suites   0 💤
 1 files     0 ❌

Results for commit 9cf3fd8.

@bpamiri

bpamiri commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

rev1-r3 review @ 9cf3fd8: APPROVE (final; restores the final I withdrew at a0e5058)

This head is a two-parent merge of a0e5058 (the approved d35a1cc plus the FORK.md row-12 link) and wheels (c3a2d2e, which includes #21–#23). The PR's src/ changes against wheels are identical to d35a1cc: LuCLI.java and LuCLIModuleHelpPreprocessTest. The conflict resolution touches only CHANGELOG and FORK.md and keeps every side:

PR CI on this head: it runs now that the PR no longer conflicts, and all 8 pass (Build ×3, Unit ×3, Integration ×2); APT smoke skipped. mergeStateStatus is CLEAN.

Ready to merge.

@bpamiri
bpamiri merged commit 4773b61 into wheels Oct 6, 2026
11 checks passed
@bpamiri
bpamiri deleted the peter/alias-root-flags branch October 6, 2026 12:03
bpamiri added a commit that referenced this pull request Oct 8, 2026
- pom.xml version 0.6.2.3 (the Windows file/product version follows
  ${project.version}); install-validation default 0.6.2.3.
- FORK.md: 'Next queue' becomes the 0.6.2.3 queue (rows 8-15: #19,
  #21-#24, #26-#28), with the row-8 compatibility note (Wheels 4.1.x
  stays at max 0.6.2.2: no __lucliMcpCall handling), and a new empty
  Next queue.
- CHANGELOG unchanged: fork entries stay under Unreleased, as for
  0.6.2.2.

Sandboxed mvn test (-Duser.home only): 788 run, 0 failures.

Signed-off-by: Peter Amiri <peter@alurium.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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