@cleocode/core 2026.6.7 → 2026.6.8

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 (50) hide show
  1. package/dist/db/index.d.ts +5 -1
  2. package/dist/db/index.d.ts.map +1 -1
  3. package/dist/db/index.js +5 -1
  4. package/dist/db/index.js.map +1 -1
  5. package/dist/docs/build-provenance-graph.d.ts +12 -0
  6. package/dist/docs/build-provenance-graph.d.ts.map +1 -1
  7. package/dist/docs/build-provenance-graph.js +52 -0
  8. package/dist/docs/build-provenance-graph.js.map +1 -1
  9. package/dist/docs/docs-read-model.d.ts +40 -0
  10. package/dist/docs/docs-read-model.d.ts.map +1 -1
  11. package/dist/docs/docs-read-model.js +29 -0
  12. package/dist/docs/docs-read-model.js.map +1 -1
  13. package/dist/docs/export-document.js +897 -730
  14. package/dist/docs/export-document.js.map +3 -3
  15. package/dist/docs/index.d.ts +4 -0
  16. package/dist/docs/index.d.ts.map +1 -1
  17. package/dist/docs/index.js +2 -0
  18. package/dist/docs/index.js.map +1 -1
  19. package/dist/docs/read-doc.d.ts +60 -0
  20. package/dist/docs/read-doc.d.ts.map +1 -0
  21. package/dist/docs/read-doc.js +188 -0
  22. package/dist/docs/read-doc.js.map +1 -0
  23. package/dist/docs/wikilinks.d.ts +119 -0
  24. package/dist/docs/wikilinks.d.ts.map +1 -0
  25. package/dist/docs/wikilinks.js +217 -0
  26. package/dist/docs/wikilinks.js.map +1 -0
  27. package/dist/llm/plugin-facade.js +941 -776
  28. package/dist/llm/plugin-facade.js.map +3 -3
  29. package/dist/store/dual-scope-db.d.ts +83 -0
  30. package/dist/store/dual-scope-db.d.ts.map +1 -1
  31. package/dist/store/dual-scope-db.js +135 -6
  32. package/dist/store/dual-scope-db.js.map +1 -1
  33. package/dist/store/exodus/abort-events.d.ts +116 -0
  34. package/dist/store/exodus/abort-events.d.ts.map +1 -0
  35. package/dist/store/exodus/abort-events.js +130 -0
  36. package/dist/store/exodus/abort-events.js.map +1 -0
  37. package/dist/store/exodus/index.d.ts +1 -0
  38. package/dist/store/exodus/index.d.ts.map +1 -1
  39. package/dist/store/exodus/index.js +1 -0
  40. package/dist/store/exodus/index.js.map +1 -1
  41. package/dist/store/repair-malformed-dbs.d.ts +87 -0
  42. package/dist/store/repair-malformed-dbs.d.ts.map +1 -0
  43. package/dist/store/repair-malformed-dbs.js +188 -0
  44. package/dist/store/repair-malformed-dbs.js.map +1 -0
  45. package/dist/store/schema/attachments.d.ts +133 -0
  46. package/dist/store/schema/attachments.d.ts.map +1 -1
  47. package/dist/store/schema/attachments.js +63 -0
  48. package/dist/store/schema/attachments.js.map +1 -1
  49. package/migrations/drizzle-tasks/20260605000001_t11826-docs-wikilinks/migration.sql +110 -0
  50. package/package.json +12 -12
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/store/exodus/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,4BAA4B,EACjC,sBAAsB,EACtB,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,KAAK,oBAAoB,EACzB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,EACjB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAClF,OAAO,EACL,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,UAAU,GACX,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,wBAAwB,EACxB,4BAA4B,EAC5B,aAAa,EACb,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,4BAA4B,EAC5B,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/store/exodus/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,eAAe,EACf,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,4BAA4B,EACjC,sBAAsB,EACtB,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,KAAK,oBAAoB,EACzB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,EACjB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAClF,OAAO,EACL,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,UAAU,GACX,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,wBAAwB,EACxB,4BAA4B,EAC5B,aAAa,EACb,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,4BAA4B,EAC5B,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
@@ -8,6 +8,7 @@
8
8
  * @task T11248 (E5 · SG-DB-SUBSTRATE-V2)
9
9
  * @saga T11242
10
10
  */
11
+ export { clearExodusAborts, emitExodusAbort, exodusAbortEvents, getRecordedExodusAbort, } from './abort-events.js';
11
12
  export { archiveMigratedSources, archiveSourceDb, archiveStrandedResidue, detectStrandedResidue, exodusArchiveDir, exodusMarkerPath, hasExodusCompleteMarker, writeExodusCompleteMarker, } from './archive.js';
12
13
  export { computeCountParity, } from './count-parity.js';
13
14
  export { buildExodusHealth, } from './health.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/store/exodus/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAGL,sBAAsB,EACtB,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EAErB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EAEvB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,GAKlB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAClF,OAAO,EAIL,UAAU,GACX,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,wBAAwB,EACxB,4BAA4B,EAC5B,aAAa,GAEd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,4BAA4B,GAY7B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/store/exodus/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,iBAAiB,EAEjB,eAAe,EACf,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAGL,sBAAsB,EACtB,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EAErB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EAEvB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,GAKlB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAClF,OAAO,EAIL,UAAU,GACX,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,wBAAwB,EACxB,4BAA4B,EAC5B,aAAa,GAEd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,4BAA4B,GAY7B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * `cleo doctor repair` orchestrator — detect malformed CLEO databases and
3
+ * restore each from its freshest validated snapshot (T11829 · DHQ-060).
4
+ *
5
+ * ## Why this exists
6
+ *
7
+ * The corruption-resilience pipeline ALREADY existed before T11829:
8
+ *
9
+ * - {@link recoverMalformedDb} (quarantine → snapshot-restore → `quick_check`),
10
+ * - `autoRecoverFromBackup` (catches `"database disk image is malformed"` at
11
+ * open and restores the freshest VACUUM snapshot), and
12
+ * - the operator verb `cleo backup recover <role>` ({@link runBackupRecover}).
13
+ *
14
+ * The ONLY gap vs the DHQ-060 ask was a discoverable `cleo doctor` entry point
15
+ * that DETECTS corruption across the fleet and repairs only what is broken. This
16
+ * module is that orchestrator — it adds NO new recovery logic. It probes each
17
+ * role's live DB with `PRAGMA quick_check` (the same probe the pipeline runs on
18
+ * snapshot candidates, via {@link probeSnapshot}) and delegates every actual
19
+ * repair to {@link runBackupRecover} → {@link recoverMalformedDb}.
20
+ *
21
+ * ## WAL-specific corruption is covered
22
+ *
23
+ * A torn WAL frame surfaces as `"database disk image is malformed"` and makes
24
+ * `PRAGMA quick_check` return a non-`ok` result — so {@link probeSnapshot}
25
+ * detects it. The quarantine step ({@link quarantineCorruptDb}) already moves the
26
+ * `-wal`/`-shm` sidecars alongside the main file, so the WAL case is fully
27
+ * handled by the existing pipeline; this orchestrator does not need to special-case it.
28
+ *
29
+ * @module
30
+ * @task T11829 (DHQ-060 — `cleo doctor repair` entry point over the recovery pipeline)
31
+ * @epic T11833
32
+ * @saga T11242 (SG-DB-SUBSTRATE-V2)
33
+ * @see packages/core/src/store/recover-malformed-db.ts — the recovery pipeline
34
+ * @see packages/core/src/store/backup-recover.ts — `cleo backup recover <role>` wrapper
35
+ */
36
+ import { type DbRole, type DoctorRepairResult } from '@cleocode/contracts';
37
+ import { type RecoveryLogger } from './recover-malformed-db.js';
38
+ /**
39
+ * Options accepted by {@link repairMalformedDbs}.
40
+ *
41
+ * @task T11829
42
+ * @public
43
+ */
44
+ export interface RepairMalformedDbsOptions {
45
+ /** Absolute path to the project root (resolves `<projectRoot>` inventory tokens). */
46
+ projectRoot: string;
47
+ /**
48
+ * Roles to inspect. When omitted, EVERY role in {@link DB_INVENTORY} whose live
49
+ * file exists is probed. An explicit single-role list (from `--role`) is probed
50
+ * even when absent (so the operator gets a clear "not present" report).
51
+ */
52
+ roles?: DbRole[];
53
+ /**
54
+ * When `true`, detect + plan only — corruption is reported with
55
+ * `action: 'would-repair'` but NO quarantine/restore is performed.
56
+ *
57
+ * @default false
58
+ */
59
+ dryRun?: boolean;
60
+ /** Pino-shaped logger for recovery announcements. */
61
+ logger: RecoveryLogger;
62
+ }
63
+ /**
64
+ * Detect and repair malformed CLEO databases across the fleet (T11829 · DHQ-060).
65
+ *
66
+ * For each requested role (or every present role in {@link DB_INVENTORY} when none
67
+ * are specified) this probes the live DB with `PRAGMA quick_check` and, when
68
+ * corruption is found, restores the freshest validated snapshot via the existing
69
+ * {@link runBackupRecover} pipeline. No new recovery logic is introduced — this is
70
+ * the discoverable `cleo doctor repair` entry point requested by DHQ-060.
71
+ *
72
+ * @param opts - Repair inputs (project root, optional role filter, dry-run, logger).
73
+ * @returns A {@link DoctorRepairResult} aggregate with per-role outcomes.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * const report = repairMalformedDbs({ projectRoot: '/repo', logger });
78
+ * if (report.failedCount > 0) process.exitCode = 1;
79
+ * ```
80
+ *
81
+ * @task T11829
82
+ * @epic T11833
83
+ * @saga T11242
84
+ * @public
85
+ */
86
+ export declare function repairMalformedDbs(opts: RepairMalformedDbsOptions): DoctorRepairResult;
87
+ //# sourceMappingURL=repair-malformed-dbs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair-malformed-dbs.d.ts","sourceRoot":"","sources":["../../src/store/repair-malformed-dbs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,EAAgB,KAAK,MAAM,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzF,OAAO,EAAiB,KAAK,cAAc,EAAqB,MAAM,2BAA2B,CAAC;AAElG;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACxC,qFAAqF;IACrF,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qDAAqD;IACrD,MAAM,EAAE,cAAc,CAAC;CACxB;AA2HD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,yBAAyB,GAAG,kBAAkB,CAiBtF"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * `cleo doctor repair` orchestrator — detect malformed CLEO databases and
3
+ * restore each from its freshest validated snapshot (T11829 · DHQ-060).
4
+ *
5
+ * ## Why this exists
6
+ *
7
+ * The corruption-resilience pipeline ALREADY existed before T11829:
8
+ *
9
+ * - {@link recoverMalformedDb} (quarantine → snapshot-restore → `quick_check`),
10
+ * - `autoRecoverFromBackup` (catches `"database disk image is malformed"` at
11
+ * open and restores the freshest VACUUM snapshot), and
12
+ * - the operator verb `cleo backup recover <role>` ({@link runBackupRecover}).
13
+ *
14
+ * The ONLY gap vs the DHQ-060 ask was a discoverable `cleo doctor` entry point
15
+ * that DETECTS corruption across the fleet and repairs only what is broken. This
16
+ * module is that orchestrator — it adds NO new recovery logic. It probes each
17
+ * role's live DB with `PRAGMA quick_check` (the same probe the pipeline runs on
18
+ * snapshot candidates, via {@link probeSnapshot}) and delegates every actual
19
+ * repair to {@link runBackupRecover} → {@link recoverMalformedDb}.
20
+ *
21
+ * ## WAL-specific corruption is covered
22
+ *
23
+ * A torn WAL frame surfaces as `"database disk image is malformed"` and makes
24
+ * `PRAGMA quick_check` return a non-`ok` result — so {@link probeSnapshot}
25
+ * detects it. The quarantine step ({@link quarantineCorruptDb}) already moves the
26
+ * `-wal`/`-shm` sidecars alongside the main file, so the WAL case is fully
27
+ * handled by the existing pipeline; this orchestrator does not need to special-case it.
28
+ *
29
+ * @module
30
+ * @task T11829 (DHQ-060 — `cleo doctor repair` entry point over the recovery pipeline)
31
+ * @epic T11833
32
+ * @saga T11242 (SG-DB-SUBSTRATE-V2)
33
+ * @see packages/core/src/store/recover-malformed-db.ts — the recovery pipeline
34
+ * @see packages/core/src/store/backup-recover.ts — `cleo backup recover <role>` wrapper
35
+ */
36
+ import { existsSync } from 'node:fs';
37
+ import { DB_INVENTORY } from '@cleocode/contracts';
38
+ import { BackupRecoverError, runBackupRecover } from './backup-recover.js';
39
+ import { probeSnapshot, resolveRoleDbPath } from './recover-malformed-db.js';
40
+ /**
41
+ * Probe a single role's live DB and, when malformed, repair it from snapshot.
42
+ *
43
+ * Pure delegation: detection via {@link probeSnapshot} (`PRAGMA quick_check`),
44
+ * repair via {@link runBackupRecover}. Never throws — a recovery failure is
45
+ * captured in the returned record's `action: 'failed'`.
46
+ *
47
+ * @internal
48
+ */
49
+ function repairOneRole(role, opts) {
50
+ const dbPath = resolveRoleDbPath(role, { projectRoot: opts.projectRoot });
51
+ const present = existsSync(dbPath);
52
+ if (!present) {
53
+ return {
54
+ role,
55
+ dbPath,
56
+ present: false,
57
+ healthy: true,
58
+ action: 'skipped',
59
+ restoredFrom: null,
60
+ quarantinedTo: null,
61
+ dataLossWindowHours: null,
62
+ detail: 'live DB file not present on disk (nothing to repair)',
63
+ };
64
+ }
65
+ // DETECT — the same `PRAGMA quick_check` the pipeline runs on snapshots. A
66
+ // torn WAL frame ("database disk image is malformed") fails this probe.
67
+ const probe = probeSnapshot(dbPath);
68
+ if (probe.ok) {
69
+ return {
70
+ role,
71
+ dbPath,
72
+ present: true,
73
+ healthy: true,
74
+ action: 'skipped',
75
+ restoredFrom: null,
76
+ quarantinedTo: null,
77
+ dataLossWindowHours: null,
78
+ detail: 'PRAGMA quick_check passed — DB is healthy',
79
+ };
80
+ }
81
+ // MALFORMED — plan or repair.
82
+ if (opts.dryRun === true) {
83
+ try {
84
+ // Dry-run delegates to the SAME pipeline (no mutation) for an honest plan.
85
+ const plan = runBackupRecover({
86
+ role,
87
+ projectRoot: opts.projectRoot,
88
+ logger: opts.logger,
89
+ dryRun: true,
90
+ });
91
+ return {
92
+ role,
93
+ dbPath,
94
+ present: true,
95
+ healthy: false,
96
+ action: 'would-repair',
97
+ restoredFrom: plan.restoredFrom || null,
98
+ quarantinedTo: null,
99
+ dataLossWindowHours: plan.dataLossWindowHours,
100
+ detail: `malformed — would restore from ${plan.restoredFrom} (re-run without --dry-run to repair)`,
101
+ };
102
+ }
103
+ catch (err) {
104
+ return {
105
+ role,
106
+ dbPath,
107
+ present: true,
108
+ healthy: false,
109
+ action: 'failed',
110
+ restoredFrom: null,
111
+ quarantinedTo: null,
112
+ dataLossWindowHours: null,
113
+ detail: `malformed but NO valid snapshot to restore from: ${err instanceof BackupRecoverError ? err.message : String(err)}`,
114
+ };
115
+ }
116
+ }
117
+ // EXECUTE — quarantine + restore + verify via the existing pipeline.
118
+ try {
119
+ const result = runBackupRecover({
120
+ role,
121
+ projectRoot: opts.projectRoot,
122
+ logger: opts.logger,
123
+ dryRun: false,
124
+ });
125
+ return {
126
+ role,
127
+ dbPath,
128
+ present: true,
129
+ healthy: false,
130
+ action: 'repaired',
131
+ restoredFrom: result.restoredFrom || null,
132
+ quarantinedTo: result.quarantinedTo || null,
133
+ dataLossWindowHours: result.dataLossWindowHours,
134
+ detail: `repaired — restored from ${result.restoredFrom}; corrupt DB quarantined at ${result.quarantinedTo}`,
135
+ };
136
+ }
137
+ catch (err) {
138
+ return {
139
+ role,
140
+ dbPath,
141
+ present: true,
142
+ healthy: false,
143
+ action: 'failed',
144
+ restoredFrom: null,
145
+ quarantinedTo: null,
146
+ dataLossWindowHours: null,
147
+ detail: `malformed — recovery FAILED: ${err instanceof BackupRecoverError ? err.message : String(err)}`,
148
+ };
149
+ }
150
+ }
151
+ /**
152
+ * Detect and repair malformed CLEO databases across the fleet (T11829 · DHQ-060).
153
+ *
154
+ * For each requested role (or every present role in {@link DB_INVENTORY} when none
155
+ * are specified) this probes the live DB with `PRAGMA quick_check` and, when
156
+ * corruption is found, restores the freshest validated snapshot via the existing
157
+ * {@link runBackupRecover} pipeline. No new recovery logic is introduced — this is
158
+ * the discoverable `cleo doctor repair` entry point requested by DHQ-060.
159
+ *
160
+ * @param opts - Repair inputs (project root, optional role filter, dry-run, logger).
161
+ * @returns A {@link DoctorRepairResult} aggregate with per-role outcomes.
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * const report = repairMalformedDbs({ projectRoot: '/repo', logger });
166
+ * if (report.failedCount > 0) process.exitCode = 1;
167
+ * ```
168
+ *
169
+ * @task T11829
170
+ * @epic T11833
171
+ * @saga T11242
172
+ * @public
173
+ */
174
+ export function repairMalformedDbs(opts) {
175
+ const explicit = opts.roles !== undefined && opts.roles.length > 0;
176
+ const candidateRoles = explicit
177
+ ? opts.roles
178
+ : DB_INVENTORY.map((e) => e.role).filter((role) => existsSync(resolveRoleDbPath(role, { projectRoot: opts.projectRoot })));
179
+ const roles = candidateRoles.map((role) => repairOneRole(role, opts));
180
+ return {
181
+ dryRun: opts.dryRun === true,
182
+ roles,
183
+ malformedCount: roles.filter((r) => !r.healthy).length,
184
+ repairedCount: roles.filter((r) => r.action === 'repaired').length,
185
+ failedCount: roles.filter((r) => r.action === 'failed').length,
186
+ };
187
+ }
188
+ //# sourceMappingURL=repair-malformed-dbs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair-malformed-dbs.js","sourceRoot":"","sources":["../../src/store/repair-malformed-dbs.ts"],"names":[],"mappings":"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"}
@@ -391,8 +391,141 @@ export declare const attachmentRefs: import("drizzle-orm/sqlite-core").SQLiteTab
391
391
  };
392
392
  dialect: "sqlite";
393
393
  }>;
394
+ /**
395
+ * Allowed relations for a `docs_wikilinks` edge.
396
+ *
397
+ * A wikilink is a DERIVED, slug-addressed edge between two docs (or a doc and a
398
+ * task) reconstructed from the authoritative provenance columns on
399
+ * `attachments` — there is no hand-authored edge here. The relation enumerates
400
+ * which source column produced the edge:
401
+ *
402
+ * - `supersedes` — `attachments.supersedes` (newer → older)
403
+ * - `superseded-by` — `attachments.superseded_by` (older → newer)
404
+ * - `related-task` — `attachments.related_tasks` JSON membership (doc → T####)
405
+ * - `topic` — `attachments.topics` JSON co-membership (doc ↔ doc sharing a topic)
406
+ *
407
+ * Kept slug-primary so the edge table is Obsidian-grade (vault links are
408
+ * slug-addressed) and survives attachment-id churn across versions.
409
+ *
410
+ * @task T11826 (Epic T11781 / Saga T11778)
411
+ */
412
+ export declare const DOCS_WIKILINK_RELATIONS: readonly ["supersedes", "superseded-by", "related-task", "topic"];
413
+ /** Discriminated union of `docs_wikilinks.relation` values. */
414
+ export type DocsWikilinkRelation = (typeof DOCS_WIKILINK_RELATIONS)[number];
415
+ /**
416
+ * `docs_wikilinks` — DERIVED, slug-addressed edge table for the docs graph.
417
+ *
418
+ * Per the ratified Docs-SSoT model (saga T11778): `cleo.db` is the SOLE doc
419
+ * authority, and `docs_wikilinks` is a *minimal edge table derived from*
420
+ * `supersedes` + `relatedTasks` + `topics` on `attachments`. It is NOT an
421
+ * authoritative input surface — it is rebuilt idempotently from the provenance
422
+ * columns by {@link import('../../docs/wikilinks.js').rebuildDocsWikilinks}.
423
+ *
424
+ * The table makes the bidirectional backlink graph queryable in O(edges)
425
+ * without recomputing the BFS, which is what the Obsidian plugin (T11827)
426
+ * renders. `cleo docs graph` (T10164) continues to compute the provenance BFS
427
+ * but hydrates persisted backlinks from this table when present.
428
+ *
429
+ * Edges are slug-primary; `to_slug` carries a doc slug for `topic` /
430
+ * `supersedes` / `superseded-by` edges and a `T####` task id for `related-task`
431
+ * edges (`to_is_task = 1`). No markdown body `[[link]]` parsing is performed
432
+ * (AC4) — the edges derive purely from structured provenance columns.
433
+ *
434
+ * @task T11826 (Epic T11781 / Saga T11778)
435
+ */
436
+ export declare const docsWikilinks: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
437
+ name: "docs_wikilinks";
438
+ schema: undefined;
439
+ columns: {
440
+ fromSlug: import("drizzle-orm/sqlite-core").SQLiteColumn<{
441
+ name: string;
442
+ tableName: "docs_wikilinks";
443
+ dataType: "string";
444
+ data: string;
445
+ driverParam: string;
446
+ notNull: true;
447
+ hasDefault: false;
448
+ isPrimaryKey: false;
449
+ isAutoincrement: false;
450
+ hasRuntimeDefault: false;
451
+ enumValues: [string, ...string[]];
452
+ baseColumn: never;
453
+ identity: undefined;
454
+ generated: undefined;
455
+ }, {}>;
456
+ toSlug: import("drizzle-orm/sqlite-core").SQLiteColumn<{
457
+ name: string;
458
+ tableName: "docs_wikilinks";
459
+ dataType: "string";
460
+ data: string;
461
+ driverParam: string;
462
+ notNull: true;
463
+ hasDefault: false;
464
+ isPrimaryKey: false;
465
+ isAutoincrement: false;
466
+ hasRuntimeDefault: false;
467
+ enumValues: [string, ...string[]];
468
+ baseColumn: never;
469
+ identity: undefined;
470
+ generated: undefined;
471
+ }, {}>;
472
+ relation: import("drizzle-orm/sqlite-core").SQLiteColumn<{
473
+ name: string;
474
+ tableName: "docs_wikilinks";
475
+ dataType: "string enum";
476
+ data: "topic" | "supersedes" | "superseded-by" | "related-task";
477
+ driverParam: string;
478
+ notNull: true;
479
+ hasDefault: false;
480
+ isPrimaryKey: false;
481
+ isAutoincrement: false;
482
+ hasRuntimeDefault: false;
483
+ enumValues: ["supersedes", "superseded-by", "related-task", "topic"];
484
+ baseColumn: never;
485
+ identity: undefined;
486
+ generated: undefined;
487
+ }, {}>;
488
+ toIsTask: import("drizzle-orm/sqlite-core").SQLiteColumn<{
489
+ name: string;
490
+ tableName: "docs_wikilinks";
491
+ dataType: "boolean";
492
+ data: boolean;
493
+ driverParam: number;
494
+ notNull: true;
495
+ hasDefault: true;
496
+ isPrimaryKey: false;
497
+ isAutoincrement: false;
498
+ hasRuntimeDefault: false;
499
+ enumValues: undefined;
500
+ baseColumn: never;
501
+ identity: undefined;
502
+ generated: undefined;
503
+ }, {}>;
504
+ derivedAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
505
+ name: string;
506
+ tableName: "docs_wikilinks";
507
+ dataType: "string";
508
+ data: string;
509
+ driverParam: string;
510
+ notNull: true;
511
+ hasDefault: true;
512
+ isPrimaryKey: false;
513
+ isAutoincrement: false;
514
+ hasRuntimeDefault: false;
515
+ enumValues: [string, ...string[]];
516
+ baseColumn: never;
517
+ identity: undefined;
518
+ generated: undefined;
519
+ }, {}>;
520
+ };
521
+ dialect: "sqlite";
522
+ }>;
394
523
  export type AttachmentRow = typeof attachments.$inferSelect;
395
524
  export type NewAttachmentRow = typeof attachments.$inferInsert;
396
525
  export type AttachmentRefRow = typeof attachmentRefs.$inferSelect;
397
526
  export type NewAttachmentRefRow = typeof attachmentRefs.$inferInsert;
527
+ /** Row type for `docs_wikilinks` SELECT queries. */
528
+ export type DocsWikilinkRow = typeof docsWikilinks.$inferSelect;
529
+ /** Row type for `docs_wikilinks` INSERT operations. */
530
+ export type NewDocsWikilinkRow = typeof docsWikilinks.$inferInsert;
398
531
  //# sourceMappingURL=attachments.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../../../src/store/schema/attachments.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,gFAOzB,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,6BAA6B,oFAOhC,CAAC;AAEX,0DAA0D;AAC1D,MAAM,MAAM,yBAAyB,GAAG,CAAC,OAAO,6BAA6B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvF;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+HvB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqB1B,CAAC;AAIF,MAAM,MAAM,aAAa,GAAG,OAAO,WAAW,CAAC,YAAY,CAAC;AAC5D,MAAM,MAAM,gBAAgB,GAAG,OAAO,WAAW,CAAC,YAAY,CAAC;AAC/D,MAAM,MAAM,gBAAgB,GAAG,OAAO,cAAc,CAAC,YAAY,CAAC;AAClE,MAAM,MAAM,mBAAmB,GAAG,OAAO,cAAc,CAAC,YAAY,CAAC"}
1
+ {"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../../../src/store/schema/attachments.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,gFAOzB,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,6BAA6B,oFAOhC,CAAC;AAEX,0DAA0D;AAC1D,MAAM,MAAM,yBAAyB,GAAG,CAAC,OAAO,6BAA6B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvF;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+HvB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqB1B,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,uBAAuB,mEAK1B,CAAC;AAEX,+DAA+D;AAC/D,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoBzB,CAAC;AAIF,MAAM,MAAM,aAAa,GAAG,OAAO,WAAW,CAAC,YAAY,CAAC;AAC5D,MAAM,MAAM,gBAAgB,GAAG,OAAO,WAAW,CAAC,YAAY,CAAC;AAC/D,MAAM,MAAM,gBAAgB,GAAG,OAAO,cAAc,CAAC,YAAY,CAAC;AAClE,MAAM,MAAM,mBAAmB,GAAG,OAAO,cAAc,CAAC,YAAY,CAAC;AACrE,oDAAoD;AACpD,MAAM,MAAM,eAAe,GAAG,OAAO,aAAa,CAAC,YAAY,CAAC;AAChE,uDAAuD;AACvD,MAAM,MAAM,kBAAkB,GAAG,OAAO,aAAa,CAAC,YAAY,CAAC"}
@@ -4,6 +4,7 @@
4
4
  * @epic T760
5
5
  * @task T796
6
6
  */
7
+ import { sql } from 'drizzle-orm';
7
8
  import { index, integer, primaryKey, sqliteTable, text, } from 'drizzle-orm/sqlite-core';
8
9
  /**
9
10
  * Allowed owner-entity types for `attachment_refs.owner_type`.
@@ -196,4 +197,66 @@ export const attachmentRefs = sqliteTable('attachment_refs', {
196
197
  index('idx_attachment_refs_attachment_id').on(table.attachmentId),
197
198
  index('idx_attachment_refs_owner').on(table.ownerType, table.ownerId),
198
199
  ]);
200
+ /**
201
+ * Allowed relations for a `docs_wikilinks` edge.
202
+ *
203
+ * A wikilink is a DERIVED, slug-addressed edge between two docs (or a doc and a
204
+ * task) reconstructed from the authoritative provenance columns on
205
+ * `attachments` — there is no hand-authored edge here. The relation enumerates
206
+ * which source column produced the edge:
207
+ *
208
+ * - `supersedes` — `attachments.supersedes` (newer → older)
209
+ * - `superseded-by` — `attachments.superseded_by` (older → newer)
210
+ * - `related-task` — `attachments.related_tasks` JSON membership (doc → T####)
211
+ * - `topic` — `attachments.topics` JSON co-membership (doc ↔ doc sharing a topic)
212
+ *
213
+ * Kept slug-primary so the edge table is Obsidian-grade (vault links are
214
+ * slug-addressed) and survives attachment-id churn across versions.
215
+ *
216
+ * @task T11826 (Epic T11781 / Saga T11778)
217
+ */
218
+ export const DOCS_WIKILINK_RELATIONS = [
219
+ 'supersedes',
220
+ 'superseded-by',
221
+ 'related-task',
222
+ 'topic',
223
+ ];
224
+ /**
225
+ * `docs_wikilinks` — DERIVED, slug-addressed edge table for the docs graph.
226
+ *
227
+ * Per the ratified Docs-SSoT model (saga T11778): `cleo.db` is the SOLE doc
228
+ * authority, and `docs_wikilinks` is a *minimal edge table derived from*
229
+ * `supersedes` + `relatedTasks` + `topics` on `attachments`. It is NOT an
230
+ * authoritative input surface — it is rebuilt idempotently from the provenance
231
+ * columns by {@link import('../../docs/wikilinks.js').rebuildDocsWikilinks}.
232
+ *
233
+ * The table makes the bidirectional backlink graph queryable in O(edges)
234
+ * without recomputing the BFS, which is what the Obsidian plugin (T11827)
235
+ * renders. `cleo docs graph` (T10164) continues to compute the provenance BFS
236
+ * but hydrates persisted backlinks from this table when present.
237
+ *
238
+ * Edges are slug-primary; `to_slug` carries a doc slug for `topic` /
239
+ * `supersedes` / `superseded-by` edges and a `T####` task id for `related-task`
240
+ * edges (`to_is_task = 1`). No markdown body `[[link]]` parsing is performed
241
+ * (AC4) — the edges derive purely from structured provenance columns.
242
+ *
243
+ * @task T11826 (Epic T11781 / Saga T11778)
244
+ */
245
+ export const docsWikilinks = sqliteTable('docs_wikilinks', {
246
+ /** Source doc slug (→ `attachments.slug`). Always a doc. */
247
+ fromSlug: text('from_slug').notNull(),
248
+ /** Target slug — a doc slug, or a `T####` task id when `toIsTask = 1`. */
249
+ toSlug: text('to_slug').notNull(),
250
+ /** Which provenance column produced this edge — dispatch-validated, no SQL CHECK. */
251
+ relation: text('relation', { enum: DOCS_WIKILINK_RELATIONS }).notNull(),
252
+ /** 1 when `to_slug` is a task id (`related-task` edges); 0 for doc→doc edges. */
253
+ toIsTask: integer('to_is_task', { mode: 'boolean' }).notNull().default(false),
254
+ /** ISO-8601 UTC instant this edge was last (re)derived. */
255
+ derivedAt: text('derived_at').notNull().default(sql `(datetime('now'))`),
256
+ }, (table) => [
257
+ primaryKey({ columns: [table.fromSlug, table.toSlug, table.relation] }),
258
+ index('idx_docs_wikilinks_from').on(table.fromSlug),
259
+ index('idx_docs_wikilinks_to').on(table.toSlug),
260
+ index('idx_docs_wikilinks_relation').on(table.relation),
261
+ ]);
199
262
  //# sourceMappingURL=attachments.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../../src/store/schema/attachments.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../../src/store/schema/attachments.ts"],"names":[],"mappings":"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"}