@sabaiway/agent-workflow-kit 1.13.0 → 1.15.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,71 @@ 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
+ ## 1.15.0 — Velocity-profile onboarding (kit)
8
+
9
+ An opt-in **`/agent-workflow-kit velocity`** mode seeds a fixed, audited **read-only** Claude Code
10
+ allowlist into `.claude/settings.json` so an agent stops idling on approval prompts for routine
11
+ read-only commands while the maintainer is away. It never allowlists `commit`/`push`/`publish`, so a
12
+ direct invocation still ASKs — the only caveat is the trust-posture residual (below), closed by a
13
+ deferred hook.
14
+
15
+ ### Added
16
+ - **`tools/velocity-profile.mjs`** — the pure core (a frozen 18-entry `UNIVERSAL_READONLY_ALLOWLIST`,
17
+ the `screenAllowlistEntry` read-only screen, the read-only `discoverGateCandidates` gate advisor, and
18
+ the `validateProfile` drift guard) **plus** the programmatic settings writer + CLI
19
+ (`[--dry-run | --apply] [--accept-edits] [--cwd <dir>]`). Strict **preflight-then-mutate**:
20
+ merge-don't-clobber, opt-in `acceptEdits`; refuses an unsafe `permissions.defaultMode`
21
+ (`bypassPermissions` / any non-`{default,acceptEdits,plan}` mode present in **either** settings file),
22
+ a symlinked `.claude`, malformed settings JSON, or a non-current deployment stamp on `--apply`. Writes
23
+ **only** `.claude/settings.json`, never `settings.local.json`.
24
+ - A **`### Mode: velocity`** section + a `## Modes` dispatch entry + a one-line opt-in bootstrap offer
25
+ in `SKILL.md`.
26
+ - The guarded **`uninstall`** now also reports `permissions.defaultMode`/`permissions.allow` in
27
+ `.claude/settings.json` **non-committally** (REPORT_ONLY, never auto-removed — the writer stores no
28
+ ownership marker).
29
+
30
+ ### Honesty
31
+ - This is the family's **first programmatic `.claude/settings.json` writer** — a new writer subsystem
32
+ with its own tests and teardown reporting, **not** merely an extension of the attribution prose seam.
33
+ - The audited core is **read-only by intent — verified, not assumed** (no mutating command, no inline
34
+ code execution): build-time probes proved `git grep` (`--open-files-in-pager`) and `sort`
35
+ (`--compress-program`) give inline code execution; both were dropped (the core is 18, not 20).
36
+ `git diff`/`log`/`show` are kept with a documented bounded-write (`--output`) residual.
37
+ - A seeded read-only allow entry is a **trust posture, not a sandbox**: Claude Code's settings-level
38
+ allow rules do not inspect output redirection (`cmd > file`) nor command substitution (`cmd $(…)`),
39
+ so that residual is surfaced honestly in the consent copy, bounded by `acceptEdits` staying opt-in,
40
+ and **fully closed only by a deferred PreToolUse hook** (a recorded follow-up). `commit`/`push`/
41
+ `publish` are never added as allow rules.
42
+
43
+ Lineage head stays **1.3.0** (no `docs/ai` structural change; no migration). See AD-021.
44
+
45
+ ## 1.14.0 — Activity procedures: recipe-aware, configurable playbooks
46
+
47
+ A new read-only **`/agent-workflow-kit procedures <activity>`** advisor turns a bare command like
48
+ "write a plan" into a codified, recipe-aware procedure. It reads the named activity's ordered steps
49
+ **live** from the installed engine (`references/procedures.md`) and prints them verbatim, then resolves
50
+ the **effective recipe per slot** from a new per-project, hand-edited config and the read-only backend
51
+ detector. Two v1 activities: **`plan-authoring`** (slot: `review`) and **`plan-execution`** (slots:
52
+ `execute`, `review`). It composes with the AD-018 recipes; **`recipes` stays read-only** (the config is
53
+ hand-edited, never written by the kit).
54
+
55
+ ### Added
56
+ - **`tools/procedures.mjs`** — the read-only CLI: live engine read + per-activity section extraction,
57
+ config IO + validation, and the resolved recipe per slot (default = Reviewed when a backend is ready,
58
+ Council on request, slot-aware incl. Delegated). A repeatable `--override <slot>=<recipe>` adjusts one
59
+ slot per run. Exit codes: `0` success (an unsatisfiable override degrades **loudly** but still `0`),
60
+ `2` usage (unknown activity / bad `--override`), `1` config or engine error (loud `path: reason`).
61
+ - **`docs/ai/orchestration.json`** — the per-project, strict-JSON config (`{ activity: { slot: recipe } }`;
62
+ all slots optional; an optional `"_README"` is allowed + ignored). Hand-edited; kit-validated.
63
+ - **`resolveActivityRecipe` / `ACTIVITIES` / `SLOT_RECIPES`** in `tools/recipes.mjs` — the pure resolver
64
+ (graceful default vs loud override degradation), drift-guarded against the engine canon's `Slots:`
65
+ lines. `planRecipe` / `recommendRecipe` are unchanged.
66
+ - A **`workflow:methodology`** pointer clause routing to `/agent-workflow-kit procedures <activity>`
67
+ (the feature's only auto-discovery route — both engine + kit are `disable-model-invocation`).
68
+
69
+ The deployment-lineage head stays **`1.3.0`** (no `docs/ai` structural change; no migration file). See
70
+ **AD-019**.
71
+
7
72
  ## 1.13.0 — Orchestration recipes: a named way to compose the bridges
8
73
 
9
74
  The kit now knows **how to put the optional execution-backends to work**, not just whether they're set
package/README.md CHANGED
@@ -222,7 +222,9 @@ command is printed).
222
222
  | `/agent-workflow-kit setup [backend]` | opt-in, any time | **link-only** auto-setup of a bridge: places the bundled bridge skill (only into an absent / empty / managed dir — never overwrites an unmanaged one) + links its wrappers onto `PATH` via managed symlinks (idempotent; refuses to clobber a non-symlink; try `--dry-run` to preview). The binary install + the one-time subscription login stay **manual**: it prints the exact **login** command and points the binary install at each bridge's `setup/README.md`. POSIX wrappers — on Windows use WSL. Never commits, never runs a subscription CLI. |
223
223
  | `/agent-workflow-kit status` | any time | **read-only** view of the whole family: which members (kit / memory / engine / the two bridges) are installed and at what version, and — with a project — what's deployed (`docs/ai`, the version stamps, and whether the AI files are git-ignored for hidden mode). Never writes, never commits, never runs a subscription CLI. |
224
224
  | `/agent-workflow-kit recipes` | any time | **read-only** orchestration advisor: presents four named recipes for composing the bridges into plan → execute → review — **Solo / Reviewed / Council / Delegated** — plans + recommends one for your environment (degrading with a stated reason when a backend isn't ready), and offers the choice. The orchestrator runs it via the bridge skills and **always commits**; the kit never executes a recipe, never runs a subscription CLI, never commits. |
225
- | `/agent-workflow-kit uninstall` | opt-in, any time | **guarded teardown** the inverse of `init` / `setup`. Removes only what's **provably ours** (managed skill dirs + bridge wrappers; in a project, the hidden-mode git-ignore block it added + the pre-commit hook it installed); **never deletes** your `docs/ai` / `AGENTS.md` / settings for those it prints the exact `rm` commands to run by hand. Always `--dry-run` first; preflight-then-mutate; never commits. |
225
+ | `/agent-workflow-kit procedures <activity>` | any time | **read-only** activity-procedures advisor: prints a named activity's ordered steps (`plan-authoring` / `plan-execution`) read **live** from the engine, plus the **resolved recipe per slot** from your hand-edited `docs/ai/orchestration.json` + backend readiness (default Reviewed when a backend is ready, Council on request, slot-aware incl. Delegated). `--override <slot>=<recipe>` adjusts one slot per run. Composes with `recipes`; never writes, never commits, never runs a subscription CLI. |
226
+ | `/agent-workflow-kit uninstall` | opt-in, any time | **guarded teardown** — the inverse of `init` / `setup`. Removes only what's **provably ours** (managed skill dirs + bridge wrappers; in a project, the hidden-mode git-ignore block it added + the pre-commit hook it installed); **never deletes** your `docs/ai` / `AGENTS.md` (prints the exact `rm` to run by hand) or your `.claude/settings.json` (prints an **edit** — remove the attribution key, review any velocity `permissions.*` — never an `rm`). Always `--dry-run` first; preflight-then-mutate; never commits. |
227
+ | `/agent-workflow-kit velocity` | Claude Code · opt-in | **onboarding velocity profile** — seeds a fixed, audited **read-only** allowlist into `.claude/settings.json` so routine read-only commands stop idling on approval prompts while you're away; opt-in `acceptEdits`; plus a **read-only advisory** of likely project gate commands to add by hand. Writes **only** `.claude/settings.json` — **never** allowlists commit/push/publish, never writes `settings.local.json`, never commits. A seeded entry is a **trust posture, not a sandbox** (a redirection/command-substitution residual remains, closed by a deferred hook); a direct commit/push/publish still asks. `--dry-run` first. |
226
228
 
227
229
  It **never auto-commits** and **never overwrites** an existing `AGENTS.md` without asking.
228
230
 
@@ -311,6 +313,7 @@ agent-workflow-kit/
311
313
  │ ├── engine-source.mjs ← live engine fragment read (fail-loud)
312
314
  │ ├── detect-backends.mjs ← read-only backend detector
313
315
  │ ├── recipes.mjs ← read-only recipe planner (recipes)
316
+ │ ├── procedures.mjs ← activity-procedures advisor (procedures)
314
317
  │ ├── setup-backends.mjs ← link-only backend setup
315
318
  │ ├── fs-safe.mjs ← symlink-safe copy/link/remove/unlink
316
319
  │ ├── family-registry.mjs ← unified family registry (status)
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".
4
4
  disable-model-invocation: true
5
5
  metadata:
6
- version: '1.13.0'
6
+ version: '1.15.0'
7
7
  ---
8
8
 
9
9
  # agent-workflow-kit
@@ -104,7 +104,9 @@ Pick the mode from the user's invocation. Auto-detect an existing `docs/ai/` to
104
104
  - **`/agent-workflow-kit setup [backend]`** — the **link-only**, opt-in companion to `backends`: place the bundled bridge skill + link its wrappers onto `PATH`. **In-agent only** — `init` (npx) never places bridges. The binary install + the interactive subscription login stay **manual** (it prints the exact commands); idempotent; refuses to clobber a non-symlink; never commits, never runs a subscription CLI.
105
105
  - **`/agent-workflow-kit status`** — read-only view of the **whole family**: which members (kit / memory / engine / the two bridges) are installed, at what version, and — in a project — what is deployed (`docs/ai`, the version stamps, the hidden-mode fence). Never writes, never commits, never runs a subscription CLI.
106
106
  - **`/agent-workflow-kit recipes`** — read-only **orchestration advisor**: present the four recipes (Solo / Reviewed / Council / Delegated) over the bridges' role vocabulary, plan + recommend one for the current environment, and offer the choice. **The orchestrator executes the chosen recipe via the bridge skills and always commits** — the kit only surfaces/selects/plans it; it never executes a recipe, never runs a subscription CLI, never commits.
107
- - **`/agent-workflow-kit uninstall`**the **guarded teardown** companion to `init`/`setup`. Removes what they placed installed skill dirs + the bridge wrappers and, in a project, reverses the hidden-mode fence + the marker pre-commit hook. **Never deletes user-authored content** (`docs/ai`, `AGENTS.md`, `.claude/settings.json`): it prints the exact commands for you to run by hand. `--dry-run` first, always; preflight-then-mutate; never commits.
107
+ - **`/agent-workflow-kit procedures <activity>`** read-only **activity-procedures advisor**: print the ordered steps of a named activity (`plan-authoring` / `plan-execution`) read **live** from the installed engine (`references/procedures.md`), and the **resolved effective recipe per slot** from the per-project `docs/ai/orchestration.json` (hand-edited, strict JSON) + backend readiness (default = Reviewed when a backend is ready, Council on request, slot-aware incl. Delegated; graceful default vs loud override degradation). A per-run `--override <slot>=<recipe>` overrides one slot. Composes with `recipes` (which stays read-only); never writes, never commits, never runs a subscription CLI.
108
+ - **`/agent-workflow-kit uninstall`** — the **guarded teardown** companion to `init`/`setup`. Removes what they placed — installed skill dirs + the bridge wrappers — and, in a project, reverses the hidden-mode fence + the marker pre-commit hook. **Never deletes user-authored content**: it prints the exact `rm` for `docs/ai` / `AGENTS.md` and an **edit** for `.claude/settings.json` (the `includeCoAuthoredBy` key + any velocity `permissions.*`), for you to run by hand. `--dry-run` first, always; preflight-then-mutate; never commits.
109
+ - **`/agent-workflow-kit velocity`** — opt-in onboarding **velocity profile**: seed a fixed, audited **read-only** Claude Code allowlist into `.claude/settings.json` so routine read-only commands stop idling on approval prompts; opt-in `acceptEdits`; plus a **read-only advisory** of likely project gate commands to add by hand. **Writes only `.claude/settings.json`, never allowlists commit/push/publish, never writes `settings.local.json`, never commits.** `--dry-run` first.
108
110
 
109
111
  ### Version status & the two axes — surface this on every invocation
110
112
 
@@ -112,7 +114,7 @@ Before acting, read `docs/ai/.workflow-version` (the project's stamp), state a o
112
114
 
113
115
  - **absent** → bootstrap (a fresh deployment).
114
116
  - **stamp < `1.3.0`** (the deployment-lineage head) → `upgrade`.
115
- - **stamp == `1.3.0`** → already current; only the stamp-independent reconciles may run (the methodology slot **and** the hidden-mode footprint, *Mode: upgrade* step 3).
117
+ - **stamp == `1.3.0`** → already current; only the stamp-independent reconciles may run (the methodology slot, the hidden-mode footprint, **and** the `docs/ai/orchestration.json` config ensure, *Mode: upgrade* step 3).
116
118
  - **stamp > head / unparseable** → STOP — never-downgrade gate (see *Mode: upgrade* step 2).
117
119
 
118
120
  **Two independent version axes — never conflate them:**
@@ -166,7 +168,7 @@ readiness, then **append an actionable recipe recommendation** from the recipe p
166
168
  3. **Choose conversational language — ASK the user explicitly and wait for the answer.** Which language should the agent *talk to them* in — questions, explanations, summaries, status updates? Offer the language they're already writing in as the default. Carry the answer into the `{{COMM_LANGUAGE}}` slot of the *Communication language* block when `AGENTS.md` is created (step 5). See [Communication contract](references/contracts.md#communication-contract). This sets the **dialogue** language only — never the files.
167
169
  4. **Choose agent attribution — ASK the user explicitly and wait for the answer.** May the agent attribute work to itself / to AI — `Co-Authored-By` trailers, "Generated with …" footers, "AI"/agent/model mentions in code, comments, commit messages, PR titles/bodies, or docs? **Default to `off`** (no agent/AI mention anywhere) unless they opt in — people are routinely surprised to find an AI listed as a repo contributor. Carry the answer into the `{{AGENT_ATTRIBUTION}}` slot of the *Attribution* block when `AGENTS.md` is created (step 5). **If `off` and the project uses Claude Code**, also set `"includeCoAuthoredBy": false` in the project's `.claude/settings.json` (create it if absent) — the trailer is added by the harness, so a doc directive alone won't stop it. See [Attribution contract](references/contracts.md#attribution-contract).
168
170
  5. **Entry-point doc.** If `AGENTS.md` / `CLAUDE.md` already exist (step-1 recon), do **not** overwrite — show the user and ask whether to merge or replace. Otherwise create `AGENTS.md` (the cross-agent standard — Codex / Cursor / Devin Desktop / Copilot read it natively) from `${CLAUDE_SKILL_DIR}/references/templates/AGENTS.md`, and symlink `CLAUDE.md -> AGENTS.md` (`ln -s AGENTS.md CLAUDE.md`) for Claude Code — single source, no duplication. For nested context, add a subdir `AGENTS.md` (+ a `CLAUDE.md` symlink beside it for Claude Code).
169
- 6. **Deploy `docs/ai/`.** Create the 11 files + `pages/` from `${CLAUDE_SKILL_DIR}/references/templates/`. Keep each file's frontmatter (`type / lastUpdated / scope / staleAfter / owner / maxLines`).
171
+ 6. **Deploy `docs/ai/`.** Create every `docs/ai/` file + `pages/` from `${CLAUDE_SKILL_DIR}/references/templates/` (the template loop deploys each non-`AGENTS.md` template — the `.md` docs **and** the seeded, user-editable **`docs/ai/orchestration.json`** config, strict JSON, the per-project recipe defaults the `procedures` advisor reads). Keep each `.md` file's frontmatter (`type / lastUpdated / scope / staleAfter / owner / maxLines`); `orchestration.json` carries no frontmatter (the docs cap-validator globs `*.md` only, so it is inherently skipped).
170
172
  7. **Fill templates** per the table below.
171
173
  8. **Install enforcement (Node projects).** Copy `${CLAUDE_SKILL_DIR}/references/scripts/*.mjs` (+ `*.test.mjs`) into the project's `scripts/`. They self-configure (project name from `package.json`, hierarchical/on-demand sections auto-discovered). **If the project has no Node runtime** (step-1 recon), skip this step and the hook in step 9 — follow the cap/archive/index policy manually, or port the scripts to the project's language.
172
174
  9. **Wire / hide** per visibility (see [Visibility contract](references/contracts.md#visibility-contract)). Install the pre-commit hook (Node projects): `node scripts/install-git-hooks.mjs`. If the installer reports a pre-existing non-marker hook, stop and ask the user to merge it manually rather than overwriting.
@@ -184,7 +186,7 @@ readiness, then **append an actionable recipe recommendation** from the recipe p
184
186
  independent axes: a packaging-only release bumps the package but leaves the lineage head until a
185
187
  migration actually changes the deployed `docs/ai` structure. A stamp greater than the head →
186
188
  STOP (never downgrade).
187
- 11. **Report & ask.** Show `tree docs/ai/`, 2–3 lines on what was filled with real data vs left as TODO, then print the **one-line backend-status line** (the shared contract above — same detector, format, invariants, and detector-unavailable skip-with-reason). Then **ask before committing** — never auto-commit.
189
+ 11. **Report & ask.** Show `tree docs/ai/`, 2–3 lines on what was filled with real data vs left as TODO, then print the **one-line backend-status line** (the shared contract above — same detector, format, invariants, and detector-unavailable skip-with-reason). Then **ask before committing** — never auto-commit. Finally, offer the optional, opt-in **velocity profile** in one line — `/agent-workflow-kit velocity` seeds a read-only allowlist so routine read-only commands stop prompting (*Mode: velocity*); never run it without a yes.
188
190
 
189
191
  Fill strategy:
190
192
 
@@ -213,8 +215,10 @@ Fill strategy:
213
215
  **No-Node project:** the fragments live only in the **installed `agent-workflow-engine`** (`references/methodology-slot.md` + `references/orchestration-slot.md`, under `~/.claude/skills/agent-workflow-engine` or `$AGENT_WORKFLOW_ENGINE_DIR`) — there is no bundled copy, and a No-Node host cannot run the `npx` engine install. Open `AGENTS.md` and classify **each** pointer by hand: a **filled / customized** pair → leave it verbatim (no engine needed); a **malformed** pair (not exactly one ordered `start → end`) → STOP, do not edit. A pair that needs filling — **absent markers OR a present-but-empty pair** — needs the engine's fragment, so: if the engine is **not installed**, that pointer **cannot be added** — report it plainly (the methodology is already in `docs/ai/agent_rules.md`; the recipes are available via `/agent-workflow-kit recipes`; install the engine to add the pointers). If the engine **is** present, **count the lines first** — if adding/filling would take the file over 100 lines, **skip that pointer and report the skip** (methodology first, then orchestration; the orchestration pair sits right under the methodology end marker). Fill each empty pair from its engine fragment (`methodology-slot.md` / `orchestration-slot.md`) — never inline a copy (that would re-create the retired mirror).
214
216
 
215
217
  **Hidden-mode footprint reconcile — stamp-independent, same gate, BEFORE the equal-head short-circuit (D9 / AD-014).** A deployment does not record whether it chose `hidden`, so first **infer visibility**: `node ${CLAUDE_SKILL_DIR}/tools/hide-footprint.mjs --dir <project> --reconcile --dry-run` (writes **zero bytes**). It reports one of — **visible** (the entry point is tracked) → nothing to do; **ambiguous** (untracked but not ignored — could be a fresh uncommitted repo, or a hide that broke) → **ASK** the user which it is, never guess; **hidden** → re-run without `--dry-run` to migrate any older **machine-global** hide to the **project-local** `.git/info/exclude` (one managed block; folds in the legacy `.claude/skills/` line), idempotently (a clean re-run is zero-diff). Handle its surfaced paths exactly as bootstrap step 9 (already-committed → show `git rm --cached`, ask before `--include`; generic-name present file → ask; **leftover machine-wide ignore block → ASK before `--remove-global`**, default keep + report). No Node on the agent host / Windows → as step 9. This runs on **every** hidden upgrade, like the methodology slot — no lineage-head bump, no migration file.
218
+
219
+ **Orchestration config ensure — stamp-independent, same gate, BEFORE the equal-head short-circuit.** Ensure `docs/ai/orchestration.json` exists: **create it if missing**, **preserve it byte-for-byte if it already exists** (a user may have edited it — never clobber it). In the **delegated** path memory does this from its own template (memory upgrade step 2); in the **fallback** path the kit seeds it from `${CLAUDE_SKILL_DIR}/references/templates/orchestration.json`. Like the pointer slots + the footprint reconcile, this lets an equal-head (`1.3.0`) deployment gain the config seed **without a lineage-head bump or a migration file** (it is a `.json`, inherently outside the docs cap-validator). Report it in the step 4 / step 8 success report (config *seeded* vs *already present*).
216
220
  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 methodology-slot **and** hidden-mode footprint reconciles) ran first and may have changed things, so this is a proper exit report, not a no-op:
217
- - **Report step 3's outcome in plain language** — for **each** pointer (workflow-methodology and orchestration-recipes) whether it was *added*, was *already present* (nothing changed), or was *skipped because the entry point is over its line limit* (the cap-refusal soft-skip from step 3, with its reason); and, for a hidden deployment, whether the hidden-mode footprint was *moved to project-local*, was *already project-local* (nothing changed), or needed a question (ambiguous visibility / a leftover machine-wide block). Plain wording only — never the reconcile/slot/anchor/marker terms (Gotcha: never leak kit internals).
221
+ - **Report step 3's outcome in plain language** — for **each** pointer (workflow-methodology and orchestration-recipes) whether it was *added*, was *already present* (nothing changed), or was *skipped because the entry point is over its line limit* (the cap-refusal soft-skip from step 3, with its reason); whether the `docs/ai/orchestration.json` config was *seeded* or was *already present* (a user edit is preserved); and, for a hidden deployment, whether the hidden-mode footprint was *moved to project-local*, was *already project-local* (nothing changed), or needed a question (ambiguous visibility / a leftover machine-wide block). Plain wording only — never the reconcile/slot/anchor/marker terms (Gotcha: never leak kit internals).
218
222
  - **Print the one-line backend-status line** — the shared contract above (run `node ${CLAUDE_SKILL_DIR}/tools/detect-backends.mjs`; same format, invariants, and detector-unavailable skip-with-reason).
219
223
  - **Then ask before committing — never auto-commit.** If step 3 added the slot (or anything else changed), report it and ask. If step 3 was a pure zero-diff no-op and nothing else changed, say **"already up to date"** and still print the read-only backend line.
220
224
  5. Show the relevant `${CLAUDE_SKILL_DIR}/CHANGELOG.md` diff (entries newer than the project's stamp).
@@ -280,6 +284,25 @@ The four recipes (defined over each bridge's `provides` roles — `codex`: execu
280
284
 
281
285
  **Invariants:** read-only · never runs a subscription CLI · never commits · the orchestrator executes the recipe via the bridge skills, not the kit.
282
286
 
287
+ ### Mode: procedures
288
+
289
+ Read-only **activity-procedures advisor**. Answers *"what are the steps of this named activity, and which recipe applies at each slot here?"* It composes the orchestration recipes (Mode: recipes) into **named activities** with **typed recipe slots**. It **never writes, never commits, never runs a subscription CLI** — the deterministic resolution lives in the kit; the orchestrator runs the resolved recipe via the bridge skills and **owns any commit when the activity has a commit boundary** (a backend never commits). Not every activity commits: `plan-authoring` ends at approval and produces no commit (plans are ephemeral, never committed); `plan-execution` commits per Step.
290
+
291
+ The two v1 activities (canon in the **installed engine**, `references/procedures.md`):
292
+
293
+ - **`plan-authoring`** (slot: `review`) — research → draft → self-review → **review {recipe}** → fold/loop → present for approval; enforce the mandatory Cleanup.
294
+ - **`plan-execution`** (slots: `execute`, `review`) — per Step: resolve the recipe → if Delegated, dispatch execution first → implement → self-review → **review {recipe}** → gates → commit boundary.
295
+
296
+ Run **`node ${CLAUDE_SKILL_DIR}/tools/procedures.mjs <activity> [--override <slot>=<recipe>]… [--json]`**. It reads the activity's steps live from the engine and prints them **verbatim**, then the **resolved effective recipe per slot** from the per-project config + the read-only backend detector:
297
+
298
+ 1. **Config = `docs/ai/orchestration.json`** — strict JSON, **hand-edited** (the kit reads + validates it; `recipes` stays read-only — there is no writer). Shape: `{ "<activity>": { "<slot>": "<recipe>" } }`; all slots optional (an absent slot → its computed default, stated); an optional `"_README"` string is allowed + ignored. `review` accepts `solo|reviewed|council`; `execute` accepts `solo|delegated`. Seeded by `init` (a user-editable template) — see *Mode: bootstrap*.
299
+ 2. **Default resolution (config silent):** `review` → Reviewed if any review-capable backend is `ready`, else Solo (never Council by default); `execute` → Solo (Delegated is opt-in). **Degradation:** a config/computed default degrades **gracefully with a stated reason** (Council → Reviewed → Solo; Delegated → Solo); a per-run **`--override <slot>=<recipe>`** that can't be satisfied degrades **loudly** (a flagged warning, so you tell the user) — but is **still exit 0** (a valid request that gracefully degraded).
300
+ 3. **Exit codes:** `0` success; `2` usage (unknown `<activity>` / bad `--override` — a bare `--override <recipe>`, an unknown slot, an invalid recipe-for-slot, or a duplicate slot); `1` config error (malformed / schema-invalid / unreadable `orchestration.json`) **or** engine error (the installed engine is absent / invalid / **too old** to ship `references/procedures.md` — upgrade it with `npx @sabaiway/agent-workflow-engine@latest init`). A `1`/`2` failure is loud (`path: reason`), never a silent fallback.
301
+
302
+ **Cap-soft-skip degradation (the feature's only AUTO route).** The activity procedures are auto-discoverable only through the one-line **`workflow:methodology`** pointer (this kit + the engine carry `disable-model-invocation:true`, so NL like "write a plan" does **not** auto-load this skill). On a deployment whose methodology pointer was cap-soft-skipped — or whose pre-existing customized pointer lacks the procedures clause — the procedures are still reachable by **explicitly** invoking `/agent-workflow-kit procedures`; surface that plainly rather than treating it as a gap.
303
+
304
+ **Invariants:** read-only · never writes · never commits · never runs a subscription CLI · the deterministic resolution is the kit's, the recipe execution is the orchestrator's.
305
+
283
306
  ### Mode: uninstall
284
307
 
285
308
  The **guarded teardown** — the inverse of `init` (the kit + engine skills) + `setup` (the bridges) + a hidden deploy. **In-agent, opt-in**, and built around one hard rule: **it never deletes user-authored content.** Run **`--dry-run` first, always**, show the user the classified plan in plain language, get explicit consent, then re-run with `--yes`. It **never commits**.
@@ -295,10 +318,29 @@ It classifies every surface into four classes and acts accordingly:
295
318
 
296
319
  - **remove** (safe) — an installed skill dir that is **provably ours** (valid manifest, `name`+`kind` match). A dir present but **not provably ours** (`foreign`/`stub`/`invalid`/unreadable) → **STOP**: left untouched **and reported**, while the teardown still removes the members that ARE ours (a not-ours surface is never clobbered, and never blocks removing the rest — the per-item `setup` posture). **Preflight-then-mutate:** if a mutable surface **changed since the dry-run** (a skill no longer ours, a wrapper turned foreign, a hook that lost our marker, a malformed fence), the run **aborts with zero changes**.
297
320
  - **reverse** (managed-marker) — a bridge **wrapper symlink that points at our source** (a foreign/non-symlink one → STOP); the hidden-mode **managed fence** (via the existing `--unhide` path — only the fenced lines); a **pre-commit hook carrying our marker** (an unmarked / user hook → left + reported).
298
- - **KEEP — never deleted** (report-only) — `docs/ai`, `AGENTS.md`, `CLAUDE.md`, `docs/plans`, and the `.claude/settings.json` `includeCoAuthoredBy` edit. The tool **prints the exact `rm` / `git rm --cached` commands**; the **user** runs them. Surface this in plain language; never delete on their behalf.
321
+ - **KEEP — never deleted** (report-only) — `docs/ai`, `AGENTS.md`, `CLAUDE.md`, `docs/plans`, and `.claude/settings.json` (the `includeCoAuthoredBy` edit **and** any `permissions.defaultMode`/`permissions.allow` the velocity profile may have seeded). The tool **prints the exact `rm` / `git rm --cached`** for the docs/entry files and an **edit** instruction for `settings.json` (never an `rm` — it may hold your own settings); the **user** runs them. Surface this in plain language; never delete on their behalf.
299
322
 
300
323
  **Shared globals:** removing `agent-workflow-memory` / `agent-workflow-engine` / a bridge removes a **global** skill that another project on the machine may use — say so before applying. **Windows:** the wrappers are POSIX; the skill-dir + project arms still work, the wrapper arm reports *use WSL*.
301
324
 
325
+ ### Mode: velocity
326
+
327
+ The opt-in onboarding **velocity profile** — it seeds a fixed, audited **read-only** Claude Code allowlist into `.claude/settings.json` so an agent stops idling on approval prompts for routine read-only commands while the maintainer is away. It is the family's **first programmatic `.claude/settings.json` writer** (attribution stayed an agent-driven prose merge). **In-agent, opt-in, writes only `.claude/settings.json`**, on one hard rule: **it never allowlists `commit`/`push`/`publish`** — so a direct commit/push/publish still ASKs; the only caveat is the trust-posture residual (below), closed by the deferred hook.
328
+
329
+ **Version-status routing (like the other writer modes):** read `docs/ai/.workflow-version` first — not-deployed → bootstrap; stamp < `1.3.0` → `upgrade`; stamp > head / unparseable → STOP. The tool enforces this in code too (`--apply` STOPs unless the stamp is the lineage head).
330
+
331
+ Run `node ${CLAUDE_SKILL_DIR}/tools/velocity-profile.mjs [--dry-run | --apply] [--accept-edits] [--cwd <dir>]`:
332
+
333
+ 1. **`--dry-run` first, always** (the default — changes nothing). It prints: the fixed read-only core it would add; a **read-only advisory** that lists your `package.json` `scripts` as **unaudited candidates you may add BY HAND** (inspect each first) to `.claude/settings.json` / `settings.local.json` — the tool **never** writes them and flags obviously-mutating names as "do not add"; any **pre-existing non-read-only `Bash(...)` entries** to consider removing by hand; and the honest residual notice (below). It STOPs (zero writes) on a symlinked `.claude` / non-regular `settings.json`, malformed settings JSON, or an unsafe `permissions.defaultMode` — `bypassPermissions` or anything outside `default`/`acceptEdits`/`plan`, present in **either** `settings.json` or `settings.local.json`.
334
+ 2. **Ask the `acceptEdits` opt-in** via **`AskUserQuestion` where supported**, the safe option FIRST:
335
+ - **"Keep per-edit approval prompts (recommended)"** — seed only the read-only allowlist; file edits still prompt.
336
+ - **"Auto-accept file edits (`defaultMode: acceptEdits`)"** — present the honest FULL posture: it auto-applies Edit/Write AND auto-runs `mkdir`/`touch`/`mv`/`cp` in the working dir, is paired with the read-only allowlist, and — stated plainly — a settings-level allow rule is a **trust posture, not a sandbox**: a read-only entry can still write a file via output redirection, and (Claude Code's allow rules do not inspect command substitution) could in principle run another command via `cmd $(…)`. velocity **never adds `commit`/`push`/`publish` as allow rules** — so a direct `git push` still ASKs — but that same redirection/substitution residual means they are not *fully* closed until the deferred PreToolUse hook (family backlog). Note also that a `defaultMode` in `settings.local.json` would override this project-level write (local > project), since velocity writes only `.claude/settings.json`.
337
+ 3. **Only on an explicit yes**, re-run with `--apply` (add `--accept-edits` only if they chose the second option). It merges-don't-clobber (preserves `includeCoAuthoredBy`, every key, and existing allow entries) and writes **only** `.claude/settings.json`.
338
+ 4. **Surface delegation-readiness, read-only.** If they want a step run Delegated, point them at the hand-edited `docs/ai/orchestration.json` (*Mode: procedures*); the tool never writes it.
339
+
340
+ **Invariants:** creates `.claude/` if absent and writes **only** `.claude/settings.json` (no other file); **never** allowlists commit/push/publish; **never** writes `settings.local.json`; never commits; opt-in `acceptEdits`, never silent.
341
+
342
+ **Exit codes:** `0` done / dry-run; `1` a precondition STOP (stamp not current, unsafe mode, malformed settings, symlinked `.claude` / non-regular target); `2` bad arguments.
343
+
302
344
  ---
303
345
 
304
346
  ## Gotchas
@@ -315,7 +357,7 @@ The non-obvious traps — scan these before bootstrapping or upgrading. Each is
315
357
  - **Conversational language never translates artifacts.** It governs *dialogue only*. Code, identifiers, paths, commands, log output, abbreviations, and every deployed `docs/ai/` / `AGENTS.md` file stay in their source language. See [Communication contract](references/contracts.md#communication-contract).
316
358
  - **Never auto-commit.** Report quality-gate results and wait for explicit approval — in both modes.
317
359
  - **Never leak kit internals to the user.** No ADR ids, tool / function / operation names (`reconcile`, `inject`, `ensureSlot`), marker / slot / fragment / anchor terminology, or verbatim tool stderr in anything the user reads. Translate every tool outcome into plain language a third-party user — who has never read this `SKILL.md` — can understand and act on (e.g. the cap-refusal report in *Mode: upgrade* step 3).
318
- - **Uninstall never deletes user-authored content, and dry-runs first.** `/agent-workflow-kit uninstall` removes only what is **provably ours** (a managed skill dir / wrapper symlink / fenced block / marker hook) and **prints — never runs** the `rm` / `git rm --cached` for `docs/ai`, the entry-point docs, and `.claude/settings.json`. Always run `--dry-run` first, show the plan, get consent, then `--yes`. A skill dir or symlink that is not provably ours is a STOP, never a clobber (the `setup` posture, inverted). Removing a shared global (memory/engine/a bridge) may affect another project — say so.
360
+ - **Uninstall never deletes user-authored content, and dry-runs first.** `/agent-workflow-kit uninstall` removes only what is **provably ours** (a managed skill dir / wrapper symlink / fenced block / marker hook) and **prints — never runs** the `rm` / `git rm --cached` for `docs/ai` and the entry-point docs, and an **edit** instruction (not an `rm`) for `.claude/settings.json`. Always run `--dry-run` first, show the plan, get consent, then `--yes`. A skill dir or symlink that is not provably ours is a STOP, never a clobber (the `setup` posture, inverted). Removing a shared global (memory/engine/a bridge) may affect another project — say so.
319
361
 
320
362
  ---
321
363
 
@@ -368,10 +410,10 @@ Deploy these into `AGENTS.md`; remove rows that don't apply to the stack.
368
410
 
369
411
  - [`references/contracts.md`](references/contracts.md) — the three setup contracts (visibility, conversational language, agent attribution) in full; the *Setup contracts* section above points here.
370
412
  - **Plan vocabulary** (Plan→Phase→Step→Substep), lifecycle, `queue.md` series-index, mandatory Cleanup, session-continuity heuristic — the single home is the **installed `agent-workflow-engine`** canon (`~/.claude/skills/agent-workflow-engine/references/planning.md`, or `$AGENT_WORKFLOW_ENGINE_DIR`); there is no bundled mirror. `npx @sabaiway/agent-workflow-kit@latest init` installs the engine.
371
- - [`references/templates/`](references/templates/) — stack-agnostic `AGENTS.md`, `agent_rules.md`, and all `docs/ai/` files to deploy.
413
+ - [`references/templates/`](references/templates/) — stack-agnostic `AGENTS.md`, `agent_rules.md`, the seeded user-editable `orchestration.json` config (byte-identical to the memory copy — `test/template-parity.test.mjs`), and all `docs/ai/` files to deploy.
372
414
  - [`references/scripts/`](references/scripts/) — the Node enforcement scripts (caps + staleness + index-freshness gate, 3-tier archive, hook installer) and their unit tests.
373
415
  - [`migrations/`](migrations/) — per-version upgrade steps; see `migrations/README.md`.
374
416
  - [`launchers/`](launchers/) — run the bootstrapper from non-Claude agents (`SKILL.md` is a native Codex skill; a Devin Desktop workflow launcher + install script). See `launchers/README.md`.
375
- - [`tools/`](tools/) — the family-wide tooling the kit **owns and ships**: `manifest/{schema.md,validate.mjs}` (the `capability.json` schema + the validator the kit runs as the memory detector, and root CI invokes), `delegation.mjs` (the executable delegate/fallback decision + hand-off plan), `inject-methodology.mjs` + `engine-source.mjs` (the bounded **two-slot** reconciliation — ensure-slot / inject-if-empty / cap for the `workflow:methodology` **and** `workflow:orchestration` pointers; both fragments read **live** from the installed `agent-workflow-engine` via `engine-source.mjs` (`deps.rel` selects which) — the family's one source of truth, no bundled mirror; fail-loud when the engine is needed but absent, orchestration soft-skipped when it would bust the cap), `detect-backends.mjs` (the read-only **backend detector** behind `/agent-workflow-kit backends`, plus the axis-aware `guideFor`; exports the readiness consts the planner reuses), `recipes.mjs` (the read-only **recipe planner** behind `/agent-workflow-kit recipes` — `RECIPES` / `planRecipe` / `recommendRecipe` over the bridges' role vocabulary, drift-guarded against `provides`/`cost`/`quota` + an engine↔kit recipe-name parity guard; pure, never runs a subscription CLI), `setup-backends.mjs` (the **link-only** backend setup behind `/agent-workflow-kit setup` — place the bundled bridge + link wrappers), `fs-safe.mjs` (the shared symlink-traversal-safe copy/link/**remove/unlink** primitives that `setup-backends`, the npx installer, and the uninstaller use), `known-footprint.mjs` + `hide-footprint.mjs` (the **hidden-mode** registry + the single hide-writer behind step 9 / the upgrade reconcile — one managed block in the **project-local** `.git/info/exclude` covering the full AI/agent footprint; pinned by `known-footprint.test.mjs` drift-guard + `hide-footprint.test.mjs` / `.integration.test.mjs`), `family-registry.mjs` (the **unified family registry** behind `/agent-workflow-kit status` — aggregates every member's `capability.json`; pinned by a `family-registry.test.mjs` drift-guard), `uninstall.mjs` (the **guarded teardown** behind `/agent-workflow-kit uninstall` — classify each surface, preflight-then-mutate, never delete user-authored content), and `release-scan.mjs` (the attribution-off release gate). The bundled bridge skill mirrors live under [`bridges/`](bridges/) (byte-identical to the repo-root bridges, pinned by `test/bridges-mirror.test.mjs`). See [`tools/manifest/schema.md`](tools/manifest/schema.md).
417
+ - [`tools/`](tools/) — the family-wide tooling the kit **owns and ships**: `manifest/{schema.md,validate.mjs}` (the `capability.json` schema + the validator the kit runs as the memory detector, and root CI invokes), `delegation.mjs` (the executable delegate/fallback decision + hand-off plan), `inject-methodology.mjs` + `engine-source.mjs` (the bounded **two-slot** reconciliation — ensure-slot / inject-if-empty / cap for the `workflow:methodology` **and** `workflow:orchestration` pointers; both fragments read **live** from the installed `agent-workflow-engine` via `engine-source.mjs` (`deps.rel` selects which) — the family's one source of truth, no bundled mirror; fail-loud when the engine is needed but absent, orchestration soft-skipped when it would bust the cap), `detect-backends.mjs` (the read-only **backend detector** behind `/agent-workflow-kit backends`, plus the axis-aware `guideFor`; exports the readiness consts the planner reuses), `recipes.mjs` (the read-only **recipe planner** behind `/agent-workflow-kit recipes` — `RECIPES` / `planRecipe` / `recommendRecipe` over the bridges' role vocabulary, drift-guarded against `provides`/`cost`/`quota` + an engine↔kit recipe-name parity guard; pure, never runs a subscription CLI; also exports the pure `ACTIVITIES` / `resolveActivityRecipe` activity-procedures resolver), `procedures.mjs` (the read-only **activity-procedures advisor** behind `/agent-workflow-kit procedures` — reads `references/procedures.md` live from the engine via `engine-source.mjs`, reads + validates the hand-edited `docs/ai/orchestration.json`, and prints the steps + the resolved effective recipe per slot; drift-guarded activity/slot table vs the canon; read-only, never runs a subscription CLI), `setup-backends.mjs` (the **link-only** backend setup behind `/agent-workflow-kit setup` — place the bundled bridge + link wrappers), `fs-safe.mjs` (the shared symlink-traversal-safe copy/link/**remove/unlink** primitives that `setup-backends`, the npx installer, and the uninstaller use), `known-footprint.mjs` + `hide-footprint.mjs` (the **hidden-mode** registry + the single hide-writer behind step 9 / the upgrade reconcile — one managed block in the **project-local** `.git/info/exclude` covering the full AI/agent footprint; pinned by `known-footprint.test.mjs` drift-guard + `hide-footprint.test.mjs` / `.integration.test.mjs`), `family-registry.mjs` (the **unified family registry** behind `/agent-workflow-kit status` — aggregates every member's `capability.json`; pinned by a `family-registry.test.mjs` drift-guard), `uninstall.mjs` (the **guarded teardown** behind `/agent-workflow-kit uninstall` — classify each surface, preflight-then-mutate, never delete user-authored content), and `release-scan.mjs` (the attribution-off release gate). The bundled bridge skill mirrors live under [`bridges/`](bridges/) (byte-identical to the repo-root bridges, pinned by `test/bridges-mirror.test.mjs`). See [`tools/manifest/schema.md`](tools/manifest/schema.md).
376
418
  - [`capability.json`](capability.json) — the kit's own `agent-workflow` family manifest (`kind: composition-root`).
377
419
  - [`CHANGELOG.md`](CHANGELOG.md) — version history of this kernel.
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": "1.13.0",
6
+ "version": "1.15.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": "1.13.0",
3
+ "version": "1.15.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",
@@ -139,7 +139,10 @@ export const parseStaleAfter = (value) => {
139
139
  return Number(m[1]);
140
140
  };
141
141
 
142
- const walkMarkdownFiles = async (dir) => {
142
+ // Discover the docs to validate: ONLY `*.md` files (recursively). Non-`.md` files — e.g. a hand-edited
143
+ // `docs/ai/orchestration.json` config — are inherently skipped, so they are never subject to the
144
+ // frontmatter / maxLines caps. Exported so that skip is pinned by a regression test.
145
+ export const walkMarkdownFiles = async (dir) => {
143
146
  const entries = await readdir(dir, { withFileTypes: true });
144
147
  const files = [];
145
148
  for (const entry of entries) {
@@ -10,6 +10,7 @@ import {
10
10
  inspectFile,
11
11
  buildIndex,
12
12
  checkIndexFreshness,
13
+ walkMarkdownFiles,
13
14
  } from './check-docs-size.mjs';
14
15
 
15
16
  describe('parseFrontmatter', () => {
@@ -126,6 +127,29 @@ describe('inspectFile', () => {
126
127
  });
127
128
  });
128
129
 
130
+ // The cap-validator discovers ONLY `*.md` files, so a non-`.md` doc — e.g. a hand-edited
131
+ // `docs/ai/orchestration.json` config — is inherently skipped: never validated for frontmatter / caps.
132
+ // This belt-and-suspenders regression pins that skip so adding a config `.json` under docs/ai can never
133
+ // start failing the docs gate.
134
+ describe('walkMarkdownFiles — only *.md is discovered (a config .json is skipped)', () => {
135
+ let dir;
136
+ beforeEach(async () => {
137
+ dir = await mkdtemp(join(tmpdir(), 'walk-md-test-'));
138
+ });
139
+ afterEach(async () => {
140
+ await rm(dir, { recursive: true, force: true });
141
+ });
142
+
143
+ it('returns the .md file and NOT a sibling orchestration.json', async () => {
144
+ await writeFile(join(dir, 'doc.md'), '---\ntype: reference\nmaxLines: 100\n---\n\nbody.\n');
145
+ await writeFile(join(dir, 'orchestration.json'), '{ "plan-authoring": { "review": "reviewed" } }\n');
146
+ const found = await walkMarkdownFiles(dir);
147
+ expect(found.some((f) => f.endsWith('doc.md'))).toBe(true);
148
+ expect(found.some((f) => f.endsWith('.json'))).toBe(false);
149
+ expect(found.some((f) => f.endsWith('orchestration.json'))).toBe(false);
150
+ });
151
+ });
152
+
129
153
  // Synthetic row matching the shape produced by `inspectFile` + `formatRow`.
130
154
  const makeRow = (path, overrides = {}) => ({
131
155
  path,
@@ -0,0 +1,5 @@
1
+ {
2
+ "_README": "Per-project orchestration config: the recipe used at each step (slot) of each named activity. Hand-edit this file — it is never written for you. Each activity is configured independently (e.g. plan-authoring, plan-execution), and so is each slot within it. A slot's value is a recipe: a 'review' slot accepts solo | reviewed | council (you self-review / one backend reviews / both review and you synthesize); an 'execute' slot accepts solo | delegated (you implement / a backend runs a bounded sub-task). The default below is 'solo' everywhere — no execution backend required. Raise a slot to reviewed or council for a second opinion, or to delegated to hand off execution; those need an execution backend set up first. Remove a slot's line to fall back to the computed default (reviewed when a review backend is ready, otherwise solo). Run the read-only procedures advisor to see an activity's steps plus the recipe resolved for your environment, and pass a per-run override to change one slot just once. Strict JSON — no comments.",
3
+ "plan-authoring": { "review": "solo" },
4
+ "plan-execution": { "execute": "solo", "review": "solo" }
5
+ }
@@ -26,6 +26,11 @@ export const ENGINE_FRAGMENT_REL = 'references/methodology-slot.md';
26
26
  // engine older than 1.2.0 does not ship it; detectEngine({ rel }) lets a caller verify the specific
27
27
  // fragment so `status` can caveat a too-old engine instead of failing the methodology read.
28
28
  export const ORCHESTRATION_FRAGMENT_REL = 'references/orchestration-slot.md';
29
+ // The activity-procedures canon — a NEW live-read engine fragment (engine >= 1.3.0). The procedures
30
+ // CLI reads it via readEngineFragment({ rel }); detectEngine({ rel }) lets `status` caveat — and the
31
+ // CLI fail loudly on — an engine too old to ship it. readEngineFragment accepts an arbitrary `rel`
32
+ // (no whitelist), so no further plumbing is needed beyond this constant.
33
+ export const PROCEDURES_FRAGMENT_REL = 'references/procedures.md';
29
34
  const ENGINE_DEFAULT_REL = '.claude/skills/agent-workflow-engine';
30
35
 
31
36
  const defaultStatType = (path) => {
@@ -9,6 +9,7 @@ import {
9
9
  EXPECTED_ENGINE_NAME,
10
10
  ENGINE_FRAGMENT_REL,
11
11
  ORCHESTRATION_FRAGMENT_REL,
12
+ PROCEDURES_FRAGMENT_REL,
12
13
  } from './engine-source.mjs';
13
14
 
14
15
  // A valid engine dir = it exists (dir), the fragment file exists, and the validator reports a
@@ -230,3 +231,50 @@ describe('detectEngine / readEngineFragment — orchestration fragment via rel',
230
231
  );
231
232
  });
232
233
  });
234
+
235
+ // The activity-procedures canon is a THIRD live-read fragment (engine >= 1.3.0), selected the same way
236
+ // (deps.rel / detectEngine({ rel })). An engine too old to ship it must read as not-ok naming the
237
+ // fragment, and readEngineFragment must STOP loudly — the procedures CLI maps that to a clean exit 1.
238
+ describe('detectEngine / readEngineFragment — procedures fragment via rel', () => {
239
+ const proceduresPresent = (path) =>
240
+ path === ENGINE_DIR
241
+ ? 'dir'
242
+ : path === join(ENGINE_DIR, ENGINE_FRAGMENT_REL) || path === join(ENGINE_DIR, PROCEDURES_FRAGMENT_REL)
243
+ ? 'file'
244
+ : null;
245
+
246
+ it('exposes the procedures fragment rel constant', () => {
247
+ assert.equal(PROCEDURES_FRAGMENT_REL, 'references/procedures.md');
248
+ });
249
+
250
+ it('verifies the procedures fragment when rel is the procedures path', () => {
251
+ const out = detectEngine(ENGINE_DIR, { source: 'default', rel: PROCEDURES_FRAGMENT_REL }, deps({ statType: proceduresPresent }));
252
+ assert.equal(out.ok, true);
253
+ });
254
+
255
+ it('an older engine (no procedures fragment) → detect not-ok, the reason names that fragment', () => {
256
+ const out = detectEngine(ENGINE_DIR, { source: 'default', rel: PROCEDURES_FRAGMENT_REL }, deps()); // okStatType: only the methodology fragment is a file
257
+ assert.equal(out.ok, false);
258
+ assert.match(out.reason, /procedures\.md/);
259
+ });
260
+
261
+ it('readEngineFragment reads the procedures fragment bytes when deps.rel is set', () => {
262
+ const out = readEngineFragment(
263
+ ENGINE_DIR,
264
+ deps({ source: 'default', rel: PROCEDURES_FRAGMENT_REL, statType: proceduresPresent, readFileSync: () => 'PROCEDURES BODY' }),
265
+ );
266
+ assert.equal(out, 'PROCEDURES BODY');
267
+ });
268
+
269
+ it('readEngineFragment STOPs loudly when the procedures fragment is absent (engine < 1.3.0)', () => {
270
+ assert.throws(
271
+ () => readEngineFragment(ENGINE_DIR, deps({ source: 'default', rel: PROCEDURES_FRAGMENT_REL })),
272
+ (err) => {
273
+ assert.match(err.message, /methodology engine not found\/invalid/);
274
+ assert.match(err.message, /procedures\.md/);
275
+ assert.match(err.message, /npx @sabaiway\/agent-workflow-engine@latest init/);
276
+ return true;
277
+ },
278
+ );
279
+ });
280
+ });
@@ -23,7 +23,7 @@ import os from 'node:os';
23
23
  import { resolveDir } from './detect-backends.mjs';
24
24
  import { validateManifest, readAuthoritativeVersion, UNSUPPORTED, INVALID } from './manifest/validate.mjs';
25
25
  import { START_MARKER, excludePath } from './hide-footprint.mjs';
26
- import { readEngineFragment, ORCHESTRATION_FRAGMENT_REL } from './engine-source.mjs';
26
+ import { readEngineFragment, ORCHESTRATION_FRAGMENT_REL, PROCEDURES_FRAGMENT_REL } from './engine-source.mjs';
27
27
 
28
28
  // ── manifestState values (the detect-backends precedence, generalized to any member kind) ──────────
29
29
  export const NOT_INSTALLED = 'not-installed';
@@ -144,28 +144,34 @@ export const classifyMember = (member, deps = {}) => {
144
144
  return { name: member.name, kind: member.kind, installed, skillDir: installed ? skillDir : null, manifestState, version };
145
145
  };
146
146
 
147
- // An engine OLDER than 1.2.0 has a valid manifest + version but ships no orchestration-recipes
148
- // fragment (references/orchestration-slot.md), so it cannot supply the recipes pointer the kit
149
- // injects. surveyFamily attaches a plain-language caveat to that engine row instead of a bare "ok".
150
- // The check mirrors what a RECONCILE actually does `readEngineFragment(..., { rel: orchestration })`
151
- // validates the manifest AND reads the fragment so an absent, non-file, OR present-but-unreadable
152
- // fragment all surface as a caveat (status never claims "ok" for a fragment the reconcile would STOP
153
- // on), and a current, readable fragment never gets the caveat. Read-only, best-effort.
147
+ // An installed engine may be a VALID methodology-engine yet too old (or incomplete) to ship one of the
148
+ // kit's live-read fragments: `references/orchestration-slot.md` (the recipes pointer, engine >= 1.2.0)
149
+ // and `references/procedures.md` (the activity-procedures canon, engine >= 1.3.0). Each missing
150
+ // fragment is a DISTINCT, plain-language caveat. They are collected into `row.caveats` (an ARRAY) so an
151
+ // engine missing BOTH surfaces both a single `row.caveat` would overwrite one with the other. The
152
+ // check mirrors what each consumer actually does `readEngineFragment(..., { rel })` validates
153
+ // the manifest AND reads the fragment so an absent, non-file, OR present-but-unreadable fragment all
154
+ // surface (status never claims "ok" for a fragment a reconcile / the procedures CLI would STOP on), and
155
+ // a current, readable fragment never gets the caveat. Read-only, best-effort.
156
+ const ENGINE_FRAGMENT_CAVEATS = [
157
+ { rel: ORCHESTRATION_FRAGMENT_REL, caveat: 'engine present but does not supply the recipes pointer (too old / incomplete) — run `npx @sabaiway/agent-workflow-engine@latest init`' },
158
+ { rel: PROCEDURES_FRAGMENT_REL, caveat: 'engine present but does not ship the activity-procedures canon (too old / incomplete) — run `npx @sabaiway/agent-workflow-engine@latest init`' },
159
+ ];
160
+
154
161
  export const surveyFamily = (deps = {}) =>
155
162
  FAMILY_MEMBERS.map((member) => {
156
163
  const row = classifyMember(member, deps);
157
164
  if (row.kind === 'methodology-engine' && row.manifestState === OK && row.skillDir) {
158
- const orchUsable = (() => {
165
+ const fragmentUsable = (rel) => {
159
166
  try {
160
- readEngineFragment(row.skillDir, { source: 'default', rel: ORCHESTRATION_FRAGMENT_REL, ...deps });
167
+ readEngineFragment(row.skillDir, { source: 'default', rel, ...deps });
161
168
  return true;
162
169
  } catch {
163
- return false; // absent / non-file / unreadable fragment → the engine can't supply the pointer
170
+ return false; // absent / non-file / unreadable fragment → the engine can't supply it
164
171
  }
165
- })();
166
- if (!orchUsable) {
167
- row.caveat = 'engine present but does not supply the recipes pointer (too old / incomplete) run `npx @sabaiway/agent-workflow-engine@latest init`';
168
- }
172
+ };
173
+ const caveats = ENGINE_FRAGMENT_CAVEATS.filter((f) => !fragmentUsable(f.rel)).map((f) => f.caveat);
174
+ if (caveats.length) row.caveats = caveats;
169
175
  }
170
176
  return row;
171
177
  });
@@ -233,7 +239,7 @@ export const formatStatus = (family, project = null) => {
233
239
  for (const m of family) {
234
240
  const ver = m.version ? `v${m.version}` : '—';
235
241
  lines.push(` ${pad(m.name, 26)}[${pad(m.manifestState, 16)}] ${pad(ver, 10)} ${m.kind}`);
236
- if (m.caveat) lines.push(` ↳ ${m.caveat}`);
242
+ for (const c of m.caveats ?? []) lines.push(` ↳ ${c}`);
237
243
  }
238
244
  if (project) {
239
245
  lines.push('', `project deployment (${project.dir})`, '');