@iowarp/clio-coder 0.3.3 → 0.3.4

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 (244) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +3 -3
  4. package/dist/{acp-P2AQILE2.js → acp-S5R4RR5B.js} +7 -6
  5. package/dist/{agents-72W3BI7I.js → agents-P6DMMVZY.js} +24 -21
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-5TWEIYDN.js → auth-2XCZLPKS.js} +12 -8
  8. package/dist/{chunk-GGXXDWE4.js → chunk-22NAGB7X.js} +2 -2
  9. package/dist/{chunk-2DJ2KNFG.js → chunk-2LZI5CAG.js} +133 -13
  10. package/dist/{chunk-OAO4GE4M.js → chunk-2TZWSW76.js} +2 -2
  11. package/dist/{chunk-OOJYHWRB.js → chunk-34475P3I.js} +2 -2
  12. package/dist/{chunk-4XUGQOHA.js → chunk-35MKKU5R.js} +4 -4
  13. package/dist/{chunk-LM5TQCJZ.js → chunk-3HZ5RWN2.js} +5 -5
  14. package/dist/{chunk-AGYYIBLL.js → chunk-3JLKSKD7.js} +2 -2
  15. package/dist/{chunk-KZWTDYJF.js → chunk-4JUF2NNX.js} +7 -7
  16. package/dist/{chunk-ZDOOVTXZ.js → chunk-4OC57DA6.js} +27 -4
  17. package/dist/chunk-5M54SPOL.js +926 -0
  18. package/dist/{chunk-STBPMHSX.js → chunk-7RXG6QRZ.js} +51 -11
  19. package/dist/{chunk-X6IAEBZR.js → chunk-A2GZF7DC.js} +5 -5
  20. package/dist/{chunk-A3CYT5EX.js → chunk-AD2SYQYC.js} +55 -2
  21. package/dist/chunk-AOCYTWAV.js +449 -0
  22. package/dist/chunk-BEY543CS.js +258 -0
  23. package/dist/{chunk-6N5PTWMY.js → chunk-BP4OYD6A.js} +32 -13
  24. package/dist/chunk-BPGS2WCQ.js +612 -0
  25. package/dist/{chunk-V6RTAOC2.js → chunk-BRXQQJFP.js} +8 -8
  26. package/dist/chunk-CFGTUFWB.js +67 -0
  27. package/dist/{chunk-CBCAPZAA.js → chunk-E25LMLRW.js} +2 -2
  28. package/dist/{chunk-DUYJ5IO6.js → chunk-EDRHSCIE.js} +4 -4
  29. package/dist/{chunk-XBXAASKX.js → chunk-EFADSJET.js} +2 -2
  30. package/dist/{chunk-M6SHUN7Q.js → chunk-FO5ZOVUY.js} +2 -2
  31. package/dist/chunk-FYYLNIL5.js +313 -0
  32. package/dist/{chunk-FNTMWMX5.js → chunk-HV5X7OR2.js} +14 -12
  33. package/dist/{chunk-TZTZS7QK.js → chunk-HXG4IURW.js} +5 -3
  34. package/dist/{chunk-5UFT4SUX.js → chunk-K6WL7QZT.js} +3 -3
  35. package/dist/chunk-K7VKOLQQ.js +15 -0
  36. package/dist/{chunk-BMEMKKIT.js → chunk-KOHPCX4K.js} +2 -2
  37. package/dist/{chunk-OC7FIQPC.js → chunk-KRPY7NTG.js} +10 -7
  38. package/dist/chunk-LL4KHSZI.js +22 -0
  39. package/dist/{chunk-DSELYM6W.js → chunk-MEQ45TQ4.js} +15 -9
  40. package/dist/{chunk-5UUP6MWO.js → chunk-MV3K5QF2.js} +5 -436
  41. package/dist/{chunk-6SGHMWE3.js → chunk-N4CZJQRK.js} +5 -5
  42. package/dist/{chunk-TZK7PACC.js → chunk-NILBFAPG.js} +14 -8
  43. package/dist/chunk-OZNBF4L3.js +23 -0
  44. package/dist/{verify-375KUB3Y.js → chunk-PCZJO5TI.js} +127 -42
  45. package/dist/chunk-QQK64KLB.js +1360 -0
  46. package/dist/{chunk-SRDMMSEP.js → chunk-QQL5RT5M.js} +979 -1619
  47. package/dist/{chunk-LZSJBIVT.js → chunk-QWU7ZBO7.js} +70 -720
  48. package/dist/{chunk-2TLUCQVG.js → chunk-RD5U66HV.js} +3 -3
  49. package/dist/{chunk-OKGUZO2U.js → chunk-SPULKLCF.js} +4 -3
  50. package/dist/{chunk-OQ33BKR3.js → chunk-TTNYS3EA.js} +3 -60
  51. package/dist/chunk-TW3WDMVS.js +677 -0
  52. package/dist/chunk-TZSKNMZG.js +434 -0
  53. package/dist/{chunk-7MNJORFF.js → chunk-UL3WSD3F.js} +6 -1
  54. package/dist/{chunk-PIWWS5BL.js → chunk-UZHIZC5S.js} +7 -7
  55. package/dist/{chunk-UFIIWP2H.js → chunk-VAWWTKDP.js} +8 -8
  56. package/dist/{chunk-COU2UHX6.js → chunk-VEZEGCGW.js} +170 -2
  57. package/dist/{chunk-LW6DSM3M.js → chunk-VMNQ6OZA.js} +98 -202
  58. package/dist/chunk-VSNATDE6.js +122 -0
  59. package/dist/chunk-W6GROXXM.js +69 -0
  60. package/dist/chunk-WPQLXFOZ.js +375 -0
  61. package/dist/{chunk-ORBHGJC5.js → chunk-WR67VIZY.js} +3 -3
  62. package/dist/{chunk-PAJK6MAQ.js → chunk-X6COSD2O.js} +5 -5
  63. package/dist/chunk-ZGVHUX3M.js +66 -0
  64. package/dist/{chunk-LWLEKMDQ.js → chunk-ZYKPLLNQ.js} +510 -547
  65. package/dist/cli/index.js +27 -23
  66. package/dist/{clio-JOU4FXVA.js → clio-J5JIOIDS.js} +7 -6
  67. package/dist/{code-nav-7AX6FYE6.js → code-nav-AXCXSBHX.js} +5 -3
  68. package/dist/{config-XCDVKR23.js → config-OEBMIN2U.js} +37 -27
  69. package/dist/{configure-4GAP54ZW.js → configure-PUQOSIXQ.js} +16 -13
  70. package/dist/{context-5VKGUVJJ.js → context-EKDCKUUZ.js} +82 -7
  71. package/dist/{context-4UOGGLQ5.js → context-MGSE4Z2T.js} +33 -23
  72. package/dist/{context-77FM5DV5.js → context-URSXPBCK.js} +17 -9
  73. package/dist/{context-clear-XXJRLCJJ.js → context-clear-KDAJRNUK.js} +33 -23
  74. package/dist/context-working-set-SBKMPPI2.js +1552 -0
  75. package/dist/{dispatch-runner-QPRDDBDX.js → dispatch-runner-MSWN72NK.js} +43 -29
  76. package/dist/{doctor-HR46URBJ.js → doctor-7BSE27PJ.js} +10 -10
  77. package/dist/{eval-XSSNATB4.js → eval-IZGDOO4H.js} +9 -8
  78. package/dist/{evidence-6HG2PY2B.js → evidence-SR7WXB5B.js} +51 -23
  79. package/dist/{evolve-K7YU3NCY.js → evolve-K7VE2CBX.js} +30 -20
  80. package/dist/{fleet-VY3HHKN6.js → fleet-7XMJNQNF.js} +48 -38
  81. package/dist/{fleet-preflight-DDN536IT.js → fleet-preflight-AQNAH644.js} +3 -3
  82. package/dist/{init-JYGXI3FK.js → init-JGNPAYXT.js} +41 -31
  83. package/dist/{memory-WFZMGYHX.js → memory-4ALKDJ4Q.js} +32 -22
  84. package/dist/{models-I5QWSEOM.js → models-ZMMLFJNN.js} +22 -19
  85. package/dist/{monitor-GE4ID3IA.js → monitor-2F3T5KHP.js} +55 -43
  86. package/dist/{orchestrator-EM5MC3HM.js → orchestrator-ORHT43JB.js} +572 -388
  87. package/dist/{reset-L2FQEE3E.js → reset-NXGTYNUO.js} +4 -3
  88. package/dist/{run-ZU3QMZPZ.js → run-RF4WJGMT.js} +51 -41
  89. package/dist/{share-S5BZQC5I.js → share-UT3W6E4M.js} +5 -4
  90. package/dist/{skills-X5VXCRNQ.js → skills-PSACKC5Q.js} +2 -2
  91. package/dist/{skills-eval-WKIHWTHR.js → skills-eval-WJSI55RZ.js} +34 -24
  92. package/dist/{targets-SNCPI2NR.js → targets-PIIRAOYS.js} +23 -20
  93. package/dist/{terminal-lease-BNAHVHBS.js → terminal-lease-ULWXWNVY.js} +4 -3
  94. package/dist/{upgrade-JQHHPQ4K.js → upgrade-346TZ6AV.js} +18 -17
  95. package/dist/{usage-OR4O5SMZ.js → usage-6KKXR32N.js} +34 -24
  96. package/dist/verifiers-4UUM6TEE.js +1214 -0
  97. package/dist/verify-X5HDROLA.js +25 -0
  98. package/dist/{wiki-generate-UEXP2ARI.js → wiki-generate-7STOCIFZ.js} +42 -31
  99. package/dist/worker/entry.js +33 -24
  100. package/docs/README.md +7 -6
  101. package/docs/acp.md +1 -1
  102. package/docs/alcf-provider.md +1 -1
  103. package/docs/architecture.md +2 -2
  104. package/docs/artifact-versions.md +1 -1
  105. package/docs/built-in-agents.md +1 -1
  106. package/docs/capacity-and-scheduling.md +1 -1
  107. package/docs/commands-and-modes.md +53 -21
  108. package/docs/config-knobs-audit.md +1 -2
  109. package/docs/configuration-and-targets.md +15 -1
  110. package/docs/context-engine.md +64 -12
  111. package/docs/context-working-set.md +194 -0
  112. package/docs/development-pipeline.md +1 -1
  113. package/docs/documentation-coverage.md +5 -5
  114. package/docs/documentation-guide.md +6 -5
  115. package/docs/environment-variables.md +2 -1
  116. package/docs/eval-runner.md +1 -1
  117. package/docs/evals-internal.md +14 -1
  118. package/docs/evidence-and-memory.md +74 -2
  119. package/docs/evolution.md +1 -1
  120. package/docs/exit-codes-and-output.md +1 -1
  121. package/docs/extensions-and-sharing.md +2 -2
  122. package/docs/fleet-dispatch.md +22 -7
  123. package/docs/glossary.md +21 -1
  124. package/docs/installation-and-lifecycle.md +2 -2
  125. package/docs/middleware-and-components.md +1 -1
  126. package/docs/model-catalog.md +7 -9
  127. package/docs/observability.md +4 -4
  128. package/docs/proactive-memory.md +1 -1
  129. package/docs/prompt-envelope-and-tools.md +4 -4
  130. package/docs/provider-adapter-cookbook.md +1 -1
  131. package/docs/release-cut-checklist.md +31 -31
  132. package/docs/safety-model.md +23 -4
  133. package/docs/scientific-validation.md +21 -3
  134. package/docs/session-lifecycle.md +3 -3
  135. package/docs/skills-marketplace.md +1 -1
  136. package/docs/tool-usage.md +79 -12
  137. package/docs/trace-store.md +1 -1
  138. package/docs/troubleshooting.md +1 -1
  139. package/docs/tui-design.md +1 -1
  140. package/docs/worker-dispatch-mechanics.md +11 -1
  141. package/package.json +8 -11
  142. package/skills/meta/clio-test/SKILL.md +20 -17
  143. package/skills/meta/clio-test/evals.md +3 -3
  144. package/skills/meta/clio-test/references/harness.md +35 -6
  145. package/skills/meta/clio-test/references/test-map.md +20 -10
  146. package/skills/registry.yaml +2 -2
  147. package/skills/skill-marketplace.json +1 -1
  148. package/src/cli/context-working-set.ts +513 -0
  149. package/src/cli/context.ts +8 -0
  150. package/src/cli/evidence.ts +20 -2
  151. package/src/cli/index.ts +4 -0
  152. package/src/cli/verifiers.ts +325 -0
  153. package/src/core/bash-exec.ts +39 -14
  154. package/src/core/bus-events.ts +19 -4
  155. package/src/core/config.ts +54 -0
  156. package/src/core/defaults.ts +50 -3
  157. package/src/core/verification-scripts.ts +6 -0
  158. package/src/domains/agents/builtins/verifier.md +3 -0
  159. package/src/domains/config/classify.ts +1 -0
  160. package/src/domains/context/working-set/contract.ts +161 -0
  161. package/src/domains/context/working-set/defaults.ts +28 -0
  162. package/src/domains/context/working-set/engine.ts +203 -0
  163. package/src/domains/context/working-set/fold.ts +62 -0
  164. package/src/domains/context/working-set/horizon.ts +38 -0
  165. package/src/domains/context/working-set/marker.ts +103 -0
  166. package/src/domains/context/working-set/path-index.ts +436 -0
  167. package/src/domains/context/working-set/payload.ts +152 -0
  168. package/src/domains/context/working-set/policies/age-horizon.ts +55 -0
  169. package/src/domains/context/working-set/policies/index.ts +21 -0
  170. package/src/domains/context/working-set/policies/structural.ts +160 -0
  171. package/src/domains/context/working-set/project.ts +132 -0
  172. package/src/domains/context/working-set/protect.ts +109 -0
  173. package/src/domains/context/working-set/recall.ts +177 -0
  174. package/src/domains/context/working-set/replay/controls.ts +112 -0
  175. package/src/domains/context/working-set/replay/load-clio.ts +199 -0
  176. package/src/domains/context/working-set/replay/metrics.ts +185 -0
  177. package/src/domains/context/working-set/replay/reference-graph.ts +79 -0
  178. package/src/domains/context/working-set/replay/report.ts +139 -0
  179. package/src/domains/context/working-set/replay/runner.ts +325 -0
  180. package/src/domains/context/working-set/replay/synthetic.ts +422 -0
  181. package/src/domains/context/working-set/replay/trace.ts +21 -0
  182. package/src/domains/context/working-set/visible.ts +54 -0
  183. package/src/domains/evidence/build.ts +112 -45
  184. package/src/domains/evidence/eval.ts +24 -7
  185. package/src/domains/evidence/index.ts +53 -0
  186. package/src/domains/evidence/ordering.ts +12 -0
  187. package/src/domains/evidence/run-trust.ts +221 -0
  188. package/src/domains/evidence/store.ts +46 -6
  189. package/src/domains/evidence/trust-status.ts +854 -0
  190. package/src/domains/evidence/types.ts +26 -0
  191. package/src/domains/middleware/memory-intervention.ts +3 -0
  192. package/src/domains/middleware/stalled-turn.ts +165 -4
  193. package/src/domains/safety/autonomy.ts +1 -1
  194. package/src/domains/safety/default-path-policy.ts +8 -0
  195. package/src/domains/safety/finish-contract.ts +4 -3
  196. package/src/domains/safety/policy-engine.ts +48 -6
  197. package/src/domains/session/compaction/compact.ts +23 -1
  198. package/src/domains/session/compaction/cut-point.ts +2 -0
  199. package/src/domains/session/compaction/tokens.ts +16 -1
  200. package/src/domains/session/context-ledger.ts +2 -0
  201. package/src/domains/session/entries.ts +107 -1
  202. package/src/domains/session/manager.ts +9 -2
  203. package/src/domains/session/migrations/index.ts +22 -3
  204. package/src/engine/acp/server.ts +3 -0
  205. package/src/engine/agent.ts +18 -1
  206. package/src/engine/session.ts +9 -3
  207. package/src/entry/orchestrator.ts +16 -4
  208. package/src/interactive/chat-loop-messages.ts +18 -6
  209. package/src/interactive/chat-panel.ts +17 -1
  210. package/src/interactive/chat-renderer.ts +30 -21
  211. package/src/interactive/context-meter.ts +10 -0
  212. package/src/interactive/context-overlay.ts +81 -6
  213. package/src/interactive/context-recall-command.ts +110 -0
  214. package/src/interactive/interactive-slash-runtime.ts +37 -1
  215. package/src/interactive/model-session-replay.ts +21 -0
  216. package/src/interactive/overlay-general-openers.ts +6 -0
  217. package/src/interactive/overlay-session-lifecycle.ts +8 -4
  218. package/src/interactive/renderers/tool-execution.ts +18 -2
  219. package/src/interactive/session-transcript.ts +2 -2
  220. package/src/interactive/slash-commands.ts +29 -2
  221. package/src/interactive/turn-context.ts +238 -88
  222. package/src/interactive/turn-middleware.ts +6 -6
  223. package/src/tools/agent-tools.ts +11 -4
  224. package/src/tools/bash.ts +144 -82
  225. package/src/tools/builtin-tool-catalog.ts +11 -5
  226. package/src/tools/context/index.ts +105 -3
  227. package/src/tools/context/surface.ts +3 -2
  228. package/src/tools/core-bootstrap.ts +21 -0
  229. package/src/tools/dispatch-runner.ts +9 -7
  230. package/src/tools/monitor.ts +28 -20
  231. package/src/tools/registry.ts +59 -7
  232. package/src/tools/result-disposition.ts +550 -0
  233. package/src/tools/result-shaping.ts +262 -19
  234. package/src/tools/safe-exec.ts +2 -0
  235. package/src/tools/verify/authoring.ts +1119 -0
  236. package/src/tools/verify/catalog.ts +346 -0
  237. package/src/tools/verify/index.ts +13 -3
  238. package/src/tools/verify/scripts.ts +135 -37
  239. package/src/tools/verify/surface.ts +9 -5
  240. package/src/tools/worker-evidence.ts +35 -12
  241. package/dist/chunk-J7CWMCQD.js +0 -255
  242. package/dist/chunk-T6YILFSB.js +0 -80
  243. package/dist/chunk-VAKQQHWR.js +0 -434
  244. package/dist/chunk-VPAYEGVX.js +0 -184
@@ -0,0 +1,325 @@
1
+ import { SAFE_EXEC_DEFAULT_TIMEOUT_MS } from "../core/safe-exec.js";
2
+ import {
3
+ createVerifierDraft,
4
+ discoverVerifierAuthoring,
5
+ previewVerifierDraft,
6
+ runVerifierAuthoringWorkflow,
7
+ type VerifierRevision,
8
+ } from "../tools/verify/authoring.js";
9
+ import { loadProjectVerifierCatalog, PROJECT_VERIFIER_CATALOG_RELATIVE_PATH } from "../tools/verify/catalog.js";
10
+ import { verifyTool } from "../tools/verify/index.js";
11
+ import { printError, printOk } from "./argv.js";
12
+
13
+ const HELP = `Project verifier authoring
14
+
15
+ Usage:
16
+ clio-coder verifiers discover
17
+ clio-coder verifiers author [--exclude <id>] [--rename <old>=<new>] [--dry-run <id>] [--yes]
18
+ clio-coder verifiers validate
19
+ clio-coder verifiers dry-run <id>
20
+ clio-coder verifiers add --id <id> --description <text> --command <json-argv> [fields] [--yes]
21
+ clio-coder verifiers edit <id> [--description <text>] [--command <json-argv>] [fields] [--yes]
22
+ clio-coder verifiers rename <old> <new> [--yes]
23
+ clio-coder verifiers remove <id> [--yes]
24
+
25
+ Fields:
26
+ --cwd <path> repository-relative working directory (default .)
27
+ --timeout-ms <number> bounded timeout in milliseconds (default 120000)
28
+ --tags <a,b> comma-separated catalog tags
29
+
30
+ Discovery and every preview are read-only. Mutating commands write only with
31
+ --yes after printing the exact argv, cwd, timeout, tags, source provenance, and
32
+ effective execution authority. dry-run invokes the production verify path.
33
+ `;
34
+
35
+ const VALUE_OPTIONS = new Set([
36
+ "--id",
37
+ "--description",
38
+ "--command",
39
+ "--cwd",
40
+ "--timeout-ms",
41
+ "--tags",
42
+ "--exclude",
43
+ "--rename",
44
+ "--dry-run",
45
+ ]);
46
+
47
+ interface ParsedAuthoringArgs {
48
+ yes: boolean;
49
+ help: boolean;
50
+ positional: string[];
51
+ values: Map<string, string[]>;
52
+ error?: string;
53
+ }
54
+
55
+ function parseAuthoringArgs(argv: ReadonlyArray<string>): ParsedAuthoringArgs {
56
+ const positional: string[] = [];
57
+ const values = new Map<string, string[]>();
58
+ let yes = false;
59
+ let help = false;
60
+ for (let index = 0; index < argv.length; index += 1) {
61
+ const arg = argv[index];
62
+ if (arg === "--yes") {
63
+ yes = true;
64
+ continue;
65
+ }
66
+ if (arg === "--help" || arg === "-h") {
67
+ help = true;
68
+ continue;
69
+ }
70
+ if (arg?.startsWith("--") === true) {
71
+ if (!VALUE_OPTIONS.has(arg)) return { yes, help, positional, values, error: `unknown option: ${arg}` };
72
+ const value = argv[index + 1];
73
+ if (value === undefined || value.startsWith("--")) {
74
+ return { yes, help, positional, values, error: `${arg} requires a value` };
75
+ }
76
+ const prior = values.get(arg) ?? [];
77
+ prior.push(value);
78
+ values.set(arg, prior);
79
+ index += 1;
80
+ continue;
81
+ }
82
+ if (arg !== undefined) positional.push(arg);
83
+ }
84
+ return { yes, help, positional, values };
85
+ }
86
+
87
+ function oneValue(parsed: ParsedAuthoringArgs, option: string): string | undefined {
88
+ return parsed.values.get(option)?.at(-1);
89
+ }
90
+
91
+ function parseCommand(value: string | undefined): string[] | Error {
92
+ if (value === undefined) return new Error("--command is required and must be a JSON argv array");
93
+ let parsed: unknown;
94
+ try {
95
+ parsed = JSON.parse(value) as unknown;
96
+ } catch (error) {
97
+ return new Error(`--command must be valid JSON: ${error instanceof Error ? error.message : String(error)}`);
98
+ }
99
+ if (!Array.isArray(parsed) || parsed.length === 0 || parsed.some((entry) => typeof entry !== "string")) {
100
+ return new Error("--command must be a non-empty JSON string array");
101
+ }
102
+ return parsed as string[];
103
+ }
104
+
105
+ function parseTimeout(value: string | undefined): number | Error {
106
+ if (value === undefined) return SAFE_EXEC_DEFAULT_TIMEOUT_MS;
107
+ const parsed = Number(value);
108
+ if (!Number.isInteger(parsed) || parsed <= 0) return new Error("--timeout-ms must be a positive integer");
109
+ return parsed;
110
+ }
111
+
112
+ function tags(value: string | undefined): string[] {
113
+ return value === undefined
114
+ ? []
115
+ : value
116
+ .split(",")
117
+ .map((tag) => tag.trim())
118
+ .filter((tag) => tag.length > 0);
119
+ }
120
+
121
+ function requiredValue(parsed: ParsedAuthoringArgs, option: string): string | Error {
122
+ const value = oneValue(parsed, option);
123
+ return value === undefined || value.length === 0 ? new Error(`${option} is required`) : value;
124
+ }
125
+
126
+ function authorRevisions(parsed: ParsedAuthoringArgs): VerifierRevision[] | Error {
127
+ const revisions: VerifierRevision[] = [];
128
+ for (const id of parsed.values.get("--exclude") ?? []) revisions.push({ kind: "remove", id });
129
+ for (const rename of parsed.values.get("--rename") ?? []) {
130
+ const separator = rename.indexOf("=");
131
+ if (separator <= 0 || separator === rename.length - 1) {
132
+ return new Error("--rename must use <old>=<new>");
133
+ }
134
+ revisions.push({ kind: "rename", id: rename.slice(0, separator), newId: rename.slice(separator + 1) });
135
+ }
136
+ return revisions;
137
+ }
138
+
139
+ function addRevision(parsed: ParsedAuthoringArgs): VerifierRevision | Error {
140
+ const id = requiredValue(parsed, "--id");
141
+ if (id instanceof Error) return id;
142
+ const description = requiredValue(parsed, "--description");
143
+ if (description instanceof Error) return description;
144
+ const command = parseCommand(oneValue(parsed, "--command"));
145
+ if (command instanceof Error) return command;
146
+ const timeoutMs = parseTimeout(oneValue(parsed, "--timeout-ms"));
147
+ if (timeoutMs instanceof Error) return timeoutMs;
148
+ return {
149
+ kind: "add",
150
+ check: {
151
+ id,
152
+ description,
153
+ command,
154
+ cwd: oneValue(parsed, "--cwd") ?? ".",
155
+ timeoutMs,
156
+ tags: tags(oneValue(parsed, "--tags")),
157
+ },
158
+ };
159
+ }
160
+
161
+ function editRevision(parsed: ParsedAuthoringArgs): VerifierRevision | Error {
162
+ const id = parsed.positional[1];
163
+ if (id === undefined) return new Error("edit requires a check ID");
164
+ const changes: Extract<VerifierRevision, { kind: "edit" }>["changes"] = {};
165
+ const description = oneValue(parsed, "--description");
166
+ if (description !== undefined) changes.description = description;
167
+ const commandValue = oneValue(parsed, "--command");
168
+ if (commandValue !== undefined) {
169
+ const command = parseCommand(commandValue);
170
+ if (command instanceof Error) return command;
171
+ changes.command = command;
172
+ }
173
+ const cwd = oneValue(parsed, "--cwd");
174
+ if (cwd !== undefined) changes.cwd = cwd;
175
+ const timeoutValue = oneValue(parsed, "--timeout-ms");
176
+ if (timeoutValue !== undefined) {
177
+ const timeoutMs = parseTimeout(timeoutValue);
178
+ if (timeoutMs instanceof Error) return timeoutMs;
179
+ changes.timeoutMs = timeoutMs;
180
+ }
181
+ const tagValue = oneValue(parsed, "--tags");
182
+ if (tagValue !== undefined) changes.tags = tags(tagValue);
183
+ if (Object.keys(changes).length === 0) return new Error("edit requires at least one changed field");
184
+ return { kind: "edit", id, changes };
185
+ }
186
+
187
+ function mutationRevision(parsed: ParsedAuthoringArgs): VerifierRevision | Error {
188
+ const command = parsed.positional[0];
189
+ if (command === "add") return addRevision(parsed);
190
+ if (command === "edit") return editRevision(parsed);
191
+ if (command === "rename") {
192
+ const id = parsed.positional[1];
193
+ const newId = parsed.positional[2];
194
+ if (id === undefined || newId === undefined) return new Error("rename requires <old> and <new> IDs");
195
+ return { kind: "rename", id, newId };
196
+ }
197
+ if (command === "remove") {
198
+ const id = parsed.positional[1];
199
+ if (id === undefined) return new Error("remove requires a check ID");
200
+ return { kind: "remove", id };
201
+ }
202
+ return new Error(`unknown verifiers command: ${command ?? "(none)"}`);
203
+ }
204
+
205
+ function printDryRun(id: string, result: Awaited<ReturnType<typeof verifyTool.run>>): number {
206
+ if (result.kind === "error") {
207
+ printError(`dry-run '${id}' failed: ${result.message}`);
208
+ return 1;
209
+ }
210
+ process.stdout.write(`Dry run '${id}':\n${result.output}${result.output.endsWith("\n") ? "" : "\n"}`);
211
+ return 0;
212
+ }
213
+
214
+ async function executeWorkflow(
215
+ parsed: ParsedAuthoringArgs,
216
+ options: { includeProposals: boolean; revisions: VerifierRevision[] },
217
+ ): Promise<number> {
218
+ let printedPreview = false;
219
+ const dryRunCheckIds = parsed.values.get("--dry-run") ?? [];
220
+ const result = await runVerifierAuthoringWorkflow({
221
+ includeProposals: options.includeProposals,
222
+ initialRevisions: options.revisions,
223
+ confirmed: parsed.yes,
224
+ decide(context) {
225
+ process.stdout.write(`${context.preview}\n`);
226
+ printedPreview = true;
227
+ if (!parsed.yes || (context.draft.checks.length === 0 && options.revisions.length === 0)) {
228
+ return { kind: "reject" };
229
+ }
230
+ return { kind: "confirm", dryRunCheckIds };
231
+ },
232
+ });
233
+ if (result.status === "invalid") {
234
+ if (!printedPreview && result.preview !== undefined) process.stdout.write(`${result.preview}\n`);
235
+ printError(result.reason);
236
+ if (result.wrote && result.path !== undefined) {
237
+ process.stderr.write(` ${result.path} was written; repair or remove it before running a check.\n`);
238
+ }
239
+ return 1;
240
+ }
241
+ if (result.status === "rejected") {
242
+ if (!parsed.yes) {
243
+ process.stdout.write("Review complete. Nothing changed; rerun the same command with --yes to write this preview.\n");
244
+ } else {
245
+ process.stdout.write(
246
+ "Nothing changed because the preview contains no catalog checks. Package checks remain active; unsupported commands can be added with `clio-coder verifiers add`.\n",
247
+ );
248
+ }
249
+ return 0;
250
+ }
251
+ printOk(`wrote ${result.path}; production parser accepted the catalog`);
252
+ let code = 0;
253
+ for (const dryRun of result.dryRuns) code = Math.max(code, printDryRun(dryRun.id, dryRun.result));
254
+ return code;
255
+ }
256
+
257
+ async function discoverCommand(): Promise<number> {
258
+ const discovery = discoverVerifierAuthoring();
259
+ if (!discovery.ok) {
260
+ printError(discovery.reason);
261
+ process.stderr.write(`${discovery.manualEntry}\n`);
262
+ return 1;
263
+ }
264
+ process.stdout.write(`${previewVerifierDraft(createVerifierDraft(discovery))}\n`);
265
+ return 0;
266
+ }
267
+
268
+ function validateCommand(): number {
269
+ const loaded = loadProjectVerifierCatalog(process.cwd());
270
+ if (!loaded.ok) {
271
+ printError(`production catalog parser rejected ${loaded.reason}`);
272
+ return 1;
273
+ }
274
+ if (loaded.source === null) {
275
+ process.stdout.write(`No ${PROJECT_VERIFIER_CATALOG_RELATIVE_PATH} exists. Run \`clio-coder verifiers author\`.\n`);
276
+ return 0;
277
+ }
278
+ printOk(
279
+ `production catalog parser accepted ${PROJECT_VERIFIER_CATALOG_RELATIVE_PATH} (${loaded.source.checks.length} check${loaded.source.checks.length === 1 ? "" : "s"})`,
280
+ );
281
+ return 0;
282
+ }
283
+
284
+ export async function runVerifiersCommand(argv: string[]): Promise<number> {
285
+ const parsed = parseAuthoringArgs(argv);
286
+ if (parsed.error !== undefined) {
287
+ printError(parsed.error);
288
+ process.stderr.write(HELP);
289
+ return 2;
290
+ }
291
+ if (parsed.help || parsed.positional.length === 0) {
292
+ process.stdout.write(HELP);
293
+ return 0;
294
+ }
295
+ const command = parsed.positional[0];
296
+ if (command === "discover") return discoverCommand();
297
+ if (command === "validate") return validateCommand();
298
+ if (command === "dry-run") {
299
+ const id = parsed.positional[1];
300
+ if (id === undefined) {
301
+ printError("dry-run requires a check ID");
302
+ return 2;
303
+ }
304
+ return printDryRun(id, await verifyTool.run({ check: id }));
305
+ }
306
+ if (command === "author") {
307
+ const revisions = authorRevisions(parsed);
308
+ if (revisions instanceof Error) {
309
+ printError(revisions.message);
310
+ return 2;
311
+ }
312
+ return executeWorkflow(parsed, { includeProposals: true, revisions });
313
+ }
314
+ if (["add", "edit", "rename", "remove"].includes(command ?? "")) {
315
+ const revision = mutationRevision(parsed);
316
+ if (revision instanceof Error) {
317
+ printError(revision.message);
318
+ return 2;
319
+ }
320
+ return executeWorkflow(parsed, { includeProposals: false, revisions: [revision] });
321
+ }
322
+ printError(`unknown verifiers command: ${command}`);
323
+ process.stderr.write(HELP);
324
+ return 2;
325
+ }
@@ -1,4 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
+ import { StringDecoder } from "node:string_decoder";
2
3
  import { AI_AGENT_NAME } from "./agent-environment.js";
3
4
  import {
4
5
  gitCommitAttributionEnabled,
@@ -28,6 +29,8 @@ export interface BashCommandResult {
28
29
  aborted: boolean;
29
30
  timedOut: boolean;
30
31
  outputCapped: boolean;
32
+ /** Exact bytes retained from stdout and stderr, never above the hard cap. */
33
+ outputBytes: number;
31
34
  }
32
35
 
33
36
  export interface BashCommandProgress {
@@ -151,6 +154,9 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
151
154
  let killSent = false;
152
155
  let stdout = "";
153
156
  let stderr = "";
157
+ const stdoutDecoder = new StringDecoder("utf8");
158
+ const stderrDecoder = new StringDecoder("utf8");
159
+ let decodersFinished = false;
154
160
  let outputBytes = 0;
155
161
  let outputCapped = false;
156
162
  let updateTimer: ReturnType<typeof setTimeout> | null = null;
@@ -164,6 +170,16 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
164
170
  stdio: ["ignore", "pipe", "pipe"],
165
171
  });
166
172
 
173
+ const finishDecoders = (): void => {
174
+ if (decodersFinished) return;
175
+ decodersFinished = true;
176
+ // A hard byte cap may bisect a multibyte code point. Discard the
177
+ // decoder's incomplete suffix instead of manufacturing U+FFFD.
178
+ if (outputCapped) return;
179
+ stdout += stdoutDecoder.end();
180
+ stderr += stderrDecoder.end();
181
+ };
182
+
167
183
  const emitUpdate = (): void => {
168
184
  if (options.onUpdate === undefined || !updateDirty) return;
169
185
  updateDirty = false;
@@ -250,24 +266,20 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
250
266
 
251
267
  const appendChunk = (target: "stdout" | "stderr", chunk: Buffer): void => {
252
268
  if (outputCapped) return;
253
- outputBytes += chunk.byteLength;
254
- if (outputBytes > BASH_HARD_CAP_BYTES) {
255
- // Keep the bytes seen so far (including this chunk's prefix would
256
- // overshoot memory, so append the remaining budget) and stop the
257
- // child. Truncation for display is tail-biased downstream.
258
- const remaining = BASH_HARD_CAP_BYTES - (outputBytes - chunk.byteLength);
259
- if (remaining > 0) {
260
- const slice = chunk.subarray(0, remaining).toString("utf8");
261
- if (target === "stdout") stdout += slice;
262
- else stderr += slice;
263
- }
269
+ const remaining = BASH_HARD_CAP_BYTES - outputBytes;
270
+ const accepted = chunk.byteLength <= remaining ? chunk : chunk.subarray(0, Math.max(0, remaining));
271
+ outputBytes += accepted.byteLength;
272
+ if (accepted.byteLength > 0) {
273
+ const decoded = target === "stdout" ? stdoutDecoder.write(accepted) : stderrDecoder.write(accepted);
274
+ if (target === "stdout") stdout += decoded;
275
+ else stderr += decoded;
276
+ }
277
+ if (accepted.byteLength < chunk.byteLength) {
264
278
  outputCapped = true;
265
279
  scheduleUpdate();
266
280
  killChild();
267
281
  return;
268
282
  }
269
- if (target === "stdout") stdout += chunk.toString("utf8");
270
- else stderr += chunk.toString("utf8");
271
283
  scheduleUpdate();
272
284
  };
273
285
 
@@ -278,6 +290,7 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
278
290
  settled = true;
279
291
  if (timeoutId) clearTimeout(timeoutId);
280
292
  clearKillGraceTimer();
293
+ finishDecoders();
281
294
  finishUpdates();
282
295
  options.signal?.removeEventListener("abort", onAbort);
283
296
  resolve({
@@ -289,6 +302,7 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
289
302
  aborted,
290
303
  timedOut,
291
304
  outputCapped,
305
+ outputBytes,
292
306
  });
293
307
  });
294
308
  child.on("close", (code, signalName) => {
@@ -296,6 +310,7 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
296
310
  settled = true;
297
311
  if (timeoutId) clearTimeout(timeoutId);
298
312
  clearKillGraceTimer();
313
+ finishDecoders();
299
314
  finishUpdates();
300
315
  options.signal?.removeEventListener("abort", onAbort);
301
316
  const error =
@@ -307,7 +322,17 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
307
322
  code: code ?? undefined,
308
323
  signal: signalName ?? undefined,
309
324
  } as NodeJS.ErrnoException);
310
- resolve({ error, stdout, stderr, exitCode: code, signal: signalName, aborted, timedOut, outputCapped });
325
+ resolve({
326
+ error,
327
+ stdout,
328
+ stderr,
329
+ exitCode: code,
330
+ signal: signalName,
331
+ aborted,
332
+ timedOut,
333
+ outputCapped,
334
+ outputBytes,
335
+ });
311
336
  });
312
337
  });
313
338
  }
@@ -61,6 +61,7 @@ export const BusChannels = {
61
61
  ContextActivity: "context.activity",
62
62
  ContextWarning: "context.warning",
63
63
  ContextPruned: "context.pruned",
64
+ ContextRecalled: "context.recalled",
64
65
  AgentStatusChanged: "agent.status.changed",
65
66
  RunAborted: "run.aborted",
66
67
  BudgetAlert: "budget.alert",
@@ -253,21 +254,34 @@ export function isRunAbortedPayload(value: unknown): value is RunAbortedPayload
253
254
  return true;
254
255
  }
255
256
 
256
- /** Payload published on {@link BusChannels.ContextPruned} after compaction reclaims tokens. */
257
+ /** Payload published on {@link BusChannels.ContextPruned} after projected or summarized context shrinks. */
257
258
  export interface ContextPrunedPayload {
258
- stage: "mask_observations" | "llm_summary";
259
+ stage: "mask_observations" | "working_set" | "llm_summary";
259
260
  tokensBefore: number;
260
261
  tokensAfter: number;
261
262
  trigger: string;
262
263
  snapshotIdBefore: string | null;
263
264
  snapshotIdAfter: string;
264
265
  at: number;
265
- /** Used/window ratio at trigger time; mask stage only. */
266
+ /** Used/window ratio at trigger time; working-set and legacy-mask stages only. */
266
267
  pressure?: number | null;
268
+ /** Legacy destructive-mask count. */
267
269
  maskedObservations?: number;
268
- /** Thinking blocks stripped from stale assistant messages; mask stage only. */
270
+ /** Thinking blocks stripped from stale assistant messages; legacy mask only. */
269
271
  maskedThinkingBlocks?: number;
270
272
  maskedThinkingChars?: number;
273
+ /** Working-set policy that selected an applied eviction event. */
274
+ policyId?: string;
275
+ /** Number of working-set units evicted by the event. */
276
+ evictedItems?: number;
277
+ }
278
+
279
+ /** Payload published on {@link BusChannels.ContextRecalled} after an exact working-set recall. */
280
+ export interface ContextRecalledPayload {
281
+ ref: string;
282
+ trigger: "tool" | "operator";
283
+ tokensReadmitted: number;
284
+ at: number;
271
285
  }
272
286
 
273
287
  // ---------------------------------------------------------------------------
@@ -727,6 +741,7 @@ export type BusPayloadMap = {
727
741
  [BusChannels.ContextActivity]: ContextActivityPayload;
728
742
  [BusChannels.ContextWarning]: ContextWarningPayload;
729
743
  [BusChannels.ContextPruned]: ContextPrunedPayload;
744
+ [BusChannels.ContextRecalled]: ContextRecalledPayload;
730
745
  [BusChannels.AgentStatusChanged]: AgentStatusChangedPayload;
731
746
  [BusChannels.RunAborted]: RunAbortedPayload;
732
747
  [BusChannels.BudgetAlert]: BudgetAlertPayload;
@@ -968,6 +968,7 @@ const TOP_LEVEL_KEYS = [
968
968
  "delegation",
969
969
  "keybindings",
970
970
  "compaction",
971
+ "context",
971
972
  "retry",
972
973
  "guardrails",
973
974
  ] as const;
@@ -1405,6 +1406,59 @@ export function validateSettings(raw: unknown): SettingsValidationResult {
1405
1406
  }
1406
1407
  }
1407
1408
 
1409
+ if ("context" in raw) {
1410
+ if (!isPlainObject(raw.context)) {
1411
+ issues.add("context", `expected a map, got ${describe(raw.context)}`);
1412
+ } else {
1413
+ issues.unknownKeys("context", raw.context, ["workingSet"]);
1414
+ if ("workingSet" in raw.context) {
1415
+ if (!isPlainObject(raw.context.workingSet)) {
1416
+ issues.add("context.workingSet", `expected a map, got ${describe(raw.context.workingSet)}`);
1417
+ } else {
1418
+ const workingSet = raw.context.workingSet;
1419
+ issues.unknownKeys("context.workingSet", workingSet, [
1420
+ "enabled",
1421
+ "policy",
1422
+ "target",
1423
+ "protectLastTurns",
1424
+ "minEvictableTokens",
1425
+ ]);
1426
+ if ("enabled" in workingSet) {
1427
+ const v = expectBoolean(issues, "context.workingSet.enabled", workingSet.enabled);
1428
+ if (v !== undefined) settings.context.workingSet.enabled = v;
1429
+ }
1430
+ if ("policy" in workingSet) {
1431
+ const v = expectEnum(issues, "context.workingSet.policy", workingSet.policy, [
1432
+ "age-horizon",
1433
+ "structural-v1",
1434
+ ] as const);
1435
+ if (v !== undefined) settings.context.workingSet.policy = v;
1436
+ }
1437
+ if ("target" in workingSet) {
1438
+ const v = expectNumber(issues, "context.workingSet.target", workingSet.target);
1439
+ if (v !== undefined && (v <= 0 || v >= 1)) {
1440
+ issues.add("context.workingSet.target", `expected a number > 0 and < 1, got ${v}`);
1441
+ } else if (v !== undefined) {
1442
+ settings.context.workingSet.target = v;
1443
+ }
1444
+ }
1445
+ if ("protectLastTurns" in workingSet) {
1446
+ const v = expectInteger(issues, "context.workingSet.protectLastTurns", workingSet.protectLastTurns, {
1447
+ min: 1,
1448
+ });
1449
+ if (v !== undefined) settings.context.workingSet.protectLastTurns = v;
1450
+ }
1451
+ if ("minEvictableTokens" in workingSet) {
1452
+ const v = expectInteger(issues, "context.workingSet.minEvictableTokens", workingSet.minEvictableTokens, {
1453
+ min: 0,
1454
+ });
1455
+ if (v !== undefined) settings.context.workingSet.minEvictableTokens = v;
1456
+ }
1457
+ }
1458
+ }
1459
+ }
1460
+ }
1461
+
1408
1462
  if ("retry" in raw) {
1409
1463
  if (!isPlainObject(raw.retry)) {
1410
1464
  issues.add("retry", `expected a map, got ${describe(raw.retry)}`);
@@ -4,6 +4,7 @@
4
4
  * exist. Users edit the file directly or through TUI overlays.
5
5
  */
6
6
 
7
+ import { DEFAULT_WORKING_SET_SETTINGS } from "../domains/context/working-set/defaults.js";
7
8
  import type { TargetDescriptor } from "../domains/providers/types/target-descriptor.js";
8
9
  import type { AutonomyLevel } from "../domains/safety/autonomy.js";
9
10
  import { GUARDRAIL_DEFAULTS, type GuardrailValues } from "./guardrails.js";
@@ -98,6 +99,32 @@ export interface CompactionSettings {
98
99
  systemPrompt?: string;
99
100
  }
100
101
 
102
+ /**
103
+ * Working-set layer settings (`context.workingSet`). The layer decides which
104
+ * tool-result bodies and thinking blocks leave the model's working set when
105
+ * pressure crosses `compaction.threshold`; it records evictions as ledger
106
+ * entries and never rewrites history. Defaults and prose live in
107
+ * src/domains/context/working-set/defaults.ts.
108
+ *
109
+ * - enabled: master switch. Off skips eviction and goes straight to summary
110
+ * compaction (the legacy destructive mask is only reachable through
111
+ * CLIO_CODER_LEGACY_MASK=1).
112
+ * - policy: candidate selection rule set.
113
+ * - target: used/window ratio an applied event batches down to.
114
+ * - protectLastTurns: recent user turns whose observations are never evicted.
115
+ * - minEvictableTokens: results below this estimate are never evicted; the
116
+ * marker would cost more than it saves.
117
+ */
118
+ export type WorkingSetPolicyId = "age-horizon" | "structural-v1";
119
+
120
+ export interface WorkingSetSettings {
121
+ enabled: boolean;
122
+ policy: WorkingSetPolicyId;
123
+ target: number;
124
+ protectLastTurns: number;
125
+ minEvictableTokens: number;
126
+ }
127
+
101
128
  /**
102
129
  * Transient provider retry controls for the interactive chat loop. These are
103
130
  * intentionally small and mirror the session retry helper defaults. Dispatched
@@ -357,6 +384,9 @@ export const DEFAULT_SETTINGS = {
357
384
  threshold: 0.8,
358
385
  excludeLastTurns: 6,
359
386
  } as CompactionSettings,
387
+ context: {
388
+ workingSet: DEFAULT_WORKING_SET_SETTINGS,
389
+ },
360
390
  retry: {
361
391
  enabled: true,
362
392
  maxRetries: 3,
@@ -610,9 +640,9 @@ keybindings: {}
610
640
  # auto master switch for the pre-request compaction trigger.
611
641
  # Manual /context compact always runs the LLM summary.
612
642
  # threshold pressure = estimated_tokens / context_window. Crossing
613
- # it masks stale tool observations first, then runs a
614
- # full LLM summary if pressure stays above the threshold.
615
- # excludeLastTurns recent user turns protected from observation masking.
643
+ # it evicts from the working set first, then runs a full
644
+ # LLM summary if pressure stays above the threshold.
645
+ # excludeLastTurns recent turns protected only by the temporary legacy mask.
616
646
  # model optional pattern (e.g. provider/summary-model-id) for a
617
647
  # dedicated summarization model. Absent ⇒ orchestrator target.
618
648
  # systemPrompt optional path to a prompt-override file.
@@ -623,6 +653,23 @@ compaction:
623
653
  # model: provider/summary-model-id
624
654
  # systemPrompt: ~/.config/clio-coder/prompts/compaction.md
625
655
 
656
+ # Non-destructive working-set eviction before summary compaction.
657
+ # enabled false skips eviction and goes directly to the summary stage.
658
+ # policy structural-v1 evicts by what the session did since
659
+ # (re-reads, edits, resolved failures, consumed listings)
660
+ # and falls back to age only under pressure;
661
+ # age-horizon is the previous age-based selection.
662
+ # target pressure ratio an applied eviction batches down to.
663
+ # protectLastTurns recent user turns whose observations remain in the working set.
664
+ # minEvictableTokens entries below this estimate remain in the working set.
665
+ context:
666
+ workingSet:
667
+ enabled: true
668
+ policy: structural-v1
669
+ target: 0.6
670
+ protectLastTurns: 6
671
+ minEvictableTokens: 200
672
+
626
673
  # Transient provider/stream retry controls for interactive chat.
627
674
  # Retryable errors include overloads, rate limits, 5xx responses, network
628
675
  # resets, and timeouts. Context overflow uses compaction recovery instead.
@@ -1,11 +1,17 @@
1
1
  export const VERIFICATION_SCRIPT_FAMILY_HINT = "test*/lint*/build*/typecheck*/check*/format*/ci*";
2
2
 
3
3
  const VERIFICATION_SCRIPT_PATTERN = /^(?:test|lint|build|typecheck|check|format|ci)(?:[:.-].*)?$/;
4
+ const PROJECT_VERIFIER_CHECK_ID_PATTERN = /^[a-z0-9][a-z0-9._:-]*$/;
4
5
 
5
6
  export function isVerificationScriptName(name: string): boolean {
6
7
  return VERIFICATION_SCRIPT_PATTERN.test(name);
7
8
  }
8
9
 
10
+ /** Stable identifier grammar for project-declared verifier catalog checks. */
11
+ export function isProjectVerifierCheckId(name: string): boolean {
12
+ return PROJECT_VERIFIER_CHECK_ID_PATTERN.test(name);
13
+ }
14
+
9
15
  export function declaredVerificationScripts(scripts: Record<string, unknown>): string[] {
10
16
  return Object.keys(scripts)
11
17
  .filter((name) => isVerificationScriptName(name))
@@ -24,6 +24,9 @@ Inspect scripts, docs, recent diffs, and touched files before choosing commands.
24
24
  When a codewiki exists, prefer `code_nav` (symbol, dependents) over broad reads to scope what a diff touches.
25
25
  Run only the checks required for the requested confidence level.
26
26
  Prefer typed validation tools over arbitrary shell execution.
27
+ Call `verify()` before choosing an executable check. It lists package scripts and strict project entries from `.clio-coder/verifiers.yaml` through the same metadata shape.
28
+ Run a project entry only by its listed ID. Do not add args, cwd, timeout, environment, or shell composition; the catalog's exact argv, cwd, and timeout are authoritative.
29
+ Treat scientific validation contracts and generated handbook expectations as advisory evidence requirements. They do not become executable until the project declares a corresponding verifier-catalog entry.
27
30
  Do not edit source files, tests, docs, configs, or generated artifacts from this role.
28
31
  When a gate fails, report the exact command, exit status, relevant error lines, and likely owner.
29
32
  Distinguish pre-existing failures from introduced failures when the evidence allows.