@isaacriehm/cairn-core 0.26.0 → 0.31.0

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 (91) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/gc/config-drift.d.ts +60 -0
  3. package/dist/gc/config-drift.js +262 -0
  4. package/dist/gc/config-drift.js.map +1 -0
  5. package/dist/gc/index.d.ts +2 -0
  6. package/dist/gc/index.js +1 -0
  7. package/dist/gc/index.js.map +1 -1
  8. package/dist/gc/sweep.js +11 -0
  9. package/dist/gc/sweep.js.map +1 -1
  10. package/dist/gc/types.d.ts +2 -2
  11. package/dist/hooks/post-tool-use/sot-align.d.ts +4 -0
  12. package/dist/hooks/post-tool-use/sot-align.js +26 -3
  13. package/dist/hooks/post-tool-use/sot-align.js.map +1 -1
  14. package/dist/hooks/sot-align-common.d.ts +10 -0
  15. package/dist/hooks/sot-align-common.js +32 -0
  16. package/dist/hooks/sot-align-common.js.map +1 -1
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.js +1 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/init/brand-setup.js +8 -3
  21. package/dist/init/brand-setup.js.map +1 -1
  22. package/dist/init/curator/emit.d.ts +10 -0
  23. package/dist/init/curator/emit.js +29 -17
  24. package/dist/init/curator/emit.js.map +1 -1
  25. package/dist/init/curator/index.d.ts +2 -0
  26. package/dist/init/curator/index.js +2 -0
  27. package/dist/init/curator/index.js.map +1 -1
  28. package/dist/init/curator/walker.d.ts +7 -0
  29. package/dist/init/curator/walker.js +17 -4
  30. package/dist/init/curator/walker.js.map +1 -1
  31. package/dist/init/index.d.ts +1 -1
  32. package/dist/init/index.js +1 -1
  33. package/dist/init/index.js.map +1 -1
  34. package/dist/init/phases/4-seed.js +7 -2
  35. package/dist/init/phases/4-seed.js.map +1 -1
  36. package/dist/init/seed.d.ts +7 -0
  37. package/dist/init/seed.js +24 -16
  38. package/dist/init/seed.js.map +1 -1
  39. package/dist/init/topic-index/index.d.ts +8 -0
  40. package/dist/init/topic-index/index.js +17 -8
  41. package/dist/init/topic-index/index.js.map +1 -1
  42. package/dist/invariants/prune.d.ts +6 -4
  43. package/dist/invariants/prune.js +40 -7
  44. package/dist/invariants/prune.js.map +1 -1
  45. package/dist/mcp/schemas.d.ts +26 -0
  46. package/dist/mcp/schemas.js +23 -0
  47. package/dist/mcp/schemas.js.map +1 -1
  48. package/dist/mcp/tools/in-scope.js +7 -1
  49. package/dist/mcp/tools/in-scope.js.map +1 -1
  50. package/dist/mcp/tools/index.js +3 -0
  51. package/dist/mcp/tools/index.js.map +1 -1
  52. package/dist/mcp/tools/invariant-get.js +37 -28
  53. package/dist/mcp/tools/invariant-get.js.map +1 -1
  54. package/dist/mcp/tools/resolve-attention.d.ts +1 -1
  55. package/dist/mcp/tools/resolve-attention.js +92 -0
  56. package/dist/mcp/tools/resolve-attention.js.map +1 -1
  57. package/dist/mcp/tools/resync.d.ts +19 -0
  58. package/dist/mcp/tools/resync.js +109 -0
  59. package/dist/mcp/tools/resync.js.map +1 -0
  60. package/dist/migrate/migrations/0005-demote-autofilled-brand.d.ts +11 -3
  61. package/dist/migrate/migrations/0005-demote-autofilled-brand.js +67 -21
  62. package/dist/migrate/migrations/0005-demote-autofilled-brand.js.map +1 -1
  63. package/dist/migrate/migrations/0006-prune-sot-align-invariants.d.ts +17 -15
  64. package/dist/migrate/migrations/0006-prune-sot-align-invariants.js +23 -17
  65. package/dist/migrate/migrations/0006-prune-sot-align-invariants.js.map +1 -1
  66. package/dist/migrate/migrations/0008-clean-adoption-scaffolding.d.ts +39 -0
  67. package/dist/migrate/migrations/0008-clean-adoption-scaffolding.js +206 -0
  68. package/dist/migrate/migrations/0008-clean-adoption-scaffolding.js.map +1 -0
  69. package/dist/migrate/registry.js +2 -0
  70. package/dist/migrate/registry.js.map +1 -1
  71. package/dist/resync/archive.d.ts +12 -0
  72. package/dist/resync/archive.js +29 -0
  73. package/dist/resync/archive.js.map +1 -0
  74. package/dist/resync/index.d.ts +73 -0
  75. package/dist/resync/index.js +389 -0
  76. package/dist/resync/index.js.map +1 -0
  77. package/dist/resync/recluster.d.ts +59 -0
  78. package/dist/resync/recluster.js +66 -0
  79. package/dist/resync/recluster.js.map +1 -0
  80. package/dist/session-start/build.js +47 -30
  81. package/dist/session-start/build.js.map +1 -1
  82. package/dist/session-start/templates.js +4 -4
  83. package/dist/session-start/templates.js.map +1 -1
  84. package/package.json +2 -2
  85. package/templates/.cairn/config/sensors.yaml +1 -1
  86. package/templates/.cairn/config/workflow.md +5 -12
  87. package/templates/.cairn/git-hooks/commit-msg +0 -2
  88. package/templates/.cairn/git-hooks/post-commit +0 -2
  89. package/templates/.cairn/ground/brand/overview.md +2 -2
  90. package/templates/.cairn/ground/brand/voice.md +2 -2
  91. package/templates/.cairn/ground/product/positioning.md +2 -2
@@ -0,0 +1,389 @@
1
+ /**
2
+ * `cairn resync` — operator-initiated incremental re-discovery (Stage 3).
3
+ *
4
+ * Stage 1's config-drift sensor SURFACES the gap between declared config and
5
+ * the grown tree; resync is the operator-initiated verb that RESOLVES it. This
6
+ * v1 closes the deterministic half of the loop: it re-runs the config-drift
7
+ * detector and turns each finding into a concrete `config.yaml` edit —
8
+ *
9
+ * - `config_uncovered_dir` → add the dir to the owning workspace's
10
+ * `componentDirs`
11
+ * - `config_uncovered_ext` → add the file type to that workspace's
12
+ * `extensions`
13
+ * - `config_gitignore_drift` → add the ignored path to top-level
14
+ * `off_limits`
15
+ * - `config_orphan_path` → drop the dead `componentDir`
16
+ *
17
+ * Safety (Q23): `--dry-run` (the default) mutates nothing — it returns the
18
+ * proposed edits for the operator to review. Apply archives the pre-resync
19
+ * `config.yaml` to `.cairn/ground/.archive/` first, edits via the comment-
20
+ * preserving yaml Document API, and is idempotent on a clean delta (re-run
21
+ * after apply proposes nothing). The edit is a `review`-class mutation of
22
+ * committed config the operator commits; derived state stays gitignored +
23
+ * per-clone, so there is no new multi-dev conflict surface (Q22).
24
+ *
25
+ * Deferred to the LLM half (Q3/Q16, opt-in, quota-gated): hash-rematch of moved
26
+ * entities, Haiku re-cluster of topic-index/canonical-map over genuinely-new
27
+ * prose, and re-curation of new areas into DEC/INV drafts. `domain_summary`
28
+ * (Q15) is an init-time seed for the brand bodies, not live agent context, so
29
+ * resync does not refresh it.
30
+ */
31
+ import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
32
+ import { extname, join } from "node:path";
33
+ import { configPath, decisionsDir, invariantsDir, knownExtensions, loadComponentsConfig, parseFrontmatterRecord, } from "@isaacriehm/cairn-state";
34
+ import { runConfigDrift } from "../gc/config-drift.js";
35
+ import { walkSourceTree } from "../gc/walk-source.js";
36
+ import { loadConfigDoc, writeConfigDoc } from "../migrate/config-io.js";
37
+ import { archiveFile } from "./archive.js";
38
+ export { runResyncRecluster, } from "./recluster.js";
39
+ function trimSlash(p) {
40
+ return p.endsWith("/") ? p.slice(0, -1) : p;
41
+ }
42
+ function commonDirPrefix(dirs) {
43
+ if (dirs.length === 0)
44
+ return "";
45
+ const split = dirs.map((d) => trimSlash(d).split("/"));
46
+ const first = split[0];
47
+ let len = first.length;
48
+ for (const segs of split) {
49
+ let i = 0;
50
+ while (i < len && i < segs.length && segs[i] === first[i])
51
+ i++;
52
+ len = i;
53
+ }
54
+ return first.slice(0, len).join("/");
55
+ }
56
+ function isUnder(rel, dir) {
57
+ const n = trimSlash(dir);
58
+ return n.length > 0 && (rel === n || rel.startsWith(`${n}/`));
59
+ }
60
+ /** Workspace whose root (common prefix of its componentDirs) contains `dir`. */
61
+ function attributeDirToWorkspace(dir, workspaces) {
62
+ let best = null;
63
+ let bestLen = -1;
64
+ for (const ws of workspaces) {
65
+ const root = commonDirPrefix(ws.componentDirs);
66
+ if (isUnder(dir, root) && root.length > bestLen) {
67
+ best = ws.name;
68
+ bestLen = root.length;
69
+ }
70
+ }
71
+ return best ?? workspaces[0]?.name ?? "";
72
+ }
73
+ /** Workspace whose componentDir is the longest prefix of `file`. */
74
+ function owningWorkspace(file, workspaces) {
75
+ let best = null;
76
+ let bestLen = -1;
77
+ for (const ws of workspaces) {
78
+ for (const d of ws.componentDirs) {
79
+ const n = trimSlash(d);
80
+ if (isUnder(file, n) && n.length > bestLen) {
81
+ best = ws.name;
82
+ bestLen = n.length;
83
+ }
84
+ }
85
+ }
86
+ return best ?? workspaces[0]?.name ?? "";
87
+ }
88
+ /** Workspace that declares `dir` verbatim as a componentDir, or null. */
89
+ function workspaceDeclaring(dir, workspaces) {
90
+ const target = trimSlash(dir);
91
+ for (const ws of workspaces) {
92
+ if (ws.componentDirs.map(trimSlash).includes(target))
93
+ return ws.name;
94
+ }
95
+ return null;
96
+ }
97
+ function dedupKey(p) {
98
+ return `${p.kind} ${p.workspace ?? ""} ${p.entityPath ?? ""} ${p.value}`;
99
+ }
100
+ const INV_CITE_RE = /§INV-([0-9a-f]{7,})\b/g;
101
+ const DEC_CITE_RE = /§DEC-([0-9a-f]{7,})\b/g;
102
+ function citeScanExtensions() {
103
+ return new Set([...knownExtensions(), ".html", ".css", ".scss", ".md"]);
104
+ }
105
+ function fileExt(rel) {
106
+ const i = rel.lastIndexOf(".");
107
+ return i === -1 ? "" : rel.slice(i).toLowerCase();
108
+ }
109
+ /** Map every cited `§DEC-`/`§INV-` id → the set of files that cite it. */
110
+ function buildCiteFileMap(repoRoot) {
111
+ const exts = citeScanExtensions();
112
+ const map = new Map();
113
+ const add = (id, file) => {
114
+ const s = map.get(id) ?? new Set();
115
+ s.add(file);
116
+ map.set(id, s);
117
+ };
118
+ for (const rel of walkSourceTree(repoRoot)) {
119
+ if (!exts.has(fileExt(rel)))
120
+ continue;
121
+ let content;
122
+ try {
123
+ content = readFileSync(join(repoRoot, rel), "utf8");
124
+ }
125
+ catch {
126
+ continue;
127
+ }
128
+ for (const m of content.matchAll(DEC_CITE_RE))
129
+ add(`DEC-${m[1]}`, rel);
130
+ for (const m of content.matchAll(INV_CITE_RE))
131
+ add(`INV-${m[1]}`, rel);
132
+ }
133
+ return map;
134
+ }
135
+ /**
136
+ * Hash-rematch's deterministic, committed-mode case (Q3 "free" half): a
137
+ * ledger-backed DEC/INV whose recorded `source_file` no longer exists, but
138
+ * whose `§cite` now lives in exactly one OTHER file — the file was renamed and
139
+ * the cite moved with its content, so the cite is the authoritative new home.
140
+ * Re-point `source_file` there (keeps `cairn_in_scope`'s source_file match —
141
+ * the Stage 0 fix — accurate after a rename). Ambiguous (0 or >1 citing files)
142
+ * is left to the entity-orphan pass. Ghost (no cites) yields nothing here.
143
+ */
144
+ function deriveSourceRematch(repoRoot) {
145
+ const citeMap = buildCiteFileMap(repoRoot);
146
+ const out = [];
147
+ const groups = [
148
+ { dir: decisionsDir(repoRoot), rel: ".cairn/ground/decisions" },
149
+ { dir: invariantsDir(repoRoot), rel: ".cairn/ground/invariants" },
150
+ ];
151
+ for (const g of groups) {
152
+ let names;
153
+ try {
154
+ names = readdirSync(g.dir, { encoding: "utf8" });
155
+ }
156
+ catch {
157
+ continue;
158
+ }
159
+ for (const name of names) {
160
+ if (!name.endsWith(".md") || name.startsWith("_"))
161
+ continue;
162
+ const abs = join(g.dir, name);
163
+ let fm;
164
+ try {
165
+ fm = parseFrontmatterRecord(readFileSync(abs, "utf8")).fm;
166
+ }
167
+ catch {
168
+ continue;
169
+ }
170
+ const status = typeof fm["status"] === "string" ? fm["status"] : "";
171
+ if (status === "archived" || status === "superseded")
172
+ continue;
173
+ const sotKind = typeof fm["sot_kind"] === "string" ? fm["sot_kind"] : "ledger";
174
+ if (sotKind !== "ledger")
175
+ continue;
176
+ const src = typeof fm["source_file"] === "string" ? fm["source_file"] : "";
177
+ const id = typeof fm["id"] === "string" ? fm["id"] : "";
178
+ if (src.length === 0 || id.length === 0)
179
+ continue;
180
+ if (existsSync(join(repoRoot, src)))
181
+ continue; // source still present → fine
182
+ const files = [...(citeMap.get(id) ?? [])];
183
+ if (files.length !== 1)
184
+ continue; // 0 = orphan (entity-orphan's job); >1 = ambiguous
185
+ const newSrc = files[0];
186
+ if (newSrc === src)
187
+ continue;
188
+ out.push({
189
+ kind: "repoint_source",
190
+ value: newSrc,
191
+ from: "stale_source",
192
+ entityId: id,
193
+ entityPath: `${g.rel}/${name}`,
194
+ detail: `re-point ${id} source_file \`${src}\` → \`${newSrc}\` (file moved; cite followed)`,
195
+ });
196
+ }
197
+ }
198
+ return out;
199
+ }
200
+ /** Map current config-drift findings → deterministic config-edit proposals. */
201
+ function deriveProposals(repoRoot, area) {
202
+ const { workspaces } = loadComponentsConfig(repoRoot);
203
+ const findings = runConfigDrift({ repoRoot }).findings.filter((f) => area === undefined || f.path === area || f.path.startsWith(`${trimSlash(area)}/`));
204
+ const proposals = [];
205
+ const skipped = [];
206
+ const seen = new Set();
207
+ const push = (p) => {
208
+ const k = dedupKey(p);
209
+ if (seen.has(k))
210
+ return;
211
+ seen.add(k);
212
+ proposals.push(p);
213
+ };
214
+ for (const f of findings) {
215
+ switch (f.kind) {
216
+ case "config_uncovered_dir": {
217
+ const ws = attributeDirToWorkspace(f.path, workspaces);
218
+ push({
219
+ kind: "add_component_dir",
220
+ workspace: ws,
221
+ value: f.path,
222
+ from: f.kind,
223
+ detail: `add componentDir \`${f.path}\`${ws ? ` to workspace ${ws}` : ""}`,
224
+ });
225
+ break;
226
+ }
227
+ case "config_uncovered_ext": {
228
+ const ws = owningWorkspace(f.path, workspaces);
229
+ const ext = extname(f.path);
230
+ if (ext.length === 0) {
231
+ skipped.push({ finding: f.kind, path: f.path, reason: "no file extension" });
232
+ break;
233
+ }
234
+ push({
235
+ kind: "add_extension",
236
+ workspace: ws,
237
+ value: ext,
238
+ from: f.kind,
239
+ detail: `add extension \`${ext}\`${ws ? ` to workspace ${ws}` : ""}`,
240
+ });
241
+ break;
242
+ }
243
+ case "config_gitignore_drift": {
244
+ push({
245
+ kind: "add_off_limits",
246
+ value: f.path,
247
+ from: f.kind,
248
+ detail: `add \`${f.path}\` to off_limits`,
249
+ });
250
+ break;
251
+ }
252
+ case "config_orphan_path": {
253
+ const ws = workspaceDeclaring(f.path, workspaces);
254
+ if (ws === null) {
255
+ skipped.push({ finding: f.kind, path: f.path, reason: "no workspace declares this dir" });
256
+ break;
257
+ }
258
+ push({
259
+ kind: "drop_component_dir",
260
+ workspace: ws,
261
+ value: f.path,
262
+ from: f.kind,
263
+ detail: `drop dead componentDir \`${f.path}\`${ws ? ` from workspace ${ws}` : ""}`,
264
+ });
265
+ break;
266
+ }
267
+ default:
268
+ // Non-config-drift findings (other GC passes) aren't resync's job.
269
+ break;
270
+ }
271
+ }
272
+ // Source rematch (Q3 free half) — re-point stale source_file pointers.
273
+ for (const p of deriveSourceRematch(repoRoot)) {
274
+ if (area !== undefined && p.value !== area && !p.value.startsWith(`${trimSlash(area)}/`)) {
275
+ continue;
276
+ }
277
+ push(p);
278
+ }
279
+ proposals.sort((a, b) => dedupKey(a).localeCompare(dedupKey(b)));
280
+ return { proposals, skipped };
281
+ }
282
+ /** Config Document path to a workspace's array field (flat single-app vs map). */
283
+ function fieldPath(workspace, field) {
284
+ return workspace !== undefined && workspace.length > 0
285
+ ? ["components", "workspaces", workspace, field]
286
+ : ["components", field];
287
+ }
288
+ function asArray(node) {
289
+ if (node === null || node === undefined)
290
+ return [];
291
+ const json = node.toJSON?.() ?? node;
292
+ return Array.isArray(json) ? json.filter((x) => typeof x === "string") : [];
293
+ }
294
+ /** Resolve a `.cairn/ground/{decisions,invariants}/X.md` rel path to abs (mode-aware). */
295
+ function entityAbs(repoRoot, entityPath) {
296
+ const base = entityPath.split("/").pop();
297
+ return entityPath.includes("/decisions/")
298
+ ? join(decisionsDir(repoRoot), base)
299
+ : join(invariantsDir(repoRoot), base);
300
+ }
301
+ /** Rewrite the (frontmatter) `source_file:` line in place. */
302
+ function repointSource(abs, newSrc) {
303
+ let raw;
304
+ try {
305
+ raw = readFileSync(abs, "utf8");
306
+ }
307
+ catch {
308
+ return false;
309
+ }
310
+ if (!/^source_file:\s*.*$/m.test(raw))
311
+ return false;
312
+ const next = raw.replace(/^source_file:\s*.*$/m, `source_file: ${newSrc}`);
313
+ if (next === raw)
314
+ return false;
315
+ try {
316
+ writeFileSync(abs, next, "utf8");
317
+ }
318
+ catch {
319
+ return false;
320
+ }
321
+ return true;
322
+ }
323
+ export function runResync(opts) {
324
+ const dryRun = opts.dryRun !== false; // default true (safe)
325
+ const { proposals, skipped } = deriveProposals(opts.repoRoot, opts.area);
326
+ if (dryRun || proposals.length === 0) {
327
+ return {
328
+ dryRun: true,
329
+ proposals,
330
+ applied: false,
331
+ archivedConfig: null,
332
+ archivedEntities: [],
333
+ skipped,
334
+ };
335
+ }
336
+ const nowIso = opts.nowIso ?? new Date().toISOString();
337
+ const configProposals = proposals.filter((p) => p.kind !== "repoint_source");
338
+ const rematchProposals = proposals.filter((p) => p.kind === "repoint_source");
339
+ // ── Config edits (config.yaml) ──────────────────────────────────────
340
+ let archivedConfig = null;
341
+ if (configProposals.length > 0) {
342
+ archivedConfig = archiveFile(configPath(opts.repoRoot), opts.repoRoot, "config.yaml", nowIso);
343
+ const doc = loadConfigDoc(opts.repoRoot);
344
+ if (doc !== null) {
345
+ for (const p of configProposals) {
346
+ if (p.kind === "add_off_limits") {
347
+ const path = ["off_limits"];
348
+ const cur = asArray(doc.getIn(path));
349
+ if (!cur.includes(p.value))
350
+ doc.setIn(path, [...cur, p.value]);
351
+ continue;
352
+ }
353
+ const field = p.kind === "add_extension" ? "extensions" : "componentDirs";
354
+ const path = fieldPath(p.workspace, field);
355
+ const cur = asArray(doc.getIn(path));
356
+ if (p.kind === "drop_component_dir") {
357
+ const next = cur.filter((d) => trimSlash(d) !== trimSlash(p.value));
358
+ if (next.length !== cur.length)
359
+ doc.setIn(path, next);
360
+ }
361
+ else if (!cur.includes(p.value)) {
362
+ doc.setIn(path, [...cur, p.value]);
363
+ }
364
+ }
365
+ writeConfigDoc(opts.repoRoot, doc);
366
+ }
367
+ }
368
+ // ── Source rematch (entity .md frontmatter) ─────────────────────────
369
+ const archivedEntities = [];
370
+ for (const p of rematchProposals) {
371
+ if (p.entityPath === undefined)
372
+ continue;
373
+ const abs = entityAbs(opts.repoRoot, p.entityPath);
374
+ const base = p.entityPath.split("/").pop();
375
+ const archived = archiveFile(abs, opts.repoRoot, base, nowIso);
376
+ if (repointSource(abs, p.value) && archived !== null) {
377
+ archivedEntities.push(archived);
378
+ }
379
+ }
380
+ return {
381
+ dryRun: false,
382
+ proposals,
383
+ applied: true,
384
+ archivedConfig,
385
+ archivedEntities,
386
+ skipped,
387
+ };
388
+ }
389
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resync/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * `cairn resync --recluster` — the LLM half of re-discovery (Stage 3b).
3
+ *
4
+ * The deterministic config-resync (`runResync`) re-points moved entities and
5
+ * patches `config.yaml`, but it can't notice that the project's *prose* has
6
+ * grown a new concept or that two now-separate docs describe the same topic.
7
+ * That re-clustering is what init's Phase 7 does with a Haiku judge; this verb
8
+ * re-runs exactly that pass over the grown tree.
9
+ *
10
+ * Why it's a distinct, opt-in verb (not folded into `runResync` or a sensor):
11
+ * - It spends Haiku. The judge fires for every fresh semantic-similarity
12
+ * collision, so it's quota-gated — never auto-run, never on a hook.
13
+ * - The on-disk judge cache makes it *incremental for free* (Q3): unchanged
14
+ * prose pairs hit the cache (no quota burn); only genuinely-new prose
15
+ * produces fresh judge calls. `judgeFresh` is the real cost of a re-run.
16
+ *
17
+ * Safety (Q23): the maps are gitignored, per-clone derived state — overwriting
18
+ * them is not a committed mutation and raises no multi-dev conflict (Q22). Even
19
+ * so, apply archives the pre-resync `topic-index.yaml` + `anchor-map.yaml` to
20
+ * `.cairn/ground/.archive/` first (a bad re-cluster is recoverable), and
21
+ * `--dry-run` (the default) resolves + reports without overwriting anything.
22
+ *
23
+ * The judge is injected (`opts.judge`) so the gate smoke drives a deterministic
24
+ * mock — same runner seam Phase 7 already exposes — and burns zero quota.
25
+ */
26
+ import type { ProseBlock, SemanticJudge } from "../init/topic-index/resolve.js";
27
+ export interface ResyncReclusterOptions {
28
+ repoRoot: string;
29
+ /** Preview only — resolve + report but DON'T overwrite the maps. Default true (safe). */
30
+ dryRun?: boolean;
31
+ /** Judge seam — smokes inject a deterministic mock to avoid Haiku. */
32
+ judge?: SemanticJudge;
33
+ /** Walker seam — smokes inject canned blocks instead of walking the tree. */
34
+ blocks?: ProseBlock[];
35
+ /** Hard cap on judge calls (passed through to the resolver). */
36
+ maxJudgeCalls?: number;
37
+ /** Injected ISO for archive filenames (determinism in tests). */
38
+ nowIso?: string;
39
+ }
40
+ export interface ResyncReclusterResult {
41
+ dryRun: boolean;
42
+ applied: boolean;
43
+ /** Distinct topic count before / after the re-cluster. */
44
+ topicsBefore: number;
45
+ topicsAfter: number;
46
+ /** Prose blocks walked. */
47
+ blockCount: number;
48
+ /** Total judge calls dispatched (fresh + cached). */
49
+ judgeCalls: number;
50
+ /** Fresh `claude --print` judge calls — the real quota cost of this run. */
51
+ judgeFresh: number;
52
+ /** Judge calls served from the on-disk cache (no quota burn). */
53
+ judgeCached: number;
54
+ /** Judge calls that threw. */
55
+ judgeErrors: number;
56
+ /** Repo-relative paths of the archived pre-resync maps (apply only). */
57
+ archivedMaps: string[];
58
+ }
59
+ export declare function runResyncRecluster(opts: ResyncReclusterOptions): Promise<ResyncReclusterResult>;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * `cairn resync --recluster` — the LLM half of re-discovery (Stage 3b).
3
+ *
4
+ * The deterministic config-resync (`runResync`) re-points moved entities and
5
+ * patches `config.yaml`, but it can't notice that the project's *prose* has
6
+ * grown a new concept or that two now-separate docs describe the same topic.
7
+ * That re-clustering is what init's Phase 7 does with a Haiku judge; this verb
8
+ * re-runs exactly that pass over the grown tree.
9
+ *
10
+ * Why it's a distinct, opt-in verb (not folded into `runResync` or a sensor):
11
+ * - It spends Haiku. The judge fires for every fresh semantic-similarity
12
+ * collision, so it's quota-gated — never auto-run, never on a hook.
13
+ * - The on-disk judge cache makes it *incremental for free* (Q3): unchanged
14
+ * prose pairs hit the cache (no quota burn); only genuinely-new prose
15
+ * produces fresh judge calls. `judgeFresh` is the real cost of a re-run.
16
+ *
17
+ * Safety (Q23): the maps are gitignored, per-clone derived state — overwriting
18
+ * them is not a committed mutation and raises no multi-dev conflict (Q22). Even
19
+ * so, apply archives the pre-resync `topic-index.yaml` + `anchor-map.yaml` to
20
+ * `.cairn/ground/.archive/` first (a bad re-cluster is recoverable), and
21
+ * `--dry-run` (the default) resolves + reports without overwriting anything.
22
+ *
23
+ * The judge is injected (`opts.judge`) so the gate smoke drives a deterministic
24
+ * mock — same runner seam Phase 7 already exposes — and burns zero quota.
25
+ */
26
+ import { anchorMapPath, readTopicIndex, topicIndexPath } from "@isaacriehm/cairn-state";
27
+ import { buildTopicIndex } from "../init/topic-index/index.js";
28
+ import { archiveFile } from "./archive.js";
29
+ export async function runResyncRecluster(opts) {
30
+ const dryRun = opts.dryRun !== false; // default true (safe)
31
+ const topicsBefore = Object.keys(readTopicIndex(opts.repoRoot).topics).length;
32
+ // On apply, snapshot the live maps before the rebuild overwrites them.
33
+ const archivedMaps = [];
34
+ if (!dryRun) {
35
+ const nowIso = opts.nowIso ?? new Date().toISOString();
36
+ for (const [abs, base] of [
37
+ [topicIndexPath(opts.repoRoot), "topic-index.yaml"],
38
+ [anchorMapPath(opts.repoRoot), "anchor-map.yaml"],
39
+ ]) {
40
+ const archived = archiveFile(abs, opts.repoRoot, base, nowIso);
41
+ if (archived !== null)
42
+ archivedMaps.push(archived);
43
+ }
44
+ }
45
+ const result = await buildTopicIndex({
46
+ repoRoot: opts.repoRoot,
47
+ write: !dryRun,
48
+ emitProgress: false,
49
+ ...(opts.judge !== undefined ? { judge: opts.judge } : {}),
50
+ ...(opts.blocks !== undefined ? { blocks: opts.blocks } : {}),
51
+ ...(opts.maxJudgeCalls !== undefined ? { maxJudgeCalls: opts.maxJudgeCalls } : {}),
52
+ });
53
+ return {
54
+ dryRun,
55
+ applied: !dryRun,
56
+ topicsBefore,
57
+ topicsAfter: Object.keys(result.topicIndex.topics).length,
58
+ blockCount: result.blockCount,
59
+ judgeCalls: result.judgeCalls,
60
+ judgeFresh: result.judgeFresh,
61
+ judgeCached: result.judgeCached,
62
+ judgeErrors: result.judgeErrors,
63
+ archivedMaps,
64
+ };
65
+ }
66
+ //# sourceMappingURL=recluster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recluster.js","sourceRoot":"","sources":["../../src/resync/recluster.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACxF,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAoC3C,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAA4B;IAE5B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,sBAAsB;IAC5D,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IAE9E,uEAAuE;IACvE,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACvD,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI;YACxB,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAC;YACnD,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,iBAAiB,CAAC;SACzC,EAAE,CAAC;YACX,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAC/D,IAAI,QAAQ,KAAK,IAAI;gBAAE,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC;QACnC,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,KAAK,EAAE,CAAC,MAAM;QACd,YAAY,EAAE,KAAK;QACnB,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1D,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,GAAG,CAAC,IAAI,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnF,CAAC,CAAC;IAEH,OAAO;QACL,MAAM;QACN,OAAO,EAAE,CAAC,MAAM;QAChB,YAAY;QACZ,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM;QACzD,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,YAAY;KACb,CAAC;AACJ,CAAC"}
@@ -233,7 +233,11 @@ export async function buildSessionStartContext(args) {
233
233
  // 500+ soft findings as "⚑ N pending" gives the operator a count
234
234
  // they can't drain item-by-item.
235
235
  const latestBaseline = readLatestBaselineAudit(args.repoRoot, warnings);
236
- counts.baselineFindings = latestBaseline?.hardFindings ?? 0;
236
+ // Config-drift (24h GC pass) writes its own baseline family; its findings are
237
+ // actionable nudges, so roll them into the same hard-findings count.
238
+ const latestConfigDrift = readLatestBaselineAudit(args.repoRoot, warnings, "config-drift-");
239
+ counts.baselineFindings =
240
+ (latestBaseline?.hardFindings ?? 0) + (latestConfigDrift?.hardFindings ?? 0);
237
241
  // ── Section 1.5 — brand + product context (only when CONFIRMED) ───
238
242
  // Overview, positioning, AND voice — but only files the operator has
239
243
  // confirmed (status: current). Drafts / generic auto-fill are skipped.
@@ -813,38 +817,47 @@ function renderQualityGradesSection(grades) {
813
817
  return lines.join("\n");
814
818
  }
815
819
  function listPendingDrafts(repoRoot, warnings) {
816
- const dir = cairnDir(repoRoot, "ground", "decisions", "_inbox");
817
- if (!existsSync(dir))
818
- return [];
819
- let entries;
820
- try {
821
- entries = readdirSync(dir, { withFileTypes: true, encoding: "utf8" });
822
- }
823
- catch {
824
- return [];
825
- }
820
+ // Both DEC (`decisions/_inbox/`) and INV (`invariants/_inbox/`) drafts are
821
+ // operator-pending review items: init-curator + manual record_decision write
822
+ // the former, resync re-curation writes both. The id prefix (DEC-/INV-)
823
+ // tells the operator which is which; cairn-attention dispatches on it.
824
+ const dirs = [
825
+ cairnDir(repoRoot, "ground", "decisions", "_inbox"),
826
+ cairnDir(repoRoot, "ground", "invariants", "_inbox"),
827
+ ];
826
828
  const out = [];
827
- for (const e of entries) {
828
- if (!e.isFile())
829
- continue;
830
- if (!e.name.endsWith(".draft.md"))
829
+ for (const dir of dirs) {
830
+ if (!existsSync(dir))
831
831
  continue;
832
- const abs = join(dir, e.name);
833
- let text;
832
+ let entries;
834
833
  try {
835
- text = readFileSync(abs, "utf8");
834
+ entries = readdirSync(dir, { withFileTypes: true, encoding: "utf8" });
836
835
  }
837
- catch (err) {
838
- warnings.push(`draft ${e.name} unreadable: ${err instanceof Error ? err.message : String(err)}`);
836
+ catch {
839
837
  continue;
840
838
  }
841
- const parsed = parseFrontmatter(text);
842
- const fm = (parsed.frontmatter ?? {});
843
- const id = typeof fm["id"] === "string" ? fm["id"] : e.name.replace(/\.draft\.md$/, "");
844
- const title = typeof fm["title"] === "string" ? fm["title"] : "(untitled draft)";
845
- const captureSource = typeof fm["capture_source"] === "string" ? fm["capture_source"] : null;
846
- const decidedAt = typeof fm["decided_at"] === "string" ? fm["decided_at"] : null;
847
- out.push({ id, title, capture_source: captureSource, decided_at: decidedAt });
839
+ for (const e of entries) {
840
+ if (!e.isFile())
841
+ continue;
842
+ if (!e.name.endsWith(".draft.md"))
843
+ continue;
844
+ const abs = join(dir, e.name);
845
+ let text;
846
+ try {
847
+ text = readFileSync(abs, "utf8");
848
+ }
849
+ catch (err) {
850
+ warnings.push(`draft ${e.name} unreadable: ${err instanceof Error ? err.message : String(err)}`);
851
+ continue;
852
+ }
853
+ const parsed = parseFrontmatter(text);
854
+ const fm = (parsed.frontmatter ?? {});
855
+ const id = typeof fm["id"] === "string" ? fm["id"] : e.name.replace(/\.draft\.md$/, "");
856
+ const title = typeof fm["title"] === "string" ? fm["title"] : "(untitled draft)";
857
+ const captureSource = typeof fm["capture_source"] === "string" ? fm["capture_source"] : null;
858
+ const decidedAt = typeof fm["decided_at"] === "string" ? fm["decided_at"] : null;
859
+ out.push({ id, title, capture_source: captureSource, decided_at: decidedAt });
860
+ }
848
861
  }
849
862
  out.sort((a, b) => a.id.localeCompare(b.id));
850
863
  return out;
@@ -900,7 +913,7 @@ function renderFirstSessionOnboarding(args) {
900
913
  lines.push(" To capture a new decision during this session: `/cairn-direction <your instruction>`");
901
914
  return lines.join("\n");
902
915
  }
903
- function readLatestBaselineAudit(repoRoot, warnings) {
916
+ function readLatestBaselineAudit(repoRoot, warnings, prefix = "sensor-audit-") {
904
917
  const dir = cairnDir(repoRoot, "baseline");
905
918
  if (!existsSync(dir))
906
919
  return null;
@@ -912,8 +925,10 @@ function readLatestBaselineAudit(repoRoot, warnings) {
912
925
  warnings.push(`baseline dir read failed: ${err instanceof Error ? err.message : String(err)}`);
913
926
  return null;
914
927
  }
928
+ const escaped = prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
929
+ const re = new RegExp(`^${escaped}.*\\.yaml$`);
915
930
  const matching = entries
916
- .filter((name) => /^sensor-audit-.*\.yaml$/.test(name))
931
+ .filter((name) => re.test(name))
917
932
  .sort();
918
933
  const latest = matching.at(-1);
919
934
  if (latest === undefined)
@@ -1016,7 +1031,9 @@ function renderPendingDraftsSection(drafts) {
1016
1031
  if (drafts.length === 0)
1017
1032
  return null;
1018
1033
  const lines = [];
1019
- lines.push(`## Decision drafts pending operator confirm (${drafts.length})`);
1034
+ // Both DEC and INV drafts surface here (the id prefix disambiguates each row);
1035
+ // INV drafts come from resync re-curation.
1036
+ lines.push(`## Decision / invariant drafts pending operator confirm (${drafts.length})`);
1020
1037
  lines.push("");
1021
1038
  const slice = drafts.slice(0, DRAFTS_CAP);
1022
1039
  for (const d of slice) {