@sabaiway/agent-workflow-kit 14.38.0 → 14.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,34 @@ Semantically versioned ([semver](https://semver.org)), newest first. The `versio
4
4
  is the current release. `upgrade` mode reads a project's `docs/ai/.workflow-version` and applies
5
5
  every `migrations/<version>-<slug>.md` newer than it, in semver order.
6
6
 
7
+ ## 14.39.0 — agents find the project rules from a row in the entry file's Memory Map, and you change them through one command, `/agent-workflow-kit rules` (AD-191)
8
+
9
+ **Every upgrade now keeps a Memory Map row in `AGENTS.md` that links `docs/ai/rules/effective/project-layout.md`, so an
10
+ agent reads the layout rules before it creates, moves or names a file.** The new `/agent-workflow-kit rules` changes
11
+ your own rules: it shows the exact text and writes `docs/ai/rules/local/<theme>.md` only on your yes, then rebuilds and
12
+ checks the file agents read.
13
+
14
+ - **The Memory Map row.** `inject-methodology.mjs reconcile` adds one row per rules theme on the line after the
15
+ `docs/ai/agent_rules.md` row, in the same one write as the three pointers, whether or not the effective file exists
16
+ yet. A row already there is kept byte for byte, also when you edited it; a row with a text an earlier release
17
+ shipped is refreshed (none yet). Where it cannot be placed (no `docs/ai/agent_rules.md` row, that row on two lines,
18
+ or the 100-line limit) the row is skipped with its reason and nothing stops. The reconcile's line names the row's
19
+ outcome beside the pointers, the zero-diff line included; a run that adds a row is no longer zero-diff.
20
+ - **`/agent-workflow-kit rules`, a Configure writer.** It starts with the rules ensure, then does one of three things you
21
+ ask: change a rule in your local file; move an edit you made to the kit file `docs/ai/rules/<theme>.md` (its ensure
22
+ line says it carries your edit) into your local file and delete the kit file, which the next ensure seeds again; or
23
+ drop your local file. On `pending` it does the merge, shows what changed and records it. It stops on a refusal, ends
24
+ on `rules-cli.mjs --check` answering `current`, and never writes the effective file by hand.
25
+ - **The upgrade offers the mode** on a kit-file line that says it carries your edit, and its step-4 and step-8 reports
26
+ relay the Memory Map row's outcome.
27
+ - **The architecture template points to the layout rules.** `references/templates/architecture.md` drops the Layers and
28
+ Module Boundaries sections for one section naming `docs/ai/rules/effective/project-layout.md`; a deployed
29
+ `docs/ai/architecture.md` is yours and is not changed.
30
+ - The router budget for a mode read with the router is 53248 B (was 52224 B); the router stays within 12288 B.
31
+
32
+ **Upgrade note.** No step: the next upgrade adds the row to `AGENTS.md`. To change a layout rule, run
33
+ `/agent-workflow-kit rules` and say what to change.
34
+
7
35
  ## 14.38.0 — the upgrade and bootstrap lay the project-layout rules into your project and keep the file agents read current; with your own local rules the agent does the merge (AD-190)
8
36
 
9
37
  **`/agent-workflow-kit upgrade` and bootstrap now write the project-layout rules into `docs/ai/rules/`, keep
package/README.md CHANGED
@@ -107,7 +107,7 @@ your-repo/
107
107
  ├── active_plan.md ← current task
108
108
  ├── agent_rules.md ← session protocols + self-review
109
109
  ├── current_state.md ← snapshot of the codebase now
110
- ├── architecture.md ← layers & boundaries
110
+ ├── architecture.md ← layout rules pointer & extension points
111
111
  ├── technical_specification.md
112
112
  ├── decisions.md ← ADRs — the HOT window (newest)
113
113
  ├── adr/ ← one file per archived ADR + log.md navigator
@@ -252,6 +252,7 @@ file), or run the guarded `/agent-workflow-kit uninstall`.
252
252
  | `/agent-workflow-kit review-state` | any time | **read-only review-receipt checker** — makes "reviewed ≠ shipped" detectable: the bridge review wrappers append a receipt per successful review (into a file inside the git dir — never committable); this checks that every backend your configured `plan-execution.review` recipe names holds a **fresh, grounded receipt for the current uncommitted tree** (any later edit the payload can see moves the fingerprint and stales the receipt; a review continuation never re-attests a folded tree). `--check` gives a gate exit code to declare in `docs/ai/gates.json` **by hand or via the consent-gated seeder** (preview → your explicit yes; never without consent). `--await [--timeout <s>]` (AD-049) BLOCKS until every recipe-named backend has receipted the current tree — the durable completion signal is the receipt, never a process event — so you wait for the bridges instead of hand-polling. Never writes, never commits, never runs a subscription CLI; it spawns read-only `git` queries to fingerprint the tree — and `git commit --no-verify` stays possible (discipline, not a sandbox). |
253
253
  | `/agent-workflow-kit sandbox-masks` | any time | **exclude lane for sandbox masks** — an OS sandbox (Claude Code) masks work-tree paths with a device or, on Linux, a foreign bind mount, as untracked `git status` noise; the review domain already ignores a **device mask** **by construction** (never-committable untracked classes — char/block devices, FIFOs, sockets — are excluded from the fingerprint, the assembled review payload, and the clean checks), while a **mount-only mask** rides the fingerprint and `git add -A` while git does not ignore it (load-bearing there). This mode hides them from `git status` too: flagless = read-only probe (derives the CURRENT mask set from the unfiltered walk + lstat + `/proc/self/mountinfo` — never a frozen list; off Linux the mount signal is not read and it says so, an unreadable or malformed table refuses — and revalidates fenced entries, loudly flagging one that became a real path); `--apply` = consent-gated FULL-BLOCK replace of its own fenced block in `git rev-parse --git-path info/exclude` (stale masks drop by construction; `--clear` always means REMOVE the block — it takes precedence over the derivation). Writes ONLY its fence — never `.gitignore`, never global config; symlinked/non-regular exclude paths and malformed fences fail closed. Watch note: a real file at an excluded path is silently skipped by bulk staging (`git add -A`/`git add .`) — delete the stale line first; the probe flags exactly this case. |
254
254
  | `/agent-workflow-kit mcp` | Claude Code · opt-in | **typed-channel registration** — the kit ships a read-only stdio **MCP server** exposing its two promptless readers as TYPED tools (`path_inventory`: exists / type / size / line count / listing / a small file's text, many paths in ONE call; `repo_search`: literal search, the pattern a JSON string). Their arguments are named JSON fields rather than a string handed to a shell, so a pipe, a redirect or a quote inside a pattern or a path stays DATA and is never interpreted — legal bytes to search for, with no shell to read them as operators. Shipping the server does nothing on its own — a client sees it only once the project declares it, and this mode is that declaration: the `agent-workflow` entry in **`.mcp.json`** (command `node`, args = the absolute path of the RUNNING kit's server) plus `enabledMcpjsonServers` and the two derived tool allow rules in **`.claude/settings.json`**. Preview by default and the **exact entry is printed before consent** (registering a server means your client will run that command); `--apply` writes `.mcp.json` FIRST, then settings, merge-don't-clobber with each file's EOL kept, and a re-apply adds nothing twice. An existing `agent-workflow` entry that **structurally differs** from what this kit copy would write is refused unwritten unless `--replace` is given (the comparison ignores key order, so re-serialized identical bytes are the same registration) — silently changing what an MCP server launches is what consent must not slide past. `--replace` prints the entry standing in `.mcp.json` and the entry it would write, and replaces it only with `--apply`. An existing `.mcp.json` or `.claude/settings.json` is written in place: it keeps its inode, never removed or renamed over; an absent file is created. Where an OS sandbox masks `.mcp.json` with a device node, it writes nothing, exits 0 and names the advisor's `mcp-channel` item, which applies the registration by the consent route. The server is a read-only child of your client (no write, no exec API) running outside the Bash sandbox as the client does. Never writes `settings.local.json`; never commits. |
255
+ | `/agent-workflow-kit rules` | any time | **your project rules** — the kit lays down default rules per theme (the first is project layout: where code, tests and specs live) in `docs/ai/rules/<theme>.md`; your own rules go into `docs/ai/rules/local/<theme>.md` and win where they differ. Tell the agent in plain words what to change: it shows the exact text first and writes your local file only on your yes, then rebuilds the effective file `docs/ai/rules/effective/<theme>.md` that agents read (merging your rules over the kit’s by meaning) and checks it is current. It also moves an edit you made to the kit file into your local file, or drops your local rules on request. Never commits. |
255
256
  | `/agent-workflow-kit grounding` | any time | **grounded-review facts assembler** — mechanizes populating `agy-review --facts @f`: slices your entry-point's **Hard Constraints** section verbatim (exactly one match, else a loud stop) and/or a plan's three canon sections (`## Goal and boundary` + `## Module ledger` + `## Verification`, each required; duplicates stop), under the same byte budget the agy wrapper enforces (minus `--reserve-bytes` for the artifact share), with a loud tail-trim on overflow. `--autonomy` (AD-044) appends the COMPUTED effective autonomy policy from the git-top `docs/ai/autonomy.json` (every red-line + per-activity level, stated source line; absent file → the computed defaults ARE the policy, exit 0; a malformed policy fails CLOSED, exit 1). `--extra <text|@file>` (repeatable) appends your own facts **byte-verbatim** after the mechanical sections, so the merge is a tool input rather than a shell append — an `@file` must sit inside the proven git work tree (never the git dir) or the system temp surface, and is read through a no-follow descriptor (a FIFO cannot block it, a symlink leaf cannot substitute its target). Prints to stdout; `--out` writes **one scratch file only** — system-temp outside the repo ($TMPDIR / /tmp, rewritable) or a **fresh** gitignored in-repo path (create-only, exclusive write; an existing in-repo file, even gitignored, is refused — the `.env` clobber class); tracked, not-ignored-in-repo, other outside-repo, and symlink/non-regular destinations are all refused. Never commits, never runs a subscription CLI. |
256
257
  | `/agent-workflow-kit core-evidence` | any time | **the ONE loop-evidence writer** (strip-the-kit) — every core evidence record lands in a single append-only JSONL store inside the git dir (never committable; versioned schema, latest-per-key supersession, byte-identical duplicates refused, malformed lines fail every reader closed). `red-proof "<file>#<pattern>"` declares an observed-red **BEFORE a bugfix** (N/N red runs + content custody + base + the pre-fix fingerprint; green/mixed/timeout are DISTINGUISHED refusals — nothing written); `degrade --backend --reason` is the ONLY escape for an unavailable review backend (per-tree, never all backends); `summary` renders the whole loop state statelessly (gate result · per-backend verdicts · red-proofs · degrades) — no ledger, no rounds, nothing remembered. Honest residual: records are forgeable — self-discipline, not a security boundary. Never commits, never runs a subscription CLI. |
257
258
  | `/agent-workflow-kit coverage-check` | any time | **the final-run checker** (D3(c)+(d)) — **certifies coverage ONLY inside the `--final` run that owns the lcov** (ownership is exclusive by CONVENTION over the fixed path, not enforced — a concurrent writer to it is a stated residual, queued as LCOV-EXCLUSIVE-OWNERSHIP): an artifact on disk proves nothing about the tree it came from, so a standalone run prints its findings and states `attested=no` / `NO VERDICT` rather than a PASS (an lcov that predates an edit would otherwise certify a line the suite never executed). `attested=` claims a verdict was ISSUED, pass or fail — a run over uncovered lines still reads `attested=yes` and still exits 1, and a run whose coverage arm never executed (no lcov) reads `attested=no` even inside `--final`, because nothing was read and nothing is certified. The runner passes a nonce whose one-way commitment over `{nonce, fingerprint, base}` is the `final-start.attempt` it recorded; a context describing another tree, or matching no recorded attempt, is a REFUSAL, never a verdict. Findings are unchanged — reads the lcov the declared `unit-tests` gate produced at the FIXED git-dir path and fails on any uncovered CHANGED executable Node line (listed `file:line`; a changed file absent from the map is a file-level red; out-of-domain/unsupported files are LISTED — the claim narrowed honestly); VERIFIES every current-base red-proof declaration (bound test exists · custody hash unchanged · green N/N now · pre-fix fingerprint differs); prints `lcov-sha256=<hex|none>` of the exact bytes it consumed — the sha the `--final` receipt binds and re-hashes. An absent lcov is a LOUD `skipped-no-lcov`; a symlinked path is a refusal. `--check` is the gate exit code — declare it as the LAST gate (`run-gates --final` refuses otherwise). Read-only. |
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: agent-workflow-kit
3
3
  description: Deploy or upgrade a portable AI-agent memory-and-workflow system in any project. Use when the user wants to bootstrap `docs/ai/` + an entry-point `AGENTS.md` (+ `CLAUDE.md` alias) + cap/archive/index enforcement in a new or existing repo, set up the Memory Map and session protocols, install the docs-rotation pre-commit hook, or run `/agent-workflow-kit` / `/agent-workflow-kit upgrade`. Triggers on phrases like "set up the memory system", "deploy the AI workflow here", "bootstrap docs/ai", "upgrade the workflow". New to the epic, story and task tier? Run `/agent-workflow-kit tier`. A typed decision with a confidence from Jev? Run `/agent-workflow-kit jev`.
4
4
  disable-model-invocation: true
5
5
  metadata:
6
- version: '14.38.0'
6
+ version: '14.39.0'
7
7
  ---
8
8
 
9
9
  # agent-workflow-kit
@@ -163,6 +163,10 @@ guarded — read `${CLAUDE_SKILL_DIR}/references/modes/sandbox-masks.md` before
163
163
 
164
164
  guarded — read `${CLAUDE_SKILL_DIR}/references/modes/mcp.md` before acting.
165
165
 
166
+ ### Mode: rules
167
+
168
+ writer — read `${CLAUDE_SKILL_DIR}/references/modes/rules.md` before acting.
169
+
166
170
  ### Mode: uninstall
167
171
 
168
172
  guarded — read `${CLAUDE_SKILL_DIR}/references/modes/uninstall.md` before acting.
package/capability.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "schema": 1,
4
4
  "name": "agent-workflow-kit",
5
5
  "kind": "composition-root",
6
- "version": "14.38.0",
6
+ "version": "14.39.0",
7
7
  "provides": [],
8
8
  "roles": {},
9
9
  "detect": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sabaiway/agent-workflow-kit",
3
- "version": "14.38.0",
3
+ "version": "14.39.0",
4
4
  "description": "Portable, cross-agent memory & workflow for AI coding agents — Claude Code, Codex, Cursor, Devin Desktop. One command deploys an AGENTS.md entry point + docs/ai context with cap/archive/index enforcement into any repo.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -0,0 +1,23 @@
1
+ ### Mode: rules
2
+
3
+ <!-- opt-in-capability: none — the user's own rules are changed on request, never a capability the advisor offers -->
4
+
5
+ Requires: ${CLAUDE_SKILL_DIR}/references/shared/command-shapes.md
6
+
7
+ **The one place the user changes the project rules.** Each theme (the first is project layout) has three files under `docs/ai/rules/`: the kit file `docs/ai/rules/<theme>.md`, which only the kit writes; the user's local file `docs/ai/rules/local/<theme>.md`, which wins where it differs; and the effective file `docs/ai/rules/effective/<theme>.md`, which agents read. This mode writes the local file on the user's request and yes, and keeps the effective file and its record current with the kit's own tools. It never commits.
8
+
9
+ **1. Start with the ensure.** Run `node ${CLAUDE_SKILL_DIR}/tools/ensure-configs.mjs --reconcile --only rules --cwd <project>` and relay its line in plain words. The effective file is built, never written by hand: this mode moves no edit out of it, and the ensure's rebuild or the next merge replaces one.
10
+
11
+ **2. Do what the user asks — one of three.**
12
+
13
+ - **Change a rule.** The user says in plain words what to change. Show the exact text you will write to `docs/ai/rules/local/<theme>.md`, its frontmatter included, holding only the user's own rules and never a copy of the kit's, and write it on the user's yes. That request and that yes are the consent for the write of the local file.
14
+ - **Move an edit of the kit file.** When an ensure line for the kit file says it `carries your edit`, whatever token the run printed (`pending` and `regenerated` included), offer the move: compare `docs/ai/rules/<theme>.md` with the bundled `${CLAUDE_SKILL_DIR}/references/rules/<theme>.md`, write the user's changes, by meaning, into the local file (shown first, written on the yes), then delete the kit file. The next ensure seeds it again from the template, so the kit file keeps its one writer.
15
+ - **Drop the local rules.** On the user's request, delete the local file; the next ensure rebuilds the effective file from the kit file and records it.
16
+
17
+ **3. After each of these, run the ensure again.** On `pending`, do the merge its line states, show the user what changed in the final rules, then run the record command the line prints with the sha256 of the merged file (one plain command for the digest, per the shared command-shapes contract).
18
+
19
+ **It stops on a refusal.** When an ensure run answers `failed`, or the record command exits non-zero, relay the line and stop: write, delete and record nothing further, and run no closing check as if the change held.
20
+
21
+ **4. End with the checks.** Run `node ${CLAUDE_SKILL_DIR}/tools/ensure-configs.mjs --reconcile --only index --cwd <project>`, then `node ${CLAUDE_SKILL_DIR}/tools/project-rules/rules-cli.mjs --check --cwd <project>`, which answers `current` for every theme; relay both in plain words.
22
+
23
+ **What it never does.** It never writes the effective file except with the merge a `pending` line states, never changes the kit file's text (the move deletes the file whole and the ensure seeds it again), and never writes a local file without the user's request.
@@ -10,7 +10,7 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
10
10
  2. **Never-downgrade gate — FIRST, before any write.** Compare the stamp to the **deployment-lineage head** (`4.0.0` — NOT this kit's package version). If the stamp is **greater than the head** or unparseable → **STOP and report**; do not touch a newer / unknown deployment at all (not even the methodology slot). This STOP is one of the few places the number is actionable (*Version disclosure* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`): show the user **the `docs/ai` structure version** their deployment carries versus the one this kit expects, plus the plain one-line two-axes note — naming it the structure version, **never** "lineage head".
11
11
  3. **The step-3 run-list — stamp-independent reconciles, BEFORE the equal-head short-circuit.** Reached only when the stamp **≤ head**. Run the **eight operations below in order, on every upgrade** (equal-head too) — no lineage-head bump, no migration file. Each backticked id anchors its rationale block below the checklist; a consent marker is a CONDITIONAL gate — an explicit yes is required exactly where the row's clause states one:
12
12
 
13
- 1. `pointers` — `node ${CLAUDE_SKILL_DIR}/tools/inject-methodology.mjs reconcile <project>/AGENTS.md` → per pointer: added · already present · skipped (reported) · a hard STOP.
13
+ 1. `pointers` — `node ${CLAUDE_SKILL_DIR}/tools/inject-methodology.mjs reconcile <project>/AGENTS.md` → per pointer: added · already present · skipped (reported) · a hard STOP; per Memory Map row of a rules theme: added · already present · refreshed · skipped (reported).
14
14
  2. `footprint` — `node ${CLAUDE_SKILL_DIR}/tools/hide-footprint.mjs --dir <project> --reconcile --dry-run` → visibility: visible · ambiguous · hidden — consent (conditional): ambiguous → ask which it is BEFORE anything; hidden → the conditional re-run without `--dry-run` (its surfaced paths ask per bootstrap step 9).
15
15
  3. `configs` — `node ${CLAUDE_SKILL_DIR}/tools/ensure-configs.mjs --reconcile --cwd <project>` → one line per ensure: `seeded` / `note-refreshed` / `refreshed` / `regenerated` / `pending` / `already-current` / `customized-preserved` / `malformed-preserved` / `already-present` / `not-placed` / `skipped-no-node-evidence` / `old-adr-layout-migration-instructed` / `failed` — consent (conditional): a specs replace line → ONE yes naming every file the custom lines name and saying their edits are overwritten, asked even on a non-zero exit; on the yes run the printed line, then STOP if that run fails or an op it does not re-run failed; on a no STOP on any failed op.
16
16
  4. `gates-migration` — `node ${CLAUDE_SKILL_DIR}/references/scripts/migrate-gates.mjs --kit-tools ${CLAUDE_SKILL_DIR}/tools --cwd <project>` → the preview plan · INERT checker · CUSTOMIZED entries, each named — consent: apply only on an explicit yes, re-run with `--apply`.
@@ -19,11 +19,11 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
19
19
  7. `tier` — `node ${CLAUDE_SKILL_DIR}/tools/tier-preview.mjs --cwd <project>` → per gap: present · offered · declined · undecidable · applied · recorded · refused — consent: one ask, only when offered.
20
20
  8. `bridge-settings` — `node ${CLAUDE_SKILL_DIR}/tools/bridge-settings.mjs --reconcile` → outcome line(s), pasted verbatim: ok · absent · flagged · duplicates · unusable.
21
21
 
22
- **`pointers` — the bounded pointers.** ONE call reconciles **three** pointers — the **workflow-methodology** pointer, the **orchestration-recipes** pointer (Solo / Reviewed / Council / Delegated / Subagent, routing to `/agent-workflow-kit recipes`) right below it, and the **autonomy-policy** pointer (the `docs/ai/autonomy.json` read contract, routing to `/agent-workflow-kit set-autonomy`) below that — and is filled on **every** upgrade, idempotently (zero-diff when all are already present + filled), so even a legacy or current equal-head deployment gains them **without a lineage-head bump or a migration** (the `agent-workflow-memory` **package** template did get docs-only headroom trims for the chained pointers, but no deployed-`docs/ai` structure changed). Per slot it inserts an empty pair at its anchor if absent, preserves a customized pair verbatim, and STOPs (never edits) on a malformed pair, a missing / duplicate anchor, or **when a fill is needed but the installed `agent-workflow-engine` is absent/invalid** (the fragments are read live from it — see the distinct outcomes below).
22
+ **`pointers` — the bounded pointers.** ONE call reconciles **three** pointers — the **workflow-methodology** pointer, the **orchestration-recipes** pointer (Solo / Reviewed / Council / Delegated / Subagent, routing to `/agent-workflow-kit recipes`) right below it, and the **autonomy-policy** pointer (the `docs/ai/autonomy.json` read contract, routing to `/agent-workflow-kit set-autonomy`) below that — and is filled on **every** upgrade, idempotently (zero-diff when all are already present + filled and the Memory Map rows present), so even a legacy or current equal-head deployment gains them **without a lineage-head bump or a migration** (the `agent-workflow-memory` **package** template did get docs-only headroom trims for the chained pointers, but no deployed-`docs/ai` structure changed). Per slot it inserts an empty pair at its anchor if absent, preserves a customized pair verbatim, and STOPs (never edits) on a malformed pair, a missing / duplicate anchor, or **when a fill is needed but the installed `agent-workflow-engine` is absent/invalid** (the fragments are read live from it — see the distinct outcomes below). The same call keeps one Memory Map row per project-rules theme, after the `docs/ai/agent_rules.md` row; one it cannot place is skipped and reported, never a STOP.
23
23
 
24
- **Classify the exit — there are THREE non-zero exits + one soft in-band skip; handle each differently:**
24
+ **Classify the exit — there are THREE non-zero exits + soft in-band skips; handle each differently:**
25
25
 
26
- (a) **Soft, reported skip of a CHAINED pointer — orchestration and/or autonomy (CONTINUE the upgrade).** The chained pointers are the less-critical ones; when one can't be added right now `reconcile` exits **zero**, keeps every prior fill, and **reports each skip on stdout** (the `… skipped …` line) — never silent (Hard Constraint). Three reasons: **(i)** the prior pointers fit but adding this one would push the file past the `AGENTS.md` 100-line cap; **(ii)** the installed `agent-workflow-engine` is **present but too old** to ship that fragment (`<1.2.0` lacks the recipes one; every engine before the autonomy release lacks the autonomy one) — it can still supply the earlier pointers, so only that pointer is withheld; or **(iii)** — autonomy only — its **anchor is absent** because the orchestration pair is itself absent or was skipped, so the autonomy pointer has nowhere to chain (add the orchestration pointer first, then re-run). Report it in the successful-exit report (**step 4** equal-head, else **step 8**) in plain language, e.g. *"The orchestration-recipes / autonomy pointer wasn't added — the entry point is at its 100-line limit / your methodology engine is older than that feature. The recipes stay available via `/agent-workflow-kit recipes` and the autonomy policy via `/agent-workflow-kit set-autonomy`; to add the pointer, trim the entry point and/or refresh the engine with `npx @sabaiway/agent-workflow-engine@latest init`, then re-run upgrade."* (The separate case where the **methodology** pointer ITSELF can't fit the cap is a non-zero exit that changes nothing — continue without the pointers, same plain-language framing.)
26
+ (a) **Soft, reported skip of a CHAINED pointer — orchestration and/or autonomy (CONTINUE the upgrade).** The chained pointers are the less-critical ones; when one can't be added right now `reconcile` exits **zero**, keeps every prior fill, and **reports each skip on stdout** (the `… skipped …` line) — never silent (Hard Constraint). Three reasons: **(i)** the prior pointers fit but adding this one would push the file past the `AGENTS.md` 100-line cap; **(ii)** the installed `agent-workflow-engine` is **present but too old** to ship that fragment (`<1.2.0` lacks the recipes one; every engine before the autonomy release lacks the autonomy one) — it can still supply the earlier pointers, so only that pointer is withheld; or **(iii)** — autonomy only — its **anchor is absent** because the orchestration pair is itself absent or was skipped, so the autonomy pointer has nowhere to chain (add the orchestration pointer first, then re-run). Report it in the successful-exit report (**step 4** equal-head, else **step 8**) in plain language, e.g. *"The orchestration-recipes / autonomy pointer wasn't added — the entry point is at its 100-line limit / your methodology engine is older than that feature. The recipes stay available via `/agent-workflow-kit recipes` and the autonomy policy via `/agent-workflow-kit set-autonomy`; to add the pointer, trim the entry point and/or refresh the engine with `npx @sabaiway/agent-workflow-engine@latest init`, then re-run upgrade."* (The separate case where the **methodology** pointer ITSELF can't fit the cap is a non-zero exit that changes nothing — continue without the pointers, same plain-language framing.) A skipped Memory Map row is its own clause, never a pointer skip: report it as step 4 words the rows.
27
27
 
28
28
  (b) **Malformed pair / missing-or-duplicate anchor (any pointer) — a hard STOP (do NOT continue).** A non-zero exit whose message names a marker/anchor problem; never soft-skip it. (The one deliberate exception is (a)(iii): the autonomy pointer's ABSENT anchor is the chained soft skip, exit zero — a reported skip, not this STOP.)
29
29
 
@@ -49,7 +49,7 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
49
49
 
50
50
  **What the spec-layer ensure does.** A deployment older than the feature-spec layer lacks `scripts/spec-schema.mjs` and `docs/ai/specs/index.md`, and its deployed `check-docs-size.mjs` predates the store collapse. In ONE order: the reader pair, then the checker pair — each seeded when absent and REFRESHED only while a file's bytes are a body a release shipped (an append-only digest catalog → `refreshed`; an edited body is preserved verbatim and withholds the writes that depend on it, the checker lane waiting on a byte-current reader pair); the store root is seeded from the bundled template, date rendered, ONLY behind a checker pair current after the run — an older or edited checker renders the store row by row and reds the hook's `--check-index`; edited files are replaced only on row 3's yes. One token by precedence: `seeded` > `refreshed` > `customized-preserved` (an edited pair, and this run wrote nothing) > `already-present`; `skipped-no-node-evidence` only when neither a root `package.json` nor any kit-seeded `scripts/*.mjs` is present (the same Node-evidence probe the enforcement-script ensure uses; an unreadable probe is `failed` / `node-evidence-unverifiable`). The legacy-ADR instruct never withholds it. **The adoption state this seed leaves behind is reported, never assumed:** the Recommendations section (step 4 / 8) carries the `spec-adoption` item — `not adopted` (the store is absent: the seed offered, the decline the agent records), `adopting` (a store with no live contract: the decline offered), silenced by a recorded decline (`ack-write --lane spec-adoption`), and a store the probe cannot read is a stated skip — and `status` prints the same state on its own line.
51
51
 
52
- **What the project-rules ensure does.** Each theme's kit file `docs/ai/rules/<theme>.md` is seeded, refreshed only from a body a release shipped (an edit is kept → `customized-preserved`), and with no local file its effective copy and record are rebuilt. With a local file it writes neither: `pending` prints the merge to do and its record command — do the merge, show the user what changed, then run that command.
52
+ **What the project-rules ensure does.** Each theme's kit file `docs/ai/rules/<theme>.md` is seeded, refreshed only from a body a release shipped (an edit is kept, its line saying it carries your edit), and with no local file its effective copy and record are rebuilt. With a local file it writes neither: `pending` prints the merge to do and its record command — do the merge, show the user what changed, then run that command. A kit-file line saying it carries your edit, whatever the run's token, is offered `/agent-workflow-kit rules` to move the edit into the local file.
53
53
 
54
54
  **`gates-migration` — legacy gates.json migration (consented preview — D8).** An EXISTING declaration may still carry the retired review-ledger / fold-completeness checks. Run the preview (dry-run — writes NOTHING), show the user the exact plan, and only on an explicit yes re-run it with `--apply`: canonical legacy entries (matched by their documented single-invocation cmd forms) are REMOVED, the canonical `unit-tests` cmd gains the built-in lcov reporters, and the coverage-check gate is ADDED last — atomic and COMPLETE, so the migrated declaration satisfies `run-gates --final`. **The checker rides a PRODUCER or is not declared at all** (`${CLAUDE_SKILL_DIR}/references/modes/gates.md`): with no gate producing the lcov it reads, the migration does NOT add it, an already-declared one is reported INERT, the result is not called final-run-capable, and the preview prints the paste-ready suite cmd to declare by hand — nothing is ever removed for you. CUSTOMIZED entries are NEVER auto-touched: the preview names each with a paste-ready recovery, and the commit guard must NOT be installed until they are resolved. Of step 3's run-list, only this migration and `tools/checker-gates.mjs` under the `tier` ask change an existing gates.json (the FILL preview runs at init).
55
55
 
@@ -113,7 +113,7 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
113
113
  **The LATE navigator finalizer — the AUTHORITATIVE run, after the last `docs/ai` mutation.** `lens` above rewrites `docs/ai/agent_rules.md`, so the step-3 index ensure is already behind by the time the run-list ends. Re-run that ONE op here, before the step-4 report: `node ${CLAUDE_SKILL_DIR}/tools/ensure-configs.mjs --reconcile --only index --cwd <project>`. Relay **this** line in the report (it supersedes the early one; an untouched tree changes nothing, a failure STOPs the upgrade like any other ensure). On the migrated path the same rung runs again at the END of step 7 — after the migrations, before the step-8 re-stamp.
114
114
  4. **Equal-head exit — a real successful-exit report, not a bare stop.** If the stamp **equals** the head, the lineage is up to date — but step 3 (the stamp-independent reconciles) ran first and may have changed things, so this is a proper exit report, not a no-op:
115
115
  - **A skip line that contradicts the observed tree is a FINDING, never pasted as neutral.** Every ensure skip names the fact it proved (the probes that answered absent, the layout it read); if the tree you can see disproves that reason — a "no Node" skip beside deployed Node scripts, a "not deployed" skip beside a stamped `docs/ai` — do not relay the line as an outcome: raise it in the report as a defect of the tool, with the contradicting fact named, and stop short of any step that would build on the skip.
116
- - **Report step 3's outcome in plain language** — for **each** `pointers` slot (workflow-methodology, orchestration-recipes and autonomy-policy) whether it was *added*, was *already present* (nothing changed), or was *skipped* (the soft-skip from step 3, with its reason — over the line limit / engine too old / the autonomy pointer's anchor absent); the **nine project-configuration ensure** (`configs`) lines exactly as the one ensure run composed them (the ensures and their outcome tokens are enumerated in step 3), each rendered in plain language: what was created, what was left exactly as the user wrote it, and — for a `failed` line — what stopped it; for the navigator, relay the **late** `--only index` line, not the early one; the **`gates-migration`** result — *nothing to migrate*, the shown plan *applied* on your explicit yes, or the plan *left unapplied* (consent not given), with any INERT checker or CUSTOMIZED entries named; the **placed-bridge refresh** (`bridges`) outcome — paste the tool's per-bridge lines verbatim (they are already plain: *refreshed* / *already current* / *skipped — not placed* / `skipped-readonly` with its re-scan verdict / *could not refresh* + recovery); the **agent-rules lens** (`lens`) outcome (*refreshed* / *already current* / *custom edit preserved + note* / *file absent* / *engine too old* / *over the line cap*) and the **Communication-section** outcome (its own set: refreshed / already current / custom preserved + note / section absent — noted / over the cap — refused) and the **story-sessions section** outcome (its own set: refreshed / already current / custom edit preserved + note / section absent — noted / over the file's line cap — refused); the **full-flow offer** (`tier`) lines; the **bridge-settings reconcile** (`bridge-settings`) outcome (paste the tool's line(s) verbatim); and, for a hidden deployment, whether the hidden-mode footprint (`footprint`) was *moved to project-local*, was *already project-local* (nothing changed), or needed a question (ambiguous visibility). Plain wording only — never the reconcile/slot/anchor/marker terms (the never-leak-kit-internals Gotcha — `${CLAUDE_SKILL_DIR}/references/shared/deploy-tail.md`).
116
+ - **Report step 3's outcome in plain language** — for **each** `pointers` slot (workflow-methodology, orchestration-recipes and autonomy-policy) whether it was *added*, was *already present* (nothing changed), or was *skipped* (the soft-skip from step 3, with its reason — over the line limit / engine too old / the autonomy pointer's anchor absent), and each Memory Map row the same way (or *refreshed*); the **nine project-configuration ensure** (`configs`) lines exactly as the one ensure run composed them (the ensures and their outcome tokens are enumerated in step 3), each rendered in plain language: what was created, what was left exactly as the user wrote it, and — for a `failed` line — what stopped it; for the navigator, relay the **late** `--only index` line, not the early one; the **`gates-migration`** result — *nothing to migrate*, the shown plan *applied* on your explicit yes, or the plan *left unapplied* (consent not given), with any INERT checker or CUSTOMIZED entries named; the **placed-bridge refresh** (`bridges`) outcome — paste the tool's per-bridge lines verbatim (they are already plain: *refreshed* / *already current* / *skipped — not placed* / `skipped-readonly` with its re-scan verdict / *could not refresh* + recovery); the **agent-rules lens** (`lens`) outcome (*refreshed* / *already current* / *custom edit preserved + note* / *file absent* / *engine too old* / *over the line cap*) and the **Communication-section** outcome (its own set: refreshed / already current / custom preserved + note / section absent — noted / over the cap — refused) and the **story-sessions section** outcome (its own set: refreshed / already current / custom edit preserved + note / section absent — noted / over the file's line cap — refused); the **full-flow offer** (`tier`) lines; the **bridge-settings reconcile** (`bridge-settings`) outcome (paste the tool's line(s) verbatim); and, for a hidden deployment, whether the hidden-mode footprint (`footprint`) was *moved to project-local*, was *already project-local* (nothing changed), or needed a question (ambiguous visibility). Plain wording only — never the reconcile/slot/anchor/marker terms (the never-leak-kit-internals Gotcha — `${CLAUDE_SKILL_DIR}/references/shared/deploy-tail.md`).
117
117
  - **Never surface the structure number on this exit.** Whatever step 3 did, do **not** recite the `docs/ai` structure version, the internal versioning vocabulary, or the two-axes note here — the number is inert on an equal-head exit; it belongs to *Version disclosure* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md` (shown at the never-downgrade STOP, the explicit status view, or on an explicit ask). Frame the success itself per the final bullet: if step 3 changed anything, say **what changed** in plain human terms; only a pure zero-diff no-op is *settings already current — no update needed*.
118
118
  - **Render the mandatory Recommendations section — on this exit too, BEFORE the footer.** Run `node ${CLAUDE_SKILL_DIR}/tools/recommendations.mjs --cwd <project-root>` and PRESENT its output — from the `## Recommendations (agent-workflow)` header — in the user's conversational language: every fact, count and item from the tool, nothing added or dropped; commands, paths, hosts and rule strings byte-exact; show the raw tool block on request. The section is present-even-when-empty (with everything optimal the body is exactly `no recommendations — flow optimal.`) and VERDICT-FIRST — the composed verdict line renders from the frozen templates `{K} item(s) need attention` / `nothing is broken` / `{N} optional recommendation(s), apply any you want` / `optimality NOT attested — {M} probe check(s) skipped`. Then OFFER the consent route: the user picks items in plain language; surface each picked item's posture note and get the explicit confirm; run its lane's rendered lines — chat: the one-liner; harness: the check line, then on the yes to its result the apply line; user: the user's own step — per the full consent route in `${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`. Pinned order on this exit: Recommendations block → optional applies → report footer → the commit ask (the advisor/apply lane never lands after the commit ask).
119
119
  - **Recommendations lanes — both exits.** The three lanes (`chat`, `harness`, `user`) and the consent route live in `${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`; `init` stays a route the user may take.
@@ -123,4 +123,4 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
123
123
  5. Show the relevant `${CLAUDE_SKILL_DIR}/CHANGELOG.md` diff (entries newer than the project's stamp).
124
124
  6. **Collect the migration answers FIRST, then apply.** Run `node ${CLAUDE_SKILL_DIR}/tools/migration-notes.mjs --cwd <project>` and relay its stdout lines verbatim, in order; a non-zero exit relays its one named line and STOPs, except `stamp-absent`, which routes back to step 1. Then run the preview `node ${CLAUDE_SKILL_DIR}/tools/migration-blocks.mjs --cwd <project>`. If its lines say both blocks are missing (pre-1.1.0), ask the two questions as ONE structured multi-question prompt; record each answer individually, write nothing until ALL are answered, and carry the answers into the migrations below: a migration whose answer was already collected never re-asks; a single missing block keeps its single ask (step 7). Then apply exactly the notes the tool listed, in its order ("only those newer than the project's stamp" is why, never a second selection). Migrations are **idempotent** — safe to re-run.
125
125
  7. Reconcile drift: add any kernel files/scripts the project is missing; never clobber project-authored content (their `decisions.md`, `known_issues.md`, page specs stay). Any user question a migration raises follows the same rule as bootstrap — **structured multiple-choice where supported** (`AskUserQuestion` in Claude Code), otherwise prose. For a block the preview names as needing its answer, **ask the user their conversational language** (`--language`) or **whether the agent may attribute work to itself / AI** (`--attribution`, defaulting to `off`), never re-asking a step-6 answer; then re-run the preview with `--apply` and the answers, relaying its lines. Exit 1 `answer-missing`: ask exactly what its lines name and re-run `--apply` once; a second one STOPs. Any other exit 1 is a named refusal: relay it with its recovery (`cap`: trim `AGENTS.md`, re-run) and STOP before the finalizer and the stamp. **Then, before the stamp, re-run the LATE navigator finalizer:** the migrations of steps 6–7 may have changed `docs/ai` files, so run the one op again — `node ${CLAUDE_SKILL_DIR}/tools/ensure-configs.mjs --reconcile --only index --cwd <project>` — and relay THAT line in the step-8 report (idempotent: an untouched tree changes nothing; a failure STOPs this upgrade like any other ensure).
126
- 8. Re-stamp `docs/ai/.workflow-version` to the **deployment-lineage head** (`4.0.0`, not the package version — mechanics unchanged: the atomic write to the stamp file). In the report, **describe what the upgrade changed in plain human terms** — which parts of their `docs/ai` are now different (the migrations that ran), plus the step-3 run-list outcomes, each as step 4 words it: the `pointers` slots, the `footprint` visibility verdict (and what a consented re-run migrated), the **nine project-configuration ensure** (`configs`) lines (enumerated in step 3) — the navigator one being the late `--only index` run at the end of step 7, the **`gates-migration`** result, the **placed-bridge refresh** (`bridges`) lines (pasted verbatim), the **agent-rules lens** (`lens`) + **Communication-section** + **story-sessions section** outcomes (same outcome sets as step 4), the **full-flow offer** (`tier`) outcome, and the **bridge-settings reconcile** (`bridge-settings`) outcome (line(s) pasted verbatim) — rather than reciting a version number; **omit the raw structure number**, and do **not** print the two-axes note here (it belongs to *Version disclosure* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`, on demand only). Then **render the mandatory Recommendations section**: run `node ${CLAUDE_SKILL_DIR}/tools/recommendations.mjs --cwd <project-root>` and PRESENT its output — from the `## Recommendations (agent-workflow)` header — in the user's conversational language (every fact, count and item, nothing added or dropped; commands, paths, hosts and rule strings byte-exact; raw tool block on request; present-even-when-empty: `no recommendations — flow optimal.`), then OFFER the consent route (per picked item: posture note → explicit confirm → chat: the one-liner; harness: the check line, then on the yes to its result the apply line; user: the user's own step — `${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`). **Every current host/session claim in this report is tool-composed only** — prompts fired, sandbox scope, whether a bypass was needed, network reachability and approval counts must trace to **live tool output** from **this session**, a memory/handover snapshot is **context, never report facts**, and an unbacked claim is **omitted or explicitly marked unverified** (full clause: *Live host/session facts* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`). Then **print the report footer** in the canonical order (version block → one-line backend-status line → welcome mat — the shared contracts in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`; rendered from the helpers, same host-can't-run skip-with-reason; the welcome mat closes on one caveat-aware next step). Then **ask before committing** — the pinned order on this exit is: Recommendations block → optional applies → report footer → the commit ask.
126
+ 8. Re-stamp `docs/ai/.workflow-version` to the **deployment-lineage head** (`4.0.0`, not the package version — mechanics unchanged: the atomic write to the stamp file). In the report, **describe what the upgrade changed in plain human terms** — which parts of their `docs/ai` are now different (the migrations that ran), plus the step-3 run-list outcomes, each as step 4 words it: the `pointers` slots and Memory Map rows, the `footprint` visibility verdict (and what a consented re-run migrated), the **nine project-configuration ensure** (`configs`) lines (enumerated in step 3) — the navigator one being the late `--only index` run at the end of step 7, the **`gates-migration`** result, the **placed-bridge refresh** (`bridges`) lines (pasted verbatim), the **agent-rules lens** (`lens`) + **Communication-section** + **story-sessions section** outcomes (same outcome sets as step 4), the **full-flow offer** (`tier`) outcome, and the **bridge-settings reconcile** (`bridge-settings`) outcome (line(s) pasted verbatim) — rather than reciting a version number; **omit the raw structure number**, and do **not** print the two-axes note here (it belongs to *Version disclosure* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`, on demand only). Then **render the mandatory Recommendations section**: run `node ${CLAUDE_SKILL_DIR}/tools/recommendations.mjs --cwd <project-root>` and PRESENT its output — from the `## Recommendations (agent-workflow)` header — in the user's conversational language (every fact, count and item, nothing added or dropped; commands, paths, hosts and rule strings byte-exact; raw tool block on request; present-even-when-empty: `no recommendations — flow optimal.`), then OFFER the consent route (per picked item: posture note → explicit confirm → chat: the one-liner; harness: the check line, then on the yes to its result the apply line; user: the user's own step — `${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`). **Every current host/session claim in this report is tool-composed only** — prompts fired, sandbox scope, whether a bypass was needed, network reachability and approval counts must trace to **live tool output** from **this session**, a memory/handover snapshot is **context, never report facts**, and an unbacked claim is **omitted or explicitly marked unverified** (full clause: *Live host/session facts* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`). Then **print the report footer** in the canonical order (version block → one-line backend-status line → welcome mat — the shared contracts in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`; rendered from the helpers, same host-can't-run skip-with-reason; the welcome mat closes on one caveat-aware next step). Then **ask before committing** — the pinned order on this exit is: Recommendations block → optional applies → report footer → the commit ask.
@@ -31,8 +31,8 @@ installed `agent-workflow-engine`** (`references/methodology-slot.md` + `referen
31
31
  + `references/autonomy-slot.md`,
32
32
  the family's one source of truth) — **not** a bundled mirror, and **not** the full references. The
33
33
  live read is **lazy + fail-loud**: the engine is consulted **only when a slot actually needs filling**,
34
- so a deployment with **all three** pointers filled reconciles to a zero-diff no-op even on a host
35
- without the engine (one still missing the autonomy pair needs the engine to gain it); when a fill
34
+ so a deployment with **all three** pointers filled and its Memory Map rows present reconciles to a
35
+ zero-diff no-op even on a host without the engine (one still missing the autonomy pair needs the engine to gain it); when a fill
36
36
  **is** needed and the engine is **absent/invalid**, reconcile
37
37
  **STOPs** — report it in plain language with the one-line install command
38
38
  `npx @sabaiway/agent-workflow-engine@latest init` (`npx @sabaiway/agent-workflow-kit@latest init`
@@ -9,28 +9,13 @@ maxLines: 350
9
9
 
10
10
  # Architecture
11
11
 
12
- > How the code is organized. Layers, boundaries, where new code goes.
12
+ > How the code is organized: where each module lives and where new code goes.
13
13
 
14
- ## Layers
14
+ ## Layout rules
15
15
 
16
- ```
17
- UI Components ← {{src/components/}}
18
- ↓
19
- Hooks / Composition ← {{src/hooks/}}
20
- ↓
21
- Services (pure) ← {{src/services/}}
22
- ↓
23
- State / Storage ← {{src/store/ or src/data/}}
24
- ↓
25
- Types / Schemas ← {{src/types/}}
26
- ```
27
-
28
- ## Module Boundaries
29
-
30
- - **Components** — render only. No fetching, no business logic.
31
- - **Hooks** — orchestration; combine services + state.
32
- - **Services** — pure functions. Easy to test.
33
- - **State** — single source of truth per domain.
16
+ The code is laid out by the project-layout rules: modules nest by meaning, and a name says where
17
+ code, tests and specs live. In a kit deployment those rules live in
18
+ `docs/ai/rules/effective/project-layout.md`, laid down by the kit's upgrade.
34
19
 
35
20
  ## File Tree (top level)
36
21
 
@@ -196,6 +196,13 @@ const CATALOG = [
196
196
  kind: GUARDED,
197
197
  oneLine: 'Register the kit’s read-only MCP server in this project, so path questions and literal searches run as typed tool calls instead of shell command strings — previews the exact entry it would declare, writes only on your yes, and where a sandbox hides the file it writes nothing and names the advisor’s mcp-channel item, which applies it by the consent route.',
198
198
  },
199
+ {
200
+ key: 'rules',
201
+ invocation: invocationOf('rules'),
202
+ group: 'Configure',
203
+ kind: WRITER,
204
+ oneLine: 'Change your own project rules, such as how files and modules are laid out: your rules go into your local file, which wins over the kit’s defaults; the exact text is shown first and written only on your yes.',
205
+ },
199
206
  {
200
207
  key: 'recipes',
201
208
  invocation: invocationOf('recipes'),
@@ -6,10 +6,9 @@
6
6
  // 2. workflow:orchestration — the recipe-vocabulary pointer (references/orchestration-slot.md).
7
7
  // 3. workflow:autonomy — the autonomy-policy read contract (references/autonomy-slot.md).
8
8
  //
9
- // All share ONE generic marker engine (a slot DESCRIPTOR parameterizes the markers / anchor /
10
- // empty-slot / leading-blank). The methodology exports (findSlot / injectMethodology / ensureSlot /
11
- // reconcileSlot / slotNeedsFill / extractSlot) delegate to it byte-for-byte, so the methodology
12
- // contract is unchanged; the later slots are the SAME engine with different descriptors.
9
+ // All share ONE generic marker engine (a slot DESCRIPTOR parameterizes the markers / anchor / empty-slot
10
+ // / leading-blank); the methodology exports delegate to it byte-for-byte. After the three slots,
11
+ // reconcile keeps one Memory Map row per project-rules theme (project-rules/memory-map-row.mjs).
13
12
  //
14
13
  // Contract per slot, strictly enforced:
15
14
  // exactly one ordered start→end pair → replace only the bytes between them;
@@ -17,17 +16,15 @@
17
16
  // any malformed state (single, reversed, nested, duplicate) → NO-OP WITH AN ERROR, never edit.
18
17
  // The live read is lazy + fail-loud: resolve+read the engine ONLY when a fill is actually needed, and
19
18
  // STOP loudly (never a silent fallback) when the engine is needed but absent/invalid.
20
- //
21
19
  // Pure string functions (testable with byte-preservation fixtures); dependency-free, Node >= 22.
22
20
  //
23
- // Canonical-refresh (AD-025): a slot filled BEFORE a clause existed is normally preserved verbatim
24
- // (reconcile only fills an EMPTY slot). To push a NEW canonical clause to the EXISTING filled base
25
- // without clobbering a user's customization, reconcileMarkerSlot also REFRESHES a filled slot — but
26
- // ONLY when its content normalize-matches a KNOWN PRIOR canonical fragment (drift-guarded, append-only
27
- // per descriptor). A customized slot never matches → preserved verbatim + a read-only upgrade advisory.
21
+ // Canonical-refresh (AD-025): reconcileMarkerSlot REFRESHES a filled slot ONLY when its content
22
+ // normalize-matches a KNOWN PRIOR canonical fragment (drift-guarded, append-only per descriptor); a
23
+ // customized slot never matches → preserved verbatim + a read-only upgrade advisory.
28
24
 
29
25
  import { isDirectRun } from './direct-run.mjs';
30
26
  import { normalizeCanonical } from './orchestration-config.mjs';
27
+ import { reconcileMemoryMapRows } from './project-rules/index.mjs';
31
28
 
32
29
  export const START_MARKER = '<!-- workflow:methodology:start -->';
33
30
  export const END_MARKER = '<!-- workflow:methodology:end -->';
@@ -326,6 +323,7 @@ export const isFillCapRefusal = (errorMessage) =>
326
323
  isCapRefusal(errorMessage) && errorMessage.includes('injection would push');
327
324
 
328
325
  const EXIT = Symbol('inject-methodology.exit');
326
+ const ROW_WORDS = { inserted: 'added', kept: 'already present', refreshed: 'refreshed to the current text' };
329
327
 
330
328
  // The return-code entry point (5.1): argv[] + injected env/home → { code, stdout, stderr }, no
331
329
  // process.argv / process.exit / console inside — an in-process caller is as hermetic as a spawned
@@ -574,16 +572,18 @@ export const runCli = async (argv, deps = {}) => {
574
572
  }
575
573
  }
576
574
 
577
- // ── One atomic write of the final (three-slot) text ──
578
- if (finalText === text) {
575
+ // ── The Memory Map rows on the three-slot text, then one atomic write ──
576
+ const rows = reconcileMemoryMapRows(finalText, { maxLines: AGENTS_MD_CAP });
577
+ const describeRows = rows.outcomes.map((o) => `the ${o.theme} Memory Map row ${ROW_WORDS[o.status] ?? `skipped — ${o.note}`}`).join('; ');
578
+ if (rows.text === text) {
579
579
  // Byte-unchanged. Still report a skip (it is not "nothing to do" — a pointer was withheld).
580
- if (orchSkipped || autSkipped) log(`[inject-methodology] reconcile: ${describeMeth}; ${describeOrch}; ${describeAut}.`);
581
- else log('[inject-methodology] reconcile: all three pointers already present and filled — nothing to do (zero-diff).');
580
+ if (orchSkipped || autSkipped) log(`[inject-methodology] reconcile: ${describeMeth}; ${describeOrch}; ${describeAut}; ${describeRows}.`);
581
+ else log(`[inject-methodology] reconcile: all three pointers already present and filled; ${describeRows} — nothing to do (zero-diff).`);
582
582
  reportNotes();
583
583
  return result(0);
584
584
  }
585
- await writeAtomic(finalText);
586
- log(`[inject-methodology] reconcile: ${describeMeth}; ${describeOrch}; ${describeAut}.`);
585
+ await writeAtomic(rows.text);
586
+ log(`[inject-methodology] reconcile: ${describeMeth}; ${describeOrch}; ${describeAut}; ${describeRows}.`);
587
587
  reportNotes();
588
588
  return result(0);
589
589
  }
@@ -2,3 +2,4 @@
2
2
  export { THEMES } from './themes.mjs';
3
3
  export { checkRules } from './rules-check.mjs';
4
4
  export { ensureRules } from './rules-ensure.mjs';
5
+ export { reconcileMemoryMapRows } from './memory-map-row.mjs';
@@ -0,0 +1,62 @@
1
+ // The Memory Map row of each theme in a deployed AGENTS.md: a pure text leaf that reads and writes
2
+ // nothing; inject-methodology.mjs reconcile calls it and stays the file's one writer.
3
+ // Contract: docs/ai/specs/kit/project-rules/memory-map-row.md.
4
+
5
+ import { THEMES, themePaths } from './themes.mjs';
6
+
7
+ // The row texts a release shipped, append-only: a release that changes a row's text first appends
8
+ // the outgoing one here.
9
+ export const MEMORY_MAP_PRIOR_ROWS = Object.freeze([]);
10
+
11
+ const ANCHOR_PATH = 'docs/ai/agent_rules.md';
12
+ const opening = (path) => `| [\`${path}\`](./${path}) |`;
13
+ const rowOf = (theme) => {
14
+ const { effective } = themePaths(theme.name);
15
+ return `${opening(effective)} ${theme.readWhen} | Only through \`/agent-workflow-kit rules\` |`;
16
+ };
17
+
18
+ // The lines of a text, each keeping its own ending, so joining them gives the text back byte for byte.
19
+ const splitLines = (text) => text.match(/[^\n]*\n|[^\n]+$/g) ?? [];
20
+ const bodyOf = (line) => line.replace(/\r?\n$/, '');
21
+ const endingOf = (line) => line.slice(bodyOf(line).length);
22
+ // A table line that does not close its last cell goes on over the next line.
23
+ const isWhole = (line) => bodyOf(line).trimEnd().endsWith('|');
24
+
25
+ // placeAfter(lines, i, row) → the lines with row on the line after i, taking line i's ending.
26
+ const placeAfter = (lines, i, row) => {
27
+ const ending = endingOf(lines[i]);
28
+ if (ending !== '') return lines.toSpliced(i + 1, 0, `${row}${ending}`);
29
+ return lines.toSpliced(i, 1, `${lines[i]}\n`, row);
30
+ };
31
+
32
+ // reconcileMemoryMapRows(text, { maxLines, priors }) → { text, outcomes }: one outcome
33
+ // { theme, status, note } per registry theme in registry order, status inserted, kept, refreshed or
34
+ // skipped; a skipped theme changes no byte and its note names the reason.
35
+ export const reconcileMemoryMapRows = (text, { maxLines, priors = MEMORY_MAP_PRIOR_ROWS } = {}) => {
36
+ let lines = splitLines(text);
37
+ let last = -1;
38
+ const outcomes = [];
39
+ for (const theme of THEMES) {
40
+ const row = rowOf(theme);
41
+ const answer = (status, note = '') => outcomes.push({ theme: theme.name, status, note });
42
+ const at = lines.findIndex((line) => line.startsWith(opening(themePaths(theme.name).effective)));
43
+ if (at !== -1) {
44
+ if (priors.includes(bodyOf(lines[at]))) {
45
+ lines = lines.with(at, `${row}${endingOf(lines[at])}`);
46
+ answer('refreshed');
47
+ } else answer('kept');
48
+ continue;
49
+ }
50
+ const anchor = last !== -1 ? last : lines.findIndex((line) => line.startsWith(opening(ANCHOR_PATH)));
51
+ if (anchor === -1) answer('skipped', `the Memory Map has no ${ANCHOR_PATH} row to place it after`);
52
+ else if (!isWhole(lines[anchor])) answer('skipped', `the Memory Map's ${ANCHOR_PATH} row spans more than one line`);
53
+ else if (maxLines != null && lines.length + 1 > maxLines) {
54
+ answer('skipped', `adding it would take the file past its ${maxLines}-line limit`);
55
+ } else {
56
+ lines = placeAfter(lines, anchor, row);
57
+ last = anchor + 1;
58
+ answer('inserted');
59
+ }
60
+ }
61
+ return { text: lines.join(''), outcomes };
62
+ };