@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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/gc/config-drift.d.ts +60 -0
- package/dist/gc/config-drift.js +262 -0
- package/dist/gc/config-drift.js.map +1 -0
- package/dist/gc/index.d.ts +2 -0
- package/dist/gc/index.js +1 -0
- package/dist/gc/index.js.map +1 -1
- package/dist/gc/sweep.js +11 -0
- package/dist/gc/sweep.js.map +1 -1
- package/dist/gc/types.d.ts +2 -2
- package/dist/hooks/post-tool-use/sot-align.d.ts +4 -0
- package/dist/hooks/post-tool-use/sot-align.js +26 -3
- package/dist/hooks/post-tool-use/sot-align.js.map +1 -1
- package/dist/hooks/sot-align-common.d.ts +10 -0
- package/dist/hooks/sot-align-common.js +32 -0
- package/dist/hooks/sot-align-common.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/init/brand-setup.js +8 -3
- package/dist/init/brand-setup.js.map +1 -1
- package/dist/init/curator/emit.d.ts +10 -0
- package/dist/init/curator/emit.js +29 -17
- package/dist/init/curator/emit.js.map +1 -1
- package/dist/init/curator/index.d.ts +2 -0
- package/dist/init/curator/index.js +2 -0
- package/dist/init/curator/index.js.map +1 -1
- package/dist/init/curator/walker.d.ts +7 -0
- package/dist/init/curator/walker.js +17 -4
- package/dist/init/curator/walker.js.map +1 -1
- package/dist/init/index.d.ts +1 -1
- package/dist/init/index.js +1 -1
- package/dist/init/index.js.map +1 -1
- package/dist/init/phases/4-seed.js +7 -2
- package/dist/init/phases/4-seed.js.map +1 -1
- package/dist/init/seed.d.ts +7 -0
- package/dist/init/seed.js +24 -16
- package/dist/init/seed.js.map +1 -1
- package/dist/init/topic-index/index.d.ts +8 -0
- package/dist/init/topic-index/index.js +17 -8
- package/dist/init/topic-index/index.js.map +1 -1
- package/dist/invariants/prune.d.ts +6 -4
- package/dist/invariants/prune.js +40 -7
- package/dist/invariants/prune.js.map +1 -1
- package/dist/mcp/schemas.d.ts +26 -0
- package/dist/mcp/schemas.js +23 -0
- package/dist/mcp/schemas.js.map +1 -1
- package/dist/mcp/tools/in-scope.js +7 -1
- package/dist/mcp/tools/in-scope.js.map +1 -1
- package/dist/mcp/tools/index.js +3 -0
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp/tools/invariant-get.js +37 -28
- package/dist/mcp/tools/invariant-get.js.map +1 -1
- package/dist/mcp/tools/resolve-attention.d.ts +1 -1
- package/dist/mcp/tools/resolve-attention.js +92 -0
- package/dist/mcp/tools/resolve-attention.js.map +1 -1
- package/dist/mcp/tools/resync.d.ts +19 -0
- package/dist/mcp/tools/resync.js +109 -0
- package/dist/mcp/tools/resync.js.map +1 -0
- package/dist/migrate/migrations/0005-demote-autofilled-brand.d.ts +11 -3
- package/dist/migrate/migrations/0005-demote-autofilled-brand.js +67 -21
- package/dist/migrate/migrations/0005-demote-autofilled-brand.js.map +1 -1
- package/dist/migrate/migrations/0006-prune-sot-align-invariants.d.ts +17 -15
- package/dist/migrate/migrations/0006-prune-sot-align-invariants.js +23 -17
- package/dist/migrate/migrations/0006-prune-sot-align-invariants.js.map +1 -1
- package/dist/migrate/migrations/0008-clean-adoption-scaffolding.d.ts +39 -0
- package/dist/migrate/migrations/0008-clean-adoption-scaffolding.js +206 -0
- package/dist/migrate/migrations/0008-clean-adoption-scaffolding.js.map +1 -0
- package/dist/migrate/registry.js +2 -0
- package/dist/migrate/registry.js.map +1 -1
- package/dist/resync/archive.d.ts +12 -0
- package/dist/resync/archive.js +29 -0
- package/dist/resync/archive.js.map +1 -0
- package/dist/resync/index.d.ts +73 -0
- package/dist/resync/index.js +389 -0
- package/dist/resync/index.js.map +1 -0
- package/dist/resync/recluster.d.ts +59 -0
- package/dist/resync/recluster.js +66 -0
- package/dist/resync/recluster.js.map +1 -0
- package/dist/session-start/build.js +47 -30
- package/dist/session-start/build.js.map +1 -1
- package/dist/session-start/templates.js +4 -4
- package/dist/session-start/templates.js.map +1 -1
- package/package.json +2 -2
- package/templates/.cairn/config/sensors.yaml +1 -1
- package/templates/.cairn/config/workflow.md +5 -12
- package/templates/.cairn/git-hooks/commit-msg +0 -2
- package/templates/.cairn/git-hooks/post-commit +0 -2
- package/templates/.cairn/ground/brand/overview.md +2 -2
- package/templates/.cairn/ground/brand/voice.md +2 -2
- 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
|
-
|
|
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
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
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
|
|
828
|
-
if (!
|
|
829
|
-
continue;
|
|
830
|
-
if (!e.name.endsWith(".draft.md"))
|
|
829
|
+
for (const dir of dirs) {
|
|
830
|
+
if (!existsSync(dir))
|
|
831
831
|
continue;
|
|
832
|
-
|
|
833
|
-
let text;
|
|
832
|
+
let entries;
|
|
834
833
|
try {
|
|
835
|
-
|
|
834
|
+
entries = readdirSync(dir, { withFileTypes: true, encoding: "utf8" });
|
|
836
835
|
}
|
|
837
|
-
catch
|
|
838
|
-
warnings.push(`draft ${e.name} unreadable: ${err instanceof Error ? err.message : String(err)}`);
|
|
836
|
+
catch {
|
|
839
837
|
continue;
|
|
840
838
|
}
|
|
841
|
-
const
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
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) =>
|
|
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
|
-
|
|
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) {
|