@haposoft/cafekit 0.16.6 → 0.18.0
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 +0 -36
- package/bin/__tests__/codex-native.test.js +46 -88
- package/bin/__tests__/develop-contract.test.js +26 -57
- 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 +159 -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 +100 -541
- 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 +0 -27
- package/bin/__tests__/specs-v2-execution-closeout.test.js +4 -69
- 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 +9 -28
- package/bin/phases/codex-runtime.js +24 -0
- package/bin/phases/omp-runtime.js +22 -0
- package/package.json +1 -1
- package/src/claude/CLAUDE.md +3 -10
- package/src/claude/agents/brainstormer.md +7 -3
- package/src/claude/agents/code-auditor.md +35 -50
- package/src/claude/agents/debugger.md +5 -79
- package/src/claude/agents/deployer.md +3 -5
- package/src/claude/agents/docs-keeper.md +2 -7
- package/src/claude/agents/implementer.md +6 -9
- package/src/claude/agents/inspector.md +2 -3
- package/src/claude/agents/project-manager.md +7 -15
- package/src/claude/agents/researcher.md +9 -3
- package/src/claude/agents/spec-maker.md +11 -11
- package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -1
- package/src/claude/hooks/__tests__/spec-gate.test.js +112 -3
- package/src/claude/hooks/__tests__/spec-state-touched.test.js +134 -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/context.cjs +0 -89
- 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 +18 -28
- package/src/claude/hooks/spec-state.cjs +95 -1
- package/src/claude/hooks/state.cjs +21 -2
- package/src/claude/migration-manifest.json +30 -15
- package/src/claude/rules/ai-dev-rules.md +1 -1
- 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 +10 -18
- 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 +52 -6
- package/src/claude/scripts/spec-resolver.cjs +1 -2
- package/src/claude/scripts/validate-spec-output.cjs +5 -32
- package/src/claude/scripts/workflow-policy.cjs +22 -10
- package/src/claude/settings/settings.json +1 -32
- package/src/claude/skills/{question → ask}/SKILL.md +9 -75
- package/src/claude/skills/brainstorm/SKILL.md +43 -131
- package/src/claude/skills/code-review/SKILL.md +48 -17
- package/src/claude/skills/code-review/references/spec-compliance-review.md +2 -3
- package/src/claude/skills/code-review/references/verification-gate.md +22 -27
- package/src/claude/skills/debug/SKILL.md +29 -149
- package/src/claude/skills/develop/SKILL.md +27 -36
- 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/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 +2 -0
- 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 +38 -44
- package/src/claude/skills/sync/SKILL.md +8 -15
- 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 +22 -3
- package/src/claude/skills/test/references/execution-strategy.md +83 -0
- 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 +1 -1
- 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 +5 -0
- package/src/codex/hooks/lib/spec-utils.cjs +0 -7
- package/src/codex/hooks/rules.cjs +2 -1
- package/src/codex/hooks/session.cjs +0 -2
- package/src/codex/hooks/spec-gate.cjs +8 -18
- package/src/codex/hooks.json +0 -20
- package/src/codex/rules/state-sync.md +3 -12
- package/src/common/AGENTS.md +2 -2
- package/src/omp/extensions/cafekit-bridge.mjs +6 -6
- 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/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/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/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
|
@@ -13,8 +13,6 @@ const os = require('os');
|
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const { execSync } = require('child_process');
|
|
15
15
|
|
|
16
|
-
// Usage cache file path (written by usage.cjs hook)
|
|
17
|
-
const USAGE_CACHE_FILE = path.join(os.tmpdir(), 'ck-usage-limits-cache.json');
|
|
18
16
|
const WARN_THRESHOLD = 70;
|
|
19
17
|
const CRITICAL_THRESHOLD = 90;
|
|
20
18
|
const {
|
|
@@ -214,51 +212,6 @@ function buildSessionSection(staticEnv = {}) {
|
|
|
214
212
|
];
|
|
215
213
|
}
|
|
216
214
|
|
|
217
|
-
/**
|
|
218
|
-
* Read usage limits from cache file (written by usage.cjs)
|
|
219
|
-
* @returns {Object|null} Usage data or null if unavailable
|
|
220
|
-
*/
|
|
221
|
-
function readUsageCache() {
|
|
222
|
-
try {
|
|
223
|
-
if (fs.existsSync(USAGE_CACHE_FILE)) {
|
|
224
|
-
const cache = JSON.parse(fs.readFileSync(USAGE_CACHE_FILE, 'utf-8'));
|
|
225
|
-
// Cache is valid for 5 minutes for injection purposes
|
|
226
|
-
if (Date.now() - cache.timestamp < 300000 && cache.data) {
|
|
227
|
-
return cache.data;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
} catch { }
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Format time until reset
|
|
236
|
-
* @param {string} resetAt - ISO timestamp
|
|
237
|
-
* @returns {string|null} Formatted time or null
|
|
238
|
-
*/
|
|
239
|
-
function formatTimeUntilReset(resetAt) {
|
|
240
|
-
if (!resetAt) return null;
|
|
241
|
-
const resetTime = new Date(resetAt);
|
|
242
|
-
const remaining = Math.floor(resetTime.getTime() / 1000) - Math.floor(Date.now() / 1000);
|
|
243
|
-
if (remaining <= 0 || remaining > 18000) return null; // Only show if < 5 hours
|
|
244
|
-
const hours = Math.floor(remaining / 3600);
|
|
245
|
-
const mins = Math.floor((remaining % 3600) / 60);
|
|
246
|
-
return `${hours}h ${mins}m`;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Format percentage with warning level
|
|
251
|
-
* @param {number} value - Percentage value
|
|
252
|
-
* @param {string} label - Label prefix
|
|
253
|
-
* @returns {string} Formatted string with warning if applicable
|
|
254
|
-
*/
|
|
255
|
-
function formatUsagePercent(value, label) {
|
|
256
|
-
const pct = Math.round(value);
|
|
257
|
-
if (pct >= CRITICAL_THRESHOLD) return `${label}: ${pct}% [CRITICAL]`;
|
|
258
|
-
if (pct >= WARN_THRESHOLD) return `${label}: ${pct}% [WARNING]`;
|
|
259
|
-
return `${label}: ${pct}%`;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
215
|
/**
|
|
263
216
|
* Build context window section from statusline cache
|
|
264
217
|
* @param {string} sessionId - Session ID
|
|
@@ -297,43 +250,6 @@ function buildContextSection(sessionId) {
|
|
|
297
250
|
}
|
|
298
251
|
}
|
|
299
252
|
|
|
300
|
-
/**
|
|
301
|
-
* Build usage section from cache
|
|
302
|
-
* @returns {string[]} Lines for usage section
|
|
303
|
-
*/
|
|
304
|
-
function buildUsageSection() {
|
|
305
|
-
const usage = readUsageCache();
|
|
306
|
-
if (!usage) return [];
|
|
307
|
-
|
|
308
|
-
const lines = [];
|
|
309
|
-
const parts = [];
|
|
310
|
-
|
|
311
|
-
// 5-hour limit
|
|
312
|
-
if (usage.five_hour) {
|
|
313
|
-
const util = usage.five_hour.utilization;
|
|
314
|
-
if (typeof util === 'number') {
|
|
315
|
-
parts.push(formatUsagePercent(util, '5h'));
|
|
316
|
-
}
|
|
317
|
-
const timeLeft = formatTimeUntilReset(usage.five_hour.resets_at);
|
|
318
|
-
if (timeLeft) {
|
|
319
|
-
parts.push(`resets in ${timeLeft}`);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// 7-day limit
|
|
324
|
-
if (usage.seven_day?.utilization != null) {
|
|
325
|
-
parts.push(formatUsagePercent(usage.seven_day.utilization, '7d'));
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (parts.length > 0) {
|
|
329
|
-
lines.push(`## Usage Limits`);
|
|
330
|
-
lines.push(`- ${parts.join(' | ')}`);
|
|
331
|
-
lines.push(``);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return lines;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
253
|
/**
|
|
338
254
|
* Build rules section
|
|
339
255
|
* @param {Object} params
|
|
@@ -489,13 +405,11 @@ function buildReminder(params) {
|
|
|
489
405
|
// Respect hooks config — skip sections when their corresponding hook is disabled
|
|
490
406
|
const hooksConfig = hooks || {};
|
|
491
407
|
const contextEnabled = hooksConfig['context-tracking'] !== false;
|
|
492
|
-
const usageEnabled = hooksConfig['usage'] !== false;
|
|
493
408
|
|
|
494
409
|
return [
|
|
495
410
|
...buildLanguageSection({ thinkingLanguage, responseLanguage }),
|
|
496
411
|
...buildSessionSection(staticEnv),
|
|
497
412
|
...(contextEnabled ? buildContextSection(sessionId) : []),
|
|
498
|
-
...(usageEnabled ? buildUsageSection() : []),
|
|
499
413
|
...buildRulesSection({ devRulesPath, catalogScript, skillsVenv, plansPath, docsPath }),
|
|
500
414
|
...buildModularizationSection(),
|
|
501
415
|
...buildPathsSection({ reportsPath, plansPath, docsPath, docsMaxLoc }),
|
|
@@ -564,7 +478,6 @@ function buildReminderContext({ sessionId, config, staticEnv, configDirName = '.
|
|
|
564
478
|
// Respect hooks config for sections object too
|
|
565
479
|
const hooksConfig = cfg.hooks || {};
|
|
566
480
|
const contextEnabled = hooksConfig['context-tracking'] !== false;
|
|
567
|
-
const usageEnabled = hooksConfig['usage'] !== false;
|
|
568
481
|
|
|
569
482
|
return {
|
|
570
483
|
content: lines.join('\n'),
|
|
@@ -573,7 +486,6 @@ function buildReminderContext({ sessionId, config, staticEnv, configDirName = '.
|
|
|
573
486
|
language: buildLanguageSection({ thinkingLanguage: params.thinkingLanguage, responseLanguage: params.responseLanguage }),
|
|
574
487
|
session: buildSessionSection(staticEnv),
|
|
575
488
|
context: contextEnabled ? buildContextSection(sessionId) : [],
|
|
576
|
-
usage: usageEnabled ? buildUsageSection() : [],
|
|
577
489
|
rules: buildRulesSection({ devRulesPath, catalogScript, skillsVenv, plansPath: params.plansPath, docsPath: params.docsPath }),
|
|
578
490
|
modularization: buildModularizationSection(),
|
|
579
491
|
paths: buildPathsSection({ reportsPath: params.reportsPath, plansPath: params.plansPath, docsPath: params.docsPath, docsMaxLoc: params.docsMaxLoc }),
|
|
@@ -596,7 +508,6 @@ module.exports = {
|
|
|
596
508
|
buildLanguageSection,
|
|
597
509
|
buildSessionSection,
|
|
598
510
|
buildContextSection,
|
|
599
|
-
buildUsageSection,
|
|
600
511
|
buildRulesSection,
|
|
601
512
|
buildModularizationSection,
|
|
602
513
|
buildPathsSection,
|
|
@@ -54,8 +54,8 @@ const EVENT_NAMES = Object.freeze({
|
|
|
54
54
|
* Grok's registry and matcher aliases come first, then omp's lowercase registry. Where
|
|
55
55
|
* both hosts use a spelling (`write`, `grep`) they agree on the destination. `write` is
|
|
56
56
|
* grok's file-creating tool and maps to `Write`, while `search_replace` edits an existing
|
|
57
|
-
* file and maps to `Edit
|
|
58
|
-
*
|
|
57
|
+
* file and maps to `Edit`, which keeps grok's file creation distinct from an edit.
|
|
58
|
+
* `delete_file` maps to `Write` so a delete is gated like any other file write.
|
|
59
59
|
*/
|
|
60
60
|
const TOOL_ALIASES = Object.freeze({
|
|
61
61
|
// grok — every spelling that reaches a Bash matcher
|
|
@@ -70,6 +70,7 @@ try {
|
|
|
70
70
|
const baseDir = cwd;
|
|
71
71
|
const plansPath = path.join(baseDir, runtime.paths?.plans || 'plans');
|
|
72
72
|
const docsPath = path.join(baseDir, runtime.paths?.docs || 'docs');
|
|
73
|
+
const specsPath = path.join(baseDir, runtime.paths?.specs || 'specs');
|
|
73
74
|
const maxLoc = runtime.docs?.maxLoc || 800;
|
|
74
75
|
|
|
75
76
|
const lines = [];
|
|
@@ -84,7 +85,7 @@ try {
|
|
|
84
85
|
|
|
85
86
|
lines.push(
|
|
86
87
|
'## Rules',
|
|
87
|
-
`- Markdown files: Plans → "${plansPath}/" | Docs → "${docsPath}/"`,
|
|
88
|
+
`- Markdown files: Specs → "${specsPath}/" | Plans → "${plansPath}/" | Docs → "${docsPath}/"`,
|
|
88
89
|
'- **DO NOT** create markdown files outside of those directories unless explicitly asked.',
|
|
89
90
|
`- docs.maxLoc: ${maxLoc} lines max per doc file`
|
|
90
91
|
);
|
|
@@ -202,7 +202,6 @@ try {
|
|
|
202
202
|
const envFile = process.env.CLAUDE_ENV_FILE;
|
|
203
203
|
const cwd = projectRoot();
|
|
204
204
|
const runtime = readRuntime(cwd);
|
|
205
|
-
try { require('./completion-authority-state.cjs').clearState(cwd, payload.session_id); } catch { /* old installs remain fail-open */ }
|
|
206
205
|
|
|
207
206
|
// Check CafeKit update in parallel with project detection (async, fail-open).
|
|
208
207
|
const updateCheckPromise = checkCafeKitUpdate(cwd);
|
|
@@ -90,17 +90,17 @@ try {
|
|
|
90
90
|
let POLICY;
|
|
91
91
|
let RESOLVER;
|
|
92
92
|
let RECEIPT;
|
|
93
|
-
let FINAL_STATE;
|
|
94
93
|
try {
|
|
95
94
|
POLICY = require(policyPath);
|
|
95
|
+
// One gate run checks every done receipt against the same checkout; capture it once.
|
|
96
|
+
require(path.join(__dirname, '..', 'scripts', 'provenance.cjs')).enableSnapshotMemo();
|
|
96
97
|
RESOLVER = require(path.join(__dirname, '..', 'scripts', 'spec-resolver.cjs'));
|
|
97
98
|
RECEIPT = require(path.join(__dirname, '..', 'scripts', 'spec-receipt.cjs'));
|
|
98
|
-
FINAL_STATE = require('./completion-authority-check.cjs');
|
|
99
99
|
if (typeof POLICY.validateCanonicalReceipt !== 'function'
|
|
100
100
|
|| typeof POLICY.completionDecisionForSpec !== 'function'
|
|
101
101
|
|| typeof RECEIPT.checkTaskReceipt !== 'function'
|
|
102
102
|
|| typeof RECEIPT.checkWorkflowReceiptSet !== 'function'
|
|
103
|
-
|| typeof
|
|
103
|
+
|| typeof RESOLVER.resolveWorkflowCandidate !== 'function') {
|
|
104
104
|
throw new Error('shared workflow policy lacks completion authority functions');
|
|
105
105
|
}
|
|
106
106
|
} catch (error) {
|
|
@@ -127,9 +127,7 @@ try {
|
|
|
127
127
|
|| (typeof RESOLVER.readActiveFeatureTarget === 'function'
|
|
128
128
|
? RESOLVER.readActiveFeatureTarget({ projectRoot: baseDir, runtime })
|
|
129
129
|
: null);
|
|
130
|
-
let resolved =
|
|
131
|
-
? RESOLVER.resolveWorkflowCandidate({ projectRoot: baseDir, runtime, target, includeCompleted: true })
|
|
132
|
-
: FINAL_STATE.resolveCandidate({ resolver: RESOLVER, projectRoot: baseDir, runtime, payload });
|
|
130
|
+
let resolved = RESOLVER.resolveWorkflowCandidate({ projectRoot: baseDir, runtime, target, includeCompleted: true });
|
|
133
131
|
if (typeof RESOLVER.refineWorkflowGateResolution === 'function') {
|
|
134
132
|
resolved = RESOLVER.refineWorkflowGateResolution(resolved);
|
|
135
133
|
}
|
|
@@ -150,7 +148,8 @@ try {
|
|
|
150
148
|
const lines = [`⚠️ Completion gate: ${checked.failures.length} done task(s) lack a verification receipt.`];
|
|
151
149
|
for (const failure of checked.failures) {
|
|
152
150
|
lines.push(`- \`${failure.featureName}/${failure.taskPath}\`: failed check(s) ${failure.failures.join(', ')}`);
|
|
153
|
-
|
|
151
|
+
const hint = typeof RECEIPT.receiptFixHint === 'function' ? RECEIPT.receiptFixHint(failure.failures, failure.body) : null;
|
|
152
|
+
lines.push(` Fix in \`specs/${failure.featureName}/${failure.taskPath}\`: ${hint || 'write a runtime-bound `## Receipt` with the planned command'}.`);
|
|
154
153
|
}
|
|
155
154
|
emitBlock(lines.slice(0, 8).join('\n'));
|
|
156
155
|
process.exit(0);
|
|
@@ -185,19 +184,10 @@ try {
|
|
|
185
184
|
const lifecyclePhase = activeSpec.current_phase || activeSpec.phase;
|
|
186
185
|
const explicitCloseout = ['done', 'completed', 'complete'].includes(activeSpec.status)
|
|
187
186
|
|| ['closeout', 'completion', 'completed', 'complete'].includes(lifecyclePhase);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
projectRoot: baseDir,
|
|
193
|
-
runtime,
|
|
194
|
-
payload: { ...payload, session_id: sessionIdentity(payload) },
|
|
195
|
-
});
|
|
196
|
-
if (!finalState.ok) {
|
|
197
|
-
emitBlock(`Completion gate: ${finalState.reason}`);
|
|
198
|
-
process.exit(0);
|
|
199
|
-
}
|
|
200
|
-
if (finalState.active) process.exit(0);
|
|
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);
|
|
201
191
|
}
|
|
202
192
|
const runtimeContext = POLICY.deriveRuntimeContext({
|
|
203
193
|
projectRoot: baseDir,
|
|
@@ -250,12 +240,12 @@ try {
|
|
|
250
240
|
);
|
|
251
241
|
const featureDir = path.join(specsPath, featureName);
|
|
252
242
|
const failures = allDoneTasks
|
|
253
|
-
.map((taskPath) =>
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
})
|
|
243
|
+
.map((taskPath) => {
|
|
244
|
+
const proof = processWorkflow
|
|
245
|
+
? RECEIPT.checkWorkflowTaskReceipt(featureDir, taskPath, runtimeContext, POLICY)
|
|
246
|
+
: RECEIPT.checkTaskReceipt(featureDir, taskPath, taskRegistry[taskPath], runtimeContext, POLICY);
|
|
247
|
+
return { taskPath, fails: proof.failures, body: proof.body };
|
|
248
|
+
})
|
|
259
249
|
.filter((f) => f.fails.length > 0);
|
|
260
250
|
|
|
261
251
|
const featureCloseoutRequired = !processWorkflow && explicitCloseout && allDoneTasks.length > 0;
|
|
@@ -297,12 +287,12 @@ try {
|
|
|
297
287
|
? `⚠️ Completion gate: ${failures.length} done task(s) lack a verification receipt.`
|
|
298
288
|
: '⚠️ Completion gate: workflow completion proof is incomplete.',
|
|
299
289
|
];
|
|
300
|
-
for (const { taskPath, fails } of failures) {
|
|
290
|
+
for (const { taskPath, fails, body } of failures) {
|
|
301
291
|
lines.push(`- \`${taskPath}\`: failed check(s) ${fails.join(', ')}`);
|
|
302
292
|
lines.push(taskPath === 'feature-receipt.md'
|
|
303
293
|
? ` Fix: run final integration proof, then write \`specs/${featureName}/feature-receipt.md\`.`
|
|
304
294
|
: processWorkflow
|
|
305
|
-
? ` Fix
|
|
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'}.`
|
|
306
296
|
: ` Fix: write canonical proof to \`specs/${featureName}/receipts/${path.posix.basename(taskPath)}\`; legacy \`## Evidence\` remains read-compatible.`);
|
|
307
297
|
}
|
|
308
298
|
if (completionBlocked) {
|
|
@@ -44,6 +44,40 @@ function canonicalPath(value) {
|
|
|
44
44
|
try { return fs.realpathSync(value); } catch { return null; }
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/** Real path of `value`, resolving its deepest existing ancestor (the file may not exist yet). */
|
|
48
|
+
function canonicalTarget(value) {
|
|
49
|
+
if (typeof value !== 'string' || !value) return null;
|
|
50
|
+
let current = path.resolve(value);
|
|
51
|
+
const rest = [];
|
|
52
|
+
while (!fs.existsSync(current)) {
|
|
53
|
+
const parent = path.dirname(current);
|
|
54
|
+
if (parent === current) return null;
|
|
55
|
+
rest.unshift(path.basename(current));
|
|
56
|
+
current = parent;
|
|
57
|
+
}
|
|
58
|
+
const real = canonicalPath(current);
|
|
59
|
+
return real ? path.join(real, ...rest) : null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function escapeRegExp(text) {
|
|
63
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Packets the prompt names: `specs/<name>`, a `/cf:<command> <name>` token, or the bare
|
|
68
|
+
* name when it carries `-`, `_` or a digit. A plain word such as `docs` must use `specs/`,
|
|
69
|
+
* so an ordinary sentence does not touch a packet.
|
|
70
|
+
*/
|
|
71
|
+
function packetsNamedIn(text, names) {
|
|
72
|
+
if (typeof text !== 'string' || !text) return [];
|
|
73
|
+
return names.filter((name) => {
|
|
74
|
+
const n = escapeRegExp(name);
|
|
75
|
+
if (new RegExp(`(^|[^\\w-])specs/${n}(?![\\w-])`).test(text)) return true;
|
|
76
|
+
if (new RegExp(`/cf:[\\w-]+\\s+(?:specs/)?${n}(?![\\w-])`).test(text)) return true;
|
|
77
|
+
return /[-_0-9]/.test(name) && new RegExp(`(^|[^\\w/-])${n}(?![\\w-])`).test(text);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
47
81
|
try {
|
|
48
82
|
// ── Main ──────────────────────────────────────────────────────────────────
|
|
49
83
|
|
|
@@ -51,7 +85,8 @@ try {
|
|
|
51
85
|
if (!stdin) process.exit(0);
|
|
52
86
|
|
|
53
87
|
const { normalizeHookPayload } = require('./lib/hook-payload.cjs');
|
|
54
|
-
const
|
|
88
|
+
const rawPayload = JSON.parse(stdin);
|
|
89
|
+
const payload = normalizeHookPayload(rawPayload);
|
|
55
90
|
const cwd = payload.cwd || process.cwd();
|
|
56
91
|
|
|
57
92
|
let POLICY;
|
|
@@ -84,6 +119,64 @@ try {
|
|
|
84
119
|
const baseDir = installedRoot || process.env.PROJECT_ROOT || cwd;
|
|
85
120
|
const explicitFeature = payload.featureName || payload.feature || payload.explicitFeature || null;
|
|
86
121
|
const explicitPath = payload.specPath || payload.spec_path || payload.featurePath || null;
|
|
122
|
+
|
|
123
|
+
// ── Per-session touch set (Claude only) ───────────────────────────────────
|
|
124
|
+
// Several sessions can share one checkout. On Claude the tollgate speaks only about a
|
|
125
|
+
// packet this session touched. omp and Codex run their own copies, and grok sends
|
|
126
|
+
// `sessionId` before normalization, so all three keep the old behaviour.
|
|
127
|
+
const claudeSession = rawPayload && typeof rawPayload.session_id === 'string' && rawPayload.session_id
|
|
128
|
+
? rawPayload.session_id
|
|
129
|
+
: null;
|
|
130
|
+
const touchFilter = runtimeDirName() === '.claude' && claudeSession !== null;
|
|
131
|
+
const touchFile = touchFilter
|
|
132
|
+
? path.join(
|
|
133
|
+
require('./lib/hook-state-dir.cjs').hookStateDir(),
|
|
134
|
+
`spec-touched-${require('crypto').createHash('sha256').update(claudeSession).digest('hex')}.json`,
|
|
135
|
+
)
|
|
136
|
+
: null;
|
|
137
|
+
const readTouched = () => {
|
|
138
|
+
try {
|
|
139
|
+
const list = JSON.parse(fs.readFileSync(touchFile, 'utf8'));
|
|
140
|
+
return new Set(Array.isArray(list) ? list : []);
|
|
141
|
+
} catch { return new Set(); }
|
|
142
|
+
};
|
|
143
|
+
const addTouched = (names) => {
|
|
144
|
+
const touched = readTouched();
|
|
145
|
+
const before = touched.size;
|
|
146
|
+
for (const name of names) touched.add(name);
|
|
147
|
+
if (touched.size === before) return;
|
|
148
|
+
try {
|
|
149
|
+
fs.mkdirSync(path.dirname(touchFile), { recursive: true });
|
|
150
|
+
fs.writeFileSync(touchFile, JSON.stringify([...touched].sort()));
|
|
151
|
+
} catch { /* an unwritable state dir only loses the touch */ }
|
|
152
|
+
};
|
|
153
|
+
let specsRoot = null;
|
|
154
|
+
try { specsRoot = RESOLVER.specsDirectory(baseDir, runtime); } catch { /* resolver reports it below */ }
|
|
155
|
+
|
|
156
|
+
if (payload.hook_event_name === 'PostToolUse') {
|
|
157
|
+
// Record only; no resolve, no git, no tollgate cache.
|
|
158
|
+
if (touchFilter && specsRoot && /^(Edit|Write|MultiEdit)$/.test(payload.tool_name || '')) {
|
|
159
|
+
const realRoot = canonicalPath(specsRoot);
|
|
160
|
+
const realTarget = canonicalTarget(payload.tool_input && payload.tool_input.file_path);
|
|
161
|
+
if (realRoot && realTarget) {
|
|
162
|
+
const rel = path.relative(realRoot, realTarget);
|
|
163
|
+
const parts = rel.split(path.sep);
|
|
164
|
+
if (rel && parts[0] !== '..' && !path.isAbsolute(rel) && parts.length > 1) addTouched([parts[0]]);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
process.exit(0);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (touchFilter) {
|
|
171
|
+
let packets = [];
|
|
172
|
+
try {
|
|
173
|
+
packets = fs.readdirSync(specsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
174
|
+
} catch { /* no specs root */ }
|
|
175
|
+
const named = packetsNamedIn(payload.prompt, packets);
|
|
176
|
+
if (typeof explicitFeature === 'string' && explicitFeature) named.push(explicitFeature);
|
|
177
|
+
if (named.length) addTouched(named);
|
|
178
|
+
}
|
|
179
|
+
|
|
87
180
|
const resolveWorkflow = typeof RESOLVER.resolveWorkflowCandidate === 'function'
|
|
88
181
|
? RESOLVER.resolveWorkflowCandidate
|
|
89
182
|
: RESOLVER.resolveActiveSpec;
|
|
@@ -118,6 +211,7 @@ try {
|
|
|
118
211
|
const activeSpec = resolved.spec || {};
|
|
119
212
|
const processWorkflow = resolved.layoutKind === 'process-v3';
|
|
120
213
|
const featureName = resolved.featureName;
|
|
214
|
+
if (touchFilter && !readTouched().has(featureName)) process.exit(0);
|
|
121
215
|
const specsPath = resolved.specsDir;
|
|
122
216
|
const runtimeContext = POLICY.deriveRuntimeContext({
|
|
123
217
|
projectRoot: baseDir,
|
|
@@ -148,6 +148,21 @@ try {
|
|
|
148
148
|
return data;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
/**
|
|
152
|
+
* True when latest.md holds something beyond the filler this hook writes itself:
|
|
153
|
+
* headings, HTML comments, the three placeholder bullets and timestamp-only agent results.
|
|
154
|
+
*/
|
|
155
|
+
function hasPriorContent(text) {
|
|
156
|
+
const filler = [
|
|
157
|
+
/^#{1,6}\s/,
|
|
158
|
+
/^<!--.*-->$/,
|
|
159
|
+
/^- \((No completed tasks recorded|All tasks completed|No file changes detected)\)$/,
|
|
160
|
+
/^- Completed at \d{2}:\d{2}:\d{2}$/,
|
|
161
|
+
];
|
|
162
|
+
return text.split('\n').map((line) => line.trim())
|
|
163
|
+
.some((line) => line && !filler.some((pattern) => pattern.test(line)));
|
|
164
|
+
}
|
|
165
|
+
|
|
151
166
|
function buildStateContent(data) {
|
|
152
167
|
const done = data.todos.filter((todo) => todo.status === 'completed' || todo.status === 'done');
|
|
153
168
|
const pending = data.todos.filter((todo) => !['completed', 'done'].includes(todo.status));
|
|
@@ -226,14 +241,18 @@ try {
|
|
|
226
241
|
if (!stdin) process.exit(0);
|
|
227
242
|
|
|
228
243
|
const { normalizeHookPayload } = require('./lib/hook-payload.cjs');
|
|
229
|
-
const
|
|
244
|
+
const rawPayload = JSON.parse(stdin);
|
|
245
|
+
const data = normalizeHookPayload(rawPayload);
|
|
230
246
|
const event = data.hook_event_name || '';
|
|
231
247
|
const cwd = data.cwd || process.cwd();
|
|
232
248
|
const dir = stateDir(cwd);
|
|
233
249
|
|
|
234
250
|
if (event === 'SessionStart') {
|
|
235
251
|
const previous = loadLatest(cwd);
|
|
236
|
-
|
|
252
|
+
// Claude only: grok (camelCase sessionId), omp and Codex keep printing the block.
|
|
253
|
+
const claudeSession = runtimeDirName() === '.claude'
|
|
254
|
+
&& typeof rawPayload.session_id === 'string' && rawPayload.session_id.length > 0;
|
|
255
|
+
if (previous && (!claudeSession || hasPriorContent(previous))) {
|
|
237
256
|
console.log('\n=== Prior Execution Context ===');
|
|
238
257
|
console.log(previous.trim());
|
|
239
258
|
console.log('=== End of Prior Context ===\n');
|
|
@@ -7,23 +7,21 @@
|
|
|
7
7
|
"skills": {
|
|
8
8
|
"required": [
|
|
9
9
|
"agent-browser",
|
|
10
|
+
"ask",
|
|
10
11
|
"brainstorm",
|
|
11
12
|
"chrome-devtools",
|
|
12
13
|
"code-review",
|
|
13
14
|
"debug",
|
|
14
|
-
"delegate",
|
|
15
15
|
"develop",
|
|
16
|
+
"fix",
|
|
16
17
|
"git",
|
|
17
|
-
"hotfix",
|
|
18
|
-
"inspect",
|
|
19
|
-
"loop",
|
|
20
18
|
"orca",
|
|
21
|
-
"question",
|
|
22
19
|
"research",
|
|
23
|
-
"
|
|
20
|
+
"scout",
|
|
24
21
|
"specs",
|
|
25
22
|
"sync",
|
|
26
23
|
"test",
|
|
24
|
+
"ui-ux",
|
|
27
25
|
"ui-ux-pro-max",
|
|
28
26
|
"web-testing"
|
|
29
27
|
],
|
|
@@ -86,7 +84,13 @@
|
|
|
86
84
|
"frontend-design",
|
|
87
85
|
"mobile-development",
|
|
88
86
|
"devops",
|
|
89
|
-
"react-best-practices"
|
|
87
|
+
"react-best-practices",
|
|
88
|
+
"inspect",
|
|
89
|
+
"hotfix",
|
|
90
|
+
"question",
|
|
91
|
+
"loop",
|
|
92
|
+
"route",
|
|
93
|
+
"delegate"
|
|
90
94
|
],
|
|
91
95
|
"agents": {
|
|
92
96
|
"claude": ["agents/god-developer.md"],
|
|
@@ -112,10 +116,27 @@
|
|
|
112
116
|
"skills/specs/rules/ears-format.md",
|
|
113
117
|
"skills/specs/rules/phase-decision-matrix.md",
|
|
114
118
|
"skills/specs/rules/task-scoring-rubric.md",
|
|
115
|
-
"skills/specs/rules/tasks-generation.md"
|
|
119
|
+
"skills/specs/rules/tasks-generation.md",
|
|
120
|
+
"hooks/usage.cjs",
|
|
121
|
+
"hooks/completion-authority.cjs",
|
|
122
|
+
"hooks/completion-authority-check.cjs",
|
|
123
|
+
"hooks/completion-authority-state.cjs",
|
|
124
|
+
"hooks/semantic-review-authority.cjs",
|
|
125
|
+
"hooks/task-scaffold-guard.cjs"
|
|
116
126
|
],
|
|
117
127
|
"settingsHookCommandSubstrings": [
|
|
118
|
-
"hooks/skill-router.cjs"
|
|
128
|
+
"hooks/skill-router.cjs",
|
|
129
|
+
".claude/hooks/usage.cjs",
|
|
130
|
+
".claude/hooks/docs-sync.cjs",
|
|
131
|
+
".claude/hooks/completion-authority.cjs",
|
|
132
|
+
".codex/hooks/completion-authority.cjs",
|
|
133
|
+
".codex\\hooks\\completion-authority.cjs",
|
|
134
|
+
".claude/hooks/semantic-review-authority.cjs",
|
|
135
|
+
".codex/hooks/semantic-review-authority.cjs",
|
|
136
|
+
".codex\\hooks\\semantic-review-authority.cjs",
|
|
137
|
+
".claude/hooks/task-scaffold-guard.cjs",
|
|
138
|
+
".codex/hooks/task-scaffold-guard.cjs",
|
|
139
|
+
".codex\\hooks\\task-scaffold-guard.cjs"
|
|
119
140
|
]
|
|
120
141
|
},
|
|
121
142
|
"runtime": {
|
|
@@ -128,18 +149,12 @@
|
|
|
128
149
|
"hooks/precompact.cjs",
|
|
129
150
|
"hooks/docs-sync.cjs",
|
|
130
151
|
"hooks/agent.cjs",
|
|
131
|
-
"hooks/usage.cjs",
|
|
132
152
|
"hooks/privacy-block.cjs",
|
|
133
153
|
"hooks/secret-output-guardrail.cjs",
|
|
134
154
|
"hooks/inspect-block.cjs",
|
|
135
|
-
"hooks/task-scaffold-guard.cjs",
|
|
136
155
|
"hooks/rules.cjs",
|
|
137
156
|
"hooks/spec-state.cjs",
|
|
138
157
|
"hooks/spec-gate.cjs",
|
|
139
|
-
"hooks/completion-authority.cjs",
|
|
140
|
-
"hooks/completion-authority-check.cjs",
|
|
141
|
-
"hooks/completion-authority-state.cjs",
|
|
142
|
-
"hooks/semantic-review-authority.cjs",
|
|
143
158
|
"hooks/state.cjs",
|
|
144
159
|
"hooks/lib/color.cjs",
|
|
145
160
|
"hooks/lib/parser.cjs",
|
|
@@ -4,28 +4,21 @@ Hooks are instruction boundaries. Do not bypass or work around them.
|
|
|
4
4
|
|
|
5
5
|
## Privacy Block Hook
|
|
6
6
|
|
|
7
|
-
When a tool call
|
|
7
|
+
When a tool call touches a sensitive file, or the hook cannot classify it safely,
|
|
8
|
+
the privacy hook returns a permission decision of `ask` with the reason, and the
|
|
9
|
+
host asks the user to approve or deny that one call. Do not ask again yourself.
|
|
10
|
+
On a host whose hook can only deny, the call is blocked; ask the user instead of
|
|
11
|
+
retrying it another way.
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
...
|
|
12
|
-
@@PRIVACY_PROMPT_END@@
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Required flow:
|
|
16
|
-
|
|
17
|
-
1. Parse the JSON payload from the marker.
|
|
18
|
-
2. Ask the user for approval using the available user-question tool and the prompt/options from the payload.
|
|
19
|
-
3. If approved, retry only the blocked action.
|
|
20
|
-
4. If denied, continue without reading that file or performing that blocked action.
|
|
13
|
+
- If approved, the call runs; do not widen it to other files.
|
|
14
|
+
- If denied, continue without reading that file or performing that blocked action.
|
|
21
15
|
|
|
22
16
|
Never use another command, path, encoding, or side channel to access a privacy-blocked file without explicit approval.
|
|
23
17
|
|
|
24
18
|
## State And Spec Hooks
|
|
25
19
|
|
|
26
20
|
- If a hook reports state drift, run the appropriate sync/audit flow before continuing.
|
|
27
|
-
- If a hook rejects task completion, keep the task `in_progress` or `blocked` until proof exists.
|
|
28
|
-
- Do not mark a task `done` unless the matching task file contains a valid verification receipt.
|
|
21
|
+
- If a hook rejects task completion, keep the task `in_progress` or `blocked` until proof exists; the receipt contract lives in `state-sync.md`.
|
|
29
22
|
|
|
30
23
|
## Hook Failure Handling
|
|
31
24
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Living Documents
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
When the project keeps these core documents in `./docs`, keep them current (a project may have only some of them):
|
|
6
6
|
|
|
7
7
|
| Document | Purpose |
|
|
8
8
|
|----------|---------|
|
|
@@ -14,8 +14,6 @@ The project maintains these core documents in `./docs`:
|
|
|
14
14
|
### Freshness Rule
|
|
15
15
|
|
|
16
16
|
- Before updating any doc, check its last modified date
|
|
17
|
-
- If a doc hasn't been updated in >2 weeks while development is active, flag it for review
|
|
18
|
-
- The `docs-keeper` agent should proactively scan for stale docs during weekly reviews
|
|
19
17
|
|
|
20
18
|
## When to Update
|
|
21
19
|
|
|
@@ -26,7 +24,6 @@ The `docs-keeper` agent is responsible for keeping these documents current. Trig
|
|
|
26
24
|
- A significant feature ships or a critical bug is resolved
|
|
27
25
|
- Security patches are applied or dependencies change
|
|
28
26
|
- Project scope or timeline shifts
|
|
29
|
-
- Weekly progress reviews are due
|
|
30
27
|
|
|
31
28
|
### Update Discipline
|
|
32
29
|
|
|
@@ -43,7 +40,7 @@ Use [Keep a Changelog](https://keepachangelog.com/) convention:
|
|
|
43
40
|
## [version] - YYYY-MM-DD
|
|
44
41
|
|
|
45
42
|
### Added
|
|
46
|
-
- Feature description (#PR-number)
|
|
43
|
+
- Feature description (#PR-number when there is one)
|
|
47
44
|
|
|
48
45
|
### Fixed
|
|
49
46
|
- Bug fix description (#issue-number)
|
|
@@ -74,38 +71,27 @@ specs/
|
|
|
74
71
|
|
|
75
72
|
Task files are direct children beside `plan.md`; do not place them in a task
|
|
76
73
|
subdirectory.
|
|
77
|
-
The plan records the
|
|
74
|
+
The plan records the GATE-SCOPE scope decision, explicit exclusions, acceptance
|
|
78
75
|
criteria, and task mapping. Each task owns one usable outcome, bounded paths,
|
|
79
76
|
dependencies, acceptance, and a runnable Verification Plan.
|
|
80
77
|
|
|
81
78
|
### Gates and execution handoff
|
|
82
79
|
|
|
83
|
-
-
|
|
84
|
-
-
|
|
80
|
+
- GATE-SCOPE: the user chooses EXPAND, KEEP, or CUT before the plan is written.
|
|
81
|
+
- GATE-REVIEW: the user accepts, rejects, or revises deduplicated adversarial findings.
|
|
85
82
|
- Specs stops after planning; implementation begins only through a new explicit
|
|
86
83
|
Develop invocation.
|
|
87
|
-
-
|
|
84
|
+
- GATE-DONE: after execution, current receipts and limitations are shown and the user
|
|
88
85
|
decides whether the feature is complete.
|
|
89
86
|
|
|
90
87
|
### Task state and proof
|
|
91
88
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
`Verification: PASS`, runtime-derived Base and Head values, and non-empty
|
|
95
|
-
fenced current output before the task becomes done.
|
|
89
|
+
Task status, the inline `## Receipt` and its proof contract are defined in
|
|
90
|
+
`state-sync.md`.
|
|
96
91
|
|
|
97
92
|
Sync only observed state with surgical edits. Re-read the changed task after
|
|
98
93
|
every update; never infer missing proof, approval, or readiness. When a done
|
|
99
94
|
task changes user-facing behavior, architecture, API contracts, operations, or
|
|
100
95
|
project status, classify docs impact and update only affected existing docs.
|
|
101
96
|
|
|
102
|
-
Comply with the overarching rules in
|
|
103
|
-
|
|
104
|
-
## Legacy specification layout
|
|
105
|
-
|
|
106
|
-
Existing features containing `spec.json`, nested `tasks/task-R*.md`, or other
|
|
107
|
-
legacy kernel artifacts keep their installed adapters and original layout.
|
|
108
|
-
Preserve `task_registry`, `semantic_model`, `planning_depth`, lane,
|
|
109
|
-
`execution_tier`, machine authority, typed topology, separate receipts, and
|
|
110
|
-
feature closeout files. Do not migrate them as part of unrelated documentation
|
|
111
|
-
work.
|
|
97
|
+
Comply with the overarching rules in `ai-dev-rules.md`.
|