@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,343 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { relative } from 'node:path';
3
+ import { getSpecLastModified } from '../lib/archive.js';
4
+ import { createdTables, findMigrationCollisions, migrationTables } from '../lib/migration-collision.js';
5
+ import { ErrorCode, emit, getGlobalOptions } from '../lib/output.js';
6
+ import { searchSpecs } from '../lib/precedent-search.js';
7
+ import { coresList, getRepoRoot, isKnownCore } from '../lib/repo.js';
8
+ import { findSpecsById, listSpecs } from '../lib/specs.js';
9
+ /** Ranked --topic matches shown by default; --all lifts the cap. */
10
+ const TOPIC_LIMIT = 15;
11
+ /** Document-frequency cap for --spec overlap matching. Tables referenced by more specs
12
+ * than this are ubiquitous infrastructure (pf_module_permissions, pf_organizations,
13
+ * hr_employees, …) and say nothing about WHICH prior art is relevant, so they are
14
+ * zero-weighted for overlap matching only (HR-41 measured 90% of its 251 matches came
15
+ * solely from 4 such tables). Absolute floor of 30 with a 2.5%-of-corpus scale-up —
16
+ * the measured max df is ~15.8% of the corpus, so a relative threshold like
17
+ * 30%-of-corpus would never fire. */
18
+ export function thresholdForCorpus(corpusSize) {
19
+ return Math.max(30, Math.ceil(0.025 * corpusSize));
20
+ }
21
+ /** Backtick-quoted {core}_* identifiers referenced in a spec body. */
22
+ export function extractCoreIdentifiers(body, core) {
23
+ const re = new RegExp(`\`(${core.toLowerCase()}_[a-z0-9_]+)\``, 'gi');
24
+ const out = new Set();
25
+ let m;
26
+ while ((m = re.exec(body)) !== null)
27
+ out.add(m[1].toLowerCase());
28
+ return [...out];
29
+ }
30
+ /** Backtick-quoted `{core}_*` identifiers for ANY known core prefix — a spec that
31
+ * touches another core's tables is exactly the precedent worth surfacing. */
32
+ export function extractKnownCoreIdentifiers(body, cores) {
33
+ if (cores.length === 0)
34
+ return [];
35
+ const re = new RegExp(`\`((?:${cores.map((c) => c.toLowerCase()).join('|')})_[a-z0-9_]+)\``, 'gi');
36
+ const out = new Set();
37
+ let m;
38
+ while ((m = re.exec(body)) !== null)
39
+ out.add(m[1].toLowerCase());
40
+ return [...out];
41
+ }
42
+ /** Error text for a --core value outside the canonical core list; null when valid.
43
+ * A silently-accepted bogus core returns 0 matches with exit 0 — lethal for a
44
+ * HALT-to-review gate, so the command exits 1 instead. */
45
+ export function unknownCoreError(core, repoRoot) {
46
+ if (isKnownCore(core, repoRoot))
47
+ return null;
48
+ return `precedent: unknown core "${core}" — valid cores: ${coresList(repoRoot).join(', ')}`;
49
+ }
50
+ export function registerPrecedent(program) {
51
+ program
52
+ .command('precedent')
53
+ .description('Search active + archived specs, registry, and deferred dashboard for prior art.')
54
+ .option('--spec <id>', 'Lookup precedent for one spec ID')
55
+ .option('--topic <text>', 'Lookup precedent for a topic keyword')
56
+ .option('--core <core>', 'Limit search to one core')
57
+ .option('--all', 'Show every ranked topic match (default: top 15)')
58
+ .option('--check-migrations', 'Check for migration table collisions on body identifiers (requires --spec)')
59
+ .action(async (options, cmd) => {
60
+ const global = getGlobalOptions(cmd);
61
+ const repoRoot = getRepoRoot(global.repoRoot);
62
+ const spec = options.spec ? String(options.spec).toUpperCase() : undefined;
63
+ const topic = options.topic ? String(options.topic).trim() : undefined;
64
+ const core = options.core ? String(options.core).toLowerCase() : undefined;
65
+ const all = Boolean(options.all);
66
+ const checkMigrations = Boolean(options.checkMigrations);
67
+ if (!spec && !topic) {
68
+ emit(global, {
69
+ ok: false,
70
+ error: { code: ErrorCode.INVALID_ARGUMENT, message: 'precedent: pass --spec <id> or --topic <text>' },
71
+ data: null,
72
+ text: 'precedent: pass --spec <id> or --topic <text>',
73
+ });
74
+ }
75
+ if (core) {
76
+ const coreError = unknownCoreError(core, repoRoot);
77
+ if (coreError) {
78
+ // F7: emit the machine code on the TOP-LEVEL `error` envelope (matching
79
+ // reconcile.ts) so an agent can switch on envelope.error.code. The
80
+ // candidate-core list stays in `data.valid` for suggestions.
81
+ emit(global, {
82
+ ok: false,
83
+ error: { code: ErrorCode.UNKNOWN_CORE, message: coreError },
84
+ data: { core, valid: coresList(repoRoot) },
85
+ text: coreError,
86
+ });
87
+ }
88
+ }
89
+ let report;
90
+ try {
91
+ report = buildPrecedentReport(repoRoot, { spec, topic, core, checkMigrations, all });
92
+ }
93
+ catch (err) {
94
+ emit(global, {
95
+ ok: false,
96
+ data: null,
97
+ text: `precedent: ${err instanceof Error ? err.message : String(err)}`,
98
+ });
99
+ }
100
+ const total = report.active.length + report.archived.length + report.registry.length + report.deferred.length;
101
+ const text = formatPrecedentText(report);
102
+ emit(global, {
103
+ ok: true,
104
+ data: report,
105
+ text: `${text}\n\nTotal matches: ${total}`,
106
+ });
107
+ });
108
+ }
109
+ export function buildPrecedentReport(repoRoot, query) {
110
+ const idUpper = query.spec?.toUpperCase();
111
+ const warnings = [];
112
+ let active = [];
113
+ let archived = [];
114
+ let targetSpec;
115
+ let targetBody = '';
116
+ let targetBodyIds = [];
117
+ if (idUpper) {
118
+ // --spec path: identifier-overlap analysis reads spec bodies, so it stays on listSpecs.
119
+ const specs = listSpecs(repoRoot, query.core, { includeArchived: true });
120
+ targetSpec = specs.find((s) => s.id.toUpperCase() === idUpper);
121
+ // Duplicate spec IDs are live in the corpus (e.g. PF-76/PF-77) — surface them
122
+ // instead of silently analyzing whichever file listSpecs happened to find first.
123
+ const duplicates = findSpecsById(repoRoot, idUpper, { includeArchived: true });
124
+ if (duplicates.length > 1) {
125
+ const paths = duplicates.map((d) => relative(repoRoot, d.path)).join(', ');
126
+ const used = targetSpec ? relative(repoRoot, targetSpec.path) : 'none';
127
+ warnings.push(`${duplicates.length} spec files share ID ${idUpper}: ${paths} — identifier analysis uses ${used}`);
128
+ }
129
+ const cores = coresList(repoRoot);
130
+ if (targetSpec && existsSync(targetSpec.path)) {
131
+ targetBody = readFileSync(targetSpec.path, 'utf8');
132
+ targetBodyIds = extractKnownCoreIdentifiers(targetBody, cores);
133
+ }
134
+ // De-noise stage 1: only identifiers naming REAL tables (a CREATE TABLE in
135
+ // supabase/migrations) count toward surfacing other specs. The inline scan
136
+ // measured ~85ms over the live corpus (91 files / 11MB). Identifiers for
137
+ // not-yet-migrated tables are excluded from overlap but still flow into
138
+ // bodyIdentifiers and the --check-migrations analysis.
139
+ const knownTables = migrationTables(repoRoot);
140
+ const targetTableIds = targetBodyIds.filter((t) => /^[a-z]+_[a-z0-9_]+$/.test(t) && knownTables.has(t));
141
+ // One pass over the corpus (each spec body read once): which target tables each
142
+ // spec shares, plus per-table document frequency (df = number of specs whose body
143
+ // references that table).
144
+ const sharedByPath = new Map();
145
+ const df = new Map();
146
+ if (targetTableIds.length > 0) {
147
+ for (const s of specs) {
148
+ if (!existsSync(s.path))
149
+ continue;
150
+ const sIds = extractKnownCoreIdentifiers(readFileSync(s.path, 'utf8'), cores);
151
+ const shared = targetTableIds.filter((t) => sIds.includes(t));
152
+ if (shared.length === 0)
153
+ continue;
154
+ sharedByPath.set(s.path, shared);
155
+ for (const t of shared)
156
+ df.set(t, (df.get(t) ?? 0) + 1);
157
+ }
158
+ }
159
+ // De-noise stage 2: zero-weight ubiquitous infrastructure tables (df above the
160
+ // corpus threshold) for OVERLAP matching only — bodyIdentifiers and
161
+ // --check-migrations keep the full identifier set.
162
+ const dfCap = thresholdForCorpus(specs.length);
163
+ const dfOf = (t) => df.get(t) ?? 0;
164
+ const toMatch = (s, sharedTables) => ({
165
+ id: s.id,
166
+ core: s.core,
167
+ title: s.title ?? '',
168
+ status: s.status ?? 'unknown',
169
+ path: relative(repoRoot, s.path),
170
+ archived: s.archived,
171
+ archivedAt: s.archived ? getSpecLastModified(repoRoot, s.path)?.toISOString() : undefined,
172
+ sharedTables,
173
+ });
174
+ // The queried spec itself always leads; other specs rank by Σ(1/df) of their
175
+ // distinctive shared tables, so the most-distinctive prior art sorts first.
176
+ const selves = specs.filter((s) => s.id.toUpperCase() === idUpper).map((s) => toMatch(s));
177
+ const ranked = specs
178
+ .filter((s) => s.id.toUpperCase() !== idUpper)
179
+ .map((s) => {
180
+ const shared = (sharedByPath.get(s.path) ?? []).filter((t) => dfOf(t) <= dfCap);
181
+ return { spec: s, shared, weight: shared.reduce((sum, t) => sum + 1 / dfOf(t), 0) };
182
+ })
183
+ .filter((r) => r.shared.length > 0)
184
+ .sort((a, b) => b.weight - a.weight || a.spec.id.localeCompare(b.spec.id))
185
+ .map((r) => toMatch(r.spec, [...r.shared].sort((a, b) => dfOf(a) - dfOf(b) || a.localeCompare(b))));
186
+ const matches = [...selves, ...ranked];
187
+ active = matches.filter((m) => !m.archived);
188
+ archived = matches.filter((m) => m.archived);
189
+ }
190
+ else if (query.topic) {
191
+ // --topic path (precedent v2): ranked, body-aware lexical search over specs/spec-index.json.
192
+ const limit = query.all ? Number.POSITIVE_INFINITY : TOPIC_LIMIT;
193
+ const hits = searchSpecs(query.topic, {
194
+ core: query.core,
195
+ repoRoot,
196
+ includeArchived: true,
197
+ limit: Number.POSITIVE_INFINITY,
198
+ });
199
+ const isArchivedHit = (h) => h.status === 'archived' || h.file.includes('/archive/');
200
+ const toRanked = (h, isArchived) => ({
201
+ id: h.id,
202
+ core: h.core,
203
+ title: h.title,
204
+ status: h.status,
205
+ path: h.file,
206
+ archived: isArchived,
207
+ score: h.score,
208
+ matchedFields: h.matchedFields,
209
+ });
210
+ active = hits
211
+ .filter((h) => !isArchivedHit(h))
212
+ .slice(0, limit)
213
+ .map((h) => toRanked(h, false));
214
+ archived = hits
215
+ .filter(isArchivedHit)
216
+ .slice(0, limit)
217
+ .map((h) => toRanked(h, true));
218
+ }
219
+ const registry = scanLines(`${repoRoot}/specs/SPEC_STATUS_REGISTRY.md`, query);
220
+ const deferred = scanLines(`${repoRoot}/specs/DEFERRED_DASHBOARD.md`, query);
221
+ const report = { query, warnings, active, archived, registry, deferred };
222
+ // Migration collision check (only meaningful with --spec)
223
+ if (query.checkMigrations && targetSpec) {
224
+ const created = createdTables(targetBody, targetBodyIds);
225
+ report.bodyIdentifiers = targetBodyIds;
226
+ report.migrationCollisions = findMigrationCollisions(repoRoot, created);
227
+ }
228
+ return report;
229
+ }
230
+ function scanLines(path, query) {
231
+ if (!existsSync(path))
232
+ return [];
233
+ const lines = readFileSync(path, 'utf8').split('\n');
234
+ const topicLower = query.topic?.toLowerCase();
235
+ const specUpper = query.spec?.toUpperCase();
236
+ const coreUpper = query.core?.toUpperCase();
237
+ const out = [];
238
+ for (const rawLine of lines) {
239
+ const line = rawLine.trim();
240
+ if (!line)
241
+ continue;
242
+ if (line.startsWith('#'))
243
+ continue;
244
+ if (specUpper && !line.toUpperCase().includes(specUpper))
245
+ continue;
246
+ if (topicLower && !line.toLowerCase().includes(topicLower))
247
+ continue;
248
+ const id = extractId(line);
249
+ if (coreUpper && id && !id.startsWith(`${coreUpper}-`))
250
+ continue;
251
+ if (coreUpper && !id && !line.toUpperCase().includes(`${coreUpper}-`))
252
+ continue;
253
+ out.push({ id, line });
254
+ }
255
+ return out.slice(0, 30);
256
+ }
257
+ function extractId(line) {
258
+ const match = /([A-Z]{2,3}-\d+(?:\.\d+)?(?:-(?:EN-\d+|E\d+|P\d+(?:\.\d+)?(?:-EN-\d+)?))?)/.exec(line);
259
+ return match?.[1];
260
+ }
261
+ function formatSpecLine(item, rank) {
262
+ if (item.score !== undefined) {
263
+ return ` ${rank}. ${item.id} (score ${item.score}) ${item.title} — ${item.path}`;
264
+ }
265
+ const shares = item.sharedTables && item.sharedTables.length > 0 ? ` — shares: ${item.sharedTables.join(', ')}` : '';
266
+ return ` - ${item.id} [${item.status}] ${item.title} (${item.path})${shares}`;
267
+ }
268
+ export function formatPrecedentText(report) {
269
+ const q = report.query.spec ? `spec=${report.query.spec}` : `topic="${report.query.topic ?? ''}"`;
270
+ const lines = [`precedent: ${q}${report.query.core ? ` core=${report.query.core}` : ''}`];
271
+ lines.push('');
272
+ if (report.warnings.length > 0) {
273
+ for (const w of report.warnings)
274
+ lines.push(`⚠ ${w}`);
275
+ lines.push('');
276
+ }
277
+ // --spec overlap lists are ranked but uncapped in the report data; cap the DISPLAY
278
+ // at TOPIC_LIMIT (--all lifts it) so the section header keeps the true count.
279
+ // --topic lists are already capped at build time, so the slice is a no-op there.
280
+ const displayLimit = report.query.spec && !report.query.all ? TOPIC_LIMIT : Number.POSITIVE_INFINITY;
281
+ const pushSpecSection = (label, items) => {
282
+ lines.push(`${label} (${items.length}):`);
283
+ if (items.length === 0) {
284
+ lines.push(' - none');
285
+ return;
286
+ }
287
+ items.slice(0, displayLimit).forEach((item, i) => {
288
+ lines.push(formatSpecLine(item, i + 1));
289
+ });
290
+ if (items.length > displayLimit) {
291
+ lines.push(` … ${items.length - displayLimit} more (use --all)`);
292
+ }
293
+ };
294
+ pushSpecSection('Active specs', report.active);
295
+ lines.push('');
296
+ pushSpecSection('Archived specs', report.archived);
297
+ lines.push('');
298
+ lines.push(`Registry hits (${report.registry.length}):`);
299
+ if (report.registry.length === 0) {
300
+ lines.push(' - none');
301
+ }
302
+ else {
303
+ for (const item of report.registry.slice(0, 8)) {
304
+ lines.push(` - ${item.line}`);
305
+ }
306
+ }
307
+ lines.push('');
308
+ lines.push(`Deferred hits (${report.deferred.length}):`);
309
+ if (report.deferred.length === 0) {
310
+ lines.push(' - none');
311
+ }
312
+ else {
313
+ for (const item of report.deferred.slice(0, 8)) {
314
+ lines.push(` - ${item.line}`);
315
+ }
316
+ }
317
+ // Migration collision block
318
+ if (report.bodyIdentifiers !== undefined) {
319
+ lines.push('');
320
+ lines.push(`Body identifiers (${report.bodyIdentifiers.length}):`);
321
+ if (report.bodyIdentifiers.length === 0) {
322
+ lines.push(' - none');
323
+ }
324
+ else {
325
+ for (const id of report.bodyIdentifiers) {
326
+ lines.push(` - ${id}`);
327
+ }
328
+ }
329
+ lines.push('');
330
+ const cols = report.migrationCollisions ?? [];
331
+ lines.push(`Migration collisions (${cols.length}):`);
332
+ if (cols.length === 0) {
333
+ lines.push(' - none');
334
+ }
335
+ else {
336
+ for (const c of cols) {
337
+ lines.push(` - ${c.table} → ${c.migration}`);
338
+ }
339
+ }
340
+ }
341
+ return lines.join('\n');
342
+ }
343
+ //# sourceMappingURL=precedent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"precedent.js","sourceRoot":"","sources":["../../src/commands/precedent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { Command } from 'commander';
2
+ import { type BlockerRef, type Readiness } from '../lib/blockers.js';
3
+ import { type SpecFile } from '../lib/specs.js';
4
+ /** In-progress = has a status that is not Complete and not terminal (deferred/archived/blocked/
5
+ * superseded). Shares the canonical predicate with `next`'s candidate filter. */
6
+ export declare function inProgress(s: SpecFile): boolean;
7
+ export type Proposal = {
8
+ specId: string;
9
+ /** Blocker ids extracted from the spec's dirty `dependencies`, with resolved state. */
10
+ candidateBlockers: BlockerRef[];
11
+ /** A `dependencies` block was present but yielded no extractable spec-id (prose only). */
12
+ hadProse: boolean;
13
+ };
14
+ /**
15
+ * Pure core of the migration proposal: given each in-progress spec's raw
16
+ * `dependencies` value and the full corpus, extract spec-id tokens, drop a
17
+ * self-reference, and resolve each to complete/open/unknown. No I/O.
18
+ */
19
+ export declare function buildProposals(inputs: {
20
+ id: string;
21
+ depsRaw: unknown;
22
+ }[], corpus: SpecFile[]): Proposal[];
23
+ /** Blocked-section lines for the default listing. Unknown blocker ids carry the
24
+ * same remediation hint `--propose` prints, instead of only a bare `(unknown)`. */
25
+ export declare function formatBlockedLines(blocked: Readiness[]): string[];
26
+ /** Drop specs that carry an active claim from another worktree, unless includeClaimed. */
27
+ export declare function excludeClaimed(repoRoot: string, specs: SpecFile[], includeClaimed: boolean): SpecFile[];
28
+ export declare function registerReady(program: Command): void;
@@ -0,0 +1,147 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { collectSpecIds, computeReadiness, isComplete, isTerminal, } from '../lib/blockers.js';
3
+ import { activeClaims, readClaims } from '../lib/claims.js';
4
+ import { readFrontmatter } from '../lib/frontmatter.js';
5
+ import { emit, getGlobalOptions } from '../lib/output.js';
6
+ import { getRepoRoot } from '../lib/repo.js';
7
+ import { listSpecs } from '../lib/specs.js';
8
+ import { withEventLog } from '../lib/with-event-log.js';
9
+ /** In-progress = has a status that is not Complete and not terminal (deferred/archived/blocked/
10
+ * superseded). Shares the canonical predicate with `next`'s candidate filter. */
11
+ export function inProgress(s) {
12
+ return Boolean(s.status) && !isComplete(s) && !isTerminal(s);
13
+ }
14
+ /**
15
+ * Pure core of the migration proposal: given each in-progress spec's raw
16
+ * `dependencies` value and the full corpus, extract spec-id tokens, drop a
17
+ * self-reference, and resolve each to complete/open/unknown. No I/O.
18
+ */
19
+ export function buildProposals(inputs, corpus) {
20
+ const byId = new Map(corpus.map((s) => [s.id.toUpperCase(), s]));
21
+ return inputs.map(({ id, depsRaw }) => {
22
+ const ids = collectSpecIds(depsRaw).filter((dep) => dep.toUpperCase() !== id.toUpperCase());
23
+ const candidateBlockers = ids.map((dep) => {
24
+ const target = byId.get(dep.toUpperCase());
25
+ return { id: dep, state: !target ? 'unknown' : isComplete(target) ? 'complete' : 'open' };
26
+ });
27
+ return { specId: id, candidateBlockers, hadProse: depsRaw != null && ids.length === 0 };
28
+ });
29
+ }
30
+ /**
31
+ * Read-only migration proposal: extract spec-id tokens from each in-progress
32
+ * spec's dirty `dependencies` frontmatter, resolve their status, and emit a
33
+ * per-spec proposal (plus a copy-pasteable `blocked_by:` block of the ids that
34
+ * resolve to real specs) for a human to promote. Never writes frontmatter.
35
+ */
36
+ function propose(repoRoot, core) {
37
+ const corpus = listSpecs(repoRoot);
38
+ const candidates = listSpecs(repoRoot, core).filter(inProgress);
39
+ const inputs = candidates.map((spec) => {
40
+ let depsRaw;
41
+ try {
42
+ depsRaw = readFrontmatter(readFileSync(spec.path, 'utf8')).dependencies;
43
+ }
44
+ catch {
45
+ depsRaw = undefined;
46
+ }
47
+ return { id: spec.id, depsRaw };
48
+ });
49
+ const proposals = buildProposals(inputs, corpus);
50
+ const withCandidates = proposals.filter((p) => p.candidateBlockers.length > 0);
51
+ const lines = [
52
+ `ready --propose: ${candidates.length} in-progress spec(s)${core ? ` in ${core}` : ''}; ${withCandidates.length} have extractable blocker ids.`,
53
+ 'Review each, then paste the suggested block into the spec frontmatter. Drop any that are "builds-on" (not hard blockers); entries already marked (complete) are cleared and can be omitted:',
54
+ '',
55
+ ];
56
+ for (const p of withCandidates) {
57
+ const resolved = p.candidateBlockers.filter((b) => b.state !== 'unknown');
58
+ const unknown = p.candidateBlockers.filter((b) => b.state === 'unknown');
59
+ lines.push(`▌ ${p.specId}`);
60
+ for (const b of p.candidateBlockers)
61
+ lines.push(` - ${b.id} (${b.state})`);
62
+ if (unknown.length > 0) {
63
+ lines.push(` ⚠ unresolved id(s) — fix the reference or drop: ${unknown.map((b) => b.id).join(', ')}`);
64
+ }
65
+ if (resolved.length > 0) {
66
+ lines.push(' suggested frontmatter:');
67
+ lines.push(' blocked_by:');
68
+ for (const b of resolved)
69
+ lines.push(` - ${b.id}`);
70
+ }
71
+ lines.push('');
72
+ }
73
+ const proseOnly = proposals.filter((p) => p.hadProse).map((p) => p.specId);
74
+ if (proseOnly.length > 0) {
75
+ lines.push(`(${proseOnly.length} spec(s) had a dependencies block with no extractable spec-id — prose only.)`);
76
+ }
77
+ return { text: lines.join('\n').trimEnd(), data: { proposals } };
78
+ }
79
+ /** Blocked-section lines for the default listing. Unknown blocker ids carry the
80
+ * same remediation hint `--propose` prints, instead of only a bare `(unknown)`. */
81
+ export function formatBlockedLines(blocked) {
82
+ const lines = ['⛔ blocked:'];
83
+ for (const r of blocked) {
84
+ const open = r.blockers
85
+ .filter((b) => b.state !== 'complete')
86
+ .map((b) => `${b.id} (${b.state})`)
87
+ .join(', ');
88
+ lines.push(` ${r.spec.id} ← ${open}`);
89
+ const unknown = r.blockers.filter((b) => b.state === 'unknown');
90
+ if (unknown.length > 0) {
91
+ lines.push(` ⚠ unresolved id(s) — fix the reference or drop: ${unknown.map((b) => b.id).join(', ')}`);
92
+ }
93
+ }
94
+ return lines;
95
+ }
96
+ /** Drop specs that carry an active claim from another worktree, unless includeClaimed. */
97
+ export function excludeClaimed(repoRoot, specs, includeClaimed) {
98
+ if (includeClaimed)
99
+ return specs;
100
+ const claimed = new Set(activeClaims(readClaims(repoRoot), Date.now()).map((c) => c.id.toUpperCase()));
101
+ return specs.filter((s) => !claimed.has(s.id.toUpperCase()));
102
+ }
103
+ export function registerReady(program) {
104
+ program
105
+ .command('ready')
106
+ .description('List in-progress specs whose blockers are all Complete (the bd-ready query); --propose seeds blocked_by from legacy dependencies.')
107
+ .option('--core <core>', 'Limit to one core')
108
+ .option('--propose', 'Read-only: propose blocked_by entries extracted from each spec’s dirty dependencies field', false)
109
+ .option('--include-claimed', 'Include specs already claimed by another worktree', false)
110
+ .action(withEventLog('ready', async (options, cmd) => {
111
+ const global = getGlobalOptions(cmd);
112
+ const repoRoot = getRepoRoot(global.repoRoot);
113
+ if (options.propose) {
114
+ const { text, data } = propose(repoRoot, options.core);
115
+ emit(global, { ok: true, data, text });
116
+ }
117
+ const corpus = listSpecs(repoRoot);
118
+ const candidates = excludeClaimed(repoRoot, listSpecs(repoRoot, options.core).filter(inProgress), Boolean(options.includeClaimed));
119
+ const readiness = computeReadiness(candidates, corpus);
120
+ const ready = readiness.filter((r) => r.ready);
121
+ const blocked = readiness.filter((r) => !r.ready);
122
+ const lines = [
123
+ `ready${options.core ? ` [${options.core}]` : ''}: ${ready.length} unblocked, ${blocked.length} blocked (of ${candidates.length} in-progress).`,
124
+ '',
125
+ ];
126
+ if (ready.length > 0) {
127
+ lines.push('✅ ready:');
128
+ for (const r of ready)
129
+ lines.push(` ${r.spec.id}`);
130
+ lines.push('');
131
+ }
132
+ if (blocked.length > 0) {
133
+ lines.push(...formatBlockedLines(blocked));
134
+ }
135
+ emit(global, {
136
+ ok: true,
137
+ data: {
138
+ core: options.core ?? null,
139
+ ready: ready.map((r) => r.spec.id),
140
+ blocked: blocked.map((r) => ({ specId: r.spec.id, blockers: r.blockers })),
141
+ totalInProgress: candidates.length,
142
+ },
143
+ text: lines.join('\n').trimEnd(),
144
+ });
145
+ }));
146
+ }
147
+ //# sourceMappingURL=ready.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ready.js","sourceRoot":"","sources":["../../src/commands/ready.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { Command } from 'commander';
2
+ export type ReconcileDirection = 'ok' | 'lagging' | 'overstated';
3
+ /** Classify frontmatter status vs the artifact-derived stage. */
4
+ export declare function classify(written: string | undefined, computed: string | undefined): ReconcileDirection;
5
+ export type ReconcileRow = {
6
+ id: string;
7
+ core: string;
8
+ written?: string;
9
+ computed?: string;
10
+ direction: ReconcileDirection;
11
+ };
12
+ /** Human-output scope label: `--spec` wins over `--core`; `--changed` → 'changed'; default `all`. */
13
+ export declare function reconcileScopeLabel(opts: {
14
+ spec?: string;
15
+ core?: string;
16
+ changed?: boolean;
17
+ }): string;
18
+ /** Human-readable report: header + BOTH divergence directions, each with ids. */
19
+ export declare function formatReconcileText(args: {
20
+ scope: string;
21
+ total: number;
22
+ rows: ReconcileRow[];
23
+ write: boolean;
24
+ }): string;
25
+ /**
26
+ * Map a list of changed file paths to the spec IDs they belong to. Counts
27
+ * canonical spec markdown under `specs/<core>/specs/…` AND wizard/UX specs under
28
+ * `specs/<core>/ux/…` (ignores research/reviews/reports and non-spec files).
29
+ * Ids are extracted with the same parser `listSpecs` uses (`extractSpecId`) so
30
+ * the `--changed` filter matches corpus identity exactly — including qualifier
31
+ * ids like `HR-UX-14`. Upper-cased, de-duplicated, input order preserved.
32
+ * Used by `reconcile --changed` to scope the write to branch-touched specs.
33
+ */
34
+ export declare function specIdsFromChangedPaths(paths: string[]): string[];
35
+ /** Parse `git status --porcelain` output into pathnames (rename rows yield the new path). */
36
+ export declare function pathsFromPorcelain(out: string): string[];
37
+ /**
38
+ * Files changed on this branch vs `base` (three-dot; falling back to the diff
39
+ * vs HEAD when the base ref is unavailable), UNIONED with uncommitted
40
+ * working-tree changes from `git status --porcelain` — so post-signoff spec
41
+ * edits that haven't been committed yet are still in scope for `--changed`.
42
+ */
43
+ export declare function gitChangedPaths(repoRoot: string, base: string): string[];
44
+ export declare function registerReconcile(program: Command): void;