@encore-os/eos-spec 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/LICENSE +17 -0
  2. package/bin/check-dist-fresh.js +51 -0
  3. package/bin/eos-spec.js +7 -0
  4. package/dist/commands/ac-backfill.d.ts +31 -0
  5. package/dist/commands/ac-backfill.js +198 -0
  6. package/dist/commands/ac-backfill.js.map +1 -0
  7. package/dist/commands/archive.d.ts +2 -0
  8. package/dist/commands/archive.js +101 -0
  9. package/dist/commands/archive.js.map +1 -0
  10. package/dist/commands/audit.d.ts +30 -0
  11. package/dist/commands/audit.js +107 -0
  12. package/dist/commands/audit.js.map +1 -0
  13. package/dist/commands/backfill.d.ts +41 -0
  14. package/dist/commands/backfill.js +239 -0
  15. package/dist/commands/backfill.js.map +1 -0
  16. package/dist/commands/checkArtifacts.d.ts +2 -0
  17. package/dist/commands/checkArtifacts.js +48 -0
  18. package/dist/commands/checkArtifacts.js.map +1 -0
  19. package/dist/commands/checkPlacement.d.ts +2 -0
  20. package/dist/commands/checkPlacement.js +31 -0
  21. package/dist/commands/checkPlacement.js.map +1 -0
  22. package/dist/commands/claim.d.ts +4 -0
  23. package/dist/commands/claim.js +46 -0
  24. package/dist/commands/claim.js.map +1 -0
  25. package/dist/commands/complete.d.ts +2 -0
  26. package/dist/commands/complete.js +138 -0
  27. package/dist/commands/complete.js.map +1 -0
  28. package/dist/commands/conformance.d.ts +23 -0
  29. package/dist/commands/conformance.js +124 -0
  30. package/dist/commands/conformance.js.map +1 -0
  31. package/dist/commands/create.d.ts +6 -0
  32. package/dist/commands/create.js +277 -0
  33. package/dist/commands/create.js.map +1 -0
  34. package/dist/commands/deferred.d.ts +2 -0
  35. package/dist/commands/deferred.js +85 -0
  36. package/dist/commands/deferred.js.map +1 -0
  37. package/dist/commands/delta.d.ts +17 -0
  38. package/dist/commands/delta.js +292 -0
  39. package/dist/commands/delta.js.map +1 -0
  40. package/dist/commands/freshness.d.ts +13 -0
  41. package/dist/commands/freshness.js +72 -0
  42. package/dist/commands/freshness.js.map +1 -0
  43. package/dist/commands/funnel.d.ts +2 -0
  44. package/dist/commands/funnel.js +61 -0
  45. package/dist/commands/funnel.js.map +1 -0
  46. package/dist/commands/gen-tests.d.ts +2 -0
  47. package/dist/commands/gen-tests.js +53 -0
  48. package/dist/commands/gen-tests.js.map +1 -0
  49. package/dist/commands/init.d.ts +2 -0
  50. package/dist/commands/init.js +64 -0
  51. package/dist/commands/init.js.map +1 -0
  52. package/dist/commands/list.d.ts +9 -0
  53. package/dist/commands/list.js +61 -0
  54. package/dist/commands/list.js.map +1 -0
  55. package/dist/commands/metrics.d.ts +2 -0
  56. package/dist/commands/metrics.js +66 -0
  57. package/dist/commands/metrics.js.map +1 -0
  58. package/dist/commands/next.d.ts +48 -0
  59. package/dist/commands/next.js +344 -0
  60. package/dist/commands/next.js.map +1 -0
  61. package/dist/commands/precedent.d.ts +66 -0
  62. package/dist/commands/precedent.js +343 -0
  63. package/dist/commands/precedent.js.map +1 -0
  64. package/dist/commands/ready.d.ts +28 -0
  65. package/dist/commands/ready.js +147 -0
  66. package/dist/commands/ready.js.map +1 -0
  67. package/dist/commands/reconcile.d.ts +44 -0
  68. package/dist/commands/reconcile.js +187 -0
  69. package/dist/commands/reconcile.js.map +1 -0
  70. package/dist/commands/rollup.d.ts +2 -0
  71. package/dist/commands/rollup.js +43 -0
  72. package/dist/commands/rollup.js.map +1 -0
  73. package/dist/commands/scorecard.d.ts +11 -0
  74. package/dist/commands/scorecard.js +55 -0
  75. package/dist/commands/scorecard.js.map +1 -0
  76. package/dist/commands/signoff.d.ts +2 -0
  77. package/dist/commands/signoff.js +114 -0
  78. package/dist/commands/signoff.js.map +1 -0
  79. package/dist/commands/status.d.ts +2 -0
  80. package/dist/commands/status.js +100 -0
  81. package/dist/commands/status.js.map +1 -0
  82. package/dist/commands/tasks.d.ts +13 -0
  83. package/dist/commands/tasks.js +81 -0
  84. package/dist/commands/tasks.js.map +1 -0
  85. package/dist/commands/validate.d.ts +16 -0
  86. package/dist/commands/validate.js +89 -0
  87. package/dist/commands/validate.js.map +1 -0
  88. package/dist/commands/verify-acs.d.ts +15 -0
  89. package/dist/commands/verify-acs.js +196 -0
  90. package/dist/commands/verify-acs.js.map +1 -0
  91. package/dist/commands/verifyRollout.d.ts +2 -0
  92. package/dist/commands/verifyRollout.js +125 -0
  93. package/dist/commands/verifyRollout.js.map +1 -0
  94. package/dist/index.d.ts +21 -0
  95. package/dist/index.js +138 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/lib/ac-draft-prompt.d.ts +18 -0
  98. package/dist/lib/ac-draft-prompt.js +38 -0
  99. package/dist/lib/ac-draft-prompt.js.map +1 -0
  100. package/dist/lib/ac-response-parser.d.ts +18 -0
  101. package/dist/lib/ac-response-parser.js +57 -0
  102. package/dist/lib/ac-response-parser.js.map +1 -0
  103. package/dist/lib/ai-call.d.ts +13 -0
  104. package/dist/lib/ai-call.js +17 -0
  105. package/dist/lib/ai-call.js.map +1 -0
  106. package/dist/lib/archive.d.ts +21 -0
  107. package/dist/lib/archive.js +136 -0
  108. package/dist/lib/archive.js.map +1 -0
  109. package/dist/lib/artifact-integrity.d.ts +5 -0
  110. package/dist/lib/artifact-integrity.js +65 -0
  111. package/dist/lib/artifact-integrity.js.map +1 -0
  112. package/dist/lib/backfill-queue.d.ts +11 -0
  113. package/dist/lib/backfill-queue.js +14 -0
  114. package/dist/lib/backfill-queue.js.map +1 -0
  115. package/dist/lib/blockers.d.ts +66 -0
  116. package/dist/lib/blockers.js +103 -0
  117. package/dist/lib/blockers.js.map +1 -0
  118. package/dist/lib/claims.d.ts +39 -0
  119. package/dist/lib/claims.js +133 -0
  120. package/dist/lib/claims.js.map +1 -0
  121. package/dist/lib/classify-debt.d.ts +17 -0
  122. package/dist/lib/classify-debt.js +13 -0
  123. package/dist/lib/classify-debt.js.map +1 -0
  124. package/dist/lib/code-evidence.d.ts +35 -0
  125. package/dist/lib/code-evidence.js +171 -0
  126. package/dist/lib/code-evidence.js.map +1 -0
  127. package/dist/lib/coherence.d.ts +14 -0
  128. package/dist/lib/coherence.js +188 -0
  129. package/dist/lib/coherence.js.map +1 -0
  130. package/dist/lib/completion-gate.d.ts +17 -0
  131. package/dist/lib/completion-gate.js +28 -0
  132. package/dist/lib/completion-gate.js.map +1 -0
  133. package/dist/lib/compliance-signoff.d.ts +39 -0
  134. package/dist/lib/compliance-signoff.js +81 -0
  135. package/dist/lib/compliance-signoff.js.map +1 -0
  136. package/dist/lib/config.d.ts +63 -0
  137. package/dist/lib/config.js +40 -0
  138. package/dist/lib/config.js.map +1 -0
  139. package/dist/lib/cost.d.ts +12 -0
  140. package/dist/lib/cost.js +12 -0
  141. package/dist/lib/cost.js.map +1 -0
  142. package/dist/lib/debt-dashboard.d.ts +25 -0
  143. package/dist/lib/debt-dashboard.js +36 -0
  144. package/dist/lib/debt-dashboard.js.map +1 -0
  145. package/dist/lib/embedding-store.d.ts +103 -0
  146. package/dist/lib/embedding-store.js +133 -0
  147. package/dist/lib/embedding-store.js.map +1 -0
  148. package/dist/lib/embeddings-source.d.ts +36 -0
  149. package/dist/lib/embeddings-source.js +62 -0
  150. package/dist/lib/embeddings-source.js.map +1 -0
  151. package/dist/lib/enforcement-allowlist.d.ts +12 -0
  152. package/dist/lib/enforcement-allowlist.js +25 -0
  153. package/dist/lib/enforcement-allowlist.js.map +1 -0
  154. package/dist/lib/events.d.ts +47 -0
  155. package/dist/lib/events.js +75 -0
  156. package/dist/lib/events.js.map +1 -0
  157. package/dist/lib/exec.d.ts +8 -0
  158. package/dist/lib/exec.js +25 -0
  159. package/dist/lib/exec.js.map +1 -0
  160. package/dist/lib/frontmatter.d.ts +112 -0
  161. package/dist/lib/frontmatter.js +261 -0
  162. package/dist/lib/frontmatter.js.map +1 -0
  163. package/dist/lib/funnel.d.ts +22 -0
  164. package/dist/lib/funnel.js +41 -0
  165. package/dist/lib/funnel.js.map +1 -0
  166. package/dist/lib/git-mtime.d.ts +4 -0
  167. package/dist/lib/git-mtime.js +23 -0
  168. package/dist/lib/git-mtime.js.map +1 -0
  169. package/dist/lib/metrics.d.ts +42 -0
  170. package/dist/lib/metrics.js +92 -0
  171. package/dist/lib/metrics.js.map +1 -0
  172. package/dist/lib/migration-collision.d.ts +20 -0
  173. package/dist/lib/migration-collision.js +61 -0
  174. package/dist/lib/migration-collision.js.map +1 -0
  175. package/dist/lib/next-action.d.ts +44 -0
  176. package/dist/lib/next-action.js +138 -0
  177. package/dist/lib/next-action.js.map +1 -0
  178. package/dist/lib/ollama-embed.d.ts +21 -0
  179. package/dist/lib/ollama-embed.js +65 -0
  180. package/dist/lib/ollama-embed.js.map +1 -0
  181. package/dist/lib/output.d.ts +35 -0
  182. package/dist/lib/output.js +46 -0
  183. package/dist/lib/output.js.map +1 -0
  184. package/dist/lib/pipeline-config.d.ts +20 -0
  185. package/dist/lib/pipeline-config.js +208 -0
  186. package/dist/lib/pipeline-config.js.map +1 -0
  187. package/dist/lib/pipeline-map.d.ts +68 -0
  188. package/dist/lib/pipeline-map.js +256 -0
  189. package/dist/lib/pipeline-map.js.map +1 -0
  190. package/dist/lib/pipeline-schema.d.ts +48 -0
  191. package/dist/lib/pipeline-schema.js +22 -0
  192. package/dist/lib/pipeline-schema.js.map +1 -0
  193. package/dist/lib/placement.d.ts +6 -0
  194. package/dist/lib/placement.js +40 -0
  195. package/dist/lib/placement.js.map +1 -0
  196. package/dist/lib/precedent-search.d.ts +18 -0
  197. package/dist/lib/precedent-search.js +160 -0
  198. package/dist/lib/precedent-search.js.map +1 -0
  199. package/dist/lib/predicate-registry.d.ts +6 -0
  200. package/dist/lib/predicate-registry.js +40 -0
  201. package/dist/lib/predicate-registry.js.map +1 -0
  202. package/dist/lib/recall-cases.d.ts +15 -0
  203. package/dist/lib/recall-cases.js +39 -0
  204. package/dist/lib/recall-cases.js.map +1 -0
  205. package/dist/lib/repo.d.ts +20 -0
  206. package/dist/lib/repo.js +82 -0
  207. package/dist/lib/repo.js.map +1 -0
  208. package/dist/lib/requirements.d.ts +91 -0
  209. package/dist/lib/requirements.js +179 -0
  210. package/dist/lib/requirements.js.map +1 -0
  211. package/dist/lib/rollup.d.ts +24 -0
  212. package/dist/lib/rollup.js +34 -0
  213. package/dist/lib/rollup.js.map +1 -0
  214. package/dist/lib/scorecard-run.d.ts +6 -0
  215. package/dist/lib/scorecard-run.js +48 -0
  216. package/dist/lib/scorecard-run.js.map +1 -0
  217. package/dist/lib/scorecard.d.ts +69 -0
  218. package/dist/lib/scorecard.js +113 -0
  219. package/dist/lib/scorecard.js.map +1 -0
  220. package/dist/lib/semantic-search.d.ts +36 -0
  221. package/dist/lib/semantic-search.js +56 -0
  222. package/dist/lib/semantic-search.js.map +1 -0
  223. package/dist/lib/session-state.d.ts +8 -0
  224. package/dist/lib/session-state.js +25 -0
  225. package/dist/lib/session-state.js.map +1 -0
  226. package/dist/lib/spec-debt.d.ts +8 -0
  227. package/dist/lib/spec-debt.js +17 -0
  228. package/dist/lib/spec-debt.js.map +1 -0
  229. package/dist/lib/spec-pure.d.ts +59 -0
  230. package/dist/lib/spec-pure.js +215 -0
  231. package/dist/lib/spec-pure.js.map +1 -0
  232. package/dist/lib/specs.d.ts +148 -0
  233. package/dist/lib/specs.js +373 -0
  234. package/dist/lib/specs.js.map +1 -0
  235. package/dist/lib/test-generator.d.ts +5 -0
  236. package/dist/lib/test-generator.js +72 -0
  237. package/dist/lib/test-generator.js.map +1 -0
  238. package/dist/lib/user-stories.d.ts +6 -0
  239. package/dist/lib/user-stories.js +56 -0
  240. package/dist/lib/user-stories.js.map +1 -0
  241. package/dist/lib/verifies-scan.d.ts +9 -0
  242. package/dist/lib/verifies-scan.js +73 -0
  243. package/dist/lib/verifies-scan.js.map +1 -0
  244. package/dist/lib/verify-core.d.ts +23 -0
  245. package/dist/lib/verify-core.js +48 -0
  246. package/dist/lib/verify-core.js.map +1 -0
  247. package/dist/lib/with-event-log.d.ts +12 -0
  248. package/dist/lib/with-event-log.js +65 -0
  249. package/dist/lib/with-event-log.js.map +1 -0
  250. package/dist/lib/wizard-specs.d.ts +17 -0
  251. package/dist/lib/wizard-specs.js +34 -0
  252. package/dist/lib/wizard-specs.js.map +1 -0
  253. package/dist/pure.d.ts +19 -0
  254. package/dist/pure.js +31 -0
  255. package/dist/pure.js.map +1 -0
  256. package/package.json +69 -0
@@ -0,0 +1,112 @@
1
+ import type { AcEntry } from './ac-response-parser.js';
2
+ export type { AcEntry } from './ac-response-parser.js';
3
+ /** Raw frontmatter shape — only the fields the pipeline navigator cares about. */
4
+ export type SpecFrontmatter = {
5
+ id?: string;
6
+ core?: string;
7
+ /** Human-readable spec title — preferred over the body `# …` heading when present. */
8
+ title?: string;
9
+ status?: string;
10
+ /** The id of the spec that supersedes this one. Present ⇒ terminal (C2/C3). */
11
+ superseded_by?: string;
12
+ pipeline_status?: string;
13
+ research_appendix?: string;
14
+ acceptance_criteria?: unknown[];
15
+ user_stories?: unknown[];
16
+ /** Typed functional requirements (FR-*) — consumed by `verify-acs` alongside AC/US. */
17
+ requirements?: unknown[];
18
+ e2e_tests?: unknown[];
19
+ /** Globs/paths the spec expects to touch — used to detect UI-bearing specs. */
20
+ affected_files?: unknown[];
21
+ tags?: unknown[];
22
+ /** Canonical machine-readable cross-spec blockers (list of spec IDs). May be null/dirty. */
23
+ blocked_by?: unknown;
24
+ /** Human-authored "builds on / related" list — historically dirty (prose + emoji). */
25
+ dependencies?: unknown;
26
+ };
27
+ /**
28
+ * Surgically set `pipeline_status` inside the leading frontmatter block without
29
+ * reformatting the rest of the YAML. Replaces the existing line, or appends one.
30
+ * Returns the text unchanged when there is no frontmatter block.
31
+ */
32
+ export declare function setPipelineStatus(text: string, status: string): string;
33
+ /**
34
+ * Idempotently set `artifacts.tasks: <rel>` inside the leading frontmatter block.
35
+ * Replaces an existing `tasks:` child under `artifacts:`, inserts one under an
36
+ * existing `artifacts:` block, or appends a fresh `artifacts:` block. Returns the
37
+ * text unchanged when already correct or when there is no frontmatter block.
38
+ *
39
+ * Closes the `pipeline/missing-tasks-artifact` validator gap: `reconcile`
40
+ * materializes `pipeline_status: tasks_generated` from the on-disk TASKS file, so
41
+ * it must also record `artifacts.tasks` (which the pipeline-status validator
42
+ * requires) rather than leaving it to be hand-added.
43
+ */
44
+ export declare function setTasksArtifact(text: string, rel: string): string;
45
+ /**
46
+ * Decide which relative path `reconcile` should write into `artifacts.tasks`, or
47
+ * `null` to mean "leave the frontmatter untouched".
48
+ *
49
+ * Non-destructive of valid pointers: many real specs use a non-canonical TASKS
50
+ * filename (e.g. `HR-02-credentialing-compliance-TASKS.md`,
51
+ * `<id>-PHASE-2-3-TASKS.md`). The earlier behavior unconditionally normalized
52
+ * `artifacts.tasks` to the canonical `<id>-TASKS.md` guess, rewriting a VALID
53
+ * pointer to a NON-EXISTENT path and tripping `pipeline/missing-tasks-artifact`.
54
+ *
55
+ * Rules (`fileExists(rel)` resolves a repo-relative path against repoRoot):
56
+ * - No frontmatter block → `null` (nothing to do).
57
+ * - Existing pointer whose target EXISTS → `null` (preserve — canonical or not).
58
+ * - Pointer ABSENT and the canonical file EXISTS → return the canonical path.
59
+ * - Pointer STALE (target missing) and the canonical file EXISTS → return the
60
+ * canonical path (heal the dangling pointer).
61
+ * - Otherwise (no pointer + no canonical file, or stale pointer + no canonical
62
+ * file) → `null`; the gate, not reconcile, surfaces the genuine gap.
63
+ */
64
+ export declare function resolveTasksArtifact(text: string, canonicalRel: string, fileExists: (rel: string) => boolean): string | null;
65
+ /** Kinds of artifact-derived sign-off markers the navigator detects.
66
+ * `compliance` is file-based (a `{ID}-COMPLIANCE-SIGNOFF.md`), not a body comment. */
67
+ export type SignoffType = 'validated' | 'spec-reviewed' | 'ux-reviewed' | 'heal-verified' | 'docshots' | 'ui-gate-waived' | 'submit-e2e-waived' | 'compliance';
68
+ /**
69
+ * Insert an HTML-comment sign-off marker (e.g. `<!-- validated: 2026-05-28 -->`)
70
+ * immediately after the spec's frontmatter block, or at the start of the file when
71
+ * none exists. Idempotent: when a marker of the same type already exists, returns
72
+ * the text unchanged and `inserted: false`.
73
+ */
74
+ export declare function insertSignoffMarker(text: string, type: SignoffType, date: string): {
75
+ text: string;
76
+ inserted: boolean;
77
+ };
78
+ /**
79
+ * Parse the leading YAML frontmatter block of a spec markdown string.
80
+ * Returns `{}` when absent or malformed — never throws.
81
+ */
82
+ export declare function readFrontmatter(text: string): SpecFrontmatter;
83
+ /**
84
+ * Remove the leading YAML frontmatter block (when present) so body-only regexes
85
+ * (the `# Title` heading, `**Status:**` prose) cannot match lines inside the
86
+ * frontmatter — a YAML comment like `# Replace placeholder values when
87
+ * authoring.` (SPEC_TEMPLATE.md line 2) is indistinguishable from an H1 by
88
+ * line shape alone.
89
+ */
90
+ export declare function stripFrontmatter(text: string): string;
91
+ /**
92
+ * Append AC entries to the frontmatter's `acceptance_criteria:` array. Preserves
93
+ * other frontmatter via a slice → re-serialize-only-the-AC-list strategy. Returns
94
+ * the input unchanged when there is no frontmatter block.
95
+ */
96
+ export declare function appendAcceptanceCriteria(text: string, entries: AcEntry[]): string;
97
+ /**
98
+ * Within the leading frontmatter block, flip the `status:` of one list item
99
+ * (under `acceptance_criteria:` / `user_stories:` / `requirements:`) identified
100
+ * by its `- id: <id>` line. Line-scoped so all other lines — comments, ordering,
101
+ * sibling items — are preserved byte-for-byte. Returns the text unchanged when
102
+ * there is no frontmatter, the section/id is absent, or the status already matches.
103
+ * Used by `verify-acs` to auto-promote planned -> verified on a real test pass.
104
+ */
105
+ export declare function setRequirementStatus(text: string, section: 'acceptance_criteria' | 'user_stories' | 'requirements', id: string, status: 'planned' | 'verified'): string;
106
+ /**
107
+ * Idempotently add `rel` to the `e2e_tests:` list inside the leading frontmatter block.
108
+ * Appends a fresh `e2e_tests:` block when absent. Returns the text unchanged when
109
+ * the path is already listed or there is no frontmatter block. Used by `gen-tests
110
+ * --update-frontmatter`.
111
+ */
112
+ export declare function setE2eTests(text: string, rel: string): string;
@@ -0,0 +1,261 @@
1
+ import { parse, stringify as yamlStringify } from 'yaml';
2
+ const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/;
3
+ /**
4
+ * Surgically set `pipeline_status` inside the leading frontmatter block without
5
+ * reformatting the rest of the YAML. Replaces the existing line, or appends one.
6
+ * Returns the text unchanged when there is no frontmatter block.
7
+ */
8
+ export function setPipelineStatus(text, status) {
9
+ const match = FRONTMATTER_RE.exec(text);
10
+ if (!match)
11
+ return text;
12
+ const block = match[1];
13
+ const line = `pipeline_status: ${status}`;
14
+ const newBlock = /^pipeline_status:.*$/m.test(block)
15
+ ? block.replace(/^pipeline_status:.*$/m, line)
16
+ : `${block}\n${line}`;
17
+ // function replacer avoids `$` in newBlock being treated as a backreference
18
+ return text.replace(FRONTMATTER_RE, () => `---\n${newBlock}\n---`);
19
+ }
20
+ /**
21
+ * Idempotently set `artifacts.tasks: <rel>` inside the leading frontmatter block.
22
+ * Replaces an existing `tasks:` child under `artifacts:`, inserts one under an
23
+ * existing `artifacts:` block, or appends a fresh `artifacts:` block. Returns the
24
+ * text unchanged when already correct or when there is no frontmatter block.
25
+ *
26
+ * Closes the `pipeline/missing-tasks-artifact` validator gap: `reconcile`
27
+ * materializes `pipeline_status: tasks_generated` from the on-disk TASKS file, so
28
+ * it must also record `artifacts.tasks` (which the pipeline-status validator
29
+ * requires) rather than leaving it to be hand-added.
30
+ */
31
+ export function setTasksArtifact(text, rel) {
32
+ const match = FRONTMATTER_RE.exec(text);
33
+ if (!match)
34
+ return text;
35
+ const block = match[1];
36
+ const lines = block.split('\n');
37
+ const artIdx = lines.findIndex((l) => /^artifacts:\s*$/.test(l));
38
+ let newLines;
39
+ if (artIdx === -1) {
40
+ newLines = [...lines, 'artifacts:', ` tasks: ${rel}`];
41
+ }
42
+ else {
43
+ // Span of contiguous indented children directly under `artifacts:`.
44
+ let end = artIdx + 1;
45
+ while (end < lines.length && /^[ \t]+\S/.test(lines[end]))
46
+ end++;
47
+ const indent = lines[artIdx + 1]?.match(/^([ \t]+)/)?.[1] ?? ' ';
48
+ const childOffset = lines.slice(artIdx + 1, end).findIndex((l) => /^[ \t]+tasks:/.test(l));
49
+ if (childOffset === -1) {
50
+ newLines = [...lines.slice(0, artIdx + 1), `${indent}tasks: ${rel}`, ...lines.slice(artIdx + 1)];
51
+ }
52
+ else {
53
+ const absIdx = artIdx + 1 + childOffset;
54
+ const curIndent = lines[absIdx].match(/^([ \t]+)/)?.[1] ?? indent;
55
+ newLines = [...lines];
56
+ newLines[absIdx] = `${curIndent}tasks: ${rel}`;
57
+ }
58
+ }
59
+ const newBlock = newLines.join('\n');
60
+ if (newBlock === block)
61
+ return text;
62
+ return text.replace(FRONTMATTER_RE, () => `---\n${newBlock}\n---`);
63
+ }
64
+ /**
65
+ * Decide which relative path `reconcile` should write into `artifacts.tasks`, or
66
+ * `null` to mean "leave the frontmatter untouched".
67
+ *
68
+ * Non-destructive of valid pointers: many real specs use a non-canonical TASKS
69
+ * filename (e.g. `HR-02-credentialing-compliance-TASKS.md`,
70
+ * `<id>-PHASE-2-3-TASKS.md`). The earlier behavior unconditionally normalized
71
+ * `artifacts.tasks` to the canonical `<id>-TASKS.md` guess, rewriting a VALID
72
+ * pointer to a NON-EXISTENT path and tripping `pipeline/missing-tasks-artifact`.
73
+ *
74
+ * Rules (`fileExists(rel)` resolves a repo-relative path against repoRoot):
75
+ * - No frontmatter block → `null` (nothing to do).
76
+ * - Existing pointer whose target EXISTS → `null` (preserve — canonical or not).
77
+ * - Pointer ABSENT and the canonical file EXISTS → return the canonical path.
78
+ * - Pointer STALE (target missing) and the canonical file EXISTS → return the
79
+ * canonical path (heal the dangling pointer).
80
+ * - Otherwise (no pointer + no canonical file, or stale pointer + no canonical
81
+ * file) → `null`; the gate, not reconcile, surfaces the genuine gap.
82
+ */
83
+ export function resolveTasksArtifact(text, canonicalRel, fileExists) {
84
+ const match = FRONTMATTER_RE.exec(text);
85
+ if (!match)
86
+ return null;
87
+ const fm = readFrontmatter(text);
88
+ const artifacts = fm && typeof fm === 'object' ? fm.artifacts : undefined;
89
+ const existing = artifacts && typeof artifacts === 'object' ? artifacts.tasks : undefined;
90
+ const current = typeof existing === 'string' && existing.trim() !== '' ? existing.trim() : undefined;
91
+ // Existing pointer that resolves to a real file: preserve it (canonical or not).
92
+ if (current && fileExists(current))
93
+ return null;
94
+ // Absent or stale pointer: only adopt the canonical guess if it actually exists.
95
+ if (fileExists(canonicalRel))
96
+ return canonicalRel;
97
+ // No valid target anywhere — leave the gate to flag the genuine gap.
98
+ return null;
99
+ }
100
+ /**
101
+ * Insert an HTML-comment sign-off marker (e.g. `<!-- validated: 2026-05-28 -->`)
102
+ * immediately after the spec's frontmatter block, or at the start of the file when
103
+ * none exists. Idempotent: when a marker of the same type already exists, returns
104
+ * the text unchanged and `inserted: false`.
105
+ */
106
+ export function insertSignoffMarker(text, type, date) {
107
+ const markerRe = new RegExp(`<!--\\s*${type}:`, 'i');
108
+ if (markerRe.test(text))
109
+ return { text, inserted: false };
110
+ const marker = `<!-- ${type}: ${date} -->`;
111
+ const match = FRONTMATTER_RE.exec(text);
112
+ if (!match) {
113
+ return { text: `${marker}\n${text}`, inserted: true };
114
+ }
115
+ const blockEnd = match.index + match[0].length;
116
+ const before = text.slice(0, blockEnd);
117
+ const after = text.slice(blockEnd);
118
+ const sep = after.startsWith('\n') ? '' : '\n';
119
+ return { text: `${before}${sep}\n${marker}\n${after.replace(/^\n+/, '')}`, inserted: true };
120
+ }
121
+ /**
122
+ * Parse the leading YAML frontmatter block of a spec markdown string.
123
+ * Returns `{}` when absent or malformed — never throws.
124
+ */
125
+ export function readFrontmatter(text) {
126
+ const match = FRONTMATTER_RE.exec(text);
127
+ if (!match)
128
+ return {};
129
+ try {
130
+ const parsed = parse(match[1]);
131
+ if (parsed && typeof parsed === 'object')
132
+ return parsed;
133
+ return {};
134
+ }
135
+ catch {
136
+ return {};
137
+ }
138
+ }
139
+ /**
140
+ * Remove the leading YAML frontmatter block (when present) so body-only regexes
141
+ * (the `# Title` heading, `**Status:**` prose) cannot match lines inside the
142
+ * frontmatter — a YAML comment like `# Replace placeholder values when
143
+ * authoring.` (SPEC_TEMPLATE.md line 2) is indistinguishable from an H1 by
144
+ * line shape alone.
145
+ */
146
+ export function stripFrontmatter(text) {
147
+ return text.replace(FRONTMATTER_RE, '');
148
+ }
149
+ /**
150
+ * Append AC entries to the frontmatter's `acceptance_criteria:` array. Preserves
151
+ * other frontmatter via a slice → re-serialize-only-the-AC-list strategy. Returns
152
+ * the input unchanged when there is no frontmatter block.
153
+ */
154
+ export function appendAcceptanceCriteria(text, entries) {
155
+ if (entries.length === 0)
156
+ return text;
157
+ const match = FRONTMATTER_RE.exec(text);
158
+ if (!match)
159
+ return text;
160
+ const block = match[1];
161
+ const hasKey = /^acceptance_criteria\s*:/m.test(block);
162
+ let newBlock;
163
+ if (hasKey) {
164
+ const parsed = readFrontmatter(text);
165
+ const existing = Array.isArray(parsed.acceptance_criteria) ? parsed.acceptance_criteria : [];
166
+ const merged = yamlStringify({ acceptance_criteria: [...existing, ...entries] }).trimEnd();
167
+ // Replace the `acceptance_criteria:` block: from that line through the next
168
+ // top-level key OR the end of the frontmatter block.
169
+ newBlock = block.replace(/^acceptance_criteria\s*:[\s\S]*?(?=^[A-Za-z_][A-Za-z0-9_]*\s*:|\s*$)/m, `${merged}\n`);
170
+ }
171
+ else {
172
+ newBlock = `${block}\n${yamlStringify({ acceptance_criteria: entries }).trimEnd()}`;
173
+ }
174
+ return text.replace(FRONTMATTER_RE, () => `---\n${newBlock}\n---`);
175
+ }
176
+ /**
177
+ * Within the leading frontmatter block, flip the `status:` of one list item
178
+ * (under `acceptance_criteria:` / `user_stories:` / `requirements:`) identified
179
+ * by its `- id: <id>` line. Line-scoped so all other lines — comments, ordering,
180
+ * sibling items — are preserved byte-for-byte. Returns the text unchanged when
181
+ * there is no frontmatter, the section/id is absent, or the status already matches.
182
+ * Used by `verify-acs` to auto-promote planned -> verified on a real test pass.
183
+ */
184
+ export function setRequirementStatus(text, section, id, status) {
185
+ const match = FRONTMATTER_RE.exec(text);
186
+ if (!match)
187
+ return text;
188
+ const lines = match[1].split('\n');
189
+ const sectionIdx = lines.findIndex((l) => new RegExp(`^${section}:\\s*$`).test(l));
190
+ if (sectionIdx === -1)
191
+ return text;
192
+ const escId = id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
193
+ const idRe = new RegExp(`^\\s*-\\s+id:\\s*["']?${escId}["']?\\s*$`);
194
+ let itemIdx = -1;
195
+ for (let i = sectionIdx + 1; i < lines.length; i++) {
196
+ if (/^\S/.test(lines[i]))
197
+ break; // next column-0 key ends the section
198
+ if (idRe.test(lines[i])) {
199
+ itemIdx = i;
200
+ break;
201
+ }
202
+ }
203
+ if (itemIdx === -1)
204
+ return text;
205
+ let statusIdx = -1;
206
+ for (let i = itemIdx + 1; i < lines.length; i++) {
207
+ if (/^\S/.test(lines[i]) || /^\s*-\s+id:/.test(lines[i]))
208
+ break; // next item or section end
209
+ if (/^\s*status:\s*/.test(lines[i])) {
210
+ statusIdx = i;
211
+ break;
212
+ }
213
+ }
214
+ const indent = lines[itemIdx].match(/^(\s*)-\s/)?.[1] ?? ' ';
215
+ const statusLine = `${indent} status: ${status}`;
216
+ let newLines;
217
+ if (statusIdx !== -1) {
218
+ if (lines[statusIdx].trim() === `status: ${status}`)
219
+ return text;
220
+ newLines = [...lines];
221
+ newLines[statusIdx] = statusLine;
222
+ }
223
+ else {
224
+ newLines = [...lines.slice(0, itemIdx + 1), statusLine, ...lines.slice(itemIdx + 1)];
225
+ }
226
+ const newBlock = newLines.join('\n');
227
+ if (newBlock === match[1])
228
+ return text;
229
+ return text.replace(FRONTMATTER_RE, () => `---\n${newBlock}\n---`);
230
+ }
231
+ /**
232
+ * Idempotently add `rel` to the `e2e_tests:` list inside the leading frontmatter block.
233
+ * Appends a fresh `e2e_tests:` block when absent. Returns the text unchanged when
234
+ * the path is already listed or there is no frontmatter block. Used by `gen-tests
235
+ * --update-frontmatter`.
236
+ */
237
+ export function setE2eTests(text, rel) {
238
+ const match = FRONTMATTER_RE.exec(text);
239
+ if (!match)
240
+ return text;
241
+ const block = match[1];
242
+ const lines = block.split('\n');
243
+ const keyIdx = lines.findIndex((l) => /^e2e_tests:\s*$/.test(l));
244
+ let newLines;
245
+ if (keyIdx === -1) {
246
+ newLines = [...lines, 'e2e_tests:', ` - ${rel}`];
247
+ }
248
+ else {
249
+ let end = keyIdx + 1;
250
+ while (end < lines.length && /^[ \t]+-\s/.test(lines[end]))
251
+ end++;
252
+ if (lines.slice(keyIdx + 1, end).some((l) => l.trim() === `- ${rel}`))
253
+ return text;
254
+ newLines = [...lines.slice(0, end), ` - ${rel}`, ...lines.slice(end)];
255
+ }
256
+ const newBlock = newLines.join('\n');
257
+ if (newBlock === block)
258
+ return text;
259
+ return text.replace(FRONTMATTER_RE, () => `---\n${newBlock}\n---`);
260
+ }
261
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/lib/frontmatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { SpecFile } from './specs.js';
2
+ export type FunnelInput = {
3
+ specs: SpecFile[];
4
+ /** Map spec.path → last-modified epoch-ms (from git). Missing entry = treat as fresh. */
5
+ mtimeBySpecPath: Record<string, number>;
6
+ stallDays: number;
7
+ /** Override of Date.now() for testability. */
8
+ now?: number;
9
+ };
10
+ export type FunnelReport = {
11
+ total: number;
12
+ byStage: Record<string, number>;
13
+ withStories: number;
14
+ withACs: number;
15
+ storiedButNoACs: number;
16
+ /** Advisory (canonical seed-fixture Phase 3): specs that declare a Lane-4 seed fixture. */
17
+ withSeedFixtures: number;
18
+ /** Specs with ACs but no declared seed fixture — the "ships rows, no fixture" gap signal. */
19
+ acsButNoSeedFixture: number;
20
+ stalls: number;
21
+ };
22
+ export declare function computeFunnel(input: FunnelInput): FunnelReport;
@@ -0,0 +1,41 @@
1
+ import { computeStage } from './pipeline-map.js';
2
+ /** Treat tasks_generated as terminal for stall purposes. */
3
+ const TERMINAL_STAGES = new Set(['tasks_generated']);
4
+ export function computeFunnel(input) {
5
+ const now = input.now ?? Date.now();
6
+ const cutoff = now - input.stallDays * 24 * 60 * 60 * 1000;
7
+ const byStage = {};
8
+ let withStories = 0, withACs = 0, storied = 0, withSeedFixtures = 0, acsButNoSeedFixture = 0, stalls = 0;
9
+ for (const s of input.specs) {
10
+ const stage = computeStage(s);
11
+ byStage[stage] = (byStage[stage] ?? 0) + 1;
12
+ if (s.signals.hasUserStories)
13
+ withStories++;
14
+ if (s.signals.hasAcceptanceCriteria)
15
+ withACs++;
16
+ if (s.signals.hasUserStories && !s.signals.hasAcceptanceCriteria)
17
+ storied++;
18
+ if (s.signals.hasSeedFixture)
19
+ withSeedFixtures++;
20
+ // "Ships rows, no fixture" proxy: a spec mature enough to have ACs but with no
21
+ // declared seed fixture — its surfaces likely render empty in E2E/docs.
22
+ if (s.signals.hasAcceptanceCriteria && !s.signals.hasSeedFixture)
23
+ acsButNoSeedFixture++;
24
+ const mtime = input.mtimeBySpecPath[s.path];
25
+ // A spec is terminal (not stalled) if its computed stage is terminal OR if it already has tasks.
26
+ const isTerminal = TERMINAL_STAGES.has(stage) || s.hasTasks;
27
+ if (typeof mtime === 'number' && mtime <= cutoff && !isTerminal)
28
+ stalls++;
29
+ }
30
+ return {
31
+ total: input.specs.length,
32
+ byStage,
33
+ withStories,
34
+ withACs,
35
+ storiedButNoACs: storied,
36
+ withSeedFixtures,
37
+ acsButNoSeedFixture,
38
+ stalls,
39
+ };
40
+ }
41
+ //# sourceMappingURL=funnel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"funnel.js","sourceRoot":"","sources":["../../src/lib/funnel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAyBjD,4DAA4D;AAC5D,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAErD,MAAM,UAAU,aAAa,CAAC,KAAkB;IAC9C,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IACpC,MAAM,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,SAAS,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC3D,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,IAAI,WAAW,GAAG,CAAC,EACjB,OAAO,GAAG,CAAC,EACX,OAAO,GAAG,CAAC,EACX,gBAAgB,GAAG,CAAC,EACpB,mBAAmB,GAAG,CAAC,EACvB,MAAM,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,CAAC,OAAO,CAAC,cAAc;YAAE,WAAW,EAAE,CAAC;QAC5C,IAAI,CAAC,CAAC,OAAO,CAAC,qBAAqB;YAAE,OAAO,EAAE,CAAC;QAC/C,IAAI,CAAC,CAAC,OAAO,CAAC,cAAc,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,qBAAqB;YAAE,OAAO,EAAE,CAAC;QAC5E,IAAI,CAAC,CAAC,OAAO,CAAC,cAAc;YAAE,gBAAgB,EAAE,CAAC;QACjD,+EAA+E;QAC/E,wEAAwE;QACxE,IAAI,CAAC,CAAC,OAAO,CAAC,qBAAqB,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc;YAAE,mBAAmB,EAAE,CAAC;QACxF,MAAM,KAAK,GAAG,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5C,iGAAiG;QACjG,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC;QAC5D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,MAAM,IAAI,CAAC,UAAU;YAAE,MAAM,EAAE,CAAC;IAC5E,CAAC;IACD,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;QACzB,OAAO;QACP,WAAW;QACX,OAAO;QACP,eAAe,EAAE,OAAO;QACxB,gBAAgB;QAChB,mBAAmB;QACnB,MAAM;KACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ /** Last-modified epoch-ms for each path, derived from `git log -1 --format=%ct`.
2
+ * Falls back silently (path omitted) when git has no history for a path. The
3
+ * caller treats absence as "fresh" (no stall). */
4
+ export declare function gitMtimes(repoRoot: string, paths: string[]): Record<string, number>;
@@ -0,0 +1,23 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ /** Last-modified epoch-ms for each path, derived from `git log -1 --format=%ct`.
3
+ * Falls back silently (path omitted) when git has no history for a path. The
4
+ * caller treats absence as "fresh" (no stall). */
5
+ export function gitMtimes(repoRoot, paths) {
6
+ const out = {};
7
+ for (const p of paths) {
8
+ try {
9
+ const sec = execFileSync('git', ['log', '-1', '--format=%ct', '--', p], {
10
+ cwd: repoRoot,
11
+ encoding: 'utf8',
12
+ stdio: ['ignore', 'pipe', 'ignore'],
13
+ }).trim();
14
+ if (sec)
15
+ out[p] = parseInt(sec, 10) * 1000;
16
+ }
17
+ catch {
18
+ // path has no git history — leave it out of the map
19
+ }
20
+ }
21
+ return out;
22
+ }
23
+ //# sourceMappingURL=git-mtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-mtime.js","sourceRoot":"","sources":["../../src/lib/git-mtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;mDAEmD;AACnD,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,KAAe;IACzD,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;gBACtE,GAAG,EAAE,QAAQ;gBACb,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;aACpC,CAAC,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,GAAG;gBAAE,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,oDAAoD;QACtD,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { EosSpecEvent } from './events.js';
2
+ export type CommandStats = {
3
+ count: number;
4
+ wallClockMs: number;
5
+ successRate: number;
6
+ };
7
+ export type ModelStats = {
8
+ calls: number;
9
+ tokensIn: number;
10
+ tokensOut: number;
11
+ totalCostUsd: number | null;
12
+ };
13
+ export type HaltCauseStats = {
14
+ /** Halts where a governance gate fired (autonomy working as designed). */
15
+ expectedHalts: number;
16
+ /** Halts from crashes/tooling failures/stalls (genuine intervention). */
17
+ unexpectedHalts: number;
18
+ /** Count by cause key: a gate name (expected), 'crash', or 'stalled' (unexpected). */
19
+ byCause: Record<string, number>;
20
+ };
21
+ export type AutonomyStats = {
22
+ longestRun: number;
23
+ humanTouches: number;
24
+ lastHalt?: {
25
+ command: string;
26
+ error?: string;
27
+ ts: string;
28
+ };
29
+ byHaltCause: HaltCauseStats;
30
+ };
31
+ export type MetricsReport = {
32
+ total: number;
33
+ windowStart?: string;
34
+ windowEnd?: string;
35
+ byCommand: Record<string, CommandStats>;
36
+ byModel: Record<string, ModelStats>;
37
+ autonomy: AutonomyStats;
38
+ };
39
+ export declare function computeMetrics(events: EosSpecEvent[], opts?: {
40
+ since?: Date;
41
+ core?: string;
42
+ }): MetricsReport;
@@ -0,0 +1,92 @@
1
+ /** D3 stall threshold. The funnel 30-day idiom is far too coarse for an
2
+ * intra-run heartbeat; a multi-hour gap between consecutive events with no
3
+ * terminating event is treated as a silent death (context exhaustion / wedged
4
+ * process). Tunable via EOS_SPEC_STALL_GAP_MS; default 2 hours. */
5
+ const STALL_GAP_MS = Number(process.env.EOS_SPEC_STALL_GAP_MS) || 2 * 60 * 60 * 1000;
6
+ export function computeMetrics(events, opts = {}) {
7
+ const filtered = events.filter((e) => {
8
+ if (opts.since && new Date(e.ts).getTime() < opts.since.getTime())
9
+ return false;
10
+ if (opts.core && e.core !== opts.core)
11
+ return false;
12
+ return true;
13
+ });
14
+ const byCommand = {};
15
+ const byModel = {};
16
+ let longestRun = 0;
17
+ let run = 0;
18
+ let humanTouches = 0;
19
+ let lastHalt;
20
+ let expectedHalts = 0;
21
+ let unexpectedHalts = 0;
22
+ const byCause = {};
23
+ const bump = (k) => {
24
+ byCause[k] = (byCause[k] ?? 0) + 1;
25
+ };
26
+ let prevLifecycleTs;
27
+ for (const e of filtered) {
28
+ const c = (byCommand[e.command] ??= { count: 0, ms: 0, oks: 0 });
29
+ c.count++;
30
+ c.ms += e.ms;
31
+ if (e.ok)
32
+ c.oks++;
33
+ if (e.model && e.tokens) {
34
+ const m = (byModel[e.model] ??= { calls: 0, tokensIn: 0, tokensOut: 0, cost: 0, anyNull: false });
35
+ m.calls++;
36
+ m.tokensIn += e.tokens.prompt;
37
+ m.tokensOut += e.tokens.completion;
38
+ if (e.costUsd === null || e.costUsd === undefined)
39
+ m.anyNull = true;
40
+ else
41
+ m.cost += e.costUsd;
42
+ }
43
+ // D1 taxonomy — a gate event (verdict:block, or an explicit halt) is an
44
+ // EXPECTED halt; an ok:false with no gate is an unexpected crash. A gate
45
+ // event with ok:true (warn/respected-pass) is neither a halt nor a touch.
46
+ if (e.gate !== undefined) {
47
+ if (e.verdict === 'block' || e.ok === false) {
48
+ expectedHalts++;
49
+ bump(e.gate);
50
+ }
51
+ // Gate events never participate in the run/human-touch counters below.
52
+ continue;
53
+ }
54
+ // D2 — only command-lifecycle events (no gate) drive the run counters.
55
+ // D3 — a multi-hour gap between consecutive lifecycle events with no
56
+ // terminating event is a silent stall (unexpected halt).
57
+ const tsMs = new Date(e.ts).getTime();
58
+ if (prevLifecycleTs !== undefined && tsMs - prevLifecycleTs > STALL_GAP_MS) {
59
+ run = 0;
60
+ unexpectedHalts++;
61
+ bump('stalled');
62
+ }
63
+ prevLifecycleTs = tsMs;
64
+ if (e.ok) {
65
+ run++;
66
+ if (run > longestRun)
67
+ longestRun = run;
68
+ }
69
+ else {
70
+ run = 0;
71
+ humanTouches++;
72
+ unexpectedHalts++;
73
+ bump(e.haltCause ?? 'crash');
74
+ lastHalt = { command: e.command, error: e.error, ts: e.ts };
75
+ }
76
+ }
77
+ return {
78
+ total: filtered.length,
79
+ windowStart: filtered[0]?.ts,
80
+ windowEnd: filtered[filtered.length - 1]?.ts,
81
+ byCommand: Object.fromEntries(Object.entries(byCommand).map(([k, v]) => [
82
+ k,
83
+ { count: v.count, wallClockMs: v.ms, successRate: v.oks / v.count },
84
+ ])),
85
+ byModel: Object.fromEntries(Object.entries(byModel).map(([k, v]) => [
86
+ k,
87
+ { calls: v.calls, tokensIn: v.tokensIn, tokensOut: v.tokensOut, totalCostUsd: v.anyNull ? null : v.cost },
88
+ ])),
89
+ autonomy: { longestRun, humanTouches, lastHalt, byHaltCause: { expectedHalts, unexpectedHalts, byCause } },
90
+ };
91
+ }
92
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/lib/metrics.ts"],"names":[],"mappings":"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"}