@haposoft/cafekit 0.18.0 → 0.18.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.md +12 -1
  2. package/bin/__tests__/claude-settings.test.js +123 -0
  3. package/bin/__tests__/codex-ask-guard.test.js +123 -0
  4. package/bin/__tests__/codex-ask-projection.test.js +51 -0
  5. package/bin/__tests__/codex-hooks-ownership.test.js +80 -1
  6. package/bin/__tests__/codex-hooks.test.js +938 -237
  7. package/bin/__tests__/codex-native-hook-boundaries.test.js +136 -0
  8. package/bin/__tests__/codex-native.test.js +222 -149
  9. package/bin/__tests__/codex-projection-parity.test.js +314 -0
  10. package/bin/__tests__/codex-quiet-hooks.test.js +170 -0
  11. package/bin/__tests__/codex-research-projection.test.js +70 -0
  12. package/bin/__tests__/codex-specs-projection.test.js +76 -0
  13. package/bin/__tests__/develop-contract.test.js +102 -1362
  14. package/bin/__tests__/docs-sync-on-demand.test.js +25 -20
  15. package/bin/__tests__/inspect-paths.test.js +183 -0
  16. package/bin/__tests__/installer-safety.test.js +335 -0
  17. package/bin/__tests__/legacy-authority-decoupled.test.js +14 -19
  18. package/bin/__tests__/legacy-hooks-retired.test.js +18 -8
  19. package/bin/__tests__/omp-bridge.test.js +66 -12
  20. package/bin/__tests__/omp-hooks.test.js +11 -5
  21. package/bin/__tests__/orca-session.test.js +7 -0
  22. package/bin/__tests__/package-inventory.test.js +1 -395
  23. package/bin/__tests__/privacy-paths.test.js +317 -0
  24. package/bin/__tests__/spec-narrowing.test.js +30 -134
  25. package/bin/__tests__/specs-v2-execution-closeout.test.js +0 -372
  26. package/bin/__tests__/test-runtime-dependency-closure.cjs +1 -2
  27. package/bin/__tests__/usage-hook-retired.test.js +5 -3
  28. package/bin/__tests__/validator-grounding.test.js +17 -2929
  29. package/bin/install.js +25 -0
  30. package/bin/lib/codex-install.js +134 -12
  31. package/bin/lib/context.js +1 -1
  32. package/bin/lib/managed-writer.js +2 -3
  33. package/bin/phases/claude-runtime.js +91 -25
  34. package/bin/phases/claude-settings.js +22 -1
  35. package/bin/phases/codex-hooks.js +96 -33
  36. package/bin/phases/codex-runtime.js +45 -49
  37. package/bin/phases/copy-payload.js +11 -28
  38. package/bin/phases/omp-runtime.js +6 -3
  39. package/package.json +2 -3
  40. package/src/claude/CLAUDE.md +4 -0
  41. package/src/claude/agents/code-auditor.md +9 -6
  42. package/src/claude/agents/debugger.md +3 -9
  43. package/src/claude/agents/docs-keeper.md +4 -7
  44. package/src/claude/agents/git-ops.md +2 -4
  45. package/src/claude/agents/implementer.md +6 -7
  46. package/src/claude/agents/inspector.md +2 -0
  47. package/src/claude/agents/project-manager.md +2 -2
  48. package/src/claude/agents/spec-maker.md +4 -4
  49. package/src/claude/agents/test-runner.md +8 -11
  50. package/src/claude/agents/ui-ux-designer.md +0 -1
  51. package/src/claude/gitignore +3 -0
  52. package/src/claude/hooks/__tests__/installed-root.test.js +111 -0
  53. package/src/claude/hooks/__tests__/precompact.test.js +206 -10
  54. package/src/claude/hooks/__tests__/privacy-block.test.js +27 -0
  55. package/src/claude/hooks/__tests__/session-writeenv.test.js +25 -0
  56. package/src/claude/hooks/__tests__/spec-gate.test.js +311 -340
  57. package/src/claude/hooks/__tests__/spec-state-touched.test.js +230 -8
  58. package/src/claude/hooks/__tests__/state.test.js +207 -7
  59. package/src/claude/hooks/__tests__/update-check.test.js +88 -0
  60. package/src/claude/hooks/agent.cjs +7 -6
  61. package/src/claude/hooks/inspect-block.cjs +15 -63
  62. package/src/claude/hooks/lib/inspect-paths.cjs +304 -0
  63. package/src/claude/hooks/lib/parser.cjs +1 -1
  64. package/src/claude/hooks/lib/privacy-command-analysis.cjs +191 -17
  65. package/src/claude/hooks/lib/privacy-paths.cjs +313 -0
  66. package/src/claude/hooks/lib/project-root.cjs +15 -0
  67. package/src/claude/hooks/lib/rules-reservation.cjs +36 -0
  68. package/src/claude/hooks/lib/spec-touch.cjs +67 -0
  69. package/src/claude/hooks/lib/update-check.cjs +77 -0
  70. package/src/claude/hooks/precompact.cjs +8 -15
  71. package/src/claude/hooks/privacy-block.cjs +23 -205
  72. package/src/claude/hooks/rules.cjs +6 -28
  73. package/src/claude/hooks/session.cjs +34 -117
  74. package/src/claude/hooks/spec-gate.cjs +17 -89
  75. package/src/claude/hooks/spec-state.cjs +89 -96
  76. package/src/claude/hooks/state.cjs +96 -51
  77. package/src/claude/migration-manifest.json +20 -8
  78. package/src/claude/output-styles/coding-level-0-eli5.md +9 -9
  79. package/src/claude/output-styles/coding-level-1-junior.md +3 -3
  80. package/src/claude/output-styles/coding-level-2-mid.md +3 -3
  81. package/src/claude/output-styles/coding-level-3-senior.md +2 -2
  82. package/src/claude/output-styles/coding-level-4-lead.md +5 -5
  83. package/src/claude/output-styles/coding-level-5-god.md +6 -6
  84. package/src/claude/rules/ai-dev-rules.md +1 -2
  85. package/src/claude/rules/hook-protocols.md +1 -1
  86. package/src/claude/rules/workflow.md +1 -1
  87. package/src/claude/runtime.json +0 -3
  88. package/src/claude/scripts/generate-skill-catalog.cjs +2 -2
  89. package/src/claude/scripts/provenance.cjs +14 -0
  90. package/src/claude/scripts/spec-receipt.cjs +0 -114
  91. package/src/claude/scripts/spec-resolver.cjs +41 -457
  92. package/src/claude/scripts/workflow-policy.cjs +3 -1801
  93. package/src/claude/settings/settings.json +1 -1
  94. package/src/claude/skills/agent-browser/SKILL.md +1 -1
  95. package/src/claude/skills/ai-multimodal/scripts/check_setup.py +8 -68
  96. package/src/claude/skills/ai-multimodal/scripts/gemini_batch_process.py +6 -28
  97. package/src/claude/skills/chrome-devtools/SKILL.md +14 -31
  98. package/src/claude/skills/code-review/SKILL.md +25 -108
  99. package/src/claude/skills/code-review/references/spec-compliance-review.md +4 -7
  100. package/src/claude/skills/develop/SKILL.md +22 -70
  101. package/src/claude/skills/develop/references/subagent-patterns.md +0 -6
  102. package/src/claude/skills/git/SKILL.md +1 -1
  103. package/src/claude/skills/git/references/worktree-blueprint.md +1 -1
  104. package/src/claude/skills/research/SKILL.md +24 -55
  105. package/src/claude/skills/sync/SKILL.md +12 -34
  106. package/src/claude/skills/sync/references/rebind-and-audit.md +2 -2
  107. package/src/claude/skills/test/SKILL.md +39 -123
  108. package/src/claude/skills/test/references/execution-strategy.md +11 -22
  109. package/src/claude/skills/ui-ux/SKILL.md +1 -1
  110. package/src/claude/skills/ui-ux-pro-max/SKILL.md +24 -79
  111. package/src/claude/skills/web-testing/SKILL.md +2 -20
  112. package/src/codex/AGENTS.md +8 -0
  113. package/src/codex/hooks/ask-guard.cjs +54 -0
  114. package/src/codex/hooks/inspect-block.cjs +11 -7
  115. package/src/codex/hooks/lib/inspect-paths.cjs +1 -199
  116. package/src/codex/hooks/lib/privacy-paths.cjs +1 -0
  117. package/src/codex/hooks/lib/privacy-state.cjs +98 -16
  118. package/src/codex/hooks/lib/rules-reservation.cjs +38 -0
  119. package/src/codex/hooks/lib/secret-keywords.cjs +1 -0
  120. package/src/codex/hooks/lib/spec-receipt.cjs +2 -30
  121. package/src/codex/hooks/lib/spec-session-touch.cjs +100 -0
  122. package/src/codex/hooks/lib/spec-touch.cjs +3 -0
  123. package/src/codex/hooks/lib/spec-utils.cjs +6 -45
  124. package/src/codex/hooks/lib/update-check.cjs +1 -0
  125. package/src/codex/hooks/precompact.cjs +39 -0
  126. package/src/codex/hooks/privacy-approval.cjs +28 -17
  127. package/src/codex/hooks/privacy-block.cjs +9 -215
  128. package/src/codex/hooks/rules.cjs +4 -30
  129. package/src/codex/hooks/secret-output-guardrail.cjs +1 -1
  130. package/src/codex/hooks/session.cjs +47 -11
  131. package/src/codex/hooks/spec-gate.cjs +32 -74
  132. package/src/codex/hooks/spec-state.cjs +68 -46
  133. package/src/codex/hooks/state.cjs +25 -4
  134. package/src/codex/hooks.json +39 -7
  135. package/src/codex/rules/hook-protocols.md +13 -6
  136. package/src/common/AGENTS.md +4 -10
  137. package/src/omp/extensions/cafekit-bridge.mjs +12 -4
  138. package/src/omp/hooks/privacy-block.cjs +24 -206
  139. package/bin/__tests__/change-firewall.semantic-firewall.test.js +0 -2133
  140. package/bin/__tests__/finalizer-pass-gate.semantic-firewall.test.js +0 -342
  141. package/bin/__tests__/release-preflight.semantic-firewall.test.js +0 -586
  142. package/bin/__tests__/review-receipt-c2.semantic-firewall.test.js +0 -378
  143. package/bin/__tests__/specs-v2-policy-and-scaffold.test.js +0 -665
  144. package/bin/__tests__/specs-v2-validator-grounder-contract.test.js +0 -1132
  145. package/src/claude/hooks/lib/context.cjs +0 -527
  146. package/src/claude/hooks/lib/detect.cjs +0 -474
  147. package/src/claude/scripts/change-firewall.cjs +0 -1748
  148. package/src/claude/scripts/spec-authoring-digest.cjs +0 -151
  149. package/src/claude/scripts/spec-authoring-validation.cjs +0 -126
  150. package/src/claude/scripts/spec-final-state.cjs +0 -290
  151. package/src/claude/scripts/spec-ground.cjs +0 -650
  152. package/src/claude/scripts/spec-readiness.cjs +0 -425
  153. package/src/claude/scripts/spec-scaffold.cjs +0 -2317
  154. package/src/claude/scripts/spec-semantic-model.cjs +0 -396
  155. package/src/claude/scripts/validate-spec-output.cjs +0 -3914
  156. package/src/claude/skills/code-review/references/verification-gate.md +0 -41
  157. package/src/claude/skills/specs/templates/design.md +0 -82
  158. package/src/claude/skills/specs/templates/requirements-init.md +0 -8
  159. package/src/claude/skills/specs/templates/requirements.md +0 -42
  160. package/src/claude/skills/specs/templates/spec-state.json +0 -56
  161. package/src/claude/skills/specs/templates/task.md +0 -47
  162. package/src/codex/rules/state-sync.md +0 -60
@@ -17,21 +17,81 @@ const { PLATFORMS } = require('../lib/context');
17
17
 
18
18
  const CODEX_SRC = path.join(__dirname, '../../src/codex');
19
19
  const HOOK_COMMAND_TEMPLATE = /^node "\.codex\/hooks\/([a-z0-9-]+\.cjs)"$/;
20
- // Exact shapes earlier installers wrote, kept only to recognize and upgrade them. Before
21
- // 0.16.2 the path came from Git; 0.16.2 fixed the path but still resolved `node` through
22
- // PATH. Matching the whole command keeps a hook the user wrote from ever being rewritten.
23
- const LEGACY_POSIX_COMMANDS = [
24
- /^node "\$\(git rev-parse --show-toplevel\)\/\.codex\/hooks\/([a-z0-9-]+\.cjs)"$/,
25
- /^node '(?:[^']|'\\'')*[/\\]hooks[/\\]([a-z0-9-]+\.cjs)'$/,
26
- ];
27
-
28
- function legacyPosixScript(command) {
29
- if (typeof command !== 'string') return null;
30
- for (const pattern of LEGACY_POSIX_COMMANDS) {
31
- const match = command.match(pattern);
32
- if (match) return match[1];
20
+ const LEGACY_GIT_COMMAND = /^node "\$\(git rev-parse --show-toplevel\)\/\.codex\/hooks\/([a-z0-9-]+\.cjs)"$/;
21
+ const SHELL_QUOTED = "'(?:[^']|'\\\\'')*'";
22
+ const POSIX_COMMAND = new RegExp(`^(?:PATH="\\$PATH"(?::${SHELL_QUOTED})+ )?node (${SHELL_QUOTED})$`);
23
+ const WINDOWS_LAUNCHER = 'node -e "process.argv[1]=Buffer.from(process.argv[1],\'base64url\').toString(\'utf8\');require(\'module\').runMain()" ';
24
+
25
+ // Former template values from the hooks.json history. Custom expressions never
26
+ // establish ownership, even when every handler belongs to CafeKit.
27
+ const FORMER_MATCHERS = {
28
+ PreToolUse: new Set([
29
+ 'Bash|apply_patch|Edit|Write|Read|Glob|Grep|mcp__.*',
30
+ 'Bash|exec_command|apply_patch|Edit|Write|Read|Glob|Grep|mcp__.*|shell',
31
+ ]),
32
+ PostToolUse: new Set([
33
+ 'Agent|spawn_agent|update_plan|task|Task|TodoWrite',
34
+ 'apply_patch|Edit|Write|MultiEdit',
35
+ ]),
36
+ };
37
+
38
+ function refreshManagedMatchers(config, template, ctx) {
39
+ let updated = 0;
40
+ const hooks = { ...(config.hooks || {}) };
41
+ for (const [event, former] of Object.entries(FORMER_MATCHERS)) {
42
+ if (!Array.isArray(hooks[event])) continue;
43
+ hooks[event] = hooks[event].map(group => {
44
+ const handlers = Array.isArray(group?.hooks) ? group.hooks : [];
45
+ const scripts = handlers.map(managedHandlerScript);
46
+ if (!scripts.some(Boolean)) return group;
47
+ const candidates = handlers.length && scripts.every(Boolean)
48
+ ? (template.hooks[event] || []).filter(target => {
49
+ const wanted = target.hooks.map(managedHandlerScript);
50
+ return scripts.every(script => wanted.includes(script));
51
+ }) : [];
52
+ const target = candidates.length === 1 ? candidates[0] : null;
53
+ if (target && (group.matcher || '') === (target.matcher || '')) return group;
54
+ if (target && former.has(group.matcher)) {
55
+ updated += 1;
56
+ ctx.ui.detail(` ↻ ${ctx.dryRun ? '[dry-run] ' : ''}Codex hooks: ${event} matcher updated`);
57
+ return { ...group, matcher: target.matcher };
58
+ }
59
+ ctx.ui.warn(`Codex hooks: ${event} matcher preserved; custom, mixed, or ambiguous group. Review it manually.`);
60
+ return group;
61
+ });
33
62
  }
34
- return null;
63
+ return { config: { ...config, hooks }, updated };
64
+ }
65
+
66
+ // A basename alone cannot prove ownership: users may also run tools/hooks/session.cjs.
67
+ // Only known source scripts at the native runtime path and an exact emitted launcher
68
+ // shape may be rebound or count as already registered.
69
+ const MANAGED_SCRIPTS = new Set(fs.readdirSync(path.join(CODEX_SRC, 'hooks')).filter((name) => /^[a-z0-9-]+\.cjs$/.test(name)));
70
+
71
+ function nativeScript(target) {
72
+ if (!path.posix.isAbsolute(target) && !path.win32.isAbsolute(target)) return null;
73
+ const match = target.match(/[/\\]\.codex[/\\]hooks[/\\]([a-z0-9-]+\.cjs)$/);
74
+ return match && MANAGED_SCRIPTS.has(match[1]) ? match[1] : null;
75
+ }
76
+
77
+ function managedCommandScript(command) {
78
+ if (typeof command !== 'string') return null;
79
+ const template = command.match(HOOK_COMMAND_TEMPLATE) || command.match(LEGACY_GIT_COMMAND);
80
+ if (template) return MANAGED_SCRIPTS.has(template[1]) ? template[1] : null;
81
+ const posix = command.match(POSIX_COMMAND);
82
+ if (posix) return nativeScript(posix[1].slice(1, -1).replace(/'\\''/g, "'"));
83
+ if (!command.startsWith(WINDOWS_LAUNCHER)) return null;
84
+ const encoded = command.slice(WINDOWS_LAUNCHER.length);
85
+ if (!/^[A-Za-z0-9_-]+$/.test(encoded)) return null;
86
+ const target = Buffer.from(encoded, 'base64url').toString('utf8');
87
+ return Buffer.from(target, 'utf8').toString('base64url') === encoded ? nativeScript(target) : null;
88
+ }
89
+
90
+ function managedHandlerScript(handler) {
91
+ if (handler?.type !== 'command') return null;
92
+ const fields = ['command', 'commandWindows'].filter((field) => handler[field] !== undefined);
93
+ const scripts = fields.map((field) => managedCommandScript(handler[field]));
94
+ return scripts.length > 0 && scripts.every((script) => script && script === scripts[0]) ? scripts[0] : null;
35
95
  }
36
96
 
37
97
  /** The hook script a command runs, which is stable across installs and platforms. */
@@ -57,8 +117,8 @@ function hookPathFor(script, projectRoot) {
57
117
  * nothing outside a repository and the *outer* root for a project nested inside one. Both
58
118
  * cases produced a path that does not exist, so Codex started every hook, every hook died,
59
119
  * and the gates were silently absent. The path is known at install time, so it is written
60
- * directly. The literal `/hooks/<script>.cjs` segment must survive, because hookScript()
61
- * reads it as the identity that lets a reinstall recognize CafeKit's own entries.
120
+ * directly. The literal `.codex/hooks/<script>.cjs` path and emitted launcher shape
121
+ * let managedHandlerScript() recognize CafeKit's own entries on a reinstall.
62
122
  */
63
123
  /**
64
124
  * Directories to fall back on when PATH carries no `node`.
@@ -88,7 +148,7 @@ function posixHookCommand(script, projectRoot) {
88
148
  */
89
149
  function windowsHookCommand(script, projectRoot) {
90
150
  const encoded = Buffer.from(hookPathFor(script, projectRoot), 'utf8').toString('base64url');
91
- return 'node -e "process.argv[1]=Buffer.from(process.argv[1],\'base64url\').toString(\'utf8\');require(\'module\').runMain()" ' + encoded;
151
+ return WINDOWS_LAUNCHER + encoded;
92
152
  }
93
153
 
94
154
  /** Rewrite a managed template handler into absolute launchers for both platforms. */
@@ -104,10 +164,9 @@ function materializeHookCommands(handler, projectRoot) {
104
164
  }
105
165
 
106
166
  /**
107
- * Repair launchers an older installer wrote. A reinstall treats an already-registered
108
- * script as the user's placement and leaves it alone, so without this pass every existing
109
- * project would keep its broken git-derived command forever. Only the exact byte pattern
110
- * CafeKit itself emitted is rewritten; anything else stays untouched.
167
+ * Rebind recognized CafeKit launchers after an upgrade or a project move. Registration
168
+ * preserves the user's event/matcher placement; only emitted command shapes change,
169
+ * and a custom field for another platform remains untouched.
111
170
  */
112
171
  function repairLegacyCodexLaunchers(config, projectRoot) {
113
172
  if (!config.hooks) return { config, repaired: 0 };
@@ -120,13 +179,13 @@ function repairLegacyCodexLaunchers(config, projectRoot) {
120
179
  return {
121
180
  ...group,
122
181
  hooks: group.hooks.map((handler) => {
123
- const legacy = legacyPosixScript(handler?.command);
124
- const template = typeof handler?.commandWindows === 'string'
125
- && handler.commandWindows.match(HOOK_COMMAND_TEMPLATE);
126
- if (!legacy && !template) return handler;
182
+ if (handler?.type !== 'command') return handler;
127
183
  const next = { ...handler };
128
- if (legacy) next.command = posixHookCommand(legacy, projectRoot);
129
- if (template) next.commandWindows = windowsHookCommand(template[1], projectRoot);
184
+ for (const [field, build] of [['command', posixHookCommand], ['commandWindows', windowsHookCommand]]) {
185
+ const script = managedCommandScript(handler[field]);
186
+ if (script) next[field] = build(script, projectRoot);
187
+ }
188
+ if (next.command === handler.command && next.commandWindows === handler.commandWindows) return handler;
130
189
  repaired += 1;
131
190
  return next;
132
191
  }),
@@ -139,7 +198,7 @@ function repairLegacyCodexLaunchers(config, projectRoot) {
139
198
  /** Drop CafeKit hooks the manifest has retired, leaving foreign entries untouched. */
140
199
  function pruneObsoleteCodexHooks(config, ctx) {
141
200
  const obsolete = ctx.manifest?.obsolete?.settingsHookCommandSubstrings || [];
142
- if (!config.hooks || obsolete.length === 0) return { config, removed: 0 };
201
+ if (!config.hooks) return { config, removed: 0 };
143
202
 
144
203
  let removed = 0;
145
204
  const pruned = {};
@@ -150,7 +209,8 @@ function pruneObsoleteCodexHooks(config, ctx) {
150
209
  if (!Array.isArray(group?.hooks)) { keptGroups.push(group); continue; }
151
210
  const keptHooks = group.hooks.filter((handler) => {
152
211
  const command = handler?.command || '';
153
- const isObsolete = obsolete.some((substring) => command.includes(substring));
212
+ const isObsolete = obsolete.some((substring) => command.includes(substring))
213
+ || (eventName === 'SessionStart' && managedHandlerScript(handler) === 'docs-sync.cjs');
154
214
  if (isObsolete) removed += 1;
155
215
  return !isObsolete;
156
216
  });
@@ -196,12 +256,15 @@ function mergeCodexHooks(ctx, platformKey, projectRoot = process.cwd()) {
196
256
  ctx.results.updated++;
197
257
  }
198
258
 
199
- const { config: base, repaired } = repairLegacyCodexLaunchers(pruned, projectRoot);
259
+ const { config: rebound, repaired } = repairLegacyCodexLaunchers(pruned, projectRoot);
200
260
  if (repaired > 0) {
201
- ctx.ui.detail(` ↻ ${ctx.dryRun ? '[dry-run] ' : ''}Codex hooks: rebound ${repaired} launcher(s) written by an older installer`);
261
+ ctx.ui.detail(` ↻ ${ctx.dryRun ? '[dry-run] ' : ''}Codex hooks: rebound ${repaired} launcher(s) to the current project`);
202
262
  ctx.results.updated++;
203
263
  }
204
264
 
265
+ const { config: base, updated: matcherUpdates } = refreshManagedMatchers(rebound, managed, ctx);
266
+ ctx.results.updated += matcherUpdates;
267
+
205
268
  const merged = { ...base };
206
269
  if (typeof managed.description === 'string' && typeof merged.description !== 'string') {
207
270
  merged.description = managed.description;
@@ -215,7 +278,7 @@ function mergeCodexHooks(ctx, platformKey, projectRoot = process.cwd()) {
215
278
  // it. Matching on the script rather than the whole command survives the path
216
279
  // differences between installs and platforms.
217
280
  const registered = new Set(
218
- groups.flatMap((group) => (group?.hooks || []).map((handler) => hookScript(handler?.command))).filter(Boolean),
281
+ groups.flatMap((group) => (Array.isArray(group?.hooks) ? group.hooks : []).map(managedHandlerScript)).filter(Boolean),
219
282
  );
220
283
 
221
284
  for (const managedGroup of managedGroups) {
@@ -242,7 +305,7 @@ function mergeCodexHooks(ctx, platformKey, projectRoot = process.cwd()) {
242
305
  merged.hooks[eventName] = groups;
243
306
  }
244
307
 
245
- if (added === 0 && removed === 0) {
308
+ if (added === 0 && removed === 0 && matcherUpdates === 0) {
246
309
  ctx.ui.detail(` → ${ctx.dryRun ? '[dry-run] ' : ''}Codex hooks: already registered`);
247
310
  ctx.results.unchanged++;
248
311
  }
@@ -5,7 +5,8 @@ const path = require('path');
5
5
  const { PLATFORMS } = require('../lib/context');
6
6
  const { writeManagedFile, copyManagedTree } = require('../lib/managed-writer');
7
7
  const { report, treeAction } = require('./report');
8
- const { copyRulesDirectory, copyOutputStylesDirectory } = require('./claude-runtime');
8
+ const { isGeneratedArtifact } = require('../lib/copy-utils');
9
+ const { copyRulesDirectory, copyOutputStylesDirectory, removeObsoleteRuntimeFiles } = require('./claude-runtime');
9
10
  const {
10
11
  managedRange,
11
12
  upsertManagedCodexBlock
@@ -23,27 +24,9 @@ const CODEX_OWN_RUNTIME = [
23
24
  ['runtime.json', 'runtime.json']
24
25
  ];
25
26
 
26
- /**
27
- * Delete hooks the manifest marks obsolete. Codex tracker keys are project-relative
28
- * (`.codex/hooks/…`), so the record is pruned through keyFor, and always: a record whose
29
- * file is already gone must still leave the manifest.
30
- */
27
+ /** Use the shared ownership and symlink checks for Codex runtime cleanup. */
31
28
  function removeObsoleteCodexHooks(ctx, platformKey) {
32
- const platform = PLATFORMS[platformKey];
33
- const tracker = ctx.trackers && ctx.trackers[platformKey];
34
- const obsoleteHooks = (ctx.manifest?.obsolete?.runtimeFiles || []).filter((rel) => rel.startsWith('hooks/'));
35
- for (const rel of obsoleteHooks) {
36
- const target = path.join(platform.folder, rel);
37
- const exists = fs.existsSync(target);
38
- if (!ctx.dryRun) {
39
- if (exists) fs.rmSync(target, { force: true, recursive: fs.statSync(target).isDirectory() });
40
- if (tracker) tracker.prune(tracker.keyFor(target));
41
- }
42
- if (exists) {
43
- ctx.ui.detail(` ↻ ${ctx.dryRun ? '[dry-run] ' : ''}Removed obsolete Codex runtime: ${rel}`);
44
- ctx.results.updated++;
45
- }
46
- }
29
+ removeObsoleteRuntimeFiles(ctx, platformKey);
47
30
  }
48
31
 
49
32
  function writeSourceFile(ctx, platformKey, src, dest, label) {
@@ -79,13 +62,32 @@ function installRuntimeFiles(ctx, platformKey) {
79
62
 
80
63
  mergeCodexHooks(ctx, platformKey);
81
64
 
82
- const agg = copyManagedTree({
83
- src: path.join(CODEX_SRC, 'hooks'),
84
- dest: path.join(platform.folder, 'hooks'),
85
- platformFolder: platform.folder,
86
- ctx,
87
- tracker: ctx.trackers[platformKey]
65
+ const hooksSource = path.join(CODEX_SRC, 'hooks');
66
+ const libsSource = path.join(hooksSource, 'lib');
67
+ const shimPattern = /^(?:['"]use strict['"];?\s*)?module\.exports\s*=\s*require\(['"]\.\.\/\.\.\/\.\.\/claude\/hooks\/lib\/([^'"/]+)['"]\);?\s*$/;
68
+ const sharedLibraries = fs.readdirSync(libsSource).filter(name => {
69
+ const file = path.join(libsSource, name);
70
+ if (!fs.lstatSync(file).isFile()) return false;
71
+ const match = fs.readFileSync(file, 'utf8').match(shimPattern);
72
+ return match && match[1] === name;
88
73
  });
74
+ const agg = { created: 0, updated: 0, unchanged: 0, preserved: 0, missing: 0 };
75
+ const copyHookEntry = relative => {
76
+ if (isGeneratedArtifact(path.basename(relative))) return;
77
+ const child = copyManagedTree({
78
+ src: path.join(hooksSource, relative),
79
+ dest: path.join(platform.folder, 'hooks', relative),
80
+ sourceRoot: hooksSource,
81
+ platformFolder: platform.folder, ctx, tracker: ctx.trackers[platformKey]
82
+ });
83
+ for (const key of Object.keys(agg)) agg[key] += child[key];
84
+ };
85
+ for (const name of fs.readdirSync(hooksSource)) {
86
+ if (name !== 'lib') copyHookEntry(name);
87
+ else for (const lib of fs.readdirSync(libsSource)) {
88
+ if (!sharedLibraries.includes(lib)) copyHookEntry(path.join('lib', lib));
89
+ }
90
+ }
89
91
  report(ctx, treeAction(agg), 'Codex native hooks');
90
92
  removeObsoleteCodexHooks(ctx, platformKey);
91
93
  // Keep one canonical command analyzer in the Claude source tree, then
@@ -99,27 +101,21 @@ function installRuntimeFiles(ctx, platformKey) {
99
101
  path.join(platform.folder, 'runtime.schema.json'),
100
102
  'Codex runtime schema'
101
103
  );
102
- writeSourceFile(
103
- ctx,
104
- platformKey,
105
- path.join(SRC, 'claude', 'hooks', 'lib', 'privacy-command-analysis.cjs'),
106
- path.join(platform.folder, 'hooks', 'lib', 'privacy-command-analysis.cjs'),
107
- 'Codex privacy command analyzer'
108
- );
109
- writeSourceFile(
110
- ctx,
111
- platformKey,
112
- path.join(SRC, 'claude', 'hooks', 'lib', 'secret-keywords.cjs'),
113
- path.join(platform.folder, 'hooks', 'lib', 'secret-keywords.cjs'),
114
- 'Codex secret keyword matcher'
115
- );
116
- writeSourceFile(
117
- ctx,
118
- platformKey,
119
- path.join(SRC, 'claude', 'hooks', 'lib', 'runtime-path-safety.cjs'),
120
- path.join(platform.folder, 'hooks', 'lib', 'runtime-path-safety.cjs'),
121
- 'Codex runtime path safety'
122
- );
104
+ const helperLabels = {
105
+ 'privacy-command-analysis.cjs': 'Codex privacy command analyzer',
106
+ 'privacy-paths.cjs': 'Codex privacy path analyzer',
107
+ 'secret-keywords.cjs': 'Codex secret keyword matcher',
108
+ 'runtime-path-safety.cjs': 'Codex runtime path safety',
109
+ 'inspect-paths.cjs': 'Codex inspection path analyzer',
110
+ 'spec-touch.cjs': 'Codex spec touch helpers',
111
+ 'update-check.cjs': 'Codex update check'
112
+ };
113
+ for (const name of sharedLibraries) {
114
+ writeSourceFile(ctx, platformKey,
115
+ path.join(SRC, 'claude', 'hooks', 'lib', name),
116
+ path.join(platform.folder, 'hooks', 'lib', name),
117
+ helperLabels[name] || `Codex hook helper: ${name}`);
118
+ }
123
119
  }
124
120
 
125
121
  function installNativeRuleOverrides(ctx, platformKey) {
@@ -167,7 +163,7 @@ function installManagedAgentsMd(ctx) {
167
163
  function installCodexRuntime(ctx, platformKey) {
168
164
  if (platformKey !== 'codex') return ctx;
169
165
  installRuntimeFiles(ctx, platformKey);
170
- copyRulesDirectory(ctx, platformKey);
166
+ copyRulesDirectory(ctx, platformKey, new Set(fs.readdirSync(path.join(CODEX_SRC, 'rules'))));
171
167
  copyOutputStylesDirectory(ctx, platformKey);
172
168
  installNativeRuleOverrides(ctx, platformKey);
173
169
  installManagedAgentsMd(ctx);
@@ -94,37 +94,20 @@ function copyPlatformFiles(ctx, platformKey) {
94
94
  report(ctx, treeAction(agg), 'Skill: specs');
95
95
  }
96
96
 
97
- // Keep spec templates in sync (Claude command runtime reads these).
97
+ // The specs skill tree above carries its templates; remove the ones retired
98
+ // with the spec.json planning flow from earlier installs.
98
99
  if (platformKey === 'claude' || platformKey === 'codex') {
99
- const legacyInitTemplate = path.join(platform.skillsDir, 'specs', 'templates', 'init.json');
100
- if (platformKey === 'claude' && fs.existsSync(legacyInitTemplate)) {
101
- if (!ctx.dryRun) fs.rmSync(legacyInitTemplate, { force: true });
102
- ctx.ui.detail(` ↻ ${ctx.dryRun ? '[dry-run] ' : ''}Removed legacy template: ${legacyInitTemplate}`);
100
+ const retiredTemplates = ['init.json', 'spec-state.json', 'requirements-init.md', 'requirements.md', 'design.md', 'task.md'];
101
+ for (const fileName of retiredTemplates) {
102
+ const retired = path.join(platform.skillsDir, 'specs', 'templates', fileName);
103
+ if (!fs.existsSync(retired)) continue;
104
+ if (!ctx.dryRun) {
105
+ fs.rmSync(retired, { force: true });
106
+ if (tracker) tracker.prune(tracker.keyFor(retired));
107
+ }
108
+ ctx.ui.detail(` ↻ ${ctx.dryRun ? '[dry-run] ' : ''}Removed legacy template: ${retired}`);
103
109
  ctx.results.updated++;
104
110
  }
105
-
106
- const specTemplates = [
107
- 'spec-state.json',
108
- 'requirements-init.md',
109
- 'requirements.md',
110
- 'design.md',
111
- 'research.md',
112
- 'task.md'
113
- ];
114
-
115
- specTemplates.forEach((fileName) => {
116
- const src = path.join(specSkillSource, 'templates', fileName);
117
- const dest = path.join(platform.skillsDir, 'specs', 'templates', fileName);
118
- const { action } = writeManagedFile({
119
- src,
120
- dest,
121
- platformFolder,
122
- ctx,
123
- tracker,
124
- transform: bodyTransform
125
- });
126
- report(ctx, action, `template: ${fileName}`);
127
- });
128
111
  }
129
112
 
130
113
  // Core skills plus the optional document bundle selected for this runtime.
@@ -129,16 +129,19 @@ function installBridge(ctx, platformKey) {
129
129
  }
130
130
 
131
131
  /**
132
- * Delete hooks the manifest marks obsolete. Mirrors removeObsoleteClaudeRuntimeFiles, but the
132
+ * Delete hooks, scripts and skill files the manifest marks obsolete. Mirrors removeObsoleteClaudeRuntimeFiles, but the
133
133
  * omp tracker records keys relative to the project root (`.omp/hooks/…`), so the record is
134
134
  * pruned through keyFor rather than by the bare `hooks/…` path.
135
135
  */
136
136
  function removeObsoleteOmpHooks(ctx, platformKey) {
137
137
  const platform = PLATFORMS[platformKey];
138
138
  const tracker = ctx.trackers && ctx.trackers[platformKey];
139
- const obsoleteHooks = (ctx.manifest?.obsolete?.runtimeFiles || []).filter((rel) => rel.startsWith('hooks/'));
139
+ const obsoleteHooks = (ctx.manifest?.obsolete?.runtimeFiles || []).filter((rel) => rel.startsWith('hooks/') || rel.startsWith('scripts/') || rel.startsWith('skills/'));
140
140
  for (const rel of obsoleteHooks) {
141
- const target = path.join(platform.folder, rel);
141
+ // Skill files live in the shared skills directory, not under the runtime folder.
142
+ const target = rel.startsWith('skills/')
143
+ ? path.join(platform.skillsDir, rel.slice('skills/'.length))
144
+ : path.join(platform.folder, rel);
142
145
  if (!fs.existsSync(target)) continue;
143
146
  if (!ctx.dryRun) {
144
147
  fs.rmSync(target, { force: true, recursive: fs.statSync(target).isDirectory() });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haposoft/cafekit",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "description": "Native spec-driven workflows for Claude Code and Codex CLI. Bundles CafeKit skills, runtime hooks, agents, and installer scaffolding.",
5
5
  "author": "Haposoft <nghialt@haposoft.com>",
6
6
  "license": "MIT",
@@ -12,8 +12,7 @@
12
12
  "test": "node scripts/run-skill-self-tests.mjs",
13
13
  "test:package": "node --test bin/__tests__/package-inventory.test.js",
14
14
  "test:secrets": "node --test bin/__tests__/secret-scanner.test.js",
15
- "test:policy": "node --test bin/__tests__/develop-contract.test.js",
16
- "release:freeze": "node scripts/benchmark-workflow.mjs freeze"
15
+ "test:policy": "node --test bin/__tests__/develop-contract.test.js"
17
16
  },
18
17
  "files": [
19
18
  "bin",
@@ -7,6 +7,10 @@
7
7
 
8
8
  ## Claude Code runtime
9
9
 
10
+ Project notes (commands, files to leave unchanged, slow or expensive operations)
11
+ belong in the root AGENTS.md after the CAFEKIT CORE END marker; Claude Code reads
12
+ them through `@AGENTS.md`.
13
+
10
14
  - Edit project skills under `.claude/skills/`, not `~/.claude/skills`.
11
15
  - Run Python skill scripts with the skill venv:
12
16
  - macOS/Linux: `.claude/skills/.venv/bin/python3 scripts/<script>.py`
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: code-auditor
3
3
  tools: Glob, Grep, Read, Bash, WebFetch, WebSearch
4
- description: "Source Code Auditor. Verifies code quality, severities (🔴 Critical / 🟠 High / 🟡 Medium / 🔵 Low), Automatic Criticals, and task/spec completion drift. Returns one review verdict from the shared surface: PASS | PASS_WITH_WARNINGS | FAIL | BLOCKED."
4
+ description: "Source Code Auditor. Verifies code quality, severities (🔴 Critical / 🟠 High / 🟡 Medium / 🔵 Low), Automatic Criticals, and task/spec completion drift. Returns one review verdict from the shared surface: PASS | PASS_WITH_WARNINGS | FAIL | BLOCKED. It never runs tests. When it states execution proof as unavailable (owned by /cf:test) and the user asked only for a review, a caller relaying its review must not run tests itself to fill that gap and offers /cf:test instead; a user request to run tests, or a workflow that owns its own test step, is unaffected."
5
5
  ---
6
6
 
7
7
  # Code Auditor — Source Code Inspector
@@ -22,10 +22,11 @@ Never report a test result, pass count, or exit code as the review's evidence.
22
22
  Without a `test-proof-v1` handoff, state execution proof as unavailable (owned by
23
23
  `cf:test`): write the proof line exactly as `**Execution proof:** unavailable (owned by cf:test)`,
24
24
  do not otherwise say whether tests pass, and still return the verdict.
25
+ When the proof line says unavailable, end the report with the caller line of the template, verbatim: if the user asked only for a review, the session that relays it must not run tests itself to fill the proof gap and offers `/cf:test` instead; a user request to run tests, or a workflow that owns its test step, is unaffected. With a consumed `test-proof-v1` handoff, omit the caller line.
25
26
 
26
- ## Pre-Review: Task / Spec Compliance (MANDATORY)
27
+ ## Pre-Review: Task / Spec Compliance
27
28
 
28
- If the prompt includes task file paths, requirement IDs, completion criteria, or design contracts, you MUST read them before reviewing code.
29
+ If the prompt includes task file paths, requirement IDs, completion criteria, or design contracts, read them before reviewing code.
29
30
  If the prompt says `SPEC COMPLIANCE REVIEW ONLY`, do not perform a general
30
31
  quality review yet. For process-first work, first prove the implementation
31
32
  matches `plan.md` accepted GATE-SCOPE/GATE-REVIEW decisions and the active flat `task-NN-*.md` Outcome,
@@ -47,9 +48,9 @@ Any missing declared deliverable, placeholder-only wiring, or contract drift is
47
48
  Any scoped behavior omitted, unapproved behavior added, orphaned component/service/route/command/worker/provider/reducer, unmounted UI, unregistered route, uncalled loader/service, or unreachable runtime surface is a **Critical** issue even if tests/build pass.
48
49
  If the task/spec explicitly names Better Auth, Hono, Next.js proxy routes, Redis, Drizzle, or any other concrete choice, replacing it with a custom simplification is a **Critical** issue unless the spec was amended first.
49
50
 
50
- ## Pre-Review: Blast Radius Check (MANDATORY)
51
+ ## Pre-Review: Blast Radius Check
51
52
 
52
- Before reading any specific logic, you MUST run a Dependency Scope Check (Blast Radius):
53
+ Before reading any specific logic, run a Dependency Scope Check (Blast Radius):
53
54
  1. Obtain the list of modified functions/components exported from the changed files.
54
55
  2. Run a global `Grep` across `src/` to find ALL files that import or call these functions.
55
56
  3. Identify if the signature change or internal state mutation breaks these dependents.
@@ -149,11 +150,13 @@ Keep `## Review Report`, its headings and the severity labels verbatim in Englis
149
150
 
150
151
  ### ✅ Positive Observations
151
152
  - [Acknowledge good code, good patterns]
153
+
154
+ **For the caller:** this review ran no tests by design. If execution proof is unavailable and the user asked only for a review, do not run tests yourself to fill the gap; offer `/cf:test` to the user instead. A user request to run tests, or a workflow that owns its test step, is unaffected.
152
155
  ```
153
156
 
154
157
  ## Pass/Fail Thresholds (Used in Quality Gate)
155
158
 
156
- When called from `develop` Step 4 (Quality Gate Auto-Fix):
159
+ When called from the `cf:develop` quality gate (`references/quality-gate.md`):
157
160
 
158
161
  | Condition | Result |
159
162
  |-----------|--------|
@@ -8,8 +8,6 @@ tools: Glob, Grep, Read, Bash, WebFetch, WebSearch
8
8
 
9
9
  You are a veteran incident responder who has survived hundreds of production outages. You think in evidence chains: every hypothesis must be backed by log lines, stack traces, metrics, browser evidence, or code facts. You never guess when you can grep.
10
10
 
11
- **IMPORTANT**: Ensure token efficiency while maintaining high quality.
12
-
13
11
  ## Core Competencies
14
12
 
15
13
  You excel at:
@@ -23,7 +21,7 @@ You excel at:
23
21
  - **Side-Effect Analysis**: Mapping blast radius and defining the checks needed to prove a fix does not regress nearby behavior
24
22
  - **Strict Protocol (MANDATORY)**: Read the relevant manuals in `.claude/references/debugger/` before conclusions. At minimum read `core-philosophy.md`, `root-cause-tracing.md`, `verification-protocol.md`, and `side-effect-gate.md` before recommending a fix direction. Add domain references such as `log-ci-analysis.md`, `frontend-verification.md`, `performance-diagnostics.md`, or `condition-based-waiting.md` when they apply.
25
23
 
26
- **IMPORTANT**: Analyze the skills catalog and activate the skills that are needed for the task during the process.
24
+ Activate a skill from the catalog when it covers part of the investigation.
27
25
 
28
26
  ## Operating Boundary
29
27
 
@@ -52,10 +50,7 @@ When investigating issues, you will:
52
50
  - Read repository instructions, README, and relevant existing docs, then verify their claims against current source, tests, config, logs, and runtime evidence.
53
51
  - Use `/cf:scout` or focused local `rg`/reads for missing or conflicting context.
54
52
  - Use `repomix` only when the user authorized the broad snapshot and it materially improves a wide investigation. Never create or refresh documentation merely to satisfy Debug.
55
- - When you are given a Github repository URL, use `repomix --remote <github-repo-url>` bash command to generate a fresh codebase summary:
56
- ```bash
57
- # usage: repomix --remote <github-repo-url>
58
- ```
53
+ - For a GitHub repository URL, the same authorization applies to `repomix --remote <github-repo-url>`.
59
54
 
60
55
  3. **Analysis Process**
61
56
  - Correlate events across different log sources
@@ -122,8 +117,7 @@ You will:
122
117
  - Highlight critical findings that require immediate attention
123
118
  - Offer risk assessments for proposed solutions
124
119
  - Maintain a systematic, methodical approach to problem-solving
125
- - **IMPORTANT:** Sacrifice grammar for the sake of concision when writing reports.
126
- - **IMPORTANT:** In reports, list any unresolved questions at the end, if any.
120
+ - Keep reports concise, and list any unresolved questions at the end.
127
121
 
128
122
  ## Report Output
129
123
 
@@ -9,7 +9,7 @@ tools: Glob, Grep, Read, Edit, Write, Bash, WebFetch, TaskCreate, TaskGet, TaskU
9
9
 
10
10
  You are the authoritative **Documentation Guardian** for this repository.
11
11
  Stale docs and phantom specs are worse than no docs — they waste developer hours.
12
- Your core operational rule: **Read the code FIRST, verify it WORKS, THEN write the words.**
12
+ Your core operational rule: read the code and verify it works before you write about it.
13
13
 
14
14
  You juggle two parallel universes defined by the `specs` ecosystem: The agile feature specification lifecycle (`specs/`) and the global project documentation (`docs/`).
15
15
 
@@ -59,16 +59,13 @@ When called from `develop` after a verified task is complete:
59
59
  - If impact is `minor` or `major`, prefer surgical edits to `docs/project-overview-pdr.md`, `docs/system-architecture.md`, `docs/code-standards.md`, changelog/roadmap files, or other already-existing docs
60
60
  - Do NOT run `repomix` just because code changed; use it only if direct verification is insufficient
61
61
 
62
- ### 5. File Size Discipline
63
- If any doc file exceeds **800 LOC**, enforce modularity:
64
- 1. Identify semantic boundaries (distinct topics that can stand alone).
65
- 2. Split into `docs/{topic}/index.md` + part files.
66
- 3. Hyperlink heavily. Do not repeat context unnecessarily.
62
+ ### 5. File Size
63
+ Split a doc only when it holds distinct topics that readers need separately, never just to meet a line count; link the parts instead of repeating context.
67
64
 
68
65
  ## Writing Style
69
66
  - Lead with purpose, not background prose.
70
67
  - Use Markdown tables instead of paragraph lists for structured data.
71
- - Absolute ban on fluff. One concept per section.
68
+ - One concept per section; cut sentences that carry no information.
72
69
  - Always use relative paths for internal linking: `[text](./path.md)`.
73
70
 
74
71
  ## Integration Points & Hooks
@@ -1,13 +1,11 @@
1
1
  ---
2
2
  name: git-ops
3
- description: Executes staging, committing, and pushing branches using conventional commits conventions. Use this when the user says "commit", "push", or after completing a feature/bug fix.
3
+ description: Executes staging, committing, and pushing branches using conventional commits conventions. Use this when the user asks to commit or push; finishing a feature or fix does not by itself authorize either.
4
4
  model: haiku
5
5
  tools: Glob, Grep, Read, Bash, TaskCreate, TaskGet, TaskUpdate, TaskList, SendMessage
6
6
  ---
7
7
 
8
- You are the Git Operations Specialist (Chief of the Git Station). You must execute operations with EXTREME SPEED, typically finishing within 2-4 tool calls. Do not perform lengthy explorations. Simply activate the `git` skill and finalize the operation.
9
-
10
- **CRITICAL REQUIREMENT**: Strictly optimize for token efficiency and operational speed, yet maintain high-quality conventional git histories.
8
+ You are the Git Operations Specialist. Activate the `git` skill and carry out the requested operation; git operations are narrow, so inspect only what the commit or push needs.
11
9
 
12
10
  ## Team Mode
13
11
 
@@ -10,25 +10,24 @@ tools: Glob, Grep, Read, Edit, Write, NotebookEdit, Bash, WebFetch, WebSearch
10
10
  You are a senior engineer specialized in turning one approved CafeKit task into
11
11
  real code. Work uses `specs/<feature>/plan.md` plus one flat
12
12
  `task-NN-*.md`.
13
- Your code must be production-ready on the first pass — not prototypes.
14
- Any logic gaps must be clarified BEFORE typing, not discovered after bugs ship.
13
+ Write production code, not prototypes, and resolve logic gaps in the task before coding rather than after.
15
14
 
16
15
  ## Core Principles
17
16
 
18
17
  - **YAGNI**: Do not add any feature outside the Spec.
19
18
  - **KISS**: Always prefer the simplest solution.
20
19
  - **DRY**: No code duplication. Reuse existing utils/helpers.
21
- - **Token efficiency**: Write concisely, report briefly, no prose.
20
+ - **Reports**: brief and specific to the task.
22
21
  - **Canonical state ownership:** This rule applies under every dispatch mode.
23
22
  Do NOT edit `plan.md`, task `Status:`, or inline `## Receipt`; the controller
24
23
  is the sole process-first state-and-proof writer.
25
24
  - **Surgical Reading (Large Files):** Never use blanket `Read` commands on files > 800 lines. Use nested `Grep` or chunked reading (offset/limit) to surgically target modified points.
26
- - **Component Scaffold Limit:** Any React/UI component file that exceeds 200 LOC must trigger a proactive modularization step (split into smaller child files).
25
+ - **Component size:** Consider splitting a source file over 200 lines when it has a clear logical boundary and the split stays inside the task's Ownership.
27
26
 
28
27
 
29
28
  ## Self-Check Checklist (Before Reporting Complete)
30
29
 
31
- - [ ] Every async operation has explicit `try/catch` or `.catch()` — no silent failures allowed.
30
+ - [ ] Errors are handled at meaningful boundaries and none is silently swallowed; do not wrap every call defensively.
32
31
  - [ ] All external data (API requests, form inputs, env vars) is validated at system boundaries.
33
32
  - [ ] No `TODO` or `FIXME` blocking the main flow. If a workaround is needed, it must have an explanatory comment.
34
33
  - [ ] Public API/Interface matches the Spec requirements exactly — do not add or remove fields arbitrarily.
@@ -79,7 +78,7 @@ Upon completion, output a concise report in this format:
79
78
  ```markdown
80
79
  ## Implementation Report
81
80
 
82
- ### Status: [completed | in_progress | blocked]
81
+ ### Status: [DONE | CONCERNS | BLOCKED | NEEDS_INFO]
83
82
 
84
83
  ### Files Modified/Created
85
84
  - `path/to/file.ts` — Brief description of changes
@@ -111,4 +110,4 @@ When dispatched into an isolated git worktree by `cf:develop --parallel`:
111
110
  - Preserve the global canonical state ownership rule above.
112
111
  - For process-first work, do NOT touch files outside the task's Ownership
113
112
  boundary.
114
- - Commit your completed work: `git commit -m "task(<id>): <title>"` — an uncommitted worktree cannot be merged back.
113
+ - Commit only with the user's authorization, as a conventional commit with no task ID in the message; an uncommitted worktree cannot be merged back, so without authorization report the uncommitted state to the controller.
@@ -48,6 +48,8 @@ Before packaging your report, verify:
48
48
 
49
49
  ## Report Format
50
50
 
51
+ Cite each file as `path:line` where a line matters, as `cf:scout` expects from delegated scouting.
52
+
51
53
  ```markdown
52
54
  # Scout Report
53
55