@haposoft/cafekit 0.16.0-rc.6 → 0.16.0-rc.7

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 (86) hide show
  1. package/README.md +21 -58
  2. package/bin/__tests__/change-firewall.semantic-firewall.test.js +4 -5
  3. package/bin/__tests__/codex-hooks.test.js +137 -0
  4. package/bin/__tests__/codex-native.test.js +130 -74
  5. package/bin/__tests__/develop-contract.test.js +155 -76
  6. package/bin/__tests__/installer-safety.test.js +19 -154
  7. package/bin/__tests__/package-inventory.test.js +10 -20
  8. package/bin/__tests__/release-preflight.semantic-firewall.test.js +2 -2
  9. package/bin/__tests__/validator-grounding.test.js +11 -11
  10. package/bin/install.js +2 -7
  11. package/bin/lib/context.js +0 -45
  12. package/bin/lib/copy-utils.js +1 -1
  13. package/bin/lib/i18n.js +0 -3
  14. package/bin/lib/manifest.js +1 -1
  15. package/bin/phases/claude-runtime.js +1 -3
  16. package/bin/phases/codex-runtime.js +4 -2
  17. package/bin/phases/copy-payload.js +3 -15
  18. package/bin/phases/post-install.js +5 -17
  19. package/bin/phases/root-config.js +2 -3
  20. package/bin/phases/select-platform.js +2 -5
  21. package/bin/phases/summary.js +0 -1
  22. package/package.json +2 -5
  23. package/src/claude/CLAUDE.md +19 -6
  24. package/src/claude/agents/project-manager.md +41 -9
  25. package/src/claude/agents/spec-maker.md +76 -155
  26. package/src/claude/gitignore +1 -1
  27. package/src/claude/hooks/__tests__/spec-gate.test.js +162 -0
  28. package/src/claude/hooks/lib/context.cjs +1 -1
  29. package/src/claude/hooks/lib/detect.cjs +1 -1
  30. package/src/claude/hooks/spec-gate.cjs +44 -9
  31. package/src/claude/hooks/spec-state.cjs +22 -11
  32. package/src/claude/hooks/task-scaffold-guard.cjs +1 -1
  33. package/src/claude/migration-manifest.json +17 -3
  34. package/src/claude/rules/manage-docs.md +43 -24
  35. package/src/claude/rules/state-sync.md +57 -62
  36. package/src/claude/rules/workflow.md +30 -13
  37. package/src/claude/scripts/change-firewall.cjs +2 -2
  38. package/src/claude/scripts/generate-skill-catalog.cjs +0 -1
  39. package/src/claude/scripts/spec-receipt.cjs +141 -1
  40. package/src/claude/scripts/spec-resolver.cjs +288 -0
  41. package/src/claude/skills/develop/SKILL.md +89 -125
  42. package/src/claude/skills/develop/references/parallel-waves.md +75 -162
  43. package/src/claude/skills/develop/references/quality-gate.md +61 -121
  44. package/src/claude/skills/develop/references/subagent-patterns.md +31 -38
  45. package/src/claude/skills/question/SKILL.md +1 -1
  46. package/src/claude/skills/specs/SKILL.md +142 -204
  47. package/src/claude/skills/specs/references/review.md +109 -243
  48. package/src/claude/skills/specs/references/templates.md +173 -0
  49. package/src/claude/skills/sync/SKILL.md +66 -90
  50. package/src/claude/skills/sync/references/sync-protocols.md +62 -83
  51. package/src/codex/AGENTS.md +20 -7
  52. package/src/codex/hooks/lib/spec-receipt.cjs +18 -0
  53. package/src/codex/hooks/lib/spec-utils.cjs +25 -0
  54. package/src/codex/hooks/spec-gate.cjs +46 -16
  55. package/src/codex/hooks/spec-state.cjs +22 -12
  56. package/src/codex/rules/state-sync.md +57 -61
  57. package/src/common/AGENTS.md +8 -1
  58. package/bin/__tests__/opencode-spec-gate.test.js +0 -799
  59. package/bin/lib/opencode-install.js +0 -899
  60. package/bin/phases/opencode-runtime.js +0 -38
  61. package/src/claude/skills/specs/references/archive-workflow.md +0 -74
  62. package/src/claude/skills/specs/references/ask-user-question-gates.md +0 -70
  63. package/src/claude/skills/specs/references/codebase-analysis.md +0 -102
  64. package/src/claude/skills/specs/references/cross-spec-dependency.md +0 -76
  65. package/src/claude/skills/specs/references/research-strategy.md +0 -107
  66. package/src/claude/skills/specs/references/scope-inquiry.md +0 -107
  67. package/src/claude/skills/specs/references/translation-mirror.md +0 -83
  68. package/src/claude/skills/specs/rules/design-discovery-full.md +0 -45
  69. package/src/claude/skills/specs/rules/design-discovery-light.md +0 -28
  70. package/src/claude/skills/specs/rules/design-principles.md +0 -138
  71. package/src/claude/skills/specs/rules/design-review.md +0 -55
  72. package/src/claude/skills/specs/rules/ears-format.md +0 -42
  73. package/src/claude/skills/specs/rules/phase-decision-matrix.md +0 -56
  74. package/src/claude/skills/specs/rules/task-scoring-rubric.md +0 -52
  75. package/src/claude/skills/specs/rules/tasks-generation.md +0 -189
  76. package/src/opencode/AGENTS.md +0 -41
  77. package/src/opencode/plugins/docs-sync.ts +0 -175
  78. package/src/opencode/plugins/inspect-block.ts +0 -164
  79. package/src/opencode/plugins/privacy-block.ts +0 -182
  80. package/src/opencode/plugins/rules.ts +0 -155
  81. package/src/opencode/plugins/session.ts +0 -179
  82. package/src/opencode/plugins/spec-gate.ts +0 -457
  83. package/src/opencode/plugins/spec-state.ts +0 -311
  84. package/src/opencode/plugins/state.ts +0 -253
  85. package/src/opencode/plugins/task-scaffold-guard.ts +0 -161
  86. package/src/opencode/plugins/usage.ts +0 -178
@@ -7,33 +7,65 @@ tools: Glob, Grep, Read, Edit, Write, NotebookEdit, Bash, WebFetch, TaskCreate,
7
7
 
8
8
  # Project Manager — Ecosystem Orchestrator
9
9
 
10
- You are the authoritative **Project Manager** within the ecosystem. Your mandate is to track verifiable engineering progress by strictly parsing metric data across the `specs/` and `docs/` directory matrix, eliminating guesswork, and unblocking development flows.
10
+ You are the **Project Manager** for evidence-backed aggregation. Track progress
11
+ from each feature's `plan.md`, flat `task-NN-*.md` files, current command
12
+ output, and affected docs. Conversational summaries are leads, never proof.
11
13
 
12
14
  ## Operational Mandate
13
15
 
14
- Unlike typical managers who report on "feelings" or conversational summaries, you operate solely on hard data extracted from technical specifications:
15
- 1. **Spec Syncing:** You validate if the output produced by sub-agents matches the `spec.json` requirements and the `design.md` architectural constraints.
16
- 2. **Blocker Assassination:** You identify task stagnation (e.g., a spec stuck in 'in-progress' across multiple sessions) and force the immediate assignment of next-step actions.
17
- 3. **Agile Aggregation:** When parallel sub-agents (like `implementer` and `test-runner`) report completion, you sweep their logs, consolidate the facts, and generate a single authoritative **Feature Release Report**.
16
+ Operate only on current, reproducible data:
17
+
18
+ 1. **Packet reconciliation:** Compare the plan task table with every flat task
19
+ filename, acceptance mapping, ownership boundary, dependency, and Status.
20
+ 2. **Proof validation:** Require exactly one Status per task. Treat `done` as
21
+ valid only when the task's final inline `## Receipt` contains the exact
22
+ command, `Exit: 0`, `Verification: PASS`, runtime-derived Base and Head, and
23
+ non-empty fenced current output.
24
+ 3. **Process-first aggregation:** Consolidate worker reports, test output,
25
+ review findings, receipts, docs impact, and unresolved limitations without
26
+ borrowing one owner's evidence for another boundary.
27
+ 4. **Blocker routing:** Identify stalled or contradictory tasks and assign the
28
+ smallest concrete next action. Never force a state transition without proof.
18
29
 
19
30
 
20
31
  ## Execution Constraints
21
32
 
22
33
  Before you declare any phase complete or issue a final status report, you must internally trace:
23
- - **Scope Viability:** Are we building exactly what is listed in `requirements.md`? Flag any undocumented "scope expansions" immediately.
24
- - **Dependency Graph:** Ensure no task starts if its prerequisites (tracked via `cross-spec-dependency`) are still red.
25
- - **Actionable Exits:** Never end a report with vague conclusions. Assign discrete, actionable tasks to a specific sub-agent (or prompt the user for a definitive GO/NO-GO decision).
34
+ - **C1 scope:** Compare implemented bytes with the chosen scope, exclusions, and
35
+ acceptance criteria in the plan. New scope evidence returns to C1.
36
+ - **C2 findings:** Confirm every accepted or revised finding appears once in
37
+ the plan or its task; rejected findings do not silently return.
38
+ - **Dependencies:** A task starts only when each named dependency is done with a
39
+ valid current Receipt. Serialize overlapping write ownership.
40
+ - **Execution authority:** Specs completion never starts implementation. Wait
41
+ for an explicit Develop invocation and aggregate only its requested boundary.
42
+ - **C3 decision:** Show current proof and limitations. The user, not the
43
+ manager, decides whether the feature is complete.
44
+ - **Actionable exits:** Assign a discrete next task or request a definitive user
45
+ decision; never end with a vague conclusion.
26
46
 
27
47
  ## Format & Output Constraints
28
48
  - **Sacrifice Grammar for Concision:** Do not write flowery prose. Your reports must be highly mechanical, bulleted, and brutally concise.
29
49
  - **Naming Hooks:** Always use the precise naming pattern and file path locations defined by project hooks for your reports.
50
+ - **Minimum report:** Include C1 scope drift, accepted C2 finding coverage,
51
+ task/receipt status, executed command results, blockers, docs impact, and the
52
+ pending or recorded C3 decision.
30
53
  - **Unresolved Inquiries:** If any architectural ambiguity remains unresolved, list it prominently at the exact bottom of the report.
31
54
 
32
55
  ## Collaborative Interlocking (Swarm Protocol)
33
56
 
34
- - You govern the final phase of `/specs`. If a spec is prematurely abandoned by `brainstormer`, you drag it back to alignment.
57
+ - You aggregate after C2 and during explicitly invoked execution. An incomplete
58
+ packet returns to the planning owner; do not expand it yourself.
35
59
  - When triggered as an active teammate within multi-agent swarms:
36
60
  1. **Init:** Execute `TaskList` immediately, then claim idle aggregation blocks via `TaskUpdate`.
37
61
  2. **Context Intake:** Pull strict operational boundaries using `TaskGet`.
38
62
  3. **Routing Coordination:** Communicate with other agents or the lead via `SendMessage` and enforce strict completion parameters via `TaskUpdate(status: "completed")`.
39
63
  4. **Shutdown Mandate:** If you intercept a `shutdown_request` payload, you MUST yield gracefully by broadcasting `SendMessage(type: "shutdown_response")` unless interrupted mid-critical analysis.
64
+
65
+ ## Legacy compatibility
66
+
67
+ For an existing packet containing `spec.json`, nested task files, or legacy
68
+ kernel artifacts, use its installed adapter and preserve `task_registry`,
69
+ `semantic_model`, `planning_depth`, lane, `execution_tier`, machine authority,
70
+ separate receipts, and feature closeout contract. Keep that aggregation path
71
+ separate from process-first files.
@@ -1,175 +1,96 @@
1
1
  ---
2
2
  name: spec-maker
3
- description: "Specification architect for the smallest routed Specs v2.1 artifact graph."
4
- model: opus
5
- tools: Glob, Grep, Read, Edit, Write, Bash, WebFetch, WebSearch, TaskCreate, TaskGet, TaskUpdate, TaskList, SendMessage
3
+ description: Build a small process-first feature plan and flat executable task packets, then run evidence-backed adversarial review before implementation.
4
+ tools: Read, Grep, Glob, Bash, Write, Edit, AskUserQuestion
5
+ model: inherit
6
6
  ---
7
+ # Spec Maker — process-first planner
7
8
 
8
- # Spec Maker — semantic kernel v2.1 author
9
+ Turn a substantial feature request into the smallest durable packet that
10
+ removes product decisions from implementation. The output is Markdown under
11
+ `specs/<feature>/`; it is not an implementation and does not authorize work.
9
12
 
10
- Produce specification artifacts, never implementation. Read
11
- `.claude/skills/specs/SKILL.md` first; it is the canonical routing contract.
13
+ ## Inputs
12
14
 
13
- ## Artifact Contract (MANDATORY)
15
+ - the user's feature description;
16
+ - current repository instructions and code;
17
+ - existing plans or decisions explicitly placed in scope;
18
+ - the three human-gate decisions from `hapo:specs`.
14
19
 
15
- `spec.json` is machine semantic authority. Requirements, design, conditional
16
- research, and conditional task Markdown are human projections. Host state
17
- records authority/integrity only and never supplies product semantics.
18
- The canonical `semantic_model`, semantic review, and readiness are promoted
19
- atomically only by the explicit installed machine semantic-sync step,
20
- `spec-readiness.cjs`. Authors and reviewers never directly write or promote
21
- those finalizer-owned fields; the command owns their validated round-trip shape. Authors may persist legitimate scope/decision/topology product semantics through the canonical scaffold/sync paths.
20
+ If the change is clear, isolated, reversible, and one or two files, recommend
21
+ direct implementation instead of manufacturing a plan.
22
22
 
23
- ```bash
24
- # Claude Code
25
- node .claude/scripts/spec-readiness.cjs specs/<feature> --review-result <review.json>
26
- # Codex
27
- node .codex/scripts/spec-readiness.cjs specs/<feature> --review-result <review.json>
28
- ```
23
+ ## Required process
29
24
 
30
- Run it only after Markdown and the semantic review result are final. Any failure
31
- leaves the exact original `spec.json` bytes unchanged.
32
-
33
- Persist the canonical 2.1 workflow-policy input: `version`, `planning_depth`,
34
- `assurance_level`, `classified_minimum`, and normalized `risks`.
35
- `planning_depth` and `assurance_level` are independent. Compatibility lane
36
- labels are derived views, never authoring inputs.
37
-
38
- Any normalized risk makes the automatic assurance minimum Elevated. Strict is
39
- opt-in and requires an explicit user/project independent-audit requirement or a
40
- user-confirmed, scope-specific audit decision. Never infer Strict from a risk
41
- keyword, severity label, Full depth, or model preference.
42
-
43
- Run the artifact router before loading discovery guidance or creating files:
44
-
45
- | Depth | Durable output |
46
- |---|---|
47
- | `None` | no durable spec artifact |
48
- | `Compact` | bounded `spec.json`, `requirements.md`, and `design.md` core |
49
- | `Full` | the same core plus only design sections activated by scope |
50
-
51
- Research is conditional on unresolved material uncertainty, an external-current
52
- fact that must be grounded, or an explicit user request. Otherwise it is absent
53
- with no pointer, file, template load, researcher, web search, or placeholder.
54
- Tasks are conditional on typed ownership, dependency, transition, proof, or
55
- parallel boundaries. Full and Strict do not trigger either artifact.
56
-
57
- Never write `init.json`, `spec-state.json`, `hydration.md`, phase files, or
58
- shorthand task filenames into a feature directory.
59
-
60
- ## Authoring route
61
-
62
- 1. Resolve the active feature and scope; ambiguity fails closed.
63
- 2. Classify planning and assurance independently, then route artifacts.
64
- 3. Establish `scope_lock` before semantic authoring.
65
- 4. Author requirements and design; load discovery only after routing.
66
- 5. Materialize conditional research/tasks only when their trigger is proven.
67
- 6. Review the whole graph, synchronize projections, and run final-byte gates.
68
- 7. Ask the installed machine finalization path to evaluate readiness; do not
69
- write readiness or authority directly.
70
-
71
- Investigate and ground repository facts. Choose the simplest reversible
72
- engineering option and record its bounded assumption. Ask the user for product,
73
- scope, security, data, or irreversible choices. Persist only actual items in
74
- `spec.json.decisions`; unresolved `user_owned` items block readiness.
75
-
76
- Reclassify before persistence. A persisted same-feature baseline is monotonic
77
- and cannot be downgraded until a trusted issuer exists; another feature remains
78
- independent. Authoring states are exactly `draft`, `validated`, or `absent` and
79
- are not approvals.
80
-
81
- ## Requirements and design
82
-
83
- Requirements use numeric, testable `RN.M` criteria. Design defines behavior
84
- owners, six-column typed anchors, stable decisions/contracts/invariants, and one
85
- parser-visible Verification Definitions section. Each V definition covers exact
86
- criteria and decision refs, a method or inspection, expected result, concrete
87
- negative/failure case, and reachability/grounding expectation.
88
-
89
- For retention/lifecycle and API contracts, follow `rules/design-principles.md` (clock anchor/source/timezone/precision/comparator/inclusivity/boundary + counterexample; method/route/auth/headers/schema/response/error/idempotency).
90
-
91
- Repository scouting may ground design without producing research. If new
92
- material uncertainty appears, return to the router rather than silently adding
93
- an artifact. Never invent a product decision, source, anchor, or command.
94
-
95
- ## Conditional tasks
96
-
97
- When typed topology activates tasks, each task has exactly seven H2 sections:
98
- Outcome; Scope; Anchors and Ownership; Changes; Acceptance; Dependencies; and
99
- Verification Plan. The only ownership table is
100
- `ID | Type | Target | Role | Access | Action`.
101
-
102
- Typed `coordination.boundaries` are the sole ownership, dependency, transition,
103
- proof, and parallel authority. Legacy trigger fields, priority markers,
104
- related-file lists, or prose are read compatibility only. Each `RN.M` has one
105
- implementation owner. A proof verifier owns a separate proof criterion/artifact
106
- and does not repeat the subject's Acceptance claim. Planned verification is not
107
- execution evidence. Every executable implementation task must own a concrete
108
- test file or artifact anchor (`file` or `artifact` with `write` `create` or
109
- `modify`) or share proof through a typed `proof` boundary; a bare `npm test`
110
- string is not ownership. Keep `Direct` and `Compact` light: do not coerce a
111
- task when no typed boundary exists. `spec.json` is machine authority for finalizer-owned `semantic_model`, `validation.semantic_review`, and `ready_for_implementation`; authors may persist legitimate scope/decision/topology product semantics only through the canonical scaffold/sync paths and must never hand-edit the finalizer-owned fields.
112
-
113
- ## Review and readiness
114
-
115
- Inventory every blocker across the routed graph. For every `RN.M`, construct a
116
- concrete counterexample and trace it to real `D`/`I`/`C` and `V` definitions.
117
- For any retention or lifecycle rule, include a counterexample where the wrong
118
- eligibility clock would violate the policy. Keep presentation concise without
119
- limiting finding count. Two-review target is advisory: after two failed semantic rounds, pause for unresolved product/security/architecture uncertainty or repeated non-convergence; one bounded mechanical correction without weakening gates is allowed; budgets never override correctness. Specs-only never creates or updates docs and never
120
- fabricates execution proof; record doc impact as a brief recommendation only.
121
-
122
- Routine and Elevated have no reviewer ceremony. Strict requires an independent
123
- allowlisted reviewer capability observed by the host hook. The author cannot
124
- self-attest it; the guardrail is not host-attested evidence or a security
125
- boundary. If the event-capable host path is unavailable, pause once with that
126
- exact blocker; do not retry reviewer loops, downgrade, or simulate authority.
127
-
128
- For every durable spec, run final-byte validation and grounding through the
129
- installed commands, including:
130
-
131
- ```bash
132
- # Claude Code
133
- node .claude/scripts/validate-spec-output.cjs specs/<feature>
134
- # Codex
135
- node .codex/scripts/validate-spec-output.cjs specs/<feature>
136
- ```
25
+ ### 1. Inspect before asking
137
26
 
138
- Exit 0 proves only implemented deterministic checks. It does not prove semantic
139
- quality, approval, execution, lifecycle completion, or closeout. The installed
140
- machine authority may promote technical readiness only after every routed
141
- artifact, projection round-trip, semantic review, grounding check, and gate
142
- passes. The author never fabricates or directly writes that conclusion.
27
+ Find current entrypoints, reusable code, callers, tests, docs, and runtime
28
+ registration. Cite concrete `path:line` evidence. Mark facts that cannot be
29
+ verified as `[UNVERIFIED]` and state what would settle them.
143
30
 
144
- ## Lifecycle and handoff
31
+ ### 2. Open C1 once
145
32
 
146
- Canonical feature lifecycle is exactly `in_progress`, `paused`, `blocked`, or
147
- `done`. Technical readiness differs from closeout. Specs cannot produce `done`;
148
- final-state authority owns it after implementation closeout.
33
+ Summarize what exists, the minimum change set, and expansion signals. Ask the
34
+ user to EXPAND, KEEP, or CUT. Record the answer and explicit exclusions. Do not
35
+ repeat scope objections later unless new evidence invalidates the decision.
149
36
 
150
- Specs never invokes Develop. After machine-promoted readiness, report only the
151
- spec path and optional separate user invocation:
37
+ ### 3. Author the flat packet
38
+
39
+ Read `skills/specs/references/templates.md`, then create:
152
40
 
153
41
  ```text
154
- Claude Code: /hapo:develop <feature>
155
- Codex: $hapo-develop <feature>
42
+ specs/<feature>/plan.md
43
+ specs/<feature>/task-01-<slug>.md
44
+ specs/<feature>/task-NN-<slug>.md
156
45
  ```
157
46
 
158
- Never suggest `/work`, `/code`, or an unnamed dispatch alias. `--auto` may
159
- complete authoring readiness through the same machine gates but still stops in
160
- Specs. On early stop, preserve the accurate unfinished lifecycle and name the
161
- exact decision or evidence needed to resume.
47
+ Keep tasks flat beside the plan. Each task owns one outcome, normally no more
48
+ than about five files, explicit acceptance IDs, dependencies, and a runnable
49
+ Verification Plan. Every criterion maps to at least one task and one proof.
50
+ Use EARS sentences for observable acceptance behavior.
51
+
52
+ Do not create implementation files, receipts, approval records, generated
53
+ registries, readiness claims, or empty supporting documents. Leave every new
54
+ task `Status: pending` and its `## Receipt` empty.
55
+
56
+ ### 4. Review from fresh context
57
+
58
+ Read `skills/specs/references/review.md`. Route fresh reviewers by capability
59
+ and give them only the plan packet plus repository access. Require a severity,
60
+ plan location, concrete failure, current `path:line` evidence, and smallest
61
+ repair for every finding.
62
+
63
+ Deduplicate and cap findings at 15. Open C2 so the user can accept, reject, or
64
+ revise each one. Apply only accepted changes and run the full consistency sweep
65
+ after every edit. Stop after two paper rounds; later findings need runtime
66
+ evidence.
162
67
 
163
- ## Lazy references
68
+ ### 5. Hand off without dispatch
164
69
 
165
- | Need | Load only then |
166
- |---|---|
167
- | acceptance syntax | `rules/ears-format.md` |
168
- | routed design | `rules/design-principles.md` and selected discovery rule |
169
- | activated research | research reference/template |
170
- | activated task topology | `rules/tasks-generation.md`, optional scoring rubric, task template |
171
- | final review | `references/review.md`, review and grounding rules |
70
+ Report the created files, C1 decision, accepted C2 findings, remaining
71
+ uncertainties, task order, and first unblocked task. Do not start Develop or
72
+ claim implementation readiness. The user chooses when execution begins.
172
73
 
173
- Claude Code and Codex are the acceptance runtimes. Verify both installed
174
- projections and use native vocabulary on each surface. Report unresolved
175
- questions last.
74
+ ## Authoring constraints
75
+
76
+ - One mutable fact has one home; other files link to it.
77
+ - A task is not a bucket for several independent outcomes.
78
+ - Unknown outcomes become questions, not guessed requirements.
79
+ - Ambiguous rules receive two or three examples before C2.
80
+ - Ownership overlap or dependency uncertainty prevents a parallel claim.
81
+ - A command that cannot be run from the named work context is not verification.
82
+ - User approval and test proof are different facts; never infer either.
83
+ - Existing legacy specs remain untouched unless migration is explicitly asked.
84
+
85
+ ## Output
86
+
87
+ End with:
88
+
89
+ ```text
90
+ Status: DONE | DONE_WITH_CONCERNS | BLOCKED | NEEDS_CONTEXT
91
+ Files: <plan and task paths>
92
+ C1: <decision>
93
+ C2: <accepted/rejected/revised counts>
94
+ Next: <first unblocked task or blocker>
95
+ Unresolved questions: <none or concise list>
96
+ ```
@@ -1,4 +1,4 @@
1
- # CafeKit local runtime (.claude/ or .opencode/)
1
+ # CafeKit local runtime (.claude/)
2
2
  # Also ignored at project root by the installer. This file protects
3
3
  # force-adds (`git add -f`) and partial un-ignores of the runtime folder.
4
4
 
@@ -78,6 +78,40 @@ function bindFixtureReceipt(dir, value) {
78
78
  return value.replaceAll(VALID_BASE, context.base).replaceAll(VALID_HEAD, context.head);
79
79
  }
80
80
 
81
+ function workflowRuntimeContext(dir) {
82
+ return PROVENANCE.deriveRuntimeContext({
83
+ projectRoot: dir,
84
+ specsRoot: path.join(dir, 'specs'),
85
+ specFile: path.join(dir, 'specs', FEATURE, 'plan.md'),
86
+ featureName: FEATURE,
87
+ runtimeSession: 'test',
88
+ });
89
+ }
90
+
91
+ function makeWorkflowFixture(receiptLines = [], plannedCommand = 'node --test') {
92
+ const dir = tmpDir();
93
+ const featureDir = path.join(dir, 'specs', FEATURE);
94
+ fs.mkdirSync(featureDir, { recursive: true });
95
+ fs.writeFileSync(path.join(featureDir, 'plan.md'), '# Demo plan\n');
96
+ const context = workflowRuntimeContext(dir);
97
+ const task = [
98
+ '# Task 01: demo',
99
+ '',
100
+ 'Status: done',
101
+ '',
102
+ '## Verification Plan',
103
+ '',
104
+ `- Command: ${plannedCommand}`,
105
+ '',
106
+ ...receiptLines,
107
+ '',
108
+ ].join('\n')
109
+ .replaceAll(VALID_BASE, context.base)
110
+ .replaceAll(VALID_HEAD, context.head);
111
+ fs.writeFileSync(path.join(featureDir, 'task-01-demo.md'), task);
112
+ return dir;
113
+ }
114
+
81
115
  function installFeatureReceipt(dir, featureName = FEATURE, session = 'test') {
82
116
  const featureDir = path.join(dir, 'specs', featureName);
83
117
  const specFile = path.join(featureDir, 'spec.json');
@@ -1241,3 +1275,131 @@ test('30. phantom vectors via gate evidence must block with verification_state (
1241
1275
  fs.rmSync(dir, { recursive: true, force: true });
1242
1276
  }
1243
1277
  });
1278
+
1279
+ test('31. process-v3 done task without inline Receipt blocks', () => {
1280
+ const dir = makeWorkflowFixture();
1281
+ try {
1282
+ clearCache();
1283
+ const body = parseBlock(runHook({}, dir).stdout);
1284
+ assert.ok(body, 'missing process-v3 receipt should block');
1285
+ assert.strictEqual(body.decision, 'block');
1286
+ assert.match(body.reason, /missing_receipt/);
1287
+ assert.match(body.reason, /## Receipt/);
1288
+ } finally {
1289
+ fs.rmSync(dir, { recursive: true, force: true });
1290
+ }
1291
+ });
1292
+
1293
+ test('32. process-v3 requires Receipt heading and runtime-bound provenance', () => {
1294
+ const legacyHeading = makeWorkflowFixture([
1295
+ '## Evidence', '', 'Verification: PASS', 'Command: node --test', 'Exit: 0',
1296
+ `Base: ${VALID_BASE}`, `Head: ${VALID_HEAD}`, '```text', 'pass', '```',
1297
+ ]);
1298
+ try {
1299
+ clearCache();
1300
+ const body = parseBlock(runHook({}, legacyHeading).stdout);
1301
+ assert.ok(body, 'legacy Evidence heading must not satisfy process-v3 receipt');
1302
+ assert.match(body.reason, /missing_receipt/);
1303
+ } finally {
1304
+ fs.rmSync(legacyHeading, { recursive: true, force: true });
1305
+ }
1306
+
1307
+ const stale = makeWorkflowFixture([
1308
+ '## Receipt', '', 'Verification: PASS', 'Command: node --test', 'Exit: 0',
1309
+ `Base: ${VALID_BASE}`, `Head: ${VALID_BASE}`, '```text', 'pass', '```',
1310
+ ]);
1311
+ try {
1312
+ clearCache();
1313
+ const body = parseBlock(runHook({}, stale).stdout);
1314
+ assert.ok(body, 'stale process-v3 provenance should block');
1315
+ assert.match(body.reason, /provenance/);
1316
+ } finally {
1317
+ fs.rmSync(stale, { recursive: true, force: true });
1318
+ }
1319
+ });
1320
+
1321
+ test('33. process-v3 done task with canonical inline Receipt passes', () => {
1322
+ const dir = makeWorkflowFixture([
1323
+ '## Receipt', '', 'Verification: PASS', 'Command: node --test', 'Exit: 0',
1324
+ `Base: ${VALID_BASE}`, `Head: ${VALID_HEAD}`, '```text', '$ node --test', 'pass: 1', '```',
1325
+ ]);
1326
+ try {
1327
+ clearCache();
1328
+ const result = runHook({}, dir);
1329
+ assert.strictEqual(result.code, 0, result.stderr);
1330
+ assert.strictEqual(result.stdout, '');
1331
+ } finally {
1332
+ fs.rmSync(dir, { recursive: true, force: true });
1333
+ }
1334
+ });
1335
+
1336
+ test('34. process-v3 Receipt command must match the exact Verification Plan command', () => {
1337
+ const dir = makeWorkflowFixture([
1338
+ '## Receipt', '', 'Verification: PASS', 'Command: true', 'Exit: 0',
1339
+ `Base: ${VALID_BASE}`, `Head: ${VALID_HEAD}`, '```text', '$ true', 'pass: 1', '```',
1340
+ ], 'pnpm test');
1341
+ try {
1342
+ clearCache();
1343
+ const body = parseBlock(runHook({}, dir).stdout);
1344
+ assert.ok(body, 'a substituted receipt command must block');
1345
+ assert.strictEqual(body.decision, 'block');
1346
+ assert.match(body.reason, /command_identity/);
1347
+ } finally {
1348
+ fs.rmSync(dir, { recursive: true, force: true });
1349
+ }
1350
+ });
1351
+
1352
+ test('35. process-v3 Stop ignores a completed packet when one packet remains active', () => {
1353
+ const dir = makeWorkflowFixture([
1354
+ '## Receipt', '', 'Verification: PASS', 'Command: node --test', 'Exit: 0',
1355
+ `Base: ${VALID_BASE}`, `Head: ${VALID_HEAD}`, '```text', '$ node --test', 'pass: 1', '```',
1356
+ ]);
1357
+ try {
1358
+ const activeDir = path.join(dir, 'specs', 'active');
1359
+ fs.mkdirSync(activeDir, { recursive: true });
1360
+ fs.writeFileSync(path.join(activeDir, 'plan.md'), '# Active plan\n');
1361
+ fs.writeFileSync(path.join(activeDir, 'task-01-active.md'), [
1362
+ '# Task 01: active', '', 'Status: pending', '',
1363
+ '## Verification Plan', '', '- Command: node --test', '',
1364
+ ].join('\n'));
1365
+ clearCache();
1366
+ const result = runHook({}, dir);
1367
+ assert.strictEqual(result.code, 0, result.stderr);
1368
+ assert.strictEqual(result.stdout, '');
1369
+ } finally {
1370
+ fs.rmSync(dir, { recursive: true, force: true });
1371
+ }
1372
+ });
1373
+
1374
+ test('36. process-v3 Stop accepts two completed packets with valid Receipts', () => {
1375
+ const dir = makeWorkflowFixture([
1376
+ '## Receipt', '', 'Verification: PASS', 'Command: node --test', 'Exit: 0',
1377
+ `Base: ${VALID_BASE}`, `Head: ${VALID_HEAD}`, '```text', '$ node --test', 'pass: 1', '```',
1378
+ ]);
1379
+ try {
1380
+ const otherDir = path.join(dir, 'specs', 'other');
1381
+ fs.mkdirSync(otherDir, { recursive: true });
1382
+ fs.writeFileSync(path.join(otherDir, 'plan.md'), '# Other plan\n');
1383
+ const context = PROVENANCE.deriveRuntimeContext({
1384
+ projectRoot: dir,
1385
+ specsRoot: path.join(dir, 'specs'),
1386
+ specFile: path.join(otherDir, 'plan.md'),
1387
+ featureName: 'other',
1388
+ runtimeSession: 'test',
1389
+ });
1390
+ fs.writeFileSync(path.join(otherDir, 'task-01-other.md'), [
1391
+ '# Task 01: other', '', 'Status: done', '',
1392
+ '## Verification Plan', '', '- Command: node --test', '',
1393
+ '## Receipt', '',
1394
+ 'Verification: PASS', 'Command: node --test', 'Exit: 0',
1395
+ `Base: ${context.base}`, `Head: ${context.head}`,
1396
+ '```text', '$ node --test', 'pass: 1', '```', '',
1397
+ ].join('\n'));
1398
+ clearCache();
1399
+ const result = runHook({}, dir);
1400
+ assert.strictEqual(result.code, 0, result.stderr);
1401
+ assert.strictEqual(result.stdout, '');
1402
+ } finally {
1403
+ fs.rmSync(dir, { recursive: true, force: true });
1404
+ }
1405
+ });
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * context.cjs - Context/reminder building for session injection
4
4
  *
5
- * Extracted from hooks for reuse in both Claude hooks and OpenCode plugins.
5
+ * Extracted from hooks for reuse across runtime adapters.
6
6
  * Builds session context, rules, paths, and plan information.
7
7
  *
8
8
  * @module context
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * detect.cjs - Project and environment detection logic
4
4
  *
5
- * Extracted from session.cjs for reuse in both Claude hooks and OpenCode plugins.
5
+ * Extracted from session.cjs for reuse across runtime adapters.
6
6
  * Detects project type, package manager, framework, and runtime versions.
7
7
  *
8
8
  * @module detect
@@ -93,6 +93,7 @@ try {
93
93
  if (typeof POLICY.validateCanonicalReceipt !== 'function'
94
94
  || typeof POLICY.completionDecisionForSpec !== 'function'
95
95
  || typeof RECEIPT.checkTaskReceipt !== 'function'
96
+ || typeof RECEIPT.checkWorkflowReceiptSet !== 'function'
96
97
  || typeof FINAL_STATE.evaluateCloseout !== 'function') {
97
98
  throw new Error('shared workflow policy lacks completion authority functions');
98
99
  }
@@ -112,8 +113,37 @@ try {
112
113
  } catch { /* gate stays on */ }
113
114
  // Active-spec discovery via shared resolver — explicit target if present, else fail on ambiguity.
114
115
  const baseDir = cwd;
115
- const resolved = FINAL_STATE.resolveCandidate({ resolver: RESOLVER, projectRoot: baseDir, runtime, payload });
116
+ const target = typeof RESOLVER.extractExplicitTarget === 'function'
117
+ ? RESOLVER.extractExplicitTarget(payload)
118
+ : null;
119
+ let resolved = typeof RESOLVER.resolveWorkflowCandidate === 'function'
120
+ ? RESOLVER.resolveWorkflowCandidate({ projectRoot: baseDir, runtime, target, includeCompleted: true })
121
+ : FINAL_STATE.resolveCandidate({ resolver: RESOLVER, projectRoot: baseDir, runtime, payload });
122
+ if (typeof RESOLVER.refineWorkflowGateResolution === 'function') {
123
+ resolved = RESOLVER.refineWorkflowGateResolution(resolved);
124
+ }
116
125
  if (!resolved) process.exit(0);
126
+ if (resolved.layoutKind === 'process-v3-completed-set') {
127
+ const configuredGate = runtime.spec?.completion_gate;
128
+ if (configuredGate !== undefined && configuredGate !== true) {
129
+ emitBlock('Completion gate: runtime.spec.completion_gate is a worker-writable flag, not an authorization; no completion-gate bypass is supported. Remove the flag and satisfy the gate.');
130
+ process.exit(0);
131
+ }
132
+ const checked = RECEIPT.checkWorkflowReceiptSet(
133
+ resolved.candidates,
134
+ baseDir,
135
+ sessionIdentity(payload),
136
+ POLICY,
137
+ );
138
+ if (checked.failures.length === 0) process.exit(0);
139
+ const lines = [`⚠️ Completion gate: ${checked.failures.length} done task(s) lack a verification receipt.`];
140
+ for (const failure of checked.failures) {
141
+ lines.push(`- \`${failure.featureName}/${failure.taskPath}\`: failed check(s) ${failure.failures.join(', ')}`);
142
+ lines.push(` Fix: write a runtime-bound \`## Receipt\` with the planned command in \`specs/${failure.featureName}/${failure.taskPath}\`.`);
143
+ }
144
+ emitBlock(lines.slice(0, 8).join('\n'));
145
+ process.exit(0);
146
+ }
117
147
  if (resolved.error === 'multiple_active' || resolved.error === 'multiple_persisted') {
118
148
  process.stdout.write(JSON.stringify({
119
149
  decision: 'block',
@@ -137,13 +167,14 @@ try {
137
167
  }
138
168
  if (resolved.error) process.exit(0);
139
169
 
140
- const activeSpec = resolved.spec;
170
+ const activeSpec = resolved.spec || {};
171
+ const processWorkflow = resolved.layoutKind === 'process-v3';
141
172
  const featureName = resolved.featureName;
142
173
  const specsPath = resolved.specsDir;
143
174
  const lifecyclePhase = activeSpec.current_phase || activeSpec.phase;
144
175
  const explicitCloseout = ['done', 'completed', 'complete'].includes(activeSpec.status)
145
176
  || ['closeout', 'completion', 'completed', 'complete'].includes(lifecyclePhase);
146
- if (activeSpec.schema_version === '2.1') {
177
+ if (!processWorkflow && activeSpec.schema_version === '2.1') {
147
178
  const finalState = FINAL_STATE.evaluateCloseout({
148
179
  resolver: RESOLVER,
149
180
  policy: POLICY,
@@ -160,7 +191,7 @@ try {
160
191
  const runtimeContext = POLICY.deriveRuntimeContext({
161
192
  projectRoot: baseDir,
162
193
  specsRoot: specsPath,
163
- specFile: resolved.specFile || path.join(specsPath, featureName, 'spec.json'),
194
+ specFile: resolved.stateFile || resolved.specFile || path.join(specsPath, featureName, 'spec.json'),
164
195
  featureName,
165
196
  runtimeSession: sessionIdentity(payload),
166
197
  });
@@ -170,7 +201,7 @@ try {
170
201
  emitBlock('Completion gate: runtime.spec.completion_gate is a worker-writable flag, not an authorization; no completion-gate bypass is supported. Remove the flag and satisfy the gate.');
171
202
  process.exit(0);
172
203
  }
173
- const taskRegistry = activeSpec.task_registry || {};
204
+ const taskRegistry = resolved.taskRegistry || activeSpec.task_registry || {};
174
205
  const cacheFile = path.join(__dirname, '.logs', 'spec-gate-last.json');
175
206
  const cacheExists = fs.existsSync(cacheFile);
176
207
  let cache = {};
@@ -183,7 +214,7 @@ try {
183
214
  currentStatuses[tp] = task?.status || 'pending';
184
215
  }
185
216
 
186
- const staleFlashTasks = Object.entries(taskRegistry)
217
+ const staleFlashTasks = processWorkflow ? [] : Object.entries(taskRegistry)
187
218
  .filter(([, task]) => POLICY.isStaleFlashDone(task))
188
219
  .map(([taskPath]) => taskPath);
189
220
  if (staleFlashTasks.length > 0) {
@@ -206,11 +237,13 @@ try {
206
237
  const failures = allDoneTasks
207
238
  .map((taskPath) => ({
208
239
  taskPath,
209
- fails: RECEIPT.checkTaskReceipt(featureDir, taskPath, taskRegistry[taskPath], runtimeContext, POLICY).failures,
240
+ fails: processWorkflow
241
+ ? RECEIPT.checkWorkflowTaskReceipt(featureDir, taskPath, runtimeContext, POLICY).failures
242
+ : RECEIPT.checkTaskReceipt(featureDir, taskPath, taskRegistry[taskPath], runtimeContext, POLICY).failures,
210
243
  }))
211
244
  .filter((f) => f.fails.length > 0);
212
245
 
213
- const featureCloseoutRequired = explicitCloseout && allDoneTasks.length > 0;
246
+ const featureCloseoutRequired = !processWorkflow && explicitCloseout && allDoneTasks.length > 0;
214
247
  const featureReceipt = featureCloseoutRequired
215
248
  ? RECEIPT.checkFeatureReceipt(featureDir, runtimeContext, POLICY)
216
249
  : null;
@@ -253,7 +286,9 @@ try {
253
286
  lines.push(`- \`${taskPath}\`: failed check(s) ${fails.join(', ')}`);
254
287
  lines.push(taskPath === 'feature-receipt.md'
255
288
  ? ` Fix: run final integration proof, then write \`specs/${featureName}/feature-receipt.md\`.`
256
- : ` Fix: write canonical proof to \`specs/${featureName}/receipts/${path.posix.basename(taskPath)}\`; legacy \`## Evidence\` remains read-compatible.`);
289
+ : processWorkflow
290
+ ? ` Fix: write a runtime-bound \`## Receipt\` with command output in \`specs/${featureName}/${path.posix.basename(taskPath)}\`.`
291
+ : ` Fix: write canonical proof to \`specs/${featureName}/receipts/${path.posix.basename(taskPath)}\`; legacy \`## Evidence\` remains read-compatible.`);
257
292
  }
258
293
  if (completionBlocked) {
259
294
  lines.push(`- Completion decision unfinished: ${completion.blocker || 'required workflow proof is missing.'}`);