@amsterdamdatalabs/enact-extensions 0.1.12 → 0.1.25

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 (148) hide show
  1. package/README.md +11 -12
  2. package/dist/create/enact.js +1 -1
  3. package/dist/create/enact.js.map +1 -1
  4. package/dist/create/index.d.ts +4 -3
  5. package/dist/create/index.d.ts.map +1 -1
  6. package/dist/create/index.js +9 -2
  7. package/dist/create/index.js.map +1 -1
  8. package/dist/index.d.ts +8 -6
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +4 -3
  11. package/dist/index.js.map +1 -1
  12. package/dist/install.d.ts +5 -0
  13. package/dist/install.d.ts.map +1 -1
  14. package/dist/install.js +10 -3
  15. package/dist/install.js.map +1 -1
  16. package/dist/internal/agents.d.ts +6 -1
  17. package/dist/internal/agents.d.ts.map +1 -1
  18. package/dist/internal/agents.js +8 -4
  19. package/dist/internal/agents.js.map +1 -1
  20. package/dist/internal/claude.d.ts +24 -0
  21. package/dist/internal/claude.d.ts.map +1 -1
  22. package/dist/internal/claude.js +99 -0
  23. package/dist/internal/claude.js.map +1 -1
  24. package/dist/internal/platform.d.ts +3 -1
  25. package/dist/internal/platform.d.ts.map +1 -1
  26. package/dist/internal/platform.js +7 -1
  27. package/dist/internal/platform.js.map +1 -1
  28. package/dist/internal/types.d.ts +2 -1
  29. package/dist/internal/types.d.ts.map +1 -1
  30. package/dist/principles.d.ts +28 -0
  31. package/dist/principles.d.ts.map +1 -0
  32. package/dist/principles.js +159 -0
  33. package/dist/principles.js.map +1 -0
  34. package/extensions/dev-state/.agents/plugin.json +2 -1
  35. package/extensions/enact-context/.agents/plugin.json +2 -1
  36. package/extensions/enact-context/hooks/hooks.json +0 -10
  37. package/extensions/enact-context/skills/enact-context/SKILL.md +14 -12
  38. package/extensions/enact-context/skills/enact-context/scripts/install.sh +7 -7
  39. package/extensions/enact-core/.agents/plugin.json +2 -1
  40. package/extensions/enact-core/OPERATING-PRINCIPLES.md +7 -0
  41. package/extensions/enact-core/hooks/hooks.json +12 -0
  42. package/extensions/enact-evolve/.agents/plugin.json +47 -0
  43. package/extensions/enact-evolve/agents/evolve-session-analyst.toml +37 -0
  44. package/extensions/enact-evolve/skills/session-analysis/SKILL.md +98 -0
  45. package/extensions/enact-evolve/skills/session-analysis/scripts/run-evolve-analysis.sh +343 -0
  46. package/extensions/enact-factory/.agents/plugin.json +2 -2
  47. package/extensions/enact-factory/agents/architect.toml +9 -5
  48. package/extensions/enact-factory/agents/code-reviewer.toml +9 -5
  49. package/extensions/enact-factory/agents/critic.toml +9 -5
  50. package/extensions/enact-factory/agents/executor.toml +4 -1
  51. package/extensions/enact-factory/agents/explore.toml +4 -1
  52. package/extensions/enact-factory/agents/planner.toml +4 -1
  53. package/extensions/enact-factory/agents/verifier.toml +9 -5
  54. package/extensions/enact-factory/skills/advisor/SKILL.md +82 -0
  55. package/extensions/enact-factory/skills/ai-slop-cleaner/SKILL.md +6 -1
  56. package/extensions/enact-factory/skills/autonomous-runner/SKILL.md +347 -0
  57. package/extensions/enact-factory/skills/azdo-ci-strategy/SKILL.md +42 -15
  58. package/extensions/enact-factory/skills/committee/SKILL.md +80 -0
  59. package/extensions/enact-factory/skills/deep-interview/SKILL.md +9 -13
  60. package/extensions/enact-factory/skills/drive-loop/SKILL.md +161 -31
  61. package/extensions/enact-factory/skills/drive-loop/references/contract-schema.md +26 -6
  62. package/extensions/enact-factory/skills/handoff/SKILL.md +72 -0
  63. package/extensions/enact-factory/skills/hyperplan/SKILL.md +11 -3
  64. package/extensions/enact-factory/skills/looplan/SKILL.md +34 -17
  65. package/extensions/enact-factory/skills/plan/SKILL.md +40 -8
  66. package/extensions/enact-factory/skills/remove-deadcode/SKILL.md +6 -1
  67. package/extensions/enact-factory/skills/research/SKILL.md +14 -4
  68. package/extensions/enact-factory/skills/review/SKILL.md +21 -2
  69. package/extensions/enact-factory/skills/security-research/SKILL.md +5 -2
  70. package/extensions/enact-factory/skills/tdd/SKILL.md +7 -1
  71. package/extensions/enact-factory/skills/testing-strategy/SKILL.md +5 -0
  72. package/extensions/enact-factory/skills/trace/SKILL.md +5 -0
  73. package/extensions/enact-factory/skills/ultraqa/SKILL.md +21 -15
  74. package/extensions/enact-factory/skills/work-with-workitem/SKILL.md +5 -0
  75. package/extensions/enact-factory/skills/workitem-triage/SKILL.md +5 -0
  76. package/extensions/enact-loop/.agents/plugin.json +5 -4
  77. package/extensions/enact-loop/scripts/validate.mjs +123 -0
  78. package/extensions/enact-loop/skills/enact-loop/SKILL.md +189 -30
  79. package/extensions/enact-wiki/.agents/plugin.json +2 -1
  80. package/extensions/net-revenue-management/.agents/plugin.json +2 -1
  81. package/extensions/plugin-dev/.agents/plugin.json +2 -1
  82. package/extensions/plugin-dev/skills/start/SKILL.md +3 -3
  83. package/package.json +1 -1
  84. package/scripts/check-hooks.mjs +5 -5
  85. package/scripts/check-principles.mjs +19 -4
  86. package/scripts/enact-extensions.mjs +237 -90
  87. package/scripts/lib/hooks.mjs +61 -217
  88. package/scripts/lib/migrate-artifacts.mjs +144 -0
  89. package/scripts/lib/principles.mjs +109 -0
  90. package/scripts/lib/provision-mcp.mjs +1 -1
  91. package/scripts/lib/run-install.mjs +72 -2
  92. package/scripts/lib/run-prune.mjs +23 -2
  93. package/scripts/lib/run-sync.mjs +4 -1
  94. package/scripts/postinstall.mjs +6 -6
  95. package/scripts/setup-enact-context.sh +20 -15
  96. package/scripts/version-bump.sh +22 -1
  97. package/spec/codex.json +5 -0
  98. package/spec/enact.json +3 -3
  99. package/spec/enact.md +1 -4
  100. package/spec/index.json +1 -1
  101. package/extensions/enact-factory/hooks/hooks.json +0 -14
  102. package/extensions/enact-operator/.agents/plugin.json +0 -56
  103. package/extensions/enact-operator/.app.json +0 -3
  104. package/extensions/enact-operator/.mcp.json +0 -10
  105. package/extensions/enact-operator/_taxonomy.md +0 -86
  106. package/extensions/enact-operator/agents/README.md +0 -5
  107. package/extensions/enact-operator/agents/architect.toml +0 -25
  108. package/extensions/enact-operator/agents/code-reviewer.toml +0 -24
  109. package/extensions/enact-operator/agents/critic.toml +0 -30
  110. package/extensions/enact-operator/agents/executor.toml +0 -24
  111. package/extensions/enact-operator/agents/explore.toml +0 -23
  112. package/extensions/enact-operator/agents/planner.toml +0 -24
  113. package/extensions/enact-operator/agents/verifier.toml +0 -24
  114. package/extensions/enact-operator/docs/skill-variants.md +0 -44
  115. package/extensions/enact-operator/hooks/hooks.json +0 -91
  116. package/extensions/enact-operator/skills/ai-slop-cleaner/SKILL.md +0 -50
  117. package/extensions/enact-operator/skills/analyze/SKILL.md +0 -91
  118. package/extensions/enact-operator/skills/ask/SKILL.md +0 -47
  119. package/extensions/enact-operator/skills/autopilot/SKILL.md +0 -170
  120. package/extensions/enact-operator/skills/autoresearch-goal/SKILL.md +0 -79
  121. package/extensions/enact-operator/skills/cancel/SKILL.md +0 -99
  122. package/extensions/enact-operator/skills/configure-notifications/SKILL.md +0 -77
  123. package/extensions/enact-operator/skills/deep-interview/SKILL.md +0 -80
  124. package/extensions/enact-operator/skills/doctor/SKILL.md +0 -48
  125. package/extensions/enact-operator/skills/hud/SKILL.md +0 -49
  126. package/extensions/enact-operator/skills/hyperplan/SKILL.md +0 -47
  127. package/extensions/enact-operator/skills/plan/SKILL.md +0 -78
  128. package/extensions/enact-operator/skills/ralph/SKILL.md +0 -201
  129. package/extensions/enact-operator/skills/ralph/gemini.md +0 -18
  130. package/extensions/enact-operator/skills/ralplan/SKILL.md +0 -151
  131. package/extensions/enact-operator/skills/remove-deadcode/SKILL.md +0 -45
  132. package/extensions/enact-operator/skills/research/SKILL.md +0 -74
  133. package/extensions/enact-operator/skills/review/SKILL.md +0 -58
  134. package/extensions/enact-operator/skills/security-research/SKILL.md +0 -54
  135. package/extensions/enact-operator/skills/setup/SKILL.md +0 -91
  136. package/extensions/enact-operator/skills/setup/scripts/install.sh +0 -50
  137. package/extensions/enact-operator/skills/skill/SKILL.md +0 -82
  138. package/extensions/enact-operator/skills/tdd/SKILL.md +0 -59
  139. package/extensions/enact-operator/skills/team/SKILL.md +0 -199
  140. package/extensions/enact-operator/skills/trace/SKILL.md +0 -41
  141. package/extensions/enact-operator/skills/ultragoal/SKILL.md +0 -99
  142. package/extensions/enact-operator/skills/ultraqa/SKILL.md +0 -113
  143. package/extensions/enact-operator/skills/ultrawork/SKILL.md +0 -145
  144. package/extensions/enact-operator/skills/ultrawork/planner.md +0 -28
  145. package/extensions/enact-operator/skills/wiki/SKILL.md +0 -41
  146. package/extensions/enact-operator/skills/work-with-workitem/SKILL.md +0 -51
  147. /package/extensions/{enact-operator → enact-evolve}/assets/icon.png +0 -0
  148. /package/extensions/{enact-operator → enact-evolve}/assets/logo.png +0 -0
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: committee
3
+ description: >-
4
+ Two contrasting-vendor agents root-cause a stuck, blocked, or looping WorkItem
5
+ and produce a plan. Use when asked to "convene a committee", "root-cause this",
6
+ "the item is stuck/looping", "debate the approach", or when the driver's loop
7
+ stalls and needs a way forward. Produces a plan that feeds `plan` / `drive-loop`;
8
+ may propose a different approach. Read + plan only — no implementation.
9
+ metadata:
10
+ author: Amsterdam Data Labs
11
+ version: 1.0.0
12
+ ---
13
+
14
+ # committee
15
+
16
+ > **Storage is GLOBAL — never write to a repo-local `.enact` dir.** Resolve the project's global dir:
17
+ > 1. `ROOT="$(git rev-parse --show-toplevel)"`
18
+ > 2. In `~/.enact/workspaces.json` → `workspaces`, find the entry whose `.root` == `$ROOT`; its object key is `<name>-<id>`.
19
+ > 3. Factory planning artifacts go under `~/.enact/factory/projects/<key>/…`.
20
+
21
+ Two **contrasting-vendor** agents independently diagnose a stuck WorkItem, then
22
+ their findings are reconciled into a single forward plan. Heavier than
23
+ [`advisor`](../advisor/SKILL.md) (one strong opinion); this is the **strong escalation** for
24
+ architecture decisions and risky or irreversible changes. Use when an item is **stalled, blocked,
25
+ or looping** and the path is unclear, OR when a risky migration / architecture decision requires
26
+ two independent strong perspectives before proceeding.
27
+
28
+ ## When to escalate to committee
29
+
30
+ - Architecture decisions (interface shape, system redesign, approach selection)
31
+ - Risky or irreversible migrations (data, schema, breaking API changes)
32
+ - Items blocked at the loop level where `advisor`'s single opinion is insufficient
33
+ - Branch policy / CI strategy changes with wide blast radius
34
+
35
+ ## Model selection
36
+
37
+ Committee always uses **opus 4.8 + gpt-5.5** — the two strongest available models from different
38
+ vendors. This pairing provides genuine diversity: one OpenAI-lineage, one Anthropic-lineage.
39
+
40
+ ## Boundaries (hard)
41
+
42
+ - **Diagnose + plan only.** No code changes, no commits, no board mutation. The
43
+ output is a plan; implementation happens later via `drive-loop`.
44
+ - **Two distinct vendors: opus 4.8 + gpt-5.5.** Both members run on models that are different
45
+ from each other and from the executor (genuine diversity). If two distinct models are
46
+ unavailable, run what you can and STATE the reduced diversity loudly — do not
47
+ pretend two same-model passes are a committee.
48
+ - **Reconcile honestly.** If the two disagree, surface the disagreement and the
49
+ decisive evidence; do not paper over it.
50
+
51
+ ## Sequence
52
+
53
+ 1. Assemble the dossier: `factory_workitem_get` (goal, acceptance, current
54
+ state, `blockedBy`), the loop status / stall reason, recent failing output.
55
+ 2. Spawn TWO agents on **opus 4.8 + gpt-5.5**, with contrasting lenses:
56
+
57
+ ```
58
+ Agent({ subagent_type: "critic", model: "claude-opus-4-8", prompt: "<dossier> — root-cause why this is stuck; what is the real blocker?" })
59
+ Agent({ subagent_type: "architect", model: "gpt-5.5", prompt: "<dossier> — is the current approach right? propose the smallest viable path to closure." })
60
+ ```
61
+
62
+ 3. Reconcile their findings into ONE plan: the root cause, the chosen approach
63
+ (note if it differs from the original), and ordered next steps mapped to
64
+ loop stages.
65
+ 4. Hand the result to `$plan` so it is persisted as a new timestamped
66
+ `~/.enact/factory/projects/<key>/plans/<YYMMDDHH>-<phase>.md` artifact rather
67
+ than left only in chat output.
68
+
69
+ ## Output
70
+
71
+ `{ rootCause, approach (kept | changed + why), steps[], dissent? , members[] }`
72
+ where `members[]` shows the two distinct models. The `steps[]` are ready to feed
73
+ [`plan`](../plan/SKILL.md) or directly into a [`drive-loop`](../drive-loop/SKILL.md)
74
+ contract.
75
+
76
+ ## Related
77
+
78
+ - [`advisor`](../advisor/SKILL.md) — when one opinion is enough.
79
+ - [`plan`](../plan/SKILL.md) — turn the committee's steps into an execution plan.
80
+ - [`drive-loop`](../drive-loop/SKILL.md) — execute the chosen path to closure.
@@ -5,6 +5,11 @@ description: "Intent-first clarification loop for vague, risky, or product-heavy
5
5
 
6
6
  # Deep Interview
7
7
 
8
+ > **Storage is GLOBAL — never write to a repo-local `.enact` dir.** Resolve the project's global dir:
9
+ > 1. `ROOT="$(git rev-parse --show-toplevel)"`
10
+ > 2. In `~/.enact/workspaces.json` → `workspaces`, find the entry whose `.root` == `$ROOT`; its object key is `<name>-<id>` (e.g. `enact-evolve-a38d1e`).
11
+ > 3. Factory planning artifacts go under `~/.enact/factory/projects/<key>/…`.
12
+
8
13
  ## Purpose
9
14
 
10
15
  Use `$deep-interview` to turn a fuzzy request into an execution-ready spec. This is not generic
@@ -30,29 +35,20 @@ brainstorming. It is a focused clarification loop that removes ambiguity before
30
35
  - use repo facts before asking about codebase internals
31
36
  - force clarity on non-goals and decision boundaries before handoff
32
37
  - keep the interview moving toward a durable artifact, not an endless conversation
33
-
34
- ## Question Order
35
-
36
- 1. Why does this need to exist?
37
- 2. What should be true when it is done?
38
- 3. How far should it go?
39
- 4. What should explicitly stay out?
40
- 5. What may the agent decide without checking again?
41
- 6. What constraints or preferences are hard?
38
+ - **never overwrite a prior requirements file in place** — each durable snapshot gets a fresh `YYMMDDHH` prefix
42
39
 
43
40
  ## Workflow
44
41
 
45
42
  1. Inspect the repo for brownfield context if relevant.
46
- 2. Capture the current hypothesis in `.enact/loop/plans/<phase>-requirements.md`.
43
+ 2. Capture the current hypothesis in a **new** `~/.enact/factory/projects/<key>/plans/<YYMMDDHH>-<phase>-requirements.md` (e.g. `plans/26062101-auth-requirements.md`). Resolve `<key>` from `~/.enact/workspaces.json`; get the timestamp prefix via `date +%y%m%d%H`.
47
44
  3. Run a one-question loop until these are explicit:
48
45
  - goal
49
46
  - in-scope
50
47
  - out-of-scope
51
48
  - acceptance criteria
52
49
  - decision boundaries
53
- 4. Update:
54
- - `.enact/loop/plans/<phase>-requirements.md`
55
- 5. Hand off to `$plan` when the spec is concrete.
50
+ 4. During the interview, revise the **same** requirements file for the current pass only. A new interview session or phase restart writes a **new** timestamped file instead of editing an old one.
51
+ 5. Hand off to `$plan` when the spec is concrete. `$plan` resolves the latest `*-requirements.md` per its Active Artifact Resolution rules.
56
52
 
57
53
  ## Output Standard
58
54
 
@@ -14,6 +14,11 @@ metadata:
14
14
 
15
15
  # drive-loop
16
16
 
17
+ > **Storage is GLOBAL — never write to a repo-local `.enact` dir.** Resolve the project's global dir:
18
+ > 1. `ROOT="$(git rev-parse --show-toplevel)"`
19
+ > 2. In `~/.enact/workspaces.json` → `workspaces`, find the entry whose `.root` == `$ROOT`; its object key is `<name>-<id>`.
20
+ > 3. Factory artifacts go under `~/.enact/factory/projects/<key>/…`; loop runtime state and closure summaries go under `~/.enact/loop/projects/<key>/…`.
21
+
17
22
  ## Model
18
23
 
19
24
  **enact-loop** is a domain-neutral contract-runner. **enact-factory** is the
@@ -56,6 +61,7 @@ required unless marked optional.
56
61
  "grader": {
57
62
  "role": "architect | critic | code-reviewer | verifier",
58
63
  "model": "<model-id — MUST differ from executor>",
64
+ "fallbackModels": ["<optional fallback models on 529/overload>"],
59
65
  "harness": "paseo | subagent",
60
66
  "rounds": 1
61
67
  },
@@ -74,6 +80,10 @@ required unless marked optional.
74
80
 
75
81
  - `grader.model` **MUST** differ from the executor model. Cross-vendor is
76
82
  preferred (e.g. executor = codex, grader = claude-opus-4-8). No self-grading.
83
+ - `grader.fallbackModels` — optional ordered list tried on re-dispatch after spawn failure
84
+ (`loop_grader_report_spawn_failure` on 529/overload). Does not consume the round budget.
85
+ - `requires` — stages run in contract order; `loop_grade` fails loudly if prerequisites are not
86
+ passed: `Cannot pass stage "X": requires stages not yet passed: …`.
77
87
  - `required: true` → stage must pass before closure is declared.
78
88
  - Closure = all required stages passed.
79
89
 
@@ -88,6 +98,15 @@ Inspect the WorkItem's `closureRequirements`. Map each requirement to a stage:
88
98
  - Deterministic check → `type: mechanical`, set `command`.
89
99
  - Subjective quality gate → `type: judgment`, set `grader`.
90
100
 
101
+ > **Authoring note — mechanical stage commands:**
102
+ > The `tests` stage command must mirror CI's containerized/Linux-parity
103
+ > environment (e.g. `bash scripts/ci-local.sh`), not a bare host run. A
104
+ > host-passing suite can still fail the CI Linux agent due to `HOME`/path
105
+ > divergence. Where the project exposes a `typecheck:tests` script, include it
106
+ > — as its own mechanical stage or folded into the typecheck command
107
+ > (`npm run typecheck && npm run typecheck:tests`) — so test-file type
108
+ > regressions do not slip past.
109
+
91
110
  ### Step 2 — Start the loop
92
111
 
93
112
  ```
@@ -100,11 +119,12 @@ agent cannot stop until the loop signals closure.
100
119
 
101
120
  ### Step 3 — Drive mechanical stages
102
121
 
103
- For each mechanical stage the loop surfaces:
122
+ For each mechanical stage the loop surfaces (after any `requires` preconditions pass):
104
123
 
105
- 1. Run the stage's `command`.
106
- 2. Capture stdout/stderr and exit code.
107
- 3. Record the result: `loop_grade({ stageId, pass: <bool>, output: <string> })`.
124
+ 1. Call `loop_grade({ stageId, timeoutMs? })` — the engine runs the stage `command` in the loop
125
+ root cwd and records pass/fail from exit code.
126
+ 2. Caller-supplied `status`, `pass`, or `output` are **rejected** do not run the command
127
+ separately and self-report.
108
128
 
109
129
  Repeat until the stage passes or the loop escalates to a retry/fail policy.
110
130
 
@@ -112,9 +132,11 @@ Repeat until the stage passes or the loop escalates to a retry/fail policy.
112
132
 
113
133
  For each judgment stage:
114
134
 
115
- 1. Call `loop_grader_dispatch({ stageId, role: <grader role> })`.
116
- 2. Spawn an **independent subagent** on a **different model** (never the
117
- executor's model or session):
135
+ 1. Call `loop_grader_dispatch({ stageId, childSessionId?, childAgentId? })` — mints `continuationId`.
136
+ 2. Spawn an **independent subagent** on a **different model** (never the executor's model or
137
+ session). Pass grader role metadata so the grader MCP session is gated:
138
+ `_meta.enactLoop: { sessionRole: "grader", sessionId, continuationId }` or env vars
139
+ `ENACT_LOOP_SESSION_ROLE=grader`, `ENACT_LOOP_CONTINUATION_ID=<uuid>`.
118
140
 
119
141
  ```
120
142
  Agent({
@@ -124,20 +146,52 @@ For each judgment stage:
124
146
  })
125
147
  ```
126
148
 
127
- 3. The grader returns a GO or NO-GO verdict with rationale.
128
- 4. Record the verdict: `loop_grader_verdict({ stageId, verdict: "GO | NO-GO", rationale })`.
129
-
130
- The boulder prevents the session from completing until every judgment stage
131
- has a GO from an independent grader.
149
+ 3. The grader reviews and returns a verdict envelope (GO/NO-GO + evidence). Grader child sessions
150
+ have **no closure authority** runtime rejects dispatch/resume/complete/grade/summary tools.
151
+ Graders may only call `loop_grader_verdict`, `loop_status`, `loop_stop_evaluate`.
152
+ 4. On grader spawn failure (529/overload): `loop_grader_report_spawn_failure({ continuationId, error })`
153
+ then re-dispatch (auto-selects next `fallbackModels` entry).
154
+ 5. **Driver-relayed (default):** executor records via `loop_grader_verdict` with
155
+ `captureSource: "relayed"` and the minted `continuationId`.
156
+ **Direct:** grader child may call `loop_grader_verdict` with `captureSource: "direct"`.
157
+ Without dispatch → `E_UNKNOWN_GRADER_SESSION`. Grader timeout → `grader-unavailable` blocker.
158
+
159
+ > **Paseo graders — use `factory_paseo_grader_run` for structured capture.** When a judgment
160
+ > stage runs on Paseo (`harness: "paseo"`, or `grader.dispatchTool: "factory_paseo_grader_run"`),
161
+ > the factory MCP tool `factory_paseo_grader_run` runs the grader with
162
+ > `paseo run --output-schema --json --wait-timeout` so **stdout is the validated `{verdict,
163
+ > evidence}` object** — no `paseo logs | grep`. It bakes in the per-provider `--mode`
164
+ > (codex/kimi `auto`, cursor/opencode default) and recovers the actual resolved model via
165
+ > `paseo ls --label` / `inspect`, which you relay as `graderModelActual` on `loop_grader_verdict`.
166
+ > Full design: `enact-loop/docs/design/2026-06-22-paseo-verdict-capture.md`.
167
+
168
+ The boulder prevents the session from completing until every judgment stage has a GO from an
169
+ independent grader.
132
170
 
133
171
  ### Step 5 — Closure → factory lifecycle advance
134
172
 
135
- When the loop signals closure (all required stages passed):
173
+ When all required stages pass (executor/driver owns closure):
174
+
175
+ 1. Run `enact-loop summary write` (driver only — graders cannot author summaries).
176
+ 2. Call `loop_complete` via MCP.
177
+ 3. Treat `~/.enact/loop/projects/<key>/state/` as the active surface only.
178
+ The next `loop_start` archives the prior terminal loop into
179
+ `~/.enact/loop/projects/<key>/archive/<loop-id>-<contract-id>/`, including
180
+ `loop.json`, `closure-manifest.json`, `grader-sessions.json`,
181
+ `summary-current.json`, and the loop's timestamped summaries.
182
+ 4. Call `pushLoopClosure` (enact-factory MCP/CLI) with the loop's closure record.
183
+ 5. Factory maps closure → lifecycle booleans → WorkItem advances on the board.
136
184
 
137
- 1. Control returns to factory.
138
- 2. Call `pushLoopClosure` (enact-factory MCP/CLI) with the loop's closure
139
- record.
140
- 3. Factory maps closure lifecycle booleans WorkItem advances on the board.
185
+ **Branch base:** `integration`. **No `git stash` during an active loop** — stash invalidates
186
+ worktree fingerprints and stale verdicts.
187
+
188
+ **Stacked-wave squash merges:** feature PRs squash into `integration`; later waves may conflict in
189
+ shared barrel/test/snapshot files. When the wave branch is a superset of `integration`, resolution
190
+ is usually `git checkout --ours -- <paths>` on those shared files — do not panic.
191
+
192
+ **Summary + active loop:** CLI and MCP must share the same workspace root. `summary write` stamps
193
+ `summary-current.json` keyed to active `loop.id`. If closure fails with loop-id mismatch, re-run
194
+ summary write from the same cwd as MCP (`enact-loop mcp .`).
141
195
 
142
196
  ---
143
197
 
@@ -157,6 +211,85 @@ from the executor.
157
211
 
158
212
  ---
159
213
 
214
+ ## Tiered contract shaping + grader ops
215
+
216
+ > This matters: in a validated 3-wave run, the two-judgment Frontend/UI contract shape
217
+ > and cross-vendor model-distinct graders caught FIVE real defects (faked-AA contrast,
218
+ > missing focus indicator, consumer-prop a11y override, untested variant, theme flash)
219
+ > that all mechanical gates passed.
220
+
221
+ ### Shape the contract by tier
222
+
223
+ | Tier | Mechanical stages | Judgment stages |
224
+ |---|---|---|
225
+ | Frontend / UI | tests, typecheck, lint, build, a11y-scan | `code-review` (impl correctness) + `verify` (acceptance criteria met) |
226
+ | Backend / logic | tests, typecheck, lint | `code-review` (+ optional `verify` for high blast-radius) |
227
+ | Docs | build | ONE lighter `review` (e.g. `critic` for scope fidelity) |
228
+ | Infra / CI | pipeline-validation, tests | architecture-fit + pipeline-correctness |
229
+
230
+ Frontend/UI gets **two** judgment stages because visual, a11y, and consumer-API
231
+ surfaces require both implementation review and evidence the deliverable meets
232
+ acceptance criteria in a real surface.
233
+
234
+ ### Paseo grader operational notes
235
+
236
+ #### Grader selection — cheap default + risk-based escalation
237
+
238
+ **minimax M3** (opencode) and **kimi K2.7** are the default cheap graders for routine judgment
239
+ (~80% of grading). Escalate to a strong cross-vendor grader when:
240
+ - Frontend / UI a11y or visual / UX judgment → `advisor` = **gpt-5.5** (or opus 4.8 if executor was codex)
241
+ - Security, public API / barrel / contract changes → **opus 4.8**
242
+ - Architecture decisions / risky or irreversible migrations → `committee` (opus 4.8 + gpt-5.5)
243
+
244
+ **Independence (hard gate — model-distinct):** grader model ≠ executor model. minimax M3 / kimi K2.7
245
+ are vendor-distinct from both sonnet and codex workers, so default cheap graders are always
246
+ independent. On escalation keep cross-vendor: grade codex work with **opus** (not gpt-5.5 — same
247
+ OpenAI lineage); grade sonnet work with gpt-5.5 / opus / minimax / kimi.
248
+
249
+ #### Permission modes (REQUIRED — non-default modes prevent stall/empty sessions)
250
+
251
+ | Provider | Paseo flag | Notes |
252
+ |---|---|---|
253
+ | `codex` | `--mode auto` | `--mode bypass` is rejected by codex |
254
+ | `cursor` | default / agent | `--mode bypass` is a claude mode, NOT cursor's — previously documented wrong |
255
+ | `kimi` | `--mode auto` | Same as codex |
256
+ | `opencode` (minimax M3) | default | No special mode flag needed |
257
+
258
+ #### Sonnet-executor caveat
259
+
260
+ `cursor` (`cursor/claude-sonnet-4-6`) is NOT model-distinct from a sonnet executor.
261
+ Use **minimax M3** or **kimi K2.7** as the clean default cheap graders for sonnet-executor work;
262
+ escalate to opus 4.8 / gpt-5.5 for high-risk judgment. Reserve `cursor` for opus-tier executors.
263
+
264
+ #### Verdict capture — structured `--output-schema`, not log-scraped
265
+
266
+ Dispatch the grader with:
267
+ `paseo run --output-schema <verdict.json> --json --label continuation=<cid>`
268
+ — stdout is the validated `{verdict, evidence}` object (verified on codex / cursor / opencode / kimi;
269
+ coexists with tool use). Recover actual grader model + cost via
270
+ `paseo ls -a --label continuation=<cid> --json` + `paseo inspect <id> --json`. Watch live by
271
+ recovering the id by label then `paseo attach <id>`.
272
+ Design doc: `enact-loop/docs/design/2026-06-22-paseo-verdict-capture.md`.
273
+
274
+ #### fallbackModels rotation on spawn failure
275
+
276
+ 1. Record the failure: `loop_grader_report_spawn_failure({ continuationId, error })`.
277
+ 2. Re-dispatch: `loop_grader_dispatch` auto-selects the next `fallbackModels` entry.
278
+ 3. With no `model`, re-dispatch auto-advances `fallbackModels`; pass an explicit `model` to **pin
279
+ a specific grader** (the override is honored verbatim, not consumed by the pointer). Record the
280
+ verdict under the minted `continuationId` and set `graderModelActual` to the model that actually
281
+ ran. Independence holds as long as the actual grader model ≠ executor model.
282
+
283
+ #### Verdict relay when structured capture is unavailable
284
+
285
+ Preferred: call `factory_paseo_grader_run` (wraps `paseo run --output-schema <verdict.json>
286
+ --json --wait-timeout --label continuation=<cid>`) — stdout is the validated `{verdict, evidence}`
287
+ object, no log parsing — and it returns the resolved `graderModelActual` for provenance.
288
+ Fallback (provider without schema support): `paseo wait <id> --json` and read `.message` (the final
289
+ assistant text is guaranteed there) — never raw `paseo logs` scraping.
290
+
291
+ ---
292
+
160
293
  ## Worked Example — Bug Delivery
161
294
 
162
295
  ```json
@@ -214,29 +347,26 @@ from the executor.
214
347
 
215
348
  1. Factory builds this contract from WI-2041's `closureRequirements`.
216
349
  2. `loop_start({ contract })` — boulder active.
217
- 3. Loop surfaces `unit-tests` → executor runs `cargo test` exit 0
218
- `loop_grade({ stageId: "unit-tests", pass: true, output: "..." })`.
219
- 4. Loop surfaces `regression-evidence` → executor runs grep → match found →
220
- `loop_grade({ stageId: "regression-evidence", pass: true, output: "..." })`.
350
+ 3. Loop surfaces `unit-tests` → `loop_grade({ stageId: "unit-tests" })` engine runs cargo test.
351
+ 4. Loop surfaces `regression-evidence` → `loop_grade({ stageId: "regression-evidence" })`.
221
352
  5. Loop surfaces `code-review` →
222
- `loop_grader_dispatch({ stageId: "code-review", role: "code-reviewer" })` →
353
+ `loop_grader_dispatch({ stageId: "code-review" })` →
223
354
  spawn `code-reviewer` subagent on `claude-opus-4-8` →
224
- grader returns GO
225
- `loop_grader_verdict({ stageId: "code-review", verdict: "GO", rationale: "..." })`.
355
+ driver relays GO via
356
+ `loop_grader_verdict({ stageId: "code-review", continuationId, verdict: "GO", evidence: "...", captureSource: "relayed", ... })`.
226
357
  6. Loop surfaces `verifier-signoff` →
227
- `loop_grader_dispatch(...)` →
228
- spawn `verifier` subagent on `claude-opus-4-8` →
229
- grader returns GO
230
- `loop_grader_verdict({ stageId: "verifier-signoff", verdict: "GO", rationale: "..." })`.
231
- 7. All required stages passed → loop signals closure → boulder lifts.
358
+ `loop_grader_dispatch({ stageId: "verifier-signoff" })` →
359
+ spawn `verifier` subagent driver relays GO via `loop_grader_verdict`.
360
+ 7. All required stages passed `enact-loop summary write` → `loop_complete`.
232
361
  8. Factory calls `pushLoopClosure(closureRecord)` → WI-2041 advances to Done.
233
362
 
234
363
  ---
235
364
 
236
365
  ## Anti-patterns
237
366
 
238
- - **Never self-grade.** The executor that wrote the code cannot be the grader.
239
- Grader must run on a different model and in a different session.
367
+ - **Never self-grade judgment stages.** Grader must run on a different model and session.
368
+ - **Never self-report mechanical passes.** Use `loop_grade({ stageId })` the engine runs the command.
369
+ - **Never stash during an active loop.** Stash invalidates worktree fingerprints.
240
370
  - **Never skip the boulder.** Do not call loop completion tools unless all
241
371
  required stages have passed — the boulder enforces this, but do not attempt
242
372
  to work around it.
@@ -23,7 +23,8 @@ disclosure supplement to `SKILL.md`.
23
23
  | `name` | string | always | Human-readable label. |
24
24
  | `type` | `"mechanical" \| "judgment"` | always | Determines which fields are used and how pass is determined. |
25
25
  | `required` | boolean | always | `true` → must pass before closure. `false` → informational only. |
26
- | `command` | string | mechanical | Shell command run by the executor. Exit 0 = pass. |
26
+ | `requires` | string[] | optional | Stage ids that must pass before this stage may run. |
27
+ | `command` | string | mechanical | Shell command run by the engine via `loop_grade` in loop root cwd. Exit 0 = pass. |
27
28
  | `grader` | GraderSpec | judgment | Specification for the independent grader subagent. |
28
29
  | `passCriteria` | string | optional | Plain-text description of what constitutes a pass. Included in grader prompts. |
29
30
 
@@ -36,7 +37,9 @@ disclosure supplement to `SKILL.md`.
36
37
  | `role` | `"architect" \| "critic" \| "code-reviewer" \| "verifier"` | yes | Determines the subagent type spawned. |
37
38
  | `model` | string | recommended | Model ID for the grader. **Must differ from the executor model.** Cross-vendor preferred. Omit only if the harness selects automatically. |
38
39
  | `harness` | `"paseo" \| "subagent"` | optional | How to dispatch. Default: `subagent`. Use `paseo` when the grader needs multi-step tool access. |
40
+ | `dispatchTool` | string | optional | Factory tool the driver uses to run a Paseo grader and capture a structured verdict — `"factory_paseo_grader_run"` (runs `paseo run --output-schema --json`, returns `{verdict, evidence}` + `graderModelActual`). Replaces `paseo logs` scraping. |
39
41
  | `rounds` | integer | optional | Number of independent grading rounds before aggregating. Default: `1`. |
42
+ | `timeoutMs` | integer | optional | Grader session timeout in ms (default 30 min). Timeout → `grader-unavailable` blocker. |
40
43
 
41
44
  ---
42
45
 
@@ -49,6 +52,17 @@ disclosure supplement to `SKILL.md`.
49
52
  4. At least one stage must be `required: true`, or the contract is trivially
50
53
  closed and should not be submitted to the loop.
51
54
  5. Stage `id` values must be unique within the contract.
55
+ 6. `requires` references must point to existing stage ids; cycles are rejected.
56
+ 7. **(Hard requirement #9 — tests stage Linux parity)** The `tests` stage
57
+ `command` **must** invoke a Linux-parity / containerized runner (e.g.
58
+ `bash scripts/ci-local.sh`) — not a bare host `npm test`. A host suite can
59
+ pass green while the same deliverable fails the CI Linux agent due to
60
+ `HOME`/path divergence.
61
+ 8. **(Rule #7 — typecheck:tests)** When the project has a separate test-file
62
+ typecheck script (`typecheck:tests`), it **must** be included — either as its
63
+ own mechanical stage or folded into the typecheck stage command (e.g.
64
+ `npm run typecheck && npm run typecheck:tests`). Omitting it allows test-file
65
+ type regressions to slip past undetected.
52
66
 
53
67
  ---
54
68
 
@@ -58,8 +72,14 @@ A contract reaches **closure** when:
58
72
 
59
73
  - Every stage where `required: true` has status `PASSED`.
60
74
  - `PASSED` means:
61
- - `mechanical`: `loop_grade` was called with `pass: true`.
62
- - `judgment`: `loop_grader_verdict` was called with `verdict: "GO"`.
75
+ - `mechanical`: `loop_grade({ stageId })` ran the command and exit code was 0.
76
+ - `judgment`: `loop_grader_verdict` recorded `verdict: "GO"` with a valid
77
+ dispatch-minted `continuationId` (`captureSource`: `relayed` or `direct`).
78
+ - Executor/driver authored a current closure summary via `enact-loop summary write`.
79
+ - `loop_complete` called from phase `verifying`.
80
+
81
+ Worktree fingerprint drift invalidates stale `passed` verdicts. `loop_retarget`
82
+ preserves verdicts only when `id + type + worktreeFingerprint` match.
63
83
 
64
84
  Optional stages (`required: false`) do not block closure but their results
65
85
  are included in the closure record returned to factory.
@@ -71,9 +91,9 @@ are included in the closure record returned to factory.
71
91
  | Tool | When to call | Key params |
72
92
  |------|-------------|------------|
73
93
  | `loop_start` | Once, at the start of the delivery | `{ contract: <Contract JSON> }` |
74
- | `loop_grade` | After each mechanical stage run | `{ stageId, pass, output }` |
75
- | `loop_grader_dispatch` | Before spawning the grader subagent | `{ stageId, role }` |
76
- | `loop_grader_verdict` | After the grader subagent returns | `{ stageId, verdict: "GO\|NO-GO", rationale }` |
94
+ | `loop_grade` | For each mechanical stage | `{ stageId, timeoutMs? }` — engine runs command; rejects `status`/`evidence` |
95
+ | `loop_grader_dispatch` | Before spawning the grader subagent | `{ stageId }` → `continuationId` |
96
+ | `loop_grader_verdict` | After grader returns verdict envelope | `{ stageId, continuationId, verdict, evidence, graderAgentId, graderSessionId, captureSource? }` |
77
97
 
78
98
  Factory-side tool (after loop closure):
79
99
 
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: handoff
3
+ description: >-
4
+ Delegate a WorkItem to a detached agent with a self-contained briefing, and
5
+ record the assignment on the factory board. Use when asked to "hand off",
6
+ "delegate this work-item", "assign to an agent", "spin up a detached agent for
7
+ this item", or when the driver wants to fan out an independent item rather than
8
+ drive it inline. The factory tracks the handoff via factory_assignment_record.
9
+ metadata:
10
+ author: Amsterdam Data Labs
11
+ version: 1.0.0
12
+ ---
13
+
14
+ # handoff
15
+
16
+ > **Storage is GLOBAL — never write to a repo-local `.enact` dir.** Resolve the project's global dir:
17
+ > 1. `ROOT="$(git rev-parse --show-toplevel)"`
18
+ > 2. In `~/.enact/workspaces.json` → `workspaces`, find the entry whose `.root` == `$ROOT`; its object key is `<name>-<id>`.
19
+ > 3. Factory planning artifacts go under `~/.enact/factory/projects/<key>/…`; loop runtime state and closure summaries go under `~/.enact/loop/projects/<key>/…`.
20
+
21
+ Package a WorkItem into a briefing complete enough that a fresh detached agent
22
+ can deliver it without the driver's context, launch that agent, and **record the
23
+ assignment on the board**. The board-tracking (`factory_assignment_record`) is
24
+ the factory value-add the generic delegate pattern lacks.
25
+
26
+ ## Boundaries (hard)
27
+
28
+ - **The briefing must be self-contained.** The detached agent does not share
29
+ your context — everything it needs goes in the brief. A handoff that assumes
30
+ the receiver "knows what we discussed" is a defect.
31
+ - **Record every handoff.** Call `factory_assignment_record` so the board
32
+ reflects the assignment. An untracked detached agent is invisible work — fail
33
+ loud rather than launch without recording.
34
+ - **Scope unchanged.** Handoff delegates the item as-is; it does not toggle
35
+ repos, change scope, or split the item. Out-of-scope discoveries → new
36
+ WorkItem (the receiver follows the same rule).
37
+ - **The receiver still drives the loop** (its own contract + cross-vendor
38
+ grader). Handoff delegates the work, not the gates.
39
+
40
+ ## Sequence
41
+
42
+ 1. `factory_workitem_get` → build the self-contained briefing:
43
+ - goal + acceptance criteria + closure requirements
44
+ - lifecycle state + next required boolean
45
+ - blockers / dependencies (must be satisfied before handoff)
46
+ - branch base (`integration`) + worktree, relevant files/paths
47
+ - the loop contract to run (or instruction to build one via `drive-loop`)
48
+ - latest timestamped plan / verification / requirements artifact paths when present
49
+ - current loop closure summary path (`summary-current.json` / `enact-loop summary show`) when resuming an active loop
50
+ 2. `EnterWorktree` to isolate the receiver's workspace.
51
+ 3. Launch the receiver as a **detached agent** carrying the briefing (a detached
52
+ subagent, or via the factory's Paseo dispatch) with `notifyOnFinish`; do not
53
+ poll it.
54
+ 4. **`factory_assignment_record({ workItemId, repoRoot, host, operatorLane,
55
+ dispatcher, paseoAgentId, paseoWorktree, status: 'active' })`** — register the
56
+ assignment so the board tracks it. (To update its state later — e.g.
57
+ `paseoStatus`, `status: 'completed'` — call `factory_assignment_record` again
58
+ with the returned `assignmentId`.)
59
+
60
+ ## Output
61
+
62
+ `{ workItemId, assignmentId, branch, worktree, recorded: true }`. If
63
+ `factory_assignment_record` fails, treat the handoff as failed — surface it and
64
+ do not leave an unrecorded detached agent running.
65
+
66
+ ## Related
67
+
68
+ - [`work-with-workitem`](../work-with-workitem/SKILL.md) — the delivery lane the
69
+ receiver runs.
70
+ - [`drive-loop`](../drive-loop/SKILL.md) — the contract the receiver drives.
71
+ - [`autonomous-runner`](../autonomous-runner/SKILL.md) — the driver that may hand
72
+ off independent items instead of driving them inline.
@@ -5,6 +5,11 @@ description: "Three-round adversarial planning debate that hands a distilled bun
5
5
 
6
6
  # Hyperplan
7
7
 
8
+ > **Storage is GLOBAL — never write to a repo-local `.enact` dir.** Resolve the project's global dir:
9
+ > 1. `ROOT="$(git rev-parse --show-toplevel)"`
10
+ > 2. In `~/.enact/workspaces.json` → `workspaces`, find the entry whose `.root` == `$ROOT`; its object key is `<name>-<id>` (e.g. `enact-evolve-a38d1e`).
11
+ > 3. Loop artifacts go under `~/.enact/factory/projects/<key>/…`.
12
+
8
13
  ## Purpose
9
14
 
10
15
  Use `$hyperplan` when a plan needs adversarial pressure before execution starts.
@@ -30,14 +35,17 @@ Run exactly 3 rounds:
30
35
 
31
36
  Persist each round under:
32
37
 
33
- - `.enact/loop/hyperplan/<sessionId>/round-1.md`
34
- - `.enact/loop/hyperplan/<sessionId>/round-2.md`
35
- - `.enact/loop/hyperplan/<sessionId>/round-3.md`
38
+ - `~/.enact/factory/projects/<key>/hyperplan/<sessionId>/<YYMMDDHH>-round-1.md`
39
+ - `~/.enact/factory/projects/<key>/hyperplan/<sessionId>/<YYMMDDHH>-round-2.md`
40
+ - `~/.enact/factory/projects/<key>/hyperplan/<sessionId>/<YYMMDDHH>-round-3.md`
41
+
42
+ Resolve `<key>` from `~/.enact/workspaces.json`; get the timestamp prefix via `date +%y%m%d%H`. **Append-only:** each debate run writes fresh round files; never overwrite prior `YYMMDDHH-round-*.md` artifacts.
36
43
 
37
44
  ## Handoff Rule
38
45
 
39
46
  The lead agent does not write the final implementation plan directly.
40
47
  After round 3, hand the distilled debate bundle to a separate `$plan` invocation.
48
+ `$plan` must read the latest `hyperplan/<sessionId>/<YYMMDDHH>-round-*.md` files before writing new timestamped plan artifacts.
41
49
 
42
50
  ## Execution Notes
43
51