@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,179 @@
1
+ /**
2
+ * Typed acceptance-criteria / user-story / functional-requirement schema for spec
3
+ * frontmatter, plus a lenient normalizer. Adapted from encoreos's
4
+ * `ac-response-parser.ts`, but reads already-parsed YAML frontmatter values
5
+ * (objects) rather than an LLM JSON response, and coerces scalar Given/When/Then
6
+ * strings into single-element arrays so scalar-shaped legacy specs parse unchanged.
7
+ */
8
+ /** The status values the audit's "AC Validated" gate accepts as closed
9
+ * (everything except `planned`). */
10
+ export const TERMINAL_AC_STATUSES = ['verified', 'hand_closed', 'deferred'];
11
+ export const AC_ID_RE = /^AC-\d+$/;
12
+ export const US_ID_RE = /^US-\d+$/;
13
+ export const FR_ID_RE = /^FR-\d+$/;
14
+ /** Scalar string -> `[string]`; array -> trimmed non-empty strings; anything else -> `[]`. */
15
+ export function coerceStringArray(v) {
16
+ if (typeof v === 'string') {
17
+ const t = v.trim();
18
+ return t === '' ? [] : [t];
19
+ }
20
+ if (Array.isArray(v)) {
21
+ return v.filter((x) => typeof x === 'string' && x.trim() !== '').map((x) => x.trim());
22
+ }
23
+ return [];
24
+ }
25
+ function toStatus(v) {
26
+ if (v === 'verified' || v === 'hand_closed' || v === 'deferred')
27
+ return v;
28
+ return 'planned';
29
+ }
30
+ function isValidStatus(v) {
31
+ return v === 'planned' || v === 'verified' || v === 'hand_closed' || v === 'deferred';
32
+ }
33
+ /**
34
+ * Normalizes a raw YAML frontmatter `acceptance_criteria` value into typed
35
+ * `AcceptanceCriterion` objects, collecting `ShapeIssue`s for any structural
36
+ * problems found. An entry with an unrecognized status is still included with
37
+ * `status` defaulted to `'planned'` plus a ShapeIssue — callers must check
38
+ * `issues` before trusting `status`.
39
+ */
40
+ export function normalizeAcceptanceCriteria(raw) {
41
+ const issues = [];
42
+ if (raw === undefined)
43
+ return { acs: [], issues };
44
+ if (!Array.isArray(raw)) {
45
+ return { acs: [], issues: [{ field: 'acceptance_criteria', message: 'must be a list of acceptance criteria' }] };
46
+ }
47
+ const acs = [];
48
+ raw.forEach((entry, i) => {
49
+ if (!entry || typeof entry !== 'object') {
50
+ issues.push({ field: `acceptance_criteria[${i}]`, message: 'entry is not a mapping' });
51
+ return;
52
+ }
53
+ const rec = entry;
54
+ const id = typeof rec.id === 'string' ? rec.id : undefined;
55
+ if (!id) {
56
+ issues.push({ field: `acceptance_criteria[${i}]`, message: 'missing string id' });
57
+ return;
58
+ }
59
+ const statementOnly = rec.statement_only === true;
60
+ const given = coerceStringArray(rec.given);
61
+ const when = coerceStringArray(rec.when);
62
+ const then = coerceStringArray(rec.then);
63
+ if (!statementOnly && (given.length === 0 || when.length === 0 || then.length === 0)) {
64
+ const missing = [
65
+ given.length === 0 ? 'given' : null,
66
+ when.length === 0 ? 'when' : null,
67
+ then.length === 0 ? 'then' : null,
68
+ ].filter((x) => x !== null);
69
+ issues.push({
70
+ id,
71
+ field: 'given/when/then',
72
+ message: `AC ${id} is missing non-empty: ${missing.join(', ')} (or set statement_only: true)`,
73
+ });
74
+ }
75
+ if (rec.status !== undefined && !isValidStatus(rec.status)) {
76
+ issues.push({
77
+ id,
78
+ field: 'status',
79
+ message: `AC ${id} has invalid status "${String(rec.status)}" (planned|verified|hand_closed|deferred)`,
80
+ });
81
+ }
82
+ acs.push({
83
+ id,
84
+ userStory: typeof rec.user_story === 'string' ? rec.user_story : undefined,
85
+ status: toStatus(rec.status),
86
+ given,
87
+ when,
88
+ then,
89
+ statementOnly,
90
+ });
91
+ });
92
+ return { acs, issues };
93
+ }
94
+ export function normalizeUserStories(raw) {
95
+ const issues = [];
96
+ if (raw === undefined)
97
+ return { uss: [], issues };
98
+ if (!Array.isArray(raw))
99
+ return { uss: [], issues: [{ field: 'user_stories', message: 'must be a list' }] };
100
+ const uss = [];
101
+ raw.forEach((entry, i) => {
102
+ if (!entry || typeof entry !== 'object') {
103
+ issues.push({ field: `user_stories[${i}]`, message: 'entry is not a mapping' });
104
+ return;
105
+ }
106
+ const rec = entry;
107
+ const id = typeof rec.id === 'string' ? rec.id : undefined;
108
+ if (!id) {
109
+ issues.push({ field: `user_stories[${i}]`, message: 'missing string id' });
110
+ return;
111
+ }
112
+ const text = typeof rec.text === 'string' ? rec.text.trim() : '';
113
+ if (!text)
114
+ issues.push({ id, field: 'text', message: `US ${id} is missing text` });
115
+ uss.push({ id, text });
116
+ });
117
+ return { uss, issues };
118
+ }
119
+ export function normalizeRequirements(raw) {
120
+ const issues = [];
121
+ if (raw === undefined)
122
+ return { frs: [], issues };
123
+ if (!Array.isArray(raw))
124
+ return { frs: [], issues: [{ field: 'requirements', message: 'must be a list' }] };
125
+ const frs = [];
126
+ raw.forEach((entry, i) => {
127
+ if (!entry || typeof entry !== 'object') {
128
+ issues.push({ field: `requirements[${i}]`, message: 'entry is not a mapping' });
129
+ return;
130
+ }
131
+ const rec = entry;
132
+ const id = typeof rec.id === 'string' ? rec.id : undefined;
133
+ if (!id) {
134
+ issues.push({ field: `requirements[${i}]`, message: 'missing string id' });
135
+ return;
136
+ }
137
+ const text = typeof rec.text === 'string' ? rec.text.trim() : '';
138
+ if (!text)
139
+ issues.push({ id, field: 'text', message: `FR ${id} is missing text` });
140
+ frs.push({ id, text, status: toStatus(rec.status) });
141
+ });
142
+ return { frs, issues };
143
+ }
144
+ /** Per-AC status tally for the pipeline status reader (consumed by the P3 audit).
145
+ * `hand_closed` and `deferred` are terminal-but-not-`planned` statuses the audit
146
+ * treats as closed (issue #174); they are counted separately so the audit can
147
+ * still apply the Verification-backlink guard to `hand_closed`. */
148
+ export function acStatusCounts(raw) {
149
+ const { acs } = normalizeAcceptanceCriteria(raw);
150
+ const counts = { planned: 0, verified: 0, handClosed: 0, deferred: 0 };
151
+ for (const ac of acs) {
152
+ if (ac.status === 'verified')
153
+ counts.verified += 1;
154
+ else if (ac.status === 'hand_closed')
155
+ counts.handClosed += 1;
156
+ else if (ac.status === 'deferred')
157
+ counts.deferred += 1;
158
+ else
159
+ counts.planned += 1;
160
+ }
161
+ return counts;
162
+ }
163
+ /**
164
+ * True when a spec has ≥1 acceptance criterion and EVERY (well-formed) AC is in a
165
+ * terminal status (`verified` | `hand_closed` | `deferred`) — i.e. none remain
166
+ * `planned`. This is the typed, ADR-0015-aware replacement for the legacy
167
+ * `status === 'verified'` check `specs.ts` used to hand-roll: a `hand_closed`
168
+ * (proven by a CI suite the src-lane prover can't reach) or `deferred` AC
169
+ * terminally resolves the generate-tests gate exactly as a `verified` one does
170
+ * (issue #174). Malformed (id-less) AC entries are dropped by
171
+ * `normalizeAcceptanceCriteria` — and `validate` flags them — so they are not
172
+ * counted here; an empty / non-array value is `false`.
173
+ */
174
+ export function allAcceptanceCriteriaTerminal(raw) {
175
+ const c = acStatusCounts(raw);
176
+ const total = c.planned + c.verified + c.handClosed + c.deferred;
177
+ return total > 0 && c.planned === 0;
178
+ }
179
+ //# sourceMappingURL=requirements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requirements.js","sourceRoot":"","sources":["../../src/lib/requirements.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { ScorecardReport } from './scorecard.js';
2
+ export type RollupCoreRow = {
3
+ core: string;
4
+ total: number;
5
+ authored: number;
6
+ claimsComplete: number;
7
+ claimedCompleteButUnverified: number;
8
+ withVerifiedRequirements: number;
9
+ verifiedPct: number;
10
+ authoredPct: number;
11
+ };
12
+ export type RollupReport = {
13
+ cores: string[];
14
+ totals: {
15
+ specs: number;
16
+ claimsComplete: number;
17
+ claimedCompleteButUnverified: number;
18
+ withVerifiedRequirements: number;
19
+ verifiedPct: number;
20
+ };
21
+ rows: RollupCoreRow[];
22
+ };
23
+ /** Aggregate per-core scorecards into a ranked platform readiness view (most-ready first). */
24
+ export declare function computeRollup(perCore: ScorecardReport[]): RollupReport;
@@ -0,0 +1,34 @@
1
+ const pct = (n, d) => (d > 0 ? Math.round((n / d) * 1000) / 10 : 0);
2
+ /** Aggregate per-core scorecards into a ranked platform readiness view (most-ready first). */
3
+ export function computeRollup(perCore) {
4
+ const rows = perCore.map((r) => {
5
+ const authored = r.byStage.tasks_generated ?? 0;
6
+ return {
7
+ core: r.core,
8
+ total: r.total,
9
+ authored,
10
+ claimsComplete: r.claimsComplete,
11
+ claimedCompleteButUnverified: r.claimedCompleteButUnverified,
12
+ withVerifiedRequirements: r.withVerifiedRequirements,
13
+ verifiedPct: pct(r.withVerifiedRequirements, r.total),
14
+ authoredPct: pct(authored, r.total),
15
+ };
16
+ });
17
+ rows.sort((a, b) => b.verifiedPct - a.verifiedPct || b.authoredPct - a.authoredPct || a.core.localeCompare(b.core));
18
+ const specs = rows.reduce((s, r) => s + r.total, 0);
19
+ const claimsComplete = rows.reduce((s, r) => s + r.claimsComplete, 0);
20
+ const claimedCompleteButUnverified = rows.reduce((s, r) => s + r.claimedCompleteButUnverified, 0);
21
+ const withVerifiedRequirements = rows.reduce((s, r) => s + r.withVerifiedRequirements, 0);
22
+ return {
23
+ cores: perCore.map((r) => r.core),
24
+ totals: {
25
+ specs,
26
+ claimsComplete,
27
+ claimedCompleteButUnverified,
28
+ withVerifiedRequirements,
29
+ verifiedPct: pct(withVerifiedRequirements, specs),
30
+ },
31
+ rows,
32
+ };
33
+ }
34
+ //# sourceMappingURL=rollup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollup.js","sourceRoot":"","sources":["../../src/lib/rollup.ts"],"names":[],"mappings":"AAyBA,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE5F,8FAA8F;AAC9F,MAAM,UAAU,aAAa,CAAC,OAA0B;IACtD,MAAM,IAAI,GAAoB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC9C,MAAM,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC,eAAe,IAAI,CAAC,CAAC;QAChD,OAAO;YACL,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,QAAQ;YACR,cAAc,EAAE,CAAC,CAAC,cAAc;YAChC,4BAA4B,EAAE,CAAC,CAAC,4BAA4B;YAC5D,wBAAwB,EAAE,CAAC,CAAC,wBAAwB;YACpD,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,wBAAwB,EAAE,CAAC,CAAC,KAAK,CAAC;YACrD,WAAW,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC;SACpC,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEpH,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACpD,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;IACtE,MAAM,4BAA4B,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,4BAA4B,EAAE,CAAC,CAAC,CAAC;IAClG,MAAM,wBAAwB,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,wBAAwB,EAAE,CAAC,CAAC,CAAC;IAE1F,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACjC,MAAM,EAAE;YACN,KAAK;YACL,cAAc;YACd,4BAA4B;YAC5B,wBAAwB;YACxB,WAAW,EAAE,GAAG,CAAC,wBAAwB,EAAE,KAAK,CAAC;SAClD;QACD,IAAI;KACL,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { type ScorecardReport } from './scorecard.js';
2
+ /**
3
+ * Gather per-spec inputs for one core and compute its scorecard. `verifiedBySpec` is passed in so the
4
+ * caller can run scanVerifies(repoRoot) ONCE and reuse it across cores (the rollup does this).
5
+ */
6
+ export declare function buildScorecardForCore(repoRoot: string, core: string | undefined, verifiedBySpec: Map<string, Set<string>>): ScorecardReport;
@@ -0,0 +1,48 @@
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { readFrontmatter } from './frontmatter.js';
4
+ import { collectRequirementIds, computeScorecard, countRequirements, } from './scorecard.js';
5
+ import { listSpecs } from './specs.js';
6
+ /** True if dir exists and contains at least one entry matching `pred`. */
7
+ function dirHas(dir, pred) {
8
+ try {
9
+ return readdirSync(dir).some(pred);
10
+ }
11
+ catch {
12
+ return false;
13
+ }
14
+ }
15
+ /** Core-level Definition-of-Done signals: a DocShot lane and hand-written E2E (generated scaffolds excluded). */
16
+ function coreSignalsFor(repoRoot, core) {
17
+ if (!core)
18
+ return { hasDocshots: false, hasE2E: false };
19
+ const imagesDir = join(repoRoot, 'docs/images', core);
20
+ const e2eDir = join(repoRoot, 'tests/e2e', core);
21
+ const hasDocshots = existsSync(imagesDir) && dirHas(imagesDir, (n) => /\.(png|jpe?g|webp)$/i.test(n));
22
+ const hasE2E = dirHas(e2eDir, (n) => /\.(spec|test)\.tsx?$/.test(n));
23
+ return { hasDocshots, hasE2E };
24
+ }
25
+ /**
26
+ * Gather per-spec inputs for one core and compute its scorecard. `verifiedBySpec` is passed in so the
27
+ * caller can run scanVerifies(repoRoot) ONCE and reuse it across cores (the rollup does this).
28
+ */
29
+ export function buildScorecardForCore(repoRoot, core, verifiedBySpec) {
30
+ const specs = listSpecs(repoRoot, core);
31
+ const rows = specs.map((spec) => {
32
+ const text = readFileSync(spec.path, 'utf8');
33
+ const fm = readFrontmatter(text);
34
+ const claimedStatus = (typeof fm.status === 'string' ? fm.status : undefined) ?? spec.status;
35
+ const { acTotal, usTotal, frTotal } = countRequirements(text);
36
+ return {
37
+ spec,
38
+ claimedStatus,
39
+ acTotal,
40
+ usTotal,
41
+ frTotal,
42
+ declaredIds: collectRequirementIds(text),
43
+ verifiedIds: verifiedBySpec.get(spec.id) ?? new Set(),
44
+ };
45
+ });
46
+ return computeScorecard({ core: core ?? 'all', rows, coreSignals: coreSignalsFor(repoRoot, core) });
47
+ }
48
+ //# sourceMappingURL=scorecard-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scorecard-run.js","sourceRoot":"","sources":["../../src/lib/scorecard-run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,GAGlB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,0EAA0E;AAC1E,SAAS,MAAM,CAAC,GAAW,EAAE,IAA+B;IAC1D,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,iHAAiH;AACjH,SAAS,cAAc,CAAC,QAAgB,EAAE,IAAwB;IAChE,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACxD,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;IACjD,MAAM,WAAW,GAAG,UAAU,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACtG,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AACjC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,QAAgB,EAChB,IAAwB,EACxB,cAAwC;IAExC,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACxC,MAAM,IAAI,GAAwB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACnD,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,EAAE,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC;QAC7F,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC9D,OAAO;YACL,IAAI;YACJ,aAAa;YACb,OAAO;YACP,OAAO;YACP,OAAO;YACP,WAAW,EAAE,qBAAqB,CAAC,IAAI,CAAC;YACxC,WAAW,EAAE,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,GAAG,EAAU;SAC9D,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,OAAO,gBAAgB,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;AACtG,CAAC"}
@@ -0,0 +1,69 @@
1
+ import type { SpecFile } from './specs.js';
2
+ export type ScorecardRowInput = {
3
+ spec: SpecFile;
4
+ /** Claimed status — frontmatter `status` or the body `**Status:**` banner (raw). */
5
+ claimedStatus?: string;
6
+ acTotal: number;
7
+ usTotal: number;
8
+ /** Functional requirements (body `**FR-N**`) — first-class verifiable. */
9
+ frTotal?: number;
10
+ /** AC/US/FR ids backlinked for this spec via @verifies. */
11
+ verifiedIds: Set<string>;
12
+ /** Requirement ids the spec DECLARES (AC/US/FR). When provided, verifiedIds is intersected
13
+ * with this set; backlinks to undeclared ids count as warnings, not evidence. */
14
+ declaredIds?: Set<string>;
15
+ };
16
+ export type ScorecardRow = {
17
+ id: string;
18
+ core: string;
19
+ title?: string;
20
+ stage: string;
21
+ claimedStatus?: string;
22
+ claimsComplete: boolean;
23
+ requirementsTotal: number;
24
+ requirementsVerified: number;
25
+ /** @verifies backlinks pointing at ids the spec never declared (typo'd AC, stale fixture). */
26
+ verifiedUndeclared: number;
27
+ frontmatterAccurate: 'match' | 'overstated' | 'understated';
28
+ claimedCompleteButUnverified: boolean;
29
+ };
30
+ export type ScorecardReport = {
31
+ core: string;
32
+ total: number;
33
+ byStage: Record<string, number>;
34
+ claimsComplete: number;
35
+ claimedCompleteButUnverified: number;
36
+ withVerifiedRequirements: number;
37
+ /** Total backlinked-but-undeclared ids across the core — a data-quality warning, not evidence. */
38
+ verifiedUndeclaredTotal: number;
39
+ /** Core has a DocShot lane (docs/images/<core>/ with screenshots). */
40
+ hasDocshots: boolean;
41
+ /** Core has hand-written E2E (tests/e2e/<core>/). Generated scaffolds don't count. */
42
+ hasE2E: boolean;
43
+ rows: ScorecardRow[];
44
+ };
45
+ /** Count acceptance criteria (frontmatter), user stories, and functional requirements (body) declared by a spec.
46
+ * FR is first-class verifiable (Definition of Done). The `**` anchor means `**NFR-misc-N**` never counts as FR. */
47
+ export declare function countRequirements(specText: string): {
48
+ acTotal: number;
49
+ usTotal: number;
50
+ frTotal: number;
51
+ };
52
+ /** Collect the requirement ids a spec DECLARES: frontmatter acceptance_criteria / user_stories
53
+ * entry ids plus body `- **US-n**` / `- **FR-n**` items. Used to intersect @verifies backlinks —
54
+ * a backlink to an undeclared id is a warning, not verification evidence. */
55
+ export declare function collectRequirementIds(specText: string): Set<string>;
56
+ /**
57
+ * Deterministic readiness scorecard. Per spec it records the computed authoring stage, whether the
58
+ * spec *claims* completion, how many requirements (ACs + user stories) are `@verifies`-backed, and a
59
+ * frontmatter-accuracy verdict. The rollup's headline — `claimedCompleteButUnverified` — is the exact
60
+ * column every completion audit hand-derives: "says complete, zero AC-linked tests".
61
+ */
62
+ export declare function computeScorecard(input: {
63
+ core: string;
64
+ rows: ScorecardRowInput[];
65
+ coreSignals?: {
66
+ hasDocshots: boolean;
67
+ hasE2E: boolean;
68
+ };
69
+ }): ScorecardReport;
@@ -0,0 +1,113 @@
1
+ import { readFrontmatter } from './frontmatter.js';
2
+ import { computeStage } from './pipeline-map.js';
3
+ const COMPLETE_RE = /\b(complete|completed|done|shipped)\b|✅/i;
4
+ /** Funnel's terminal authoring stage. */
5
+ const TERMINAL_STAGE = 'tasks_generated';
6
+ /** Count acceptance criteria (frontmatter), user stories, and functional requirements (body) declared by a spec.
7
+ * FR is first-class verifiable (Definition of Done). The `**` anchor means `**NFR-misc-N**` never counts as FR. */
8
+ export function countRequirements(specText) {
9
+ const fm = readFrontmatter(specText);
10
+ const acTotal = Array.isArray(fm.acceptance_criteria) ? fm.acceptance_criteria.length : 0;
11
+ const fmUs = Array.isArray(fm.user_stories) ? fm.user_stories.length : 0;
12
+ const bodyUs = new Set((specText.match(/(?:^|\n)\s*-\s+\*{0,2}US-\d+/g) ?? []).map((s) => /US-\d+/.exec(s)?.[0] ?? ''));
13
+ bodyUs.delete('');
14
+ const bodyFr = new Set((specText.match(/(?:^|\n)\s*-\s+\*{0,2}FR-\d+/g) ?? []).map((s) => /FR-\d+/.exec(s)?.[0] ?? ''));
15
+ bodyFr.delete('');
16
+ return { acTotal, usTotal: Math.max(fmUs, bodyUs.size), frTotal: bodyFr.size };
17
+ }
18
+ /** Collect the requirement ids a spec DECLARES: frontmatter acceptance_criteria / user_stories
19
+ * entry ids plus body `- **US-n**` / `- **FR-n**` items. Used to intersect @verifies backlinks —
20
+ * a backlink to an undeclared id is a warning, not verification evidence. */
21
+ export function collectRequirementIds(specText) {
22
+ const fm = readFrontmatter(specText);
23
+ const ids = new Set();
24
+ const addFromEntries = (v) => {
25
+ if (!Array.isArray(v))
26
+ return;
27
+ for (const entry of v) {
28
+ if (typeof entry === 'string') {
29
+ const id = /^(?:AC|US|FR)-\d+/.exec(entry.trim())?.[0];
30
+ if (id)
31
+ ids.add(id);
32
+ continue;
33
+ }
34
+ const id = entry?.id;
35
+ if (typeof id === 'string' && /^(?:AC|US|FR)-\d+$/.test(id))
36
+ ids.add(id);
37
+ }
38
+ };
39
+ addFromEntries(fm.acceptance_criteria);
40
+ addFromEntries(fm.user_stories);
41
+ for (const m of specText.match(/(?:^|\n)\s*-\s+\*{0,2}(?:US|FR)-\d+/g) ?? []) {
42
+ const id = /(?:US|FR)-\d+/.exec(m)?.[0];
43
+ if (id)
44
+ ids.add(id);
45
+ }
46
+ return ids;
47
+ }
48
+ /**
49
+ * Deterministic readiness scorecard. Per spec it records the computed authoring stage, whether the
50
+ * spec *claims* completion, how many requirements (ACs + user stories) are `@verifies`-backed, and a
51
+ * frontmatter-accuracy verdict. The rollup's headline — `claimedCompleteButUnverified` — is the exact
52
+ * column every completion audit hand-derives: "says complete, zero AC-linked tests".
53
+ */
54
+ export function computeScorecard(input) {
55
+ const byStage = {};
56
+ let claimsCompleteCount = 0;
57
+ let claimedCompleteButUnverified = 0;
58
+ let withVerifiedRequirements = 0;
59
+ let verifiedUndeclaredTotal = 0;
60
+ const rows = input.rows.map((r) => {
61
+ const stage = computeStage(r.spec);
62
+ byStage[stage] = (byStage[stage] ?? 0) + 1;
63
+ const claimsComplete = !!r.claimedStatus && COMPLETE_RE.test(r.claimedStatus);
64
+ const requirementsTotal = r.acTotal + r.usTotal + (r.frTotal ?? 0);
65
+ // Only backlinks to ids the spec actually declares count as evidence; the rest are warnings.
66
+ const declared = r.declaredIds;
67
+ const requirementsVerified = declared
68
+ ? [...r.verifiedIds].filter((id) => declared.has(id)).length
69
+ : r.verifiedIds.size;
70
+ const verifiedUndeclared = r.verifiedIds.size - requirementsVerified;
71
+ verifiedUndeclaredTotal += verifiedUndeclared;
72
+ if (claimsComplete)
73
+ claimsCompleteCount++;
74
+ if (requirementsVerified > 0)
75
+ withVerifiedRequirements++;
76
+ const unverifiedComplete = claimsComplete && requirementsVerified === 0;
77
+ if (unverifiedComplete)
78
+ claimedCompleteButUnverified++;
79
+ let frontmatterAccurate = 'match';
80
+ if (claimsComplete && stage !== TERMINAL_STAGE && stage !== 'planned') {
81
+ frontmatterAccurate = 'overstated';
82
+ }
83
+ else if (!claimsComplete && stage === TERMINAL_STAGE) {
84
+ frontmatterAccurate = 'understated';
85
+ }
86
+ return {
87
+ id: r.spec.id,
88
+ core: r.spec.core,
89
+ title: r.spec.title,
90
+ stage,
91
+ claimedStatus: r.claimedStatus,
92
+ claimsComplete,
93
+ requirementsTotal,
94
+ requirementsVerified,
95
+ verifiedUndeclared,
96
+ frontmatterAccurate,
97
+ claimedCompleteButUnverified: unverifiedComplete,
98
+ };
99
+ });
100
+ return {
101
+ core: input.core,
102
+ total: rows.length,
103
+ hasDocshots: input.coreSignals?.hasDocshots ?? false,
104
+ hasE2E: input.coreSignals?.hasE2E ?? false,
105
+ byStage,
106
+ claimsComplete: claimsCompleteCount,
107
+ claimedCompleteButUnverified,
108
+ withVerifiedRequirements,
109
+ verifiedUndeclaredTotal,
110
+ rows,
111
+ };
112
+ }
113
+ //# sourceMappingURL=scorecard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scorecard.js","sourceRoot":"","sources":["../../src/lib/scorecard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { type EmbeddingsSource } from './embeddings-source.js';
2
+ import { type PrecedentHit } from './precedent-search.js';
3
+ /** Async query embedder: returns a normalized vector, or null when unavailable. */
4
+ export type QueryEmbedder = (query: string) => Promise<Float32Array | null>;
5
+ /**
6
+ * Semantic precedent search over the committed embedding index. Returns ranked
7
+ * hits, or null when the index is absent OR the embedder can't produce a query
8
+ * vector — callers then fall back to the always-on lexical {@link searchSpecs}.
9
+ *
10
+ * Validated: qwen3-embedding:0.6b + the query instruction prefix lifts paraphrase
11
+ * recall to 7/8 (lexical 4/8) while holding exact@5 at 8/8. The lexical layer
12
+ * stays the zero-dependency baseline (CI / no-Ollama environments).
13
+ */
14
+ export declare function searchSpecsSemantic(topic: string, opts: {
15
+ repoRoot: string;
16
+ limit?: number;
17
+ embedQuery: QueryEmbedder;
18
+ includeArchived?: boolean;
19
+ /** Inject a non-default embedding backend (tests / future pgvector source). */
20
+ source?: EmbeddingsSource;
21
+ }): Promise<PrecedentHit[] | null>;
22
+ /**
23
+ * Best-available precedent search: semantic when the index + embedder resolve,
24
+ * else lexical. Single entry point for callers that want the strongest ranking
25
+ * without branching on availability themselves.
26
+ */
27
+ export declare function searchSpecsBest(topic: string, opts: {
28
+ repoRoot: string;
29
+ core?: string;
30
+ limit?: number;
31
+ embedQuery?: QueryEmbedder;
32
+ source?: EmbeddingsSource;
33
+ }): Promise<{
34
+ hits: PrecedentHit[];
35
+ mode: 'semantic' | 'lexical';
36
+ }>;
@@ -0,0 +1,56 @@
1
+ import { cosineRank } from './embedding-store.js';
2
+ import { embeddingsSourceFromConfig } from './embeddings-source.js';
3
+ import { searchSpecs } from './precedent-search.js';
4
+ /**
5
+ * Semantic precedent search over the committed embedding index. Returns ranked
6
+ * hits, or null when the index is absent OR the embedder can't produce a query
7
+ * vector — callers then fall back to the always-on lexical {@link searchSpecs}.
8
+ *
9
+ * Validated: qwen3-embedding:0.6b + the query instruction prefix lifts paraphrase
10
+ * recall to 7/8 (lexical 4/8) while holding exact@5 at 8/8. The lexical layer
11
+ * stays the zero-dependency baseline (CI / no-Ollama environments).
12
+ */
13
+ export async function searchSpecsSemantic(topic, opts) {
14
+ const index = (opts.source ?? embeddingsSourceFromConfig(opts.repoRoot)).loadIndex();
15
+ if (!index)
16
+ return null;
17
+ const qv = await opts.embedQuery(topic);
18
+ if (!qv)
19
+ return null;
20
+ const ranked = cosineRank(qv, index);
21
+ const hits = ranked.map((h) => ({
22
+ id: h.id,
23
+ title: h.title,
24
+ file: h.file,
25
+ core: h.id.split('-')[0]?.toLowerCase() ?? '',
26
+ status: 'unknown',
27
+ score: h.score,
28
+ matchedFields: ['semantic'],
29
+ }));
30
+ return typeof opts.limit === 'number' ? hits.slice(0, opts.limit) : hits;
31
+ }
32
+ /**
33
+ * Best-available precedent search: semantic when the index + embedder resolve,
34
+ * else lexical. Single entry point for callers that want the strongest ranking
35
+ * without branching on availability themselves.
36
+ */
37
+ export async function searchSpecsBest(topic, opts) {
38
+ if (opts.embedQuery) {
39
+ const semantic = await searchSpecsSemantic(topic, {
40
+ repoRoot: opts.repoRoot,
41
+ limit: opts.limit,
42
+ embedQuery: opts.embedQuery,
43
+ source: opts.source,
44
+ });
45
+ // Core filtering on semantic hits (id-prefix based) when a core is requested.
46
+ if (semantic) {
47
+ const filtered = opts.core ? semantic.filter((h) => h.core === opts.core.toLowerCase()) : semantic;
48
+ return { hits: filtered, mode: 'semantic' };
49
+ }
50
+ }
51
+ return {
52
+ hits: searchSpecs(topic, { repoRoot: opts.repoRoot, core: opts.core, limit: opts.limit }),
53
+ mode: 'lexical',
54
+ };
55
+ }
56
+ //# sourceMappingURL=semantic-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-search.js","sourceRoot":"","sources":["../../src/lib/semantic-search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAyB,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAC3F,OAAO,EAAqB,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAKvE;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAAa,EACb,IAOC;IAED,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,0BAA0B,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;IACrF,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACxC,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAErB,MAAM,MAAM,GAAkB,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACpD,MAAM,IAAI,GAAmB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9C,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE;QAC7C,MAAM,EAAE,SAAS;QACjB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,aAAa,EAAE,CAAC,UAAU,CAAC;KAC5B,CAAC,CAAC,CAAC;IACJ,OAAO,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3E,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAa,EACb,IAAgH;IAEhH,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE;YAChD,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CAAC;QACH,8EAA8E;QAC9E,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;YACpG,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,OAAO;QACL,IAAI,EAAE,WAAW,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QACzF,IAAI,EAAE,SAAS;KAChB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ export type SessionState = {
2
+ specId: string;
3
+ currentTask: string;
4
+ };
5
+ /** Parse STATE.md text into a session pointer, or null if no in-progress task. Never throws. */
6
+ export declare function parseSessionState(text: string): SessionState | null;
7
+ /** Read `.cursor/session/STATE.md` (gitignored, optional). Returns null if absent/unparseable. */
8
+ export declare function readSessionState(repoRoot: string): SessionState | null;
@@ -0,0 +1,25 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ const SPEC_RE = /\*\*Spec(?:\s*ID)?:\*\*\s*([A-Z]{2,3}-\d+(?:[A-Z0-9-]*)?)/i;
4
+ const TASK_RE = /\*\*Current task:\*\*\s*(T\d+)\b/i;
5
+ /** Parse STATE.md text into a session pointer, or null if no in-progress task. Never throws. */
6
+ export function parseSessionState(text) {
7
+ const spec = SPEC_RE.exec(text)?.[1];
8
+ const task = TASK_RE.exec(text)?.[1];
9
+ if (!spec || !task)
10
+ return null;
11
+ return { specId: spec.toUpperCase(), currentTask: task.toUpperCase() };
12
+ }
13
+ /** Read `.cursor/session/STATE.md` (gitignored, optional). Returns null if absent/unparseable. */
14
+ export function readSessionState(repoRoot) {
15
+ const path = join(repoRoot, '.cursor', 'session', 'STATE.md');
16
+ if (!existsSync(path))
17
+ return null;
18
+ try {
19
+ return parseSessionState(readFileSync(path, 'utf8'));
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ //# sourceMappingURL=session-state.js.map