feat: aidlc single-command CLI - dark-launch dispatcher, grammar, and build (2.3.8-2.3.9) - #560
Conversation
6c3fd8a to
91a7108
Compare
047d8f8 to
61bd7ef
Compare
61bd7ef to
7a59be1
Compare
leandrodamascena
left a comment
There was a problem hiding this comment.
CHANGES REQUESTED
I built the binary, ran the live engine, diffed the classifiers against base, and set up real workspaces. The dispatcher and new nouns are genuinely dark — nothing shipped invokes them, dev-spawn is byte-for-byte the legacy tool. But two things change live behavior, and one breaks a remedy this PR advertises.
One framing note: "no current behavior changes" isn't literally true. 2.3.8 is a deliberate, disclosed live workspace-grammar change (bare intent list now runs LIST instead of switching to a record named "list"). That's fine — the problem is the escape hatch it points to doesn't work.
Required fixes (confirmed, live impact):
1. The switch <name> escape hatch is broken for verb-named records — aidlc-lib.ts:571. workspaceCommandUtilityArgv returns [noun, name] for a switch, dropping the switch token, so the utility re-reads the name as a verb. This runs on the live orchestrate path and the Kiro adapter, not just the dark dispatcher. I reproduced it: orchestrate next intent switch list emits intent list (record unreachable), and intent switch birth emits intent birth, which births a new intent — a switch mutating state. On v2 you could create records named list/birth/create; after upgrade the advertised remedy fails for exactly those, while the doctor advisory and CHANGELOG promise it works. archive/rename/show survive (not utility verbs). t229 hides it — the "coverage" test calls the 3-token argv directly, which the engine never emits. Fix: emit [noun, "switch", name] (handlers already accept it) and test the engine path.
2. The runtime-compile reject leg isn't inert — aidlc-lib.ts:832. The PR adds /\baidlc\s+runtime\b/ as a reject disjunct feeding the live PostToolUse hook. A legacy transition whose --user-input contains the words "aidlc runtime" flips base "fire" → PR "reject", suppressing the runtime-graph recompile — reachable today, no cutover. It self-heals (next clean transition catches up), so it's below #1, but it's a live matcher change contradicting "inert until cutover". Fix: keep the reject leg path-anchored, not a bare two-word substring.
Majors (no current impact, but block the future binary cutover):
3. The compiled binary is broken for every delegate — aidlc.ts:712. I ran bun build --compile on the shipped dispatcher: version/help work, but doctor/state get/plugin sync all throw Cannot find module '/$bunfs/root/aidlc-utility.ts'. The 15 tools are dynamically imported via a computed path --compile can't discover, so they never embed. No binary ships today — but this is the artifact WP7 would ship.
4. The build gate is blind to #3 — build-binaries.ts:383. build-binaries.ts --target native printed native ok, exit 0, while the artifact fails every delegate — the gates (version/help/grep/pathless) never delegate. Exactly the BYTECODE-1 "compiles but doesn't run" case it claims to guard. Add a gate that runs one real delegate against the compiled artifact.
5. Plugin-compose hook couples to an aidlc on PATH — package.ts:914. The emitted hook probes command -v aidlc and runs aidlc plugin sync; exit $? if found. Today it falls through to bun compose.ts (no-op, confirmed). But once a binary named aidlc is on PATH it runs plugin sync (exits 1 per #3) and the fallback is unreachable — plugin compose silently stops. Dormant.
Non-blocking: router drops --project-dir on workspace routes (post-cutover only; live channel is the env var). Stale init emitter left in audit-format.md (the PR fixed the parallel 12-state-machine.md). Version 2.3.9 is consistent and correct, but open #572/#573 bump from a 2.3.7 base — coordinate merge order.
Didn't fully verify: whether the release names the binary aidlc on PATH (no .github/workflows in the repo) — that decides if #5 is dormant or live. Didn't run the Kiro/Codex adapters end-to-end (diffed the bodies; mechanical wrapper refactor).
The dark-launch discipline is careful and most holds. But #1 breaks an advertised remedy on the live path and #2 changes a live matcher, so land those first (plus the binary/gate fixes before any cutover).
…that runs its delegates Fixes leandrodamascena's PR #560 review findings: - workspaceCommandUtilityArgv now emits [noun, "switch", name] for an explicit switch instead of dropping the switch token, so a pre-existing intent or space whose slug shadows a verb (list, birth, create) stays reachable through the engine and dispatcher instead of being misread as that verb. - classifyRuntimeCompileCommand anchors its new aidlc-runtime reject leg to the start of a shell command segment instead of matching the bare two-word substring anywhere in the command string, so a legacy transition whose --user-input prose happens to contain "aidlc runtime" no longer suppresses the post-transition runtime-graph recompile. - The compiled native binary now statically imports each of the 15 dispatcher delegate modules through a literal switch instead of a runtime-computed import path, so Bun's --compile bundler can discover and embed them; previously every delegated subcommand failed with a missing module error. - build-binaries.ts gates the native artifact with a real plugin sync delegate call in addition to the existing version/help/grep checks, so a future regression that breaks delegate embedding fails the build instead of shipping a broken binary. - The plugin-compose hook's PATH-preferred aidlc invocation now only exits early on success; a nonzero plugin sync falls through to the bun compose path instead of leaving compose unreachable. - Mirrors the intent-birth-for-init emitter correction in core/knowledge/aidlc-shared/audit-format.md that the parallel state machine doc already had. Extends t226 (path-anchored reject leg, prose-in-flag-value negative case), t229 (engine and dispatcher switch to a verb-named record without birthing), t230 (dispatcher parity for the corrected switch argv), t231 (the exit-vs-fallthrough plugin hook shape), and t238 (a real plugin sync delegate call against the rebuilt native artifact, plus a fake-entry negative case for the new gate).
7a59be1 to
f369c76
Compare
|
Thanks for the thorough review. The requested fixes landed in f1f0b5f and were followed by runtime-data and dispatch hardening through the current head:
Regression coverage was extended across the detector, dispatcher, workspace, plugin-hook, binary-build, and doctor tests. Re-requesting review. |
|
Follow-up on my earlier CHANGES REQUESTED. I re-reviewed the reworked branch and my three original findings (workspace P1 — the compiled binary is non-functional beyond version/help/doctor
P2 — grammar/parser gaps
Why t238 doesn't catch these: the build gate's None of this is a production regression today — the binary is dark (not on PATH, WP7 out of scope). But shipping the build script + gate now, with the gate certifying a binary that's non-functional for its real delegated work, is the concern: the gate should exercise |
leandrodamascena
left a comment
There was a problem hiding this comment.
My previous findings are resolved. I rebuilt and tested the current binary, but I found two remaining blockers:
-
Mutable commands can write into the packaged runtime. Running
plugin selectorgraph compileagainst a project without an installed harness modifies files beside the executable, such asharness.jsonandstage-graph.json. Project commands should never modify the shared packaged runtime. -
--project-diris dropped forhook,adapter, andstatuslineroutes. I confirmed that a hook from the current working directory runs even when--project-dirpoints to another project.
I also found some non-blocking issues:
- Relative
--project-dirvalues break Bolt/Swarm re-entry. - The runtime matcher still rejects quoted user input containing a separator followed by
aidlc runtime. __sensor-script-filecan execute any matching TypeScript file. This matters if theaidlcbinary will be allowlisted as a trusted command.
The targeted tests, native binary tests, and package drift check pass. These issues are dormant while the binary remains dark, but the first two conflict with the claim that the packaged binary is safe and self-contained. I recommend fixing them before merge, or clearly scoping the binary as experimental until WP7.
…that runs its delegates Fixes leandrodamascena's PR #560 review findings: - workspaceCommandUtilityArgv now emits [noun, "switch", name] for an explicit switch instead of dropping the switch token, so a pre-existing intent or space whose slug shadows a verb (list, birth, create) stays reachable through the engine and dispatcher instead of being misread as that verb. - classifyRuntimeCompileCommand anchors its new aidlc-runtime reject leg to the start of a shell command segment instead of matching the bare two-word substring anywhere in the command string, so a legacy transition whose --user-input prose happens to contain "aidlc runtime" no longer suppresses the post-transition runtime-graph recompile. - The compiled native binary now statically imports each of the 15 dispatcher delegate modules through a literal switch instead of a runtime-computed import path, so Bun's --compile bundler can discover and embed them; previously every delegated subcommand failed with a missing module error. - build-binaries.ts gates the native artifact with a real plugin sync delegate call in addition to the existing version/help/grep checks, so a future regression that breaks delegate embedding fails the build instead of shipping a broken binary. - The plugin-compose hook's PATH-preferred aidlc invocation now only exits early on success; a nonzero plugin sync falls through to the bun compose path instead of leaving compose unreachable. - Mirrors the intent-birth-for-init emitter correction in core/knowledge/aidlc-shared/audit-format.md that the parallel state machine doc already had. Extends t226 (path-anchored reject leg, prose-in-flag-value negative case), t229 (engine and dispatcher switch to a verb-named record without birthing), t230 (dispatcher parity for the corrected switch argv), t231 (the exit-vs-fallthrough plugin hook shape), and t238 (a real plugin sync delegate call against the rebuilt native artifact, plus a fake-entry negative case for the new gate).
97c9971 to
4531b71
Compare
|
@leandrodamascena I addressed the latest review findings and rebased the branch onto the current
The branch is now versioned |
leandrodamascena
left a comment
There was a problem hiding this comment.
Approved. All five previous blockers have been addressed, and the implementation checks passed. The remaining t144 failure is a non-blocking test portability issue caused by /tmp resolving to /private/tmp on macOS.
…ooks Extract the stop hook's engine-engagement classifier and the runtime-compile hook's command filter into shared pure functions in aidlc-lib.ts, and extend both to recognize the upcoming aidlc <noun> <verb> grammar beside the legacy bun <path>/aidlc-X.ts shape. The legacy regexes are retained verbatim (plugin manifests and dev mode keep that shape as a permanent input); the new-shape branches are inert until the new grammar ships anywhere, so this lands dark - detectors first, so the grammar never runs unguarded. Deliberate delta: new-shape 'aidlc park' counts as engine engagement (legacy orchestrate park did not - the old segment logic recognized only next/report). Internal change only: no version bump per the changelog policy (no user-visible behavior change; both hooks classify every existing command exactly as before, pinned by the corpus). Tests: t225-detector-corpus pins the 77-case both-shape corpus (labels, negatives, documented false-positive classes, four named regression guards); t131 gains new-shape twins for the runtime-compile seam (state approve, report-after-gate, runtime recursion guard). Coverage registry regenerated for the new file.
Every argv-dispatching tool (19) now exports main(argv) and keeps the guarded dev invocation; the five tools that ran main() unconditionally on import (sensor, swarm, validate, sensor-required-sections, sensor-upstream-coverage) gain the import.meta.main guard. All 11 hooks and the 3 harness adapters move every module-scope statement into an exported run(input) (adapters: run(target, input)) with process.exit sites converted to return codes; the spawned process contract (stdout/stderr bytes, exit codes, files written) is byte-identical, pinned by the existing hook suites. mint-presence no longer audit-appends at import time. This lets a future single dispatcher statically import every module without side effects; until that dispatcher exists nothing user-visible changes, so no version bump (internal refactor per the changelog policy). utility.ts die() now takes its error-context argv from main's parameter instead of re-reading process.argv (same output, dispatcher-safe). Tests: t226 pins main(argv) exports + no-side-effect-on-import for all 19 tools plus dev-path envelopes; t227 pins run() exports + quiet import for all 11 hooks and 3 adapters plus spawned-contract smokes; the coverage-registry none-to-cli pin gains both files.
…visory (2.3.8)
The workspace nouns gain real verbs through one shared parser in aidlc-lib
(parseWorkspaceCommand), driving both existing call sites: the terminal
classifier (classifyTerminalCommand) and the engine's next parser/handler.
space list/switch/create and intent list/switch/birth now mean what they say;
bare-name switch sugar and the legacy space-create spelling keep working;
archive/rename/show error as reserved future verbs. Missing names are usage
errors instead of switches to a verb-named record.
Precedence unified deliberately: a leading workspace noun owns the whole
command, so a later --status is that command's token, not a mode switch
(previously the two sites disagreed; the classifier's early return wins).
Reservation: RESERVED_RECORD_NAMES grows from {help} to the verb union at
both creation chokepoints (intent birth slugify, space create). No
grandfather machinery - explicit switch <name> reaches pre-existing
verb-named records by construction, and doctor gains an advisory row naming
any such records.
Kiro CLI adapter: the off-band tokenizer now preserves double-quoted names
(splitDoubleQuotedArgs) and forwards the full semantic argv tail, so
space create "My Space" survives the seam.
Version 2.3.5 + CHANGELOG (user-visible migration delta listed there);
docs updated to the new spellings with legacy notes.
core/tools/aidlc.ts is the single-command CLI entry the binary distribution plan compiles: a routes table (29 entries: 14 passthrough, 10 translation, stubs for future handlers, routing-only hook/statusline/adapter nodes) with per-row argv synthesis, two help renderers off the same table (short human help; help --all with the plumbing banner and the slash-flag alias table), and a one-line unknown-command error naming the nearest help node. Two delegation modes, discriminated by the compiled-binary marker in import.meta.url: dev spawns bun on the sibling tool file (byte-identical stdio passthrough); compiled calls the tool's exported main(argv) in-process. hook <name>, statusline, and adapter <target> dispatch through the exported run() entries. version prints the static AIDLC_VERSION without touching any data (binary plan requires a data-free version path). Workspace nouns route through the shared parseWorkspaceCommand, so the dispatcher and the engine can never disagree on workspace semantics. DARK: no shipped string references the dispatcher; nothing user-visible changes, so no version bump (internal per the changelog policy). Tests: t229 pins the parity suite (29 translated/passthrough cases byte-identical old-vs-new shape), dev-vs-in-process mode parity, routes-table completeness (every tool target on disk, every exported tool reachable, help --all one-entry-per-verb), help shapes and error UX, and hook/statusline routing through run().
New deterministic handlers per the Utility Handler Checklist: - config get <key> / config list [--json] read the active workflow's Depth and Test Strategy beside the existing write path; the dispatcher's config set translates to config-change, which stays accepted. - plugin list [--json] renders known plugins with enabled state from the harness.json selection (absent selection = all enabled). - plugin sync fronts the plugin fold: runs each installed plugin root's hooks/compose.ts with the emitted hook's env contract; idempotent, exits 0 with a note when no plugin trees are installed. init changes meaning for one transition release: instead of silently routing to intent-birth (the deprecated alias), it errors loudly telling the user to describe what to build - the future init lays down the project data tree from the installed harness dist. upgrade reserves its name with a clear not-available error until the packaged distribution lands. The emitted plugin SessionStart hook now probes for an installed aidlc binary FIRST (running aidlc plugin sync) and falls back to the bun compose.ts path, still skipping cleanly when neither exists. Test-suite migration for the init meaning change: ~25 files used the deprecated init alias as their fixture driver; all repointed to intent-birth (same handler, same flags) - the exact stale-caller class the transition error exists to surface. Version 2.3.6 + CHANGELOG; docs updated (cli-commands table, plugin authoring guide, stale init references swept).
scripts/build-binaries.ts compiles the generated Claude dispatcher (dist/claude/.claude/tools/aidlc.ts) per target with bun build --compile, SEPARATE from package.ts (which stays the deterministic drift-guarded projection; this script refuses to build on a drifted dist). Default = native only; --target / --all-targets cover the release matrix. Gates, because the build exit code is a liar (BYTECODE-1: a bytecode build exits 0 while emitting an artifact that crashes at launch - bytecode is never enabled here and a comment says why): - native: run version (must equal the stamped AIDLC_VERSION) and help from a neutral cwd; re-run version with an EMPTY PATH proving the compiled version path needs no PATH bun. - bundle inspection: a text bundle of the entry must contain no literal bun spawn outside the marked dev-mode line (the dispatcher's dev spawn carries an inline marker). - cross targets: file(1) needle (ELF/PE32+/Mach-O) + >10MB size; Windows outfile expects bun's appended .exe. Artifacts land in gitignored build/binaries/ with build-results.json. Internal build tooling: no version bump. Tests: t231 builds the real native artifact, re-runs it from /tmp against the stamped version, and proves the version gate can FAIL via an injected fake entry (AIDLC_BUILD_ENTRY test seam).
v2 shipped 2.3.10 (linter eslint pin) and reserved 2.3.11-2.3.14, so this branch's versions shift again: 2.3.8 (workspace verbs) -> 2.3.15 and 2.3.9 (config/plugin verbs) -> 2.3.16. CHANGELOG headings renumbered to match with the 2.3.10 entry preserved above 2.3.7; core + dist version files and the README badge bump to 2.3.16. Test slots t226-t231 + t238 do not collide with v2's t221-t225/t232/t233/t237 and are unchanged. dist regenerated.
…that runs its delegates Fixes leandrodamascena's PR #560 review findings: - workspaceCommandUtilityArgv now emits [noun, "switch", name] for an explicit switch instead of dropping the switch token, so a pre-existing intent or space whose slug shadows a verb (list, birth, create) stays reachable through the engine and dispatcher instead of being misread as that verb. - classifyRuntimeCompileCommand anchors its new aidlc-runtime reject leg to the start of a shell command segment instead of matching the bare two-word substring anywhere in the command string, so a legacy transition whose --user-input prose happens to contain "aidlc runtime" no longer suppresses the post-transition runtime-graph recompile. - The compiled native binary now statically imports each of the 15 dispatcher delegate modules through a literal switch instead of a runtime-computed import path, so Bun's --compile bundler can discover and embed them; previously every delegated subcommand failed with a missing module error. - build-binaries.ts gates the native artifact with a real plugin sync delegate call in addition to the existing version/help/grep checks, so a future regression that breaks delegate embedding fails the build instead of shipping a broken binary. - The plugin-compose hook's PATH-preferred aidlc invocation now only exits early on success; a nonzero plugin sync falls through to the bun compose path instead of leaving compose unreachable. - Mirrors the intent-birth-for-init emitter correction in core/knowledge/aidlc-shared/audit-format.md that the parallel state machine doc already had. Extends t226 (path-anchored reject leg, prose-in-flag-value negative case), t229 (engine and dispatcher switch to a verb-named record without birthing), t230 (dispatcher parity for the corrected switch argv), t231 (the exit-vs-fallthrough plugin hook shape), and t238 (a real plugin sync delegate call against the rebuilt native artifact, plus a fake-entry negative case for the new gate).
…hes on a bad stage graph
Finishes the delegate-embedding fix from the previous commit: the compiled
binary's delegates now run, but doctor and next still crashed because
stage-graph.json, scope-grid.json, harness.json, scopes/, and agents/ are
resolved relative to import.meta.url, which points at a synthetic /$bunfs/
path inside a compiled binary with no real filesystem presence there.
Bun's static-asset embedding (import x from "./data/foo.json" with { type:
"file" }) is not viable for these paths: stage-graph.json and friends are
compiled, per-harness artifacts that live only under dist/<harness>/, never
under core/tools/data/ where the harness-neutral aidlc-lib.ts and
aidlc-graph.ts source lives, so a static import of that path fails
typecheck and dev-mode bun run for every caller that loads these modules
straight out of core/ (six unit tests, plus dev-mode delegation).
Instead, each data-path resolver now tries, in order: the existing env-var
override (unchanged), the module-relative path if it exists on disk
(unchanged behavior for every source-tree and dist/<harness>/ install), and
only then a new fallback relative to the running executable's own location
(dirname(process.execPath)), which resolves correctly even through a PATH
lookup or a symlink. build-binaries.ts now stages data/, scopes/, agents/,
and aidlc-common/stages/ beside the compiled artifact so that fallback has
something to find.
Also fixes a related pre-existing crash in handleDoctor: an unguarded
loadGraph() call threw straight out of the command instead of degrading to
a failing advisory row, unlike every other doctor check in the same
function. Reproduces on any real install with a missing or corrupt
stage-graph.json, not just the compiled binary.
New build-binaries.ts gates: runtime-assets (proves the sibling trees
staged) and delegate-doctor-data (runs doctor from a scratch tmpdir against
the compiled artifact and fails on any Cannot find module / $bunfs / ENOENT
signature). Extends t238 with both, and t37 with the handleDoctor crash
reproduction.
…ase, not bare-name sugar The finding-1 fix in the previous commit (workspaceCommandUtilityArgv emitting the literal "switch" token) was applied unconditionally to every switch-kind WorkspaceCommand. That over-corrected: bare-name switch sugar (space teamB, explicit: false) got 3-tokenized into space switch teamB too, even though the reviewer's finding and the shipped CHANGELOG both describe bare-name sugar as unaffected, still-desired behavior. workspaceCommandUtilityArgv now branches on the command's existing explicit flag: explicit switch <name> still emits [noun, "switch", name] (the actual fix), bare-name sugar keeps the original [noun, name] shape. t229's own migration-parity table and precedence-pin test were edited in the prior commit to match the regressed behavior instead of catching it; reverted those four rows and the precedence-pin assertion back to the 2-token shape they pinned before. t114 (cases 20 and 22), t178, and t198 needed no test changes - they already pinned the correct sugar behavior and were red against the code bug, not stale. Regenerated tests/.coverage-registry.json + .coverage-ratchet.json (the prior two commits changed covered units without refreshing them).
Close the five gaps found by compiling the dispatcher and running its delegated paths as a real binary: - Add aidlc-runtime-paths.ts, a shared resolution ladder (env seam -> project install -> module-relative -> executable-packaged runtime) and route sensorsDir, data/scopes/agents/stages/skills/conductor lookups through it, so `aidlc sensor list`, graph compiles, and validation work outside a seeded project layout. - Re-enter the compiled executable by public noun in bolt/swarm spawnSibling and utility runBunTool instead of `bun run <import.meta.url path>`, so compiled fork/merge and plugin-selection regeneration need no bun on PATH. Bundle the four sensor worker scripts and dispatch them via internal __sensor-script verbs. - Extract --project-dir as a global flag before noun/verb parsing, so interleaved forms (`space --project-dir X create teamB`) route correctly; thread it to delegates via AIDLC_PROJECT_DIR. - Accept legacy top-level `space-create` in the dispatcher grammar. - Stage complete per-harness distributions under build/binaries/<target>/runtime/<harness>/ and grow the native gate set: sensor list/fire, unseeded graph compile, validate outputs, generated-surface checks, per-harness runtime resolution, plugin select, conductor persona, workspace flag forms, and bolt/swarm self-reentry, all with an empty PATH; doctor must report a non-zero complete schema count. The compiled binary detects the project's harness dir when AIDLC_HARNESS_DIR is unset - main() pins runtimeHarnessDir()'s probe (.claude/.kiro/.codex by tools/data/harness.json) instead of assuming .claude, and resolveHookPath shares the same ladder; a kiro-only install now resolves .kiro data with no env, gated by harness-probe-kiro. rulesDir keeps a module-relative rung ahead of the packaged-runtime fallback so dev checkouts and installed trees resolve rules without env pinning; fixture copy-lists that cherry-pick aidlc-lib.ts gain its new aidlc-runtime-paths.ts sibling.
50db1f3 to
d5b235f
Compare
…ards - Renumber test to t244 (t238 taken by awslabs#560 on v2) - Test 4: compare row order directly (no sort) so scrambled tables fail - Test 1: derive INIT_ROW_LABEL from graph numbers so renumbered init stages trip the guard - Conflict resolution: keep v2's 14-agent wording, add matrix link only
…ards - Renumber test to t244 (t238 taken by awslabs#560 on v2) - Test 4: compare row order directly (no sort) so scrambled tables fail - Test 1: derive INIT_ROW_LABEL from graph numbers so renumbered init stages trip the guard - Conflict resolution: keep v2's 14-agent wording, add matrix link only
…ards - Renumber test to t244 (t238 taken by awslabs#560 on v2) - Test 4: compare row order directly (no sort) so scrambled tables fail - Test 1: derive INIT_ROW_LABEL from graph numbers so renumbered init stages trip the guard - Conflict resolution: keep v2's 14-agent wording, add matrix link only
…ards - Renumber test to t244 (t238 taken by awslabs#560 on v2) - Test 4: compare row order directly (no sort) so scrambled tables fail - Test 1: derive INIT_ROW_LABEL from graph numbers so renumbered init stages trip the guard - Conflict resolution: keep v2's 14-agent wording, add matrix link only
…#596) * docs: add stage-by-scope matrix to the scopes guide, drift-guarded by t238 The guide stated per-scope stage counts ("22 of 32") and skip prose, but the full stage x scope EXECUTE/SKIP view existed only as compiled JSON (scope-grid.json) or the runtime scope-table command - a reader could not see which stages a scope walks through without joining 32 stage frontmatter blocks by hand. - docs/guide/05-scopes-and-depth.md: new Stage-by-Scope Matrix section after the Scope Routing Table - all 9 stock scopes x 32 stages, generated from the compiled scope-grid.json + stage-graph.json, wrapped in BEGIN/END scope-stage-matrix markers. Footnote clarifies membership vs runtime-conditional execution and that composed scopes live in scope-grid.json only. - tests/unit/t238-scope-matrix-doc-sync.test.ts: drift guard in the t132 two-direction discipline - columns, row identity, every cell, the collapsed initialization row, and totals, all cross-checked against the compiled grid (itself pinned to stage frontmatter by t124), forward and reverse. Mechanism: none (pure in-process reads). - docs/guide/04-phases-and-stages.md: cross-link the matrix from the stage-colors note and Next Steps. - docs/reference/11-contributing.md: "Adding a Scope" step 8 names the matrix and its guard so scope authors update it in the same PR. Doc + test only - no version/CHANGELOG bump per the changelog policy. * fix: address PR review — rebase on v2, renumber t238→t244, tighten guards - Renumber test to t244 (t238 taken by #560 on v2) - Test 4: compare row order directly (no sort) so scrambled tables fail - Test 1: derive INIT_ROW_LABEL from graph numbers so renumbered init stages trip the guard - Conflict resolution: keep v2's 14-agent wording, add matrix link only * fix: correct ALWAYS legend wording, guard duplicate special rows - docs/guide/04-phases-and-stages.md: ALWAYS now reads 'runs whenever the selected scope includes it' — matches the orchestrator reference and the matrix (1.4 Scope Definition is ALWAYS yet skipped by 6 scopes) - t244: parseDocTable throws on duplicate init row, totals row, or stage row — a duplicated special row no longer silently overwrites - t244: template literal for init-label assertion (Biome preference) * fix: validate separator row and tighten special-row label/numeric checks - parseDocTable now validates lines[1] is a well-formed Markdown table separator (each cell matches :?-{3,}:?) with correct column count - Initialization row requires exact label 'Initialization (all 3 stages)' - Footer row requires exact label '**Total stages**' (not substring match) - Total cells must be strictly numeric (/^\d+$/ after stripping bold) * fix: harden parser — validate header labels, cell content, body row column count, derive init label - Validate header[0] is '#' and header[1] is 'Stage' at parse time - Constrain scope cells to only ✓ or empty (catches typos/wrong Unicode) - Validate every body row has the same column count as the header - Derive INIT_STAGE_LABEL from initStages.length instead of hard-coding '3' * fix: correct runtime semantics, validate contiguous table and full marker - docs/guide/05-scopes-and-depth.md: rewrite matrix footnote — ✓ marks static scope membership; CONDITIONAL stages self-skip; pending stages reshaped via composer proposal (not 'skip at approval gate') - Same file: fix identical claim in 'When in doubt, start with feature' - t244: validate BEGIN marker line ends with --> (broken marker no longer hides the matrix inside an HTML comment) - t244: require pipe-lines form one contiguous block (blank line mid-table breaks Markdown rendering and now throws) - docs/reference/11-contributing.md: 'Adding a Stage' step 5 now mentions the matrix and t244 --------- Co-authored-by: osataken <osataken@amazon.co.th>
What this is
The aidlc single-command CLI, shipped dark: a noun-verb dispatcher (
aidlc <noun> <verb>) that replaces path-typed invocations (bun .claude/tools/aidlc-X.ts ...), built as the user-facing half of the binary distribution plan. The grammar exists atcore/tools/aidlc.ts, fully tested under bun and compiled/smoke-gated as a native binary - but no shipped string references it yet. The cutover (WP7) waits for a signed binary on PATH.Rebased 2026-07-14 onto v2 after the four-PR merge train (#550 2.3.5, #565, #562 2.3.6, #563 2.3.7): the base auto-retargeted to v2, this branch's versions shifted to 2.3.8/2.3.9, and two test slots moved (t230-handler-additions -> t231, t231-build-binaries -> t238; t232-t237 are claimed by merged and in-flight work). Previously rebased 2026-07-12 onto #550's review-fix head.
The commits (landing order = the dark-launch safety order)
main(argv), 11 hooks + 3 adapters exportrun(input), zero side effects on import; spawned behavior byte-identical (t227/t228).space create teamBnow creates instead of switching to a space named "create"); verb names reserved at creation; doctor advisory; Kiro quoted-name fix (t229).help/help --allwith plumbing banner + alias table; 29 byte-parity cases old-vs-new shape (t230).config get/list,plugin list/sync, theinittransition error (loud, one release, replaces the silent intent-birth alias),upgradestub; emitted plugin hook probes aidlc-then-bun (t231).scripts/build-binaries.tswith mandatory smoke gates (run version+help from neutral cwd, empty-PATH version, bundle grep-gate); bytecode permanently banned with the reproduced failure documented (t238).User-visible changes (the complete list)
Everything else is byte-identical - the parity suite pins it. Two CHANGELOG'd releases ride along:
list/switch/create/birthverbs (bare-name sugar preserved, legacyspace-createaccepted);archive/rename/showreserved; verb-shaped names refused at record creation; doctor flags pre-existing collisions; Kiro preserves quoted multi-word names.config get/list,plugin list/sync;initerrors loudly for one transition release instead of silently creating an intent (its future meaning is data-tree scaffolding per the install-model decision);upgradereserves its name.Evidence
package.ts --checkclean; typecheck clean; coverage registry fresh.intent-birthin the same commit.Out of scope (later, per the plan)
WP7 cutover (rewrites the 67 authored + ~22-26 generated invocation strings, hook wiring, allowlists - only after a signed binary is on PATH) and WP8 docs. The sliced live pre-merge gate runs against the cutover PR; this PR ran the default deterministic tier per WP plus at tip.