@sabaiway/agent-workflow-kit 1.44.0 → 1.45.1

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +6 -3
  3. package/SKILL.md +9 -1
  4. package/bridges/antigravity-cli-bridge/SKILL.md +1 -1
  5. package/bridges/antigravity-cli-bridge/bin/agy-review.sh +87 -16
  6. package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +3 -0
  7. package/bridges/antigravity-cli-bridge/bin/agy.test.mjs +4 -2
  8. package/bridges/antigravity-cli-bridge/capability.json +3 -2
  9. package/bridges/codex-cli-bridge/SKILL.md +7 -7
  10. package/bridges/codex-cli-bridge/bin/codex-exec.sh +3 -3
  11. package/bridges/codex-cli-bridge/bin/codex-exec.test.mjs +4 -1
  12. package/bridges/codex-cli-bridge/bin/codex-review.sh +93 -22
  13. package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +4 -0
  14. package/bridges/codex-cli-bridge/capability.json +4 -3
  15. package/bridges/codex-cli-bridge/references/driving-codex.md +5 -4
  16. package/bridges/codex-cli-bridge/references/sandbox-and-flags.md +5 -4
  17. package/capability.json +1 -1
  18. package/package.json +1 -1
  19. package/references/modes/bootstrap.md +1 -1
  20. package/references/modes/bridge-settings.md +1 -1
  21. package/references/modes/grounding.md +8 -7
  22. package/references/modes/recommendations.md +14 -0
  23. package/references/modes/review-state.md +1 -1
  24. package/references/modes/sandbox-masks.md +15 -0
  25. package/references/modes/upgrade.md +10 -7
  26. package/references/modes/velocity.md +13 -2
  27. package/references/shared/composition-handoff.md +21 -16
  28. package/references/shared/report-footer.md +5 -1
  29. package/references/templates/AGENTS.md +2 -1
  30. package/references/templates/autonomy.json +3 -0
  31. package/tools/autonomy-config.mjs +13 -3
  32. package/tools/commands.mjs +15 -1
  33. package/tools/delegation.mjs +9 -5
  34. package/tools/detect-backends.mjs +2 -2
  35. package/tools/doc-parity.mjs +8 -0
  36. package/tools/engine-source.mjs +6 -0
  37. package/tools/family-registry.mjs +21 -7
  38. package/tools/fold-completeness-run.mjs +5 -0
  39. package/tools/grounding.mjs +139 -22
  40. package/tools/inject-methodology.mjs +117 -28
  41. package/tools/manifest/schema.md +20 -0
  42. package/tools/manifest/validate.mjs +26 -0
  43. package/tools/procedures.mjs +61 -8
  44. package/tools/recipes.mjs +94 -15
  45. package/tools/recommendations.mjs +469 -0
  46. package/tools/review-ledger-write.mjs +14 -0
  47. package/tools/review-ledger.mjs +3 -2
  48. package/tools/review-state.mjs +101 -24
  49. package/tools/run-gates.mjs +3 -0
  50. package/tools/sandbox-masks.mjs +370 -0
  51. package/tools/set-recipe.mjs +13 -1
  52. package/tools/velocity-profile.mjs +228 -22
@@ -4,7 +4,7 @@ The opt-in onboarding **velocity profile** — it seeds a fixed, audited **read-
4
4
 
5
5
  **Version-status routing (like the other writer modes):** read `docs/ai/.workflow-version` first — not-deployed → bootstrap; stamp < `2.0.0` → `upgrade`; stamp > head / unparseable → STOP. The tool enforces this in code too (`--apply` STOPs unless the stamp is the lineage head).
6
6
 
7
- Run `node ${CLAUDE_SKILL_DIR}/tools/velocity-profile.mjs [--dry-run | --apply] [--kit-tools] [--accept-edits] [--cwd <dir>]`:
7
+ Run `node ${CLAUDE_SKILL_DIR}/tools/velocity-profile.mjs [--dry-run | --apply] [--kit-tools] [--bridge-tier] [--accept-edits] [--cwd <dir>]`:
8
8
 
9
9
  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`.
10
10
  2. **Ask the opt-ins** via **`AskUserQuestion` where supported** — two separate choices, the safe option FIRST in each. First the **`--kit-tools` tier** (separate from `acceptEdits`; **"core only" remains the default answer** — seed the tier only on an explicit yes, then add `--kit-tools` to the `--apply` re-run). Then the `acceptEdits` opt-in:
@@ -21,6 +21,7 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/velocity-profile.mjs [--dry-run | --apply] [
21
21
  - `node ${CLAUDE_SKILL_DIR}/tools/detect-backends.mjs` (wildcard)
22
22
  - `node ${CLAUDE_SKILL_DIR}/tools/commands.mjs` (wildcard)
23
23
  - `node ${CLAUDE_SKILL_DIR}/tools/review-state.mjs --check` (wildcard)
24
+ - `node ${CLAUDE_SKILL_DIR}/tools/recommendations.mjs --cwd ${PROJECT_ROOT}` (wildcard — the read-only deployment advisor; its rendered apply one-liners are writers and still prompt)
24
25
  - `node ${CLAUDE_SKILL_DIR}/tools/manifest/validate.mjs --strict <skill-dir>` (wildcard)
25
26
  - `node ${CLAUDE_SKILL_DIR}/tools/release-scan.mjs <path>` (wildcard)
26
27
  - `node ${CLAUDE_SKILL_DIR}/tools/run-gates.mjs --cwd ${PROJECT_ROOT}` — **EXACT byte-string only**, and honestly **project-exec, not read-only**: it runs YOUR declared `docs/ai/gates.json` commands — the same trust boundary the opt-in hook grants byte-exact per-cmd. A wildcard would be BROADER than that boundary (`--cwd <dir>` executes another project's declared gates), so the bare cwd-defaulting form, any other `--cwd`, `--only`, and **`--record`** forms all still prompt (`--record` WRITES a gate-run record into the review ledger — a recording run is never auto-approved, AD-048).
@@ -30,6 +31,16 @@ Honesty notes: tier entries get **NO PreToolUse-hook residual coverage** — the
30
31
 
31
32
  **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.
32
33
 
34
+ **The `--bridge-tier` (own opt-in, AD-044).** Seeds what a promptless council review run needs — BOTH surfaces: `permissions.allow` prefix rules AND the wrapper names in `sandbox.excludedCommands` (the harness runs an excluded command OUTSIDE the sandbox — the wrappers need network — so a plain allowlisted invocation triggers no sandbox-bypass approval). Both land in the **project** `.claude/settings.json` — the file this writer owns; an exclusion placed only in `settings.local.json` was live-observed NOT to route the command outside the sandbox (2026-07-11: the wrapper then starts sandboxed and dies on a read-only HOME + a network prompt), so hand-wiring the local file is not a working substitute for this tier. **Honesty note:** a session whose sandbox is imposed by the harness runtime itself (e.g. an IDE-managed session sandbox) may ignore settings-level exclusions entirely — there the wrappers need the session-level sandbox config (or a per-run consented bypass); the tier's seeded posture is correct for the settings-native sandbox and simply prompts again elsewhere (fail-safe, never a silent widening). Each bridge's observed egress hosts are declared in its `capability.json` `networkHosts` — the single documentation source (observed-minimal — a blocked host names itself at run time; read the manifests, this doc deliberately retypes no host list). The kit **never seeds** `sandbox.network.allowedDomains` or `sandbox.filesystem.allowWrite` (bridge council 2026-07-11, both backends concur): a network pre-allow widens egress for EVERY sandboxed command, and a write allowance on CLI state dirs (`~/.codex`, `~/.gemini/…`) would make credential dirs writable to every sandboxed command. Per-bridge picture under a harness-managed sandbox (live-observed 2026-07-11/12): an IDE-managed session sandbox ignores hand-applied `sandbox.network.allowedDomains` / `sandbox.filesystem.allowWrite` in BOTH settings scopes — its own per-host network consents govern egress; the durable zero-prompt lanes there are the session/host sandbox config (hosts from `networkHosts` + the CLI state-dir writes) or the per-run consented bypass. codex additionally needs a writable HOME (EROFS `~/.codex`); note the apex-vs-wildcard nuance — an apex domain is NOT covered by its `*.`-wildcard form, so the manifests carry both forms where observed (the blocked host names itself at run time). The upgrade Recommendations advisor renders exactly this as a HAND-APPLY item with the risk stated (`${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`). Membership is the FROZEN review-wrapper constant, **never** the execution/probe wrappers (`codex-exec`, `agy-run` keep their human prompt — delegated execution is not covered by this consent), and only the **`code` review mode** — a `plan`/`diff` invocation takes a file argument that can point OUTSIDE the repo, so those modes keep their prompt; each wrapper entry derives ONLY when its bridge is **placed on PATH** (an absent bridge is a stated skip). The seeded byte-forms (this list IS the documented-invocation source for the bridge tier):
35
+
36
+ - `Bash(codex-review code:*)` — the code-mode prefix, args wildcard; plus `codex-review` in `sandbox.excludedCommands`
37
+ - `Bash(agy-review code:*)` — the code-mode prefix, args wildcard; plus `agy-review` in `sandbox.excludedCommands`
38
+ - `Bash(node "${CLAUDE_SKILL_DIR}/tools/grounding.mjs":*)` — the grounding pre-step, **double-quoted absolute path**: exactly the byte-form the `procedures` advisor renders, so the pasted pre-step matches the rule (seeded↔rendered byte-parity). It derives ONLY when `agy-review` is placed (the pre-step exists for agy's grounded reviews; codex grounds natively) — a codex-only install never auto-allows this writer. The quoted form is accepted for THIS one tool only — never a general quote allowance; an unseedable kit path (spaces/metacharacters) is a stated skip. Its writer surface is closed by the tool's own guard: `--out` accepts only system-temp scratch (rewritable) or a FRESH gitignored in-repo path (create-only, exclusive write) — tracked, in-repo-not-ignored, EXISTING in-repo (even gitignored), outside-repo non-temp, and symlink/non-regular destinations are all refused.
39
+
40
+ **Consented posture (printed on every `--bridge-tier` run, stated not pretended away):** an auto-allowed review wrapper runs **UNATTENDED** — it reads any repo file it is pointed at and sends the assembled payload to its **subscription backend**; prefix rules cannot inspect arguments, so a code-mode argument that names a readable file (agy's `--facts`/`--decided`) rides the same consent. That is the tier's purpose (unattended council review runs) and its residual. Tier entries get no PreToolUse-hook coverage — the same documented residual class as the autonomy red-line rules. The grounding entry's writer surface is bounded by `grounding.mjs`'s own scratch-destination guard (a tracked or in-repo-not-ignored `--out` is refused by the tool itself).
41
+
42
+ **Invocation shape (why a run still prompts):** a prefix allow rule matches only a **PLAIN invocation starting with the wrapper name** — `codex-review code`, `agy-review code --facts @f`. An env-var prefix (`AGY_PROBE=1 agy-review …`) or a compound chain (`agy-review … && …`; `;`-joined statements) can NEVER match a prefix rule (observed live); output redirects are fine. Drive the wrappers as plain single commands.
43
+
33
44
  **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.
34
45
 
35
46
  ---
@@ -41,6 +52,6 @@ Honesty notes: tier entries get **NO PreToolUse-hook residual coverage** — the
41
52
  1. **Preview by default** (writes nothing); **`--apply`** merges the render-owned blocks into `.claude/settings.json` (merge-don't-clobber — foreign top-level keys, existing `permissions.allow`, and foreign ask/deny entries preserved; a policy flip MOVES a red-line rule between ask/deny rather than duplicating it). It **refuses an absent policy** loudly (seed one first with `set-autonomy`). It **never writes `settings.local.json`**; a `settings.local.json` `defaultMode` that would mask the render is reported (local > project).
42
53
  2. **`--autonomy --check`** is a **read-only drift gate**: it recomputes the render and compares against the live render-owned blocks — exit `0` in sync, exit `1` on drift (naming the exact key). A hand-edit OUTSIDE those blocks never flags.
43
54
  3. **Version-sensitive (characterized against the real claude 2.1.185).** Sandbox keys are `sandbox.enabled` + `sandbox.autoAllowBashIfSandboxed`; `sandbox` level ⇒ auto-allow + `defaultMode: acceptEdits`, `prompt` level ⇒ auto-allow OFF + `defaultMode: default` (the sandbox stays enabled as a confine floor either way). Red-lines use the argument-matching `:*` wildcard (`Bash(git commit:*)` / `Bash(git push:*)` / `Bash(npm publish:*)`). Where 2.1.185 cannot express a red-line distinctly — a **network hard-block** (regular settings only prompt on egress), **credential denial** (`sandbox.credentials` is 2.1.187+ / mask 2.1.199+), or a **prompt-on-outside-write** — the render **DEGRADES LOUDLY** (never a silent allow, never pretend-security). A missing Linux dependency (`socat`/`bwrap`) or an unsupported platform degrades the WHOLE sandbox to unsandboxed — the render still lands the red-lines + `defaultMode` and caveats that ad-hoc scripts will still prompt.
44
- 4. **`--autonomy` cannot combine** with `--accept-edits` or `--kit-tools` (allowlist-mode flags) — a loud usage error.
55
+ 4. **`--autonomy` cannot combine** with `--accept-edits`, `--kit-tools`, or `--bridge-tier` (allowlist-mode flags) — a loud usage error.
45
56
 
46
57
  **Autonomy invariants:** policy-only (never seeds the allowlist, leaves `permissions.allow` untouched) · writes only `.claude/settings.json` (never `settings.local.json`) · refuses an absent policy · merge-don't-clobber · degrades loudly where 2.1.185 can't enforce · previews by default · never commits.
@@ -3,40 +3,45 @@
3
3
  **Hand-off contract (explicit; tested independent of agent interpretation).**
4
4
  - **Delegated** (memory valid): the kit passes the **target project dir** + the **three setup
5
5
  answers** (visibility / language / attribution) to `agent-workflow-memory`, which writes
6
- `docs/ai/` + `AGENTS.md` (carrying the **two empty pointer pairs** — `workflow:methodology` and
7
- `workflow:orchestration`) + **`.memory-version`**. The kit then **reconciles both bounded pointers**
8
- (below) and writes the kit-fallback **`.workflow-version`**. → **both stamps** present. In **hidden** mode the kit is the **single
6
+ `docs/ai/` + `AGENTS.md` (carrying the **three empty pointer pairs** — `workflow:methodology`,
7
+ `workflow:orchestration` and `workflow:autonomy`) + **`.memory-version`**. The kit then **reconciles
8
+ the bounded pointers** (below) and writes the kit-fallback **`.workflow-version`**. → **both stamps** present. In **hidden** mode the kit is the **single
9
9
  hide authority**: after the hand-off it runs `tools/hide-footprint.mjs` (bootstrap step 9 — `${CLAUDE_SKILL_DIR}/references/modes/bootstrap.md`), which **absorbs
10
10
  memory's project-local footprint lines** into the one canonical `.git/info/exclude` block and adds
11
11
  the external footprint — so there is **no machine-global write** at any step (a stale memory's
12
12
  residual global block is cleaned via the upgrade reconcile, below).
13
13
  - **Fallback** (memory absent/invalid): the kit runs the bootstrap procedure (`${CLAUDE_SKILL_DIR}/references/modes/bootstrap.md`) from its own
14
- bundled assets — whose entry-point template now ships **two empty pointer pairs** (`workflow:methodology`
15
- + `workflow:orchestration`) the kit reconciles + fills — and writes **`.workflow-version`** only.
14
+ bundled assets — whose entry-point template now ships **three empty pointer pairs** (`workflow:methodology`
15
+ + `workflow:orchestration` + `workflow:autonomy`) the kit reconciles + fills — and writes **`.workflow-version`** only.
16
16
  Softly suggest installing `agent-workflow-memory` — never a prerequisite.
17
17
 
18
18
  **Bounded pointer reconciliation (the kit is the ONLY writer of these slots).** After `AGENTS.md`
19
19
  exists, run ONE command — `node ${CLAUDE_SKILL_DIR}/tools/inject-methodology.mjs reconcile
20
- <project>/AGENTS.md` — which reconciles **two** bounded pointers in a single atomic write: the
21
- **workflow-methodology** pointer (the plan → execute → review summary) **and**, right below it, the
20
+ <project>/AGENTS.md` — which reconciles **three** bounded pointers in a single atomic write: the
21
+ **workflow-methodology** pointer (the plan → execute → review summary), the
22
22
  **orchestration-recipes** pointer (the Solo / Reviewed / Council / Delegated vocabulary, routing to
23
- `/agent-workflow-kit recipes`). Each is **one atomic operation per slot**: **ensure the slot exists**
23
+ `/agent-workflow-kit recipes`) right below it, and the **autonomy-policy** pointer (the
24
+ `docs/ai/autonomy.json` read contract) below that. Each is **one atomic operation per slot**:
25
+ **ensure the slot exists**
24
26
  (insert an empty marker pair at its anchor when a legacy entry point lacks one) → **inject the bounded
25
27
  fragment ONLY IF the slot is empty** (a filled / user-customized slot is preserved verbatim) →
26
- **cap-check** (the second pointer's check runs on the file *after* the first, so it guards the
27
- **combined** ≤100-line budget). Both fragments are short summary + pointer, read **live from the
28
- installed `agent-workflow-engine`** (`references/methodology-slot.md` + `references/orchestration-slot.md`,
28
+ **cap-check** (each chained pointer's check runs on the file *after* the previous one, so it guards the
29
+ **combined** ≤100-line budget). The fragments are short summary + pointer, read **live from the
30
+ installed `agent-workflow-engine`** (`references/methodology-slot.md` + `references/orchestration-slot.md`
31
+ + `references/autonomy-slot.md`,
29
32
  the family's one source of truth) — **not** a bundled mirror, and **not** the full references. The
30
33
  live read is **lazy + fail-loud**: the engine is consulted **only when a slot actually needs filling**,
31
- so a deployment whose pointers are already filled reconciles to a zero-diff no-op even on a host
32
- without the engine; but when a fill **is** needed and the engine is **absent/invalid**, reconcile
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
36
+ **is** needed and the engine is **absent/invalid**, reconcile
33
37
  **STOPs** — report it in plain language with the one-line install command
34
38
  `npx @sabaiway/agent-workflow-engine@latest init` (`npx @sabaiway/agent-workflow-kit@latest init`
35
39
  installs the engine for you; translate, never leak tool internals). Contract per slot: exactly one
36
40
  ordered `start → end` pair; a malformed slot (single, reversed, nested, duplicate) or a missing /
37
- duplicate anchor → **STOP with an error**, never edit (the file is left byte-for-byte unchanged). The
38
- **orchestration pointer is soft-skipped** (reported, never silent) when — and only when — adding it
39
- would exceed the 100-line cap: the methodology pointer still lands and the upgrade continues.
41
+ duplicate anchor → **STOP with an error**, never edit (the file is left byte-for-byte unchanged). A
42
+ **chained pointer is soft-skipped** (reported, never silent) when — and only when — adding it would
43
+ exceed the 100-line cap, the engine is too old to ship its fragment, or (autonomy only) its anchor
44
+ (the orchestration pair) is absent: every prior pointer still lands and the upgrade continues.
40
45
 
41
46
  **Agent-rules lens refresh (runs in BOTH paths).** Its precondition is its own target file — run it
42
47
  only after `<project>/docs/ai/agent_rules.md` exists: after the substrate deploy in the
@@ -9,7 +9,11 @@ The tool runs the backend detector and appends the recipe recommendation itself
9
9
  recipe clause of its own, ever.
10
10
 
11
11
  - **Placeholder template (structure only — never copy this example; paste the tool's line):**
12
- `backends: <alias> <✓|✗> <readiness> · <alias> <✓|✗> <readiness> — run /agent-workflow-kit backends · recipes: <recommendation clause> — see /agent-workflow-kit recipes`
12
+ `backends: <alias> <✓|✗> <readiness> · <alias> <✓|✗> <readiness> — run /agent-workflow-kit backends · recipes: <recommendation clause> — see /agent-workflow-kit recipes · autonomy: <per-activity levels> (<policy state>)`
13
+ - The **`autonomy:` segment is appended by the composer itself** (`composeAutonomyFacts`, AD-044) —
14
+ per-activity levels + the policy state (computed defaults / declared / MALFORMED, loud) — the
15
+ agent never types it; the composer resolves the policy from the project root, so the line is
16
+ correct from any working directory inside the project.
13
17
  - The **`recipes:` clause is appended after** the `— run /agent-workflow-kit backends` pointer (never
14
18
  replacing it) and routes to the read-only `recipes` mode (`see /agent-workflow-kit recipes`). It is
15
19
  **never blank**: both backends ready → *"Council available, Reviewed the everyday default"*; one
@@ -57,6 +57,8 @@ Start-of-session, during-work, and task-completion procedures live in [`docs/ai/
57
57
  <!-- workflow:methodology:end -->
58
58
  <!-- workflow:orchestration:start -->
59
59
  <!-- workflow:orchestration:end -->
60
+ <!-- workflow:autonomy:start -->
61
+ <!-- workflow:autonomy:end -->
60
62
 
61
63
  ---
62
64
 
@@ -68,7 +70,6 @@ Start-of-session, during-work, and task-completion procedures live in [`docs/ai/
68
70
  | No `any` / no unsafe casts | Linter / type-checker |
69
71
  | Functional style (no mutation in app code) | Linter |
70
72
  | Interactive elements semantic (button/link, not div+onClick) | Linter / a11y |
71
- | No hardcoded colors — design tokens only | Code review |
72
73
  | No business logic in components → hooks/services | Architecture review |
73
74
  | No changes without tests (TDD) | Required |
74
75
  | Check page docs before changes; update them after | Process |
@@ -0,0 +1,3 @@
1
+ {
2
+ "_README": "Per-project autonomy policy: red-lines (always) + per-activity autonomy level. Hand-editable; or use the set-autonomy writer (previews, then writes valid JSON). Strict JSON — no comments."
3
+ }
@@ -236,6 +236,15 @@ export const resolveAutonomy = (config) => {
236
236
  return { redlines, activities };
237
237
  };
238
238
 
239
+ // isSparseSeedConfig(config) → true when the policy file is STRUCTURALLY the deploy seed: meta keys
240
+ // only (`_README` today) — no red-lines section, no activity entry. Seed detection must be
241
+ // structural, never resolved-equality: an EXPLICIT policy that declares exactly the default values
242
+ // is a real DECLARATION (its render — the red-line ask rules included — must not be suppressed as
243
+ // "just the seed"), while the seed declares nothing (codex, Segment B closing). Shared by every
244
+ // autonomy read surface (advisor / recipes / procedures / grounding) so the four can never diverge.
245
+ export const isSparseSeedConfig = (config) =>
246
+ isJsonObject(config) && Object.keys(config).every((k) => k.startsWith('_'));
247
+
239
248
  // ── pure merge + canonical serialization ────────────────────────────────────────────
240
249
 
241
250
  // A pure deep-equal over the JSON-ish policy shape (plain objects + string values). Used only for the
@@ -288,9 +297,10 @@ export const serializeAutonomy = (config) => {
288
297
 
289
298
  // ── the canonical seed (Decision 5) ──────────────────────────────────────────────────
290
299
  // AUTONOMY_README is the onboarding note; SEED_AUTONOMY is the Decision-5 fixture the set-autonomy
291
- // writer seeds and the config tests copy/validate verbatim. Plan 1 ships NO references/templates/
292
- // autonomy.json and NO template-parity test (both Plan 4) — SEED_AUTONOMY's Plan-1 consumers are the
293
- // autonomy-config tests and the render's "seed one first" guidance.
300
+ // WRITER seeds (opinionated sandbox levels) and the config tests copy/validate verbatim. DISTINCT
301
+ // from the Plan-4 deploy template `references/templates/autonomy.json` (bootstrap/upgrade ensure):
302
+ // that seed is SPARSE `_README` only, defaults-equivalent by the template-parity pin
303
+ // (resolveAutonomy(template) ≡ resolveAutonomy(null)) — so deploying it never changes behavior.
294
304
  export const AUTONOMY_README =
295
305
  'Per-project autonomy policy: red-lines (always) + per-activity autonomy level. Hand-editable; or ' +
296
306
  'use the set-autonomy writer (previews, then writes valid JSON). Strict JSON — no comments.';
@@ -98,6 +98,13 @@ const CATALOG = [
98
98
  kind: READ_ONLY,
99
99
  oneLine: 'List every command, grouped, marking each as read-only or as one that makes changes.',
100
100
  },
101
+ {
102
+ key: 'recommendations',
103
+ invocation: invocationOf('recommendations'),
104
+ group: 'Inspect',
105
+ kind: READ_ONLY,
106
+ oneLine: 'See what in this deployment is configured sub-optimally — each item with a one-line benefit and the exact consent-gated command that applies it; renders even when everything is optimal.',
107
+ },
101
108
  {
102
109
  key: 'gates',
103
110
  invocation: invocationOf('gates'),
@@ -147,6 +154,13 @@ const CATALOG = [
147
154
  kind: GUARDED,
148
155
  oneLine: 'Check whether this machine can run the Claude sandbox and, only with your explicit consent, install what is missing — privileged, preview-first, consent-per-run; it never auto-runs and never commits.',
149
156
  },
157
+ {
158
+ key: 'sandbox-masks',
159
+ invocation: invocationOf('sandbox-masks'),
160
+ group: 'Configure',
161
+ kind: GUARDED,
162
+ oneLine: 'Hide the sandbox’s injected device masks from git status via one managed block in the repo’s local exclude file — probe-derived (never a frozen list), preview first, full-block replace on your yes; reviews already ignore the masks by construction.',
163
+ },
150
164
  {
151
165
  key: 'recipes',
152
166
  invocation: invocationOf('recipes'),
@@ -187,7 +201,7 @@ const CATALOG = [
187
201
  invocation: invocationOf('grounding'),
188
202
  group: 'Orchestrate',
189
203
  kind: WRITER,
190
- oneLine: 'Assemble the verified-facts payload a grounded review runs against — the entry-point Hard Constraints plus a plan’s decision sections; prints it, or writes ONE scratch file with --out.',
204
+ oneLine: 'Assemble the verified-facts payload a grounded review runs against — the entry-point Hard Constraints, the effective autonomy policy, plus a plan’s decision sections; prints it, or writes ONE scratch file with --out.',
191
205
  },
192
206
  {
193
207
  key: 'review-ledger',
@@ -27,6 +27,10 @@ export const EXPECTED_MEMORY_NAME = 'agent-workflow-memory';
27
27
  // DOES seed orchestration.json (Mode: upgrade step 3). This closes the stale-memory trap that the
28
28
  // read-only family-registry note (MEMORY_ORCH_TEMPLATE_REL) only INFORMS about — the gate ACTS. The
29
29
  // two key on the same asset; a cross-tool parity test pins them in lockstep.
30
+ // The autonomy seed (AD-044 Plan 4) is deliberately NOT here: the Plan-3 decision stands — no
31
+ // detectMemory autonomy-marker gate. A memory too old to ship references/templates/autonomy.json
32
+ // stays delegable; the kit-side upgrade ensure seeds the policy from the kit's own template twin,
33
+ // and the family-registry caveat INFORMS about the stale memory (inform, never gate).
30
34
  export const REQUIRED_MEMORY_ASSETS = [
31
35
  { path: 'references/templates', type: 'dir' },
32
36
  { path: 'references/templates/orchestration.json', type: 'file' },
@@ -85,7 +89,7 @@ export const handoffPlan = (delegate) =>
85
89
  memoryWrites: ['docs/ai/', 'AGENTS.md', 'docs/ai/.memory-version'],
86
90
  // The lens region runs AFTER the substrate deploy (its own precondition: the file exists)
87
91
  // — it converges a stale-memory seed to the installed engine's canon (AD-041).
88
- kitWrites: ['AGENTS.md methodology slot', 'docs/ai/agent_rules.md lens region', 'docs/ai/.workflow-version'],
92
+ kitWrites: ['AGENTS.md pointer slots (methodology / orchestration / autonomy)', 'docs/ai/agent_rules.md lens region', 'docs/ai/.workflow-version'],
89
93
  stampsPresent: ['.memory-version', '.workflow-version'],
90
94
  memoryRaisesCommitGate: false,
91
95
  commitGate: 'kit-only-after-injection',
@@ -93,11 +97,11 @@ export const handoffPlan = (delegate) =>
93
97
  : {
94
98
  mode: 'fallback',
95
99
  memoryWrites: [],
96
- // Fallback now ships the kit's OWN AGENTS.md carrying the EMPTY methodology slot (Plan 2);
97
- // the kit reconciles it (ensure-slot + inject-because-empty) exactly like the delegate
98
- // path — so both paths end with a FILLED slot, not inline methodology. The lens region
100
+ // Fallback now ships the kit's OWN AGENTS.md carrying the EMPTY pointer slots (Plan 2);
101
+ // the kit reconciles them (ensure-slot + inject-because-empty) exactly like the delegate
102
+ // path — so both paths end with FILLED slots, not inline methodology. The lens region
99
103
  // runs after the fallback-template copy of docs/ai (same reconcile, both paths — AD-041).
100
- kitWrites: ['docs/ai/', 'AGENTS.md', 'AGENTS.md methodology slot', 'docs/ai/agent_rules.md lens region', 'docs/ai/.workflow-version'],
104
+ kitWrites: ['docs/ai/', 'AGENTS.md', 'AGENTS.md pointer slots (methodology / orchestration / autonomy)', 'docs/ai/agent_rules.md lens region', 'docs/ai/.workflow-version'],
101
105
  stampsPresent: ['.workflow-version'],
102
106
  memoryRaisesCommitGate: false,
103
107
  commitGate: 'kit-only-after-injection',
@@ -88,7 +88,7 @@ const RAW_BACKENDS = [
88
88
  ],
89
89
  grounding: 'automatic — the wrapper precomputes the full working-tree change set (repo map, status, diffs, untracked contents) and codex auto-merges the root AGENTS.md; no grounding flags',
90
90
  continue: [],
91
- receipt: 'side effect — a successful review appends one JSON receipt line to <git dir>/agent-workflow-review-receipts.jsonl (AW_REVIEW_RECEIPTS overrides): fingerprint = sha256 over the canonical uncommitted-state payload (staged diff + unstaged diff + untracked-not-ignored contents — the review-payload domain) in code mode, the artifact-file sha256 in plan mode; verdict parsed from the mandated literal verdict line (schema mode: the verdict field); always fresh:true (one-shot) + grounded:true (native AGENTS.md auto-merge, factsHash null); a write failure warns, never fails the review',
91
+ receipt: 'side effect — a successful review appends one JSON receipt line to <git dir>/agent-workflow-review-receipts.jsonl (AW_REVIEW_RECEIPTS overrides): fingerprint = sha256 over the canonical uncommitted-state payload (staged diff + unstaged diff + untracked-not-ignored contents — the review-payload domain; never-committable untracked paths — character/block devices, FIFOs, sockets — are excluded from the domain entirely, untracked symlinks/directories ride as name-only notes) in code mode, the artifact-file sha256 in plan mode; verdict parsed from the mandated literal verdict line (schema mode: the verdict field); always fresh:true (one-shot) + grounded:true (native AGENTS.md auto-merge, factsHash null); a write failure warns, never fails the review',
92
92
  },
93
93
  },
94
94
  bin: 'codex',
@@ -121,7 +121,7 @@ const RAW_BACKENDS = [
121
121
  'agy-review --continue [--decided @f] [--focus "…"]',
122
122
  'agy-review --conversation <id> [--decided @f] [--focus "…"]',
123
123
  ],
124
- receipt: "side effect — a successful review appends one JSON receipt line to <git dir>/agent-workflow-review-receipts.jsonl (AW_REVIEW_RECEIPTS overrides; plan/diff outside a git tree: warn + skip unless overridden): fingerprint = sha256 over the canonical uncommitted-state payload (staged diff + unstaged diff + untracked-not-ignored contents — the review-payload domain) in code mode, the artifact-file sha256 in plan/diff mode; verdict recorded verbatim from the mandated '### Verdict' section (SHIP / SHIP WITH NITS / REWORK); grounded = whether a NON-EMPTY --facts payload was supplied (an empty payload records grounded:false — fail-closed, the state gate rejects it), factsHash = sha256 of the facts payload; a continuation receipt is fresh:false (informational-only — it cannot attest the folded tree); a write failure warns, never fails the review",
124
+ receipt: "side effect — a successful review appends one JSON receipt line to <git dir>/agent-workflow-review-receipts.jsonl (AW_REVIEW_RECEIPTS overrides; plan/diff outside a git tree: warn + skip unless overridden): fingerprint = sha256 over the canonical uncommitted-state payload (staged diff + unstaged diff + untracked-not-ignored contents — the review-payload domain; never-committable untracked paths — character/block devices, FIFOs, sockets — are excluded from the domain entirely, untracked symlinks/directories ride as name-only notes) in code mode, the artifact-file sha256 in plan/diff mode; verdict recorded verbatim from the mandated '### Verdict' section (SHIP / SHIP WITH NITS / REWORK); grounded = whether a NON-EMPTY --facts payload was supplied (an empty payload records grounded:false — fail-closed, the state gate rejects it), factsHash = sha256 of the facts payload; a continuation receipt is fresh:false (informational-only — it cannot attest the folded tree); a write failure warns, never fails the review",
125
125
  },
126
126
  },
127
127
  bin: 'agy',
@@ -27,12 +27,15 @@ import { SCHEMA_VERSION, REVIEW_CAP, V4_CLASSES, V4_OVERRIDE_SCOPES } from './re
27
27
  import { HARD_MAX, DEFAULT_DIFF_CAP } from './review-ledger-write.mjs';
28
28
  import { RESULT_SCHEMA_VERSION } from './fold-completeness.mjs';
29
29
  import { EXIT as DOCTOR_EXIT, STATUS as DOCTOR_STATUS, TRUSTED_DIRS as DOCTOR_TRUSTED_DIRS } from './autonomy-doctor.mjs';
30
+ import { RECOMMENDATIONS_SECTION_HEADER, RECOMMENDATIONS_EMPTY_LINE } from './recommendations.mjs';
30
31
 
31
32
  const KIT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
32
33
 
33
34
  const REVIEW_LEDGER_DOC = 'references/modes/review-ledger.md';
34
35
  const FOLD_DOC = 'references/modes/fold-completeness.md';
35
36
  const AUTONOMY_DOCTOR_DOC = 'references/modes/autonomy-doctor.md';
37
+ const RECOMMENDATIONS_DOC = 'references/modes/recommendations.md';
38
+ const UPGRADE_DOC = 'references/modes/upgrade.md';
36
39
 
37
40
  // A typed usage failure (exit 2) for the CLI parser — the codebase's typed-error idiom (no classes).
38
41
  const usageFail = (message) => Object.assign(new Error(message), { exitCode: 2 });
@@ -72,6 +75,11 @@ export const BINDINGS = Object.freeze([
72
75
  .filter((token) => token !== DOCTOR_STATUS.usage)
73
76
  .map((token) => valueBinding(`doctor-status:${token}`, token, token, [AUTONOMY_DOCTOR_DOC])),
74
77
  valueBinding('doctor-trusted-dirs', DOCTOR_TRUSTED_DIRS.join(':'), DOCTOR_TRUSTED_DIRS.join(':'), [AUTONOMY_DOCTOR_DOC]),
78
+ // The upgrade Recommendations section contract (AD-044 Plan 4): the section header and the
79
+ // exact empty-state line must render in BOTH the mode doc and upgrade.md (both exits reference
80
+ // them) — a reworded doc would silently break the paste-verbatim contract.
81
+ valueBinding('recommendations-header', RECOMMENDATIONS_SECTION_HEADER, RECOMMENDATIONS_SECTION_HEADER, [RECOMMENDATIONS_DOC, UPGRADE_DOC]),
82
+ valueBinding('recommendations-empty-line', RECOMMENDATIONS_EMPTY_LINE, RECOMMENDATIONS_EMPTY_LINE, [RECOMMENDATIONS_DOC, UPGRADE_DOC]),
75
83
  ].map((b) => Object.freeze(b)));
76
84
 
77
85
  // ── the pure checker (readText is injectable for hermetic tests) ────────────────────────
@@ -31,6 +31,12 @@ export const ORCHESTRATION_FRAGMENT_REL = 'references/orchestration-slot.md';
31
31
  // CLI fail loudly on — an engine too old to ship it. readEngineFragment accepts an arbitrary `rel`
32
32
  // (no whitelist), so no further plumbing is needed beyond this constant.
33
33
  export const PROCEDURES_FRAGMENT_REL = 'references/procedures.md';
34
+ // The autonomy-policy slot fragment — the THIRD bounded fragment the kit injects (AD-044 Plan 3),
35
+ // the cross-agent read contract for docs/ai/autonomy.json. An engine released before it does not
36
+ // ship it; detectEngine({ rel }) lets the chained reconcile soft-skip that too-old engine (and
37
+ // `status` caveat it) instead of discarding the methodology/orchestration fills. readEngineFragment
38
+ // accepts an arbitrary `rel`, so no further plumbing is needed beyond this constant.
39
+ export const AUTONOMY_FRAGMENT_REL = 'references/autonomy-slot.md';
34
40
  // The agent-rules lens pair — the canonical lens block + its append-only prior store (engine
35
41
  // >= 1.13.0; an older engine ships neither). The kit's lens-region reconcile live-reads BOTH via
36
42
  // readEngineFragment({ rel }); detectEngine({ rel }) lets it soft-skip a too-old engine (and
@@ -26,7 +26,7 @@ import { resolveDir, detectBackends, findOnPath } from './detect-backends.mjs';
26
26
  import { parseSemver, compareSemver } from './semver-lite.mjs';
27
27
  import { validateManifest, readAuthoritativeVersion, UNSUPPORTED, INVALID } from './manifest/validate.mjs';
28
28
  import { START_MARKER, excludePath, inferVisibility } from './hide-footprint.mjs';
29
- import { readEngineFragment, ORCHESTRATION_FRAGMENT_REL, PROCEDURES_FRAGMENT_REL, LENS_FRAGMENT_REL, LENS_PRIORS_REL } from './engine-source.mjs';
29
+ import { readEngineFragment, ORCHESTRATION_FRAGMENT_REL, PROCEDURES_FRAGMENT_REL, AUTONOMY_FRAGMENT_REL, LENS_FRAGMENT_REL, LENS_PRIORS_REL } from './engine-source.mjs';
30
30
  import { ACTIVITIES, resolveActivityRecipe } from './recipes.mjs';
31
31
  // The config reader lives in orchestration-config.mjs (the single config contract). The read-only status
32
32
  // settings-survey reuses THIS reader (one strict-JSON + loud-on-malformed contract), not a second copy.
@@ -182,6 +182,7 @@ export const classifyMember = (member, deps = {}) => {
182
182
  // caveat keys on both — an engine missing only the prior store must never report healthy.
183
183
  const ENGINE_FRAGMENT_CAVEATS = [
184
184
  { rels: [ORCHESTRATION_FRAGMENT_REL], caveat: 'engine present but does not supply the recipes pointer (too old / incomplete) — run `npx @sabaiway/agent-workflow-engine@latest init`' },
185
+ { rels: [AUTONOMY_FRAGMENT_REL], caveat: 'engine present but does not supply the autonomy pointer (too old / incomplete) — run `npx @sabaiway/agent-workflow-engine@latest init`' },
185
186
  { rels: [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`' },
186
187
  { rels: [LENS_FRAGMENT_REL, LENS_PRIORS_REL], caveat: 'engine present but does not ship the agent-rules lens canon (the fragment + its prior store; too old / incomplete — engine >= 1.13.0) — run `npx @sabaiway/agent-workflow-engine@latest init`' },
187
188
  ];
@@ -192,10 +193,16 @@ const ENGINE_FRAGMENT_CAVEATS = [
192
193
  // adds a parity drift-guard tying this path to that required-asset set, so the note and the gate can
193
194
  // never key on different files (until then they are kept in lockstep by review).
194
195
  export const MEMORY_ORCH_TEMPLATE_REL = 'references/templates/orchestration.json';
196
+ // The autonomy-policy seed (AD-044 Plan 4) gets the SAME inform-never-gate treatment: the
197
+ // delegation gate deliberately does NOT key on it (the Plan-3 decision), so this caveat is the
198
+ // discovery mechanism — the recommendations advisor surfaces it via surveyFamily.
199
+ export const MEMORY_AUTONOMY_TEMPLATE_REL = 'references/templates/autonomy.json';
195
200
  // Worded as an honest OBSERVATION, not a diagnosis (absence can mean old OR incomplete), and it makes
196
201
  // NO claim about an orchestration.json seeding outcome (that depends on delegate-vs-fallback).
197
202
  const MEMORY_BEHIND_NOTE =
198
203
  "the memory installed here doesn't include the current orchestration template — refresh it with `npx @sabaiway/agent-workflow-memory@latest init`, then restart the session.";
204
+ const MEMORY_AUTONOMY_BEHIND_NOTE =
205
+ "the memory installed here doesn't include the current autonomy-policy template — refresh it with `npx @sabaiway/agent-workflow-memory@latest init`, then restart the session.";
199
206
 
200
207
  // ── the bridge freshness probe (deterministic-first — INV-A / INV-B) ─────────────
201
208
  // The bridges are not npm packages: their ONLY delivery channel is the copy bundled inside this kit
@@ -261,12 +268,19 @@ export const surveyFamily = (deps = {}) =>
261
268
  // Only attach when it is provably ABSENT (a non-ENOENT probe error → 'unknown' → skip, never a
262
269
  // false "missing" claim). Mirrors the engine-caveat SHAPE; keyed on the Step-2.4 required asset.
263
270
  if (row.kind === 'memory-substrate' && row.manifestState === OK && row.skillDir) {
264
- const templateProbe = probeMarker(join(row.skillDir, MEMORY_ORCH_TEMPLATE_REL), deps);
265
- if (templateProbe === 'absent') {
266
- row.caveats = [...(row.caveats ?? []), MEMORY_BEHIND_NOTE];
267
- } else if (templateProbe === 'unknown') {
268
- // Could not verify → this row must not be counted "checked, current" by the verdict (INV-B).
269
- row.freshness = FRESH_UNKNOWN;
271
+ // Two required-template probes, same shape: orchestration (Step 2.4) + autonomy (AD-044
272
+ // Plan 4 — inform, never gate). Each attaches its own caveat on provable absence only.
273
+ for (const [rel, note] of [
274
+ [MEMORY_ORCH_TEMPLATE_REL, MEMORY_BEHIND_NOTE],
275
+ [MEMORY_AUTONOMY_TEMPLATE_REL, MEMORY_AUTONOMY_BEHIND_NOTE],
276
+ ]) {
277
+ const templateProbe = probeMarker(join(row.skillDir, rel), deps);
278
+ if (templateProbe === 'absent') {
279
+ row.caveats = [...(row.caveats ?? []), note];
280
+ } else if (templateProbe === 'unknown') {
281
+ // Could not verify → this row must not be counted "checked, current" by the verdict (INV-B).
282
+ row.freshness = FRESH_UNKNOWN;
283
+ }
270
284
  }
271
285
  }
272
286
  // Bridge freshness probe (INV-A / INV-B): only for a provably-OURS placed bridge (manifestState
@@ -1029,6 +1029,11 @@ loud advisory failure) but leaves --check unaffected.
1029
1029
 
1030
1030
  The read-only gate is a SEPARATE tool: node fold-completeness.mjs --check / --status / --json.
1031
1031
 
1032
+ Sandbox-safe: the RUNNER itself needs no network and writes only repo-local state (the D4 sandbox
1033
+ lane). The spawned suite/bound commands are COMMAND-SHAPE dependent — --suite / AW_FOLD_SUITE_CMD /
1034
+ AW_FOLD_BOUND_CMD / the verification profile can name project-defined commands; the default
1035
+ (node --test on stdout TAP) is plain and no-network — keep overrides sandbox-safe by shape.
1036
+
1032
1037
  Exit codes: 0 written / advisory printed; 1 a typed STOP (loop derivation / suite discovery / a --red
1033
1038
  or --reattest refusal / a malformed SARIF on --findings / malformed record / fs error); 2 usage.`;
1034
1039