@haposoft/cafekit 0.18.0 → 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 +1 -1
- package/bin/__tests__/codex-hooks.test.js +169 -236
- package/bin/__tests__/codex-native.test.js +54 -114
- package/bin/__tests__/develop-contract.test.js +101 -1361
- package/bin/__tests__/legacy-authority-decoupled.test.js +14 -19
- package/bin/__tests__/package-inventory.test.js +1 -395
- package/bin/__tests__/spec-narrowing.test.js +30 -134
- package/bin/__tests__/specs-v2-execution-closeout.test.js +0 -372
- package/bin/__tests__/test-runtime-dependency-closure.cjs +1 -2
- package/bin/__tests__/validator-grounding.test.js +17 -2929
- package/bin/lib/managed-writer.js +2 -3
- package/bin/phases/codex-runtime.js +6 -3
- package/bin/phases/copy-payload.js +13 -28
- package/bin/phases/omp-runtime.js +6 -3
- package/package.json +2 -3
- package/src/claude/agents/code-auditor.md +9 -6
- package/src/claude/agents/debugger.md +3 -9
- package/src/claude/agents/docs-keeper.md +4 -7
- package/src/claude/agents/git-ops.md +2 -4
- package/src/claude/agents/implementer.md +6 -7
- package/src/claude/agents/inspector.md +2 -0
- package/src/claude/agents/project-manager.md +2 -2
- package/src/claude/agents/spec-maker.md +4 -4
- package/src/claude/agents/test-runner.md +8 -11
- package/src/claude/agents/ui-ux-designer.md +0 -1
- package/src/claude/hooks/__tests__/spec-gate.test.js +170 -330
- package/src/claude/hooks/__tests__/spec-state-touched.test.js +26 -0
- package/src/claude/hooks/spec-gate.cjs +8 -63
- package/src/claude/hooks/spec-state.cjs +47 -38
- package/src/claude/migration-manifest.json +12 -7
- 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 +1 -2
- package/src/claude/rules/workflow.md +1 -1
- package/src/claude/scripts/spec-receipt.cjs +0 -114
- package/src/claude/scripts/spec-resolver.cjs +41 -457
- package/src/claude/scripts/workflow-policy.cjs +3 -1801
- package/src/claude/skills/agent-browser/SKILL.md +1 -1
- package/src/claude/skills/chrome-devtools/SKILL.md +14 -31
- package/src/claude/skills/code-review/SKILL.md +25 -108
- package/src/claude/skills/code-review/references/spec-compliance-review.md +4 -7
- package/src/claude/skills/develop/SKILL.md +22 -70
- package/src/claude/skills/develop/references/subagent-patterns.md +0 -6
- package/src/claude/skills/research/SKILL.md +24 -55
- package/src/claude/skills/sync/SKILL.md +12 -34
- package/src/claude/skills/sync/references/rebind-and-audit.md +2 -2
- package/src/claude/skills/test/SKILL.md +39 -123
- package/src/claude/skills/test/references/execution-strategy.md +11 -22
- package/src/claude/skills/ui-ux/SKILL.md +1 -1
- package/src/claude/skills/ui-ux-pro-max/SKILL.md +24 -79
- package/src/claude/skills/web-testing/SKILL.md +2 -20
- package/src/codex/hooks/lib/spec-receipt.cjs +2 -30
- package/src/codex/hooks/lib/spec-utils.cjs +6 -45
- package/src/codex/hooks/spec-gate.cjs +11 -68
- package/src/codex/hooks/spec-state.cjs +37 -41
- package/src/common/AGENTS.md +4 -10
- 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/lib/context.cjs +0 -527
- package/src/claude/hooks/lib/detect.cjs +0 -474
- 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 -3914
- package/src/claude/skills/code-review/references/verification-gate.md +0 -41
- 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
|
@@ -132,3 +132,29 @@ test('a prompt touch is remembered even when two packets are active', () => {
|
|
|
132
132
|
assert.match(fx.prompt('s7', 'Continue'), /auth-flow/);
|
|
133
133
|
});
|
|
134
134
|
});
|
|
135
|
+
|
|
136
|
+
function writeLegacyPacket(root, name) {
|
|
137
|
+
const dir = path.join(root, 'specs', name);
|
|
138
|
+
fs.mkdirSync(path.join(dir, 'tasks'), { recursive: true });
|
|
139
|
+
fs.writeFileSync(path.join(dir, 'spec.json'), JSON.stringify({ feature_name: name, status: 'in_progress' }));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const LEGACY_NOTICE = /`specs\/old` is a legacy Specs packet without plan\.md; CafeKit no longer reads it/;
|
|
143
|
+
|
|
144
|
+
test('a leftover legacy packet is noticed once per session, before the touch filter', () => {
|
|
145
|
+
within(fixture(['auth-flow']), (fx) => {
|
|
146
|
+
writeLegacyPacket(fx.root, 'old');
|
|
147
|
+
const first = fx.prompt('s1', 'Continue');
|
|
148
|
+
assert.match(first, LEGACY_NOTICE);
|
|
149
|
+
assert.strictEqual(first.match(new RegExp(LEGACY_NOTICE.source, 'g')).length, 1);
|
|
150
|
+
assert.strictEqual(fx.prompt('s1', 'Continue'), '', 'the same session is not told twice');
|
|
151
|
+
assert.match(fx.prompt('s2', 'Continue'), LEGACY_NOTICE, 'a new session is told once');
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('a legacy packet is noticed even when it is the only packet', () => {
|
|
156
|
+
within(fixture([]), (fx) => {
|
|
157
|
+
writeLegacyPacket(fx.root, 'old');
|
|
158
|
+
assert.match(fx.prompt('s1', 'Continue'), LEGACY_NOTICE);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
@@ -97,8 +97,7 @@ try {
|
|
|
97
97
|
RESOLVER = require(path.join(__dirname, '..', 'scripts', 'spec-resolver.cjs'));
|
|
98
98
|
RECEIPT = require(path.join(__dirname, '..', 'scripts', 'spec-receipt.cjs'));
|
|
99
99
|
if (typeof POLICY.validateCanonicalReceipt !== 'function'
|
|
100
|
-
|| typeof
|
|
101
|
-
|| typeof RECEIPT.checkTaskReceipt !== 'function'
|
|
100
|
+
|| typeof RECEIPT.checkWorkflowTaskReceipt !== 'function'
|
|
102
101
|
|| typeof RECEIPT.checkWorkflowReceiptSet !== 'function'
|
|
103
102
|
|| typeof RESOLVER.resolveWorkflowCandidate !== 'function') {
|
|
104
103
|
throw new Error('shared workflow policy lacks completion authority functions');
|
|
@@ -164,7 +163,7 @@ try {
|
|
|
164
163
|
if (resolved.error === 'invalid_specs') {
|
|
165
164
|
process.stdout.write(JSON.stringify({
|
|
166
165
|
decision: 'block',
|
|
167
|
-
reason: `Completion gate: invalid
|
|
166
|
+
reason: `Completion gate: invalid workflow packet detected (${resolved.candidates.join(', ')}): ${resolved.reason}. Fix the packet's plan.md or task files before completing tasks.`,
|
|
168
167
|
}) + '\n');
|
|
169
168
|
process.exit(0);
|
|
170
169
|
}
|
|
@@ -177,22 +176,12 @@ try {
|
|
|
177
176
|
}
|
|
178
177
|
if (resolved.error) process.exit(0);
|
|
179
178
|
|
|
180
|
-
const activeSpec = resolved.spec || {};
|
|
181
|
-
const processWorkflow = resolved.layoutKind === 'process-v3';
|
|
182
179
|
const featureName = resolved.featureName;
|
|
183
180
|
const specsPath = resolved.specsDir;
|
|
184
|
-
const lifecyclePhase = activeSpec.current_phase || activeSpec.phase;
|
|
185
|
-
const explicitCloseout = ['done', 'completed', 'complete'].includes(activeSpec.status)
|
|
186
|
-
|| ['closeout', 'completion', 'completed', 'complete'].includes(lifecyclePhase);
|
|
187
|
-
// Legacy 2.1 closeout is retired; a 2.1 packet mid-execution keeps the ordinary receipt checks.
|
|
188
|
-
if (!processWorkflow && activeSpec.schema_version === '2.1' && explicitCloseout) {
|
|
189
|
-
emitBlock('Completion gate: Legacy spec.json closeout is no longer supported; move the packet to plan.md with flat task files');
|
|
190
|
-
process.exit(0);
|
|
191
|
-
}
|
|
192
181
|
const runtimeContext = POLICY.deriveRuntimeContext({
|
|
193
182
|
projectRoot: baseDir,
|
|
194
183
|
specsRoot: specsPath,
|
|
195
|
-
specFile: resolved.stateFile
|
|
184
|
+
specFile: resolved.stateFile,
|
|
196
185
|
featureName,
|
|
197
186
|
runtimeSession: sessionIdentity(payload),
|
|
198
187
|
});
|
|
@@ -202,7 +191,7 @@ try {
|
|
|
202
191
|
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.');
|
|
203
192
|
process.exit(0);
|
|
204
193
|
}
|
|
205
|
-
const taskRegistry = resolved.taskRegistry ||
|
|
194
|
+
const taskRegistry = resolved.taskRegistry || {};
|
|
206
195
|
const cacheFile = path.join(require('./lib/hook-state-dir.cjs').hookStateDir(), 'spec-gate-last.json');
|
|
207
196
|
const cacheExists = fs.existsSync(cacheFile);
|
|
208
197
|
let cache = {};
|
|
@@ -215,17 +204,6 @@ try {
|
|
|
215
204
|
currentStatuses[tp] = task?.status || 'pending';
|
|
216
205
|
}
|
|
217
206
|
|
|
218
|
-
const staleFlashTasks = processWorkflow ? [] : Object.entries(taskRegistry)
|
|
219
|
-
.filter(([, task]) => POLICY.isStaleFlashDone(task))
|
|
220
|
-
.map(([taskPath]) => taskPath);
|
|
221
|
-
if (staleFlashTasks.length > 0) {
|
|
222
|
-
process.stdout.write(JSON.stringify({
|
|
223
|
-
decision: 'block',
|
|
224
|
-
reason: `Completion gate: ${staleFlashTasks.length} task(s) marked done with FLASH_UNVERIFIED (${staleFlashTasks.join(', ')}). Run /cf:test for exact proof, then use explicit sync-finalize.`
|
|
225
|
-
}) + '\n');
|
|
226
|
-
process.exit(0);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
207
|
// Cache hardening: every Stop re-reads and re-checks the receipt of every
|
|
230
208
|
// task currently marked done, so a cached PASS cannot hide later mutations
|
|
231
209
|
// (deletion, placeholder, changed Verification/Command/Exit, missing
|
|
@@ -241,29 +219,11 @@ try {
|
|
|
241
219
|
const featureDir = path.join(specsPath, featureName);
|
|
242
220
|
const failures = allDoneTasks
|
|
243
221
|
.map((taskPath) => {
|
|
244
|
-
const proof =
|
|
245
|
-
? RECEIPT.checkWorkflowTaskReceipt(featureDir, taskPath, runtimeContext, POLICY)
|
|
246
|
-
: RECEIPT.checkTaskReceipt(featureDir, taskPath, taskRegistry[taskPath], runtimeContext, POLICY);
|
|
222
|
+
const proof = RECEIPT.checkWorkflowTaskReceipt(featureDir, taskPath, runtimeContext, POLICY);
|
|
247
223
|
return { taskPath, fails: proof.failures, body: proof.body };
|
|
248
224
|
})
|
|
249
225
|
.filter((f) => f.fails.length > 0);
|
|
250
226
|
|
|
251
|
-
const featureCloseoutRequired = !processWorkflow && explicitCloseout && allDoneTasks.length > 0;
|
|
252
|
-
const featureReceipt = featureCloseoutRequired
|
|
253
|
-
? RECEIPT.checkFeatureReceipt(featureDir, runtimeContext, POLICY)
|
|
254
|
-
: null;
|
|
255
|
-
if (featureReceipt && featureReceipt.failures.length) {
|
|
256
|
-
failures.push({ taskPath: 'feature-receipt.md', fails: featureReceipt.failures });
|
|
257
|
-
}
|
|
258
|
-
const completion = featureCloseoutRequired && featureReceipt?.failures.length === 0
|
|
259
|
-
&& Object.prototype.hasOwnProperty.call(activeSpec, 'workflow_policy')
|
|
260
|
-
? POLICY.completionDecisionForSpec(activeSpec, {
|
|
261
|
-
runtimeContext,
|
|
262
|
-
executionReceipt: featureReceipt.body,
|
|
263
|
-
taskContext: {},
|
|
264
|
-
})
|
|
265
|
-
: null;
|
|
266
|
-
|
|
267
227
|
// Always persist status transitions, including done → pending. Keep
|
|
268
228
|
// failing done tasks at their previous cached status so a stale valid
|
|
269
229
|
// entry cannot mask a mutated receipt on the next Stop; with
|
|
@@ -279,27 +239,12 @@ try {
|
|
|
279
239
|
fs.writeFileSync(cacheFile, JSON.stringify(cache));
|
|
280
240
|
} catch { /* fail-open */ }
|
|
281
241
|
|
|
282
|
-
|
|
283
|
-
if (failures.length === 0 && !completionBlocked) process.exit(0);
|
|
242
|
+
if (failures.length === 0) process.exit(0);
|
|
284
243
|
|
|
285
|
-
const lines = [
|
|
286
|
-
failures.length > 0
|
|
287
|
-
? `⚠️ Completion gate: ${failures.length} done task(s) lack a verification receipt.`
|
|
288
|
-
: '⚠️ Completion gate: workflow completion proof is incomplete.',
|
|
289
|
-
];
|
|
244
|
+
const lines = [`⚠️ Completion gate: ${failures.length} done task(s) lack a verification receipt.`];
|
|
290
245
|
for (const { taskPath, fails, body } of failures) {
|
|
291
246
|
lines.push(`- \`${taskPath}\`: failed check(s) ${fails.join(', ')}`);
|
|
292
|
-
lines.push(taskPath === '
|
|
293
|
-
? ` Fix: run final integration proof, then write \`specs/${featureName}/feature-receipt.md\`.`
|
|
294
|
-
: processWorkflow
|
|
295
|
-
? ` Fix in \`specs/${featureName}/${path.posix.basename(taskPath)}\`: ${(typeof RECEIPT.receiptFixHint === 'function' && RECEIPT.receiptFixHint(fails, body)) || 'write a runtime-bound `## Receipt` with command output'}.`
|
|
296
|
-
: ` Fix: write canonical proof to \`specs/${featureName}/receipts/${path.posix.basename(taskPath)}\`; legacy \`## Evidence\` remains read-compatible.`);
|
|
297
|
-
}
|
|
298
|
-
if (completionBlocked) {
|
|
299
|
-
lines.push(`- Completion decision unfinished: ${completion.blocker || 'required workflow proof is missing.'}`);
|
|
300
|
-
if (Array.isArray(completion.missingProof) && completion.missingProof.length > 0) {
|
|
301
|
-
lines.push(` Missing proof: ${completion.missingProof.join(', ')}`);
|
|
302
|
-
}
|
|
247
|
+
lines.push(` Fix in \`specs/${featureName}/${path.posix.basename(taskPath)}\`: ${(typeof RECEIPT.receiptFixHint === 'function' && RECEIPT.receiptFixHint(fails, body)) || 'write a runtime-bound `## Receipt` with command output'}.`);
|
|
303
248
|
}
|
|
304
249
|
process.stdout.write(JSON.stringify({ decision: 'block', reason: lines.slice(0, 8).join('\n') }) + '\n');
|
|
305
250
|
process.exit(0);
|
|
@@ -96,7 +96,7 @@ try {
|
|
|
96
96
|
POLICY = require(path.join(__dirname, '..', 'scripts', 'workflow-policy.cjs'));
|
|
97
97
|
RESOLVER = require(path.join(__dirname, '..', 'scripts', 'spec-resolver.cjs'));
|
|
98
98
|
RECEIPT = require(path.join(__dirname, '..', 'scripts', 'spec-receipt.cjs'));
|
|
99
|
-
if (typeof
|
|
99
|
+
if (typeof RESOLVER.resolveWorkflowCandidate !== 'function') throw new Error('shared spec resolver has no resolveWorkflowCandidate function');
|
|
100
100
|
} catch (error) {
|
|
101
101
|
logCrash(error);
|
|
102
102
|
emitControlledFailure(error.message);
|
|
@@ -167,6 +167,38 @@ try {
|
|
|
167
167
|
process.exit(0);
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
// A legacy packet (a specs/<x>/ directory without plan.md that the old flow left
|
|
171
|
+
// behind) is no longer read. Say so once per session instead of on every prompt.
|
|
172
|
+
const legacyPackets = typeof RESOLVER.findLegacyPackets === 'function'
|
|
173
|
+
? RESOLVER.findLegacyPackets(baseDir, runtime)
|
|
174
|
+
: [];
|
|
175
|
+
if (legacyPackets.length > 0) {
|
|
176
|
+
const sessionKey = sessionIdentity(payload);
|
|
177
|
+
const noticeFile = sessionKey
|
|
178
|
+
? path.join(
|
|
179
|
+
require('./lib/hook-state-dir.cjs').hookStateDir(),
|
|
180
|
+
`legacy-notice-${require('crypto').createHash('sha256').update(sessionKey).digest('hex')}.json`,
|
|
181
|
+
)
|
|
182
|
+
: null;
|
|
183
|
+
let noticed = [];
|
|
184
|
+
if (noticeFile) {
|
|
185
|
+
try { noticed = JSON.parse(fs.readFileSync(noticeFile, 'utf8')); } catch { noticed = []; }
|
|
186
|
+
if (!Array.isArray(noticed)) noticed = [];
|
|
187
|
+
}
|
|
188
|
+
const fresh = legacyPackets.filter((name) => !noticed.includes(name));
|
|
189
|
+
if (fresh.length > 0) {
|
|
190
|
+
for (const name of fresh) {
|
|
191
|
+
console.log(`\n> ⚠️ \`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.`);
|
|
192
|
+
}
|
|
193
|
+
if (noticeFile) {
|
|
194
|
+
try {
|
|
195
|
+
fs.mkdirSync(path.dirname(noticeFile), { recursive: true });
|
|
196
|
+
fs.writeFileSync(noticeFile, JSON.stringify([...noticed, ...fresh].sort()));
|
|
197
|
+
} catch { /* an unwritable state dir only repeats the notice */ }
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
170
202
|
if (touchFilter) {
|
|
171
203
|
let packets = [];
|
|
172
204
|
try {
|
|
@@ -177,10 +209,7 @@ try {
|
|
|
177
209
|
if (named.length) addTouched(named);
|
|
178
210
|
}
|
|
179
211
|
|
|
180
|
-
const
|
|
181
|
-
? RESOLVER.resolveWorkflowCandidate
|
|
182
|
-
: RESOLVER.resolveActiveSpec;
|
|
183
|
-
const resolved = resolveWorkflow({ projectRoot: baseDir, runtime, explicitFeature, explicitPath });
|
|
212
|
+
const resolved = RESOLVER.resolveWorkflowCandidate({ projectRoot: baseDir, runtime, explicitFeature, explicitPath });
|
|
184
213
|
|
|
185
214
|
if (!resolved) {
|
|
186
215
|
process.exit(0); // No active spec, do nothing
|
|
@@ -194,7 +223,7 @@ try {
|
|
|
194
223
|
|
|
195
224
|
if (resolved.error === 'invalid_specs') {
|
|
196
225
|
const cands = resolved.candidates.join(', ');
|
|
197
|
-
console.log(`\n> ⚠️ Invalid
|
|
226
|
+
console.log(`\n> ⚠️ Invalid workflow packet detected: ${cands}. ${resolved.reason}. Fix the packet's plan.md or task files before continuing. Tollgate paused.\n`);
|
|
198
227
|
process.exit(0);
|
|
199
228
|
}
|
|
200
229
|
|
|
@@ -208,22 +237,19 @@ try {
|
|
|
208
237
|
process.exit(0);
|
|
209
238
|
}
|
|
210
239
|
|
|
211
|
-
const activeSpec = resolved.spec || {};
|
|
212
|
-
const processWorkflow = resolved.layoutKind === 'process-v3';
|
|
213
240
|
const featureName = resolved.featureName;
|
|
214
241
|
if (touchFilter && !readTouched().has(featureName)) process.exit(0);
|
|
215
242
|
const specsPath = resolved.specsDir;
|
|
216
243
|
const runtimeContext = POLICY.deriveRuntimeContext({
|
|
217
244
|
projectRoot: baseDir,
|
|
218
245
|
specsRoot: specsPath,
|
|
219
|
-
specFile: resolved.stateFile
|
|
246
|
+
specFile: resolved.stateFile,
|
|
220
247
|
featureName,
|
|
221
248
|
runtimeSession: sessionIdentity(payload),
|
|
222
249
|
});
|
|
223
250
|
|
|
224
|
-
const phase = resolved.phase ||
|
|
225
|
-
const taskRegistry = resolved.taskRegistry ||
|
|
226
|
-
const flashTasks = POLICY.flashState(taskRegistry);
|
|
251
|
+
const phase = resolved.phase || 'unknown';
|
|
252
|
+
const taskRegistry = resolved.taskRegistry || {};
|
|
227
253
|
const taskEntries = Object.entries(taskRegistry);
|
|
228
254
|
const taskCounts = taskEntries.reduce((acc, [, task]) => {
|
|
229
255
|
const status = task?.status || 'pending';
|
|
@@ -231,10 +257,7 @@ try {
|
|
|
231
257
|
return acc;
|
|
232
258
|
}, {});
|
|
233
259
|
const featureDir = path.join(specsPath, featureName);
|
|
234
|
-
const
|
|
235
|
-
const dependencyProof = processWorkflow
|
|
236
|
-
? RECEIPT.workflowDependencyProofState(featureDir, taskRegistry, runtimeContext, POLICY)
|
|
237
|
-
: {};
|
|
260
|
+
const dependencyProof = RECEIPT.workflowDependencyProofState(featureDir, taskRegistry, runtimeContext, POLICY);
|
|
238
261
|
const taskState = taskEntries.map(([taskPath, task]) => [
|
|
239
262
|
taskPath,
|
|
240
263
|
task?.status || 'pending',
|
|
@@ -250,12 +273,9 @@ try {
|
|
|
250
273
|
const nextUnblocked = taskEntries.find(([, task]) => {
|
|
251
274
|
const status = task?.status || 'pending';
|
|
252
275
|
const deps = Array.isArray(task?.dependencies) ? task.dependencies : [];
|
|
253
|
-
return resolved.queueReady !== false && status === 'pending'
|
|
254
|
-
|
|
255
|
-
));
|
|
276
|
+
return resolved.queueReady !== false && status === 'pending'
|
|
277
|
+
&& deps.every((dep) => dependencyProof[dep]?.eligible === true);
|
|
256
278
|
});
|
|
257
|
-
const featureReceiptPresent = !processWorkflow
|
|
258
|
-
&& RECEIPT.safeRead(featureDir, 'feature-receipt.md').status === 'ok';
|
|
259
279
|
|
|
260
280
|
// ── State-change gate: only emit the full tollgate when spec state changed ──
|
|
261
281
|
// The cache is shared by installed hook copies, so its key must carry the
|
|
@@ -278,7 +298,6 @@ try {
|
|
|
278
298
|
proofState,
|
|
279
299
|
done: taskCounts.done || 0,
|
|
280
300
|
total: taskEntries.length,
|
|
281
|
-
featureReceiptPresent,
|
|
282
301
|
});
|
|
283
302
|
const cacheFile = path.join(require('./lib/hook-state-dir.cjs').hookStateDir(), 'tollgate-last.txt');
|
|
284
303
|
|
|
@@ -288,11 +307,10 @@ try {
|
|
|
288
307
|
}
|
|
289
308
|
|
|
290
309
|
if (stateKey && lastKey === stateKey) {
|
|
291
|
-
const
|
|
292
|
-
const migration = processWorkflow && resolved.queueReady === false
|
|
310
|
+
const migration = resolved.queueReady === false
|
|
293
311
|
? ' Add `Specs-Contract: process-first-ready-v1` after the plan title before tasks can enter Next.'
|
|
294
312
|
: '';
|
|
295
|
-
console.log(`\n> 🔵 Spec \`${featureName}\` @ \`${phase}\` (${taskCounts.done || 0}/${taskEntries.length} tasks done). Tollgate active — sync
|
|
313
|
+
console.log(`\n> 🔵 Spec \`${featureName}\` @ \`${phase}\` (${taskCounts.done || 0}/${taskEntries.length} tasks done). Tollgate active — sync task Markdown when state changes.${migration}\n`);
|
|
296
314
|
process.exit(0);
|
|
297
315
|
}
|
|
298
316
|
|
|
@@ -312,20 +330,11 @@ try {
|
|
|
312
330
|
if (nextUnblocked) {
|
|
313
331
|
lines.push(`- Next unblocked: \`${nextUnblocked[0]}\``);
|
|
314
332
|
}
|
|
315
|
-
if (
|
|
316
|
-
lines.push(
|
|
317
|
-
}
|
|
318
|
-
if (processWorkflow) {
|
|
319
|
-
if (resolved.queueReady === false) {
|
|
320
|
-
lines.push('- Migration required: add `Specs-Contract: process-first-ready-v1` after the plan title before tasks can enter Next.');
|
|
321
|
-
}
|
|
322
|
-
lines.push('- Sync each flat task `Status:` only after verified work; task proof belongs in that task\'s inline `## Receipt`.');
|
|
323
|
-
lines.push(`- Workflow source: \`specs/${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.`);
|
|
324
|
-
} else {
|
|
325
|
-
lines.push('- Sync `spec.json` + task Markdown status after verified work; task proof belongs in `receipts/<task-basename>.md`.');
|
|
326
|
-
lines.push(`- Create \`feature-receipt.md\` once after final integration proof${featureReceiptPresent ? ' (present)' : ' (not required before closeout)'}.`);
|
|
327
|
-
lines.push(`- Validate with \`node ${runtimeDirName()}/scripts/validate-spec-output.cjs specs/${featureName}\`; hooks revalidate receipt bytes but never grant approval.`);
|
|
333
|
+
if (resolved.queueReady === false) {
|
|
334
|
+
lines.push('- Migration required: add `Specs-Contract: process-first-ready-v1` after the plan title before tasks can enter Next.');
|
|
328
335
|
}
|
|
336
|
+
lines.push('- Sync each flat task `Status:` only after verified work; task proof belongs in that task\'s inline `## Receipt`.');
|
|
337
|
+
lines.push(`- Workflow source: \`specs/${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.`);
|
|
329
338
|
lines.push('');
|
|
330
339
|
|
|
331
340
|
console.log(lines.join('\n'));
|
|
@@ -59,15 +59,10 @@
|
|
|
59
59
|
"validate-docs.cjs",
|
|
60
60
|
"validate-docs-reconstruct.cjs",
|
|
61
61
|
"generate-skill-catalog.cjs",
|
|
62
|
-
"validate-spec-output.cjs",
|
|
63
62
|
"scan-staged-secrets.cjs",
|
|
64
63
|
"workflow-policy.cjs",
|
|
65
64
|
"spec-resolver.cjs",
|
|
66
65
|
"spec-receipt.cjs",
|
|
67
|
-
"spec-ground.cjs",
|
|
68
|
-
"spec-semantic-model.cjs",
|
|
69
|
-
"spec-final-state.cjs",
|
|
70
|
-
"spec-readiness.cjs",
|
|
71
66
|
"provenance.cjs"
|
|
72
67
|
]
|
|
73
68
|
},
|
|
@@ -97,6 +92,18 @@
|
|
|
97
92
|
"codex": ["agents/god_developer.toml"]
|
|
98
93
|
},
|
|
99
94
|
"runtimeFiles": [
|
|
95
|
+
"scripts/validate-spec-output.cjs",
|
|
96
|
+
"scripts/spec-ground.cjs",
|
|
97
|
+
"scripts/spec-authoring-digest.cjs",
|
|
98
|
+
"scripts/spec-semantic-model.cjs",
|
|
99
|
+
"scripts/spec-final-state.cjs",
|
|
100
|
+
"skills/code-review/references/verification-gate.md",
|
|
101
|
+
"scripts/change-firewall.cjs",
|
|
102
|
+
"scripts/spec-scaffold.cjs",
|
|
103
|
+
"scripts/spec-readiness.cjs",
|
|
104
|
+
"scripts/spec-authoring-validation.cjs",
|
|
105
|
+
"hooks/lib/context.cjs",
|
|
106
|
+
"hooks/lib/detect.cjs",
|
|
100
107
|
"hooks/skill-router.cjs",
|
|
101
108
|
"hooks/lib/skill-router-routes.cjs",
|
|
102
109
|
"scripts/browser-tool.cjs",
|
|
@@ -161,8 +168,6 @@
|
|
|
161
168
|
"hooks/lib/counter.cjs",
|
|
162
169
|
"hooks/lib/git.cjs",
|
|
163
170
|
"hooks/lib/config.cjs",
|
|
164
|
-
"hooks/lib/context.cjs",
|
|
165
|
-
"hooks/lib/detect.cjs",
|
|
166
171
|
"hooks/lib/hook-state-dir.cjs",
|
|
167
172
|
"hooks/lib/runtime-dir.cjs",
|
|
168
173
|
"hooks/lib/hook-payload.cjs",
|
|
@@ -10,18 +10,18 @@ You are teaching someone who has NEVER written a single line of code. They don't
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Rules
|
|
14
14
|
|
|
15
15
|
### Language Rules
|
|
16
16
|
1. **MUST** use at least ONE real-world analogy per concept (cooking recipes, LEGO blocks, labeled boxes, etc.)
|
|
17
17
|
2. **MUST** define EVERY technical term on first use with a simple comparison
|
|
18
18
|
3. **MUST** spell out ALL acronyms and explain them (e.g., "API - Application Programming Interface - think of it like a waiter taking your order to the kitchen")
|
|
19
19
|
4. **MUST** use "we" language to create partnership ("Let's try...", "We can...")
|
|
20
|
-
5. **MUST** end
|
|
20
|
+
5. **MUST** end explanations with a short check-in, in the user's language, naming the topic just covered
|
|
21
21
|
|
|
22
22
|
### Code Rules
|
|
23
|
-
1. **MUST** add a comment explaining what
|
|
24
|
-
2. **MUST** keep code
|
|
23
|
+
1. **MUST** add a comment explaining what every line does in code shown in chat (code written to files follows the project's comment rules)
|
|
24
|
+
2. **MUST** keep each code block to one small idea - break larger examples into steps
|
|
25
25
|
3. **MUST** use descriptive variable names that read like plain English (e.g., `numberOfApples` not `n`)
|
|
26
26
|
4. **MUST** show the expected output/result after EVERY code block
|
|
27
27
|
5. **MUST** start with the SIMPLEST possible version, then add complexity gradually
|
|
@@ -29,17 +29,17 @@ You are teaching someone who has NEVER written a single line of code. They don't
|
|
|
29
29
|
### Teaching Rules
|
|
30
30
|
1. **MUST** explain the "why" BEFORE the "how"
|
|
31
31
|
2. **MUST** use visual metaphors and real-world comparisons
|
|
32
|
-
3. **MUST**
|
|
32
|
+
3. **MUST** acknowledge progress specifically when the user gets something right
|
|
33
33
|
4. **MUST** encourage experimentation: "Try changing X to see what happens!"
|
|
34
34
|
5. **MUST** preemptively explain anything that might be confusing
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## Avoid at this level
|
|
39
39
|
|
|
40
40
|
1. **NEVER** assume they know ANY programming concept
|
|
41
41
|
2. **NEVER** use technical jargon without explaining it first
|
|
42
|
-
3. **NEVER** show code
|
|
42
|
+
3. **NEVER** show a code block that teaches more than one idea at once
|
|
43
43
|
4. **NEVER** use acronyms without spelling them out AND explaining
|
|
44
44
|
5. **NEVER** skip steps - break everything into the smallest pieces
|
|
45
45
|
6. **NEVER** use phrases like "obviously", "simply", "just", "easy"
|
|
@@ -50,7 +50,7 @@ You are teaching someone who has NEVER written a single line of code. They don't
|
|
|
50
50
|
|
|
51
51
|
## Required Response Structure
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
Use this structure for explanations; answer quick questions and report tool work directly:
|
|
54
54
|
|
|
55
55
|
### 1. Big Picture (1-2 sentences)
|
|
56
56
|
What are we trying to accomplish? Use everyday language.
|
|
@@ -70,7 +70,7 @@ Give them something simple to experiment with.
|
|
|
70
70
|
"Try changing the number 5 to 10 and see what happens!"
|
|
71
71
|
|
|
72
72
|
### 5. Check-In
|
|
73
|
-
|
|
73
|
+
A short question, in the user's language, about the topic just covered.
|
|
74
74
|
|
|
75
75
|
---
|
|
76
76
|
|
|
@@ -10,7 +10,7 @@ You are mentoring a junior developer who understands basic programming (variable
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Rules
|
|
14
14
|
|
|
15
15
|
### Explanation Rules
|
|
16
16
|
1. **MUST** always explain WHY before showing HOW
|
|
@@ -24,7 +24,7 @@ You are mentoring a junior developer who understands basic programming (variable
|
|
|
24
24
|
2. **MUST** use meaningful variable/function names that express intent
|
|
25
25
|
3. **MUST** show before/after comparisons when refactoring or improving code
|
|
26
26
|
4. **MUST** explain what each import/dependency does on first use
|
|
27
|
-
5. **MUST** keep code
|
|
27
|
+
5. **MUST** keep code examples focused - split larger examples
|
|
28
28
|
|
|
29
29
|
### Teaching Rules
|
|
30
30
|
1. **MUST** define technical terms on first use (briefly, not ELI5-level)
|
|
@@ -35,7 +35,7 @@ You are mentoring a junior developer who understands basic programming (variable
|
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## Avoid at this level
|
|
39
39
|
|
|
40
40
|
1. **NEVER** assume they know advanced patterns (design patterns, architecture)
|
|
41
41
|
2. **NEVER** skip explaining WHY - always give reasoning
|
|
@@ -10,7 +10,7 @@ You are collaborating with a solid developer who has 3-5 years of experience. Th
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Rules
|
|
14
14
|
|
|
15
15
|
### Communication Rules
|
|
16
16
|
1. **MUST** discuss design patterns and when/why to apply them
|
|
@@ -29,13 +29,13 @@ You are collaborating with a solid developer who has 3-5 years of experience. Th
|
|
|
29
29
|
### Growth Rules
|
|
30
30
|
1. **MUST** encourage independent problem-solving ("Consider how you might...")
|
|
31
31
|
2. **MUST** mention relevant design patterns by name
|
|
32
|
-
3. **MUST**
|
|
32
|
+
3. **MUST** point out an obvious problem you notice, without expanding the work beyond what was asked
|
|
33
33
|
4. **MUST** discuss how this fits into larger system architecture
|
|
34
34
|
5. **MUST** balance theory with practical implementation
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## Avoid at this level
|
|
39
39
|
|
|
40
40
|
1. **NEVER** explain basic programming concepts (loops, functions, variables)
|
|
41
41
|
2. **NEVER** over-explain simple syntax
|
|
@@ -10,7 +10,7 @@ You are collaborating with a senior engineer (5-8 years experience) who thinks i
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Rules
|
|
14
14
|
|
|
15
15
|
### Communication Rules
|
|
16
16
|
1. **MUST** lead with trade-offs and decision points
|
|
@@ -35,7 +35,7 @@ You are collaborating with a senior engineer (5-8 years experience) who thinks i
|
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## Avoid at this level
|
|
39
39
|
|
|
40
40
|
1. **NEVER** explain basic or intermediate concepts
|
|
41
41
|
2. **NEVER** add "Key Takeaways" or summary sections
|
|
@@ -10,17 +10,17 @@ You are advising a technical leader (8-15 years experience) who owns systems end
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Rules
|
|
14
14
|
|
|
15
15
|
### Communication Rules
|
|
16
|
-
1. **MUST** lead with executive summary
|
|
16
|
+
1. **MUST** lead with a short executive summary
|
|
17
17
|
2. **MUST** quantify everything possible (latency, throughput, cost, effort)
|
|
18
18
|
3. **MUST** be explicit about assumptions, unknowns, and confidence levels
|
|
19
19
|
4. **MUST** identify decisions that need stakeholder alignment
|
|
20
20
|
5. **MUST** consider cross-team and cross-system dependencies
|
|
21
21
|
|
|
22
22
|
### Risk Rules
|
|
23
|
-
1. **MUST** include
|
|
23
|
+
1. **MUST** include a likelihood × impact risk assessment when recommending a change
|
|
24
24
|
2. **MUST** identify single points of failure
|
|
25
25
|
3. **MUST** propose mitigation strategies for high-risk items
|
|
26
26
|
4. **MUST** flag security, compliance, and legal implications
|
|
@@ -42,7 +42,7 @@ You are advising a technical leader (8-15 years experience) who owns systems end
|
|
|
42
42
|
|
|
43
43
|
---
|
|
44
44
|
|
|
45
|
-
##
|
|
45
|
+
## Avoid at this level
|
|
46
46
|
|
|
47
47
|
1. **NEVER** explain implementation details unless asked
|
|
48
48
|
2. **NEVER** show trivial code - assume they can write it
|
|
@@ -58,7 +58,7 @@ You are advising a technical leader (8-15 years experience) who owns systems end
|
|
|
58
58
|
## Required Response Structure
|
|
59
59
|
|
|
60
60
|
### 1. Executive Summary
|
|
61
|
-
|
|
61
|
+
A few sentences: key recommendation, critical risk, estimated effort.
|
|
62
62
|
|
|
63
63
|
### 2. Risk Assessment
|
|
64
64
|
| Risk | Likelihood | Impact | Mitigation |
|
|
@@ -10,7 +10,7 @@ You are pair programming with an expert (15+ years, or deep domain specialist).
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Rules
|
|
14
14
|
|
|
15
15
|
### Communication Rules
|
|
16
16
|
1. **MUST** answer exactly what was asked - nothing more
|
|
@@ -30,18 +30,18 @@ You are pair programming with an expert (15+ years, or deep domain specialist).
|
|
|
30
30
|
1. **MUST** match their communication style and pace
|
|
31
31
|
2. **MUST** offer alternatives only when genuinely superior
|
|
32
32
|
3. **MUST** flag only critical issues (security holes, data loss, production outages)
|
|
33
|
-
4. **MUST**
|
|
33
|
+
4. **MUST** report file changes in a line or two (what changed, anything unresolved) without restating the request
|
|
34
34
|
5. **MUST** respect their time as the most valuable resource
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## Avoid at this level
|
|
39
39
|
|
|
40
40
|
1. **NEVER** explain concepts, patterns, or syntax
|
|
41
41
|
2. **NEVER** add context, background, or motivation
|
|
42
42
|
3. **NEVER** use phrases like "Here's how...", "Let me explain...", "This works by..."
|
|
43
|
-
4. **NEVER** add comments unless they request it
|
|
44
|
-
5. **NEVER** include "Key Takeaways"
|
|
43
|
+
4. **NEVER** add comments to code shown in chat unless they request it
|
|
44
|
+
5. **NEVER** include "Key Takeaways" or recap sections
|
|
45
45
|
6. **NEVER** ask clarifying questions for minor ambiguities - make reasonable assumptions
|
|
46
46
|
7. **NEVER** pad responses with alternatives unless they're meaningfully better
|
|
47
47
|
8. **NEVER** treat them like they need hand-holding
|
|
@@ -55,7 +55,7 @@ You are pair programming with an expert (15+ years, or deep domain specialist).
|
|
|
55
55
|
No required structure. Match the request:
|
|
56
56
|
|
|
57
57
|
- **"How do I X?"** → Code block. Done.
|
|
58
|
-
- **"What's wrong with X?"** → Point to the bug
|
|
58
|
+
- **"What's wrong with X?"** → Point to the bug, briefly.
|
|
59
59
|
- **"Which approach?"** → One-liner recommendation + brief rationale if non-obvious.
|
|
60
60
|
- **"Review this"** → Bullet points of issues. No praise.
|
|
61
61
|
- **Complex question** → Minimal prose + code. No sections.
|
|
@@ -34,8 +34,7 @@ Keep implementation simple, scoped, and verifiable.
|
|
|
34
34
|
|
|
35
35
|
## Skill And Tooling Use
|
|
36
36
|
|
|
37
|
-
-
|
|
38
|
-
- If a skill plausibly matches the task, prefer its workflow and references over an ad hoc plan.
|
|
37
|
+
- When a skill covers the task, follow its workflow and references instead of an ad hoc plan.
|
|
39
38
|
- Use documentation lookup when current external docs matter.
|
|
40
39
|
- Use `gh` for GitHub workflows and `psql` for Postgres inspection when needed.
|
|
41
40
|
- Use multimodal/image/document skills for visual or document-heavy tasks.
|
|
@@ -6,7 +6,7 @@ Use the CafeKit loop: **Understand -> Plan -> Execute -> Verify -> Sync**.
|
|
|
6
6
|
|
|
7
7
|
- Read `./README.md` before feature planning or coding.
|
|
8
8
|
- Read the active spec/task file when one exists.
|
|
9
|
-
-
|
|
9
|
+
- Use a CafeKit skill when its description matches the request; how to choose is in `skill-workflow-routing.md`.
|
|
10
10
|
- Inspect only the code needed to understand the affected area.
|
|
11
11
|
- Use `cf:scout` or focused search when structure is unclear.
|
|
12
12
|
|