@gtrabanco/pi-agentic-workflow 0.30.0 → 0.34.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.
Files changed (45) hide show
  1. package/README.md +41 -0
  2. package/dist/config/defaults.js +3 -0
  3. package/dist/config/merge.js +5 -0
  4. package/dist/config/schema.d.ts +0 -5
  5. package/dist/config/schema.js +31 -1
  6. package/dist/config/types.d.ts +15 -0
  7. package/dist/extension/engram.d.ts +119 -0
  8. package/dist/extension/engram.js +187 -0
  9. package/dist/extension/index.js +70 -3
  10. package/dist/settings/console.js +68 -22
  11. package/package.json +1 -1
  12. package/skills/audit-pr/SKILL.md +1 -1
  13. package/skills/audit-pr/references/03_AUDIT_PROCESS.md +3 -2
  14. package/skills/execute-phase/SKILL.md +22 -8
  15. package/skills/execute-phase/references/EXECUTION_CONTRACT.md +15 -9
  16. package/skills/execute-phase/references/FOLDING.md +11 -8
  17. package/skills/execute-phase/references/PREFLIGHT.md +7 -3
  18. package/skills/fold-findings/SKILL.md +28 -17
  19. package/skills/fold-findings/references/FOLD_PROCESS.md +5 -3
  20. package/skills/init-workspace/SKILL.md +13 -2
  21. package/skills/init-workspace/references/BOOTSTRAP_WRITE.md +11 -5
  22. package/skills/init-workspace/references/ENGRAM.md +107 -0
  23. package/skills/log-session/SKILL.md +17 -1
  24. package/skills/orchestration-envelope/references/TURN_CONTRACT.md +2 -1
  25. package/skills/pre-execution-review/SKILL.md +1 -1
  26. package/skills/pre-execution-review/references/LEDGERS.md +30 -2
  27. package/skills/review-change/SKILL.md +1 -1
  28. package/skills/review-change/references/OUTPUT_AND_GUARDRAILS.md +2 -1
  29. package/skills/review-change/references/PERSIST_AND_DECIDE.md +30 -9
  30. package/skills/review-change/references/REVIEW_PROCESS.md +14 -6
  31. package/skills/review-implementation/SKILL.md +10 -2
  32. package/skills/review-implementation/references/CLASSIFY.md +21 -4
  33. package/skills/triage-issue/SKILL.md +18 -12
  34. package/skills/triage-issue/references/FOLD_LEDGER.md +11 -3
  35. package/skills/triage-issue/references/ISSUE_PROCESS.md +3 -2
  36. package/skills/triage-issue/references/REVIEW_FINDING_PROCESS.md +17 -8
  37. package/skills/unit-lane/SKILL.md +23 -5
  38. package/skills/unit-lane/references/DESIGN.md +1 -1
  39. package/skills/unit-lane/references/EVIDENCE.md +7 -3
  40. package/skills/unit-lane/references/MEMORY.md +179 -0
  41. package/skills/unit-lane/references/PLAN.md +11 -1
  42. package/skills/unit-lane/references/RESEARCH.md +3 -3
  43. package/skills/verification-contract/SKILL.md +9 -7
  44. package/skills/workflow-status/SKILL.md +4 -2
  45. package/skills/workflow-status/references/ENVELOPE_FIELDS.md +3 -1
@@ -650,38 +650,84 @@ function describeRouting(file) {
650
650
  ];
651
651
  return routes.join(" · ");
652
652
  }
653
+ /**
654
+ * The canonical write order for a config file. `dirty()` compares the cleaned
655
+ * draft against the cleaned file as JSON text, so the order is part of the
656
+ * contract: the same keys written in a different order would read as an edit.
657
+ * Keys outside this list are appended in the draft's own order, so a key the
658
+ * console never prompts for still survives the save (F4).
659
+ */
660
+ const KEY_ORDER = [
661
+ "default",
662
+ "commands",
663
+ "onUnavailableRoute",
664
+ "onSettle",
665
+ "pathProtection",
666
+ "advance",
667
+ "recommendedModels",
668
+ "memory",
669
+ "profiles",
670
+ "profileOrder",
671
+ "profileFallback",
672
+ ];
653
673
  /**
654
674
  * Keep everything the file held or the operator set; drop only what says nothing
655
675
  * (an empty route). Eliding by VALUE — treating an explicit `inherit` or `stop` as
656
676
  * absent because a shipped default happens to agree — silently discards the only
657
677
  * two moves that matter at project scope: switching a command off under a global
658
678
  * route, and re-arming fail-closed over a global `inherit` (F4).
679
+ *
680
+ * The copy is driven by the draft's OWN keys, not by a list of the keys this
681
+ * console edits. Enumerating them silently deleted every valid key the console
682
+ * never prompts for — `memory` (the Engram consent switch, feature 66 AC3),
683
+ * `pathProtection` and `advance` — so an opt-in written by hand vanished on the
684
+ * next save with no message and no error (F4/VF-4). A key the parser accepted is
685
+ * never a key this function may drop.
659
686
  */
660
687
  function clean(draft) {
661
- const file = {};
662
- if (draft.default && Object.keys(draft.default).length > 0)
663
- file.default = { ...draft.default };
664
- const commands = {};
665
- for (const [name, route] of Object.entries(draft.commands ?? {})) {
666
- if (Object.keys(route).length > 0)
667
- commands[name] = { ...route };
668
- }
669
- if (Object.keys(commands).length > 0)
670
- file.commands = commands;
671
- if (draft.onUnavailableRoute)
672
- file.onUnavailableRoute = draft.onUnavailableRoute;
673
- if (draft.onSettle)
674
- file.onSettle = draft.onSettle;
675
- if (draft.recommendedModels !== undefined)
676
- file.recommendedModels = draft.recommendedModels;
677
- if (draft.profiles && Object.keys(draft.profiles).length > 0) {
678
- file.profiles = structuredClone(draft.profiles);
688
+ const kept = {};
689
+ for (const [key, value] of Object.entries(draft)) {
690
+ if (value === undefined)
691
+ continue;
692
+ if (key === "commands") {
693
+ const commands = {};
694
+ for (const [name, route] of Object.entries((value ?? {}))) {
695
+ if (route && Object.keys(route).length > 0)
696
+ commands[name] = { ...route };
697
+ }
698
+ if (Object.keys(commands).length > 0)
699
+ kept.commands = commands;
700
+ continue;
701
+ }
702
+ if (key === "profiles") {
703
+ const profiles = {};
704
+ for (const [name, profile] of Object.entries((value ?? {}))) {
705
+ if (profile && Object.keys(profile).length > 0)
706
+ profiles[name] = structuredClone(profile);
707
+ }
708
+ if (Object.keys(profiles).length > 0)
709
+ kept.profiles = profiles;
710
+ continue;
711
+ }
712
+ if (Array.isArray(value)) {
713
+ // `profileOrder` is the only list at this level: an empty order says
714
+ // nothing, so it is elided exactly as the empty route was.
715
+ if (value.length > 0)
716
+ kept[key] = [...value];
717
+ continue;
718
+ }
719
+ if (typeof value === "object" && value !== null && Object.keys(value).length === 0)
720
+ continue;
721
+ kept[key] = typeof value === "object" && value !== null ? structuredClone(value) : value;
679
722
  }
680
- if (draft.profileOrder && draft.profileOrder.length > 0) {
681
- file.profileOrder = [...draft.profileOrder];
723
+ const file = {};
724
+ for (const key of KEY_ORDER) {
725
+ if (key in kept)
726
+ file[key] = kept[key];
682
727
  }
683
- if (draft.profileFallback && Object.keys(draft.profileFallback).length > 0) {
684
- file.profileFallback = { ...draft.profileFallback };
728
+ for (const key of Object.keys(kept)) {
729
+ if (!(key in file))
730
+ file[key] = kept[key];
685
731
  }
686
732
  return file;
687
733
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtrabanco/pi-agentic-workflow",
3
- "version": "0.30.0",
3
+ "version": "0.34.0",
4
4
  "description": "Pi package: canonical agentic-workflow skills, friendly slash commands, and per-command model routing.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: audit-pr
3
3
  user-invocable: true
4
- version: 5.6.2
4
+ version: 5.6.3
5
5
  argument-hint: <pr-number> (optional — defaults to the current branch's PR)
6
6
  author: "Gabriel Trabanco <1969593+gtrabanco@users.noreply.github.com>"
7
7
  license: MIT
@@ -29,8 +29,9 @@
29
29
  - **BLOCKED** — one or more gates fail; output the ranked blocker list.
30
30
  6. **Persist blockers to the fold ledger (BLOCKED verdict only).** Every blocker
31
31
  on a **BLOCKED** verdict is, by definition, fix-now — merge is gated on it.
32
- Append each to the unit's fix-now fold ledger `review-findings.md` (same
33
- location and fixed schema
32
+ Append each to the unit's fix-now fold ledger — its `## Findings` section for
33
+ a migrated unit (issue #308), `review-findings.md` for a legacy one; probe the
34
+ doc first and write to exactly one carrier (same fixed schema
34
35
  `| id | file:line | axis | severity | class | route | folded |` as
35
36
  `review-change`'s persist step) — the **same ledger**, not a separate one
36
37
  (D4: the fold cycle consumes one list). **Merged unit → no write** — check
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: execute-phase
3
3
  user-invocable: true
4
- version: 6.1.0
4
+ version: 6.2.2
5
5
  argument-hint: <NN-slug> [P<k>|step] [--max-attempts N] [--force]
6
6
  allowed-tools: [Bash, Read, Edit, Write, MultiEdit]
7
7
  author: "Gabriel Trabanco <1969593+gtrabanco@users.noreply.github.com>"
@@ -35,7 +35,8 @@ done:
35
35
 
36
36
  ```
37
37
  ✓ Required user inputs were answered concretely (not defaulted). Nothing here
38
- is inferred: a gap runs `node scripts/needs-input.mjs <unit-dir>` and the
38
+ is inferred: a gap runs `bun scripts/needs-input.mjs <unit-dir>` (node fallback
39
+ if bun is not installed) and the
39
40
  `open-questions@1` block is pasted, then the run stops.
40
41
  ✓ Triage verified — unit doc Evidence section header matches
41
42
  `bun scripts/unit-route.mjs --triage <slug>` output block (PREFLIGHT.md)
@@ -44,6 +45,8 @@ done:
44
45
  ✓ Evidence rows updated (command → exit/digest → output → verified-by)
45
46
  ✓ Progress log updated with a dated `YYYY-MM-DD HH:MM` entry that names the
46
47
  phase id (what, commit, next)
48
+ ✓ Memory boundary ran — the phase's outcome saved under `unit/<NN-slug>/doc`,
49
+ or the mirror marked pending when the layer is degraded
47
50
  ✓ Diff guard passed (on implement steps) — see `diff-guard` in PREFLIGHT.md
48
51
  ✓ → Next: block printed as the absolute last output
49
52
  ```
@@ -64,8 +67,8 @@ This skill **never interviews**. It reads its inputs — the unit doc's `Tasks`,
64
67
  one is a **gap**, resolved in one fixed order and never by inference:
65
68
  **(0) provenance** (search the repo's own docs and record where) →
66
69
  **(1) research** (2–3 concrete options with trade-offs; asking for more research
67
- is a legitimate answer) → **(2) halt**: run `node scripts/needs-input.mjs
68
- <unit-dir>`, paste the `open-questions@1` block verbatim, stop. Interactive the
70
+ is a legitimate answer) → **(2) halt**: run `bun scripts/needs-input.mjs
71
+ <unit-dir>` (node fallback if bun is not installed), paste the `open-questions@1` block verbatim, stop. Interactive the
69
72
  block is the pause point, unattended it is a hard fail. Grammar and exit codes:
70
73
  `orchestration-envelope`'s `references/TURN_CONTRACT.md` §Open questions.
71
74
 
@@ -79,9 +82,13 @@ path-protection checkpoint.
79
82
  ## Step 0 — Discover the project
80
83
 
81
84
  Per the agent guide's **Workflow conventions** + **documentation map**, read the
82
- unit doc at `docs/features/<NN-slug>/SPEC.md`. Verify it carries `## Tasks` (P1…Pn),
83
- `## Evidence`, and `## Progress` sections. If absent, the unit is not ready —
84
- stop and suggest `unit-lane` triage first.
85
+ unit doc at `docs/features/<NN-slug>/SPEC.md`. Verify it carries `## Tasks` (P1…Pn)
86
+ **and the receipt sections of the shape it is written in** (#308 accepts both):
87
+ the legacy shape carries `## Evidence` + `## Progress log`, a re-cut unit doc carries
88
+ `## Specs`, `## Findings` + `## Log` (its evidence and gate runs are `E`/`G` log
89
+ lines, its progress is `P` lines). If neither pair is present, the unit is not ready —
90
+ stop and suggest `unit-lane` triage first. `scripts/unit-route.mjs` resolves the
91
+ shape; never judge a re-cut unit by the legacy headings and stop it.
85
92
 
86
93
  ## Process
87
94
 
@@ -136,7 +143,14 @@ names before implementation), then green. UI/adapter glue may test after.
136
143
  output (≤2 lines) | verified-by |`. Append to the Evidence section.
137
144
  3. **Progress log**: append a dated entry —
138
145
  `YYYY-MM-DD HH:MM — <what was done> → <commit sha> — next: <what is next>`.
139
- 4. **Commit**: `git add <changed files>` then `git commit -m "<type>(<scope>):
146
+ 4. **Memory boundary** — this step is a phase transition. Follow
147
+ [the memory protocol](../unit-lane/references/MEMORY.md): before the first
148
+ memory read run `agentic-workflow memory probe` once and record its verdict
149
+ in Evidence; after the step, save the phase id, what it changed, its Evidence
150
+ outcome and any discovery under `topic_key: "unit/<NN-slug>/doc"`, an explicit
151
+ `project` and explicit session attribution (§5). A `degraded` verdict is
152
+ recorded and the mirror marked pending — it never blocks the step.
153
+ 5. **Commit**: `git add <changed files>` then `git commit -m "<type>(<scope>):
140
154
  <step-summary>"` — run it, paste the SHA.
141
155
 
142
156
  ### 6. Red gate (repair loop)
@@ -52,8 +52,10 @@ Something forbidden looks necessary → stop, record it in `decisions.md` or
52
52
 
53
53
  ## Step completion gate
54
54
 
55
- The step completion gate verifies that Evidence rows are complete for the
56
- step's acceptance criteria, the unit doc is committed clean, and the diff guard
55
+ The step completion gate verifies that the step's acceptance criteria have
56
+ complete evidence in the unit's own carrier — the `## Evidence` table rows for a
57
+ legacy-shape unit, `## Log` `E` lines (command, exit, ≤2 lines of output) for a
58
+ re-cut one (#308) — that the unit doc is committed clean, and that the diff guard
57
59
  (if applicable) passed. The mark's shape, ownership, and reuse rule are
58
60
  declared in `LEDGERS.md` (§gate-ran@1).
59
61
 
@@ -76,7 +78,8 @@ GATE-RAN | HEAD <40-hex sha> | <cmds> | exit <code>
76
78
  ✓ Verification gate green — type-check + tests + build actually RUN (paste exit
77
79
  status), never assumed
78
80
  ✓ Every task of this step checked off in the unit doc's `Tasks` section, each
79
- mapped to an Evidence row (code path or test name)
81
+ mapped to an Evidence row (legacy shape) or an `E` log line (re-cut shape) —
82
+ code path or test name
80
83
  ✓ Tests updated/added for every behavior this step changed
81
84
  ✓ Tests-first red run recorded (core/domain + orchestration steps, n/a for
82
85
  test-after UI/adapter steps) — the failing first run's command, exit status,
@@ -88,10 +91,12 @@ GATE-RAN | HEAD <40-hex sha> | <cmds> | exit <code>
88
91
  consumers)
89
92
  ✓ Architecture doc respected (dependency directions, layer boundaries)
90
93
  ✓ Architectural invariants preserved or backed by an explicit recorded decision
91
- ✓ Evidence rows complete for the step's ACs — unit doc committed clean
92
- (one dated `YYYY-MM-DD HH:MM` Progress entry, all Evidence rows current)
93
- ✓ Docs COMMITTED with the step — the unit doc carries all required sections
94
- (Tasks, Evidence, Progress, References); no orphan files remain
94
+ ✓ The step's ACs have complete evidence in the unit's carrier — unit doc committed
95
+ clean (one dated `YYYY-MM-DD HH:MM` Progress entry or `P` log line, all evidence
96
+ current)
97
+ ✓ Docs COMMITTED with the step — the unit doc carries all required sections of its
98
+ own shape (Tasks + Evidence + Progress + References for legacy; Specs + Tasks +
99
+ Findings + Log + References for re-cut); no orphan files remain
95
100
  ```
96
101
 
97
102
  A step that cannot tick every box is **not done**: fix within the step's
@@ -123,8 +128,9 @@ planned work, and inference never prove implementation. A present ledger whose
123
128
  status is `draft`, `contradicted`, or `resolved` stops implementation and routes
124
129
  to discovery or resolution first. If no ledger exists, inspect the repository
125
130
  directly and record `n/a: no normalized repository state`; NRS is optional.
126
- Unit doc evidence rows (Evidence, Progress) are the single source of truth
127
- for step completion — no separate ACCEPTANCE.md, progress.md, or TASKS.md.
131
+ Unit doc evidence is the single source of truth for step completion — the
132
+ `## Evidence`/`## Progress log` sections of a legacy-shape doc, the `## Log` lines of
133
+ a re-cut one (#308) — never a separate ACCEPTANCE.md, progress.md, or TASKS.md.
128
134
 
129
135
  ## Architectural invariants
130
136
 
@@ -24,10 +24,12 @@ findings end up "solved" locally but absent from the merged PR:
24
24
  ✓ Gate RUN and green (exit codes pasted)
25
25
  ✓ Never edit an existing test's expectation to match behaviour — a setup
26
26
  repair keeps assertions at least as strong and never touches expectations
27
- ✓ Evidence rows updated where the finding touched them (the finding's row
28
- in the unit doc's Evidence ledger flipped `folded: no → yes` — the one and
29
- only state transition, `ledger-ownership@1` / `fold-findings:folded-flag`
30
- is the sole writer)
27
+ ✓ Evidence rows updated where the finding touched them, in the unit's own findings
28
+ carrier: a legacy row flipped `folded: no → yes`; a migrated unit's row leaves
29
+ `## Findings` and its fold is a `## Log` `F` line naming the fold commit — that
30
+ carrier has no `folded` column to flip (#308). Either close is the one and only
31
+ state transition, and `ledger-ownership@1` / `fold-findings:folded-flag` is the
32
+ sole writer of it
31
33
  ✓ Progress log advanced with a dated entry noting the fold
32
34
  ✓ `git add` + `git commit` RUN (sha pasted) — e.g.
33
35
  `fix(<scope>): fold review findings — <summary>`
@@ -56,8 +58,9 @@ No auto-merge. Explicit `P<k>` stops after one step; omitted-step mode gates
56
58
  and commits every remaining step before the same final review.
57
59
  ## Gate-run marks (gate-ran@1)
58
60
 
59
- Every verification gate this cycle runs records a `GATE-RAN` mark in the unit
60
- doc's Evidence section at the head it actually ran:
61
+ Every verification gate this cycle runs records a `GATE-RAN` mark at the head it
62
+ actually ran — a row in the legacy `review-findings.md`, a `## Log` `G` line for a
63
+ re-cut unit (#308):
61
64
  `GATE-RAN | HEAD <40-hex sha> | <cmds> | exit <code>`. A green run is recorded;
62
65
  a red run is also recorded (exit code ≠ 0 is evidence, not silence). Any skill
63
66
  may consume a green mark only at the identical HEAD — a changed head re-runs
@@ -70,5 +73,5 @@ the gate. The recorders are `execute-phase:gate-ran-marks` and
70
73
  When the final triaged step closes: mark the unit done, push the branch, and
71
74
  open the PR with `gh pr create` (per the project's Workflow conventions) —
72
75
  regardless of the review still to come. The PR body carries the unit doc's
73
- Evidence section as its verification record and closes the unit's tracked
74
- issues via `Closes #N`.
76
+ verification record (its `## Evidence` rows, or a re-cut unit's `## Log` `E`/`G`
77
+ lines) and closes the unit's tracked issues via `Closes #N`.
@@ -125,7 +125,10 @@ block text, no-forgery and legacy detail:
125
125
  Consume `skills/verification-contract/SKILL.md`. For a current-format unit,
126
126
  the Evidence section in the unit doc **is** the acceptance manifest — each
127
127
  acceptance criterion has a corresponding Evidence row with command, exit/digest,
128
- and output. No sibling file; the unit doc's Evidence section header carries the
128
+ and output. A re-cut unit (#308) has no Evidence section by design: its acceptance
129
+ manifest is `## Specs`, and each criterion's receipt is a `## Log` `E` line; the
130
+ gate below reads that carrier instead, and never stops a migrated unit for lacking
131
+ a section its own template retired. No sibling file; the unit doc's Evidence section header carries the
129
132
  last-reviewed sha. The unit doc's triage block (pasted verbatim from
130
133
  `scripts/unit-route.mjs --triage`) is the step authority: `execute-phase` runs
131
134
  only what the triage block demands.
@@ -136,8 +139,9 @@ only what the triage block demands.
136
139
  - Missing/mismatched Evidence (unit doc has been edited since last execution)
137
140
  → print the verification contract's fixed `EVIDENCE GATE` block and stop.
138
141
  `--force` never bypasses a changed acceptance state.
139
- - Unit with no Evidence section → the unit doc template was incomplete;
140
- create the section before proceeding.
142
+ - Unit with no Evidence section and no `## Log` → the unit doc template was
143
+ incomplete; create the section its shape requires before proceeding. A doc with
144
+ `## Log` is the re-cut shape: its `E`/`G` lines are the rows, never a gap.
141
145
 
142
146
  Run this check before each step in whole-unit mode. The executor may add tests
143
147
  but may not narrow acceptance criteria, weaken assertions, or edit the Acceptance
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: fold-findings
3
3
  user-invocable: true
4
- version: 1.9.0
4
+ version: 1.9.2
5
5
  argument-hint: [finding-id …]
6
6
  author: "Gabriel Trabanco <1969593+gtrabanco@users.noreply.github.com>"
7
7
  license: MIT
@@ -49,19 +49,30 @@ postpone/wontfix/promote/documented-tradeoff rows; those belong to `/triage-issu
49
49
 
50
50
  Per Workflow conventions, read:
51
51
 
52
- 1. The unit's `review-findings.md` under `docs/features/<NN>-<slug>/` or
53
- `docs/fix/<n>-<topic>/`; schema is owned by `review-change`/`audit-pr`:
54
-
55
- ```
56
- | id | file:line | axis | severity | class | route | folded |
57
- ```
58
-
59
- After `VERDICT: BLOCKED`, append missing rows (`class: fix-now`, `folded: no`,
60
- next free `Fn`, dedupe `file:line`+axis), commit as
61
- `docs(<unit>): reconstruct fold ledger from audit-pr blockers`, then fold;
62
- never report “no findings”.
63
- 2. Queue `folded: no` rows; explicit IDs restrict it and leave other rows
64
- untouched. Group by root cause, verifier and rollback boundary.
52
+ 1. The unit's findings carrier: a migrated unit (issue #308) keeps its OPEN rows
53
+ in its own `SPEC.md` under `## Findings`, with folded history and marks as
54
+ `## Log` lines; a legacy unit keeps `review-findings.md` under
55
+ `docs/features/<NN>-<slug>/` or `docs/fix/<n>-<topic>/`. Probe the doc first and
56
+ fall back to the file — a unit with the section never reads both. Each carrier
57
+ has **its own** schema, owned by `review-change`/`audit-pr` and stated for both
58
+ shapes in `pre-execution-review/references/LEDGERS.md`:
59
+
60
+ - legacy (7 cols): `\| id \| file:line \| axis \| severity \| class \| route \| folded \|`
61
+ — closing a row flips `folded: no → yes`, and this skill is that flip's sole writer
62
+ - migrated (6 cols, `findings-table@1`): `\| id \| S# / file:line \| axis \| severity \|
63
+ class \| route \|` — every row it holds is OPEN; closing one **deletes the row** and
64
+ appends a `## Log` `F` line naming the fold commit. There is no `folded` column to
65
+ flip, and `class` here is the repairing owner (`product`/`plan`/`source`/
66
+ `environment`/`runtime`), not a disposition.
67
+
68
+ Never write one carrier's row into the other's grammar. After `VERDICT: BLOCKED`,
69
+ append missing rows in the carrier's own schema (next free `Fn`, dedupe
70
+ `file:line`+axis; legacy `class: fix-now`, `folded: no`), commit as
71
+ `docs(<unit>): reconstruct fold ledger from audit-pr blockers`, then fold; never
72
+ report “no findings”.
73
+ 2. Queue the carrier's open rows — a legacy `folded: no` row, a migrated row of
74
+ `## Findings` (all of them are open); explicit IDs restrict the queue and leave
75
+ other rows untouched. Group by root cause, verifier and rollback boundary.
65
76
  3. The project's verification gate and forge CLI (use the declared forge).
66
77
  4. Whether the branch has an open PR; this decides immediate push after commit.
67
78
 
@@ -126,7 +137,7 @@ conclusion the already-run router printed, never the invocation itself: on
126
137
  `route: replan` the planner command it printed — `/unit-lane <slug>` for a
127
138
  feature unit or `/unit-lane --fix <n>` for a fix unit; on `route: decision` stop
128
139
  and surface the decision to the user, never a planner. `<unit>` is the bare folder number or the full slug (both resolve). The invocation
129
- `node scripts/unit-route.mjs <unit>` is the discovery step the fold already
140
+ `bun scripts/unit-route.mjs <unit>` (node fallback if bun is not installed) is the discovery step the fold already
130
141
  ran; it is never the recommendation.
131
142
 
132
143
  **Freeze-batch (product owner present).** Any taken `fix-now` row whose `Route`
@@ -212,8 +223,8 @@ decision to skip.
212
223
 
213
224
  | Batch state | Branch | `→ Next:` (consumer) |
214
225
  |---|---|---|
215
- | freeze-batch (≥ 1 product-owned row) | `PRODUCT-ROUTE` | `/unit-lane <slug>` (feature) or `/unit-lane --fix <n>` (fix) when the router concludes `product` — the lane repairs the half through its `design` step, its `review` step re-judges it; the conclusion printed by `node scripts/unit-route.mjs <unit>` (discovery step the fold already ran) |
216
- | freeze-batch (≥ 1 replan-class row) | `REPLAN-ROUTE` | `/unit-lane <slug>` (feature) or `/unit-lane --fix <n>` (fix) when the router concludes `replan` — user confirms, the lane's `plan` step appends the phases and its `review` step gates, then `/execute-phase` on this unit; on `route: decision` stop and surface the decision to the user — the conclusion printed by `node scripts/unit-route.mjs <unit>` (discovery step the fold already ran) |
226
+ | freeze-batch (≥ 1 product-owned row) | `PRODUCT-ROUTE` | `/unit-lane <slug>` (feature) or `/unit-lane --fix <n>` (fix) when the router concludes `product` — the lane repairs the half through its `design` step, its `review` step re-judges it; the conclusion printed by `bun scripts/unit-route.mjs <unit>` (node fallback if bun is not installed; discovery step the fold already ran) |
227
+ | freeze-batch (≥ 1 replan-class row) | `REPLAN-ROUTE` | `/unit-lane <slug>` (feature) or `/unit-lane --fix <n>` (fix) when the router concludes `replan` — user confirms, the lane's `plan` step appends the phases and its `review` step gates, then `/execute-phase` on this unit; on `route: decision` stop and surface the decision to the user — the conclusion printed by `bun scripts/unit-route.mjs <unit>` (node fallback if bun is not installed; discovery step the fold already ran) |
217
228
  | `all-repair-in-place` + docs-only + no folded row severity `high` + prior consumer skip decision | `RE-REVIEW-SKIPPED` | skip — consumer has explicitly decided to skip the re-review |
218
229
  | `all-repair-in-place` + docs-only + no folded row severity `high` | `RE-REVIEW-OPTIONAL` | `/review-change` (default, delta mode) — or the consumer's recorded skip decision |
219
230
  | empty batch (class `none`) | `RE-REVIEW-OPTIONAL` | `/review-change` by default — safe: the head is unchanged |
@@ -43,8 +43,10 @@
43
43
 
44
44
  | Condition over the taken batch | Batch class | Fold behavior |
45
45
  |---|---|---|
46
- | ≥ 1 row with frozen class `replan-in-unit` or `decision-required` | `frozen (replan present)` | **freeze-batch** — nothing folds, no `folded: yes` flips, no commits; the receipt records the REPLAN-ROUTE and every retained (unfolded) row id; the loop stops; the consumer is the conclusion printed by `node scripts/unit-route.mjs <unit>` (discovery step the fold already ran), where `<unit>` is the bare folder number or the full slug (both resolve): on `route: replan` the planner command the router printed — `/unit-lane <slug>` (feature) or `/unit-lane --fix <n>` (fix) — the lane appends the phases and its `review` step gates, then `/execute-phase` on this unit; on `route: decision` stop and surface the decision to the user, never a planner |
47
- | ≥ 1 row with frozen class `fix-now` whose `Route` cell begins `product owner`, `product-owned` or `owned by product` (head-anchored, identical to `scripts/unit-route.mjs`'s `PRODUCT_ROUTE` regex: `/^(?:product[- ]owner\|product[- ]owned\|owned by product)\b/i`, per `#293`) | `frozen (product owner present)` | **freeze-batch** — nothing folds, no `folded: yes` flips, no commits; the receipt records the PRODUCT-ROUTE and every retained (unfolded) row id; the loop stops; the consumer is the conclusion printed by `node scripts/unit-route.mjs <unit>`: `route: product` and the lane hand-off — `/unit-lane <slug>` (feature) or `/unit-lane --fix <n>` (fix) — the lane repairs the half, its `review` step re-judges it, then `/execute-phase` on this unit |
46
+ | ≥ 1 row with frozen class `replan-in-unit` or `decision-required` | `frozen (replan present)` | **freeze-batch** — nothing folds, no `folded: yes` flips, no commits; the receipt records the REPLAN-ROUTE and every retained (unfolded) row id; the loop stops; the consumer is the conclusion printed by `bun scripts/unit-route.mjs <unit>` (node fallback
47
+ if bun is not installed) (discovery step the fold already ran), where `<unit>` is the bare folder number or the full slug (both resolve): on `route: replan` the planner command the router printed — `/unit-lane <slug>` (feature) or `/unit-lane --fix <n>` (fix) — the lane appends the phases and its `review` step gates, then `/execute-phase` on this unit; on `route: decision` stop and surface the decision to the user, never a planner |
48
+ | ≥ 1 row with frozen class `fix-now` whose `Route` cell begins `product owner`, `product-owned` or `owned by product` (head-anchored, identical to `scripts/unit-route.mjs`'s `PRODUCT_ROUTE` regex: `/^(?:product[- ]owner\|product[- ]owned\|owned by product)\b/i`, per `#293`) | `frozen (product owner present)` | **freeze-batch** — nothing folds, no `folded: yes` flips, no commits; the receipt records the PRODUCT-ROUTE and every retained (unfolded) row id; the loop stops; the consumer is the conclusion printed by `bun scripts/unit-route.mjs <unit>` (node fallback
49
+ if bun is not installed): `route: product` and the lane hand-off — `/unit-lane <slug>` (feature) or `/unit-lane --fix <n>` (fix) — the lane repairs the half, its `review` step re-judges it, then `/execute-phase` on this unit |
48
50
  | all taken rows foldable, none replan-class | `all-repair-in-place` | fold as today (group → fix → gate → commit → flip) |
49
51
  | empty queue (zero findings taken) | `none` | receipt only |
50
52
 
@@ -60,7 +62,7 @@
60
62
  `DISPUTED <evidence → user decision>`; never edit classification or create an
61
63
  issue.
62
64
  9. **Replan.** If the smallest correct group exceeds a reviewable correction,
63
- run `node scripts/unit-route.mjs <unit>` — its `route: replan` line prints the
65
+ run `bun scripts/unit-route.mjs <unit>` (node fallback if bun is not installed) — its `route: replan` line prints the
64
66
  planner command (`/unit-lane <slug>` for a feature, `/unit-lane --fix <n>` for
65
67
  a fix) that appends the proposed
66
68
  phases to the unit's SPEC. The `<unit>` argument accepts the bare folder number
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: init-workspace
3
3
  user-invocable: true
4
- version: 4.1.0
4
+ version: 4.2.0
5
5
  argument-hint: <target-dir>
6
6
  author: "Gabriel Trabanco <1969593+gtrabanco@users.noreply.github.com>"
7
7
  license: MIT
@@ -78,7 +78,7 @@ Inspect the target dir (`[target-dir]`, default cwd) before touching anything:
78
78
 
79
79
  ## Progressive loading — bootstrap or upgrade, never both
80
80
 
81
- The reference allowlist is exactly the five linked paths below. Never invent or
81
+ The reference allowlist is exactly the six linked paths below. Never invent or
82
82
  read another `references/` path. Step 0 selects exactly one route:
83
83
 
84
84
  **Hard rule for an existing Claude Code, Cursor, Copilot, or OpenCode
@@ -95,6 +95,10 @@ or portability unless the runtime platform has no named adapter at all.
95
95
  only. Its additive-only rule is absolute; do not replay bootstrap.
96
96
  The upgrade route loads [SERENA language servers](references/SERENA.md)
97
97
  for the additive provisioning step.
98
+ - Provisioning Engram on a host with no pi package: read
99
+ [Engram provisioning](references/ENGRAM.md) — detect, ask, register or
100
+ instruct, report. Loaded by the bootstrap write route only (the pi package
101
+ provisions itself, feature 66).
98
102
  - No named adapter exists for the detected runtime platform: add
99
103
  [portability](references/PORTABILITY.md) only for that fallback. Claude Code,
100
104
  Cursor, Copilot, and OpenCode are named supported adapters; a missing adapter
@@ -133,6 +137,13 @@ loads exactly upgrade and skips bootstrap plus portability.
133
137
  auto-detected), a listed server that fails to start must be removed (one
134
138
  failure disables every server in the project), and LSP evidence stays
135
139
  TypeScript/JavaScript-only. Document verification is a script's job.
140
+ - **Engram provisioning is ask-first, never assumed (feature 66).** Detect the
141
+ `engram` binary, ask before writing any host MCP config, write the absolute
142
+ path with the exact `mcp --tools=agent` argv, and report exactly what was
143
+ written. An existing `engram` declaration is never overwritten, and an absent
144
+ binary is a recorded residual — the documented install route is
145
+ `pi install npm:gentle-engram@latest`, never the CLI's own setup subcommand,
146
+ which keeps serving whatever version ran it until it is re-run.
136
147
  - **Retrieval index scaffold and git hooks are consent-gated (AC22).** The
137
148
  index surface — the committed `.agentic-workflow/index.json` config plus the
138
149
  three freshness hooks (`post-merge`, `post-checkout`, `post-rewrite`, all
@@ -34,7 +34,13 @@
34
34
  health check and render the fixed provisioning report. When no installer is
35
35
  available, offer only what the machine can perform and record the residual
36
36
  with its reason — the run completes either way.
37
- 9. **Install accepted agent safety hooks.** Keep the canonical policy and
37
+ 9. **Offer Engram provisioning.** Load [ENGRAM](ENGRAM.md), detect the binary
38
+ and ask consent: register the `engram` MCP server (absolute path, exact
39
+ `mcp --tools=agent` argv) when the host has one, leave an existing
40
+ declaration untouched, and report exactly what was written. An absent binary
41
+ becomes a residual with the documented install route — never a failed run.
42
+ Skip this step when the pi package provisions the host itself.
43
+ 10. **Install accepted agent safety hooks.** Keep the canonical policy and
38
44
  fullauto wrapper at `.agentic-workflow/hooks/`. Seed the shipped
39
45
  path-protection policy at `.agentic-workflow/path-policy.json` and its doc
40
46
  page at `.agentic-workflow/path-protection.md`, beside the hooks — the
@@ -47,10 +53,10 @@
47
53
  an unknown payload, or an existing customized hook file becomes a residual,
48
54
  never a silent overwrite. Direct merges remain blocked; do not create a
49
55
  `.automerge` exception.
50
- 10. **Offer the workflow skills.** Propose installing them:
56
+ 11. **Offer the workflow skills.** Propose installing them:
51
57
  `npx skills add gtrabanco/agentic-workflow` (note the SSH/local-path variant if
52
58
  the source is private). Don't install without a yes.
53
- 11. **State that reviews are self-contained; offer optional extras.** The
59
+ 12. **State that reviews are self-contained; offer optional extras.** The
54
60
  workflow ships its **own internal review pack** (`review-code`,
55
61
  `review-security`, `review-verify`, `review-debt`, `review-design`,
56
62
  `review-a11y`, `review-brand`, `review-perf`, `review-seo`) — it installs
@@ -60,7 +66,7 @@
60
66
  under a short "Optional review extras" note so `review-change` and
61
67
  `product-audit` run them **in addition** — never as a dependency. Don't
62
68
  install anything without a yes.
63
- 12. **Seed the urgency labels (feature 15, injection-safe urgency).** Create the
69
+ 13. **Seed the urgency labels (feature 15, injection-safe urgency).** Create the
64
70
  two capability-gated GitHub labels `triage-issue` owns and applies
65
71
  (`skills/triage-issue/SKILL.md` is the sole owner of the name/color
66
72
  vocabulary — this step only seeds it, never redefines it):
@@ -73,7 +79,7 @@
73
79
  forge is unavailable or the user declines forge setup, skip this step and
74
80
  list the two labels as a residual for the user to create manually later
75
81
  (never fail the whole scaffold on it).
76
- 13. **Report.** List what was created, which placeholders still need human input,
82
+ 14. **Report.** List what was created, which placeholders still need human input,
77
83
  the companion skills recorded/installed, the urgency labels seeded (or
78
84
  skipped, with reason), and the next step:
79
85
  `discover-repository-state` → `/unit-lane` (or `workflow-status`) → catalog
@@ -0,0 +1,107 @@
1
+ ## Why this step exists
2
+
3
+ The workflow's units save what they learned into Engram, the cross-session
4
+ memory layer its memory protocol documents. On a host whose agent has no `pi`
5
+ package to provision it (the pi package registers the server itself, feature
6
+ 66 P4), the target project has no memory layer at all unless this step offers
7
+ one — so Engram provisioning is a **parity obligation**, not an optional extra:
8
+ feature 66's approval is that the pi path registers it and every other host is
9
+ offered the same thing ask-first.
10
+
11
+ ## What this file owns
12
+
13
+ The **non-pi** half of Engram provisioning. It owns exactly four moves —
14
+ detect, ask, provision-or-instruct, report — and nothing else. The workflow-side
15
+ protocol (when units save, the recall order, the degraded path) is **not** here;
16
+ it lives in `unit-lane`'s memory reference, which the skills that run units
17
+ already load.
18
+
19
+ ## 1. Detect
20
+
21
+ Look for the `engram` binary on `PATH` (`command -v engram`) and read its
22
+ version (`engram --version`). Two outcomes matter:
23
+
24
+ - **Present** — record the resolved path and the version fingerprint.
25
+ - **Absent** — record it as a residual, and go to step 2 with the install
26
+ guidance below rather than silently skipping the step.
27
+
28
+ Never write an MCP registration file for a binary that does not resolve: a
29
+ registered-but-unstartable server fails every session after it, which is a worse
30
+ default than an honest gap.
31
+
32
+ ## 2. Ask, then do exactly what was agreed
33
+
34
+ This is a **consent-gated install** like every other in this skill. Ask one
35
+ question — "register Engram as the project's memory layer?" — and act on the
36
+ answer:
37
+
38
+ | Answer | What you do |
39
+ |---|---|
40
+ | yes | write the MCP registration named in step 3 and report it verbatim |
41
+ | no | write nothing, record it as a residual, finish the run normally |
42
+ | not installed | offer the install route below, and provision **only** if the user then says yes |
43
+
44
+ **The install route is `pi install npm:gentle-engram@latest`.** The CLI's own
45
+ setup subcommand is **not** the documented route and must never be printed as an
46
+ instruction: it pins whatever version ran it and keeps serving that version
47
+ until it is re-run, so an updated binary can sit unused behind a stale
48
+ registration (decision D3, 2026-10-21).
49
+
50
+ ## 3. What "register" means on a non-pi host
51
+
52
+ There is no `pi.registerMcpServer` here, so the registration is a **declared
53
+ file** the host agent reads. Write the server into that host's own MCP config
54
+ (the agent's documented MCP surface), never into a file the host does not read:
55
+
56
+ ```json
57
+ {
58
+ "mcpServers": {
59
+ "engram": { "command": "<absolute path to engram>", "args": ["mcp", "--tools=agent"] }
60
+ }
61
+ }
62
+ ```
63
+
64
+ **Rules:**
65
+
66
+ - **Absolute path, never a bare name** — the host's `PATH` at load time is not
67
+ the shell's.
68
+ - **Args are exactly** `mcp` `--tools=agent`. A different argv changes which
69
+ tools the server exposes.
70
+ - **Never overwrite a server the user already declared** under the name
71
+ `engram` — a declared server wins over anything this step would add. Report
72
+ that you found it and left it alone.
73
+ - **Additive and idempotent** — a second run with the same content writes
74
+ nothing and says so.
75
+
76
+ ## 4. Report exactly what was written
77
+
78
+ Every outcome ends in a report the user can read back:
79
+
80
+ - **Registered** — the file path, the server name, and the exact `command` +
81
+ `args` written.
82
+ - **Declined** — that nothing was written, and that the run continued.
83
+ - **Binary missing** — that nothing was written, the install route verbatim, and
84
+ that it is recorded as a residual.
85
+ - **Already declared** — the file that already carries it, and that it was left
86
+ untouched.
87
+
88
+ "Report exactly what was written" is the same contract the Serena step follows;
89
+ a provisioning step the user cannot read back is one they cannot trust.
90
+
91
+ ## Checklist (pass only if)
92
+
93
+ - [ ] `engram` presence **and** version were detected (or absence recorded)
94
+ - [ ] Consent was asked before any file was written — never assumed
95
+ - [ ] The written server uses the absolute binary path and the exact argv
96
+ - [ ] An existing `engram` declaration was left untouched and reported
97
+ - [ ] A second run is a no-op
98
+ - [ ] The closing report states what was written — or that nothing was
99
+
100
+ ## Forbidden
101
+
102
+ - Never print the CLI's own setup subcommand as the install route
103
+ - Never write an MCP config for an unresolved binary
104
+ - Never overwrite an operator's existing `engram` declaration
105
+ - Never let a memory gap fail the run — record it as a residual and continue
106
+ - Never restate the unit-boundary save/recall protocol here; that is the lane's
107
+ memory reference