wicked-crew 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,57 @@
1
+ export interface RequirementOverride {
2
+ title?: string | undefined;
3
+ notes?: string | undefined;
4
+ status?: string | undefined;
5
+ risk?: boolean | undefined;
6
+ }
7
+ export type RequirementCategory = 'functional' | 'config-data';
8
+ /** Product-functional requirements come from CODE; statements extracted from
9
+ * lockfiles, manifests, data fixtures, and docs are honest observations about
10
+ * those assets but are NOT product behavior — they class as config-data so the
11
+ * default view can focus on the product. */
12
+ export declare function categoryOf(file: string): RequirementCategory;
13
+ export interface RequirementSummary {
14
+ key: string;
15
+ domain: string;
16
+ reqId: string;
17
+ title: string;
18
+ category: RequirementCategory;
19
+ /** First business-rule statement — the requirement's actual content (empty when none). */
20
+ statement: string;
21
+ status: string;
22
+ risk: boolean;
23
+ riskSource: 'operator' | 'data' | null;
24
+ edited: boolean;
25
+ }
26
+ export interface RequirementDetail extends RequirementSummary {
27
+ description: string;
28
+ notes: string;
29
+ sourceTitle: string;
30
+ ruleCount: number;
31
+ componentCount: number;
32
+ validationCount: number;
33
+ errorPathCount: number;
34
+ businessRules: unknown[];
35
+ legacyComponents: unknown[];
36
+ }
37
+ export interface RequirementsQuery {
38
+ q?: string | undefined;
39
+ risk?: 'risk' | 'no-risk' | undefined;
40
+ domain?: string | undefined;
41
+ category?: RequirementCategory | undefined;
42
+ offset: number;
43
+ limit: number;
44
+ }
45
+ export interface RequirementsPage {
46
+ total: number;
47
+ corpus: number;
48
+ offset: number;
49
+ limit: number;
50
+ items: RequirementSummary[];
51
+ }
52
+ /** Tokenized AND-match: every whitespace-separated term must appear in the haystack. */
53
+ export declare function listRequirements(rootPath: string, query: RequirementsQuery): Promise<RequirementsPage | null>;
54
+ export declare function getRequirement(rootPath: string, key: string): Promise<RequirementDetail | null>;
55
+ /** Merge a patch into the overrides sidecar (atomic write) and return the fresh detail. */
56
+ export declare function patchRequirement(rootPath: string, key: string, patch: RequirementOverride): Promise<RequirementDetail | null>;
57
+ //# sourceMappingURL=requirements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requirements.d.ts","sourceRoot":"","sources":["../../src/api/requirements.ts"],"names":[],"mappings":"AA4CA,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,MAAM,mBAAmB,GAAG,YAAY,GAAG,aAAa,CAAC;AAE/D;;;4CAG4C;AAC5C,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,CAM5D;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,0FAA0F;IAC1F,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;IACvC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,OAAO,EAAE,CAAC;IACzB,gBAAgB,EAAE,OAAO,EAAE,CAAC;CAC7B;AA0QD,MAAM,WAAW,iBAAiB;IAChC,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,kBAAkB,EAAE,CAAC;CAC7B;AAED,wFAAwF;AACxF,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAwBlC;AAED,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAmBnC;AAED,2FAA2F;AAC3F,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAmBnC"}
@@ -0,0 +1,331 @@
1
+ /**
2
+ * REQUIREMENTS SERVICE — server-side search + operator overrides over the LIVE
3
+ * estate store, with the `requirements_graph.json` artifact as fallback.
4
+ *
5
+ * Why the store is primary: the artifact is an evidence-gated SNAPSHOT — it only
6
+ * regenerates when `wicked-core domain-graph` passes its coverage bar, so mid-extraction
7
+ * it can lag the store by hours (observed: a UI serving day-old placeholder titles while
8
+ * the store held thousands of validated statements). Requirements content lives in
9
+ * estate (`nodes.requirement` + RULE-* annotations); the UI reads that truth directly
10
+ * (read-only `node:sqlite`) and falls back to the artifact for repos without a store
11
+ * or on runtimes without the sqlite builtin.
12
+ *
13
+ * Why server-side: 15k+ requirements — shipping them to the browser for JS-side
14
+ * filtering is not search. The daemon builds a flat index (cached, invalidated on
15
+ * store/artifact mtime with a small TTL guard against WAL-churn thrash), and queries
16
+ * run here: tokenized AND-match over id/domain/title/statements, risk + domain
17
+ * filters, offset/limit pagination.
18
+ *
19
+ * Why an OVERRIDES sidecar: the artifact is DERIVED (regenerated from the estate
20
+ * store), so operator edits written into it would be clobbered on the next
21
+ * `domain-graph` run. Edits live in `requirements_overrides.json` beside the artifact,
22
+ * keyed `domain::reqId`, and are merged at read time — the overlay survives
23
+ * regeneration and keeps provenance honest (`riskSource: operator` vs `data`).
24
+ */
25
+ import { readFile, writeFile, rename, mkdir, stat } from 'node:fs/promises';
26
+ import { join, dirname } from 'node:path';
27
+ /** Product-functional requirements come from CODE; statements extracted from
28
+ * lockfiles, manifests, data fixtures, and docs are honest observations about
29
+ * those assets but are NOT product behavior — they class as config-data so the
30
+ * default view can focus on the product. */
31
+ export function categoryOf(file) {
32
+ const f = file.toLowerCase();
33
+ const base = f.slice(f.lastIndexOf('/') + 1);
34
+ if (/\.(json|ya?ml|lock|toml|ini|env|md|mdx|txt|csv)$/.test(base))
35
+ return 'config-data';
36
+ if (base.includes('pnpm-lock') || base.includes('package-lock'))
37
+ return 'config-data';
38
+ return 'functional';
39
+ }
40
+ const RISK_RE = /risk/i;
41
+ const RISK_PREFIX = '[RISK]';
42
+ /** WAL churn during extraction touches the db every few seconds — don't rebuild a
43
+ * 15k-row index per keystroke; a fresh-enough index is authoritative for this long. */
44
+ const REBUILD_TTL_MS = 5_000;
45
+ function storePath(rootPath) {
46
+ return join(rootPath, '.codegraph', 'estate.db');
47
+ }
48
+ function artifactPath(rootPath) {
49
+ return join(rootPath, '.wicked-estate', 'requirements', 'requirements_graph.json');
50
+ }
51
+ function overridesPath(rootPath) {
52
+ return join(rootPath, '.wicked-estate', 'requirements', 'requirements_overrides.json');
53
+ }
54
+ async function mtimeMs(path) {
55
+ try {
56
+ return (await stat(path)).mtimeMs;
57
+ }
58
+ catch {
59
+ return -1;
60
+ }
61
+ }
62
+ async function readOverrides(rootPath) {
63
+ try {
64
+ const raw = await readFile(overridesPath(rootPath), 'utf8');
65
+ const parsed = JSON.parse(raw);
66
+ return typeof parsed === 'object' && parsed !== null
67
+ ? parsed
68
+ : {};
69
+ }
70
+ catch {
71
+ return {};
72
+ }
73
+ }
74
+ const cache = new Map();
75
+ let tmpSeq = 0;
76
+ /** Lazily resolved sqlite builtin — absent on older Node runtimes → artifact fallback. */
77
+ let sqliteMod;
78
+ async function sqlite() {
79
+ if (sqliteMod === undefined) {
80
+ try {
81
+ const name = 'node:sqlite';
82
+ sqliteMod = (await import(name));
83
+ }
84
+ catch {
85
+ sqliteMod = null;
86
+ }
87
+ }
88
+ return sqliteMod;
89
+ }
90
+ /** Build the index from the LIVE estate store (read-only). Returns null when the
91
+ * store can't serve (no db, no sqlite builtin, schema mismatch) — caller falls back. */
92
+ async function buildStoreIndex(rootPath, sourceMtime, ovMtime) {
93
+ const mod = await sqlite();
94
+ if (mod === null)
95
+ return null;
96
+ const overrides = await readOverrides(rootPath);
97
+ let db = null;
98
+ const entries = [];
99
+ const byKey = new Map();
100
+ try {
101
+ db = new mod.DatabaseSync(storePath(rootPath), { readOnly: true });
102
+ const rules = new Map();
103
+ for (const r of db
104
+ .prepare("SELECT node_sym, value, confidence FROM annotations WHERE key LIKE 'RULE-%'")
105
+ .all()) {
106
+ const list = rules.get(r.node_sym) ?? [];
107
+ list.push({ statement: r.value ?? '', confidence: r.confidence });
108
+ rules.set(r.node_sym, list);
109
+ }
110
+ const rows = db
111
+ .prepare(`SELECT n.symbol AS sid, s.sym AS sym, n.name, n.file, n.requirement,
112
+ n.requirement_validated AS validated
113
+ FROM nodes n JOIN symbols s ON s.sid = n.symbol
114
+ WHERE n.requirement IS NOT NULL AND n.requirement != ''`)
115
+ .all();
116
+ for (const row of rows) {
117
+ const key = row.sym;
118
+ const ov = overrides[key];
119
+ const file = row.file ?? '';
120
+ const slash = file.lastIndexOf('/');
121
+ const domain = slash > 0 ? file.slice(0, slash) : '(root)';
122
+ const dataRisk = row.requirement.startsWith(RISK_PREFIX);
123
+ const risk = ov?.risk !== undefined ? ov.risk : dataRisk;
124
+ const riskSource = ov?.risk !== undefined ? 'operator' : dataRisk ? 'data' : null;
125
+ const title = ov?.title ?? (row.name !== null && row.name !== '' ? row.name : key);
126
+ const nodeRules = rules.get(row.sid) ?? [];
127
+ const summary = {
128
+ key,
129
+ domain,
130
+ reqId: key,
131
+ title,
132
+ category: categoryOf(file),
133
+ statement: row.requirement,
134
+ status: ov?.status ?? (row.validated === 1 ? 'validated' : 'active'),
135
+ risk,
136
+ riskSource,
137
+ edited: ov !== undefined,
138
+ };
139
+ const source = {
140
+ title,
141
+ description: file,
142
+ status: summary.status,
143
+ legacy_components: file === '' ? [] : [file],
144
+ business_rules: nodeRules,
145
+ };
146
+ const haystack = `${key} ${domain} ${title} ${row.requirement} ${nodeRules.map((r) => r.statement).join(' ')} ${ov?.notes ?? ''}`.toLowerCase();
147
+ const entry = { summary, haystack, source };
148
+ entries.push(entry);
149
+ byKey.set(key, entry);
150
+ }
151
+ }
152
+ catch {
153
+ return null; // schema drift or unreadable store — artifact fallback
154
+ }
155
+ finally {
156
+ db?.close();
157
+ }
158
+ return {
159
+ entries,
160
+ byKey,
161
+ sourceMtimeMs: sourceMtime,
162
+ fromStore: true,
163
+ overridesMtimeMs: ovMtime,
164
+ builtAtMs: Date.now(),
165
+ total: entries.length,
166
+ };
167
+ }
168
+ async function buildIndex(rootPath) {
169
+ const [dbMtime, walMtime, artMtime, ovMtime] = await Promise.all([
170
+ mtimeMs(storePath(rootPath)),
171
+ mtimeMs(`${storePath(rootPath)}-wal`),
172
+ mtimeMs(artifactPath(rootPath)),
173
+ mtimeMs(overridesPath(rootPath)),
174
+ ]);
175
+ const storeMtime = Math.max(dbMtime, walMtime);
176
+ const cached = cache.get(rootPath);
177
+ // TTL applies ONLY to store-built indexes: extraction WAL churn changes the db
178
+ // mtime every few seconds and must not trigger a rebuild per request. Artifact
179
+ // mtime changes only on regen, so artifact-built indexes always mtime-check.
180
+ if (cached?.fromStore === true && Date.now() - cached.builtAtMs < REBUILD_TTL_MS)
181
+ return cached;
182
+ if (storeMtime >= 0) {
183
+ if (cached && cached.sourceMtimeMs === storeMtime && cached.overridesMtimeMs === ovMtime) {
184
+ cached.builtAtMs = Date.now();
185
+ return cached;
186
+ }
187
+ const fromStore = await buildStoreIndex(rootPath, storeMtime, ovMtime);
188
+ if (fromStore !== null) {
189
+ cache.set(rootPath, fromStore);
190
+ return fromStore;
191
+ }
192
+ }
193
+ if (artMtime < 0)
194
+ return null; // no store, no artifact — requirements not generated yet
195
+ if (cached && cached.sourceMtimeMs === artMtime && cached.overridesMtimeMs === ovMtime) {
196
+ return cached;
197
+ }
198
+ const raw = await readFile(artifactPath(rootPath), 'utf8');
199
+ const graph = JSON.parse(raw);
200
+ const overrides = await readOverrides(rootPath);
201
+ const entries = [];
202
+ const byKey = new Map();
203
+ for (const [domain, dom] of Object.entries(graph.domains ?? {})) {
204
+ for (const [reqId, req] of Object.entries(dom.requirements ?? {})) {
205
+ const key = `${domain}::${reqId}`;
206
+ const ov = overrides[key];
207
+ // Data-derived risk: any business rule whose serialized form names risk (the
208
+ // extraction harness's RESOLVED-or-RISK floor surfaces here at current fidelity).
209
+ const dataRisk = (req.business_rules ?? []).some((r) => RISK_RE.test(JSON.stringify(r)));
210
+ const risk = ov?.risk !== undefined ? ov.risk : dataRisk;
211
+ const riskSource = ov?.risk !== undefined ? 'operator' : dataRisk ? 'data' : null;
212
+ const title = ov?.title ?? req.title ?? reqId;
213
+ const statements = (req.business_rules ?? [])
214
+ .map((r) => {
215
+ const st = r.statement;
216
+ return typeof st === 'string' ? st.trim() : '';
217
+ })
218
+ .filter((st) => st !== '');
219
+ const firstComponent = (req.legacy_components ?? []).find((c) => typeof c === 'string');
220
+ const summary = {
221
+ key,
222
+ domain,
223
+ reqId,
224
+ title,
225
+ category: categoryOf(firstComponent ?? ''),
226
+ statement: statements[0] ?? '',
227
+ status: ov?.status ?? req.status ?? 'active',
228
+ risk,
229
+ riskSource,
230
+ edited: ov !== undefined,
231
+ };
232
+ // Statements are part of the haystack: searching the requirements means
233
+ // searching the actual rule text, not just titles and ids.
234
+ const haystack = `${reqId} ${domain} ${title} ${req.description ?? ''} ${statements.join(' ')} ${ov?.notes ?? ''}`.toLowerCase();
235
+ const entry = { summary, haystack, source: req };
236
+ entries.push(entry);
237
+ byKey.set(key, entry);
238
+ }
239
+ }
240
+ const index = {
241
+ entries,
242
+ byKey,
243
+ sourceMtimeMs: artMtime,
244
+ fromStore: false,
245
+ overridesMtimeMs: ovMtime,
246
+ builtAtMs: Date.now(),
247
+ total: entries.length,
248
+ };
249
+ cache.set(rootPath, index);
250
+ return index;
251
+ }
252
+ /** Tokenized AND-match: every whitespace-separated term must appear in the haystack. */
253
+ export async function listRequirements(rootPath, query) {
254
+ const index = await buildIndex(rootPath);
255
+ if (index === null)
256
+ return null;
257
+ const terms = (query.q ?? '')
258
+ .toLowerCase()
259
+ .split(/\s+/)
260
+ .filter((t) => t.length > 0);
261
+ const domainFilter = query.domain?.toLowerCase();
262
+ const matched = [];
263
+ for (const e of index.entries) {
264
+ if (query.category !== undefined && e.summary.category !== query.category)
265
+ continue;
266
+ if (query.risk === 'risk' && !e.summary.risk)
267
+ continue;
268
+ if (query.risk === 'no-risk' && e.summary.risk)
269
+ continue;
270
+ if (domainFilter !== undefined && !e.summary.domain.toLowerCase().includes(domainFilter))
271
+ continue;
272
+ if (terms.length > 0 && !terms.every((t) => e.haystack.includes(t)))
273
+ continue;
274
+ matched.push(e.summary);
275
+ }
276
+ return {
277
+ total: matched.length,
278
+ corpus: index.total,
279
+ offset: query.offset,
280
+ limit: query.limit,
281
+ items: matched.slice(query.offset, query.offset + query.limit),
282
+ };
283
+ }
284
+ export async function getRequirement(rootPath, key) {
285
+ const index = await buildIndex(rootPath);
286
+ const entry = index?.byKey.get(key);
287
+ if (index === null || entry === undefined)
288
+ return null;
289
+ const overrides = await readOverrides(rootPath);
290
+ const ov = overrides[key];
291
+ const src = entry.source;
292
+ return {
293
+ ...entry.summary,
294
+ description: src.description ?? '',
295
+ notes: ov?.notes ?? '',
296
+ sourceTitle: src.title ?? entry.summary.reqId,
297
+ ruleCount: (src.business_rules ?? []).length,
298
+ componentCount: (src.legacy_components ?? []).length,
299
+ validationCount: (src.validations ?? []).length,
300
+ errorPathCount: (src.error_paths ?? []).length,
301
+ businessRules: (src.business_rules ?? []).slice(0, 10),
302
+ legacyComponents: (src.legacy_components ?? []).slice(0, 10),
303
+ };
304
+ }
305
+ /** Merge a patch into the overrides sidecar (atomic write) and return the fresh detail. */
306
+ export async function patchRequirement(rootPath, key, patch) {
307
+ const index = await buildIndex(rootPath);
308
+ if (index === null || !index.byKey.has(key))
309
+ return null;
310
+ const overrides = await readOverrides(rootPath);
311
+ const next = { ...overrides[key] };
312
+ if (patch.title !== undefined)
313
+ next.title = patch.title;
314
+ if (patch.notes !== undefined)
315
+ next.notes = patch.notes;
316
+ if (patch.status !== undefined)
317
+ next.status = patch.status;
318
+ if (patch.risk !== undefined)
319
+ next.risk = patch.risk;
320
+ overrides[key] = next;
321
+ const path = overridesPath(rootPath);
322
+ await mkdir(dirname(path), { recursive: true });
323
+ // Collision-proof temp name: pid alone can collide for concurrent in-process
324
+ // patches; a monotonic per-process counter disambiguates.
325
+ const tmp = `${path}.tmp-${process.pid}-${++tmpSeq}`;
326
+ await writeFile(tmp, JSON.stringify(overrides, null, 2), 'utf8');
327
+ await rename(tmp, path);
328
+ cache.delete(rootPath); // next read rebuilds with the new overrides
329
+ return getRequirement(rootPath, key);
330
+ }
331
+ //# sourceMappingURL=requirements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requirements.js","sourceRoot":"","sources":["../../src/api/requirements.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAS/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAwGjD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,CAgjBrG"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAU/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAwGjD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,CA6pBrG"}
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { listRequirements, getRequirement, patchRequirement } from './requirements.js';
2
3
  import { randomUUID } from 'node:crypto';
3
4
  import { readFileSync, existsSync } from 'node:fs';
4
5
  import { promises as fsp } from 'node:fs';
@@ -458,6 +459,80 @@ export function registerRoutes(app, adapter, gateCache) {
458
459
  // ── Repo code graph (via wicked-estate graph-view) ──────────────────────────
459
460
  // Delegates to the estate CLI so the query goes through the proper service
460
461
  // layer (store-seam aware, overlay edges included). Postgres-safe.
462
+ // ── Requirements management (server-side search over requirements_graph.json +
463
+ // operator overrides sidecar; see api/requirements.ts) ─────────────────────
464
+ const ReqQuerySchema = z.object({
465
+ q: z.string().optional(),
466
+ risk: z.enum(['risk', 'no-risk']).optional(),
467
+ category: z.enum(['functional', 'config-data']).optional(),
468
+ domain: z.string().optional(),
469
+ offset: z.coerce.number().int().min(0).default(0),
470
+ limit: z.coerce.number().int().min(1).max(200).default(50),
471
+ });
472
+ app.get(`${V}/repos/:id/requirements`, async (req, reply) => {
473
+ const { id } = req.params;
474
+ const repos = await adapter.listRepos();
475
+ const repo = repos.find((r) => r.id === id);
476
+ if (!repo)
477
+ return reply.code(404).send({ error: `Repo ${id} not found` });
478
+ const parsed = ReqQuerySchema.safeParse(req.query);
479
+ if (!parsed.success) {
480
+ return reply.code(400).send({ error: 'Invalid query', details: parsed.error.issues });
481
+ }
482
+ const page = await listRequirements(repo.root_path, parsed.data);
483
+ if (page === null) {
484
+ return reply.code(404).send({ error: 'requirements_graph.json not generated for this repo yet' });
485
+ }
486
+ return page;
487
+ });
488
+ app.get(`${V}/repos/:id/requirements/:key`, async (req, reply) => {
489
+ const { id, key } = req.params;
490
+ const repos = await adapter.listRepos();
491
+ const repo = repos.find((r) => r.id === id);
492
+ if (!repo)
493
+ return reply.code(404).send({ error: `Repo ${id} not found` });
494
+ let decoded;
495
+ try {
496
+ decoded = decodeURIComponent(key);
497
+ }
498
+ catch {
499
+ return reply.code(400).send({ error: 'Malformed requirement key encoding' });
500
+ }
501
+ const detail = await getRequirement(repo.root_path, decoded);
502
+ if (detail === null)
503
+ return reply.code(404).send({ error: 'Requirement not found' });
504
+ return { requirement: detail };
505
+ });
506
+ const ReqPatchSchema = z
507
+ .object({
508
+ title: z.string().min(1).max(500).optional(),
509
+ notes: z.string().max(5000).optional(),
510
+ status: z.enum(['active', 'deprecated', 'review']).optional(),
511
+ risk: z.boolean().optional(),
512
+ })
513
+ .refine((b) => Object.keys(b).length > 0, { message: 'empty patch' });
514
+ app.patch(`${V}/repos/:id/requirements/:key`, async (req, reply) => {
515
+ const { id, key } = req.params;
516
+ const repos = await adapter.listRepos();
517
+ const repo = repos.find((r) => r.id === id);
518
+ if (!repo)
519
+ return reply.code(404).send({ error: `Repo ${id} not found` });
520
+ const parsed = ReqPatchSchema.safeParse(req.body);
521
+ if (!parsed.success) {
522
+ return reply.code(400).send({ error: 'Invalid patch', details: parsed.error.issues });
523
+ }
524
+ let decodedKey;
525
+ try {
526
+ decodedKey = decodeURIComponent(key);
527
+ }
528
+ catch {
529
+ return reply.code(400).send({ error: 'Malformed requirement key encoding' });
530
+ }
531
+ const detail = await patchRequirement(repo.root_path, decodedKey, parsed.data);
532
+ if (detail === null)
533
+ return reply.code(404).send({ error: 'Requirement not found' });
534
+ return { requirement: detail };
535
+ });
461
536
  app.get(`${V}/repos/:id/graph`, async (req, reply) => {
462
537
  const { id } = req.params;
463
538
  const repos = await adapter.listRepos();
@@ -470,8 +545,21 @@ export function registerRoutes(app, adapter, gateCache) {
470
545
  }
471
546
  try {
472
547
  const settings = await adapter.getSettings();
473
- const nodeLimit = String(settings.graphNodeLimit);
474
- const { stdout } = await execFileAsync('wicked-estate', ['graph-view', '--limit', nodeLimit, '--db', dbPath], { timeout: 30_000, cwd: repo.root_path });
548
+ const q = req.query;
549
+ const parsedLimit = q.limit !== undefined ? Number.parseInt(q.limit, 10) : NaN;
550
+ const nodeLimit = String(Number.isFinite(parsedLimit) && parsedLimit > 0 && parsedLimit <= 1000
551
+ ? parsedLimit
552
+ : settings.graphNodeLimit);
553
+ const args = ['graph-view', '--limit', nodeLimit, '--db', dbPath];
554
+ // FOCUS (ego-graph) mode: seed the slice from one symbol and expand its
555
+ // neighbourhood — the navigation primitive (estate graph-view --focus).
556
+ if (q.focus !== undefined && q.focus.trim() !== '') {
557
+ args.push('--focus', q.focus.trim());
558
+ }
559
+ const { stdout } = await execFileAsync('wicked-estate', args, {
560
+ timeout: 30_000,
561
+ cwd: repo.root_path,
562
+ });
475
563
  const raw = JSON.parse(stdout);
476
564
  const fileCount = new Set(raw.nodes.map((n) => n.file)).size;
477
565
  return reply.send({
@@ -487,6 +575,31 @@ export function registerRoutes(app, adapter, gateCache) {
487
575
  }
488
576
  });
489
577
  // ── Git history (last 20 commits via git log) ─────────────────────────────
578
+ // ── Blast radius for a symbol (via wicked-estate blast-radius --json).
579
+ // Carries the honesty contract through: dependents PLUS the unresolved-call
580
+ // count — an empty dependents list must never read as "safe to change".
581
+ app.get(`${V}/repos/:id/graph/blast-radius`, async (req, reply) => {
582
+ const { id } = req.params;
583
+ const q = req.query;
584
+ if (q.name === undefined || q.name.trim() === '') {
585
+ return reply.code(400).send({ error: 'name query parameter required' });
586
+ }
587
+ const repos = await adapter.listRepos();
588
+ const repo = repos.find((r) => r.id === id);
589
+ if (!repo)
590
+ return reply.code(404).send({ error: `Repo ${id} not found` });
591
+ const dbPath = join(repo.root_path, '.codegraph', 'estate.db');
592
+ if (!existsSync(dbPath)) {
593
+ return reply.code(404).send({ error: 'Code graph not built for this repo yet' });
594
+ }
595
+ try {
596
+ const { stdout } = await execFileAsync('wicked-estate', ['blast-radius', q.name.trim(), '--db', dbPath, '--json'], { timeout: 30_000, cwd: repo.root_path });
597
+ return reply.send(JSON.parse(stdout));
598
+ }
599
+ catch (err) {
600
+ return reply.code(500).send({ error: message(err) });
601
+ }
602
+ });
490
603
  app.get(`${V}/repos/:id/git-history`, async (req, reply) => {
491
604
  const { id } = req.params;
492
605
  const repos = await adapter.listRepos();