@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,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":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,MAAM,uBAAuB,GAAG,gBAAgB,CAAC;AASjD,MAAM,CAAC,MAAM,UAAU,GAAmB;IACxC,IAAI,EAAE,cAAc;IACpB,WAAW,EACT,khCAAkhC;IACphC,WAAW,EAAE,WAAW;IACxB,OAAO,EAAE,KAAK,EAAE,GAAe,EAAE,KAAY,EAAoB,EAAE;QACjE,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QACjC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC;QAEnC,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YAC9B,MAAM,CAAC,GAAG,MAAM,gBAAgB,CAAC;gBAC/B,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC1D,CAAC,CAAC;YACH,OAAO;gBACL,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,eAAe;gBACrB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI;gBACxB,WAAW,EAAE,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC;gBACtD,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,aAAa,EAAE,CAAC,CAAC,aAAa;gBAC9B,eAAe,EAAE,CAAC,CAAC,eAAe;gBAClC,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,IAAI,EACF,CAAC,CAAC,MAAM,KAAK,CAAC;oBACZ,CAAC,CAAC,kEAAkE;oBACpE,CAAC,CAAC,+GAA+G;aACtH,CAAC;QACJ,CAAC;QAED,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YAC9B,MAAM,CAAC,GAAG,MAAM,cAAc,CAAC;gBAC7B,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,KAAK,EAAE,IAAI;gBACX,aAAa,EAAE,uBAAuB;aACvC,CAAC,CAAC;YACH,OAAO;gBACL,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,eAAe;gBACrB,UAAU,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;gBAClF,UAAU,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;gBAClF,OAAO,EAAE,CAAC,CAAC,OAAO;gBAClB,YAAY,EAAE,CAAC,CAAC,WAAW;gBAC3B,IAAI,EAAE,mGAAmG;aAC1G,CAAC;QACJ,CAAC;QAED,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC7B,MAAM,CAAC,GAAG,MAAM,kBAAkB,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC;YAC/E,OAAO;gBACL,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,CAAC,CAAC,MAAM;gBACjB,OAAO,EAAE,CAAC,CAAC,OAAO;gBAClB,aAAa,EAAE,CAAC,CAAC,YAAY;gBAC7B,YAAY,EAAE,CAAC,CAAC,WAAW;gBAC3B,WAAW,EAAE,CAAC,CAAC,UAAU;gBACzB,WAAW,EAAE,CAAC,CAAC,UAAU;gBACzB,iBAAiB,EAAE,CAAC,CAAC,UAAU;gBAC/B,kBAAkB,EAAE,CAAC,CAAC,WAAW;gBACjC,kBAAkB,EAAE,CAAC,CAAC,WAAW;gBACjC,aAAa,EAAE,CAAC,CAAC,YAAY;gBAC7B,IAAI,EAAE,CAAC,CAAC,MAAM;oBACZ,CAAC,CAAC,oIAAoI;oBACtI,CAAC,CAAC,SAAS;aACd,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,SAAS,CAAC;YACvB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,MAAM,EAAE,CAAC,KAAK;YACd,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1D,CAAC,CAAC;QACH,OAAO;YACL,EAAE,EAAE,IAAI;YACR,OAAO,EAAE,MAAM,CAAC,MAAM;YACtB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACtC,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,MAAM,EAAE,CAAC,CAAC,MAAM;aACjB,CAAC,CAAC;YACH,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,eAAe,EAAE,MAAM,CAAC,cAAc;YACtC,iBAAiB,EAAE,MAAM,CAAC,gBAAgB;YAC1C,IAAI,EACF,MAAM,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;gBAC3B,CAAC,CAAC,8DAA8D;gBAChE,CAAC,CAAC,MAAM,CAAC,MAAM;oBACb,CAAC,CAAC,gEAAgE;oBAClE,CAAC,CAAC,SAAS;SAClB,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -15,12 +15,20 @@
15
15
  * injected. Operator-written brand is left alone; a demoted file is one
16
16
  * frontmatter edit (or a re-run of brand setup) away from `current` again.
17
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
+ *
18
25
  * `review`-class: it rewrites committed ground state, so it surfaces for
19
26
  * the operator and applies via `cairn migrate` — never silently.
20
27
  *
21
- * Ships in 0.26.0, so `introducedIn` is 0.26.0 (not 0.25.0): a repo whose
22
- * pin already advanced to 0.25.0 must still re-evaluate it. `detect()`
23
- * carries correctness.
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.
24
32
  */
25
33
  import type { Migration } from "../types.js";
26
34
  export declare const demoteAutofilledBrand: Migration;
@@ -15,12 +15,20 @@
15
15
  * injected. Operator-written brand is left alone; a demoted file is one
16
16
  * frontmatter edit (or a re-run of brand setup) away from `current` again.
17
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
+ *
18
25
  * `review`-class: it rewrites committed ground state, so it surfaces for
19
26
  * the operator and applies via `cairn migrate` — never silently.
20
27
  *
21
- * Ships in 0.26.0, so `introducedIn` is 0.26.0 (not 0.25.0): a repo whose
22
- * pin already advanced to 0.25.0 must still re-evaluate it. `detect()`
23
- * carries correctness.
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.
24
32
  */
25
33
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
26
34
  import { cairnDir, parseFrontmatter } from "@isaacriehm/cairn-state";
@@ -56,7 +64,12 @@ function readDoc(abs) {
56
64
  const m = raw.match(/^status:\s*(\S+)\s*$/m);
57
65
  status = m?.[1] ?? null;
58
66
  }
59
- return { status, body: parsed.body, raw };
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 };
60
73
  }
61
74
  const CONFIRMED = new Set(["current", "accepted"]);
62
75
  /** Strip headings + collapse whitespace, matching SessionStart's dedup. */
@@ -68,12 +81,26 @@ function normalize(s) {
68
81
  }
69
82
  /**
70
83
  * Repo-relative labels of confirmed brand docs that are provably the
71
- * pre-0.25.0 auto-fill output:
72
- * - voice.md carrying a mechanical-default marker,
73
- * - personas.yaml carrying the placeholder marker,
74
- * - overview.md and positioning.md whose bodies are byte-identical
75
- * (auto-fill wrote the same domain summary to both; a hand-written
76
- * pair would diverge).
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.
77
104
  */
78
105
  function autofilledConfirmed(repoRoot) {
79
106
  const docs = brandDocs(repoRoot);
@@ -81,10 +108,12 @@ function autofilledConfirmed(repoRoot) {
81
108
  for (const d of docs)
82
109
  byRel.set(d.rel, readDoc(d.abs));
83
110
  const hits = new Set();
111
+ const get = (rel) => byRel.get(rel) ?? null;
84
112
  const confirmed = (rel) => {
85
- const p = byRel.get(rel) ?? null;
113
+ const p = get(rel);
86
114
  return p !== null && p.status !== null && CONFIRMED.has(p.status) ? p : null;
87
115
  };
116
+ // Channel 1 — per-doc mechanical-fallback markers.
88
117
  const voice = confirmed("brand/voice.md");
89
118
  if (voice && (voice.body.includes(VOICE_MARKER) || voice.body.includes(AVOID_MARKER))) {
90
119
  hits.add("brand/voice.md");
@@ -93,14 +122,31 @@ function autofilledConfirmed(repoRoot) {
93
122
  if (personas && personas.raw.includes(PERSONAS_MARKER)) {
94
123
  hits.add("product/personas.yaml");
95
124
  }
96
- const overview = confirmed("brand/overview.md");
97
- const positioning = confirmed("product/positioning.md");
98
- if (overview &&
99
- positioning &&
100
- normalize(overview.body).length > 0 &&
101
- normalize(overview.body) === normalize(positioning.body)) {
102
- hits.add("brand/overview.md");
103
- hits.add("product/positioning.md");
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");
104
150
  }
105
151
  return [...hits];
106
152
  }
@@ -126,8 +172,8 @@ function demoteToDraft(abs) {
126
172
  }
127
173
  export const demoteAutofilledBrand = {
128
174
  id: "0005-demote-autofilled-brand",
129
- introducedIn: "0.26.0",
130
- describe: "Demote auto-generated brand drafts (marked confirmed before 0.25.0) back to status: draft so generic voice/positioning stops being injected every session",
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",
131
177
  class: "review",
132
178
  detect(repoRoot) {
133
179
  return autofilledConfirmed(repoRoot).length > 0;
@@ -1 +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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"}
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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"}
@@ -1,25 +1,27 @@
1
1
  /**
2
2
  * 0006 — prune junk Layer-A (sot-align) invariants from existing repos.
3
3
  *
4
- * Before 0.23.0 the sot-align hook minted an "invariant" from almost any
5
- * prose block — banners, separators, class/endpoint descriptions, test
6
- * notes with no structural pre-filter. On a repo adopted in that era the
7
- * invariants ledger was ~97% junk, and every junk entry is surfaced + can
8
- * be cited. 0.23.0 added the creation gate so NEW invariants are real, and
9
- * shipped `cairn invariants prune` to clean the old ones but existing
10
- * adopters had to know to run it by hand. This migration surfaces it.
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
11
  *
12
12
  * `detect`/`apply` wrap the same `pruneInvariants` surgical core the CLI
13
- * uses: it touches ONLY `capture_source: layer-a-sot-align` invariants
14
- * whose statement has no constraint shape the exact bar the creation
15
- * gate now applies and archives them to `.cairn/ground/.archive/`
16
- * (recoverable), rebuilding the ledger once at the end. Curated DEC/INV
17
- * are never touched.
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`.
18
19
  *
19
20
  * `review`-class: it archives committed ground state, so it surfaces for
20
- * the operator and applies via `cairn migrate`. Ships in 0.26.0, so
21
- * `introducedIn` is 0.26.0 — a repo pinned past 0.23.0 must still
22
- * re-evaluate it; `detect()` carries correctness.
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.
23
25
  */
24
26
  import type { Migration } from "../types.js";
25
27
  export declare const pruneSotAlignInvariants: Migration;
@@ -1,31 +1,37 @@
1
1
  /**
2
2
  * 0006 — prune junk Layer-A (sot-align) invariants from existing repos.
3
3
  *
4
- * Before 0.23.0 the sot-align hook minted an "invariant" from almost any
5
- * prose block — banners, separators, class/endpoint descriptions, test
6
- * notes with no structural pre-filter. On a repo adopted in that era the
7
- * invariants ledger was ~97% junk, and every junk entry is surfaced + can
8
- * be cited. 0.23.0 added the creation gate so NEW invariants are real, and
9
- * shipped `cairn invariants prune` to clean the old ones but existing
10
- * adopters had to know to run it by hand. This migration surfaces it.
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
11
  *
12
12
  * `detect`/`apply` wrap the same `pruneInvariants` surgical core the CLI
13
- * uses: it touches ONLY `capture_source: layer-a-sot-align` invariants
14
- * whose statement has no constraint shape the exact bar the creation
15
- * gate now applies and archives them to `.cairn/ground/.archive/`
16
- * (recoverable), rebuilding the ledger once at the end. Curated DEC/INV
17
- * are never touched.
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`.
18
19
  *
19
20
  * `review`-class: it archives committed ground state, so it surfaces for
20
- * the operator and applies via `cairn migrate`. Ships in 0.26.0, so
21
- * `introducedIn` is 0.26.0 — a repo pinned past 0.23.0 must still
22
- * re-evaluate it; `detect()` carries correctness.
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.
23
25
  */
24
26
  import { pruneInvariants } from "../../invariants/prune.js";
25
27
  export const pruneSotAlignInvariants = {
26
28
  id: "0006-prune-sot-align-invariants",
27
- introducedIn: "0.26.0",
28
- describe: "Archive junk Layer-A (sot-align) invariants minted before the 0.23.0 creation gate only shapeless sot-align entries; curated DEC/INV untouched",
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",
29
35
  class: "review",
30
36
  detect(repoRoot) {
31
37
  try {
@@ -1 +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;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAG5D,MAAM,CAAC,MAAM,uBAAuB,GAAc;IAChD,EAAE,EAAE,iCAAiC;IACrC,YAAY,EAAE,QAAQ;IACtB,QAAQ,EACN,kJAAkJ;IACpJ,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"}
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,39 @@
1
+ /**
2
+ * 0008 — clean leaked adoption scaffolding from a project's committed `.cairn/`.
3
+ *
4
+ * The shipped templates leaked Cairn-internal strings into every adopter's
5
+ * committed files that the seed never scrubbed:
6
+ * - workflow.md : a "Project-extension placeholder" meta-comment describing
7
+ * the template's OWN substitution (false the moment init
8
+ * ran) + a dangling `docs/SYSTEM_OVERVIEW.md` pointer (a
9
+ * Cairn-internal doc that never ships to adopters).
10
+ * - sensors.yaml : the "honest-agent invariants stack" framing — internal
11
+ * jargon that means nothing in an adopter's repo.
12
+ * - .gitignore : `PLUGIN_ARCHITECTURE §7/§17`, `docs/FILESYSTEM_LAYOUT.md`,
13
+ * `spec §2.2` — references to Cairn-internal docs that
14
+ * dangle in the adopter's tree.
15
+ * - git-hooks : `Spec: PLUGIN_ARCHITECTURE §17 Layer 1` refs in the
16
+ * commit-msg + post-commit hooks.
17
+ * - personas.yaml: a `DOCS_SPEC.md §3.4` shape ref written by an older
18
+ * brand-setup path (the template itself is clean).
19
+ *
20
+ * Plus synthetic, template-author timestamps in workflow.md + the brand /
21
+ * product files (`2026-05-02T…`, `2026-05-04T…`). A fake `verified-at` is one
22
+ * the freshness system can't trust, so we replace each synthetic stamp with
23
+ * the file's real git first-commit author-date — the true adoption time,
24
+ * identical on every clone. If git can't resolve it (untracked / ghost) the
25
+ * stamp is LEFT rather than fabricated (no clock value → no per-clone churn).
26
+ *
27
+ * The templates now ship clean; this converges existing repos to them.
28
+ *
29
+ * `safe` (not `review`): deterministic, zero-semantic cosmetic edits. The
30
+ * comment scrubs carry none of the matched anchor text (idempotent), and the
31
+ * timestamp swap is content-derived — whoever runs it first commits the
32
+ * canonical clean file and every other clone's `detect()` short-circuits, so
33
+ * there is no churn. Auto-applies on session open (cf. 0001/0004).
34
+ *
35
+ * Ships in 0.27.0 → `introducedIn` 0.27.0; every prior adopter re-evaluates on
36
+ * upgrade. `detect()` carries correctness.
37
+ */
38
+ import type { Migration } from "../types.js";
39
+ export declare const cleanAdoptionScaffolding: Migration;