@haposoft/cafekit 0.16.0-rc.3 → 0.16.0-rc.5

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 (98) hide show
  1. package/bin/__tests__/change-firewall.semantic-firewall.test.js +2134 -0
  2. package/bin/__tests__/codex-hooks.test.js +531 -29
  3. package/bin/__tests__/codex-native.test.js +228 -7
  4. package/bin/__tests__/develop-contract.test.js +653 -185
  5. package/bin/__tests__/finalizer-pass-gate.semantic-firewall.test.js +342 -0
  6. package/bin/__tests__/installer-safety.test.js +39 -0
  7. package/bin/__tests__/opencode-spec-gate.test.js +24 -16
  8. package/bin/__tests__/package-inventory.test.js +705 -13
  9. package/bin/__tests__/release-preflight.semantic-firewall.test.js +578 -0
  10. package/bin/__tests__/review-receipt-c2.semantic-firewall.test.js +378 -0
  11. package/bin/__tests__/specs-v2-execution-closeout.test.js +468 -0
  12. package/bin/__tests__/specs-v2-policy-and-scaffold.test.js +665 -0
  13. package/bin/__tests__/specs-v2-validator-grounder-contract.test.js +1132 -0
  14. package/bin/__tests__/test-runtime-dependency-closure.cjs +182 -0
  15. package/bin/__tests__/validator-grounding.test.js +2013 -221
  16. package/bin/lib/managed-writer.js +12 -3
  17. package/bin/phases/codex-runtime.js +17 -1
  18. package/package.json +5 -2
  19. package/src/claude/CLAUDE.md +6 -0
  20. package/src/claude/agents/code-auditor.md +20 -0
  21. package/src/claude/agents/spec-maker.md +143 -99
  22. package/src/claude/hooks/__tests__/completion-authority.test.js +321 -58
  23. package/src/claude/hooks/__tests__/privacy-block.test.js +254 -0
  24. package/src/claude/hooks/__tests__/semantic-review-authority.test.js +257 -0
  25. package/src/claude/hooks/__tests__/spec-gate.test.js +115 -40
  26. package/src/claude/hooks/completion-authority-check.cjs +37 -288
  27. package/src/claude/hooks/completion-authority-state.cjs +10 -8
  28. package/src/claude/hooks/completion-authority.cjs +13 -7
  29. package/src/claude/hooks/docs-sync.cjs +5 -2
  30. package/src/claude/hooks/lib/privacy-command-analysis.cjs +393 -0
  31. package/src/claude/hooks/lib/runtime-path-safety.cjs +119 -0
  32. package/src/claude/hooks/privacy-block.cjs +99 -28
  33. package/src/claude/hooks/semantic-review-authority.cjs +152 -0
  34. package/src/claude/hooks/spec-gate.cjs +47 -117
  35. package/src/claude/hooks/spec-state.cjs +8 -2
  36. package/src/claude/migration-manifest.json +9 -1
  37. package/src/claude/rules/state-sync.md +52 -21
  38. package/src/claude/rules/workflow.md +17 -9
  39. package/src/claude/scripts/change-firewall.cjs +1748 -0
  40. package/src/claude/scripts/spec-authoring-digest.cjs +151 -0
  41. package/src/claude/scripts/spec-authoring-validation.cjs +126 -0
  42. package/src/claude/scripts/spec-final-state.cjs +290 -0
  43. package/src/claude/scripts/spec-ground.cjs +571 -173
  44. package/src/claude/scripts/spec-readiness.cjs +425 -0
  45. package/src/claude/scripts/spec-receipt.cjs +162 -0
  46. package/src/claude/scripts/spec-resolver.cjs +67 -16
  47. package/src/claude/scripts/spec-scaffold.cjs +1594 -139
  48. package/src/claude/scripts/spec-semantic-model.cjs +396 -0
  49. package/src/claude/scripts/validate-spec-output.cjs +3110 -218
  50. package/src/claude/scripts/workflow-policy.cjs +501 -183
  51. package/src/claude/settings/settings.json +4 -0
  52. package/src/claude/skills/code-review/SKILL.md +12 -3
  53. package/src/claude/skills/code-review/references/spec-compliance-review.md +10 -2
  54. package/src/claude/skills/code-review/references/verification-gate.md +13 -0
  55. package/src/claude/skills/develop/SKILL.md +47 -60
  56. package/src/claude/skills/develop/references/parallel-waves.md +9 -6
  57. package/src/claude/skills/develop/references/quality-gate.md +19 -12
  58. package/src/claude/skills/specs/SKILL.md +183 -177
  59. package/src/claude/skills/specs/references/ask-user-question-gates.md +27 -18
  60. package/src/claude/skills/specs/references/cross-spec-dependency.md +8 -4
  61. package/src/claude/skills/specs/references/research-strategy.md +4 -1
  62. package/src/claude/skills/specs/references/review.md +214 -262
  63. package/src/claude/skills/specs/references/scope-inquiry.md +18 -5
  64. package/src/claude/skills/specs/rules/design-discovery-full.md +33 -81
  65. package/src/claude/skills/specs/rules/design-discovery-light.md +21 -42
  66. package/src/claude/skills/specs/rules/design-principles.md +136 -180
  67. package/src/claude/skills/specs/rules/design-review.md +42 -97
  68. package/src/claude/skills/specs/rules/ears-format.md +42 -50
  69. package/src/claude/skills/specs/rules/phase-decision-matrix.md +42 -41
  70. package/src/claude/skills/specs/rules/task-scoring-rubric.md +2 -1
  71. package/src/claude/skills/specs/rules/tasks-generation.md +189 -300
  72. package/src/claude/skills/specs/templates/design.md +49 -263
  73. package/src/claude/skills/specs/templates/requirements.md +26 -36
  74. package/src/claude/skills/specs/templates/research.md +9 -98
  75. package/src/claude/skills/specs/templates/spec-state.json +35 -85
  76. package/src/claude/skills/specs/templates/task.md +30 -102
  77. package/src/claude/skills/sync/SKILL.md +33 -10
  78. package/src/claude/skills/sync/references/sync-protocols.md +31 -13
  79. package/src/claude/skills/test/SKILL.md +17 -7
  80. package/src/codex/AGENTS.md +6 -0
  81. package/src/codex/hooks/completion-authority-check.cjs +43 -240
  82. package/src/codex/hooks/completion-authority-state.cjs +10 -8
  83. package/src/codex/hooks/completion-authority.cjs +13 -7
  84. package/src/codex/hooks/lib/hook-context.cjs +16 -2
  85. package/src/codex/hooks/lib/privacy-command-analysis.cjs +3 -0
  86. package/src/codex/hooks/lib/privacy-state.cjs +61 -38
  87. package/src/codex/hooks/lib/runtime-path-safety.cjs +3 -0
  88. package/src/codex/hooks/lib/spec-receipt.cjs +72 -134
  89. package/src/codex/hooks/lib/spec-utils.cjs +56 -5
  90. package/src/codex/hooks/privacy-approval.cjs +6 -2
  91. package/src/codex/hooks/privacy-block.cjs +94 -15
  92. package/src/codex/hooks/semantic-review-authority.cjs +130 -0
  93. package/src/codex/hooks/spec-gate.cjs +49 -34
  94. package/src/codex/hooks/spec-state.cjs +10 -3
  95. package/src/codex/hooks/state.cjs +2 -0
  96. package/src/codex/hooks.json +6 -1
  97. package/src/codex/rules/state-sync.md +47 -27
  98. package/src/common/AGENTS.md +1 -1
@@ -9,8 +9,13 @@ const {
9
9
  logCrash,
10
10
  readPayload
11
11
  } = require('./lib/hook-context.cjs');
12
- const { checkReceipt, evidenceBody, loadSharedPolicy } = require('./lib/spec-receipt.cjs');
12
+ const {
13
+ checkFeatureReceipt,
14
+ checkReceiptDetails,
15
+ loadSharedPolicy,
16
+ } = require('./lib/spec-receipt.cjs');
13
17
  const { taskStatusMap } = require('./lib/spec-utils.cjs');
18
+ const FINAL_STATE = require('./completion-authority-check.cjs');
14
19
 
15
20
  function emitBlock(reason) {
16
21
  process.stdout.write(`${JSON.stringify({ decision: 'block', reason })}\n`);
@@ -26,22 +31,6 @@ function sessionIdentity(payload) {
26
31
  return candidates.find((value) => typeof value === 'string' && value.trim() !== '') || null;
27
32
  }
28
33
 
29
- function readReceiptBody(featureDir, taskPath) {
30
- const featureRoot = path.resolve(featureDir);
31
- const target = path.resolve(featureRoot, taskPath);
32
- const relative = path.relative(featureRoot, target);
33
- if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) return null;
34
- try {
35
- const canonicalFeature = fs.realpathSync(featureRoot);
36
- const canonicalTarget = fs.realpathSync(target);
37
- const canonicalRelative = path.relative(canonicalFeature, canonicalTarget);
38
- if (canonicalRelative === '..' || canonicalRelative.startsWith(`..${path.sep}`) || path.isAbsolute(canonicalRelative)) return null;
39
- return evidenceBody(fs.readFileSync(canonicalTarget, 'utf8'));
40
- } catch (_) {
41
- return null;
42
- }
43
- }
44
-
45
34
  try {
46
35
  const payload = readPayload();
47
36
  if (payload.stop_hook_active === true) process.exit(0);
@@ -56,12 +45,15 @@ try {
56
45
  if (typeof POLICY.completionDecisionForSpec !== 'function') {
57
46
  throw new Error('shared workflow policy lacks completion authority functions');
58
47
  }
59
- const explicitFeature = payload.featureName || payload.feature || payload.explicitFeature || null;
60
- const explicitPath = payload.specPath || payload.spec_path || payload.featurePath || null;
61
- const { resolveActiveSpec } = require('./lib/spec-utils.cjs');
62
- const resolved = resolveActiveSpec(projectRoot, runtime, explicitFeature, explicitPath);
48
+ const resolver = require('./lib/spec-utils.cjs');
49
+ const resolved = FINAL_STATE.resolveCandidate({
50
+ resolver,
51
+ projectRoot,
52
+ runtime,
53
+ payload,
54
+ });
63
55
  if (!resolved) process.exit(0);
64
- if (resolved.error === 'multiple_active') {
56
+ if (resolved.error === 'multiple_active' || resolved.error === 'multiple_persisted') {
65
57
  process.stdout.write(`${JSON.stringify({
66
58
  decision: 'block',
67
59
  reason: `Completion gate: multiple active specs detected (${resolved.candidates.join(', ')}). Provide explicit feature target or resolve ambiguity before completing tasks.`
@@ -84,6 +76,22 @@ try {
84
76
  }
85
77
  if (resolved.error) process.exit(0);
86
78
  const active = resolved;
79
+ const lifecyclePhase = active.spec.current_phase || active.spec.phase;
80
+ const explicitCloseout = ['done', 'completed', 'complete'].includes(active.spec.status)
81
+ || ['closeout', 'completion', 'completed', 'complete'].includes(lifecyclePhase);
82
+ if (active.spec.schema_version === '2.1') {
83
+ const finalState = FINAL_STATE.evaluateCloseout({
84
+ policy: POLICY,
85
+ projectRoot,
86
+ runtime,
87
+ payload: { ...payload, session_id: sessionIdentity(payload) },
88
+ });
89
+ if (!finalState.ok) {
90
+ emitBlock(`Completion gate: ${finalState.reason}`);
91
+ process.exit(0);
92
+ }
93
+ if (finalState.active) process.exit(0);
94
+ }
87
95
  const runtimeContext = POLICY.deriveRuntimeContext({
88
96
  projectRoot,
89
97
  specsRoot: active.specsDir,
@@ -146,21 +154,28 @@ try {
146
154
  const receiptBodies = new Map();
147
155
  const failures = allDoneTasks
148
156
  .map((taskPath) => {
149
- const body = readReceiptBody(featureDir, taskPath);
150
- if (body !== null) receiptBodies.set(taskPath, body);
157
+ const proof = checkReceiptDetails(featureDir, taskPath, registry[taskPath], runtimeContext);
158
+ if (proof.body) receiptBodies.set(taskPath, proof.body);
151
159
  return {
152
160
  taskPath,
153
- failures: checkReceipt(featureDir, taskPath, registry[taskPath], runtimeContext)
161
+ failures: proof.failures
154
162
  };
155
163
  })
156
164
  .filter((result) => result.failures.length);
157
165
 
158
- const firstReceiptTask = allDoneTasks.find((taskPath) => receiptBodies.has(taskPath));
159
- const completion = allDoneTasks.length > 0 && Object.prototype.hasOwnProperty.call(active.spec, 'workflow_policy')
166
+ const featureCloseoutRequired = explicitCloseout && allDoneTasks.length > 0;
167
+ const featureReceipt = featureCloseoutRequired
168
+ ? checkFeatureReceipt(featureDir, runtimeContext)
169
+ : null;
170
+ if (featureReceipt && featureReceipt.failures.length) {
171
+ failures.push({ taskPath: 'feature-receipt.md', failures: featureReceipt.failures });
172
+ }
173
+ const completion = featureCloseoutRequired && featureReceipt?.failures.length === 0
174
+ && Object.prototype.hasOwnProperty.call(active.spec, 'workflow_policy')
160
175
  ? POLICY.completionDecisionForSpec(active.spec, {
161
176
  runtimeContext,
162
- executionReceipt: firstReceiptTask ? receiptBodies.get(firstReceiptTask) : null,
163
- taskContext: firstReceiptTask ? registry[firstReceiptTask] : null,
177
+ executionReceipt: featureReceipt.body,
178
+ taskContext: {},
164
179
  })
165
180
  : null;
166
181
 
@@ -178,14 +193,14 @@ try {
178
193
  if (!failures.length && !completionBlocked) process.exit(0);
179
194
  const lines = [
180
195
  failures.length > 0
181
- ? `Completion gate: ${failures.length} newly-done task(s) lack a verification receipt.`
196
+ ? `Completion gate: ${failures.length} done task(s) lack a verification receipt.`
182
197
  : 'Completion gate: workflow completion proof is incomplete.'
183
198
  ];
184
199
  for (const result of failures) {
185
- lines.push(
186
- `- \`${result.taskPath}\`: failed check(s) ${result.failures.join(', ')}`,
187
- ` Add \`Verification: PASS\`, commands, and successful outcomes under \`## Evidence\` in \`specs/${active.featureName}/${result.taskPath}\`, then re-sync spec.json.`
188
- );
200
+ lines.push(`- \`${result.taskPath}\`: failed check(s) ${result.failures.join(', ')}`);
201
+ lines.push(result.taskPath === 'feature-receipt.md'
202
+ ? ` Run final integration proof, then write \`specs/${active.featureName}/feature-receipt.md\`.`
203
+ : ` Write canonical proof to \`specs/${active.featureName}/receipts/${path.posix.basename(result.taskPath)}\`; legacy \`## Evidence\` remains read-compatible.`);
189
204
  }
190
205
  if (completionBlocked) {
191
206
  lines.push(`- Completion decision unfinished: ${completion.blocker || 'required workflow proof is missing.'}`);
@@ -10,7 +10,7 @@ const {
10
10
  logCrash,
11
11
  readPayload
12
12
  } = require('./lib/hook-context.cjs');
13
- const { loadSharedPolicy } = require('./lib/spec-receipt.cjs');
13
+ const { loadSharedPolicy, loadSharedReceipt } = require('./lib/spec-receipt.cjs');
14
14
 
15
15
  function emitControlledFailure(reason) {
16
16
  process.stdout.write(`> ⚠️ Spec tollgate unavailable: ${reason}. Repair the installed workflow policy before continuing.\n`);
@@ -34,6 +34,9 @@ try {
34
34
  process.exit(0);
35
35
  }
36
36
  const POLICY = loaded.policy;
37
+ const receiptLoaded = loadSharedReceipt();
38
+ if (!receiptLoaded.receipt) throw receiptLoaded.error;
39
+ const RECEIPT = receiptLoaded.receipt;
37
40
  const { projectRoot, runtime } = getHookContext(payload);
38
41
  if (runtime.spec?.tollgate === false) process.exit(0);
39
42
  const { resolveActiveSpec } = require('./lib/spec-utils.cjs');
@@ -81,6 +84,8 @@ try {
81
84
  (task?.status || 'pending') === 'pending'
82
85
  && (task?.dependencies || []).every((dependency) => statuses.get(dependency) === 'done')
83
86
  ));
87
+ const featureDir = path.join(active.specsDir, active.featureName);
88
+ const featureReceiptPresent = RECEIPT.safeRead(featureDir, 'feature-receipt.md').status === 'ok';
84
89
  const stateKey = JSON.stringify({
85
90
  project_root: runtimeContext.project_root,
86
91
  specs_root: runtimeContext.specs_root,
@@ -94,6 +99,7 @@ try {
94
99
  phase,
95
100
  done: counts.done || 0,
96
101
  total: tasks.length,
102
+ feature_receipt_present: featureReceiptPresent,
97
103
  });
98
104
  const cache = cacheFile(projectRoot, runtimeContext.runtime_session);
99
105
  let previous = '';
@@ -117,9 +123,10 @@ try {
117
123
  lines.push(`- Flash verification pending: ${flashTasks.map((taskPath) => `\`${taskPath}\``).join(', ')}. A PASS proof keeps the persisted task in_progress until explicit sync-finalize.`);
118
124
  }
119
125
  lines.push(
120
- '- Sync `spec.json` and the task file only after verified work.',
126
+ '- Sync `spec.json` and task Markdown status only after verified work; task proof belongs in `receipts/<task-basename>.md`.',
127
+ `- Create \`feature-receipt.md\` once after final integration proof${featureReceiptPresent ? ' (present)' : ' (not required before closeout)'}.`,
121
128
  `- Validate with \`node .codex/scripts/validate-spec-output.cjs specs/${active.featureName}\`.`,
122
- '- The Stop completion gate checks receipts for newly-done tasks.'
129
+ '- Hooks revalidate receipt bytes but never grant approval.'
123
130
  );
124
131
  process.stdout.write(`${lines.join('\n')}\n`);
125
132
  } catch (error) {
@@ -140,6 +140,7 @@ try {
140
140
  refreshGit(data, projectRoot);
141
141
  writeData(dir, data);
142
142
  });
143
+ process.stdout.write('{}\n');
143
144
  process.exit(0);
144
145
  }
145
146
 
@@ -151,6 +152,7 @@ try {
151
152
  writeData(dir, data);
152
153
  archive(dir);
153
154
  });
155
+ process.stdout.write('{}\n');
154
156
  }
155
157
  } catch (error) {
156
158
  logCrash('state', error);
@@ -63,7 +63,7 @@
63
63
  ],
64
64
  "PreToolUse": [
65
65
  {
66
- "matcher": "Bash|apply_patch|Edit|Write|Read|Glob|Grep|mcp__.*",
66
+ "matcher": "Bash|exec_command|apply_patch|Edit|Write|Read|Glob|Grep|mcp__.*|shell",
67
67
  "hooks": [
68
68
  {
69
69
  "type": "command",
@@ -120,6 +120,11 @@
120
120
  {
121
121
  "matcher": "*",
122
122
  "hooks": [
123
+ {
124
+ "type": "command",
125
+ "command": "node \"$(git rev-parse --show-toplevel)/.codex/hooks/semantic-review-authority.cjs\"",
126
+ "commandWindows": "node \".codex/hooks/semantic-review-authority.cjs\""
127
+ },
123
128
  {
124
129
  "type": "command",
125
130
  "command": "node \"$(git rev-parse --show-toplevel)/.codex/hooks/state.cjs\"",
@@ -1,41 +1,61 @@
1
- # Tollgate Protocol (State Sync)
1
+ # Specs v2.1 state synchronization
2
2
 
3
- ## Sources Of Truth
3
+ ## Authority layers
4
4
 
5
- Spec-driven work persists state in two synchronized layers:
5
+ `spec.json` is machine semantic authority. Requirements, design, conditional
6
+ research, and conditional task Markdown are human projections. Host state may
7
+ record authority or integrity, but it never supplies product semantics.
6
8
 
7
- 1. `spec.json` tracks phase, overall status, and `task_registry`.
8
- 2. `tasks/task-*.md` tracks the human-readable checklist and verification receipt.
9
+ Planning depth and assurance level remain independent. Route artifacts before
10
+ discovery: None persists no spec; Compact and Full persist the bounded core;
11
+ research and tasks remain absent unless their semantic triggers exist.
9
12
 
10
- ## Sync-back Rule
13
+ ## Canonical lifecycle
11
14
 
12
- Before reporting a task completed or blocked:
15
+ The feature lifecycle is exactly `in_progress`, `paused`, `blocked`, or `done`.
16
+ Authoring state is independently `draft`, `validated`, or `absent`. New 2.1
17
+ artifacts never emit legacy lifecycle aliases.
13
18
 
14
- ### On success
19
+ Technical readiness is not lifecycle completion. `ready_for_implementation`
20
+ may change only through the installed machine finalization path after every
21
+ routed artifact, projection, semantic review, grounding check, and deterministic
22
+ gate passes. The author cannot directly declare readiness, authority, approval,
23
+ review capability, execution proof, or closeout.
15
24
 
16
- 1. Update `spec.json` only after verification. Keep `current_phase`,
17
- `task_files`, status, timestamps, and the matching `task_registry` entry accurate.
18
- 2. Update the matching task Markdown status, checkboxes, completion criteria,
19
- and `## Evidence` receipt from the current verification run.
20
- 3. Update native plan or task state when the active Codex session provides it,
21
- but never use chat-only state as a substitute for the files above.
25
+ ## Projection sync
22
26
 
23
- ### On block after repeated attempts
27
+ After a semantic Markdown edit:
24
28
 
25
- 1. Set the spec and matching registry entry to `blocked`, with the root cause and timestamp.
26
- 2. Mark the task Markdown `blocked` and record the evidence.
27
- 3. Tell the user or orchestrator exactly what external input or state change is required.
29
+ 1. invoke the explicit installed machine semantic-sync step to promote `semantic_model`;
30
+ 2. regenerate or reconcile every affected Markdown projection;
31
+ 3. verify requirements, decisions, anchors, task registry, typed boundaries,
32
+ and verification refs round-trip without loss; and
33
+ 4. rerun applicable deterministic validation and grounding checks.
28
34
 
29
- New specs use `in_progress`; legacy `in-progress` may be read but not emitted.
30
- Never let `task_registry` disagree with its task Markdown file.
35
+ For Codex, the isolated promotion is
36
+ `node .codex/scripts/spec-scaffold.cjs <feature> --sync-semantic-model`.
31
37
 
32
- ## Completion Gate
38
+ Task Markdown exists only for typed ownership, dependency, transition, proof,
39
+ or parallel topology. Its status and dependencies project the matching
40
+ `task_registry` entry. Its Verification Plan is prospective, never execution
41
+ proof. Typed `coordination.boundaries` alone define topology; legacy trigger
42
+ fields, priority markers, related-file lists, and prose do not.
33
43
 
34
- The Stop hook checks newly completed tasks. Each requires:
44
+ ## Lifecycle updates
35
45
 
36
- - `Status: done`;
37
- - a non-placeholder `## Evidence` section with a command block or PASS/FAIL result;
38
- - a non-empty `task_registry[path].completed_at`.
46
+ - Active work uses `in_progress`.
47
+ - A deliberate resumable stop uses `paused` with the exact remaining decision.
48
+ - An external or semantic blocker uses `blocked` with a concrete cause.
49
+ - Task state changes update the registry and matching projection together only
50
+ from observed work and verification.
51
+ - Feature `done` is reserved for final-state authority after implementation
52
+ closeout; authoring or readiness cannot produce it.
39
53
 
40
- The first run seeds completion history without blocking. Humans can disable the
41
- gate with `"spec": { "completion_gate": false }` in `.codex/runtime.json`.
54
+ State synchronization never fabricates commands, PASS results, timestamps,
55
+ receipts, completed work, reviewer identity, or user approval. When execution
56
+ evidence is absent, preserve unfinished state and report the missing proof.
57
+ Never hand-author the `semantic_model` shape or treat an unsynchronized Markdown
58
+ edit as machine authority.
59
+
60
+ Codex-native plan or task state may mirror physical state when available, but
61
+ it is never a substitute for `spec.json` or authority to advance lifecycle.
@@ -3,7 +3,7 @@
3
3
  ## Shared CafeKit instructions
4
4
 
5
5
  - Deliver exactly what was asked. Do not expand, polish, or add optional work beyond the request. Match existing code style and structure.
6
- - For spec work, `Completion Criteria` and `## Evidence` in `specs/<feature>/tasks/*.md` are the source of truth for task state.
6
+ - For Specs v2.1, treat `spec.json` as machine authority and Markdown as human projections. Keep task status and plan synchronized with `task_registry`; derive ownership, dependencies, transitions, proof, and parallelism only from typed `coordination.boundaries`. Never invent proof, readiness, approval, or audit state.
7
7
  - `NO_TESTS` and `0 tests + exit 0` do not pass when the task requires automated tests.
8
8
  - When a hook blocks an action, that is an instruction boundary — do not work around it.
9
9
  - Use conventional commits. Do not add AI attribution unless requested.