chapterhouse 0.13.1 → 0.14.1

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 (132) hide show
  1. package/dist/api/route-coverage.test.js +1 -3
  2. package/dist/api/server.js +0 -2
  3. package/dist/api/server.test.js +0 -281
  4. package/dist/config.js +3 -85
  5. package/dist/config.test.js +5 -123
  6. package/dist/copilot/agents.js +13 -10
  7. package/dist/copilot/agents.test.js +10 -11
  8. package/dist/copilot/memory-coordinator.js +12 -227
  9. package/dist/copilot/memory-coordinator.test.js +31 -250
  10. package/dist/copilot/orchestrator.js +8 -66
  11. package/dist/copilot/orchestrator.test.js +9 -467
  12. package/dist/copilot/skills.js +15 -1
  13. package/dist/copilot/system-message.js +9 -15
  14. package/dist/copilot/system-message.test.js +9 -22
  15. package/dist/copilot/tools/index.js +3 -3
  16. package/dist/copilot/tools-deps.js +1 -1
  17. package/dist/copilot/tools.agent.test.js +6 -0
  18. package/dist/copilot/tools.inventory.test.js +1 -14
  19. package/dist/daemon.js +7 -9
  20. package/dist/memory/assets.js +33 -0
  21. package/dist/memory/domains.js +58 -0
  22. package/dist/memory/domains.test.js +47 -0
  23. package/dist/memory/git.js +66 -0
  24. package/dist/memory/git.test.js +32 -0
  25. package/dist/memory/history.js +19 -0
  26. package/dist/memory/hottier.js +32 -0
  27. package/dist/memory/hottier.test.js +33 -0
  28. package/dist/memory/index.js +5 -13
  29. package/dist/memory/instructions.js +17 -0
  30. package/dist/memory/manager.js +92 -0
  31. package/dist/memory/markdown.js +78 -0
  32. package/dist/memory/markdown.test.js +42 -0
  33. package/dist/memory/mutex.js +18 -0
  34. package/dist/memory/path-guard.js +26 -0
  35. package/dist/memory/path-guard.test.js +27 -0
  36. package/dist/memory/paths.js +12 -0
  37. package/dist/memory/reconcile.js +75 -0
  38. package/dist/memory/reconcile.test.js +50 -0
  39. package/dist/memory/scaffold.js +37 -0
  40. package/dist/memory/scaffold.test.js +52 -0
  41. package/dist/memory/tools/commit-wrapper.js +32 -0
  42. package/dist/memory/tools/domains.js +73 -0
  43. package/dist/memory/tools/domains.test.js +66 -0
  44. package/dist/memory/tools/git.js +52 -0
  45. package/dist/memory/tools/index.js +25 -0
  46. package/dist/memory/tools/read.js +101 -0
  47. package/dist/memory/tools/read.test.js +69 -0
  48. package/dist/memory/tools/search.js +103 -0
  49. package/dist/memory/tools/search.test.js +63 -0
  50. package/dist/memory/tools/sessions.js +45 -0
  51. package/dist/memory/tools/sessions.test.js +74 -0
  52. package/dist/memory/tools/shared.js +7 -0
  53. package/dist/memory/tools/write.js +116 -0
  54. package/dist/memory/tools/write.test.js +107 -0
  55. package/dist/memory/walk.js +39 -0
  56. package/dist/store/repositories/sessions.js +40 -0
  57. package/dist/wiki/consolidation.js +3 -31
  58. package/dist/wiki/consolidation.test.js +0 -19
  59. package/memory-assets/domain-skill.md +38 -0
  60. package/memory-assets/seed/cog-meta/improvements.md +8 -0
  61. package/memory-assets/seed/cog-meta/patterns.md +5 -0
  62. package/memory-assets/seed/cog-meta/reflect-cursor.md +4 -0
  63. package/memory-assets/seed/cog-meta/scenario-calibration.md +14 -0
  64. package/memory-assets/seed/cog-meta/self-observations.md +4 -0
  65. package/memory-assets/seed/domains.yml +19 -0
  66. package/memory-assets/seed/glacier/index.md +6 -0
  67. package/memory-assets/seed/hot-memory.md +5 -0
  68. package/memory-assets/seed/link-index.md +6 -0
  69. package/memory-assets/system-instructions.md +214 -0
  70. package/memory-assets/templates/action-items.md +8 -0
  71. package/memory-assets/templates/entities.md +4 -0
  72. package/memory-assets/templates/generic.md +2 -0
  73. package/memory-assets/templates/hot-memory.md +4 -0
  74. package/memory-assets/templates/observations.md +4 -0
  75. package/package.json +2 -1
  76. package/skills/system/evolve/SKILL.md +131 -0
  77. package/skills/system/foresight/SKILL.md +116 -0
  78. package/skills/system/history/SKILL.md +58 -0
  79. package/skills/system/housekeeping/SKILL.md +185 -0
  80. package/skills/system/reflect/SKILL.md +214 -0
  81. package/skills/system/scenario/SKILL.md +198 -0
  82. package/skills/system/setup/SKILL.md +113 -0
  83. package/web/dist/assets/{WikiEdit-CGRxNazp.js → WikiEdit-BTsiBfbC.js} +2 -2
  84. package/web/dist/assets/{WikiEdit-CGRxNazp.js.map → WikiEdit-BTsiBfbC.js.map} +1 -1
  85. package/web/dist/assets/{WikiGraph-eVWNhZS3.js → WikiGraph-COOZbUeH.js} +2 -2
  86. package/web/dist/assets/{WikiGraph-eVWNhZS3.js.map → WikiGraph-COOZbUeH.js.map} +1 -1
  87. package/web/dist/assets/{index-gAvLNEvJ.js → index-aCcfpaLM.js} +101 -101
  88. package/web/dist/assets/index-aCcfpaLM.js.map +1 -0
  89. package/web/dist/index.html +1 -1
  90. package/dist/api/routes/memory.js +0 -475
  91. package/dist/api/routes/memory.test.js +0 -108
  92. package/dist/copilot/tools/memory.js +0 -678
  93. package/dist/copilot/tools.memory.test.js +0 -590
  94. package/dist/memory/action-items.js +0 -100
  95. package/dist/memory/action-items.test.js +0 -83
  96. package/dist/memory/active-scope.js +0 -78
  97. package/dist/memory/active-scope.test.js +0 -80
  98. package/dist/memory/checkpoint-prompt.js +0 -71
  99. package/dist/memory/checkpoint.js +0 -274
  100. package/dist/memory/checkpoint.test.js +0 -275
  101. package/dist/memory/decisions.js +0 -54
  102. package/dist/memory/decisions.test.js +0 -92
  103. package/dist/memory/entities.js +0 -70
  104. package/dist/memory/entities.test.js +0 -65
  105. package/dist/memory/eot.js +0 -459
  106. package/dist/memory/eot.test.js +0 -949
  107. package/dist/memory/hooks.js +0 -149
  108. package/dist/memory/hooks.test.js +0 -325
  109. package/dist/memory/hot-tier.js +0 -283
  110. package/dist/memory/hot-tier.test.js +0 -275
  111. package/dist/memory/housekeeping-scheduler.js +0 -187
  112. package/dist/memory/housekeeping-scheduler.test.js +0 -236
  113. package/dist/memory/housekeeping.js +0 -497
  114. package/dist/memory/housekeeping.test.js +0 -410
  115. package/dist/memory/inbox.js +0 -83
  116. package/dist/memory/inbox.test.js +0 -178
  117. package/dist/memory/migration.js +0 -244
  118. package/dist/memory/migration.test.js +0 -108
  119. package/dist/memory/observations.js +0 -46
  120. package/dist/memory/observations.test.js +0 -86
  121. package/dist/memory/recall.js +0 -269
  122. package/dist/memory/recall.test.js +0 -265
  123. package/dist/memory/reflect.js +0 -273
  124. package/dist/memory/reflect.test.js +0 -256
  125. package/dist/memory/scope-lock.js +0 -26
  126. package/dist/memory/scope-lock.test.js +0 -118
  127. package/dist/memory/scopes.js +0 -89
  128. package/dist/memory/scopes.test.js +0 -176
  129. package/dist/memory/tiering.js +0 -223
  130. package/dist/memory/tiering.test.js +0 -323
  131. package/dist/memory/types.js +0 -2
  132. package/web/dist/assets/index-gAvLNEvJ.js.map +0 -1
@@ -0,0 +1,116 @@
1
+ // Write tools: cog_write, cog_edit, cog_append, cog_move. Each mutating tool
2
+ // runs through withMemoryWrite (write-lock + auto-commit).
3
+ // Ported from chgo's tools_write.go.
4
+ import { defineTool } from "@github/copilot-sdk";
5
+ import { z } from "zod";
6
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
7
+ import { dirname } from "path";
8
+ import { resolveMemoryPath } from "../path-guard.js";
9
+ import { hasL0Header } from "../markdown.js";
10
+ import { withMemoryWrite } from "./commit-wrapper.js";
11
+ import { toolError } from "./shared.js";
12
+ export function createWriteTools(manager) {
13
+ const root = manager.paths.memoryRoot;
14
+ return [
15
+ defineTool("cog_write", {
16
+ description: "Create or overwrite a memory file. A .md file must begin with an " +
17
+ "<!-- L0: summary --> line within the first few lines.",
18
+ parameters: z.object({
19
+ path: z.string().describe("File path relative to the memory root"),
20
+ content: z.string().describe("Full file content"),
21
+ }),
22
+ handler: async (args) => {
23
+ try {
24
+ const abs = resolveMemoryPath(root, args.path);
25
+ if (abs.endsWith(".md") && !hasL0Header(args.content)) {
26
+ return "Error: a .md memory file must begin with an <!-- L0: summary --> line";
27
+ }
28
+ return await withMemoryWrite(manager, `memory: write ${args.path}`, () => {
29
+ mkdirSync(dirname(abs), { recursive: true });
30
+ writeFileSync(abs, args.content, "utf8");
31
+ return `wrote ${args.path}`;
32
+ });
33
+ }
34
+ catch (err) {
35
+ return toolError(err);
36
+ }
37
+ },
38
+ }),
39
+ defineTool("cog_edit", {
40
+ description: "Replace an exact, unique string in a memory file (edit in place).",
41
+ parameters: z.object({
42
+ path: z.string().describe("File path"),
43
+ old: z.string().describe("Exact text to replace — must occur exactly once"),
44
+ new: z.string().describe("Replacement text"),
45
+ }),
46
+ handler: async (args) => {
47
+ try {
48
+ const abs = resolveMemoryPath(root, args.path);
49
+ const data = readFileSync(abs, "utf8");
50
+ const occurrences = data.split(args.old).length - 1;
51
+ if (occurrences === 0) {
52
+ return `Error: old string not found in ${args.path}`;
53
+ }
54
+ if (occurrences > 1) {
55
+ return `Error: old string is not unique in ${args.path} — include more context`;
56
+ }
57
+ return await withMemoryWrite(manager, `memory: edit ${args.path}`, () => {
58
+ writeFileSync(abs, data.replace(args.old, args.new), "utf8");
59
+ return `edited ${args.path}`;
60
+ });
61
+ }
62
+ catch (err) {
63
+ return toolError(err);
64
+ }
65
+ },
66
+ }),
67
+ defineTool("cog_append", {
68
+ description: "Append a block of text to a memory file (for append-only files like observations).",
69
+ parameters: z.object({
70
+ path: z.string().describe("File path"),
71
+ content: z.string().describe("Text to append"),
72
+ }),
73
+ handler: async (args) => {
74
+ try {
75
+ const abs = resolveMemoryPath(root, args.path);
76
+ const existing = readFileSync(abs, "utf8");
77
+ let next = existing;
78
+ if (next !== "" && !next.endsWith("\n"))
79
+ next += "\n";
80
+ next += args.content;
81
+ if (!next.endsWith("\n"))
82
+ next += "\n";
83
+ return await withMemoryWrite(manager, `memory: append ${args.path}`, () => {
84
+ writeFileSync(abs, next, "utf8");
85
+ return `appended to ${args.path}`;
86
+ });
87
+ }
88
+ catch (err) {
89
+ return toolError(err);
90
+ }
91
+ },
92
+ }),
93
+ defineTool("cog_move", {
94
+ description: "Move or rename a memory file (used for glacier archival).",
95
+ parameters: z.object({
96
+ from: z.string().describe("Source path"),
97
+ to: z.string().describe("Destination path"),
98
+ }),
99
+ handler: async (args) => {
100
+ try {
101
+ const from = resolveMemoryPath(root, args.from);
102
+ const to = resolveMemoryPath(root, args.to);
103
+ return await withMemoryWrite(manager, `memory: move ${args.from} -> ${args.to}`, () => {
104
+ mkdirSync(dirname(to), { recursive: true });
105
+ renameSync(from, to);
106
+ return `moved ${args.from} -> ${args.to}`;
107
+ });
108
+ }
109
+ catch (err) {
110
+ return toolError(err);
111
+ }
112
+ },
113
+ }),
114
+ ];
115
+ }
116
+ //# sourceMappingURL=write.js.map
@@ -0,0 +1,107 @@
1
+ import assert from "node:assert/strict";
2
+ import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import test from "node:test";
5
+ import { gitAvailable, gitLog } from "../git.js";
6
+ import { MemoryManager } from "../manager.js";
7
+ import { createMemoryTools } from "./index.js";
8
+ const sandbox = join(process.cwd(), ".test-work", `mem-tools-write-${process.pid}`);
9
+ function findTool(tools, name) {
10
+ const found = tools.find((t) => t.name === name);
11
+ assert.ok(found, `tool ${name} not found`);
12
+ return found;
13
+ }
14
+ async function call(tool, args) {
15
+ const result = await tool.handler(args, {
16
+ sessionId: "t",
17
+ toolCallId: "t",
18
+ toolName: tool.name,
19
+ arguments: args,
20
+ });
21
+ return String(result);
22
+ }
23
+ async function setup() {
24
+ const mgr = new MemoryManager(sandbox);
25
+ await mgr.ensureReady();
26
+ return { tools: createMemoryTools(mgr), root: mgr.paths.memoryRoot };
27
+ }
28
+ test.beforeEach(() => {
29
+ rmSync(sandbox, { recursive: true, force: true });
30
+ mkdirSync(sandbox, { recursive: true });
31
+ });
32
+ test.after(() => rmSync(sandbox, { recursive: true, force: true }));
33
+ test("cog_write rejects a .md file with no L0 header", async () => {
34
+ if (!gitAvailable())
35
+ return;
36
+ const { tools } = await setup();
37
+ const out = await call(findTool(tools, "cog_write"), {
38
+ path: "personal/note.md",
39
+ content: "# No L0 here\nbody",
40
+ });
41
+ assert.match(out, /<!-- L0: summary -->/);
42
+ });
43
+ test("cog_write accepts a .md file with an L0 header and commits it", async () => {
44
+ if (!gitAvailable())
45
+ return;
46
+ const { tools, root } = await setup();
47
+ const before = (await gitLog(root, 50)).trim().split("\n").length;
48
+ const out = await call(findTool(tools, "cog_write"), {
49
+ path: "personal/note.md",
50
+ content: "<!-- L0: a note -->\n# Note\nbody",
51
+ });
52
+ assert.match(out, /wrote personal\/note\.md/);
53
+ assert.ok(existsSync(join(root, "personal/note.md")));
54
+ const log = await gitLog(root, 50);
55
+ assert.ok(log.includes("memory: write personal/note.md"));
56
+ assert.equal(log.trim().split("\n").length, before + 1, "exactly one new commit");
57
+ });
58
+ test("cog_edit errors on missing and non-unique matches, succeeds when unique", async () => {
59
+ if (!gitAvailable())
60
+ return;
61
+ const { tools } = await setup();
62
+ await call(findTool(tools, "cog_write"), {
63
+ path: "personal/note.md",
64
+ content: "<!-- L0: a note -->\nalpha\nbeta\nalpha",
65
+ });
66
+ assert.match(await call(findTool(tools, "cog_edit"), { path: "personal/note.md", old: "zeta", new: "x" }), /not found/);
67
+ assert.match(await call(findTool(tools, "cog_edit"), { path: "personal/note.md", old: "alpha", new: "x" }), /not unique/);
68
+ assert.match(await call(findTool(tools, "cog_edit"), { path: "personal/note.md", old: "beta", new: "BETA" }), /edited/);
69
+ });
70
+ test("cog_append normalizes newlines", async () => {
71
+ if (!gitAvailable())
72
+ return;
73
+ const { tools, root } = await setup();
74
+ await call(findTool(tools, "cog_write"), {
75
+ path: "personal/observations.md",
76
+ content: "<!-- L0: obs -->\nfirst line",
77
+ });
78
+ await call(findTool(tools, "cog_append"), { path: "personal/observations.md", content: "second line" });
79
+ assert.equal(readFileSync(join(root, "personal/observations.md"), "utf8"), "<!-- L0: obs -->\nfirst line\nsecond line\n");
80
+ });
81
+ test("cog_move relocates a file", async () => {
82
+ if (!gitAvailable())
83
+ return;
84
+ const { tools, root } = await setup();
85
+ await call(findTool(tools, "cog_write"), {
86
+ path: "personal/old.md",
87
+ content: "<!-- L0: x -->\nbody",
88
+ });
89
+ const out = await call(findTool(tools, "cog_move"), {
90
+ from: "personal/old.md",
91
+ to: "glacier/personal/old.md",
92
+ });
93
+ assert.match(out, /moved/);
94
+ assert.ok(!existsSync(join(root, "personal/old.md")));
95
+ assert.ok(existsSync(join(root, "glacier/personal/old.md")));
96
+ });
97
+ test("cog_write rejects a path escaping the memory root", async () => {
98
+ if (!gitAvailable())
99
+ return;
100
+ const { tools } = await setup();
101
+ const out = await call(findTool(tools, "cog_write"), {
102
+ path: "../escape.md",
103
+ content: "<!-- L0: x -->\nbody",
104
+ });
105
+ assert.match(out, /escapes the memory root/);
106
+ });
107
+ //# sourceMappingURL=write.test.js.map
@@ -0,0 +1,39 @@
1
+ // Walks the markdown files of the memory tree (or one domain subtree),
2
+ // skipping the .git directory. Ported from chgo's tools_read.go walkMemoryMarkdown.
3
+ import { readdirSync } from "fs";
4
+ import { join, relative, sep } from "path";
5
+ import { resolveMemoryPath } from "./path-guard.js";
6
+ /**
7
+ * Calls `fn(rel, abs)` for every `.md` file under the memory root, or under a
8
+ * domain subdirectory when `domain` is given. `rel` is slash-separated and
9
+ * relative to the memory root.
10
+ */
11
+ export function walkMemoryMarkdown(memoryRoot, domain, fn) {
12
+ let base = memoryRoot;
13
+ if (domain && domain.trim() !== "") {
14
+ base = resolveMemoryPath(memoryRoot, domain);
15
+ }
16
+ const walk = (dir) => {
17
+ let entries;
18
+ try {
19
+ entries = readdirSync(dir, { withFileTypes: true });
20
+ }
21
+ catch {
22
+ return; // directory may not exist yet
23
+ }
24
+ for (const entry of entries) {
25
+ const full = join(dir, entry.name);
26
+ if (entry.isDirectory()) {
27
+ if (entry.name === ".git")
28
+ continue;
29
+ walk(full);
30
+ }
31
+ else if (entry.name.endsWith(".md")) {
32
+ const rel = relative(memoryRoot, full).split(sep).join("/");
33
+ fn(rel, full);
34
+ }
35
+ }
36
+ };
37
+ walk(base);
38
+ }
39
+ //# sourceMappingURL=walk.js.map
@@ -229,6 +229,46 @@ export function getSessionMessages(sessionKey, limit, options = {}) {
229
229
  return message;
230
230
  });
231
231
  }
232
+ /**
233
+ * Group conversation_log rows into sessions for the cog_sessions tool. Rows are
234
+ * grouped by (session_key, run_id); `agent_completion` rows map to role
235
+ * "assistant"; empty-content rows are skipped. When `since` (an ISO-8601
236
+ * string) is given, only later turns are kept. Sessions are sorted
237
+ * newest-first; `limit` (0 = no cap) bounds the count.
238
+ *
239
+ * Note: conversation_log is pruned to the last 1000 rows, so only recent
240
+ * history is visible — adequate for the reflect skill's cursor model.
241
+ */
242
+ export function getConversationSessions(since, limit = 0) {
243
+ const db = getDb();
244
+ const rows = db.prepare(`SELECT role, content, ts, session_key, run_id, agent_slug FROM conversation_log
245
+ WHERE role IN ('user', 'assistant', 'agent_completion')
246
+ ORDER BY session_key, run_id, id ASC`).all();
247
+ const sinceIso = since ? normalizeSqliteTsToIso(since) : undefined;
248
+ const byKey = new Map();
249
+ for (const r of rows) {
250
+ if (!r.content || r.content.trim() === "")
251
+ continue;
252
+ const at = normalizeSqliteTsToIso(r.ts);
253
+ if (sinceIso && at <= sinceIso)
254
+ continue;
255
+ const key = `${r.session_key}|${r.run_id ?? ""}`;
256
+ let session = byKey.get(key);
257
+ if (!session) {
258
+ session = { sessionKey: r.session_key, runId: r.run_id, startedAt: at, turns: [] };
259
+ byKey.set(key, session);
260
+ }
261
+ session.turns.push({
262
+ role: r.role === "agent_completion" ? "assistant" : r.role,
263
+ content: r.content,
264
+ ts: at,
265
+ agentSlug: r.agent_slug,
266
+ });
267
+ }
268
+ const sessions = Array.from(byKey.values()).filter((s) => s.turns.length > 0);
269
+ sessions.sort((a, b) => (a.startedAt < b.startedAt ? 1 : a.startedAt > b.startedAt ? -1 : 0));
270
+ return limit > 0 ? sessions.slice(0, limit) : sessions;
271
+ }
232
272
  /**
233
273
  * Append one event to agent_task_events and return the new event.
234
274
  * Uses a transaction so seq is monotonically incremented.
@@ -3,8 +3,6 @@ import { execFileSync } from "node:child_process";
3
3
  import { existsSync, mkdirSync, renameSync } from "node:fs";
4
4
  import { dirname, join, basename } from "node:path";
5
5
  import { config } from "../config.js";
6
- import { recordActionItem } from "../memory/action-items.js";
7
- import { getScope } from "../memory/scopes.js";
8
6
  import { getChapterhouseHome, resolveWikiRelativePath } from "../paths.js";
9
7
  import { childLogger } from "../util/logger.js";
10
8
  import { deletePage, listPages, readPage, writePage } from "./fs.js";
@@ -77,13 +75,14 @@ export async function runConsolidationWithDeps(db, partialDeps = {}) {
77
75
  await deps.commitWikiChanges(runAt);
78
76
  return result;
79
77
  }
80
- function createDefaultDeps(db) {
78
+ function createDefaultDeps(_db) {
81
79
  return {
82
80
  now: () => new Date(),
83
81
  synthesizeTruth: synthesizeTruthWithCopilot,
84
82
  rebuildIndex: rebuildWikiIndex,
85
83
  commitWikiChanges: async (runAt) => commitWikiChanges(runAt),
86
- createActionItem: ({ title, detail, source }) => createStaleSessionActionItem(db, { title, detail, source }),
84
+ // Wiki consolidation no longer records action items into agent memory.
85
+ createActionItem: () => false,
87
86
  truthRewriteBudget: config.pkbTruthRewriteBudget,
88
87
  };
89
88
  }
@@ -466,33 +465,6 @@ function notifyStaleSessions(db, deps, runAt) {
466
465
  }
467
466
  return created;
468
467
  }
469
- function createStaleSessionActionItem(db, input) {
470
- const globalScope = getScope("global");
471
- if (!globalScope) {
472
- return false;
473
- }
474
- const existing = recordDuplicateActionItemCheck(db, globalScope.id, input.title);
475
- if (existing) {
476
- return false;
477
- }
478
- recordActionItem({
479
- scope_id: globalScope.id,
480
- title: input.title,
481
- detail: input.detail,
482
- source: input.source,
483
- tier: "warm",
484
- });
485
- return true;
486
- }
487
- function recordDuplicateActionItemCheck(db, scopeId, title) {
488
- const row = db.prepare(`
489
- SELECT 1
490
- FROM mem_action_items
491
- WHERE scope_id = ? AND title = ? AND status IN ('open', 'snoozed')
492
- LIMIT 1
493
- `).get(scopeId, title);
494
- return Boolean(row);
495
- }
496
468
  async function synthesizeTruthWithCopilot(input) {
497
469
  const token = config.copilotAuthToken;
498
470
  if (!token) {
@@ -118,25 +118,6 @@ test("runConsolidation removes orphaned wiki_links rows", async () => {
118
118
  assert.equal(result.linksRepaired, 1);
119
119
  assert.equal(orphanCount.c, 0);
120
120
  });
121
- test("runConsolidation creates an action item for research sessions inactive for 7+ days", async () => {
122
- const { dbModule, wikiFs, consolidation, memory } = await loadModules();
123
- const db = dbModule.getDb();
124
- wikiFs.ensureWikiStructure();
125
- const globalScope = memory.getScope("global");
126
- assert.ok(globalScope);
127
- db.prepare(`
128
- INSERT INTO wiki_sources (id, source_type, origin, title, ingested_at, raw_path, parsed_content, pages_updated, status, session_id, session_name)
129
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
130
- `).run("source-1", "url", "https://example.test/1", "Research note", "2026-05-01T10:00:00.000Z", "sources/source-1.md", "content", "[]", "active", "session-123", "Compiler research");
131
- const result = await consolidation.runConsolidationWithDeps(db, {
132
- now: () => new Date("2026-05-14T22:30:03.086Z"),
133
- synthesizeTruth: async () => "unused",
134
- commitWikiChanges: async () => false,
135
- });
136
- const actionItems = memory.listActionItems({ scope_id: globalScope.id, includeArchived: true });
137
- assert.equal(result.staleSessionsNotified, 1);
138
- assert.equal(actionItems.some((item) => item.title.includes("Research session 'Compiler research' has been inactive for 7+ days")), true);
139
- });
140
121
  test("runConsolidation caps truth rewrites before exceeding the LLM budget", async () => {
141
122
  const { dbModule, wikiFs, indexManager, consolidation } = await loadModules();
142
123
  const db = dbModule.getDb();
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: {{ID}}
3
+ description: Use when the conversation involves {{LABEL}}. Triggers — {{TRIGGER_LINE}}. Do not use for topics belonging to other domains.
4
+ ---
5
+
6
+ # {{LABEL}}
7
+
8
+ Use this skill when the user discusses {{LABEL}} topics.
9
+
10
+ ## Memory Files
11
+
12
+ Always read on activation:
13
+ - `memory/{{PATH}}/hot-memory.md` (via `cog_read`)
14
+
15
+ Then load additional files per the memory retrieval protocol based on the query:
16
+ - Status/task query → `memory/{{PATH}}/action-items.md`
17
+ - Entity/people query → `memory/{{PATH}}/entities.md`
18
+ - Update/observation → the target file only
19
+ - Complex query → hot-memory first, then drill into referenced files
20
+
21
+ Available warm files: {{FILES}}
22
+
23
+ To find files without opening them, run `cog_l0_scan` scoped to `{{PATH}}`.
24
+ Historical data: `cog_read` on `memory/glacier/index.md`, filter by domain `{{ID}}`.
25
+
26
+ ## Routing
27
+
28
+ When the user shares information or asks to save something, write with the
29
+ `cog_*` tools:
30
+ - Task/todo → `memory/{{PATH}}/action-items.md`
31
+ - Person/entity → `memory/{{PATH}}/entities.md`
32
+ - Update/log → `memory/{{PATH}}/observations.md`
33
+ - Status/overview → `memory/{{PATH}}/hot-memory.md`
34
+
35
+ ## Activation
36
+
37
+ Read the hot-memory file, classify the query, load the minimum files needed,
38
+ and respond.
@@ -0,0 +1,8 @@
1
+ <!-- L0: Feature ideas, wishlists, and repair notes -->
2
+ # Improvements
3
+
4
+ <!-- Edit in place by section. -->
5
+
6
+ ## Ideas
7
+
8
+ ## Implemented
@@ -0,0 +1,5 @@
1
+ <!-- L0: Distilled interaction and workflow rules from experience -->
2
+ # Learned Patterns
3
+
4
+ <!-- Edit in place. Distill self-observations into actionable, timeless rules. -->
5
+ <!-- HARD LIMIT: 70 lines / 5.5KB. Domain-specific patterns go in satellite files. -->
@@ -0,0 +1,4 @@
1
+ <!-- L0: Ingestion cursor for the reflect skill over session history -->
2
+ # Reflect Cursor
3
+
4
+ last_processed: never
@@ -0,0 +1,14 @@
1
+ <!-- L0: Prediction accuracy tracker for decision scenarios -->
2
+ # Scenario Calibration
3
+
4
+ <!-- Updated by the reflect skill when scenarios resolve. -->
5
+
6
+ ## Resolved Scenarios
7
+
8
+ | Scenario | Created | Resolved | Predicted Branch | Actual Branch | Accuracy | Lesson |
9
+ |----------|---------|----------|-----------------|---------------|----------|--------|
10
+
11
+ ## Metrics
12
+
13
+ - Total resolved: 0
14
+ - Accuracy rate: —
@@ -0,0 +1,4 @@
1
+ <!-- L0: What worked and didn't in interactions — append-only -->
2
+ # Self-Observations
3
+
4
+ <!-- Append-only. Format: - YYYY-MM-DD [tag]: observation -->
@@ -0,0 +1,19 @@
1
+ # Chapterhouse Domain Manifest
2
+ # Single source of truth for all memory domains.
3
+ # Add domains with the setup skill, or edit this file directly — Chapterhouse
4
+ # reconciles directories and skill files from it on every startup.
5
+
6
+ domains:
7
+ - id: personal
8
+ path: personal
9
+ type: personal
10
+ label: "Family, health, calendar, day-to-day"
11
+ triggers: [family, health, kids, calendar, personal, home, car, finance]
12
+ files: [hot-memory, action-items, entities, observations, habits, health, calendar]
13
+
14
+ - id: cog-meta
15
+ path: cog-meta
16
+ type: system
17
+ label: "Memory self-knowledge, pipeline health, architecture"
18
+ triggers: [meta, evolve, pipeline, memory system, architecture]
19
+ files: [self-observations, patterns, improvements, scenario-calibration, reflect-cursor]
@@ -0,0 +1,6 @@
1
+ # Glacier Index
2
+ <!-- Auto-generated by the housekeeping skill. Do not edit. -->
3
+ <!-- Last updated: — -->
4
+
5
+ | File | Domain | Type | Tags | Date Range | Entries | Summary |
6
+ |------|--------|------|------|------------|---------|---------|
@@ -0,0 +1,5 @@
1
+ <!-- L0: Cross-domain strategic context — identity, active priorities -->
2
+ # Hot Memory
3
+
4
+ <!-- Rewrite freely. Keep under 50 lines. -->
5
+ <!-- Use the setup skill to populate this with your domains and priorities. -->
@@ -0,0 +1,6 @@
1
+ # Memory Link Index
2
+ <!-- Auto-generated by the housekeeping skill. Do not edit. -->
3
+ <!-- Last updated: — -->
4
+
5
+ | Target | Linked from |
6
+ |--------|-------------|