@haposoft/cafekit 0.16.7 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -22
- package/bin/__tests__/codex-hooks-ownership.test.js +2 -2
- package/bin/__tests__/codex-hooks.test.js +169 -272
- package/bin/__tests__/codex-native.test.js +100 -202
- package/bin/__tests__/develop-contract.test.js +122 -1413
- package/bin/__tests__/docs-sync-on-demand.test.js +93 -0
- package/bin/__tests__/grok-envelope.test.js +0 -56
- package/bin/__tests__/hook-payload.test.js +4 -6
- package/bin/__tests__/legacy-authority-decoupled.test.js +154 -0
- package/bin/__tests__/legacy-hooks-retired.test.js +221 -0
- package/bin/__tests__/omp-bridge.test.js +20 -17
- package/bin/__tests__/omp-hooks.test.js +0 -19
- package/bin/__tests__/optional-skill-inventory.test.js +112 -4
- package/bin/__tests__/orca-session.test.js +0 -2
- package/bin/__tests__/package-inventory.test.js +90 -925
- package/bin/__tests__/runtime-dir.test.js +0 -3
- package/bin/__tests__/runtime-schema.test.js +3 -2
- package/bin/__tests__/skill-routing-source.test.js +27 -15
- package/bin/__tests__/spec-narrowing.test.js +30 -161
- package/bin/__tests__/specs-v2-execution-closeout.test.js +0 -437
- package/bin/__tests__/test-runtime-dependency-closure.cjs +1 -2
- package/bin/__tests__/ui-ux-skill.test.js +101 -0
- package/bin/__tests__/usage-hook-retired.test.js +114 -0
- package/bin/__tests__/validator-grounding.test.js +18 -2949
- package/bin/lib/managed-writer.js +2 -3
- package/bin/phases/codex-runtime.js +27 -0
- package/bin/phases/copy-payload.js +13 -28
- package/bin/phases/omp-runtime.js +25 -0
- package/package.json +2 -3
- package/src/claude/CLAUDE.md +3 -10
- package/src/claude/agents/brainstormer.md +7 -3
- package/src/claude/agents/code-auditor.md +44 -56
- package/src/claude/agents/debugger.md +8 -88
- package/src/claude/agents/deployer.md +3 -5
- package/src/claude/agents/docs-keeper.md +6 -14
- package/src/claude/agents/git-ops.md +2 -4
- package/src/claude/agents/implementer.md +12 -16
- package/src/claude/agents/inspector.md +4 -3
- package/src/claude/agents/project-manager.md +9 -17
- package/src/claude/agents/researcher.md +9 -3
- package/src/claude/agents/spec-maker.md +15 -15
- package/src/claude/agents/test-runner.md +8 -11
- package/src/claude/agents/ui-ux-designer.md +0 -1
- package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -1
- package/src/claude/hooks/__tests__/spec-gate.test.js +244 -333
- package/src/claude/hooks/__tests__/spec-state-touched.test.js +160 -0
- package/src/claude/hooks/__tests__/state.test.js +68 -0
- package/src/claude/hooks/__tests__/statusline.test.js +140 -11
- package/src/claude/hooks/agent.cjs +2 -1
- package/src/claude/hooks/lib/hook-payload.cjs +2 -2
- package/src/claude/hooks/rules.cjs +2 -1
- package/src/claude/hooks/session.cjs +0 -1
- package/src/claude/hooks/spec-gate.cjs +17 -83
- package/src/claude/hooks/spec-state.cjs +142 -39
- package/src/claude/hooks/state.cjs +21 -2
- package/src/claude/migration-manifest.json +42 -22
- package/src/claude/output-styles/coding-level-0-eli5.md +9 -9
- package/src/claude/output-styles/coding-level-1-junior.md +3 -3
- package/src/claude/output-styles/coding-level-2-mid.md +3 -3
- package/src/claude/output-styles/coding-level-3-senior.md +2 -2
- package/src/claude/output-styles/coding-level-4-lead.md +5 -5
- package/src/claude/output-styles/coding-level-5-god.md +6 -6
- package/src/claude/rules/ai-dev-rules.md +2 -3
- package/src/claude/rules/hook-protocols.md +8 -15
- package/src/claude/rules/manage-docs.md +9 -23
- package/src/claude/rules/orchestrator.md +3 -4
- package/src/claude/rules/review-audit-self-decision.md +3 -3
- package/src/claude/rules/skill-domain-routing.md +1 -3
- package/src/claude/rules/skill-workflow-routing.md +4 -5
- package/src/claude/rules/state-sync.md +3 -12
- package/src/claude/rules/workflow.md +11 -19
- package/src/claude/runtime.schema.json +5 -3
- package/src/claude/scripts/generate-skill-catalog.cjs +7 -6
- package/src/claude/scripts/provenance.cjs +23 -5
- package/src/claude/scripts/spec-receipt.cjs +24 -121
- package/src/claude/scripts/spec-resolver.cjs +41 -458
- package/src/claude/scripts/workflow-policy.cjs +3 -1801
- package/src/claude/settings/settings.json +1 -32
- package/src/claude/skills/agent-browser/SKILL.md +1 -1
- package/src/claude/skills/{question → ask}/SKILL.md +9 -75
- package/src/claude/skills/brainstorm/SKILL.md +43 -131
- package/src/claude/skills/chrome-devtools/SKILL.md +14 -31
- package/src/claude/skills/code-review/SKILL.md +29 -81
- package/src/claude/skills/code-review/references/spec-compliance-review.md +4 -8
- package/src/claude/skills/debug/SKILL.md +29 -149
- package/src/claude/skills/develop/SKILL.md +26 -83
- package/src/claude/skills/develop/references/parallel-waves.md +0 -8
- package/src/claude/skills/develop/references/quality-gate.md +15 -13
- package/src/claude/skills/develop/references/subagent-patterns.md +0 -6
- package/src/claude/skills/docs/SKILL.md +2 -2
- package/src/claude/skills/docs/references/update-workflow.md +1 -1
- package/src/claude/skills/fix/SKILL.md +153 -0
- package/src/claude/skills/{hotfix → fix}/references/diagnosis-protocol.md +2 -2
- package/src/claude/skills/{hotfix → fix}/references/parallel-patterns.md +2 -2
- package/src/claude/skills/{hotfix → fix}/references/prevention-gate.md +3 -3
- package/src/claude/skills/{hotfix → fix}/references/review-cycle.md +1 -1
- package/src/claude/skills/git/SKILL.md +48 -9
- package/src/claude/skills/git/references/commit-protocols.md +35 -16
- package/src/claude/skills/git/references/worktree-blueprint.md +42 -6
- package/src/claude/skills/orca/SKILL.md +9 -8
- package/src/claude/skills/research/SKILL.md +17 -46
- package/src/claude/skills/scout/SKILL.md +61 -0
- package/src/claude/skills/{inspect → scout}/references/internal-inspection.md +26 -10
- package/src/claude/skills/specs/SKILL.md +22 -29
- package/src/claude/skills/specs/references/review.md +8 -6
- package/src/claude/skills/specs/references/templates.md +34 -41
- package/src/claude/skills/sync/SKILL.md +16 -45
- package/src/claude/skills/sync/references/rebind-and-audit.md +59 -0
- package/src/claude/skills/sync/references/sync-protocols.md +4 -13
- package/src/claude/skills/test/SKILL.md +39 -104
- package/src/claude/skills/test/references/execution-strategy.md +93 -21
- package/src/claude/skills/test/references/failure-triage.md +1 -1
- package/src/claude/skills/ui-ux/LICENSE +21 -0
- package/src/claude/skills/ui-ux/SKILL.md +497 -0
- package/src/claude/skills/ui-ux/references/brand-tokens.md +90 -0
- package/src/claude/skills/ui-ux/references/budgets.md +113 -0
- package/src/claude/skills/ui-ux/references/checklist.md +271 -0
- package/src/claude/skills/ui-ux/references/components/accordion.md +246 -0
- package/src/claude/skills/ui-ux/references/components/avatar.md +153 -0
- package/src/claude/skills/ui-ux/references/components/banner.md +43 -0
- package/src/claude/skills/ui-ux/references/components/breadcrumb.md +201 -0
- package/src/claude/skills/ui-ux/references/components/button.md +142 -0
- package/src/claude/skills/ui-ux/references/components/card.md +113 -0
- package/src/claude/skills/ui-ux/references/components/charts.md +376 -0
- package/src/claude/skills/ui-ux/references/components/chat.md +132 -0
- package/src/claude/skills/ui-ux/references/components/choice-controls.md +369 -0
- package/src/claude/skills/ui-ux/references/components/comment-thread.md +17 -0
- package/src/claude/skills/ui-ux/references/components/description-list.md +116 -0
- package/src/claude/skills/ui-ux/references/components/empty-state.md +98 -0
- package/src/claude/skills/ui-ux/references/components/file-upload.md +107 -0
- package/src/claude/skills/ui-ux/references/components/inline-edit.md +128 -0
- package/src/claude/skills/ui-ux/references/components/input.md +161 -0
- package/src/claude/skills/ui-ux/references/components/list-row.md +56 -0
- package/src/claude/skills/ui-ux/references/components/loading.md +198 -0
- package/src/claude/skills/ui-ux/references/components/logo.md +147 -0
- package/src/claude/skills/ui-ux/references/components/otp-input.md +43 -0
- package/src/claude/skills/ui-ux/references/components/quantity-input.md +100 -0
- package/src/claude/skills/ui-ux/references/components/range-slider.md +16 -0
- package/src/claude/skills/ui-ux/references/components/small-controls.md +340 -0
- package/src/claude/skills/ui-ux/references/components/sortable-header.md +86 -0
- package/src/claude/skills/ui-ux/references/components/tag-input.md +14 -0
- package/src/claude/skills/ui-ux/references/components/timeline.md +38 -0
- package/src/claude/skills/ui-ux/references/components/tree.md +30 -0
- package/src/claude/skills/ui-ux/references/design-process.md +650 -0
- package/src/claude/skills/ui-ux/references/layouts/app-kanban.html +303 -0
- package/src/claude/skills/ui-ux/references/layouts/app.md +1369 -0
- package/src/claude/skills/ui-ux/references/layouts/form.md +430 -0
- package/src/claude/skills/ui-ux/references/layouts/overlay.md +552 -0
- package/src/claude/skills/ui-ux/references/layouts/pricing.md +204 -0
- package/src/claude/skills/ui-ux/references/locked-rules.md +35 -0
- package/src/claude/skills/ui-ux/references/principles.md +408 -0
- package/src/claude/skills/ui-ux/references/refactor.md +245 -0
- package/src/claude/skills/ui-ux/references/responsive.md +195 -0
- package/src/claude/skills/ui-ux/references/review.md +528 -0
- package/src/claude/skills/ui-ux/references/rules-color.md +623 -0
- package/src/claude/skills/ui-ux/references/rules-form.md +235 -0
- package/src/claude/skills/ui-ux/references/rules-state.md +680 -0
- package/src/claude/skills/ui-ux/references/rules-type.md +369 -0
- package/src/claude/skills/ui-ux/references/styles.md +311 -0
- package/src/claude/skills/ui-ux/references/system.md +219 -0
- package/src/claude/skills/ui-ux/references/tailwind-v4-traps.md +237 -0
- package/src/claude/skills/ui-ux/references/tokens.css +362 -0
- package/src/claude/skills/ui-ux/scripts/lint-skill.mjs +216 -0
- package/src/claude/skills/ui-ux/scripts/package.json +9 -0
- package/src/claude/skills/ui-ux/scripts/probe.mjs +4447 -0
- package/src/claude/skills/ui-ux-pro-max/SKILL.md +25 -80
- package/src/claude/skills/web-testing/SKILL.md +2 -20
- package/src/claude/status.cjs +149 -144
- package/src/codex/AGENTS.md +8 -13
- package/src/codex/hooks/agent.cjs +2 -1
- package/src/codex/hooks/lib/spec-receipt.cjs +4 -32
- package/src/codex/hooks/lib/spec-utils.cjs +6 -52
- package/src/codex/hooks/rules.cjs +2 -1
- package/src/codex/hooks/session.cjs +0 -2
- package/src/codex/hooks/spec-gate.cjs +13 -81
- package/src/codex/hooks/spec-state.cjs +37 -41
- package/src/codex/hooks.json +0 -20
- package/src/codex/rules/state-sync.md +3 -12
- package/src/common/AGENTS.md +6 -12
- package/src/omp/extensions/cafekit-bridge.mjs +6 -6
- package/bin/__tests__/change-firewall.semantic-firewall.test.js +0 -2133
- package/bin/__tests__/finalizer-pass-gate.semantic-firewall.test.js +0 -342
- package/bin/__tests__/release-preflight.semantic-firewall.test.js +0 -586
- package/bin/__tests__/review-receipt-c2.semantic-firewall.test.js +0 -378
- package/bin/__tests__/specs-v2-policy-and-scaffold.test.js +0 -665
- package/bin/__tests__/specs-v2-validator-grounder-contract.test.js +0 -1132
- package/src/claude/hooks/__tests__/completion-authority.test.js +0 -990
- package/src/claude/hooks/__tests__/semantic-review-authority.test.js +0 -259
- package/src/claude/hooks/completion-authority-check.cjs +0 -56
- package/src/claude/hooks/completion-authority-state.cjs +0 -527
- package/src/claude/hooks/completion-authority.cjs +0 -163
- package/src/claude/hooks/lib/context.cjs +0 -616
- package/src/claude/hooks/lib/detect.cjs +0 -474
- package/src/claude/hooks/semantic-review-authority.cjs +0 -154
- package/src/claude/hooks/task-scaffold-guard.cjs +0 -114
- package/src/claude/hooks/usage.cjs +0 -197
- package/src/claude/scripts/change-firewall.cjs +0 -1748
- package/src/claude/scripts/spec-authoring-digest.cjs +0 -151
- package/src/claude/scripts/spec-authoring-validation.cjs +0 -126
- package/src/claude/scripts/spec-final-state.cjs +0 -290
- package/src/claude/scripts/spec-ground.cjs +0 -650
- package/src/claude/scripts/spec-readiness.cjs +0 -425
- package/src/claude/scripts/spec-scaffold.cjs +0 -2317
- package/src/claude/scripts/spec-semantic-model.cjs +0 -396
- package/src/claude/scripts/validate-spec-output.cjs +0 -3941
- package/src/claude/skills/code-review/references/verification-gate.md +0 -46
- package/src/claude/skills/delegate/SKILL.md +0 -124
- package/src/claude/skills/delegate/references/codex-delegation.md +0 -71
- package/src/claude/skills/delegate/references/grok-delegation.md +0 -84
- package/src/claude/skills/hotfix/SKILL.md +0 -289
- package/src/claude/skills/inspect/SKILL.md +0 -142
- package/src/claude/skills/loop/SKILL.md +0 -114
- package/src/claude/skills/loop/references/bounded-loop-protocol.md +0 -104
- package/src/claude/skills/loop/references/metric-and-guard-contract.md +0 -76
- package/src/claude/skills/route/SKILL.md +0 -94
- package/src/claude/skills/route/references/agent-timing.md +0 -46
- package/src/claude/skills/route/references/chaining-patterns.md +0 -53
- package/src/claude/skills/route/references/task-taxonomy.md +0 -48
- package/src/claude/skills/specs/templates/design.md +0 -82
- package/src/claude/skills/specs/templates/requirements-init.md +0 -8
- package/src/claude/skills/specs/templates/requirements.md +0 -42
- package/src/claude/skills/specs/templates/spec-state.json +0 -56
- package/src/claude/skills/specs/templates/task.md +0 -47
- package/src/codex/hooks/completion-authority-check.cjs +0 -67
- package/src/codex/hooks/completion-authority-state.cjs +0 -527
- package/src/codex/hooks/completion-authority.cjs +0 -109
- package/src/codex/hooks/semantic-review-authority.cjs +0 -130
- package/src/codex/hooks/task-scaffold-guard.cjs +0 -63
- /package/src/claude/skills/{question → ask}/templates/question.md +0 -0
- /package/src/claude/skills/{hotfix → fix}/references/escalation-tactics.md +0 -0
- /package/src/claude/skills/{hotfix → fix}/references/workflow-specialized.md +0 -0
package/src/codex/AGENTS.md
CHANGED
|
@@ -10,17 +10,19 @@
|
|
|
10
10
|
- For skill selection, read `.codex/rules/skill-workflow-routing.md` and
|
|
11
11
|
`.codex/rules/skill-domain-routing.md`. When capability presence is uncertain,
|
|
12
12
|
run `node .codex/scripts/generate-skill-catalog.cjs --skills`; its Codex-bound
|
|
13
|
-
root is `.agents/skills`.
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
root is `.agents/skills`. Explicit, obvious, and factual intents stay
|
|
14
|
+
direct; compose a short chain only for ambiguous, multi-step, multi-domain, or
|
|
15
|
+
risk-elevated work.
|
|
16
16
|
- Consult `.codex/rules/review-audit-self-decision.md` before applying audit
|
|
17
17
|
feedback, reversing a verified or user decision, or cutting scope, and
|
|
18
18
|
`.codex/rules/process-management.md` whenever a task starts, reuses, or ends
|
|
19
19
|
long-running processes.
|
|
20
|
+
- Read `.codex/rules/state-sync.md` before changing a task `Status:` or Receipt,
|
|
21
|
+
and `.codex/rules/hook-protocols.md` when a hook blocks or asks.
|
|
20
22
|
- Project hooks live in `.codex/hooks.json`; review trusted hooks with `/hooks`.
|
|
21
|
-
- New Specs work uses the process-first flow. `$cf-specs` opens
|
|
23
|
+
- New Specs work uses the process-first flow. `$cf-specs` opens GATE-SCOPE, writes
|
|
22
24
|
`specs/<feature>/plan.md` with flat `task-NN-*.md` files beside it, then opens
|
|
23
|
-
|
|
25
|
+
GATE-REVIEW after adversarial review. It never starts implementation.
|
|
24
26
|
- Start implementation only through a new explicit `$cf-develop` invocation.
|
|
25
27
|
Execute one unblocked task at a time; each task has exactly one `Status:`
|
|
26
28
|
field and the controller is its sole state-and-proof writer.
|
|
@@ -28,16 +30,9 @@
|
|
|
28
30
|
requires a canonical final inline `## Receipt` with the exact command,
|
|
29
31
|
`Exit: 0`, `Verification: PASS`, runtime-derived Base and Head values, and
|
|
30
32
|
non-empty fenced current output.
|
|
31
|
-
- At
|
|
33
|
+
- At GATE-DONE, show current receipts and unresolved limitations. The user decides
|
|
32
34
|
completion; no command, review, or host state may invent approval or proof.
|
|
33
35
|
|
|
34
|
-
### Legacy Specs compatibility
|
|
35
|
-
|
|
36
|
-
Existing packets containing `spec.json`, nested tasks, or legacy kernel
|
|
37
|
-
artifacts keep their installed adapter, `task_registry`, `semantic_model`,
|
|
38
|
-
`planning_depth`, lane, `execution_tier`, machine authority, separate receipts,
|
|
39
|
-
and closeout contract. Do not migrate them during unrelated process-first work.
|
|
40
|
-
|
|
41
36
|
## Codex caveats
|
|
42
37
|
|
|
43
38
|
- Custom agents use snake_case names and `fork_turns: "none"` for explicit delegation.
|
|
@@ -26,6 +26,7 @@ try {
|
|
|
26
26
|
const thinkLang = runtime.locale?.thinkingLanguage || (respondLang ? 'en' : '');
|
|
27
27
|
const plansPath = resolveProjectPath(projectRoot, runtime.paths?.plans, 'plans');
|
|
28
28
|
const docsPath = resolveProjectPath(projectRoot, runtime.paths?.docs, 'docs');
|
|
29
|
+
const specsPath = resolveProjectPath(projectRoot, runtime.paths?.specs, 'specs');
|
|
29
30
|
const venv = resolveVenv(projectRoot);
|
|
30
31
|
const lines = [
|
|
31
32
|
`## Subagent: ${payload.agent_type || 'unknown'}`,
|
|
@@ -44,7 +45,7 @@ try {
|
|
|
44
45
|
|
|
45
46
|
lines.push(
|
|
46
47
|
'## Rules',
|
|
47
|
-
`- Plans → ${plansPath}/ | Docs → ${docsPath}/`,
|
|
48
|
+
`- Specs → ${specsPath}/ | Plans → ${plansPath}/ | Docs → ${docsPath}/`,
|
|
48
49
|
'- YAGNI · KISS · DRY',
|
|
49
50
|
'- Be concise. List unresolved questions at end.'
|
|
50
51
|
);
|
|
@@ -37,8 +37,8 @@ function loadSharedReceipt() {
|
|
|
37
37
|
'spec-receipt.cjs',
|
|
38
38
|
'spec-receipt.cjs',
|
|
39
39
|
(receipt) => {
|
|
40
|
-
if (typeof receipt?.
|
|
41
|
-
throw new Error('shared spec receipt helper has no
|
|
40
|
+
if (typeof receipt?.checkWorkflowTaskReceipt !== 'function') {
|
|
41
|
+
throw new Error('shared spec receipt helper has no checkWorkflowTaskReceipt function');
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
'shared spec receipt helper is missing'
|
|
@@ -55,7 +55,6 @@ function receiptHelper() {
|
|
|
55
55
|
return loaded.receipt;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function evidenceBody(text) { return receiptHelper().evidenceBody(text, getSharedPolicy() || {}); }
|
|
59
58
|
function safeTaskFile(featureDir, taskPath) {
|
|
60
59
|
const result = receiptHelper().safeRead(featureDir, taskPath);
|
|
61
60
|
return result.status === 'ok' ? result.path : null;
|
|
@@ -64,11 +63,6 @@ function validateCanonicalReceipt(body, options = {}) {
|
|
|
64
63
|
const validate = getSharedValidate();
|
|
65
64
|
return validate ? validate(body, options) : ['shared_validator'];
|
|
66
65
|
}
|
|
67
|
-
function checkReceiptDetails(featureDir, taskPath, task, runtimeContext) {
|
|
68
|
-
const policy = getSharedPolicy();
|
|
69
|
-
if (!policy) return { failures: ['shared_validator'], status: 'missing' };
|
|
70
|
-
return receiptHelper().checkTaskReceipt(featureDir, taskPath, task, runtimeContext, policy);
|
|
71
|
-
}
|
|
72
66
|
function checkWorkflowReceiptDetails(featureDir, taskPath, runtimeContext) {
|
|
73
67
|
const policy = getSharedPolicy();
|
|
74
68
|
const receipt = receiptHelper();
|
|
@@ -85,40 +79,18 @@ function checkWorkflowReceiptSet(candidates, projectRoot, runtimeSession) {
|
|
|
85
79
|
}
|
|
86
80
|
return receipt.checkWorkflowReceiptSet(candidates, projectRoot, runtimeSession, policy);
|
|
87
81
|
}
|
|
88
|
-
function
|
|
89
|
-
const map = {
|
|
90
|
-
unsafe_path: 'a', missing_receipt: 'b', task_status: 'a', completed_at: 'd',
|
|
91
|
-
verification_state: 'c', placeholder: 'c', validator_unavailable: 'c',
|
|
92
|
-
command: 'e', command_identity: 'e', exit_result: 'f', provenance: 'g',
|
|
93
|
-
artifact_hash: 'h', artifact_declaration: 'h',
|
|
94
|
-
};
|
|
95
|
-
return [...new Set(checkReceiptDetails(featureDir, taskPath, task, runtimeContext).failures.map((failure) => map[failure] || failure))];
|
|
96
|
-
}
|
|
97
|
-
function checkFeatureReceipt(featureDir, runtimeContext) {
|
|
98
|
-
const policy = getSharedPolicy();
|
|
99
|
-
if (!policy) return { failures: ['shared_validator'], status: 'missing' };
|
|
100
|
-
return receiptHelper().checkFeatureReceipt(featureDir, runtimeContext, policy);
|
|
101
|
-
}
|
|
102
|
-
function receiptFixHint(failures) {
|
|
82
|
+
function receiptFixHint(failures, body) {
|
|
103
83
|
const helper = receiptHelper();
|
|
104
|
-
return typeof helper.receiptFixHint === 'function' ? helper.receiptFixHint(failures) : null;
|
|
105
|
-
}
|
|
106
|
-
function readTaskProof(featureDir, taskPath) {
|
|
107
|
-
return receiptHelper().readTaskProof(featureDir, taskPath, getSharedPolicy() || {});
|
|
84
|
+
return typeof helper.receiptFixHint === 'function' ? helper.receiptFixHint(failures, body) : null;
|
|
108
85
|
}
|
|
109
86
|
|
|
110
87
|
module.exports = {
|
|
111
|
-
checkFeatureReceipt,
|
|
112
|
-
checkReceipt,
|
|
113
|
-
checkReceiptDetails,
|
|
114
88
|
checkWorkflowReceiptDetails,
|
|
115
89
|
checkWorkflowReceiptSet,
|
|
116
|
-
evidenceBody,
|
|
117
90
|
getSharedPolicy,
|
|
118
91
|
getSharedValidate,
|
|
119
92
|
loadSharedPolicy,
|
|
120
93
|
loadSharedReceipt,
|
|
121
|
-
readTaskProof,
|
|
122
94
|
receiptFixHint,
|
|
123
95
|
safeTaskFile,
|
|
124
96
|
validateCanonicalReceipt,
|
|
@@ -66,8 +66,8 @@ function sharedResolver() {
|
|
|
66
66
|
// Use canonical realpath for require to avoid symlink hijack
|
|
67
67
|
const realCandidate = canonicalRealpath(candidate);
|
|
68
68
|
const resolver = require(realCandidate);
|
|
69
|
-
if (typeof resolver?.
|
|
70
|
-
throw new Error('shared spec resolver has no
|
|
69
|
+
if (typeof resolver?.resolveWorkflowCandidate !== 'function') {
|
|
70
|
+
throw new Error('shared spec resolver has no resolveWorkflowCandidate function');
|
|
71
71
|
}
|
|
72
72
|
return resolver;
|
|
73
73
|
} catch (error) {
|
|
@@ -80,31 +80,13 @@ function specsDirectory(projectRoot, runtime) {
|
|
|
80
80
|
return sharedResolver().specsDirectory(projectRoot, runtime);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
function
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
function findAllSpecCandidates(projectRoot, runtime) {
|
|
88
|
-
return sharedResolver().findAllSpecCandidates(projectRoot, runtime);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function resolveActiveSpec(projectRoot, runtime, explicitFeature, explicitPath) {
|
|
92
|
-
if (projectRoot && typeof projectRoot === 'object') {
|
|
93
|
-
return sharedResolver().resolveActiveSpec(projectRoot);
|
|
94
|
-
}
|
|
95
|
-
return sharedResolver().resolveActiveSpec({
|
|
96
|
-
projectRoot,
|
|
97
|
-
runtime,
|
|
98
|
-
explicitFeature,
|
|
99
|
-
explicitPath,
|
|
100
|
-
});
|
|
83
|
+
function findLegacyPackets(projectRoot, runtime) {
|
|
84
|
+
const resolver = sharedResolver();
|
|
85
|
+
return typeof resolver.findLegacyPackets === 'function' ? resolver.findLegacyPackets(projectRoot, runtime) : [];
|
|
101
86
|
}
|
|
102
87
|
|
|
103
88
|
function resolveWorkflowCandidate(projectRoot, runtime, explicitFeature, explicitPath) {
|
|
104
89
|
const resolver = sharedResolver();
|
|
105
|
-
if (typeof resolver.resolveWorkflowCandidate !== 'function') {
|
|
106
|
-
return resolveActiveSpec(projectRoot, runtime, explicitFeature, explicitPath);
|
|
107
|
-
}
|
|
108
90
|
if (projectRoot && typeof projectRoot === 'object') {
|
|
109
91
|
return resolver.resolveWorkflowCandidate(projectRoot);
|
|
110
92
|
}
|
|
@@ -123,23 +105,6 @@ function refineWorkflowGateResolution(resolved) {
|
|
|
123
105
|
: resolved;
|
|
124
106
|
}
|
|
125
107
|
|
|
126
|
-
function findActiveSpec(projectRoot, runtime) {
|
|
127
|
-
const resolved = resolveActiveSpec(projectRoot, runtime);
|
|
128
|
-
if (!resolved) return null;
|
|
129
|
-
if (resolved.error === 'multiple_active' || resolved.error === 'invalid_specs') return resolved;
|
|
130
|
-
if (resolved.error) return null;
|
|
131
|
-
return resolved;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function taskStatusMap(spec) {
|
|
135
|
-
return Object.fromEntries(
|
|
136
|
-
Object.entries(spec.task_registry || {}).map(([taskPath, task]) => [
|
|
137
|
-
taskPath,
|
|
138
|
-
task?.status || 'pending',
|
|
139
|
-
]),
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
108
|
function extractExplicitTarget(...sources) {
|
|
144
109
|
const resolver = sharedResolver();
|
|
145
110
|
if (typeof resolver.extractExplicitTarget !== 'function') return null;
|
|
@@ -152,22 +117,11 @@ function readActiveFeatureTarget(options) {
|
|
|
152
117
|
return resolver.readActiveFeatureTarget(options);
|
|
153
118
|
}
|
|
154
119
|
|
|
155
|
-
function resolvePersistedSpec(options) {
|
|
156
|
-
const resolver = sharedResolver();
|
|
157
|
-
if (typeof resolver.resolvePersistedSpec !== 'function') return null;
|
|
158
|
-
return resolver.resolvePersistedSpec(options);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
120
|
module.exports = {
|
|
162
|
-
findActiveSpec,
|
|
163
121
|
extractExplicitTarget,
|
|
122
|
+
findLegacyPackets,
|
|
164
123
|
readActiveFeatureTarget,
|
|
165
|
-
resolvePersistedSpec,
|
|
166
|
-
findAllActiveSpecs,
|
|
167
|
-
findAllSpecCandidates,
|
|
168
|
-
resolveActiveSpec,
|
|
169
124
|
resolveWorkflowCandidate,
|
|
170
125
|
refineWorkflowGateResolution,
|
|
171
126
|
specsDirectory,
|
|
172
|
-
taskStatusMap,
|
|
173
127
|
};
|
|
@@ -79,6 +79,7 @@ try {
|
|
|
79
79
|
const thinkLang = runtime.locale?.thinkingLanguage || (respondLang ? 'en' : '');
|
|
80
80
|
const plansPath = resolveProjectPath(projectRoot, runtime.paths?.plans, 'plans');
|
|
81
81
|
const docsPath = resolveProjectPath(projectRoot, runtime.paths?.docs, 'docs');
|
|
82
|
+
const specsPath = resolveProjectPath(projectRoot, runtime.paths?.specs, 'specs');
|
|
82
83
|
const lines = [];
|
|
83
84
|
|
|
84
85
|
if ((thinkLang && thinkLang !== respondLang) || respondLang) {
|
|
@@ -92,7 +93,7 @@ try {
|
|
|
92
93
|
|
|
93
94
|
lines.push(
|
|
94
95
|
'## Rules',
|
|
95
|
-
`- Markdown: Plans → "${plansPath}/" | Docs → "${docsPath}/"`,
|
|
96
|
+
`- Markdown: Specs → "${specsPath}/" | Plans → "${plansPath}/" | Docs → "${docsPath}/"`,
|
|
96
97
|
'- Do not create markdown outside those directories unless explicitly asked.',
|
|
97
98
|
`- docs.maxLoc: ${runtime.docs?.maxLoc || 800} lines per doc file`
|
|
98
99
|
);
|
|
@@ -10,7 +10,6 @@ const {
|
|
|
10
10
|
readPayload
|
|
11
11
|
} = require('./lib/hook-context.cjs');
|
|
12
12
|
const { clearState } = require('./lib/privacy-state.cjs');
|
|
13
|
-
const { clearState: clearCompletionState } = require('./completion-authority-state.cjs');
|
|
14
13
|
|
|
15
14
|
function readPackage(projectRoot) {
|
|
16
15
|
try {
|
|
@@ -89,7 +88,6 @@ try {
|
|
|
89
88
|
|
|
90
89
|
// Approval tokens never survive a session boundary or compaction.
|
|
91
90
|
clearState(projectRoot);
|
|
92
|
-
clearCompletionState(projectRoot, payload.session_id);
|
|
93
91
|
|
|
94
92
|
// Orca (onorca.dev) sets ORCA_PANE_KEY in every pane; it is a presence marker, not a
|
|
95
93
|
// secret. ORCA_AGENT_HOOK_TOKEN and ORCA_AGENT_LAUNCH_TOKEN live in the same
|
|
@@ -11,15 +11,11 @@ const {
|
|
|
11
11
|
readPayload
|
|
12
12
|
} = require('./lib/hook-context.cjs');
|
|
13
13
|
const {
|
|
14
|
-
checkFeatureReceipt,
|
|
15
|
-
checkReceiptDetails,
|
|
16
14
|
checkWorkflowReceiptDetails,
|
|
17
15
|
checkWorkflowReceiptSet,
|
|
18
16
|
loadSharedPolicy,
|
|
19
17
|
receiptFixHint,
|
|
20
18
|
} = require('./lib/spec-receipt.cjs');
|
|
21
|
-
const { taskStatusMap } = require('./lib/spec-utils.cjs');
|
|
22
|
-
const FINAL_STATE = require('./completion-authority-check.cjs');
|
|
23
19
|
|
|
24
20
|
function emitBlock(reason) {
|
|
25
21
|
process.stdout.write(`${JSON.stringify({ decision: 'block', reason })}\n`);
|
|
@@ -46,7 +42,7 @@ try {
|
|
|
46
42
|
}
|
|
47
43
|
const POLICY = loaded.policy;
|
|
48
44
|
const { projectRoot, runtime } = getHookContext(payload);
|
|
49
|
-
if (typeof POLICY.
|
|
45
|
+
if (typeof POLICY.deriveRuntimeContext !== 'function') {
|
|
50
46
|
throw new Error('shared workflow policy lacks completion authority functions');
|
|
51
47
|
}
|
|
52
48
|
const resolver = require('./lib/spec-utils.cjs');
|
|
@@ -54,9 +50,7 @@ try {
|
|
|
54
50
|
// the payload leaves, which is what made the block unescapable on this runtime too.
|
|
55
51
|
const target = resolver.extractExplicitTarget(payload)
|
|
56
52
|
|| resolver.readActiveFeatureTarget({ projectRoot, runtime });
|
|
57
|
-
let resolved =
|
|
58
|
-
? resolver.resolveWorkflowCandidate({ projectRoot, runtime, target, includeCompleted: true })
|
|
59
|
-
: FINAL_STATE.resolveCandidate({ resolver, projectRoot, runtime, payload });
|
|
53
|
+
let resolved = resolver.resolveWorkflowCandidate({ projectRoot, runtime, target, includeCompleted: true });
|
|
60
54
|
if (typeof resolver.refineWorkflowGateResolution === 'function') {
|
|
61
55
|
resolved = resolver.refineWorkflowGateResolution(resolved);
|
|
62
56
|
}
|
|
@@ -76,7 +70,7 @@ try {
|
|
|
76
70
|
const lines = [`Completion gate: ${checked.failures.length} done task(s) lack a verification receipt.`];
|
|
77
71
|
for (const failure of checked.failures) {
|
|
78
72
|
lines.push(`- \`${failure.featureName}/${failure.taskPath}\`: failed check(s) ${failure.failures.join(', ')}`);
|
|
79
|
-
lines.push(` In \`specs/${failure.featureName}/${failure.taskPath}\`: ${receiptFixHint(failure.failures) || 'write a runtime-bound `## Receipt` with the planned command'}.`);
|
|
73
|
+
lines.push(` In \`specs/${failure.featureName}/${failure.taskPath}\`: ${receiptFixHint(failure.failures, failure.body) || 'write a runtime-bound `## Receipt` with the planned command'}.`);
|
|
80
74
|
}
|
|
81
75
|
emitBlock(lines.slice(0, 8).join('\n'));
|
|
82
76
|
process.exit(0);
|
|
@@ -91,7 +85,7 @@ try {
|
|
|
91
85
|
if (resolved.error === 'invalid_specs') {
|
|
92
86
|
process.stdout.write(`${JSON.stringify({
|
|
93
87
|
decision: 'block',
|
|
94
|
-
reason: `Completion gate: invalid
|
|
88
|
+
reason: `Completion gate: invalid workflow packet detected (${resolved.candidates.join(', ')}): ${resolved.reason}. Fix the packet's plan.md or task files before completing tasks.`
|
|
95
89
|
})}\n`);
|
|
96
90
|
process.exit(0);
|
|
97
91
|
}
|
|
@@ -104,28 +98,10 @@ try {
|
|
|
104
98
|
}
|
|
105
99
|
if (resolved.error) process.exit(0);
|
|
106
100
|
const active = resolved;
|
|
107
|
-
const activeSpec = active.spec || {};
|
|
108
|
-
const processWorkflow = active.layoutKind === 'process-v3';
|
|
109
|
-
const lifecyclePhase = activeSpec.current_phase || activeSpec.phase;
|
|
110
|
-
const explicitCloseout = ['done', 'completed', 'complete'].includes(activeSpec.status)
|
|
111
|
-
|| ['closeout', 'completion', 'completed', 'complete'].includes(lifecyclePhase);
|
|
112
|
-
if (!processWorkflow && activeSpec.schema_version === '2.1') {
|
|
113
|
-
const finalState = FINAL_STATE.evaluateCloseout({
|
|
114
|
-
policy: POLICY,
|
|
115
|
-
projectRoot,
|
|
116
|
-
runtime,
|
|
117
|
-
payload: { ...payload, session_id: sessionIdentity(payload) },
|
|
118
|
-
});
|
|
119
|
-
if (!finalState.ok) {
|
|
120
|
-
emitBlock(`Completion gate: ${finalState.reason}`);
|
|
121
|
-
process.exit(0);
|
|
122
|
-
}
|
|
123
|
-
if (finalState.active) process.exit(0);
|
|
124
|
-
}
|
|
125
101
|
const runtimeContext = POLICY.deriveRuntimeContext({
|
|
126
102
|
projectRoot,
|
|
127
103
|
specsRoot: active.specsDir,
|
|
128
|
-
specFile: active.stateFile
|
|
104
|
+
specFile: active.stateFile,
|
|
129
105
|
featureName: active.featureName,
|
|
130
106
|
runtimeSession: sessionIdentity(payload),
|
|
131
107
|
});
|
|
@@ -134,10 +110,10 @@ try {
|
|
|
134
110
|
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.');
|
|
135
111
|
process.exit(0);
|
|
136
112
|
}
|
|
137
|
-
const registry = active.taskRegistry ||
|
|
138
|
-
const currentStatuses =
|
|
139
|
-
|
|
140
|
-
|
|
113
|
+
const registry = active.taskRegistry || {};
|
|
114
|
+
const currentStatuses = Object.fromEntries(
|
|
115
|
+
Object.entries(registry).map(([taskPath, task]) => [taskPath, task?.status || 'pending']),
|
|
116
|
+
);
|
|
141
117
|
const cacheFile = path.join(hookStateDir(projectRoot), 'spec-gate-last.json');
|
|
142
118
|
const cacheExists = fs.existsSync(cacheFile);
|
|
143
119
|
let cache = {};
|
|
@@ -163,16 +139,6 @@ try {
|
|
|
163
139
|
const previous = cacheExists && cacheEntries[runtimeContext.context_id]
|
|
164
140
|
? cacheEntries[runtimeContext.context_id].tasks || {}
|
|
165
141
|
: {};
|
|
166
|
-
const staleFlashTasks = processWorkflow ? [] : Object.entries(registry)
|
|
167
|
-
.filter(([, task]) => POLICY.isStaleFlashDone(task))
|
|
168
|
-
.map(([taskPath]) => taskPath);
|
|
169
|
-
if (staleFlashTasks.length > 0) {
|
|
170
|
-
process.stdout.write(`${JSON.stringify({
|
|
171
|
-
decision: 'block',
|
|
172
|
-
reason: `Completion gate: ${staleFlashTasks.length} task(s) marked done with FLASH_UNVERIFIED (${staleFlashTasks.join(', ')}). Run /cf:test, then use explicit sync-finalize.`
|
|
173
|
-
})}\n`);
|
|
174
|
-
process.exit(0);
|
|
175
|
-
}
|
|
176
142
|
const allDoneTasks = Object.keys(registry).filter((taskPath) => (
|
|
177
143
|
currentStatuses[taskPath] === 'done'
|
|
178
144
|
));
|
|
@@ -180,9 +146,7 @@ try {
|
|
|
180
146
|
const receiptBodies = new Map();
|
|
181
147
|
const failures = allDoneTasks
|
|
182
148
|
.map((taskPath) => {
|
|
183
|
-
const proof =
|
|
184
|
-
? checkWorkflowReceiptDetails(featureDir, taskPath, runtimeContext)
|
|
185
|
-
: checkReceiptDetails(featureDir, taskPath, registry[taskPath], runtimeContext);
|
|
149
|
+
const proof = checkWorkflowReceiptDetails(featureDir, taskPath, runtimeContext);
|
|
186
150
|
if (proof.body) receiptBodies.set(taskPath, proof.body);
|
|
187
151
|
return {
|
|
188
152
|
taskPath,
|
|
@@ -191,52 +155,20 @@ try {
|
|
|
191
155
|
})
|
|
192
156
|
.filter((result) => result.failures.length);
|
|
193
157
|
|
|
194
|
-
const featureCloseoutRequired = !processWorkflow && explicitCloseout && allDoneTasks.length > 0;
|
|
195
|
-
const featureReceipt = featureCloseoutRequired
|
|
196
|
-
? checkFeatureReceipt(featureDir, runtimeContext)
|
|
197
|
-
: null;
|
|
198
|
-
if (featureReceipt && featureReceipt.failures.length) {
|
|
199
|
-
failures.push({ taskPath: 'feature-receipt.md', failures: featureReceipt.failures });
|
|
200
|
-
}
|
|
201
|
-
const completion = featureCloseoutRequired && featureReceipt?.failures.length === 0
|
|
202
|
-
&& Object.prototype.hasOwnProperty.call(active.spec, 'workflow_policy')
|
|
203
|
-
? POLICY.completionDecisionForSpec(active.spec, {
|
|
204
|
-
runtimeContext,
|
|
205
|
-
executionReceipt: featureReceipt.body,
|
|
206
|
-
taskContext: {},
|
|
207
|
-
})
|
|
208
|
-
: null;
|
|
209
|
-
|
|
210
158
|
const next = { ...previous, ...currentStatuses };
|
|
211
159
|
for (const result of failures) {
|
|
212
160
|
if (previous[result.taskPath] === undefined) delete next[result.taskPath];
|
|
213
161
|
else next[result.taskPath] = previous[result.taskPath];
|
|
214
162
|
}
|
|
215
163
|
atomicWrite(cacheFile, `${JSON.stringify({
|
|
216
|
-
schema_version: '2',
|
|
217
164
|
entries: { ...cacheEntries, [runtimeContext.context_id]: { identity: cacheIdentity, tasks: next } },
|
|
218
165
|
})}\n`);
|
|
219
166
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const lines = [
|
|
223
|
-
failures.length > 0
|
|
224
|
-
? `Completion gate: ${failures.length} done task(s) lack a verification receipt.`
|
|
225
|
-
: 'Completion gate: workflow completion proof is incomplete.'
|
|
226
|
-
];
|
|
167
|
+
if (!failures.length) process.exit(0);
|
|
168
|
+
const lines = [`Completion gate: ${failures.length} done task(s) lack a verification receipt.`];
|
|
227
169
|
for (const result of failures) {
|
|
228
170
|
lines.push(`- \`${result.taskPath}\`: failed check(s) ${result.failures.join(', ')}`);
|
|
229
|
-
lines.push(result.taskPath
|
|
230
|
-
? ` Run final integration proof, then write \`specs/${active.featureName}/feature-receipt.md\`.`
|
|
231
|
-
: processWorkflow
|
|
232
|
-
? ` In \`specs/${active.featureName}/${path.posix.basename(result.taskPath)}\`: ${receiptFixHint(result.failures) || 'write a runtime-bound `## Receipt` with command output'}.`
|
|
233
|
-
: ` Write canonical proof to \`specs/${active.featureName}/receipts/${path.posix.basename(result.taskPath)}\`; legacy \`## Evidence\` remains read-compatible.`);
|
|
234
|
-
}
|
|
235
|
-
if (completionBlocked) {
|
|
236
|
-
lines.push(`- Completion decision unfinished: ${completion.blocker || 'required workflow proof is missing.'}`);
|
|
237
|
-
if (Array.isArray(completion.missingProof) && completion.missingProof.length > 0) {
|
|
238
|
-
lines.push(` Missing proof: ${completion.missingProof.join(', ')}`);
|
|
239
|
-
}
|
|
171
|
+
lines.push(` In \`specs/${active.featureName}/${path.posix.basename(result.taskPath)}\`: ${receiptFixHint(result.failures, result.body) || 'write a runtime-bound `## Receipt` with command output'}.`);
|
|
240
172
|
}
|
|
241
173
|
process.stdout.write(`${JSON.stringify({
|
|
242
174
|
decision: 'block',
|
|
@@ -40,7 +40,28 @@ try {
|
|
|
40
40
|
const RECEIPT = receiptLoaded.receipt;
|
|
41
41
|
const { projectRoot, runtime } = getHookContext(payload);
|
|
42
42
|
if (runtime.spec?.tollgate === false) process.exit(0);
|
|
43
|
-
const { resolveWorkflowCandidate } = require('./lib/spec-utils.cjs');
|
|
43
|
+
const { findLegacyPackets, resolveWorkflowCandidate } = require('./lib/spec-utils.cjs');
|
|
44
|
+
const sessionId = payload.session_id || payload.sessionId || payload.sessionID || payload.session?.id;
|
|
45
|
+
// A legacy packet (a specs/<x>/ directory without plan.md that the old flow left
|
|
46
|
+
// behind) is no longer read. Say so once per session instead of on every prompt.
|
|
47
|
+
const legacyPackets = findLegacyPackets(projectRoot, runtime);
|
|
48
|
+
if (legacyPackets.length > 0) {
|
|
49
|
+
const noticeFile = sessionId
|
|
50
|
+
? path.join(hookStateDir(projectRoot), `legacy-notice-${crypto.createHash('sha256').update(String(sessionId)).digest('hex').slice(0, 16)}.json`)
|
|
51
|
+
: null;
|
|
52
|
+
let noticed = [];
|
|
53
|
+
if (noticeFile) {
|
|
54
|
+
try { noticed = JSON.parse(fs.readFileSync(noticeFile, 'utf8')); } catch { noticed = []; }
|
|
55
|
+
if (!Array.isArray(noticed)) noticed = [];
|
|
56
|
+
}
|
|
57
|
+
const fresh = legacyPackets.filter((name) => !noticed.includes(name));
|
|
58
|
+
for (const name of fresh) {
|
|
59
|
+
process.stdout.write(`> ⚠️ \`specs/${name}\` is a legacy Specs packet without plan.md; CafeKit no longer reads it. Move ongoing work to a process-first plan.md packet.\n`);
|
|
60
|
+
}
|
|
61
|
+
if (noticeFile && fresh.length > 0) {
|
|
62
|
+
try { atomicWrite(noticeFile, `${JSON.stringify([...noticed, ...fresh].sort())}\n`); } catch { /* repeats the notice */ }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
44
65
|
const explicitFeature = payload.featureName || payload.feature || payload.explicitFeature || null;
|
|
45
66
|
const explicitPath = payload.specPath || payload.spec_path || payload.featurePath || null;
|
|
46
67
|
const resolved = resolveWorkflowCandidate(projectRoot, runtime, explicitFeature, explicitPath);
|
|
@@ -50,7 +71,7 @@ try {
|
|
|
50
71
|
process.exit(0);
|
|
51
72
|
}
|
|
52
73
|
if (resolved.error === 'invalid_specs') {
|
|
53
|
-
process.stdout.write(`> ⚠️ Invalid
|
|
74
|
+
process.stdout.write(`> ⚠️ Invalid workflow packet detected: ${resolved.candidates.join(', ')}. ${resolved.reason}. Fix the packet's plan.md or task files. Tollgate paused.\n`);
|
|
54
75
|
process.exit(0);
|
|
55
76
|
}
|
|
56
77
|
if (resolved.error === 'explicit_not_found' || resolved.error === 'explicit_malformed') {
|
|
@@ -60,18 +81,16 @@ try {
|
|
|
60
81
|
}
|
|
61
82
|
if (resolved.error) process.exit(0);
|
|
62
83
|
const active = resolved;
|
|
63
|
-
const processWorkflow = active.layoutKind === 'process-v3';
|
|
64
84
|
const runtimeContext = POLICY.deriveRuntimeContext({
|
|
65
85
|
projectRoot,
|
|
66
86
|
specsRoot: active.specsDir,
|
|
67
|
-
specFile: active.stateFile
|
|
87
|
+
specFile: active.stateFile,
|
|
68
88
|
featureName: active.featureName,
|
|
69
|
-
runtimeSession:
|
|
89
|
+
runtimeSession: sessionId,
|
|
70
90
|
});
|
|
71
91
|
|
|
72
|
-
const phase = active.phase ||
|
|
73
|
-
const taskRegistry = active.taskRegistry ||
|
|
74
|
-
const flashTasks = POLICY.flashState(taskRegistry);
|
|
92
|
+
const phase = active.phase || 'unknown';
|
|
93
|
+
const taskRegistry = active.taskRegistry || {};
|
|
75
94
|
const tasks = Object.entries(taskRegistry);
|
|
76
95
|
const counts = tasks.reduce((result, [, task]) => {
|
|
77
96
|
const status = task?.status || 'pending';
|
|
@@ -79,13 +98,7 @@ try {
|
|
|
79
98
|
return result;
|
|
80
99
|
}, {});
|
|
81
100
|
const featureDir = path.join(active.specsDir, active.featureName);
|
|
82
|
-
const
|
|
83
|
-
taskPath,
|
|
84
|
-
task?.status || 'pending'
|
|
85
|
-
]));
|
|
86
|
-
const dependencyProof = processWorkflow
|
|
87
|
-
? RECEIPT.workflowDependencyProofState(featureDir, taskRegistry, runtimeContext, POLICY)
|
|
88
|
-
: {};
|
|
101
|
+
const dependencyProof = RECEIPT.workflowDependencyProofState(featureDir, taskRegistry, runtimeContext, POLICY);
|
|
89
102
|
const taskState = tasks.map(([taskPath, task]) => [
|
|
90
103
|
taskPath,
|
|
91
104
|
task?.status || 'pending',
|
|
@@ -101,12 +114,8 @@ try {
|
|
|
101
114
|
const next = tasks.find(([, task]) => (
|
|
102
115
|
active.queueReady !== false
|
|
103
116
|
&& (task?.status || 'pending') === 'pending'
|
|
104
|
-
&& (task?.dependencies || []).every((dependency) =>
|
|
105
|
-
processWorkflow ? dependencyProof[dependency]?.eligible === true : statuses.get(dependency) === 'done'
|
|
106
|
-
))
|
|
117
|
+
&& (task?.dependencies || []).every((dependency) => dependencyProof[dependency]?.eligible === true)
|
|
107
118
|
));
|
|
108
|
-
const featureReceiptPresent = !processWorkflow
|
|
109
|
-
&& RECEIPT.safeRead(featureDir, 'feature-receipt.md').status === 'ok';
|
|
110
119
|
const stateKey = JSON.stringify({
|
|
111
120
|
project_root: runtimeContext.project_root,
|
|
112
121
|
specs_root: runtimeContext.specs_root,
|
|
@@ -124,14 +133,13 @@ try {
|
|
|
124
133
|
proof_state: proofState,
|
|
125
134
|
done: counts.done || 0,
|
|
126
135
|
total: tasks.length,
|
|
127
|
-
feature_receipt_present: featureReceiptPresent,
|
|
128
136
|
});
|
|
129
137
|
const cache = cacheFile(projectRoot, runtimeContext.runtime_session);
|
|
130
138
|
let previous = '';
|
|
131
139
|
try { previous = fs.readFileSync(cache, 'utf8').trim(); } catch { /* first run */ }
|
|
132
140
|
|
|
133
141
|
if (previous === stateKey) {
|
|
134
|
-
const migration =
|
|
142
|
+
const migration = active.queueReady === false
|
|
135
143
|
? ' Add `Specs-Contract: process-first-ready-v1` after the plan title before tasks can enter Next.'
|
|
136
144
|
: '';
|
|
137
145
|
process.stdout.write(
|
|
@@ -147,26 +155,14 @@ try {
|
|
|
147
155
|
`- Phase: \`${phase}\` | Tasks: ${counts.done || 0} done / ${tasks.length} total`
|
|
148
156
|
];
|
|
149
157
|
if (next) lines.push(`- Next unblocked: \`${next[0]}\``);
|
|
150
|
-
if (
|
|
151
|
-
lines.push(
|
|
152
|
-
}
|
|
153
|
-
if (processWorkflow) {
|
|
154
|
-
if (active.queueReady === false) {
|
|
155
|
-
lines.push('- Migration required: add `Specs-Contract: process-first-ready-v1` after the plan title before tasks can enter Next.');
|
|
156
|
-
}
|
|
157
|
-
lines.push(
|
|
158
|
-
'- Sync each flat task `Status:` only after verified work; task proof belongs in that task\'s inline `## Receipt`.',
|
|
159
|
-
`- Workflow source: \`specs/${active.featureName}/plan.md\`; Stop re-checks every done receipt, binding Base/Head to the runtime only while a committed task file no longer matches its committed bytes.`,
|
|
160
|
-
'- Hooks revalidate receipt bytes but never grant approval.'
|
|
161
|
-
);
|
|
162
|
-
} else {
|
|
163
|
-
lines.push(
|
|
164
|
-
'- Sync `spec.json` and task Markdown status only after verified work; task proof belongs in `receipts/<task-basename>.md`.',
|
|
165
|
-
`- Create \`feature-receipt.md\` once after final integration proof${featureReceiptPresent ? ' (present)' : ' (not required before closeout)'}.`,
|
|
166
|
-
`- Validate with \`node .codex/scripts/validate-spec-output.cjs specs/${active.featureName}\`.`,
|
|
167
|
-
'- Hooks revalidate receipt bytes but never grant approval.'
|
|
168
|
-
);
|
|
158
|
+
if (active.queueReady === false) {
|
|
159
|
+
lines.push('- Migration required: add `Specs-Contract: process-first-ready-v1` after the plan title before tasks can enter Next.');
|
|
169
160
|
}
|
|
161
|
+
lines.push(
|
|
162
|
+
'- Sync each flat task `Status:` only after verified work; task proof belongs in that task\'s inline `## Receipt`.',
|
|
163
|
+
`- Workflow source: \`specs/${active.featureName}/plan.md\`; Stop re-checks every done receipt, binding Base/Head to the runtime only while a committed task file no longer matches its committed bytes.`,
|
|
164
|
+
'- Hooks revalidate receipt bytes but never grant approval.'
|
|
165
|
+
);
|
|
170
166
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
171
167
|
} catch (error) {
|
|
172
168
|
logCrash('spec-state', error);
|
package/src/codex/hooks.json
CHANGED
|
@@ -48,11 +48,6 @@
|
|
|
48
48
|
"command": "node \".codex/hooks/privacy-approval.cjs\"",
|
|
49
49
|
"commandWindows": "node \".codex/hooks/privacy-approval.cjs\""
|
|
50
50
|
},
|
|
51
|
-
{
|
|
52
|
-
"type": "command",
|
|
53
|
-
"command": "node \".codex/hooks/completion-authority.cjs\"",
|
|
54
|
-
"commandWindows": "node \".codex/hooks/completion-authority.cjs\""
|
|
55
|
-
},
|
|
56
51
|
{
|
|
57
52
|
"type": "command",
|
|
58
53
|
"command": "node \".codex/hooks/rules.cjs\"",
|
|
@@ -79,11 +74,6 @@
|
|
|
79
74
|
"type": "command",
|
|
80
75
|
"command": "node \".codex/hooks/inspect-block.cjs\"",
|
|
81
76
|
"commandWindows": "node \".codex/hooks/inspect-block.cjs\""
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
"type": "command",
|
|
85
|
-
"command": "node \".codex/hooks/task-scaffold-guard.cjs\"",
|
|
86
|
-
"commandWindows": "node \".codex/hooks/task-scaffold-guard.cjs\""
|
|
87
77
|
}
|
|
88
78
|
]
|
|
89
79
|
}
|
|
@@ -108,11 +98,6 @@
|
|
|
108
98
|
"command": "node \".codex/hooks/spec-gate.cjs\"",
|
|
109
99
|
"commandWindows": "node \".codex/hooks/spec-gate.cjs\""
|
|
110
100
|
},
|
|
111
|
-
{
|
|
112
|
-
"type": "command",
|
|
113
|
-
"command": "node \".codex/hooks/completion-authority.cjs\"",
|
|
114
|
-
"commandWindows": "node \".codex/hooks/completion-authority.cjs\""
|
|
115
|
-
},
|
|
116
101
|
{
|
|
117
102
|
"type": "command",
|
|
118
103
|
"command": "node \".codex/hooks/state.cjs\"",
|
|
@@ -125,11 +110,6 @@
|
|
|
125
110
|
{
|
|
126
111
|
"matcher": "*",
|
|
127
112
|
"hooks": [
|
|
128
|
-
{
|
|
129
|
-
"type": "command",
|
|
130
|
-
"command": "node \".codex/hooks/semantic-review-authority.cjs\"",
|
|
131
|
-
"commandWindows": "node \".codex/hooks/semantic-review-authority.cjs\""
|
|
132
|
-
},
|
|
133
113
|
{
|
|
134
114
|
"type": "command",
|
|
135
115
|
"command": "node \".codex/hooks/state.cjs\"",
|
|
@@ -51,19 +51,10 @@ proves only the boundary it executed.
|
|
|
51
51
|
|
|
52
52
|
## Human gates and workflow ownership
|
|
53
53
|
|
|
54
|
-
-
|
|
55
|
-
-
|
|
54
|
+
- GATE-SCOPE records the user's scope decision before the plan is written.
|
|
55
|
+
- GATE-REVIEW records the user's disposition of deduplicated adversarial findings.
|
|
56
56
|
- Develop starts only after a new explicit user invocation and executes one
|
|
57
57
|
unblocked task at a time.
|
|
58
58
|
- Sync changes only observed file state and never starts implementation.
|
|
59
|
-
-
|
|
59
|
+
- GATE-DONE occurs after current receipts and limitations are shown; the user decides
|
|
60
60
|
whether the requested feature is complete.
|
|
61
|
-
|
|
62
|
-
## Legacy compatibility
|
|
63
|
-
|
|
64
|
-
Existing packets containing `spec.json`, nested `tasks/task-R*.md`, or other
|
|
65
|
-
legacy kernel artifacts stay on their installed adapters. Preserve their
|
|
66
|
-
`task_registry`, `semantic_model`, `planning_depth`, lane,
|
|
67
|
-
`execution_tier`, typed topology, separate receipts, and feature closeout
|
|
68
|
-
contract. Do not migrate that machine authority or storage shape while syncing
|
|
69
|
-
an unrelated process-first packet.
|