@haposoft/cafekit 0.16.0-rc.6 → 0.16.0-rc.8
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 +21 -58
- package/bin/__tests__/change-firewall.semantic-firewall.test.js +4 -5
- package/bin/__tests__/codex-hooks.test.js +382 -0
- package/bin/__tests__/codex-native.test.js +579 -80
- package/bin/__tests__/develop-contract.test.js +155 -76
- package/bin/__tests__/installer-safety.test.js +19 -154
- package/bin/__tests__/package-inventory.test.js +494 -26
- package/bin/__tests__/release-preflight.semantic-firewall.test.js +2 -2
- package/bin/__tests__/validator-grounding.test.js +11 -11
- package/bin/install.js +2 -7
- package/bin/lib/context.js +0 -45
- package/bin/lib/copy-utils.js +1 -1
- package/bin/lib/i18n.js +0 -3
- package/bin/lib/manifest.js +1 -1
- package/bin/phases/claude-runtime.js +1 -3
- package/bin/phases/codex-runtime.js +4 -2
- package/bin/phases/copy-payload.js +3 -15
- package/bin/phases/post-install.js +5 -17
- package/bin/phases/root-config.js +2 -3
- package/bin/phases/select-platform.js +2 -5
- package/bin/phases/summary.js +0 -1
- package/package.json +2 -5
- package/src/claude/CLAUDE.md +19 -6
- package/src/claude/agents/project-manager.md +41 -9
- package/src/claude/agents/spec-maker.md +82 -155
- package/src/claude/gitignore +1 -1
- package/src/claude/hooks/__tests__/spec-gate.test.js +238 -0
- package/src/claude/hooks/__tests__/state.test.js +249 -3
- package/src/claude/hooks/lib/context.cjs +1 -1
- package/src/claude/hooks/lib/detect.cjs +1 -1
- package/src/claude/hooks/spec-gate.cjs +44 -9
- package/src/claude/hooks/spec-state.cjs +51 -13
- package/src/claude/hooks/task-scaffold-guard.cjs +1 -1
- package/src/claude/migration-manifest.json +17 -3
- package/src/claude/rules/manage-docs.md +43 -24
- package/src/claude/rules/state-sync.md +57 -62
- package/src/claude/rules/workflow.md +30 -13
- package/src/claude/scripts/change-firewall.cjs +2 -2
- package/src/claude/scripts/generate-skill-catalog.cjs +0 -1
- package/src/claude/scripts/spec-receipt.cjs +196 -1
- package/src/claude/scripts/spec-resolver.cjs +428 -0
- package/src/claude/skills/develop/SKILL.md +89 -125
- package/src/claude/skills/develop/references/parallel-waves.md +75 -162
- package/src/claude/skills/develop/references/quality-gate.md +61 -121
- package/src/claude/skills/develop/references/subagent-patterns.md +31 -38
- package/src/claude/skills/question/SKILL.md +1 -1
- package/src/claude/skills/specs/SKILL.md +152 -204
- package/src/claude/skills/specs/references/review.md +124 -243
- package/src/claude/skills/specs/references/templates.md +202 -0
- package/src/claude/skills/sync/SKILL.md +66 -90
- package/src/claude/skills/sync/references/sync-protocols.md +62 -83
- package/src/codex/AGENTS.md +20 -7
- package/src/codex/hooks/lib/spec-receipt.cjs +18 -0
- package/src/codex/hooks/lib/spec-utils.cjs +25 -0
- package/src/codex/hooks/spec-gate.cjs +46 -16
- package/src/codex/hooks/spec-state.cjs +54 -16
- package/src/codex/rules/state-sync.md +57 -61
- package/src/common/AGENTS.md +8 -1
- package/bin/__tests__/opencode-spec-gate.test.js +0 -799
- package/bin/lib/opencode-install.js +0 -899
- package/bin/phases/opencode-runtime.js +0 -38
- package/src/claude/skills/specs/references/archive-workflow.md +0 -74
- package/src/claude/skills/specs/references/ask-user-question-gates.md +0 -70
- package/src/claude/skills/specs/references/codebase-analysis.md +0 -102
- package/src/claude/skills/specs/references/cross-spec-dependency.md +0 -76
- package/src/claude/skills/specs/references/research-strategy.md +0 -107
- package/src/claude/skills/specs/references/scope-inquiry.md +0 -107
- package/src/claude/skills/specs/references/translation-mirror.md +0 -83
- package/src/claude/skills/specs/rules/design-discovery-full.md +0 -45
- package/src/claude/skills/specs/rules/design-discovery-light.md +0 -28
- package/src/claude/skills/specs/rules/design-principles.md +0 -138
- package/src/claude/skills/specs/rules/design-review.md +0 -55
- package/src/claude/skills/specs/rules/ears-format.md +0 -42
- package/src/claude/skills/specs/rules/phase-decision-matrix.md +0 -56
- package/src/claude/skills/specs/rules/task-scoring-rubric.md +0 -52
- package/src/claude/skills/specs/rules/tasks-generation.md +0 -189
- package/src/opencode/AGENTS.md +0 -41
- package/src/opencode/plugins/docs-sync.ts +0 -175
- package/src/opencode/plugins/inspect-block.ts +0 -164
- package/src/opencode/plugins/privacy-block.ts +0 -182
- package/src/opencode/plugins/rules.ts +0 -155
- package/src/opencode/plugins/session.ts +0 -179
- package/src/opencode/plugins/spec-gate.ts +0 -457
- package/src/opencode/plugins/spec-state.ts +0 -311
- package/src/opencode/plugins/state.ts +0 -253
- package/src/opencode/plugins/task-scaffold-guard.ts +0 -161
- package/src/opencode/plugins/usage.ts +0 -178
|
@@ -1,103 +1,79 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hapo:sync
|
|
3
|
-
description: "
|
|
3
|
+
description: "Synchronize process-first task Status and inline Receipts without inventing proof; also preserves existing legacy Specs state."
|
|
4
4
|
user-invocable: true
|
|
5
|
-
when_to_use: "Invoke
|
|
5
|
+
when_to_use: "Invoke after implementation or verification changes a task's real state, or to audit a feature packet for drift."
|
|
6
6
|
category: utilities
|
|
7
7
|
keywords: [sync, state, tracking, consistency]
|
|
8
|
-
argument-hint: "<
|
|
8
|
+
argument-hint: "<feature> <task-file> <pending|in_progress|paused|blocked|done|sync-finalize> [blocker] | audit <feature>"
|
|
9
9
|
metadata:
|
|
10
10
|
author: haposoft
|
|
11
|
-
version: "
|
|
11
|
+
version: "3.0.0"
|
|
12
12
|
---
|
|
13
|
-
# Sync
|
|
13
|
+
# Sync — file-state synchronization
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Synchronize only observed state. For the primary process-first layout, the
|
|
16
|
+
single `Status:` field and inline `## Receipt` in each flat task file are the
|
|
17
|
+
state. Never infer proof, approval, review independence, or product readiness.
|
|
16
18
|
|
|
17
|
-
##
|
|
19
|
+
## Commands
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
```text
|
|
22
|
+
/hapo:sync <feature> <task-NN-slug.md> in_progress
|
|
23
|
+
/hapo:sync <feature> <task-NN-slug.md> blocked "reason"
|
|
24
|
+
/hapo:sync <feature> <task-NN-slug.md> done
|
|
25
|
+
/hapo:sync <feature> <task-NN-slug.md> sync-finalize
|
|
26
|
+
/hapo:sync audit <feature>
|
|
23
27
|
```
|
|
24
28
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
### 4. Explicit Flash Finalization
|
|
77
|
-
|
|
78
|
-
**Usage:** `/hapo:sync <feature_name> <task_id|task-file> sync-finalize` (the adapter must call `workflow-policy.cjs --sync-finalize --task-json <current-task> --verdict PASS --proof <canonical-receipt> --json`)
|
|
79
|
-
|
|
80
|
-
This is the only operation allowed to turn the current `FLASH_UNVERIFIED` task into `done` and unblock dependencies. The policy derives promotion from that current state and explicit proof; `readyForSync`, `flashTransition`, and `promotionReceipt` supplied by the caller are not proof. A normal `done` request cannot bypass flash promotion or stale `FLASH_UNVERIFIED` state.
|
|
81
|
-
|
|
82
|
-
The current state must be the exact stored form: `status: "in_progress"`,
|
|
83
|
-
`receipt: "FLASH_UNVERIFIED"`, `dependencyBlocked: true`, `unblocks: false`, a
|
|
84
|
-
concrete blocker, and no caller promotion fields. The canonical proof must be
|
|
85
|
-
bound by the runtime adapter with `policy.createReceiptBinding({ base, head })`
|
|
86
|
-
and must match both receipt anchors; arbitrary valid-length Base/Head values do
|
|
87
|
-
not authorize finalization.
|
|
88
|
-
|
|
89
|
-
### Flash implementation state
|
|
90
|
-
|
|
91
|
-
Executable policy source: `.claude/scripts/workflow-policy.cjs` (source: `src/claude/scripts/workflow-policy.cjs`). Trusted sync-finalize invokes `promoteFlashTask` only after the exact task Verification Plan and reachability return PASS; test PASS itself only supplies canonical proof and leaves the persisted task blocked in progress. FAIL, BLOCKED, and NO_TESTS remain blocked in progress.
|
|
92
|
-
|
|
93
|
-
`FLASH_UNVERIFIED` is storage for implemented-but-unverified work, not a completion status. Store it as `status: "in_progress"` with `dependencyBlocked: true`, `unblocks: false`, a concrete blocker, and no promotion fields; do not unblock dependencies. `/hapo:test` may produce canonical proof, but the supported sync-finalize boundary must receive explicit `--verdict PASS` and `--proof`, require the runtime Base/Head binding, revalidate the receipt/artifact, derive the transition, and only then set `done`. FAIL, BLOCKED, NO_TESTS, marker-only proof, minimal state, and pre-promoted caller JSON remain `in_progress`.
|
|
94
|
-
|
|
95
|
-
After the last task is done, sync does not synthesize proof. Test/Develop must
|
|
96
|
-
run final integration and create `feature-receipt.md` once. Task-bearing
|
|
97
|
-
completion requires all task receipts plus the feature receipt. Taskless
|
|
98
|
-
Compact/Full completion requires the feature receipt only; its absence before
|
|
99
|
-
final closeout is normal. No receipt grants or infers approval, readiness, or
|
|
100
|
-
audit status.
|
|
101
|
-
|
|
102
|
-
## References
|
|
103
|
-
Read `references/sync-protocols.md` for exact Search/Replace regex patterns and JSON schema expectations before acting on the files.
|
|
29
|
+
Resolve one regular direct-child task inside `specs/<feature>/`. Reject path
|
|
30
|
+
escape, symlink, ambiguity, missing `plan.md`, duplicate Status fields, and
|
|
31
|
+
unsupported status values.
|
|
32
|
+
|
|
33
|
+
## Task synchronization
|
|
34
|
+
|
|
35
|
+
1. Read the task Outcome, Acceptance, Dependencies, Verification Plan, current
|
|
36
|
+
Status, and Receipt before editing.
|
|
37
|
+
2. Starting or resuming changes only Status to `in_progress`. Update only
|
|
38
|
+
checkboxes whose implementation is real.
|
|
39
|
+
3. Blocking changes Status to `blocked` and records a concrete blocker; it does
|
|
40
|
+
not create proof.
|
|
41
|
+
4. Done requires a current inline Receipt with `Verification: PASS`, exact
|
|
42
|
+
Command, `Exit: 0`, runtime-bound Base/Head, non-empty fenced output, and any
|
|
43
|
+
required negative/reachability/artifact proof.
|
|
44
|
+
5. Write or replace the Receipt before changing Status to `done`. Missing,
|
|
45
|
+
stale, contradictory, placeholder, failure, or zero-test evidence keeps the
|
|
46
|
+
task `in_progress` or `blocked`.
|
|
47
|
+
6. Re-read the file after the surgical edit and confirm exactly one Status and
|
|
48
|
+
one Receipt section.
|
|
49
|
+
|
|
50
|
+
`sync-finalize` is the only promotion path for `FLASH_UNVERIFIED`. It requires
|
|
51
|
+
fresh canonical PASS proof, revalidates Base/Head and artifacts, clears the
|
|
52
|
+
flash blocker, then derives `done`. Caller-supplied promotion booleans and a
|
|
53
|
+
marker alone have no authority.
|
|
54
|
+
|
|
55
|
+
## Audit
|
|
56
|
+
|
|
57
|
+
Scan only `specs/<feature>/task-*.md` beside `plan.md`. Compare plan task rows,
|
|
58
|
+
task filenames, dependencies, Status, acceptance mapping, and Receipts. Report
|
|
59
|
+
missing files, unknown rows, cycles, duplicate fields, done-without-proof,
|
|
60
|
+
proof-on-unfinished-task, and overlapping ownership.
|
|
61
|
+
|
|
62
|
+
Repair deterministic formatting drift only. A semantic conflict, missing
|
|
63
|
+
evidence, or ownership decision requires user or implementation input; do not
|
|
64
|
+
pick a winner. After any edit, report exact files changed and unresolved items.
|
|
65
|
+
|
|
66
|
+
Read `references/sync-protocols.md` for surgical update and audit rules.
|
|
67
|
+
|
|
68
|
+
## Docs impact
|
|
69
|
+
|
|
70
|
+
When a task becomes done, report `Docs impact: none|minor|major`. Create no docs
|
|
71
|
+
work for `none`; update only affected existing docs for minor or major.
|
|
72
|
+
|
|
73
|
+
## Legacy workflow compatibility
|
|
74
|
+
|
|
75
|
+
If `specs/<feature>/spec.json` exists, use the legacy adapter instead: preserve
|
|
76
|
+
its machine authority, `task_registry`, nested `tasks/task-R*.md`, timestamps,
|
|
77
|
+
typed topology, separate receipts, `workflow_policy`, `planning_depth`, lane,
|
|
78
|
+
and `execution_tier`. Keep JSON and Markdown synchronized, retain the exact
|
|
79
|
+
legacy sync-finalize contract, and require its feature receipt at closeout.
|
|
@@ -1,83 +1,62 @@
|
|
|
1
|
-
# Sync
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
1. **Load Truth:** Read `specs/<feature>/spec.json`.
|
|
64
|
-
2. **Scan Directory:** Loop through `specs/<feature>/tasks/`.
|
|
65
|
-
3. **Compare Constraints:** Rebuild `task_files` from disk, ensure every file exists in `task_registry`, and compare markdown `**Status:**` headers against `task_registry[path].status`.
|
|
66
|
-
4. **Reconciliation Rules:**
|
|
67
|
-
- Missing registry entry → create it
|
|
68
|
-
- Missing disk file referenced in registry → remove or flag it
|
|
69
|
-
- Markdown says `done` but registry not done → registry wins only if a canonical separate or legacy receipt exists; otherwise downgrade markdown or flag conflict
|
|
70
|
-
- Registry says `done` but markdown still pending → update markdown only if a canonical separate or legacy receipt exists
|
|
71
|
-
- Either side says `done` but neither receipt source has concrete proof → downgrade to `in_progress` or flag conflict instead of preserving fake completion
|
|
72
|
-
- Both receipt sources exist with conflicting proof identity → fail closed and require manual reconciliation
|
|
73
|
-
- Either side says `done` but the receipt contains `PRECHECK_FAIL`, `FAIL`, `UNVERIFIED`, or explicit contract-substitution notes → downgrade to `in_progress` or flag conflict
|
|
74
|
-
5. **Correction Alert:** Output a brief markdown alert detailing mismatches fixed and any unresolved conflicts requiring manual review.
|
|
75
|
-
6. **Task Docs Alert:** If audit reveals tasks newly marked `done`, include the actual docs impact; do not emit a generic docs-sync request when no docs are affected.
|
|
76
|
-
|
|
77
|
-
## 4. Feature closeout
|
|
78
|
-
|
|
79
|
-
- Before all tasks are done, missing `feature-receipt.md` is normal.
|
|
80
|
-
- After all tasks are done, require final integration execution and one valid
|
|
81
|
-
`feature-receipt.md`; never derive it from task status alone.
|
|
82
|
-
- A taskless Compact/Full spec uses the same final feature receipt and does not
|
|
83
|
-
require a synthetic registry entry.
|
|
1
|
+
# Sync protocols
|
|
2
|
+
|
|
3
|
+
Use surgical edits and verify bytes after writing. Never replace a whole packet
|
|
4
|
+
to change one task state.
|
|
5
|
+
|
|
6
|
+
## Resolve safely
|
|
7
|
+
|
|
8
|
+
1. Canonicalize `specs/<feature>/` under the configured specs root.
|
|
9
|
+
2. Require regular non-symlink `plan.md` and flat `task-*.md` files.
|
|
10
|
+
3. Match the requested task by exact basename; reject aliases and ambiguity.
|
|
11
|
+
4. Require exactly one `Status: pending|in_progress|paused|blocked|done` line.
|
|
12
|
+
5. Read dependencies and acceptance IDs before choosing a transition.
|
|
13
|
+
|
|
14
|
+
## Transitions
|
|
15
|
+
|
|
16
|
+
- `pending|paused|blocked -> in_progress`: change Status only; preserve a real
|
|
17
|
+
blocker in history or remove it only when its prerequisite changed.
|
|
18
|
+
- `* -> blocked`: write a concrete reason and keep existing proof unchanged.
|
|
19
|
+
- `in_progress -> done`: validate the current inline Receipt first, then change
|
|
20
|
+
Status. Never perform this order in reverse.
|
|
21
|
+
- `done -> in_progress`: allowed when proof becomes stale or implementation
|
|
22
|
+
changes; retain the stale receipt only when clearly labelled non-authoritative.
|
|
23
|
+
|
|
24
|
+
For Flash, keep `Status: in_progress`, `FLASH_UNVERIFIED`, and blocker
|
|
25
|
+
`awaiting /hapo:test <feature>`. Only sync-finalize with fresh canonical proof
|
|
26
|
+
may replace that marker and set done.
|
|
27
|
+
|
|
28
|
+
## Receipt update
|
|
29
|
+
|
|
30
|
+
The primary Receipt is the final `## Receipt` section in the task file. It must
|
|
31
|
+
contain one Verification PASS, one exact Command, one successful Exit, one
|
|
32
|
+
runtime-bound Base/Head pair, and non-empty fenced current output. Preserve
|
|
33
|
+
additional concrete negative, reachability, and artifact evidence.
|
|
34
|
+
|
|
35
|
+
Reject duplicate Receipt headings, placeholders, copied output, conflicting
|
|
36
|
+
success/failure markers, zero executed tests when tests are required, stale
|
|
37
|
+
provenance, and path or artifact mismatch.
|
|
38
|
+
|
|
39
|
+
## Audit reconciliation
|
|
40
|
+
|
|
41
|
+
- Plan row without task file: report missing; do not synthesize scope.
|
|
42
|
+
- Task file without plan row: report unknown; do not silently adopt it.
|
|
43
|
+
- Done without valid Receipt: downgrade only when the repair is deterministic,
|
|
44
|
+
otherwise block and request direction.
|
|
45
|
+
- Receipt on unfinished task: preserve it only if it is clearly historical;
|
|
46
|
+
otherwise report the conflict.
|
|
47
|
+
- Missing or cyclic dependency: block affected tasks.
|
|
48
|
+
- Overlapping write ownership in one proposed wave: serialize or request an
|
|
49
|
+
ownership decision.
|
|
50
|
+
- Acceptance ID not mapped both ways: report traceability drift.
|
|
51
|
+
|
|
52
|
+
After a repair, re-read every changed file and print a concise mismatch/fix/
|
|
53
|
+
unresolved summary. Never claim execution proof from the audit itself.
|
|
54
|
+
|
|
55
|
+
## Legacy workflow compatibility
|
|
56
|
+
|
|
57
|
+
For `spec.json` packets, resolve exact `task_registry` paths and synchronize
|
|
58
|
+
registry timestamps plus nested task Markdown. Validate the canonical separate
|
|
59
|
+
receipt (with legacy inline Evidence only as the supported fallback), reject
|
|
60
|
+
conflicting proof identities, preserve classified minimum and planning_depth,
|
|
61
|
+
and use the installed legacy policy for lane, execution_tier, Flash promotion,
|
|
62
|
+
and final feature closeout.
|
package/src/codex/AGENTS.md
CHANGED
|
@@ -8,12 +8,25 @@
|
|
|
8
8
|
- CafeKit agents live in `.codex/agents/*.toml` and are auto-discovered after the repository is trusted.
|
|
9
9
|
- Runtime support lives in `.codex/rules/`, `.codex/scripts/`, `.codex/references/`, and `.codex/runtime.json`.
|
|
10
10
|
- Project hooks live in `.codex/hooks.json`; review trusted hooks with `/hooks`.
|
|
11
|
-
- Specs
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
- New Specs work uses the process-first flow. `$hapo-specs` opens C1, writes
|
|
12
|
+
`specs/<feature>/plan.md` with flat `task-NN-*.md` files beside it, then opens
|
|
13
|
+
C2 after adversarial review. It never starts implementation.
|
|
14
|
+
- Start implementation only through a new explicit `$hapo-develop` invocation.
|
|
15
|
+
Execute one unblocked task at a time; each task has exactly one `Status:`
|
|
16
|
+
field and the controller is its sole state-and-proof writer.
|
|
17
|
+
- Use `$hapo-sync` for surgical updates to observed file state. A done task
|
|
18
|
+
requires a canonical final inline `## Receipt` with the exact command,
|
|
19
|
+
`Exit: 0`, `Verification: PASS`, runtime-derived Base and Head values, and
|
|
20
|
+
non-empty fenced current output.
|
|
21
|
+
- At C3, show current receipts and unresolved limitations. The user decides
|
|
22
|
+
completion; no command, review, or host state may invent approval or proof.
|
|
23
|
+
|
|
24
|
+
### Legacy Specs compatibility
|
|
25
|
+
|
|
26
|
+
Existing packets containing `spec.json`, nested tasks, or legacy kernel
|
|
27
|
+
artifacts keep their installed adapter, `task_registry`, `semantic_model`,
|
|
28
|
+
`planning_depth`, lane, `execution_tier`, machine authority, separate receipts,
|
|
29
|
+
and closeout contract. Do not migrate them during unrelated process-first work.
|
|
17
30
|
|
|
18
31
|
## Codex caveats
|
|
19
32
|
|
|
@@ -25,4 +38,4 @@
|
|
|
25
38
|
|
|
26
39
|
Codex's native project instruction surface is root `AGENTS.md`. Combined installs keep this Codex block there because no separate Codex project entrypoint is configured by CafeKit. Other runtimes may read the same root file; this shared-root trade-off is intentional and must not be treated as filesystem isolation.
|
|
27
40
|
|
|
28
|
-
**Ownership / ignore contract (fail-safe):** This Codex block is owned by Codex CLI only. If you are Claude Code
|
|
41
|
+
**Ownership / ignore contract (fail-safe):** This Codex block is owned by Codex CLI only. If you are Claude Code or any other runtime, ignore this entire Codex block and consume only CORE plus your native block. If you cannot determine which block is yours, treat the file as CORE-only. Do not treat another runtime's block as instructions.
|
|
@@ -69,6 +69,22 @@ function checkReceiptDetails(featureDir, taskPath, task, runtimeContext) {
|
|
|
69
69
|
if (!policy) return { failures: ['shared_validator'], status: 'missing' };
|
|
70
70
|
return receiptHelper().checkTaskReceipt(featureDir, taskPath, task, runtimeContext, policy);
|
|
71
71
|
}
|
|
72
|
+
function checkWorkflowReceiptDetails(featureDir, taskPath, runtimeContext) {
|
|
73
|
+
const policy = getSharedPolicy();
|
|
74
|
+
const receipt = receiptHelper();
|
|
75
|
+
if (!policy || typeof receipt.checkWorkflowTaskReceipt !== 'function') {
|
|
76
|
+
return { failures: ['shared_validator'], status: 'missing' };
|
|
77
|
+
}
|
|
78
|
+
return receipt.checkWorkflowTaskReceipt(featureDir, taskPath, runtimeContext, policy);
|
|
79
|
+
}
|
|
80
|
+
function checkWorkflowReceiptSet(candidates, projectRoot, runtimeSession) {
|
|
81
|
+
const policy = getSharedPolicy();
|
|
82
|
+
const receipt = receiptHelper();
|
|
83
|
+
if (!policy || typeof receipt.checkWorkflowReceiptSet !== 'function') {
|
|
84
|
+
return { failures: [{ featureName: '<set>', taskPath: '<set>', failures: ['shared_validator'] }] };
|
|
85
|
+
}
|
|
86
|
+
return receipt.checkWorkflowReceiptSet(candidates, projectRoot, runtimeSession, policy);
|
|
87
|
+
}
|
|
72
88
|
function checkReceipt(featureDir, taskPath, task, runtimeContext) {
|
|
73
89
|
const map = {
|
|
74
90
|
unsafe_path: 'a', missing_receipt: 'b', task_status: 'a', completed_at: 'd',
|
|
@@ -91,6 +107,8 @@ module.exports = {
|
|
|
91
107
|
checkFeatureReceipt,
|
|
92
108
|
checkReceipt,
|
|
93
109
|
checkReceiptDetails,
|
|
110
|
+
checkWorkflowReceiptDetails,
|
|
111
|
+
checkWorkflowReceiptSet,
|
|
94
112
|
evidenceBody,
|
|
95
113
|
getSharedPolicy,
|
|
96
114
|
getSharedValidate,
|
|
@@ -100,6 +100,29 @@ function resolveActiveSpec(projectRoot, runtime, explicitFeature, explicitPath)
|
|
|
100
100
|
});
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
function resolveWorkflowCandidate(projectRoot, runtime, explicitFeature, explicitPath) {
|
|
104
|
+
const resolver = sharedResolver();
|
|
105
|
+
if (typeof resolver.resolveWorkflowCandidate !== 'function') {
|
|
106
|
+
return resolveActiveSpec(projectRoot, runtime, explicitFeature, explicitPath);
|
|
107
|
+
}
|
|
108
|
+
if (projectRoot && typeof projectRoot === 'object') {
|
|
109
|
+
return resolver.resolveWorkflowCandidate(projectRoot);
|
|
110
|
+
}
|
|
111
|
+
return resolver.resolveWorkflowCandidate({
|
|
112
|
+
projectRoot,
|
|
113
|
+
runtime,
|
|
114
|
+
explicitFeature,
|
|
115
|
+
explicitPath,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function refineWorkflowGateResolution(resolved) {
|
|
120
|
+
const resolver = sharedResolver();
|
|
121
|
+
return typeof resolver.refineWorkflowGateResolution === 'function'
|
|
122
|
+
? resolver.refineWorkflowGateResolution(resolved)
|
|
123
|
+
: resolved;
|
|
124
|
+
}
|
|
125
|
+
|
|
103
126
|
function findActiveSpec(projectRoot, runtime) {
|
|
104
127
|
const resolved = resolveActiveSpec(projectRoot, runtime);
|
|
105
128
|
if (!resolved) return null;
|
|
@@ -122,6 +145,8 @@ module.exports = {
|
|
|
122
145
|
findAllActiveSpecs,
|
|
123
146
|
findAllSpecCandidates,
|
|
124
147
|
resolveActiveSpec,
|
|
148
|
+
resolveWorkflowCandidate,
|
|
149
|
+
refineWorkflowGateResolution,
|
|
125
150
|
specsDirectory,
|
|
126
151
|
taskStatusMap,
|
|
127
152
|
};
|
|
@@ -12,6 +12,8 @@ const {
|
|
|
12
12
|
const {
|
|
13
13
|
checkFeatureReceipt,
|
|
14
14
|
checkReceiptDetails,
|
|
15
|
+
checkWorkflowReceiptDetails,
|
|
16
|
+
checkWorkflowReceiptSet,
|
|
15
17
|
loadSharedPolicy,
|
|
16
18
|
} = require('./lib/spec-receipt.cjs');
|
|
17
19
|
const { taskStatusMap } = require('./lib/spec-utils.cjs');
|
|
@@ -46,13 +48,33 @@ try {
|
|
|
46
48
|
throw new Error('shared workflow policy lacks completion authority functions');
|
|
47
49
|
}
|
|
48
50
|
const resolver = require('./lib/spec-utils.cjs');
|
|
49
|
-
|
|
50
|
-
resolver,
|
|
51
|
-
projectRoot,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
51
|
+
let resolved = typeof resolver.resolveWorkflowCandidate === 'function'
|
|
52
|
+
? resolver.resolveWorkflowCandidate({ projectRoot, runtime, target: payload, includeCompleted: true })
|
|
53
|
+
: FINAL_STATE.resolveCandidate({ resolver, projectRoot, runtime, payload });
|
|
54
|
+
if (typeof resolver.refineWorkflowGateResolution === 'function') {
|
|
55
|
+
resolved = resolver.refineWorkflowGateResolution(resolved);
|
|
56
|
+
}
|
|
55
57
|
if (!resolved) process.exit(0);
|
|
58
|
+
if (resolved.layoutKind === 'process-v3-completed-set') {
|
|
59
|
+
const configuredGate = runtime.spec?.completion_gate;
|
|
60
|
+
if (configuredGate !== undefined && configuredGate !== true) {
|
|
61
|
+
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.');
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
const checked = checkWorkflowReceiptSet(
|
|
65
|
+
resolved.candidates,
|
|
66
|
+
projectRoot,
|
|
67
|
+
sessionIdentity(payload),
|
|
68
|
+
);
|
|
69
|
+
if (checked.failures.length === 0) process.exit(0);
|
|
70
|
+
const lines = [`Completion gate: ${checked.failures.length} done task(s) lack a verification receipt.`];
|
|
71
|
+
for (const failure of checked.failures) {
|
|
72
|
+
lines.push(`- \`${failure.featureName}/${failure.taskPath}\`: failed check(s) ${failure.failures.join(', ')}`);
|
|
73
|
+
lines.push(` Write a runtime-bound \`## Receipt\` with the planned command in \`specs/${failure.featureName}/${failure.taskPath}\`.`);
|
|
74
|
+
}
|
|
75
|
+
emitBlock(lines.slice(0, 8).join('\n'));
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
56
78
|
if (resolved.error === 'multiple_active' || resolved.error === 'multiple_persisted') {
|
|
57
79
|
process.stdout.write(`${JSON.stringify({
|
|
58
80
|
decision: 'block',
|
|
@@ -76,10 +98,12 @@ try {
|
|
|
76
98
|
}
|
|
77
99
|
if (resolved.error) process.exit(0);
|
|
78
100
|
const active = resolved;
|
|
79
|
-
const
|
|
80
|
-
const
|
|
101
|
+
const activeSpec = active.spec || {};
|
|
102
|
+
const processWorkflow = active.layoutKind === 'process-v3';
|
|
103
|
+
const lifecyclePhase = activeSpec.current_phase || activeSpec.phase;
|
|
104
|
+
const explicitCloseout = ['done', 'completed', 'complete'].includes(activeSpec.status)
|
|
81
105
|
|| ['closeout', 'completion', 'completed', 'complete'].includes(lifecyclePhase);
|
|
82
|
-
if (
|
|
106
|
+
if (!processWorkflow && activeSpec.schema_version === '2.1') {
|
|
83
107
|
const finalState = FINAL_STATE.evaluateCloseout({
|
|
84
108
|
policy: POLICY,
|
|
85
109
|
projectRoot,
|
|
@@ -95,7 +119,7 @@ try {
|
|
|
95
119
|
const runtimeContext = POLICY.deriveRuntimeContext({
|
|
96
120
|
projectRoot,
|
|
97
121
|
specsRoot: active.specsDir,
|
|
98
|
-
specFile: active.specFile || path.join(active.specsDir, active.featureName, 'spec.json'),
|
|
122
|
+
specFile: active.stateFile || active.specFile || path.join(active.specsDir, active.featureName, 'spec.json'),
|
|
99
123
|
featureName: active.featureName,
|
|
100
124
|
runtimeSession: sessionIdentity(payload),
|
|
101
125
|
});
|
|
@@ -104,8 +128,10 @@ try {
|
|
|
104
128
|
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.');
|
|
105
129
|
process.exit(0);
|
|
106
130
|
}
|
|
107
|
-
const registry = active.
|
|
108
|
-
const currentStatuses =
|
|
131
|
+
const registry = active.taskRegistry || activeSpec.task_registry || {};
|
|
132
|
+
const currentStatuses = processWorkflow
|
|
133
|
+
? Object.fromEntries(Object.entries(registry).map(([taskPath, task]) => [taskPath, task?.status || 'pending']))
|
|
134
|
+
: taskStatusMap(activeSpec);
|
|
109
135
|
const cacheFile = path.join(
|
|
110
136
|
projectRoot,
|
|
111
137
|
'.codex',
|
|
@@ -137,7 +163,7 @@ try {
|
|
|
137
163
|
const previous = cacheExists && cacheEntries[runtimeContext.context_id]
|
|
138
164
|
? cacheEntries[runtimeContext.context_id].tasks || {}
|
|
139
165
|
: {};
|
|
140
|
-
const staleFlashTasks = Object.entries(registry)
|
|
166
|
+
const staleFlashTasks = processWorkflow ? [] : Object.entries(registry)
|
|
141
167
|
.filter(([, task]) => POLICY.isStaleFlashDone(task))
|
|
142
168
|
.map(([taskPath]) => taskPath);
|
|
143
169
|
if (staleFlashTasks.length > 0) {
|
|
@@ -154,7 +180,9 @@ try {
|
|
|
154
180
|
const receiptBodies = new Map();
|
|
155
181
|
const failures = allDoneTasks
|
|
156
182
|
.map((taskPath) => {
|
|
157
|
-
const proof =
|
|
183
|
+
const proof = processWorkflow
|
|
184
|
+
? checkWorkflowReceiptDetails(featureDir, taskPath, runtimeContext)
|
|
185
|
+
: checkReceiptDetails(featureDir, taskPath, registry[taskPath], runtimeContext);
|
|
158
186
|
if (proof.body) receiptBodies.set(taskPath, proof.body);
|
|
159
187
|
return {
|
|
160
188
|
taskPath,
|
|
@@ -163,7 +191,7 @@ try {
|
|
|
163
191
|
})
|
|
164
192
|
.filter((result) => result.failures.length);
|
|
165
193
|
|
|
166
|
-
const featureCloseoutRequired = explicitCloseout && allDoneTasks.length > 0;
|
|
194
|
+
const featureCloseoutRequired = !processWorkflow && explicitCloseout && allDoneTasks.length > 0;
|
|
167
195
|
const featureReceipt = featureCloseoutRequired
|
|
168
196
|
? checkFeatureReceipt(featureDir, runtimeContext)
|
|
169
197
|
: null;
|
|
@@ -200,7 +228,9 @@ try {
|
|
|
200
228
|
lines.push(`- \`${result.taskPath}\`: failed check(s) ${result.failures.join(', ')}`);
|
|
201
229
|
lines.push(result.taskPath === 'feature-receipt.md'
|
|
202
230
|
? ` Run final integration proof, then write \`specs/${active.featureName}/feature-receipt.md\`.`
|
|
203
|
-
:
|
|
231
|
+
: processWorkflow
|
|
232
|
+
? ` Write a runtime-bound \`## Receipt\` with command output in \`specs/${active.featureName}/${path.posix.basename(result.taskPath)}\`.`
|
|
233
|
+
: ` Write canonical proof to \`specs/${active.featureName}/receipts/${path.posix.basename(result.taskPath)}\`; legacy \`## Evidence\` remains read-compatible.`);
|
|
204
234
|
}
|
|
205
235
|
if (completionBlocked) {
|
|
206
236
|
lines.push(`- Completion decision unfinished: ${completion.blocker || 'required workflow proof is missing.'}`);
|