@isaacriehm/cairn-core 0.25.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/config-io.d.ts +10 -0
- package/dist/migrate/config-io.js +16 -0
- package/dist/migrate/config-io.js.map +1 -1
- package/dist/migrate/migrations/0005-demote-autofilled-brand.d.ts +34 -0
- package/dist/migrate/migrations/0005-demote-autofilled-brand.js +199 -0
- package/dist/migrate/migrations/0005-demote-autofilled-brand.js.map +1 -0
- package/dist/migrate/migrations/0006-prune-sot-align-invariants.d.ts +27 -0
- package/dist/migrate/migrations/0006-prune-sot-align-invariants.js +55 -0
- package/dist/migrate/migrations/0006-prune-sot-align-invariants.js.map +1 -0
- package/dist/migrate/migrations/0007-collapse-component-dirs.d.ts +24 -0
- package/dist/migrate/migrations/0007-collapse-component-dirs.js +131 -0
- package/dist/migrate/migrations/0007-collapse-component-dirs.js.map +1 -0
- 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 +8 -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,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cairn_resync` — operator-initiated re-discovery that resolves config drift.
|
|
3
|
+
*
|
|
4
|
+
* The config-drift sensor (24h GC) surfaces the gap between declared config and
|
|
5
|
+
* the grown tree as `baseline_finding` nudges; this tool is the verb that turns
|
|
6
|
+
* them into concrete `config.yaml` edits. It writes COMMITTED config, so it is
|
|
7
|
+
* review-class: preview first (default), summarize the proposed edits, get the
|
|
8
|
+
* operator's OK, then call again with `apply: true`. Apply archives the
|
|
9
|
+
* pre-resync config to `.cairn/ground/.archive/` and is idempotent.
|
|
10
|
+
*/
|
|
11
|
+
import { cairnDir } from "@isaacriehm/cairn-state";
|
|
12
|
+
import { runResync, runResyncRecluster } from "../../resync/index.js";
|
|
13
|
+
import { runCuratorEmit, runCuratorWalker } from "../../init/index.js";
|
|
14
|
+
import { requireBootstrap } from "../bootstrap-guard.js";
|
|
15
|
+
import { resyncInput } from "../schemas.js";
|
|
16
|
+
const RECURATE_CAPTURE_SOURCE = "resync-curator";
|
|
17
|
+
export const resyncTool = {
|
|
18
|
+
name: "cairn_resync",
|
|
19
|
+
description: "Resolve surfaced config drift (the config-drift baseline findings) by proposing concrete .cairn/config.yaml edits: add a grown dir to componentDirs, add a new file type to extensions, add an ignored path to off_limits, or drop a dead componentDir. Default previews the edits (mutates nothing) — summarize them and get the operator's OK, then call with apply:true. Apply archives the pre-resync config to .cairn/ground/.archive/ and is idempotent. Pass `area` to scope to one subtree. Pass recluster:true to instead run the LLM re-cluster pass (re-walk prose, Haiku-judge new semantic collisions, rebuild topic-index + anchor-map) — opt-in, spends Haiku on new prose only; preview first, then apply:true to overwrite the (archived) maps. Pass recurate:'walk' (with `area`) to build the curator corpus for re-curation, then — after the cairn-resync skill dispatches curator-map/reduce subagents — recurate:'emit' to write the resulting DEC/INV drafts to _inbox/ (drained via cairn-attention). recurate is skill-driven; don't call it directly.",
|
|
20
|
+
inputSchema: resyncInput,
|
|
21
|
+
handler: async (ctx, input) => {
|
|
22
|
+
const block = requireBootstrap(ctx.repoRoot);
|
|
23
|
+
if (block !== null)
|
|
24
|
+
return block;
|
|
25
|
+
const apply = input.apply === true;
|
|
26
|
+
if (input.recurate === "walk") {
|
|
27
|
+
const w = await runCuratorWalker({
|
|
28
|
+
repoRoot: ctx.repoRoot,
|
|
29
|
+
...(input.area !== undefined ? { area: input.area } : {}),
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
ok: true,
|
|
33
|
+
mode: "recurate-walk",
|
|
34
|
+
area: input.area ?? null,
|
|
35
|
+
curator_dir: cairnDir(ctx.repoRoot, "init", "curator"),
|
|
36
|
+
shards_path: w.shards_path,
|
|
37
|
+
corpus_path: w.corpus_path,
|
|
38
|
+
records_total: w.records_total,
|
|
39
|
+
records_by_kind: w.records_by_kind,
|
|
40
|
+
shards: w.shards,
|
|
41
|
+
note: w.shards === 0
|
|
42
|
+
? "no curatable prose in this area — nothing to dispatch; skip emit"
|
|
43
|
+
: "dispatch curator-map (rounds of 4) + curator-reduce over the shard plan, then call again with recurate:'emit'",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (input.recurate === "emit") {
|
|
47
|
+
const e = await runCuratorEmit({
|
|
48
|
+
repoRoot: ctx.repoRoot,
|
|
49
|
+
draft: true,
|
|
50
|
+
captureSource: RECURATE_CAPTURE_SOURCE,
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
ok: true,
|
|
54
|
+
mode: "recurate-emit",
|
|
55
|
+
dec_drafts: e.decsWritten.map((d) => ({ id: d.id, path: d.path, title: d.title })),
|
|
56
|
+
inv_drafts: e.invsWritten.map((d) => ({ id: d.id, path: d.path, title: d.title })),
|
|
57
|
+
dropped: e.dropped,
|
|
58
|
+
drop_reasons: e.dropReasons,
|
|
59
|
+
note: "DEC/INV drafts written to _inbox/ — drain via cairn-attention (accept graduates; reject archives)",
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (input.recluster === true) {
|
|
63
|
+
const r = await runResyncRecluster({ repoRoot: ctx.repoRoot, dryRun: !apply });
|
|
64
|
+
return {
|
|
65
|
+
ok: true,
|
|
66
|
+
mode: "recluster",
|
|
67
|
+
dry_run: r.dryRun,
|
|
68
|
+
applied: r.applied,
|
|
69
|
+
topics_before: r.topicsBefore,
|
|
70
|
+
topics_after: r.topicsAfter,
|
|
71
|
+
block_count: r.blockCount,
|
|
72
|
+
judge_calls: r.judgeCalls,
|
|
73
|
+
judge_calls_fresh: r.judgeFresh,
|
|
74
|
+
judge_calls_cached: r.judgeCached,
|
|
75
|
+
judge_calls_errors: r.judgeErrors,
|
|
76
|
+
archived_maps: r.archivedMaps,
|
|
77
|
+
note: r.dryRun
|
|
78
|
+
? "preview only — re-walked + judged but wrote no map; call again with apply:true (and recluster:true) to overwrite the archived maps"
|
|
79
|
+
: undefined,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const result = runResync({
|
|
83
|
+
repoRoot: ctx.repoRoot,
|
|
84
|
+
dryRun: !apply,
|
|
85
|
+
...(input.area !== undefined ? { area: input.area } : {}),
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
ok: true,
|
|
89
|
+
dry_run: result.dryRun,
|
|
90
|
+
applied: result.applied,
|
|
91
|
+
proposals: result.proposals.map((p) => ({
|
|
92
|
+
kind: p.kind,
|
|
93
|
+
workspace: p.workspace,
|
|
94
|
+
value: p.value,
|
|
95
|
+
from: p.from,
|
|
96
|
+
detail: p.detail,
|
|
97
|
+
})),
|
|
98
|
+
skipped: result.skipped,
|
|
99
|
+
archived_config: result.archivedConfig,
|
|
100
|
+
archived_entities: result.archivedEntities,
|
|
101
|
+
note: result.proposals.length === 0
|
|
102
|
+
? "no config drift to resolve — config is in sync with the tree"
|
|
103
|
+
: result.dryRun
|
|
104
|
+
? "preview only — call again with apply:true to write these edits"
|
|
105
|
+
: undefined,
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=resync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resync.js","sourceRoot":"","sources":["../../../src/mcp/tools/resync.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,6 +18,16 @@ export declare function loadConfigDoc(repoRoot: string): Document | null;
|
|
|
18
18
|
export declare function readConfigPin(repoRoot: string, doc?: Document | null): string | null;
|
|
19
19
|
/** Set the `cairn_version` pin. No-op (returns false) when already equal. */
|
|
20
20
|
export declare function writeConfigPin(repoRoot: string, version: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Write a mutated config `Document` back to disk. The migration system was
|
|
23
|
+
* built for top-level key edits (drop a dead key, set the pin); content
|
|
24
|
+
* migrations that need to repair a NESTED structure (e.g. collapse a
|
|
25
|
+
* workspace's `componentDirs`) mutate the `Document` via `loadConfigDoc` +
|
|
26
|
+
* `doc.getIn` / `doc.setIn` and persist with this. Whole-file rewrite under
|
|
27
|
+
* the caller's migrate lock; key order + comments are preserved by the yaml
|
|
28
|
+
* Document API. Returns false when the config is absent.
|
|
29
|
+
*/
|
|
30
|
+
export declare function writeConfigDoc(repoRoot: string, doc: Document): boolean;
|
|
21
31
|
/** Which of `keys` are present as top-level config keys. */
|
|
22
32
|
export declare function configHasKeys(repoRoot: string, keys: readonly string[], doc?: Document | null): string[];
|
|
23
33
|
/** Delete top-level `keys`; returns the keys actually removed. */
|
|
@@ -46,6 +46,22 @@ export function writeConfigPin(repoRoot, version) {
|
|
|
46
46
|
writeFileSync(p, doc.toString(), "utf8");
|
|
47
47
|
return true;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Write a mutated config `Document` back to disk. The migration system was
|
|
51
|
+
* built for top-level key edits (drop a dead key, set the pin); content
|
|
52
|
+
* migrations that need to repair a NESTED structure (e.g. collapse a
|
|
53
|
+
* workspace's `componentDirs`) mutate the `Document` via `loadConfigDoc` +
|
|
54
|
+
* `doc.getIn` / `doc.setIn` and persist with this. Whole-file rewrite under
|
|
55
|
+
* the caller's migrate lock; key order + comments are preserved by the yaml
|
|
56
|
+
* Document API. Returns false when the config is absent.
|
|
57
|
+
*/
|
|
58
|
+
export function writeConfigDoc(repoRoot, doc) {
|
|
59
|
+
const p = configPath(repoRoot);
|
|
60
|
+
if (!existsSync(p))
|
|
61
|
+
return false;
|
|
62
|
+
writeFileSync(p, doc.toString(), "utf8");
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
49
65
|
/** Which of `keys` are present as top-level config keys. */
|
|
50
66
|
export function configHasKeys(repoRoot, keys, doc) {
|
|
51
67
|
const d = doc !== undefined ? doc : loadConfigDoc(repoRoot);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-io.js","sourceRoot":"","sources":["../../src/migrate/config-io.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config-io.js","sourceRoot":"","sources":["../../src/migrate/config-io.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAElE,OAAO,EAAE,aAAa,EAAiB,MAAM,MAAM,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC/B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAChC,IAAI,CAAC;QACH,OAAO,aAAa,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,GAAqB;IACnE,MAAM,CAAC,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC5D,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACjC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,OAAe;IAC9D,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC/B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACjC,MAAM,GAAG,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,IAAI,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IACvD,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IAClC,aAAa,CAAC,CAAC,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,GAAa;IAC5D,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC/B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACjC,aAAa,CAAC,CAAC,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,aAAa,CAC3B,QAAgB,EAChB,IAAuB,EACvB,GAAqB;IAErB,MAAM,CAAC,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC5D,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACtC,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,IAAuB;IACxE,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC/B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,aAAa,CAAC,CAAC,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC;IACjE,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0005 — demote auto-filled brand drafts back to `status: draft`.
|
|
3
|
+
*
|
|
4
|
+
* Before 0.25.0, the adoption brand step DEFAULTED to auto-fill and, on
|
|
5
|
+
* apply, flipped each written file `draft → current`. SessionStart then
|
|
6
|
+
* injected every `current` brand file as authoritative voice — so a
|
|
7
|
+
* generic, machine-written first draft (or the mechanical fallback when
|
|
8
|
+
* the Haiku derive timed out) burned context every session as if the
|
|
9
|
+
* operator had confirmed it.
|
|
10
|
+
*
|
|
11
|
+
* 0.25.0 fixed this going forward (auto-fill writes `draft`; SessionStart
|
|
12
|
+
* injects only confirmed brand). This migration repairs EXISTING repos:
|
|
13
|
+
* it finds brand/product docs that are provably auto-generated yet marked
|
|
14
|
+
* `current`/`accepted`, and demotes them to `draft` so they stop being
|
|
15
|
+
* injected. Operator-written brand is left alone; a demoted file is one
|
|
16
|
+
* frontmatter edit (or a re-run of brand setup) away from `current` again.
|
|
17
|
+
*
|
|
18
|
+
* The 0.26.0 detection only caught the mechanical fallback (fixed marker
|
|
19
|
+
* strings) and the byte-identical overview/positioning pair — it missed
|
|
20
|
+
* Haiku-derived auto-fill (voice.md / personas.yaml), which is worded
|
|
21
|
+
* freshly each run. 0.27.0 adds a co-generation cohort channel: once the
|
|
22
|
+
* overview≡positioning identity proves the pass auto-filled brand, every
|
|
23
|
+
* confirmed doc stamped with the same `generated` timestamp is demoted too.
|
|
24
|
+
*
|
|
25
|
+
* `review`-class: it rewrites committed ground state, so it surfaces for
|
|
26
|
+
* the operator and applies via `cairn migrate` — never silently.
|
|
27
|
+
*
|
|
28
|
+
* The cohort channel ships in 0.27.0, so `introducedIn` advances to 0.27.0:
|
|
29
|
+
* a repo that already ran the 0.26.0 pass (demoting only the pair) must
|
|
30
|
+
* re-evaluate to catch the co-generated siblings. `detect()` carries
|
|
31
|
+
* correctness.
|
|
32
|
+
*/
|
|
33
|
+
import type { Migration } from "../types.js";
|
|
34
|
+
export declare const demoteAutofilledBrand: Migration;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0005 — demote auto-filled brand drafts back to `status: draft`.
|
|
3
|
+
*
|
|
4
|
+
* Before 0.25.0, the adoption brand step DEFAULTED to auto-fill and, on
|
|
5
|
+
* apply, flipped each written file `draft → current`. SessionStart then
|
|
6
|
+
* injected every `current` brand file as authoritative voice — so a
|
|
7
|
+
* generic, machine-written first draft (or the mechanical fallback when
|
|
8
|
+
* the Haiku derive timed out) burned context every session as if the
|
|
9
|
+
* operator had confirmed it.
|
|
10
|
+
*
|
|
11
|
+
* 0.25.0 fixed this going forward (auto-fill writes `draft`; SessionStart
|
|
12
|
+
* injects only confirmed brand). This migration repairs EXISTING repos:
|
|
13
|
+
* it finds brand/product docs that are provably auto-generated yet marked
|
|
14
|
+
* `current`/`accepted`, and demotes them to `draft` so they stop being
|
|
15
|
+
* injected. Operator-written brand is left alone; a demoted file is one
|
|
16
|
+
* frontmatter edit (or a re-run of brand setup) away from `current` again.
|
|
17
|
+
*
|
|
18
|
+
* The 0.26.0 detection only caught the mechanical fallback (fixed marker
|
|
19
|
+
* strings) and the byte-identical overview/positioning pair — it missed
|
|
20
|
+
* Haiku-derived auto-fill (voice.md / personas.yaml), which is worded
|
|
21
|
+
* freshly each run. 0.27.0 adds a co-generation cohort channel: once the
|
|
22
|
+
* overview≡positioning identity proves the pass auto-filled brand, every
|
|
23
|
+
* confirmed doc stamped with the same `generated` timestamp is demoted too.
|
|
24
|
+
*
|
|
25
|
+
* `review`-class: it rewrites committed ground state, so it surfaces for
|
|
26
|
+
* the operator and applies via `cairn migrate` — never silently.
|
|
27
|
+
*
|
|
28
|
+
* The cohort channel ships in 0.27.0, so `introducedIn` advances to 0.27.0:
|
|
29
|
+
* a repo that already ran the 0.26.0 pass (demoting only the pair) must
|
|
30
|
+
* re-evaluate to catch the co-generated siblings. `detect()` carries
|
|
31
|
+
* correctness.
|
|
32
|
+
*/
|
|
33
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
34
|
+
import { cairnDir, parseFrontmatter } from "@isaacriehm/cairn-state";
|
|
35
|
+
/** Distinctive substrings only the pre-0.25.0 mechanical fallback emits. */
|
|
36
|
+
const VOICE_MARKER = "Match the existing tone in CLAUDE.md / AGENTS.md if those files set a register";
|
|
37
|
+
const AVOID_MARKER = 'Marketing fluff ("world-class", "revolutionary", "game-changing")';
|
|
38
|
+
const PERSONAS_MARKER = "Refine when adding consumer-facing or external personas";
|
|
39
|
+
function brandDocs(repoRoot) {
|
|
40
|
+
return [
|
|
41
|
+
{ rel: "brand/overview.md", abs: cairnDir(repoRoot, "ground", "brand", "overview.md") },
|
|
42
|
+
{ rel: "brand/voice.md", abs: cairnDir(repoRoot, "ground", "brand", "voice.md") },
|
|
43
|
+
{ rel: "product/positioning.md", abs: cairnDir(repoRoot, "ground", "product", "positioning.md") },
|
|
44
|
+
{ rel: "product/personas.yaml", abs: cairnDir(repoRoot, "ground", "product", "personas.yaml") },
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
function readDoc(abs) {
|
|
48
|
+
if (!existsSync(abs))
|
|
49
|
+
return null;
|
|
50
|
+
let raw;
|
|
51
|
+
try {
|
|
52
|
+
raw = readFileSync(abs, "utf8");
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
// personas.yaml is pure YAML (status as a top-level key, no `---` fence);
|
|
58
|
+
// the brand .md files use frontmatter. parseFrontmatter handles the .md;
|
|
59
|
+
// for the .yaml fall back to a status-line scan over the whole text.
|
|
60
|
+
const parsed = parseFrontmatter(raw);
|
|
61
|
+
const fm = (parsed.frontmatter ?? {});
|
|
62
|
+
let status = typeof fm["status"] === "string" ? fm["status"] : null;
|
|
63
|
+
if (status === null) {
|
|
64
|
+
const m = raw.match(/^status:\s*(\S+)\s*$/m);
|
|
65
|
+
status = m?.[1] ?? null;
|
|
66
|
+
}
|
|
67
|
+
let generated = typeof fm["generated"] === "string" ? fm["generated"] : null;
|
|
68
|
+
if (generated === null) {
|
|
69
|
+
const g = raw.match(/^generated:\s*(\S+)\s*$/m);
|
|
70
|
+
generated = g?.[1] ?? null;
|
|
71
|
+
}
|
|
72
|
+
return { status, generated, body: parsed.body, raw };
|
|
73
|
+
}
|
|
74
|
+
const CONFIRMED = new Set(["current", "accepted"]);
|
|
75
|
+
/** Strip headings + collapse whitespace, matching SessionStart's dedup. */
|
|
76
|
+
function normalize(s) {
|
|
77
|
+
return s
|
|
78
|
+
.replace(/^\s*#{1,6}\s+.*$/gm, "")
|
|
79
|
+
.replace(/\s+/g, " ")
|
|
80
|
+
.trim();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Repo-relative labels of confirmed brand docs that are provably the
|
|
84
|
+
* pre-0.25.0 auto-fill output. Two evidence channels:
|
|
85
|
+
*
|
|
86
|
+
* 1. Per-doc markers — voice.md / personas.yaml carrying a mechanical
|
|
87
|
+
* fallback's distinctive string. Precise but narrow: it ONLY catches
|
|
88
|
+
* the timeout fallback, never the Haiku-derived auto-fill (which is
|
|
89
|
+
* worded freshly each run and so carries no fixed marker).
|
|
90
|
+
*
|
|
91
|
+
* 2. Co-generation cohort — overview.md and positioning.md with
|
|
92
|
+
* byte-identical bodies is near-certain auto-fill proof (a hand-author
|
|
93
|
+
* doesn't write the exact same domain summary into two files). When
|
|
94
|
+
* that fires, every confirmed brand doc stamped with the SAME
|
|
95
|
+
* `generated` timestamp came out of the same automated pass and is
|
|
96
|
+
* demoted too. This is what catches a Haiku-derived voice.md the
|
|
97
|
+
* marker channel misses. The identity check is status-independent —
|
|
98
|
+
* an earlier run may have already demoted the pair to draft, yet its
|
|
99
|
+
* still-confirmed siblings must follow.
|
|
100
|
+
*
|
|
101
|
+
* A demoted file is one frontmatter edit from `current` again, so the
|
|
102
|
+
* asymmetry favors demoting: a false demote costs one keystroke, a false
|
|
103
|
+
* keep burns context every session as machine-written "authoritative" voice.
|
|
104
|
+
*/
|
|
105
|
+
function autofilledConfirmed(repoRoot) {
|
|
106
|
+
const docs = brandDocs(repoRoot);
|
|
107
|
+
const byRel = new Map();
|
|
108
|
+
for (const d of docs)
|
|
109
|
+
byRel.set(d.rel, readDoc(d.abs));
|
|
110
|
+
const hits = new Set();
|
|
111
|
+
const get = (rel) => byRel.get(rel) ?? null;
|
|
112
|
+
const confirmed = (rel) => {
|
|
113
|
+
const p = get(rel);
|
|
114
|
+
return p !== null && p.status !== null && CONFIRMED.has(p.status) ? p : null;
|
|
115
|
+
};
|
|
116
|
+
// Channel 1 — per-doc mechanical-fallback markers.
|
|
117
|
+
const voice = confirmed("brand/voice.md");
|
|
118
|
+
if (voice && (voice.body.includes(VOICE_MARKER) || voice.body.includes(AVOID_MARKER))) {
|
|
119
|
+
hits.add("brand/voice.md");
|
|
120
|
+
}
|
|
121
|
+
const personas = confirmed("product/personas.yaml");
|
|
122
|
+
if (personas && personas.raw.includes(PERSONAS_MARKER)) {
|
|
123
|
+
hits.add("product/personas.yaml");
|
|
124
|
+
}
|
|
125
|
+
// Channel 2 — co-generation cohort, proven by the overview≡positioning
|
|
126
|
+
// identity (checked regardless of either doc's current status).
|
|
127
|
+
const overviewAny = get("brand/overview.md");
|
|
128
|
+
const positioningAny = get("product/positioning.md");
|
|
129
|
+
const pairAutofilled = overviewAny !== null &&
|
|
130
|
+
positioningAny !== null &&
|
|
131
|
+
normalize(overviewAny.body).length > 0 &&
|
|
132
|
+
normalize(overviewAny.body) === normalize(positioningAny.body);
|
|
133
|
+
if (pairAutofilled) {
|
|
134
|
+
// Cohort timestamp — the pass stamps every co-generated doc identically.
|
|
135
|
+
const cohortGen = overviewAny.generated ?? positioningAny.generated;
|
|
136
|
+
// overview/positioning are themselves cohort members; demote if still confirmed.
|
|
137
|
+
if (confirmed("brand/overview.md"))
|
|
138
|
+
hits.add("brand/overview.md");
|
|
139
|
+
if (confirmed("product/positioning.md"))
|
|
140
|
+
hits.add("product/positioning.md");
|
|
141
|
+
// A confirmed voice.md sharing the cohort timestamp is the same pass's
|
|
142
|
+
// output — the timestamp guard spares a voice the operator hand-wrote later.
|
|
143
|
+
if (voice && cohortGen !== null && voice.generated === cohortGen) {
|
|
144
|
+
hits.add("brand/voice.md");
|
|
145
|
+
}
|
|
146
|
+
// personas.yaml carries no `generated`; it's the lowest-value, most
|
|
147
|
+
// placeholder-prone doc, so the pair-proof alone demotes a confirmed one.
|
|
148
|
+
if (personas)
|
|
149
|
+
hits.add("product/personas.yaml");
|
|
150
|
+
}
|
|
151
|
+
return [...hits];
|
|
152
|
+
}
|
|
153
|
+
/** Rewrite the first `status: current|accepted` line to `status: draft`. */
|
|
154
|
+
function demoteToDraft(abs) {
|
|
155
|
+
let raw;
|
|
156
|
+
try {
|
|
157
|
+
raw = readFileSync(abs, "utf8");
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
const next = raw.replace(/^status:\s*(current|accepted)\s*$/m, "status: draft");
|
|
163
|
+
if (next === raw)
|
|
164
|
+
return false;
|
|
165
|
+
try {
|
|
166
|
+
writeFileSync(abs, next, "utf8");
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
export const demoteAutofilledBrand = {
|
|
174
|
+
id: "0005-demote-autofilled-brand",
|
|
175
|
+
introducedIn: "0.27.0",
|
|
176
|
+
describe: "Demote auto-generated brand (marked confirmed before 0.25.0) back to status: draft so machine-written voice/positioning/personas stops being injected every session — now catches the full co-generated cohort, not just the mechanical fallback",
|
|
177
|
+
class: "review",
|
|
178
|
+
detect(repoRoot) {
|
|
179
|
+
return autofilledConfirmed(repoRoot).length > 0;
|
|
180
|
+
},
|
|
181
|
+
apply(repoRoot) {
|
|
182
|
+
const docs = brandDocs(repoRoot);
|
|
183
|
+
const relToAbs = new Map(docs.map((d) => [d.rel, d.abs]));
|
|
184
|
+
const targets = autofilledConfirmed(repoRoot);
|
|
185
|
+
const demoted = [];
|
|
186
|
+
for (const rel of targets) {
|
|
187
|
+
const abs = relToAbs.get(rel);
|
|
188
|
+
if (abs !== undefined && demoteToDraft(abs))
|
|
189
|
+
demoted.push(rel);
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
changed: demoted.length > 0,
|
|
193
|
+
detail: demoted.length > 0
|
|
194
|
+
? `demoted ${demoted.length} auto-generated brand doc(s) to draft: ${demoted.join(", ")} — review and set status: current on any you actually wrote`
|
|
195
|
+
: "no auto-generated confirmed brand docs found",
|
|
196
|
+
};
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
//# sourceMappingURL=0005-demote-autofilled-brand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"0005-demote-autofilled-brand.js","sourceRoot":"","sources":["../../../src/migrate/migrations/0005-demote-autofilled-brand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0006 — prune junk Layer-A (sot-align) invariants from existing repos.
|
|
3
|
+
*
|
|
4
|
+
* The sot-align hook minted an "invariant" from almost any prose block —
|
|
5
|
+
* banners, separators, class/endpoint descriptions, test notes. The 0.23.0
|
|
6
|
+
* creation gate cut the worst of it, but a modal buried anywhere in a
|
|
7
|
+
* multi-line block still slipped through, so even post-gate repos accreted
|
|
8
|
+
* box-drawing-titled artifacts and test-fixture comments as "active
|
|
9
|
+
* invariants". 0.27.0 sharpens both the gate (skip test files) and this
|
|
10
|
+
* prune (statement-scoped shape + test-source + separator-title rejects).
|
|
11
|
+
*
|
|
12
|
+
* `detect`/`apply` wrap the same `pruneInvariants` surgical core the CLI
|
|
13
|
+
* uses: it touches ONLY `capture_source: layer-a-sot-align` invariants —
|
|
14
|
+
* archiving those captured from a test/fixture file, titled with a
|
|
15
|
+
* separator, or with no constraint shape in their statement — to
|
|
16
|
+
* `.cairn/ground/.archive/` (recoverable), rebuilding the ledger once at
|
|
17
|
+
* the end. Curated DEC/INV are never touched. For a full reset of the
|
|
18
|
+
* legacy corpus the operator can run `cairn invariants prune --all`.
|
|
19
|
+
*
|
|
20
|
+
* `review`-class: it archives committed ground state, so it surfaces for
|
|
21
|
+
* the operator and applies via `cairn migrate`. The sharpened gate ships
|
|
22
|
+
* in 0.27.0, so `introducedIn` advances to 0.27.0 — a repo that already
|
|
23
|
+
* ran the weaker 0.26.0 pass must re-evaluate; `detect()` carries
|
|
24
|
+
* correctness.
|
|
25
|
+
*/
|
|
26
|
+
import type { Migration } from "../types.js";
|
|
27
|
+
export declare const pruneSotAlignInvariants: Migration;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0006 — prune junk Layer-A (sot-align) invariants from existing repos.
|
|
3
|
+
*
|
|
4
|
+
* The sot-align hook minted an "invariant" from almost any prose block —
|
|
5
|
+
* banners, separators, class/endpoint descriptions, test notes. The 0.23.0
|
|
6
|
+
* creation gate cut the worst of it, but a modal buried anywhere in a
|
|
7
|
+
* multi-line block still slipped through, so even post-gate repos accreted
|
|
8
|
+
* box-drawing-titled artifacts and test-fixture comments as "active
|
|
9
|
+
* invariants". 0.27.0 sharpens both the gate (skip test files) and this
|
|
10
|
+
* prune (statement-scoped shape + test-source + separator-title rejects).
|
|
11
|
+
*
|
|
12
|
+
* `detect`/`apply` wrap the same `pruneInvariants` surgical core the CLI
|
|
13
|
+
* uses: it touches ONLY `capture_source: layer-a-sot-align` invariants —
|
|
14
|
+
* archiving those captured from a test/fixture file, titled with a
|
|
15
|
+
* separator, or with no constraint shape in their statement — to
|
|
16
|
+
* `.cairn/ground/.archive/` (recoverable), rebuilding the ledger once at
|
|
17
|
+
* the end. Curated DEC/INV are never touched. For a full reset of the
|
|
18
|
+
* legacy corpus the operator can run `cairn invariants prune --all`.
|
|
19
|
+
*
|
|
20
|
+
* `review`-class: it archives committed ground state, so it surfaces for
|
|
21
|
+
* the operator and applies via `cairn migrate`. The sharpened gate ships
|
|
22
|
+
* in 0.27.0, so `introducedIn` advances to 0.27.0 — a repo that already
|
|
23
|
+
* ran the weaker 0.26.0 pass must re-evaluate; `detect()` carries
|
|
24
|
+
* correctness.
|
|
25
|
+
*/
|
|
26
|
+
import { pruneInvariants } from "../../invariants/prune.js";
|
|
27
|
+
export const pruneSotAlignInvariants = {
|
|
28
|
+
id: "0006-prune-sot-align-invariants",
|
|
29
|
+
// 0.27.0: the surgical gate was sharpened (statement-scoped shape +
|
|
30
|
+
// test-source + separator-title rejects). A repo that already ran the
|
|
31
|
+
// weaker 0.26.0 pass must re-evaluate, so `introducedIn` advances to the
|
|
32
|
+
// ship version — `detect()` (a dry-run prune) stays the correctness floor.
|
|
33
|
+
introducedIn: "0.27.0",
|
|
34
|
+
describe: "Archive junk Layer-A (sot-align) invariants the creation gate would reject today — test/fixture captures, separator-titled artifacts, and entries with no rule in their statement; curated DEC/INV untouched",
|
|
35
|
+
class: "review",
|
|
36
|
+
detect(repoRoot) {
|
|
37
|
+
try {
|
|
38
|
+
return pruneInvariants({ repoRoot, dryRun: true }).pruned.length > 0;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
apply(repoRoot) {
|
|
45
|
+
const result = pruneInvariants({ repoRoot });
|
|
46
|
+
const n = result.pruned.length;
|
|
47
|
+
return {
|
|
48
|
+
changed: n > 0,
|
|
49
|
+
detail: n > 0
|
|
50
|
+
? `archived ${n} shapeless sot-align invariant(s) of ${result.sotAlignTotal} eligible (${result.kept} kept) → .cairn/ground/.archive/`
|
|
51
|
+
: "no junk sot-align invariants found",
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=0006-prune-sot-align-invariants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"0006-prune-sot-align-invariants.js","sourceRoot":"","sources":["../../../src/migrate/migrations/0006-prune-sot-align-invariants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAG5D,MAAM,CAAC,MAAM,uBAAuB,GAAc;IAChD,EAAE,EAAE,iCAAiC;IACrC,oEAAoE;IACpE,sEAAsE;IACtE,yEAAyE;IACzE,2EAA2E;IAC3E,YAAY,EAAE,QAAQ;IACtB,QAAQ,EACN,8MAA8M;IAChN,KAAK,EAAE,QAAQ;IACf,MAAM,CAAC,QAAgB;QACrB,IAAI,CAAC;YACH,OAAO,eAAe,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QACvE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,KAAK,CAAC,QAAgB;QACpB,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7C,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;QAC/B,OAAO;YACL,OAAO,EAAE,CAAC,GAAG,CAAC;YACd,MAAM,EACJ,CAAC,GAAG,CAAC;gBACH,CAAC,CAAC,YAAY,CAAC,wCAAwC,MAAM,CAAC,aAAa,cAAc,MAAM,CAAC,IAAI,kCAAkC;gBACtI,CAAC,CAAC,oCAAoC;SAC3C,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0007 — collapse redundant nested `componentDirs`.
|
|
3
|
+
*
|
|
4
|
+
* The pre-0.22.4 component-layout detector could enumerate a directory AND
|
|
5
|
+
* its sub-directories (and even individual leaf component folders) as
|
|
6
|
+
* separate `componentDirs`. On a repo adopted in that era a workspace's
|
|
7
|
+
* `componentDirs` can carry dozens of entries where a directory is already
|
|
8
|
+
* covered by an ancestor that is also listed — e.g. both `a/b` and
|
|
9
|
+
* `a/b/c`, plus `a/b/c/Leaf`.
|
|
10
|
+
*
|
|
11
|
+
* The component walk recurses (`walkFs`) and collection dedups visited
|
|
12
|
+
* files (`collectComponents`'s `seen` set), so the redundant entries change
|
|
13
|
+
* nothing at runtime — they are pure config bloat. This migration removes
|
|
14
|
+
* any `componentDir` that is a descendant (or exact duplicate) of another
|
|
15
|
+
* `componentDir` in the same workspace, keeping only the shallowest
|
|
16
|
+
* ancestors. The collected component set is byte-identical afterward.
|
|
17
|
+
*
|
|
18
|
+
* `review`-class: it rewrites the operator's curated `config.yaml`, so it
|
|
19
|
+
* surfaces the count and applies via `cairn migrate` rather than silently
|
|
20
|
+
* rewriting their list. Ships in 0.26.0 → `introducedIn` 0.26.0; `detect()`
|
|
21
|
+
* carries correctness for any older pin.
|
|
22
|
+
*/
|
|
23
|
+
import type { Migration } from "../types.js";
|
|
24
|
+
export declare const collapseComponentDirs: Migration;
|