Skip to content

Added inter unit dependency management through a dedicated stage in i… - #5

Closed
SiddhJog wants to merge 1 commit into
mainfrom
feature/inter-unit-contracts
Closed

Added inter unit dependency management through a dedicated stage in i…#5
SiddhJog wants to merge 1 commit into
mainfrom
feature/inter-unit-contracts

Conversation

@SiddhJog

Copy link
Copy Markdown
Contributor

…nception and dependency check question during construction

Issue #, if available:

Description of changes:

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

…nception and dependency check question during construction
@SiddhJog SiddhJog added the enhancement New feature or request label Nov 27, 2025
@SiddhJog
SiddhJog marked this pull request as draft November 27, 2025 05:18
@SiddhJog SiddhJog changed the title added inter unit dependency management through a dedicated stage in i… DRAFT: added inter unit dependency management through a dedicated stage in i… Nov 27, 2025
@raj-jain-aws

Copy link
Copy Markdown
Contributor

@SiddhJog - The changes are a bit obsolete as the base rules have changed since this PR was created. Could you recreate it and capture your recent observations when you tested this. Also, in general, there are a few instances where we're seeing more random behavior, hallucinations etc. We believe this could be due to over bloating of the steering language, too many mandatory clauses where the model starts ignoring them. Could we introduce this feature with minimal rules verbiage added and ensure that adding this doesn't break existing functionality?

@raj-jain-aws
raj-jain-aws requested a review from wilmatos January 14, 2026 04:20
@SiddhJog SiddhJog changed the title DRAFT: added inter unit dependency management through a dedicated stage in i… Added inter unit dependency management through a dedicated stage in i… Feb 11, 2026
@SiddhJog SiddhJog closed this Feb 11, 2026
tejasavora pushed a commit to tejasavora/aidlc-workflows that referenced this pull request Jun 16, 2026
…enforcement

Bug awslabs#1 (stale graph): stage-graph.json now includes workspace_requires
field for 14 code-producing stages. Field added to StageEntry interface
in aidlc-lib.ts.

Bug awslabs#2 (stop hook pressure): Stop hook now checks for "Parked: true" in
state file and allows immediate stop. Agent can park the workflow cleanly
between stages by writing `aidlc-state.ts set Parked true` — no need to
rubber-stamp remaining stages to escape the hook.

Bug awslabs#5 (workspace_requires enforcement): 14 stages now have
`workspace_requires: true` in their YAML frontmatter. The approve and
advance guards check: if workspace_requires is true AND no files changed
outside aidlc-docs/ → BLOCK. Writing only markdown docs is not sufficient
for code-generation, e2e-test, ci-pipeline, static-analysis, security-scan,
coverage-enforcement, integration-verification, frontend-verification,
contract-generation, data-migration, data-seeding, sandbox-deploy,
iac-execution, sandbox-provisioning.

Stages affected:
- code-generation: must write src/ files
- contract-generation: must write test files
- e2e-test: must write test files
- ci-pipeline: must write CI config (.github/workflows, buildspec, etc)
- sandbox-deploy: must create docker-compose or deploy config
- static-analysis: must modify code (auto-fixes)
- security-scan: must modify code (patches)
- coverage-enforcement: must add test files
- integration-verification: must fix stubs in src/
- frontend-verification: must wire endpoints in src/
- data-migration: must write migration files
- data-seeding: must write seed scripts
- iac-execution: must deploy (changes IaC state)
- sandbox-provisioning: must provision (changes infra)
apackeer added a commit that referenced this pull request Jun 23, 2026
… follow the cursor (#1) + templates framework-default branch (#4) + space-create skeleton (#5)

Makes AIDLC's method, its harness-native rule includes, and its own rule
resolver follow the active-space cursor instead of being pinned to `default`.
Single-team/default users are unaffected (zero churn). Bumps to 2.2.0.

#1 — resolver follows the cursor:
- aidlc-graph.ts: memoryDirFor()/memoryTemplatesDir() take an optional `space?`
  defaulting to activeSpace(projectDir) (cursorless → default), via a new
  memorySegmentsForSpace() helper mirroring the lib.ts space?-param precedent.
  The learnings/practices writers + the templates sensor follow the cursor for
  free. rulesDir()/memoryDisplayPath() stay default-pinned — rules_in_context is
  display paths frozen at PACKAGE time (not a runtime content channel), so the
  load-bearing rule delivery is the native includes + the writers.
- aidlc-includes.ts (NEW): repointHarnessIncludes(projectDir, space?) surgically
  rewrites ONLY the pointer segment of the active harness's native include in
  place — Claude @-stub, Kiro agents/*.json resources glob, Codex AIDLC_RULES_DIR.
  The include files stay COMMITTED (each carries load-bearing engine wiring the
  CLI reads at startup before any hook could regenerate it — gitignoring them is
  a fresh-clone chicken-and-egg). Idempotent: a no-op at `default`, so a
  single-team committed tree never churns. Preserves vision §11.6 "reference,
  not copy" (a committed pointer has no rule content to drift).
- Wired into the space switch (handleSpace), bootstrap (ensureWorkspaceDirs),
  the SessionStart ensure-step, and the --doctor readiness check (pinned to
  DEFAULT_SPACE). Retires the stale "fast-follow / out of scope" deferral.

#4 — templates framework-default branch:
- frameworkTemplatesDir() (NEW) + dispatcher threads --framework-templates-dir;
  the required-sections sensor resolves team → framework-default → floor
  (resolveTemplatePath, first hit wins); stage-protocol.md agent clause matches.
  Ships ZERO default files (the middle tier exists + is extensible; behaviour is
  unchanged until a default is authored).

#5 — space-create skeleton:
- handleSpaceCreate now seeds the full {memory, codekb, knowledge, intents} shape
  (codekb/ + knowledge/ with .gitkeep floors), matching default.

Folded-in cleanups (internal build + test fixture; no version impact):
- #7: remove the dead `{ src:"rules", dst:"steering" }` manifest row from
  harness/kiro + harness/kiro-ide (core/rules/ is gone; .kiro/steering/ was never
  a committed output — Kiro reads the agents/*.json resources glob now). Keep
  `rulesRename: "steering"` — it independently rewrites in-prose steering mentions
  + compiled-data paths (package.ts:87-89,383-388). dist byte-identical.
- t66: regenerate the stale designer-export golden (export.json:718 still said
  flat aidlc/codekb/<repo>/; live emits aidlc/spaces/<active-space>/codekb/<repo>/
  from the codekb merge). t66 green 86/0.

Tests: new t-active-space-includes (9); t155 +4 middle-branch tests; t165 stale
"pure cursor write" comment fixed; three journey tests updated for the #5 shape
(t-journey-workspace.sdk — verified live; t-acp-kiro-journey — verified live;
t-exec-codex-journey — edit matches, codex-exec flaked upstream so unverified-live).
coverage registry regenerated (function 77→78). Version trio → 2.2.0 + CHANGELOG +
README badge (t68). Docs + vision §8/§9 reflect shipped reality.
apackeer added a commit that referenced this pull request Jun 25, 2026
…ti-repo (2.1.0) (#429)

* feat(workspace): promote codekb to repo-level aidlc/codekb/<repo>/

* feat(templates): team output-format overrides — resolver + agent skeleton + sensor verify

* feat(memory): relocate rules to aidlc/memory/; re-point loadRules + per-harness native includes

* test: repair stale rule-path + codekb goldens after P0/P5 relocation

P0 (codekb→aidlc/codekb/<repo>/) and P5 (rules→aidlc/spaces/default/memory/,
neutral basenames) changed display paths + rule-file basenames that these
goldens/fixtures embed. The phases' scoped test runs did not cover t43/t66/t103,
so the goldens went stale. Pure relocation repair — no behavioural change.
Test-only: no version bump (AGENTS.md changelog policy).

* feat(dist): seed workspace shell (spaces/default/memory defaults + native includes) + re-rooted .gitignore

* fix(templates): point sensor template lookup at the shipped memory tree + add P6 seam tripwire

Review of Stage A surfaced one BLOCKER and the writer/reader MAJOR; both addressed here.

BLOCKER — TPL template lookup pointed where nothing ships. The required-sections
dispatcher defaulted --templates-dir to <pd>/aidlc/memory/templates, but P5/SEED ship
the floor at <pd>/aidlc/spaces/default/memory/templates (where the rules resolver and all
3 native includes point). A team's template was silently ignored at the real runtime path;
green only because t155 always injected --templates-dir. Fix: derive the default from the
same MEMORY_SEGMENTS the resolver+packager use, via new exported memoryTemplatesDir()
(and memoryDirFor()) in aidlc-graph.ts; correct the 4 doc/comment refs (manifest,
stage-protocol clause, .gitkeep, sensor-script comment) to the real path. t155 gains 2
regression tests that exercise the DEFAULT path + cross-check it against where dist ships
(proven load-bearing: revert the fix → both fail).

Fail-safe for the P5 reader/writer seam (deferred to P6). P5 re-pointed only the rule
READER; the learnings writer (aidlc-learnings.ts) + practices-promote (aidlc-state.ts)
still write the OLD <harness>/rules/ path the reader no longer reads — a silent-loss
window until P6. New t158 is a test.failing tripwire: green now (round-trip genuinely
does not close — verified persist writes .claude/rules/aidlc-team-learnings.md), flips RED
the moment P6 redirects the writer under aidlc/spaces/<space>/memory/, forcing the seam
closed and the tripwire removed. A companion guard documents the current divergence.

Test-only + a path-correctness fix; no version bump (no user-facing command/flag change;
the feature was non-functional at its real path before this).

* refactor(paths): funnel state/audit/aidlc-docs literals through helpers (no-op)

* feat(workspace): re-root aidlc-docs tree per-intent + key audit lock per-intent (P1 Step B + P3)

Re-roots the record under aidlc/spaces/<sp>/intents/<slug>-<id8>/ and keys
the audit lock per-intent in the same change (the shared-lock cliff makes
them indivisible — re-rooting state per intent while the lock stays
projectDir-keyed would serialize all intents and a SIGKILL would wedge them).

- re-root the whole aidlc-docs record family per-intent; the two engine
  resolvers (resolveArtifactPath, memoryPathFor) re-rooted with intent context
- migration-aware legacy-flat resolution via a SINGLE named legacyFlatFallback()
  (P9/Stage D retires it; t159 tripwire flips RED to force removal)
- migrateFlatLayout: marker-only idempotency, same-fs staging, one atomic
  rename, source never deleted, returns movedFrom for the caller's git-rm
- per-intent audit lock: workspace-sentinel bucket for intents.json writes,
  composite-keyed depth/handler maps, PID+mtime stale-lock reaper (rename-steal,
  unstamped-grace), doctor leaked-lock probe
- emitError holdsAuditLock fix (composite-keyed; was a 5s in-transaction
  self-deadlock after per-intent keying)
- stage-protocol restatement (artifact paths are engine-resolved)
- tests: t159 (fallback tripwire), t160 (record resolution + migration + audit
  shards), t161 (lock keying + reaper); t76/t49 stamp a live lock owner

* feat(workspace): thread space+intent selector through state/orchestrate/audit/runtime/worktree/bolt/swarm

* fix(workspace): stabilize audit shard identity + per-intent lock keying + shard ordering (Stage B review fixes)

Address the HOLD findings from the adversarial Stage B review (1 blocker + 5
majors). The re-root architecture was sound; these fix the cross-process
shard-identity plumbing and lock-bucket keying on top of it.

- BLOCKER: audit shard identity is now per-CLONE (a gitignored aidlc/.aidlc-clone-id
  token), not per-PID — fork and merge subprocesses resolve the SAME shard, so
  Bolt merge no longer fails ("main audit not found") on a migrated project
- MAJ: reaper re-verifies the stamp before the steal-rename (no two-holder race)
- MAJ: state fork/merge wrap their per-intent writes in the per-intent lock bucket,
  not the __workspace__ sentinel (restores P3 isolation)
- MAJ: fragment-fork resolves the space segment once (no write-path/read-path ENOENT)
- MAJ: runtime-compile hook reads the active intent's shard (graph refreshes after
  a transition on the new layout)
- MAJ: cross-shard audit reads order by timestamp, not lexical filename
- t162-per-intent-layout-cli: NEW — drives real CLIs (state transition + Bolt
  fork/merge) against a seeded per-intent layout, the coverage gap that let the
  blocker hide; .aidlc-clone-id gitignored across all 3 harnesses

* fix(workspace): close fork/merge lock-bucket + emitError regression + findLatestEvent ordering + atomic reaper steal (Stage B re-review fixes)

Close the 3 partial majors a targeted re-review found still open after 7607613,
including a regression that commit introduced.

- fork/merge: resolve activeIntent(pd,space,intent) BEFORE the wrapping
  withAuditLock so the lock bucket == the per-intent shard being written
  (state.ts handleFork/handleMerge) — fixes the lost-update window on the
  omitted-intent path
- REGRESSION fix: emitError now takes intent?/space? and keys
  holdsAuditLock(projectDir,intent,space); aidlc-state.ts threads the held
  lock's intent via lockIntent/lockSpace so mid-transaction errors no longer
  hit the 5s blocking-acquire branch or write ERROR_LOGGED to the wrong bucket
- findLatestEvent (aidlc-worktree.ts) delegates to findAllEvents → picks the
  max-Timestamp block (CRLF-normalized), not buffer/lexical position — the
  sibling reader the first fix-pass missed (worktree verify/info on multi-shard)
- reaper steal is now atomic: rename lockDir to a reaper-private nonce first
  (OS-atomic arbiter), re-verify the stamp inside the moved dir, restore on
  miss; residual sub-window documented, not silently labeled fixed
- t163-reaper-steal-race (spawn-contention mutual-exclusion property),
  t164-shard-ordering-and-lock-bucket (regression test for the ordering +
  lock-bucket fixes; fails 3/3 pre-fix)

* feat(workspace): retire --init; engine auto-births intent into active space; space+intent list/switch/create handlers

Stage C / P4 (keystone). Retire the user-facing /aidlc --init + --force; the
engine auto-births the first intent CONDUCTOR-SIDE (read-only engine NAMES the
move via birthPrintDirective, the deterministic aidlc-utility intent-birth
handler mutates). Add the intent/space verb families + the deterministic
listSpaces/listIntents query layer (+--json); intent status lifecycle (birth
in-flight; WORKFLOW_COMPLETED flips to complete under the workspace lock); doctor
readiness checks the shipped shell. Migration wired through the first-/aidlc path.

Two bugs fixed: birth bootstrap stub (cursor binds before full state body) and
appendAuditEvent self-deadlock (lock-aware, mirrors emitAudit). ~25 test files
repointed flat->per-intent via recordDirOf + shard-aware audit reads; harness
sdk-drive/kiro-acp readers repointed. New t165-intent-birth-p4 (concurrent-birth
integrity). Deterministic surface green (typecheck 0, --check clean); t72 a named
live-SDK wedge (flat-fixture assertion correct, no change).

* feat(memory): collapse learnings into practices; drop -learnings slots + 1.5/2.5 tiers

Stage C / P6. A confirmed learning IS a practice (vision §6): the learnings
writer (aidlc-learnings.ts learningsFilePath) and practices-promote
(aidlc-state.ts) now write the relocated aidlc/spaces/<space>/memory/{team,project}.md
via memoryDirFor — CLOSING the Stage-A reader/writer seam. Routed to the fitting
heading (## Corrections default) with ensure-exists (appendUnderHeading throws on
absent). Dropped the team-learnings/project-learnings RULE_FILE_REGEX slots +
the 1.5/2.5 SCOPE_PRIORITY tiers; the chain is clean org->team->project->phase.
Widened the Kiro fs_write allowlist to aidlc/spaces/**. §13 stage-protocol prose
updated in lockstep. The Stage-A t158 test.failing tripwire is CONVERTED to a
passing write->read round-trip (persist a learning -> loadRules reads it back).
Admission conflict-check + full audit trail retained.

Deterministic surface green: typecheck 0, --check clean (3 trees), --ci 193 pass
incl. t88 (clean 4-layer chain), t99 (§13 writes a practice), t158 (round-trip),
t86 (§13 prose). Sole red is t72 — the unrelated P4-era live-SDK wedge (P6 touched
no RE/orchestrate code).

* feat(workspace): multi-repo — sibling repos, per-repo codekb, intent repo-set, --repo through worktree/swarm/bolt

Stage C / P7. Multi-repo is first-class. An intent records the repos it touches
in intents.json (--repos flag or sibling-.git discovery; empty set = lone repo
inferred, no row). RE runs per-repo into aidlc/codekb/<repo>/. --repo threaded
through the construction path (worktree/swarm/bolt) via a shared
resolveConstructionRepo: git worktree add + assertNotSiblingWorktree(repoCwd?)
anchor to the target sibling repo dir, not the (non-git) workspace root —
decoupling 'repo to operate on' from 'the single projectDir'. Single-repo intents
infer the lone repo (no flag); --repo only disambiguates multi-repo.

Also closed 2 e2e tests P4 missed migrating (t66/t67-construction-worktrees — they
read the retired flat aidlc-docs path; applied t63's recordDirOf per-intent pattern).
New t166-multi-repo-construction; t165 extended. Deterministic surface green
(typecheck 0, --check clean, all 8 construction-worktree e2e pass); t72 = unrelated
live-SDK wedge, t66/t78-bolt = contention flakes (proven green in isolation).

* feat(hooks): resolve active intent in hooks + statusline; show active space · intent orientation; resume rebind

Stage C / P8 (final Stage C phase). Thread the active intent through the hooks +
statusline. Statusline gains a '<space> · <intent> · <phase> …' orientation prefix
(the space token renders only when listSpaces().length > 1 — single-team users
never see it; vision §11.2). Session-start gains a resume cursor-rebind OFFER:
stamp the working intent uuid into a per-conversation session→intent map at
SESSION_STARTED, and on SESSION_RESUMED if it diverges from the active-intent
cursor, offer to rebind (never a session rebuild). New session-intent-uuid helpers
in aidlc-lib (read/writeSessionIntentUuid, activeIntentUuid, findIntentByUuid); the
.aidlc-sessions/ map is gitignored (per-user runtime, like active-intent). Fixed
the aidlc-audit-logger includes('aidlc-docs/') gate that dropped ARTIFACT events on
re-rooted per-intent paths; swept residual hook literals (runtime-compile was
already re-pointed in Stage B). New t160 (statusline orientation), t161 (resume
rebind), t162 (audit-logger per-intent), t167 (session→intent helpers).

Deterministic --ci surface green (198 pass; sole red = t72 live-SDK wedge).

* test(workspace): migrate t54/t55 e2e to per-intent audit/state reader; renumber P8 tests off Stage-B collisions

Stage C cleanup (test-only, no version bump). Two fixes both invisible to the
--ci tier (which excludes --e2e):

1. t54/t55 (workflow-audit-completeness, init-then-resume) drove the retired flat
   aidlc-docs/{audit,state} reads after P4's auto-birth + audit-shard re-root —
   the same P4-era escape class as the t66/t67 P7 fixed. Migrated both to the
   layout-aware harness readers (readAuditText/readAuditEvents + stateFilePathFor/
   auditDirFor, per-intent with flat fallback). t54 passes; t55's assertion path is
   now correct (its residual red is live-SDK variance on bare /aidlc --init, same
   wedge class as t72/t126/t138 — verified stateFilePathFor resolves the per-intent
   path correctly after a real birth).

2. P8 (301dc27) committed t160/t161/t162 numbers that already existed from Stage B
   (workspace-record-resolution / per-intent-lock-reaper / per-intent-layout-cli).
   Renumbered P8's tests to the free block: t160→t168 (statusline-orientation),
   t161→t169 (session-resume-rebind), t162→t170 (audit-logger-per-intent); rewired
   covers/describe self-refs + EXPECTED_NONE_TO_CLI + regenerated the registry.
   (t163→t167 was already done.)

typecheck 0, --check clean, t55-drift/t134/gen-coverage + the 3 renamed tests green.

* fix(workspace): birth gate consults the intent registry — no duplicate auto-birth on a multi-intent clone with an unset cursor

The no-state birth gate (Branch 7b valid-scope positional / Branch 9a explicit
--scope flag) in aidlc-orchestrate.ts fired purely on `!stateContent`. But
stateContent is empty in TWO worlds: a truly empty workspace (zero intents →
birth is correct) AND a workspace that already holds intents whose per-user
active-intent cursor is unset. The cursor is gitignored, so a fresh clone of a
>1-intent workspace lands with records on disk but no cursor → activeIntent()
returns null → stateContent empty → the gate would mint a DUPLICATE intent over
the existing ones, violating the P4 hazard 'auto-birth fires only on ZERO
intents'.

Before birthing on either arm, consult the deterministic query layer
(listIntents over the active space): when intents EXIST but none is flagged
active, emit an `ask` directive that lists them and asks the human to pick one
via `/aidlc intent <slug>` instead of birthing. Zero intents still births
unchanged. The engine stays read-only — it NAMES the move, never mutates the
cursor.

Regression: tests/integration/t171-birth-gate-registry.test.ts.

* fix(workspace): migration preserves the pre-migration audit trail into the per-intent shard layout

migrateFlatLayout blind-copied the flat tree, landing the flat
`aidlc-docs/audit.md` FILE at `<record>/audit.md` (a top-level file). But the
audit readers (auditShards / readAllAuditShards) glob the `<record>/audit/` DIR
via auditShardDir, and the single-file flat-fallback fires only when the record
dir is absent — never, post-migration. So the pre-migration WORKFLOW_STARTED /
STAGE / PHASE history was on disk but INVISIBLE to runtime-graph compile,
summary/replay, and every hook — contradicting the in-code claim that the
migrated record 'carries its prior state + audit history' (vision §5.1).

During migration, relocate the flat audit.md into the shard layout
`<record>/audit/<host>-<clone>.md` (auditShardName) so it joins the shard set
the readers already merge-sort. Honours decision #1 (per-clone shards, NOT a
single committed audit.md + merge=union). Guards the no-audit case. The flat
source aidlc-docs/audit.md is untouched, so the caller's gitRmFlatTree is
unaffected; the staged copy (never the source) is what moves.

Regression: tests/integration/t172-migration-audit-trail.test.ts (seeds a
POPULATED flat audit.md — t165's migration block used a header-only file, which
is exactly why this orphaning escaped).

* fix(workspace): thread --repo through the invoke-swarm directive so multi-repo autonomous construction targets the right sibling repo

The tool layer already supported --repo (aidlc-swarm.ts prepare → resolveConstructionRepo),
but the conductor-facing seam never supplied it: the invoke-swarm directive carried only
{kind, units}, the engine emitted it with no repo, and the swarm-prepare prose in all three
harness SKILL.md named `prepare` without --repo. So on a MULTI-REPO intent the conductor ran
prepare without --repo, resolveConstructionRepo threw ('spans N repos'), and autonomous swarm
construction dead-ended.

- Directive contract (aidlc-directive.ts): add optional `repo?: string` to InvokeSwarmDirective
  (+ INVOKE_SWARM_FIELDS, + checkOptionalString in the validator, + a self-test example).
- Engine (aidlc-orchestrate.ts tryEmitSwarm): resolve the repo deterministically via
  intentRepos (read-only — never throws, returns [] for flat-legacy). 0 repos → emit units
  unchanged (no repo field, today's behaviour); 1 repo → emit the lone sibling as `repo`;
  >1 repos → emit without a repo field (the conductor's knowledge call — it supplies --repo
  from the intent's recorded set, per the three-concerns tenet). NOT resolveConstructionRepo
  here, which throws on >1.
- SKILL.md (claude/kiro/codex): the prepare command gains [--repo <name>] + the instruction to
  pass the directive's repo when present, else supply --repo from /aidlc intent --json.repos on
  a multi-repo intent.

Regression: extend t166 (swarm prepare --repo forks inside the target sibling repo; prepare
without --repo dead-ends with 'spans 2 repos') + t113 (invoke-swarm with optional repo → VALID).
t135 (flat-legacy fixtures) stays byte-identical — intentRepos returns [] → no repo field.

* fix(hooks): refresh the session→intent stamp on in-conversation intent switch — no false resume-rebind nag

The resume-rebind stamp (session-start hook) is keyed by session_id, which only
the hook sees. An in-conversation `/aidlc intent <slug>` switch moves the
active-intent cursor via a CLI tool with no session_id, so the live session's
stamp stayed pointing at the OLD intent — resuming THAT SAME conversation then
fired a FALSE rebind nag ('was working X, switch back?') even though this
conversation deliberately switched.

Plumb session-id from the hook (which owns it) to the switch tool via a fixed-name
`.current-session` marker inside the gitignored aidlc/.aidlc-sessions/ dir:
- New lib helpers writeCurrentSessionId / readCurrentSessionId (best-effort).
- The session-start hook writes the marker on EVERY fire (not gated on eventType),
  so it always names the live conversation.
- handleIntent reads the marker after moving the cursor and re-stamps that session
  to the switched-to intent's uuid.

Self-switch: the marker names THIS session → its stamp follows the cursor → no
false nag. Foreign drift (a different session moved the cursor): the marker names
that OTHER session → its stamp moves, not ours → a genuine resume still offers the
rebind. Stays a non-destructive OFFER, best-effort throughout.

Regression: tests/integration/t173-session-switch-restamp.test.ts (spawns the real
hook + the real switch; proves self-switch → no nag, foreign drift → nag fires).
t169 unchanged (it drives foreign drift via raw setActiveIntentCursor).

* fix(hooks): forward session_id/source through the Kiro + Codex SessionStart adapters so resume-rebind is not Claude-only

The core SessionStart hook gates its P8 session→intent stamp + resume-rebind OFFER
on a non-empty session_id, but the per-harness adapters that feed it never forwarded
session_id — so the feature was Claude-only.

- Codex (the clean win): the adapter already forwards a real `source`; now also
  forward session_id when present, so the full P8 path — stamp on SESSION_STARTED,
  OFFER on source=resume — works end-to-end on Codex.
- Kiro: forward session_id when present so the per-session STAMP is written. But
  Kiro's agentSpawn carries no resume discrimination (the adapter reports every
  session-start as source=startup), so SESSION_RESUMED can never fire and the
  resume-rebind OFFER is structurally unreachable on Kiro — documented in the
  adapter comment as a harness limitation, not a bug. No fake resume source.

Regression: extend t149 (codex forwards session_id → stamp written; source=resume
after a cursor drift → OFFER fires) + t147 (kiro forwards session_id → stamp written;
resume-shaped payload → no OFFER, the documented limitation pinned deterministically).

Also refreshes tests/.coverage-registry.json + .coverage-ratchet.json and the
gen-coverage-registry EXPECTED_NONE_TO_CLI allowlist to register the new spawning
integration tests t171/t172/t173 (added by the B1/B2/M2 fixes) — the t134 mechanism-
honesty gate requires the committed registry to carry every test's covers: claims.

* fix(workspace): review minors — space-switch slugify, init-runner description, onboarding --init removal, stale comments

Batch of five review minors:
- aidlc-lib.ts: correct resolveConstructionRepo's stale header — a zero-recorded-repos
  intent with an explicit --repo HONOURS it as a sibling anchor (cwd = the named
  sibling dir), it does NOT error; the comment now matches the code. (comment-only)
- aidlc-utility.ts handleSpace: slugify the switch target before lookup + cursor write,
  mirroring space-create (which stores slugify(raw)). Without this, `/aidlc space
  "My Space"` (stored as my-space) failed 'Unknown space'. (behavior fix)
- aidlc-runner-gen.ts renderInitRunner: the /aidlc-init runner forwarded $ARGUMENTS
  verbatim to intent-birth, but intent-birth reads the freeform description from the
  --arguments FLAG (not a positional), so a description was silently dropped. The
  runner now instructs the conductor to forward recognized flags as-is and pass
  freeform text via --arguments "<text>". (behavior fix)
- onboarding.fills.ts (claude + kiro): drop the retired `/aidlc --init`/`--force`
  scaffold sentence from the title_block heads; replace with the no-init birth-flow
  description (matching codex's already-updated wording). (prose fix)
- t143: fix the drifted line cite for the 'State initialized:' stdout literal
  (:2376 → :2395). (comment-only)

The docs/ getting-started --init sweep is deliberately NOT touched — that is the
disputed finding deferred to P9.

Regression: t165 (space-switch slugify → my-space resolves) + t129 (the shipped
aidlc-init runner names --arguments, no bare intent-birth $ARGUMENTS).

* feat(workspace): retire the legacy-flat fallback bridge — single per-intent layout

Delete legacyFlatFallback() + LEGACY_FLAT_ROOT/LEGACY_FLAT_RELATIVE_PREFIX
from aidlc-lib.ts; null-resolution now resolves to the bare per-intent space
record root (aidlc/spaces/<space>/intents/). Rewrite the orchestrate.ts
resolvers + retire the transitional aidlc-docs gate in the audit-logger hook.
The migration source is kept as a private flat literal only.

Also fix the two shipped sensor manifests' matches-glob from **/aidlc-docs/**
to **/{aidlc-docs,intents}/** — the per-intent layout move made the old glob
DEAD, so required-sections/upstream-coverage never fired on re-rooted paths.
Recompiled into stage-graph.json.

P9/Stage D — discharges the Stage-B carry-forward (bridge must not reach GA).

* test(workspace): migrate the fixture corpus to the per-intent workspace layout

Redirect the fixtures.ts chokepoint (createTestProject / seedStateFile /
seedAuditFile / setupIntegrationProject) + the secondary chokepoints
(tui-fixtures.ts setupTuiProject, custom-harness.ts) to emit the per-intent
layout aidlc/spaces/<space>/intents/<slug>-<id8>/ with a pinned clone-id for
deterministic audit shards. Touch the assertion-hardcoders across the corpus
that embed flat aidlc-docs/ paths. Delete the t159 legacy-flat fallback
tripwire (the bridge it guarded is gone — its test.failing flips RED on
retirement, which is the signal to remove it).

Also repoints the TUI statusline tests for the active-intent orientation
prefix (<slug> · …) the prior phases added.

P9/Stage D.

* test(workspace): space-isolation suite + docs allowlist gate

t175: space-create copies org.md but writes FRESH team/project stubs so the
default space's promoted practices do not leak; a new intent reads the space's
live memory. t174 + tests/fixtures/docs-legacy-refs.json: a closed-predicate
docs allowlist (occurrences == pinned set, ceiling 8, 5 pinned) over the
surviving aidlc-docs/--init references in docs/. Reconcile the coverage
registry + ratchet for the new surfaces.

The rest of the plan's P9 suite list is already pinned by Stage B/C
(t160/t162/t163/t164/t165/t156/t168/t171/t145/t75/t166/t172).

P9/Stage D.

* docs: workspace architecture sweep + version 0.8.0 + CHANGELOG

Sweep ~60 docs/ files to the per-intent workspace model (retire --init prose;
rewrite the aidlc-docs/knowledge paths stale on the knowledge relocation).
Bump the version trio to 0.8.0: core/tools/aidlc-version.ts + README badge +
a ## [0.8.0] CHANGELOG entry carrying the upgrade note (workspace layout,
--init retired, per-clone audit shards, learnings-as-practices). t68 pins the
trio agrees; dist version copies regenerated.

P9/Stage D.

* fix(workspace): domain knowledge is space-level, not per-intent

Align the knowledge layout to the plan/vision (workspace-vision §Spaces, plan
L296): SPACE domain knowledge lives at aidlc/spaces/<space>/knowledge/ — a
free-form, empty-at-bootstrap sibling of memory/codekb/intents that accumulates
across every intent in the space — NOT inside a per-intent record. The engine's
per-agent METHODOLOGY knowledge at <harness>/knowledge/ is distinct and stays
put.

The Stage A/B/C implementation had drifted: birth's ensureWorkspaceDirs created
knowledge per-intent (<record>/knowledge/), and migration's blanket tree-move
dragged the flat aidlc-docs/knowledge/ overlay into the record where the
(repointed) agents would never find it.

- aidlc-lib.ts: new knowledgeDir(projectDir, space?) resolver; migrateFlatLayout
  step 3b relocates the migrated flat knowledge/ up to space-level (merge
  entry-by-entry; source untouched).
- aidlc-utility.ts ensureWorkspaceDirs: knowledge dir → space-level (per-phase +
  verification dirs stay per-intent, correctly).
- 11 agent personas + stage-protocol.md: team-knowledge load paths
  aidlc-docs/knowledge/ → aidlc/knowledge/ (space shorthand); methodology refs
  untouched.
- workspace-scaffold.md + onboarding.md: describe the space-level free-form model
  (empty at bootstrap, no seeded per-agent READMEs, no scaffold command).
- tests: t21/t27 assert space-level knowledge; t07 record-artifact example uses a
  real per-intent stage path; new t160 case asserts migration relocates flat
  knowledge to space-level; custom-harness persona template repointed; coverage
  registry picks up knowledgeDir.

Resolves the docs-accuracy + scaffolding majors the P9 review surfaced.

* docs: team knowledge is space-level free-form, not per-intent scaffolded

Correct the knowledge docs to match the shipped model: team/domain knowledge
lives at the space level (aidlc/spaces/<space>/knowledge/, shorthand
aidlc/knowledge/) — a sibling of memory/codekb/intents that accumulates across
intents — and is free-form and empty at bootstrap. Removes the prior sweep's
fiction that intent birth scaffolds per-agent knowledge subdirectories seeded
with guidance READMEs: the engine ensure-exists an empty knowledge/ dir; the
team creates aidlc-shared/ and per-agent subdirs itself (a convention the agent
personas look for) if it has content for them. Tier-1 engine methodology
knowledge (<harness>/knowledge/) descriptions are unchanged.

28 docs files across guide/reference/harness-engineering, incl. the canonical
07-knowledge.md and the 11 reference/agents/ pages.

* test(workspace): fixture intent uuid ⇄ record-dir consistent by construction

The seeded default intent's record dir name and its intents.json row uuid had
drifted: the dir used suffix 0000000000000001 but idSuffix(uuid) of the seeded
uuid (…8000-000000000001) is 8000000000000001, so listIntents()/
updateIntentStatus() never joined the row to its dir — a layout the runtime can
never produce, leaving fixtures modelling a fiction (P9 review major).

Derive DEFAULT_RECORD_DIR's id8 from a single DEFAULT_INTENT_UUID constant via
the same trailing-16-hex rule the runtime uses, and reference that uuid in both
fixtures.ts and tui-fixtures.ts registries — so the row⇄dir join holds by
construction. Genericize two stale comments (t137/t99) that named the old literal.

* test(workspace): guard the sensor-glob intents arm; refresh designer-export golden

t95 only drove the legacy aidlc-docs/ arm of the framework sensor glob
**/{aidlc-docs,intents}/**, so a regression of the new intents arm (the per-intent
record path every real workflow writes) would leave the suite green while the
sensors went silent — the exact dead-glob bug P9 fixed, unguarded (review major).
Add C1a-intents: a markdown write under the per-intent record dir must fire the 2
markdown sensors via the intents arm.

Also regenerate the designer-export golden for the workspace-scaffold stage's
updated outputs: frontmatter (knowledge is space-level now — the earlier
knowledge-realignment commit changed the stage prose; t66 compares export
byte-for-byte).

* fix(workspace): relocate migration audit+knowledge BEFORE the atomic rename (crash-safety)

migrateFlatLayout relocated the flat audit.md → shard dir and the flat knowledge/
→ space-level AFTER the atomic rename into the leaf but BEFORE the .migrated
marker. Since the rename flips anyIntentRecordExists() true (→ needsFlatMigration
returns false), a crash in that window stranded the audit shard and the team's
knowledge permanently — the migration could never re-fire. Review#2 major.

Shape the STAGING tree to the target layout (audit → audit/<shard>, knowledge →
space-level) BEFORE the rename, so the rename is the SOLE commit point: the only
partial window now produces no aidlc-state.md under intents/ and no marker, so a
crash re-fires cleanly (step 1 rmSyncs any half-staging; the flat source is never
mutated; the space-knowledge move is intent-independent + idempotent).

t160: add merge-into-existing-space-knowledge (no data loss either side) + a
post-crash re-fire test; register function:knowledgeDir coverage (was UNCOVERED).

* docs+test: align onboarding/glossary/comments to space-level knowledge; gate the onboarding surface

Review#2 found the shipped onboarding doc + glossary still described the retired
flat aidlc-docs/ layout and /aidlc --init — and the t174 legacy-ref gate scanned
docs/ only, so it never saw the onboarding template (the source that renders to
the shipped CLAUDE.md/AGENTS.md, the first surface a user reads). That blind spot
is why the stale onboarding shipped green.

- core/templates/onboarding.md: rewrite Conventions/Resumption/Git + the init line
  to the per-intent workspace model (record dirs, space-level knowledge, the real
  gitignore set from the shipped .gitignore); --init retired, no separate init cmd.
- harness/kiro/onboarding.fills.ts: aidlc-docs/ → aidlc/ workspace tree.
- docs/guide/glossary.md: team knowledge is space-level (regression of review#1 #2).
- t174: extend the scan surface to core/templates/onboarding.md so the onboarding
  surface is gated (closes the blind spot).
- Stale-comment minors: t95 (sensor glob is {aidlc-docs,intents}, not unchanged),
  t21/t27/aidlc-utility (knowledge is space-level, not per-intent).
- CHANGELOG 0.8.0: add the space-level knowledge relocation to the upgrade note.

* fix(workspace): guard aidlc-log audit emit against a null-resolved intent (no stray shard in the bare space root)

aidlc-log threads no --intent/--space, so it relies on default intent
resolution. On a fresh shell (pre-birth) or a >1-intent workspace with no
active-intent cursor, activeIntent() returns null and stateFilePath()/
auditFilePath() collapse to the BARE space record root
(aidlc/spaces/<space>/intents/). An unguarded emit there drops a state/audit
shard DIRECTLY into the bare intents root, breaking the invariant that no
aidlc-state.md / audit/ ever lives in the bare intents root (aidlc-lib.ts).

aidlc-log was the lone audit emitter missing the "no active workflow → clean
error / no-op" guard every other emitter has: the hooks no-op via
`if (!existsSync(stateFilePath(...)))`, emitError() gates ERROR_LOGGED on the
same check, and handleEnableTestRun() dies on it. Mirror that idiom — resolve
the project dir, assert the resolved aidlc-state.md exists, else emit a clean
error to stderr and exit 1 (orchestrator-called per-question → a missing
workflow is a misuse, not a routine no-op).

Sweep result: all 21 writeStateFile callers SAFE (state.ts/jump.ts/bolt.ts read
an existing state first or thread a resolved intent; utility.ts is behind a
stateFilePath existence check, an explicit --intent/--space, or the birth path);
of the audit emitters, every hook is guarded (existsSync stateFilePath/auditFile
or an underRecord check) and every tool emit is gated behind a state read,
explicit intent, or the birth/init cursor — aidlc-log was the only at-risk one.

t31 updated: the suite previously documented emitting into the bare space root
as expected (the buggy behavior); its emit-success cases now seed a real
active intent (state-mid-ideation.md) so a valid record resolves, and four new
cases (g1-g4) prove decision/answer with no resolvable intent error cleanly and
leave NO aidlc-state.md / audit/ in the bare intents root.

dist regenerated; bun scripts/package.ts --check clean.

* feat(workspace): orchestrator offers a 2nd intent on unrelated new-work (P4 completion)

Completes the one unbuilt piece of P4 (the workspace refactor's auto-birth/intent
phase): the vision promised that once an intent is active, describing a genuinely
new, unrelated piece of work prompts the orchestrator to OFFER a second intent
alongside (you confirm). The engine is correctly read-only — with an intent active
it advances it (Branch 10) and every birth arm is !stateContent-gated — so the
offer is conductor PROSE (knowledge->LLM, judgement->human), NOT a new engine branch.

Adds an additive subsection after Routing to all three per-harness orchestrator
skills (harness/{claude,kiro,codex}/skills/aidlc/SKILL.md): recognise new-work vs
continuation (default to continuation; compare against the active intent via
'intent --json'), OFFER via the harness question surface (never auto-birth), and
on confirm run the existing 'intent-birth' tool then re-run 'next' — the same
run-then-continue shape the print directive already uses. The offer surfaces the
proposed --scope so the human can correct it, and pins the affirmative option to
lead with 'Yes' so the answerScript matches deterministically. Kept cleanly
separable so the Stage F reviewer-mechanism rebase reconciles without a tangle.

Tests: t176-new-work-offer-second-intent (SDK) drives the live offer beat
(offer fires -> confirm -> intent-birth -> 2 registry rows); gated on claude-CLI
presence, skips-with-reason when absent. Non-vacuous via the (b)+(c) conjunction:
with an intent active, a 2nd intent can ONLY arise from offer->confirm->birth, so
'State initialized:' (birth-only literal) + 2 rows prove the offer; question TEXT
is not asserted (non-deterministic). The deterministic birth/isolation half stays
covered by t165. No version bump — folds one bullet into the unshipped 0.8.0 entry
(Stage F owns the 2.0.0 trio).

bun scripts/package.ts --check clean; bun run typecheck 0.

* test(workspace): multi-repo/intent/space live journey — shared fixture + logic-half (SDK·ACP·exec) all 3 harnesses

* test(workspace): render-half journey (Claude TUI) — statusline orientation + gate

* test(workspace): ACP journey drives the full 5-beat composed flow + fix coverage-registry gate

Review (ultracode, 15 agents) found Stage E NOT-FINISHED. This closes the
blockers it raised:

- B2: the Kiro ACP leg drove only beats 1-2. A live spike proved multi-turn
  ACP IS drivable (the driver's session-reuse + keepAlive + stopAfterToolTitle
  pace dependably when each turn stops at a TOOL boundary). Extend the leg to
  the full 5 beats: beats 1-3 through the production `aidlc` conductor (beat 3
  via the authorized offer->confirm flow, SKILL.md:104), beats 4-5 through the
  `aidlc-developer-agent` (space verbs have no authorized conductor routing, so
  the production agent forwards them to `next` — the developer agent, lacking
  the forwarding override, is the drivable surface; documented, not faked).
  Beat-2 codekb stop made disk-condition-based (tolerant of root- or
  space-scoped codekb) to fix a pre-existing per-repo-ordering flake.
- B1: regenerate tests/.coverage-registry.json + add the codex journey spawner
  to EXPECTED_NONE_TO_CLI (the 6 new Stage-E files changed the coverage
  universe; gen-coverage-registry.test.ts is a deterministic unit-tier gate).
- Soften the kiro-acp-drive.ts multi-turn NOTE (comment only) to match the
  spike finding.

Live-verified: 212 ACP tool_call events, intent-birth/space-create/space
verbs all fired (trace 2026-06-19T12-27-54Z). Test-only, no version bump.

* feat(workspace): route space/intent navigation verbs through the conductor

Completes the P4 workspace-verb deliverable. The handlers (handleSpace,
handleIntent, handleSpaceCreate) shipped in P4 but were only reachable by
calling aidlc-utility.ts directly — the conductor forwards `$ARGUMENTS` to
`aidlc-orchestrate.ts next`, which had no branch for the switch/list verbs, so
`/aidlc space teamB` was swallowed as freeform text and the engine advanced the
active intent instead of switching (Branch 8 ask on a fresh workspace, Branch 10
advance with an active intent). The vision (§3 "/aidlc space <name> is the
explicit cd") and the P4 card ("/aidlc space <name> moves between teams")
both promise these as working user commands.

Fix mirrors the READ_ONLY_FLAGS routing pattern:
- WORKSPACE_VERBS set beside READ_ONLY_FLAGS.
- parseNextFlags recognizes a leading (i===0) space/space-create/intent token as
  a workspaceVerb + optional <name> arg (guarded so freeform prose merely
  containing "space"/"intent" mid-sentence is untouched).
- A new branch in handleNext at Branch-1 altitude (before resolveProjectDir /
  loadState, so a switch works with OR without an active workflow) emits a
  terminal print directive naming `aidlc-utility.ts <verb> [<arg>]`. All verbs
  are terminal prints (switch/create/list echo + stop; nothing for next to
  advance into); list-vs-switch stays in the unchanged deterministic handler.

No SKILL.md change: the print row already covers "a workspace command". t114
gains 5 cases (switch/list/create routing + an i===0 freeform regression guard).
Version trio → 0.8.1.

* test(workspace): correct codex journey header — render-half is Claude-TUI-only (Kiro has no statusline either)

MAJOR-2 reconcile: the codex journey header said the render-half matrix is
'Claude + Kiro TUI', but Kiro ships no statusline surface (no dist/kiro
settings.json statusLine row; AGENTS.md + SKILL.md state 'there is no
statusline'), so there is no Kiro orientation leg. Corrected to Claude-TUI-only;
Kiro + Codex are logic-half only for the render-half, by surface limitation.
Matches the t-tui-journey-orientation + t-acp-kiro journey headers. Comment-only.

* test(workspace): drive journey beats 4-5 through the real conductor; drop the workarounds

Now that d44828b routes the space/intent navigation verbs through the conductor
(next emits a terminal print directive naming aidlc-utility.ts <verb>), the
journey's space beats can drive the verbs the real user way — `/aidlc space
teamB` — instead of routing around the conductor. Removes:

- ACP: the `driveDevVerb` helper (drove space verbs through aidlc-developer-agent,
  a delegation target, because the production conductor couldn't route them).
  Beats 4-5 now use single-turn driveKiroAcp on the `aidlc` conductor; the trace
  shows `next <verb>` → `aidlc-utility.ts <verb>` for space-create/space/default.
- SDK + Codex: the `spaceSwitchPrompt` "Run this exact command … do NOT run next"
  steer. Beats 4-5 now drive the plain `/aidlc space <name>` command; the
  conductor forwards to next, gets the print directive, runs the tool.

All on-disk assertions unchanged. Each leg spike-confirmed live then re-verified
green on the full 5-beat journey (ACP 2/2 clean when the forward lands; SDK +
Codex green on re-run). Headers/comments rewritten to the conductor-routed path;
the obsolete "the conductor cannot route these" / "space default left the cursor
on teamb" rationale removed. Test-only, no version bump.

* test(workspace): repoint 4 stale TUI assertions after rule + knowledge relocation

Four e2e TUI tests carried path/string assertions from the v2-unified import
(7b824b3) that the workspace-anchor relocations made stale — verified test debt,
not regressions: none of the Stage-E commits (495e48f..fc8f093) touched these
files, and both relocations are ancestors of the Stage-D base. Pure repoints,
mirroring the sibling fix c3a193b ("repair stale rule-path goldens").

- t29: the no-state recovery string is "…or by naming a scope (/aidlc --scope
  <scope>)" (aidlc-orchestrate.ts:1245-1247), not "Name a scope to start a
  workflow" — assert the stable "naming a scope" substring.
- t50: knowledge moved from the per-intent record to the SPACE level
  (aidlc/spaces/<space>/knowledge, b29ced6), ensured at birth for every scope
  by ensureWorkspaceDirs. Repoint via a new spaceKnowledgeDirFor() helper in
  sdk-drive.ts (mirrors recordDirFor's space-cursor resolution).
- workshop: rules relocated to the harness-neutral aidlc/spaces/default/memory/
  with neutral basenames (fe7f470) — read team.md there, not
  .claude/rules/aidlc-team.md.
- custom-harness: same relocation — the compiled stage node bakes
  aidlc/spaces/default/memory/project.md (verified against the actual emitted
  rules_in_context array); repoint both the assertion and the rule-file read.

The fifth flagged test (t-tui-kiro-status) is left unchanged: its failure is NOT
a stale path but the known conductor-forwarding reliability item — the live Kiro
conductor rolls `/aidlc --status` into stage execution instead of running
next --status, so the status block never renders. Out of scope here.

Test-only; no version bump (AGENTS.md changelog policy).

* fix: make Kiro read-only/nav commands deterministic mid-workflow — turn-scoped no-op-next guard + preToolUse backstop (v0.8.2)

On the Kiro CLI harness, over an active workflow, `/aidlc --status` / `--doctor`
/ `--help` / `--version` and the navigation verbs `space` / `space-create
<name>` / `intent <name>` could "roll forward": the live conductor relayed the
seam's injected output but then ran a flag-less bare `next`, the engine returned
a run-stage for the active intent, and the conductor executed it — advancing the
workflow when the user only asked to look or switch.

The userPromptSubmit seam (shipped earlier in 0.8.2) dispatches these terminal
commands off-band, but Kiro's userPromptSubmit can only inject context, not block
the turn — so the seam alone did not stop the trailing bare `next`. This change
neutralizes that bare `next` deterministically, keyed off a turn-scoped latch:

- Seam turn-clock (harness/kiro/hooks/aidlc-kiro-adapter.ts): the verb-intercept
  bumps a per-turn counter on every fire and stamps a latch on a read-only flag
  or a workspace verb.
- Engine no-op-next guard (core/tools/aidlc-orchestrate.ts, harness-neutral):
  a truly bare advancing `next` whose latch turn equals the current counter (the
  same turn) emits a terminal `done` — the forwarding loop's only formal exit —
  instead of a run-stage. Inert on Claude/Codex (no seam writes the latch there).
- preToolUse backstop (adapter `pretool-block` target, Kiro-only): if the
  conductor retries the bare `next` past the engine's `done`, the hook hard-blocks
  it (exit 2). All conductor hooks still route through the single adapter.

The `done` reason names the command and states the workflow is unchanged, so the
conductor relays the output and stops without falsely announcing completion
(live-verified on the ACP and TUI surfaces).

Live-gate verified: t-acp-kiro-status, t-acp-kiro-utilities,
t-acp-kiro-journey-workspace (3/3) and t-tui-kiro-status (2/2).

Tests:
- t148: expected conductor hook-key set updated (preToolUse + userPromptSubmit).
- t-acp-kiro-status / t-acp-kiro-utilities: read-only/nav now run off-band, so the
  tool_call assertions are repointed to stopReason==end_turn + on-disk no-op;
  config-change (a mutation) is unchanged. Byte-verbatim output stays covered by
  the SDK/CLI twins (t20/t22/t23/t27/t31).
- t-tui-kiro-status: assert the status display name ("Requirements Analysis"),
  matching what the status tool renders (not the stage slug).
- t178 (new): deterministic unit test for the shared classifyTerminalCommand
  classifier + READ_ONLY_FLAGS / WORKSPACE_VERBS sets.

dist/** regenerated; CHANGELOG 0.8.2 entry amended (no version re-bump).

Review hardening (ultracode review of this fix, ALIGNED-WITH-FIXES):
- Engine done-guard exemption reaches true parity with the preToolUse backstop —
  a bare `next --single` / `next --test-run` is never swallowed to `done`.
- t179: deterministic test for the engine no-op-next guard (fresh latch -> done,
  stale -> run-stage, --status -> print, --single -> run-stage, no-latch -> inert);
  proven to fail if the guard is reverted.
- t180: deterministic test for the Kiro seam — verb-intercept turn-counter +
  latch stamp (read-only / nav / freeform), and the preToolUse exit-2 backstop.

* chore(workspace): rebase onto v2; reconcile reviewer + kiro-ide harness; version trio → 2.1.0

Terminal P11 of the workspace-anchor effort: rebases the P0…P10 stack onto the
moved-main v2 base (origin/v2 @ f3ce1b8) and reconciles the two features that
landed on v2 during the build — the reviewer mechanism and the Kiro IDE harness
— with the workspace refactor. Reconcile-and-prove: the P0…P10 stack replayed
plus this one consolidating commit on top.

Conflicts resolved keep-both:
- t01-file-structure.test.ts — v2's 13-agent + 65-path structural assertions
  AND our mem() helper + the two relocated-memory path repoints.
- glossary.md — v2's dual-Kiro (CLI + IDE) row AND our space-level Knowledge row.
- aidlc-orchestrate.ts auto-merged clean: our roll-forward Branch 0 guard
  (handleNext) and v2's reviewer block (buildRunStageDirective) are in distinct
  functions, so both survive. t178/t179/t180 pass.

Reconcile the workspace memory relocation with v2's new agent surfaces (this
refactor moved core/rules/ → core/memory/, emptying the .kiro/steering/ dir the
v2 agents loaded their method from):
- Repoint all 5 Kiro IDE agents and the 2 Kiro CLI reviewer personas
  (product-lead, architecture-reviewer) from file://.kiro/steering/*.md to
  file://aidlc/spaces/default/memory/**/*.md, so the AIDLC method actually loads
  on those agents (matching the conductor + non-reviewer agents).
- Ship dist/kiro-ide/.gitignore (manifest dot-gitignore row + authored file) so
  the committed-vs-ignored split holds on Kiro IDE — cursors + machine-local
  runtime ignored, the shared work committed (the per-clone audit-shard scheme
  depends on the clone-id token staying gitignored).
- Rewrite the Kiro IDE onboarding (AGENTS.md) off the retired /aidlc --init +
  flat aidlc-docs/ model to the workspace shell + auto-birth wording.
- Harden t156 tests 5 + 7 to DERIVE the harness + agent lists from disk (was a
  hardcoded claude/kiro/codex + 3-agent list that let the kiro-ide tree and the
  two reviewer personas slip through). Negative-control verified: reverting any
  Kiro-family agent to the steering glob now fails t156.

Reconcile the Kiro IDE CONDUCTOR surface (two more surfaces the stack skipped —
on origin/v2 the kiro and kiro-ide SKILL.md were byte-identical, and the stack
diverged kiro CLI while leaving kiro-ide a stale fork that shipped green because
no test reads a per-harness conductor SKILL or write-grant):
- harness/kiro-ide/agents/aidlc.json — add aidlc/spaces/** to
  toolsSettings.fs_write.allowedPaths (matching kiro CLI). The workspace layout
  writes every artifact/diary/state/audit shard under aidlc/spaces/<space>/
  intents/…; without the grant a kiro-ide conductor is prompted on every
  framework write (Kiro gates writes outside the allowlist). The stale
  aidlc-docs/** + .kiro/steering/** entries are left in place to keep kiro ==
  kiro-ide byte-for-byte.
- harness/kiro-ide/skills/aidlc/SKILL.md — re-sync to the kiro CLI SKILL for the
  5 workspace deltas it was missing: drop --init from the utilities list + add
  the "intent and space verbs" tail; the print-directive routing word init →
  intent-birth (the engine emits intent-birth --scope, aidlc-orchestrate.ts:323);
  the swarm --repo flag + multi-repo paragraph; and the entire "offer a second
  intent" P4-completion section. The diff proved zero IDE-specific divergence
  existed (subagent-only swarm, no-statusline note, numbered-prose questions,
  "(Kiro CLI harness)" title are byte-identical in both), so the re-sync brought
  across exactly the 5 deltas and lost no IDE specific.
- tests/unit/t181-conductor-skill-parity.test.ts — new deterministic gate so the
  fork cannot recur (the unmet "extend the docs-sweep to the 4th tree"
  obligation). Derives the harness-SKILL list FROM DISK (mirrors t156 §7,
  readdirSync+filter+floor — no hardcoded triple) and gates every shipped
  conductor SKILL in both directions: the retired bare --init token must be
  ABSENT (same predicate as t174) and the workspace vocabulary (intent-birth,
  --repo, "offer a second intent", "intent and space verbs") must be PRESENT.
  Negative-control proven both ways.

Version trio → 2.1.0 (a minor bump over v2's 2.0.2, reflecting the partly-breaking
workspace layout change): aidlc-version.ts, README badge, and one consolidated
## [2.1.0] CHANGELOG heading folding the former 0.8.0/0.8.1/0.8.2 notes above
v2's 2.0.x blocks. t68 green.

Also fixes stale flat-layout prose in docs/guide/harnesses/kiro-ide.md (a v2 file
that predated the P9 workspace refactor): /aidlc --init → workspace auto-birth,
aidlc-docs/ → the per-intent record dir. Restores t174 (docs legacy-refs gate).

All four dist trees (claude, kiro, codex, kiro-ide) regenerated from merged
source; bun scripts/package.ts --check clean.

---

Docs-freshness sweep (folded in): bring the prose docs up to date with the
workspace refactor + reviewer + Kiro IDE — discharging the Documentation Policy
(grep docs/ for stale refs and fix in the same commit as the rename). Pure-doc
+ test-only per the Changelog Policy carve-out, so no version re-bump (trio
stays 2.1.0). 57 files: 44 authored docs/prose, 4 harness SKILL.md, 3 test
files + 1 fixture, 8 regenerated dist renders.

- Rules relocation: .claude/rules/ (+ .kiro/steering/, .codex/aidlc-rules/,
  core/rules/) → aidlc/spaces/<space>/memory/{org,team,project}.md +
  phases/<phase>.md across guide/reference/harness-engineering, README, AGENTS,
  onboarding template, and all 4 orchestrator SKILL.md. Neutral filenames
  (org/team/project.md), read via each harness's native include.
- Learnings → practices: dropped the retired *-learnings.md surface and the
  1.5/2.5 SCOPE_PRIORITY tiers; confirmed learnings now land as practices in
  memory/{team,project}.md (clean integer chain org:0/team:1/project:2/phase:3).
- Audit shards: flat audit.md → per-clone audit/<host>-<clone>.md, and fixed a
  contradiction (15-troubleshooting said audit.md was gitignored; it is
  committed per 13-artifacts + 09-state + dist .gitignore).
- Agent count: per-occurrence 11→13 only where a file total or all-agents
  enumeration omitted the 2 reviewers; kept "11 domain experts" where correct.
- Documented missing features as sections in existing chapters (no new chapter):
  glossary terms (Space, Intent, Record dir, Reviewer, Multi-repo intent), the
  reviewer mechanism (05-agents + 15-stage-definition schema + 04-stage-protocol
  step), multi-repo (11-cli-commands + 13-artifacts), and the Kiro IDE 4th-harness
  enumerations.
- Gate extensions so the debt can't recur: t174 now derives its scanned file
  list from disk (docs/ + core/templates/*.md + harness/*/skills/aidlc/SKILL.md)
  and adds rules-dir + *-learnings.md token lanes (negative-control proven); t55
  PATH_PATTERNS gains the learnings-log lane (dotted-rules lane deferred to t174
  to avoid duplicating 77 legitimate native-include survivors). t06 positive
  layout anchor repointed to the relocated space-memory path. core/aidlc-common/
  reroot (410 aidlc-docs refs) deliberately deferred to its own migration.

* docs(aidlc-common): reroot aidlc-docs → per-intent record dir

Reroot the retired flat `aidlc-docs/<phase>/<stage>/...` workspace layout to
the per-intent record dir `<record>/...` (= `aidlc/spaces/<space>/intents/
<slug>-<id8>/`) across the 36 engine-parsed stage + protocol .md files under
core/aidlc-common/. These carried the OLD flat paths from before the P0–P11
workspace refactor; the engine already resolves the new per-intent forms.

- 410 → 1 `aidlc-docs` occurrences (the 1 survivor is the stage-definition.md
  rule prose where `aidlc-docs/…` is an explicit anti-pattern example).
- 18 flat `audit.md` path refs → per-clone shard `<record>/audit/<host>-<clone>.md`;
  shard-model semantics preserved (append-never-overwrite, header-on-create,
  .bak-on-corruption reworded for "this clone's shard").
- 29 `outputs:` frontmatter lines → relative artifact names (engine-resolved form).
- 40 `{unit-name}` per-unit construction segments preserved (moved out of
  `outputs:` into body cross-stage read paths).
- workspace-detection.md scanner/brownfield-marker refs → workspace root `aidlc/`.

Regenerated all 4 dist trees (claude/codex/kiro/kiro-ide); package.ts --check OK.
No version bump: a path-only reroot of engine prose with no change to any
user-invoked command/flag/error (outputs: is non-load-bearing).

Test fallout (the migration's own verifying tests), all repointed to the new
canonical forms preserving intent:
- t35/t43/t44 — path-pin assertions → `<record>/` forms.
- t66 designer-export golden regenerated (pure outputs:/path reroot diff).

Verified: typecheck 0; construction e2e 10/10 PASS (8 scopes + bolt-fork +
swarm-referee); full default tier 210 files / 3466 assertions / 0 fail.

* feat(workspace): deterministic per-repo codekb placement via codekb-path tool + isCodekb resolver

Reverse-engineering wrote its 9 codekb artifacts to an LLM-discretion location
(prose named the path; two live runs drifted to two wrong places). Make placement
deterministic by giving the engine the canonical path and having RE defer to it.

- New read-only `/aidlc codekb-path [--repo <name>] [--json]` verb prints the
  space-level per-repo codekb dir `aidlc/spaces/<space>/codekb/<repo>/` (no mkdir,
  no state, no audit — a read-only query verb like `intent`/`space`).
- aidlc-lib: `codekbDir` / `relativeCodekbDir` / `codekbRepoName` (mirror
  `knowledgeDir` / `relativeRecordDir`; codekbRepoName is 2-way: lone recorded
  repo, else basename(projectDir)).
- aidlc-orchestrate: `isCodekb` keyed on KNOWN_CODEKB_STAGES (reverse-engineering)
  — NO frontmatter marker (stage-schema rejects it). New resolver arm in
  resolveArtifactPath keeps the aidlc/spaces/<space>/ stem and drops the
  intents/<slug> tail, so the directive JSON + audit + consumes[] agree with the
  tool. A small {projectDir,space,codekbRepo} ctx is threaded down the same spine
  recordPrefix travels (every emit site).
- RE stage prose defers to the tool (run codekb-path, write where it prints,
  never hand-compose); 3 consumer stages repointed to the space-level read.
  memory.md stays in the record dir (the intentional split).
- Version trio 2.1.0 -> 2.1.1 (version.ts + README badge + CHANGELOG); docs
  carve-out in 16-artifact-vocabulary.md + the utility-handler note in
  11-contributing.md.
- Tests: new deterministic guard t182 (helpers + verb + resolver branch) + new
  live SDK re-verify t183 (drives RE, asserts 9 artifacts land at the space-level
  codekb, none in the record dir); repointed t116/t154/t43/t-journey/t72/t50;
  coverage registry + ratchet regenerated (new units credited), gen-coverage
  reclassification pin updated.
- dist regenerated for all 4 harness trees.

* fix(codekb): repoint re-artifacts.md shorthand to space-scoped path + extend t154 sweep to knowledge tree

The RE artifact-template knowledge doc (aidlc-developer-agent/re-artifacts.md)
still carried the bare `aidlc/codekb/<repo>/` shorthand after the
codekb-determinism placement fix made the store space-scoped. It is live
knowledge the reverse-engineering stage reads (reverse-engineering.md:91), so
the stale path is misleading. Repoint it to the space-scoped
`aidlc/spaces/<active-space>/codekb/<repo>/` form (the dir the codekb-path tool
resolves), matching the RE stage prose.

t154's bare-codekb guard (test 6) swept only aidlc-common/stages/, so this
residual escaped. Add test 7 to walk the knowledge tree recursively for the same
bare form, closing the guard gap.

* feat(workspace): active-space following — resolver + harness includes follow the cursor (#1) + templates framework-default branch (#4) + space-create skeleton (#5)

Makes AIDLC's method, its harness-native rule includes, and its own rule
resolver follow the active-space cursor instead of being pinned to `default`.
Single-team/default users are unaffected (zero churn). Bumps to 2.2.0.

#1 — resolver follows the cursor:
- aidlc-graph.ts: memoryDirFor()/memoryTemplatesDir() take an optional `space?`
  defaulting to activeSpace(projectDir) (cursorless → default), via a new
  memorySegmentsForSpace() helper mirroring the lib.ts space?-param precedent.
  The learnings/practices writers + the templates sensor follow the cursor for
  free. rulesDir()/memoryDisplayPath() stay default-pinned — rules_in_context is
  display paths frozen at PACKAGE time (not a runtime content channel), so the
  load-bearing rule delivery is the native includes + the writers.
- aidlc-includes.ts (NEW): repointHarnessIncludes(projectDir, space?) surgically
  rewrites ONLY the pointer segment of the active harness's native include in
  place — Claude @-stub, Kiro agents/*.json resources glob, Codex AIDLC_RULES_DIR.
  The include files stay COMMITTED (each carries load-bearing engine wiring the
  CLI reads at startup before any hook could regenerate it — gitignoring them is
  a fresh-clone chicken-and-egg). Idempotent: a no-op at `default`, so a
  single-team committed tree never churns. Preserves vision §11.6 "reference,
  not copy" (a committed pointer has no rule content to drift).
- Wired into the space switch (handleSpace), bootstrap (ensureWorkspaceDirs),
  the SessionStart ensure-step, and the --doctor readiness check (pinned to
  DEFAULT_SPACE). Retires the stale "fast-follow / out of scope" deferral.

#4 — templates framework-default branch:
- frameworkTemplatesDir() (NEW) + dispatcher threads --framework-templates-dir;
  the required-sections sensor resolves team → framework-default → floor
  (resolveTemplatePath, first hit wins); stage-protocol.md agent clause matches.
  Ships ZERO default files (the middle tier exists + is extensible; behaviour is
  unchanged until a default is authored).

#5 — space-create skeleton:
- handleSpaceCreate now seeds the full {memory, codekb, knowledge, intents} shape
  (codekb/ + knowledge/ with .gitkeep floors), matching default.

Folded-in cleanups (internal build + test fixture; no version impact):
- #7: remove the dead `{ src:"rules", dst:"steering" }` manifest row from
  harness/kiro + harness/kiro-ide (core/rules/ is gone; .kiro/steering/ was never
  a committed output — Kiro reads the agents/*.json resources glob now). Keep
  `rulesRename: "steering"` — it independently rewrites in-prose steering mentions
  + compiled-data paths (package.ts:87-89,383-388). dist byte-identical.
- t66: regenerate the stale designer-export golden (export.json:718 still said
  flat aidlc/codekb/<repo>/; live emits aidlc/spaces/<active-space>/codekb/<repo>/
  from the codekb merge). t66 green 86/0.

Tests: new t-active-space-includes (9); t155 +4 middle-branch tests; t165 stale
"pure cursor write" comment fixed; three journey tests updated for the #5 shape
(t-journey-workspace.sdk — verified live; t-acp-kiro-journey — verified live;
t-exec-codex-journey — edit matches, codex-exec flaked upstream so unverified-live).
coverage registry regenerated (function 77→78). Version trio → 2.2.0 + CHANGELOG +
README badge (t68). Docs + vision §8/§9 reflect shipped reality.

* docs: add Spaces and Intents user-guide chapter + workspace sections

Adds a dedicated, standalone-shareable chapter teaching how to use spaces
and multiple intents, plus cross-guide sections, all verified against source
on this branch.

- New docs/guide/03-spaces-and-intents.md: the workspace layout (annotated
  tree), intents (auto-birth, offer-a-second-intent, switching), spaces
  (create/list/switch, active-space following), multi-repo, and the
  committed-vs-gitignored split. Reads in sequence after Your First Workflow
  and stands alone as a link.
- Renumber: old guide chapters 03-13 -> 04-14 to free the 03 slot; all
  cross-references across docs/ + README rewritten; closes the pre-existing
  13->15 gap into a contiguous 00-17. Guide Map + nav pointers updated.
- Harness Engineer Guide: "Spaces: knowledge for more than one team" section
  in 07-team-knowledge.md (the per-space, data-not-code angle).
- Developer Reference: "The workspace: spaces and intents" data-model section
  in 01-architecture.md (the aidlc/ tree, cursor resolution, commit split).
- aidlc-sensor-fire.ts first-run banner: drop the literal doc-chapter path
  (now "See the AI-DLC documentation"), decoupling docs from shipped code so
  the renumber can't stale it. Regenerated dist copies.
- Fix stale audit-shard comment <host>-<pid>.md -> <host>-<clone>.md in
  aidlc-lib.ts and the four dot-gitignore files (the code uses the clone-id,
  not the PID); regenerated dist.

Comment/string/docs only — no logic change, no version bump (advisory stderr
text + comments are not user-invocable behavior).

* chore(version): reconcile workspace-anchor to 2.1.0 per roadmap

The roadmap assigns the per-intent workspace feature (this branch,
feature/workspace-anchor) to the 2.1.0 minor cut, reserving 2.2.0 for the
later reviewer-as-verifier work. The branch had drifted to 2.2.0 because the
codekb and active-space sub-parts each took their own minor bump
(2.1.0 -> 2.1.1 -> 2.2.0), but they are all part of the one workspace feature
that ships as a single minor.

- aidlc-version.ts: 2.2.0 -> 2.1.0 (+ regenerated dist copies)
- README badge: 2.2.0 -> 2.1.0
- CHANGELOG: consolidate the 2.2.0 (active-space following) and 2.1.1 (codekb
  placement) entries up into the single 2.1.0 (per-intent workspace) entry,
  dated 2026-06-24; add the Spaces and Intents chapter pointer.

t68 (version/CHANGELOG/README sync) green; headings unique; no dist drift.

* test(codex): live-verify the method-include seam — closes the t156 doc-only flag

Adds a gated live e2e probe that finalizes what t156 test 8 could only
DOC-VERIFY: that Codex resolves an @aidlc/spaces/default/memory/<file> mention
against the SHIPPED dist/codex tree and pulls the relocated method content into
context. An earlier spike hung at exit 124 and left the seam flagged untested;
it now reproduces green on codex-cli 0.139.0 (openai.gpt-5.5 on Bedrock).

- New tests/e2e/t-exec-codex-memory-include.serial.test.ts: injects a unique
  sentinel into the active space's org.md, drives `codex exec` with an @-path
  mention, asserts the sentinel round-trips. Gated by AIDLC_CODEX_EXEC_LIVE=1
  (skips cleanly otherwise); joins the t-exec-codex-* live-driver family.
- t156: drop the "FLAGGED untested / DOC-VERIFIED ONLY / exit 124" framing from
  test 8 and the file header; point at the new e2e test. Static unit-tier
  assertions unchanged (zero-LLM contract intact).

Verified: new e2e 1/1 green live; t156 9/9; t55 + coverage-registry freshness
green; typecheck clean. Test-only — no version/CHANGELOG bump.

* docs(spaces-and-intents): make the engine-dir references harness-neutral

The chapter described the engine directory as `.claude/` throughout, reading as
Claude-only. Reframe to match the guide convention (harness-neutral prose, Claude
as the shown example with variants named): the engine dir is the one
harness-specific directory (`.claude/` / `.kiro/` / `.codex/`), the workspace tree
notes the substitution inline, and the prose points at "the engine directory"
rather than `.claude/`. The active-space-following section already named all
three harnesses; left as-is. Command prefix stays `/aidlc` (the guide-wide
canonical form; the Codex `$aidlc` variant is documented in its harness chapter).

* docs(install): copy the aidlc/ workspace shell alongside the engine dir

A collaborator testing PR #429 on Kiro IDE followed the install steps literally
(engine dir only) and `/aidlc --doctor` failed its "workspace shell ready" check:
aidlc/spaces/default/memory/ was absent. Root cause: the aidlc/ workspace shell
ships as a SIBLING of the engine dir (.claude/.kiro/.codex) in every dist tree,
but the install instructi…
harmjeff added a commit to harmjeff/aidlc-workflows that referenced this pull request Jul 6, 2026
…en packager, bump 2.3.0

Compose hook (review #1/#2/awslabs#6/awslabs#7/awslabs#10/awslabs#11/awslabs#12): fold compose.sh +
compose-contributions.ts + compose-fragments.ts into a single portable
compose.ts (bun). Removes the shell-portability bug class — GNU-only
`sed -i` → replaceAll, BSD/coreutils>=9.2 `cp -rn` clobber → existsSync
no-clobber guard, swallowed `2>/dev/null || true` → recordDrop to
aidlc/.aidlc-hooks-health/plugin-compose.drops. Anchors validated +
escaped (skip-with-log, never a thrown regex); splices idempotent via
sentinel markers; compare-before-write; recompile only when changed.

Packager (review #3/#4): emit the kiro-ide projection too (four harness
trees now); `--check` byte-parity drift-guards dist/plugins/ (verified it
catches a tampered projection file).

Compose read half (review awslabs#5/awslabs#8): handle the inline-empty `field: []`
form; normalize CRLF once so every block/list regex is newline-safe.

Docs (awslabs#9/awslabs#13): README repo-layout adds plugins/ + dist/plugins/;
doc 18 gets a Kiro folder-drop security note (no install-time trust gate)
and drops stale two-helper / three-host references.

Also: remove spikes/dist-probe/ from the PR; bump 2.2.0 → 2.3.0
(version.ts + README badge + CHANGELOG); update t188 for compose.ts +
the four-harness projection set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
harmjeff added a commit to harmjeff/aidlc-workflows that referenced this pull request Jul 7, 2026
…en packager, bump 2.3.0

Compose hook (review #1/#2/awslabs#6/awslabs#7/awslabs#10/awslabs#11/awslabs#12): fold compose.sh +
compose-contributions.ts + compose-fragments.ts into a single portable
compose.ts (bun). Removes the shell-portability bug class — GNU-only
`sed -i` → replaceAll, BSD/coreutils>=9.2 `cp -rn` clobber → existsSync
no-clobber guard, swallowed `2>/dev/null || true` → recordDrop to
aidlc/.aidlc-hooks-health/plugin-compose.drops. Anchors validated +
escaped (skip-with-log, never a thrown regex); splices idempotent via
sentinel markers; compare-before-write; recompile only when changed.

Packager (review #3/#4): emit the kiro-ide projection too (four harness
trees now); `--check` byte-parity drift-guards dist/plugins/ (verified it
catches a tampered projection file).

Compose read half (review awslabs#5/awslabs#8): handle the inline-empty `field: []`
form; normalize CRLF once so every block/list regex is newline-safe.

Docs (awslabs#9/awslabs#13): README repo-layout adds plugins/ + dist/plugins/;
doc 18 gets a Kiro folder-drop security note (no install-time trust gate)
and drops stale two-helper / three-host references.

Also: remove spikes/dist-probe/ from the PR; bump 2.2.0 → 2.3.0
(version.ts + README badge + CHANGELOG); update t188 for compose.ts +
the four-harness projection set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
harmjeff added a commit to harmjeff/aidlc-workflows that referenced this pull request Jul 8, 2026
…en packager, bump 2.3.0

Compose hook (review #1/#2/awslabs#6/awslabs#7/awslabs#10/awslabs#11/awslabs#12): fold compose.sh +
compose-contributions.ts + compose-fragments.ts into a single portable
compose.ts (bun). Removes the shell-portability bug class — GNU-only
`sed -i` → replaceAll, BSD/coreutils>=9.2 `cp -rn` clobber → existsSync
no-clobber guard, swallowed `2>/dev/null || true` → recordDrop to
aidlc/.aidlc-hooks-health/plugin-compose.drops. Anchors validated +
escaped (skip-with-log, never a thrown regex); splices idempotent via
sentinel markers; compare-before-write; recompile only when changed.

Packager (review #3/#4): emit the kiro-ide projection too (four harness
trees now); `--check` byte-parity drift-guards dist/plugins/ (verified it
catches a tampered projection file).

Compose read half (review awslabs#5/awslabs#8): handle the inline-empty `field: []`
form; normalize CRLF once so every block/list regex is newline-safe.

Docs (awslabs#9/awslabs#13): README repo-layout adds plugins/ + dist/plugins/;
doc 18 gets a Kiro folder-drop security note (no install-time trust gate)
and drops stale two-helper / three-host references.

Also: remove spikes/dist-probe/ from the PR; bump 2.2.0 → 2.3.0
(version.ts + README badge + CHANGELOG); update t188 for compose.ts +
the four-harness projection set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
harmjeff added a commit that referenced this pull request Jul 9, 2026
* docs(plugins): resync to v2, rename extension→plugin, rebuild design set

Reset feat/v2-extensions onto upstream/v2 (v2.1.4, includes #429 spaces +
v2.1.1–v2.1.4) and rebuilt the design docs + fixtures with the decided naming:
"AIDLC plugin" everywhere, not "extension" or "bundle."

- Renamed: extensions/ → plugins/, .aidlc-bundle/bundle.json → .aidlc-plugin/plugin.json,
  all doc filenames (18-plugin-mechanism, 19-plugin-composition-timing, etc.)
- Bulk terminology: "extension"→"plugin", "bundle"→"plugin" in all docs, examples,
  fixture prose/comments/descriptions. YAML `bundle:` field left intact (code
  still reads it — rename when implementation lands).
- Restored ops-min + test-pro fixtures with new .aidlc-plugin/plugin.json manifests.
- Restored spikes/dist-probe/ (probe results + decision) and research reports.
- AGENTS.md: added plugins/<name>/ to project structure.
- Both overview guides (docs/reference/00-overview, docs/harness-engineering/00-overview)
  left untouched (vanilla, no plugin refs — minimizes rebase friction vs v2).

Design only; no implementation changes. The old #432 code stack is intentionally
dropped (tagged at backup/pre-resync-20260630).

* docs(plugins): update all design docs for hybrid decision + plugin naming

All 5 design docs (18–21, 10) now reflect the team-approved hybrid distribution
model and consistent "AIDLC plugin" naming:

- 18 (Plugin Mechanism): §2 principles now "one composer, multiple invocation
  sites" (host SessionStart hook vs aidlc plugin compose); §5 adds the hybrid
  distribution subsection + per-host table; §10 invariants include "a plugin IS a
  host plugin" and "trust is host-native."
- 19 (Composition Timing): §7 decision rewritten as "the hybrid" — emit real
  host plugins for Claude/Codex, thin CLI for Kiro; B.4 deleted, B.6 shrinks.
  Header updated.
- 20 (Vision): fully rewritten — new diagram showing packager → per-host plugin
  projections → host-native install; new §4 (Distribution: the hybrid) with the
  platform-team user story; new §5 per-host install paths (Claude zero-touch,
  Codex one-time-trust, Kiro folder-drop); new worked example showing all three
  hosts.
- 21 (Implementation Plan): B.4 marked DELETED (host-native trust, docs only);
  B.6 rewritten as "packager projection targets + Kiro thin CLI" (M not L);
  sequencing diagram and totals table updated.
- 10 (Authoring a Plugin): §5 rewritten as per-host install flow (host store vs
  folder-drop+compose); trust section delegates to host; stale "Extension System
  Vision" title fixed.

Also: remaining stale "Extension System" titles fixed in 10 + 21.

Design only; no implementation changes.

* feat(plugins): emit test-pro as a real Claude Code plugin (MVP compose)

First working AIDLC plugin — test-pro emitted as a real installable Claude Code
plugin at dist/plugins/test-pro/claude/. Validated end-to-end:

- .claude-plugin/plugin.json + marketplace.json: installable via /plugin
- SessionStart hook (hooks/compose.sh) copies stages, sensors, tools into the
  project's .claude/ tree and recompiles the graph
- Confirmed: 32 → 34 stages after compose (test-pro-integration + test-pro-full-suite
  correctly indexed in stage-graph.json)

The hook resolves bun via PATH then ~/.bun/bin fallback (addresses #454).
Contributions are shipped with the plugin but not yet merged by the MVP hook
(the N-way contribution-merge engine is B.1 work); stages + sensors + tools
compose cleanly.

Also:
- Removed extensions/ (old #432 remnant on v2; replaced by plugins/)
- Removed plugins/ops-min (not worth converting — demo-only fixture)

* feat(plugins): B.1 contribution merge — doctor 0 failures after compose

The compose hook now post-processes the compiled stage-graph.json to set-union
each contribution's structural surfaces (produces, consumes, sensors) into the
target stage's compiled node. This makes all plugin artifacts resolve in the
graph — doctor passes 33/0 with 34 stages and 137 artifact references.

Approach: modify the compiled output (not the fragile stage YAML). The hook:
1. Copies stages + sensors + tools into the project tree
2. Recompiles the graph (picks up new stages)
3. Post-processes stage-graph.json via compose-contributions.ts (merges
   contributions into existing stage nodes)

Confirmed: test-pro-regression-suite now correctly produced by build-and-test
(via the contribution merge), resolving the graph-reference + scope-validation
errors from the MVP.

Fragments (prose splicing into stage bodies) are future work — structural
surfaces are what the graph and doctor validate.

* feat(packager): auto-emit plugin projections from plugins/<name>/

The packager now discovers plugins/<name>/ (by .aidlc-plugin/plugin.json) and
emits per-harness host-plugin projections at dist/plugins/<name>/<harness>/:

- .claude-plugin/plugin.json + marketplace.json (generated from the source manifest)
- hooks/ (compose.sh + compose-contributions.ts + hooks.json — from a reusable
  template at scripts/plugin-hooks-template/)
- stages/ (with unknown frontmatter fields stripped for v2 compat)
- sensors/, tools/, contributions/ (copied verbatim)

Pipeline: `bun scripts/package.ts` now prints
`[plugin:test-pro] emitted dist/plugins/test-pro/claude/` alongside the 4
harness builds. `--check` still passes for all harnesses (plugin emission is
additive, doesn't touch dist/<harness>/).

End-to-end validated: packager emits → compose hook runs → 34 stages compiled →
test-pro-regression-suite produced → doctor 0 failures.

Currently emits Claude only; Codex + Kiro projections are future (B.6 items).

* feat(plugins): schema accepts number/name/bundle/when — drop the strip-hack

The v2 stage schema rejected the plugin-mechanism fields (number, name, bundle
as unknown keys; when as reserved), forcing the packager to strip them from
emitted plugin stages. This adds first-class (optional, shape-validated) support:

- aidlc-stage-schema.ts: number/name/bundle added to OPTIONAL_FIELDS + interface
  + shape checks (number is <int>.<int>; name/bundle any string). `when` removed
  from RESERVED_KEYS, added as an active structured predicate with
  WHEN_PREDICATE_KEYS = [producer-in-plan] and a single-key-map validator.
  All optional — core stages omit them and stay byte-identical (--check green).
- aidlc-lib.ts parseStageFrontmatter: parse `when:` as a nested single-key map
  (was captured as a scalar → "must be object" error). Handles block + inline forms.
- package.ts: removed the strip-hack — plugin stages now emit with full frontmatter.
- compose-contributions.ts: rewritten to merge into STAGE SOURCE files (append to
  produces/sensors lists) instead of post-compile JSON patching. Durable across
  recompiles (--init, runtime-compile) — the earlier JSON patch was wiped by any
  later compile. Idempotent, surgical list-append (no YAML rewrite).
- compose.sh: merge now runs BEFORE compile (source edit, not JSON patch).

Validated: test-pro composes to 34 stages, test-pro-regression-suite produced,
doctor 33/0 — and SURVIVES a subsequent --init recompile. Unit tests at parity
with pristine v2 (36 pre-existing env failures, 0 new); t62/t64 when-reserved
pins updated to the new predicate behavior.

consumes-merge, fragments (prose), and compile-side carry-through of authored
number/bundle/when remain future work.

* fix(plugins): compose hook substitutes {{HARNESS_DIR}} + confirm sensor binding

Two fixes to the compose hook, moving test-pro from "composes but rough" to
"clean":

1. {{HARNESS_DIR}} substitution — the hook copied stage/sensor .md files
   verbatim, leaving literal {{HARNESS_DIR}} tokens in composed prose (e.g.
   "{{HARNESS_DIR}}/knowledge/..."). Added a copy_and_substitute helper that
   applies the same token→.claude transform the packager does for core .md,
   scoped to the files the plugin adds. Verified: 0 tokens remain, .claude/
   paths correct.

2. Sensor binding — the test-pro sensors (coverage-threshold,
   requirement-coverage) are now correctly merged into build-and-test's
   sensors list by the durable contribution merge and appear in the compiled
   node's sensors_applicable. (The earlier "not bound" observation was a stale
   pre-fix compose; the durable-merge rewrite already fixed it.) Sensor scripts
   run and degrade gracefully (advisory) when their input JSON is absent.

Validated: 34 stages, test-pro-regression-suite produced, both sensors bound,
0 doctor failures, survives recompile.

* feat(plugins): fragment splicing — the prose half of the contribution seam

Implements compose-fragments.ts: a contribution's `## fragment: <anchor>` prose
blocks are spliced into the target stage's body at the declared anchor, so an
enriched core stage actually READS differently to the agent (not just resolves
in the graph).

Anchors: after-step:N (after that step's content, before the next heading),
before-step:N, end-of-steps, in:<Compartment>. Fragments at the same anchor
order by (order, bundle). {{HARNESS_DIR}} substituted in fragment prose.
Idempotent (matched by heading line) — safe on every SessionStart. Edits stage
source → durable across recompiles. Runs in compose.sh after the structural
merge, before compile.

Validated on test-pro/build-and-test: all 6 fragments land correctly — 9a/9b/9c
between Step 9 and Step 10 (ordered 100/110/120), 10a/10b between Step 10 and
Step 11, sensor prose in the Sensors compartment. Re-run keeps counts at 1
(idempotent). Doctor 33/0.

This completes the contribution seam: structural surfaces (produces, sensors)
AND prose fragments now merge. Remaining: consumes-edge merge, compile-side
carry-through of authored number/bundle/when, Codex+Kiro projections.

* feat(plugins): consumes-edge merge — contribution seam structurally complete

Adds consumes merge to compose-contributions.ts, completing the structural half
of the contribution seam (produces + sensors + consumes; fragments handle prose).

parseAddsConsumes extracts the nested {artifact, required} objects from a
contribution's adds.consumes; appendConsumes inserts them surgically after the
last existing consumes entry (before the next top-level key), never rewriting
the surrounding block — avoiding the YAML corruption an earlier full-rewrite
approach caused. Idempotent by artifact name. Handles both block form and
`consumes: []`.

Validated on test-pro/build-and-test: originals (code-generation-plan,
code-summary) intact + test-pro-testability-requirements +
test-pro-test-harness-design appended (required: false), well-formed, all 4
edges in the compiled graph. Idempotent. Doctor 33/0.

Contribution seam now complete: produces, sensors, consumes (structural) +
fragments (prose). Remaining: compile-side carry-through of authored
number/bundle/when; Codex+Kiro projections; plugin-path tests; changelog bump.

* feat(plugins): Codex + Kiro projections — hybrid delivered across all 3 harnesses

emitPlugins() now emits per-harness host-plugin projections for all three
harnesses (was Claude-only):

- Claude:  .claude-plugin/ + hooks.json (SessionStart)
- Codex:   .codex-plugin/ + hooks.json (SessionStart, lazy)
- Kiro:    .kiro-plugin/ + aidlc-plugin-compose.kiro.hook (promptSubmit)

The compose hook + both .ts helpers are now HARNESS-AGNOSTIC:
- plugin root resolves from CLAUDE_PLUGIN_ROOT | PLUGIN_ROOT | AIDLC_PLUGIN_ROOT
- project dir from CLAUDE_PROJECT_DIR | AIDLC_PROJECT_DIR | PWD (Codex unsets
  the project-dir var — PWD fallback, confirmed by the earlier probe)
- harness leaf (.claude/.codex/.kiro) from AIDLC_HARNESS_DIR, which each
  harness's hook command exports

Validated all three on real dist trees with harness-correct env patterns:
34 stages, contribution merged (test-pro-regression-suite produced), tokens
substituted, doctor 0 failures on each. Base --check still green (plugin
emission is additive, never touches dist/<harness>/).

Remaining: compile-side carry-through of authored number/bundle/when;
plugin-path tests; changelog/version bump. (required_sections enforcement +
Codex no-agents caveat noted in design docs.)

* test(plugins): t188 — end-to-end plugin compose guard

Automated regression coverage for the plugin system (was validated by hand
across 3 hosts; now guarded). Runs the real packager + the real compose hook
against a fresh dist/claude copy and asserts:

- packager emits all 3 host projections (.claude-plugin/.codex-plugin/.kiro-plugin)
- new plugin stages land in the compiled graph (34 stages)
- contribution seam merges all structural surfaces: produces, consumes, sensors
  into the target stage's compiled node
- prose fragments splice into the stage body in step order (9a<9b<9c)
- {{HARNESS_DIR}} substituted in composed prose
- compose is idempotent (re-run doesn't duplicate fragments)

10 tests, 0 fail. Discovered by the integration glob (no registry entry needed).
Co-runs cleanly with t145 (the other packager test).

* test(plugins): test-pro's own test harness (plugin-author validation)

plugins/test-pro/tests/plugin.test.ts — the plugin's OWN content guard, distinct
from the framework's t188 (which proves the compose mechanism). This validates
test-pro's content with the SAME rigor the framework applies to core, so a
plugin author (first- or third-party) validates before shipping:

- every plugin stage passes the real validateStageFrontmatter against the real
  agent roster; slug matches filename; bundle: test-pro declared; produces are
  test-pro- namespaced
- every contribution targets a REAL core stage; adds.produces namespaced;
  bundle declared
- manifest (.aidlc-plugin/plugin.json) well-formed

Reuses the framework's exported validators (parseStageFrontmatter +
validateStageFrontmatter from dist) — no reinvented validation. 22 tests, 0 fail.
Negative-checked: pointing a contribution at a missing stage correctly fails.

Doubles as the template a third-party plugin copies for its own test harness.

* docs(plugins): add test-pro README

Covers: what it does, how to install/use per harness (hybrid), the 4 core stages
it modifies (nfr-requirements/nfr-design/build-and-test/performance-validation +
what each adds), the 2 new stages it creates (test-pro-integration 3.85,
test-pro-full-suite 4.45, with scopes/when activation), and design/implementation
(contribution seam, namespacing, advisory sensors, machine-readable contract,
activation predicate, the plugin's own test harness). All facts pulled from
source; cross-links verified.

* docs(plugins): consolidate 18-21 into one as-built chapter, return to docs/ tree

Now that the plugin mechanism is built + validated on all 3 hosts, the four
design docs collapse into a single reference chapter matching the house style of
00-17, and the design set returns from plugins/docs/ to the main docs/ tree.

- docs/reference/18-plugin-mechanism.md — ONE chapter (house style: audience
  note, dense intro w/ cross-links, numbered ## sections, Cross-references),
  present-tense as-built. Absorbs: the mechanism (old 18), the install-time
  rationale (old 19 → §2), the hybrid distribution + worked example (old 20 →
  §4/§8), and implementation status (old 21 → §8 "As-built"). Deleted 19/20/21.
- docs/harness-engineering/10-authoring-a-plugin.md — moved back from plugins/docs.
- docs/reference/{examples,research}/ + the 2.0 Spec PDF — moved back.
- Re-added the index entries: reference 00-overview table row, harness-eng
  00-overview item 10. AGENTS.md pointer updated (now built, not "pending").
- aidlc-stage-schema.ts comment repointed to docs/reference/18 (+ regenerated dist).
- Corrected a stale `/aidlc --init` mention (retired command) → `aidlc-graph
  compile`; the t174 legacy-ref gate caught it and now passes.

All links resolve; --check green; t188 + test-pro content tests + t174 all pass.

* docs(plugins): drop duplicate 2.0 Spec PDF from docs/reference

The spec PDF is already tracked upstream at assets/ (README links there) and
dist/. The docs/reference/ copy was a duplicate introduced while relocating the
plugin design set — remove it. No doc links to the local copy (they use the
upstream URL), so nothing to repoint.

* fix(plugins): address PR #475 review — fold compose hook, harden packager, bump 2.3.0

Compose hook (review #1/#2/#6/#7/#10/#11/#12): fold compose.sh +
compose-contributions.ts + compose-fragments.ts into a single portable
compose.ts (bun). Removes the shell-portability bug class — GNU-only
`sed -i` → replaceAll, BSD/coreutils>=9.2 `cp -rn` clobber → existsSync
no-clobber guard, swallowed `2>/dev/null || true` → recordDrop to
aidlc/.aidlc-hooks-health/plugin-compose.drops. Anchors validated +
escaped (skip-with-log, never a thrown regex); splices idempotent via
sentinel markers; compare-before-write; recompile only when changed.

Packager (review #3/#4): emit the kiro-ide projection too (four harness
trees now); `--check` byte-parity drift-guards dist/plugins/ (verified it
catches a tampered projection file).

Compose read half (review #5/#8): handle the inline-empty `field: []`
form; normalize CRLF once so every block/list regex is newline-safe.

Docs (#9/#13): README repo-layout adds plugins/ + dist/plugins/;
doc 18 gets a Kiro folder-drop security note (no install-time trust gate)
and drops stale two-helper / three-host references.

Also: remove spikes/dist-probe/ from the PR; bump 2.2.0 → 2.3.0
(version.ts + README badge + CHANGELOG); update t188 for compose.ts +
the four-harness projection set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): address PR #475 round-2 review — parser/idempotency/packager correctness + honest docs

Rebased onto v2 (2.2.9 baseline; this bumps to 2.3.0). Round-2 fixes:

Compose correctness:
- consumes parser rewritten to parse per-entry blocks: no longer drops
  entries after the first, preserves authored `required: false` (was flipped
  to true), and appends past a `conditional_on:` line instead of splicing
  inside a core entry and stealing its brownfield gate (blocker + latent major).
- fragment idempotency now uses content-hashed open/close sentinels: an
  upgraded fragment replaces its prior block, a same-(bundle,anchor,order)
  collision is dropped-with-log, and blocks from separate plugins interleave
  by (order,bundle) regardless of hook-firing order (no more "next heading"
  boundary reversal).
- graph recompile self-heals: also recompiles when the compiled graph lacks a
  plugin stage slug, so a transient compile failure is no longer permanent.
- drops now route through the installed tree's hooksHealthDir (where core
  hooks write and --doctor scans); --doctor surfaces .drops files.
- required_sections is now an implemented merge surface (adds the field when
  the target lacks it); unimplemented adds.* keys are recorded as drops (no
  silent no-op).
- required_sections added to ARRAY_KEYS in parseStageFrontmatter (was parsed
  as a string and failed schema validation / crashed compile).

Packager / tests:
- bun-missing hook wrapper now exits 0 with a skip note instead of 127 on
  every SessionStart (incl. non-AIDLC projects).
- plugin targets derived from each harness manifest (+ optional `plugin`
  block) instead of a hardcoded map, so a new harness is covered automatically;
  checkHarness/checkPlugins share one diffTrees walk; --check adds a
  whole-repo-only orphan sweep for dist/plugins/.
- new `package.ts plugin build <plugin> <harness> <outDir>` seam; t188 builds
  into a temp dir (never rmSyncs the committed trees) and asserts the 2nd
  consumes artifact + required flag + the required_sections merge.
- plugin content tests (plugins/*/tests) are now discovered by the integration
  tier.

test-pro fixture (reference correctness):
- sensors use the two-arm `**/{aidlc-docs,intents}/**` glob; all hardcoded
  aidlc-docs/ paths replaced with engine-resolved `<record>/` prose; the two
  JSON sensor side-inputs separated from `.md` produces; sensor tools
  pass-through on a non-target write.

Docs honesty (demote to match code):
- doc 18 §3/§5/§6 + §8 Status mark which surfaces ship vs. are deferred
  (agents/scopes/memory/knowledge projection, adds.scopes/requires_stage
  merge, when: evaluation, marketplace/managed-settings/lockfile, Kiro
  .kiro.hook auto-fire + `aidlc plugin compose` CLI).
- Kiro install documents the working `bun compose.ts` invocation; the dead
  .kiro.hook / `aidlc plugin compose` presented as deferred.
- AGENTS.md drops "companions 19-21" (only ch18 exists) and lists 4 projections;
  examples/test-pro refs repointed from deleted docs 20/21 to doc 18 §8.
- version 2.2.9 → 2.3.0 (version.ts + README badge + CHANGELOG).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): address PR #475 round-3 review — 3 blockers + version-skew safety, silent-failure closes, doc honesty

Round-3 blockers (each a silent failure at a seam this PR ships):

- mergeRequiredSections is now version-skew-safe: compose probes the INSTALLED
  engine (validateStageFrontmatter via the existing dynamic-import seam) before
  writing required_sections, and skips + drop-logs when the installed schema
  won't accept the key — so an older dist install no longer has every subsequent
  graph compile permanently broken by an unknown-key. Verified both directions
  (current engine merges; simulated old engine skips and still compiles).
- `plugin build <plugin> <harness> <outDir>` no longer rmSyncs an arbitrary dir:
  refuses a non-empty outDir lacking a prior projection marker unless --force,
  and gives proper usage errors for unknown plugin/harness (no raw ENOENT stack).
  `plugin build test-pro claude .` now refuses with cwd intact.
- plugin test results no longer collide by basename: the meta key is now
  plugin-dir-qualified (`plugin-<plugin>-<stem>`), so two plugins' plugin.test.ts
  can't overwrite each other's result (a FAIL rolling up green — the round-2
  masking class). The plugin exclude is keyed the same way.

Silent-failure closes (round-3 non-blocking, done now):

- over-indented (6-space) consumes lists parse per-entry (tolerant split) and
  drop-log if entries outnumber chunks.
- cross-file same-(bundle,anchor,order) fragment collisions drop-with-log
  (run-wide seen-set), not last-writer-win via the hash-upgrade path.
- compile self-heal covers contributions-only plugins via a project-side retry
  marker (written on compile failure, cleared on success); t188 gains a case
  that strips the graph and asserts the heal.
- a set-but-nonexistent PLUGIN_ROOT now drop-logs instead of silently no-op'ing.

Doc honesty (round-3):

- doc 18: fixed the broken Kiro compose command (consistent PLUGIN_ROOT), stale
  flat drops path -> per-space hooksHealthDir, required_sections "machine-checked"
  -> "declared, not enforced yet", and expanded §8 Status (after-questions anchor,
  required_sections enforcement, dependencies).
- doc 10 authoring guide got the honesty sweep doc 18 had: adds.requires_stage/
  adds.scopes deferred, required_sections not-enforced, section-4 primitives
  (agents/scopes/memory/knowledge) marked not-yet-projected, dependencies deferred,
  after-questions anchor unimplemented; added sentinel-lookalike + hashless-upgrade
  authoring rules.
- 00-overview.md drops "companions"; test-pro README marks when: as declared-not-
  evaluated; both fixture stages declare their sensors so sensors_applicable is
  non-empty (the ## Sensors prose now matches frontmatter).

Verification: full unit tier (2069 assertions, 0 fail), --check green (harness
trees + all plugin projections + orphan sweep), t188 12/12 (incl. self-heal),
plugin tests, t68/t174/t150 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): retry-marker keys on plugin identity, not harness leaf + CRLF-normalize stage merge

Self-adversarial pass (running the reviewer's failure-mode taxonomy on our own
diff before re-review) surfaced a silent-failure regression in the round-3
compile self-heal:

- The retry marker keyed on PLUGIN_ROOT's basename, but a projection root is
  `dist/plugins/<name>/<harness>`, so the basename is the HARNESS leaf
  (claude/kiro) — shared by every plugin. Two plugins on the same harness then
  shared one marker (`aidlc/.plugin-compose-retry-claude`), and one plugin's
  successful compose would rmSync the marker, erasing another plugin's pending
  retry — the self-heal silently never fires. Now keyed by the plugin's manifest
  `name` (falling back to the `<name>` path segment), so each plugin's retry
  marker is distinct. t188 gains a test asserting the key is plugin-qualified,
  not the harness leaf.
- mergeRequiredSections (and the other merges) now normalize the stage file's
  CRLF up front, so inserting a merged field into a CRLF stage no longer yields
  mixed line endings. Contribution content was already normalized; the stage read
  was not.

Verified: t188 13/13, --check green, drift clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): close 6 silent-failure seams found by a self-adversarial pass (pre-empting round 4)

Ran the reviewer's own failure-mode taxonomy against our diff before re-review.
Six seams silently degraded a contribution while the log/exit signal said "fine"
— his exact through-line. All now drop-with-log and are pinned by tests.

- Fragment prose paired to its `## fragment: <anchor>` block BY LABEL, not array
  index. Body order != frontmatter order previously cross-paired prose to the
  wrong anchor silently; multi-fragment-per-anchor handled FIFO per label; an
  unmatched frontmatter entry drops-with-log. (compose.ts)
- Unresolvable fragment anchor now drop-logs at the source (locateAnchor): the
  virgin-anchor not-found path returned unchanged with no drop, so a
  contribution's produces/consumes/sensors landed while its prose vanished — a
  half-applied merge. All not-found branches (step / end-of-steps / in:) log.
- Range-heading step anchors resolve: core ships `### Step 4-8:`, and
  `^### Step 8\b` never matched it while `after-step:4` matched the whole 5-step
  range. locateAnchor now scans `### Step N(-M)?` and matches any step within a
  range, anchoring past the full heading.
- Stage/sensor/tool slug collision drop-logs instead of silently skipping: a
  plugin shipping a file that shadows core or another plugin was a no-op with no
  evidence. copyTreeNoClobber now logs a genuine content collision (identical
  re-copy stays quiet — idempotent).
- mergeRequiredSections handles a stage whose frontmatter has no trailing newline
  after the closing `---` (was: whole merge silently dropped). Stage content is
  also CRLF-normalized before merge.
- `/aidlc --doctor` now FAILS (exit 1) on any recorded hook drop instead of
  scoring it pass:true — a dropped contribution / failed recompile no longer
  rolls up green past a CI gate. The fix moved the signal from "a file nothing
  reads" to "a file doctor reads AND scores as a failure."

Also (prior commit in this branch): the compile retry-marker now keys on plugin
identity, not the harness leaf.

t188 gains cases for the anchor-not-found, range-heading, and slug-collision
seams (all were claimed "no silent failure" but untested). Verified: t188 16/16,
full unit tier 2083 assertions 0 fail, --check green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): close 10 more silent-failure seams from a 2-agent adversarial round (pre-empting round 5)

Ran two parallel adversarial reviewers (compose/merge + packager/runner/doctor)
against the branch, prompted to break not confirm. They found 10 issues — most in
the round-4 fixes themselves — all matching apackeer's "a failure that rolls up
green" through-line. All fixed, each pinned by a test.

Compose (the fragment splitter/replacer was the richest source):
- Fence-aware body splitter: a `## fragment:` line inside a ``` fence (how an
  author documents the format) no longer truncates the block + spawns phantom
  blocks that silently drop trailing prose. Line-scanner tracks fence state.
- Close-marker in prose no longer corrupts an upgrade: the close sentinel now
  carries the same content hash as the open, so a marker-lookalike line in prose
  can't be mistaken for the block boundary on re-splice.
- Leftover body block (more `## fragment:` blocks than frontmatter entries) is
  now dropped-with-log — the "or vice versa" the round-4 comment promised but
  never did.
- BOM / leading-blank-line before frontmatter no longer silently skips the whole
  contribution (strip BOM + leading blanks; a no-target .md now drop-logs).
- required_sections value with an interior quote is no longer silently dropped
  (capture whole value, strip only matched outer quotes).

Doctor (my round-4 fix over-corrected):
- Drops are severity-tagged; `--doctor` FAILS only on `[degraded]` drops (a
  half-applied contribution, a failed recompile) and treats `[advisory]` /
  untagged (documented-deferred surface, version-skew skip, core sensor timeout)
  as a passing row. Fixes the round-4 regression where any drop red-ed doctor on
  legal author behavior and on benign core drops in a no-plugin install.
- compose rewrites its .drops file each run (overwrite, remove when empty), so a
  fixed + re-composed install self-clears — no sticky red gate, no unbounded
  growth on a persistent collision. Troubleshooting doc updated to match.

Packager / runner:
- `plugin build <file>` (outDir is a file) gives a usage error, not a raw ENOTDIR
  stack (stat before readdir).
- Verbose per-file logs key on the qualified name (two plugins' plugin.test.ts no
  longer overwrite each other's failure detail); the summary reader matches.
- `--filter` matches the qualified name too, so filtering by the displayed name
  no longer selects nothing and reports green.

t188 gains cases for all four compose seams + the doctor severity split + the
self-clear; gen-coverage-registry's cli-spawner ratchet updated for t188.
Verified: full unit tier 2083/0, --check green, t188 23/23.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): close 5 more seams from a round-6 adversarial pass — 2 blockers in round-5's own fixes

A third 2-agent adversarial round (broad sweep) found that round-5's fixes
introduced two new blockers, both the "silent failure that rolls up green"
through-line. All fixed, pinned by tests.

Blockers:
- Per-plugin drops file. The round-5 self-clear made `plugin-compose.drops` a
  single SHARED file rewritten each run, but SessionStart composes one plugin per
  file against the same project — so a clean plugin's compose (or an early-exit
  guard) DELETED another plugin's live [degraded] drop, and doctor went green.
  Drops are now per-plugin (`plugin-compose-<PLUGIN_KEY>.drops`), keyed by the
  plugin identity computed once up front (so early exits key it too); doctor
  globs *.drops and aggregates. Reproduced the erase, then the fix.
- Fence scanner ignores fence length. The round-5 fence-aware splitter compared
  only the fence CHARACTER, so an inner ``` closed an outer ```` — documenting
  the fragment format with a nested fence corrupted the stage + dropped trailing
  prose. Now tracks length + requires the closing fence be same char, length >=
  opener, and carry no info string (CommonMark).

Lower-severity:
- required_sections value that strips to empty ("" / whitespace) is dropped-with-
  log instead of merging a useless `- ""`.
- A bundle containing ':' (the sentinel delimiter) is refused with a log — it
  would break the peer-block scan's [^:]+ and silently misorder splices.
- `plugin build` into a symlink outDir (incl. a BROKEN symlink, which existsSync
  reports absent so it slipped the round-5 stat guard → raw EEXIST) gives a proper
  usage error via lstatSync.

t188 gains cases for per-plugin drops isolation, the nested fence, and the bundle
colon; the drops-reading test helper now aggregates per-plugin files. Deferred to
docs/core follow-up (out of plugin scope): a scalar `required_sections: Foo`
parses to [] via core's general listField (and required_sections isn't machine-
enforced yet); `#` in a list item; the plugin-test-discovery vs build-discovery
predicate mismatch. Verified: full unit tier 2083/0, --check green, t188 26/26.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): tighten `plugin build` outDir guard (PR #475 pre-merge ask)

apackeer approved with one pre-merge ask — two holes in the `plugin build`
destructive-overwrite guard, both reproduced, plus no test pinning it.

- Marker false positive: the prior-projection check was `existsSync(<outDir>/
  .claude-plugin)`, but EVERY real Claude plugin has a `.claude-plugin/` dir — so
  `plugin build test-pro claude .` inside a foreign plugin checkout wiped it
  silently. Now requires the marker's plugin.json to parse AND carry an
  `aidlc-`-prefixed name (what our own projections emit), so only a genuine AIDLC
  projection is treated as overwritable.
- Trailing-slash symlink bypass: `lstatSync("<link>/")` resolves through the
  link, so a trailing-slash symlink outDir slipped the symlink refusal and wiped
  the target. Strip trailing separators before resolving/lstat.
- Pinned with tests: t188 now covers non-empty refusal, foreign-.claude-plugin
  (non-aidlc name) refusal, file outDir, symlink (plain + trailing-slash), broken
  symlink, and a genuine-prior-projection overwrite.

CLI-guard only — no dist change. Verified: t188 32/32, full unit tier 2083/0,
--check green. The remaining review items (stage-copy skew, doctor severity for
core-hook drops, listOf shortfall, self-upgrade copy path, uninstall/decompose,
etc.) are tracked as post-merge follow-ups per both reviewers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@leandrodamascena
leandrodamascena deleted the feature/inter-unit-contracts branch August 13, 2026 00:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants