@cleocode/contracts 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.
- package/dist/db-recovery.d.ts +63 -0
- package/dist/db-recovery.d.ts.map +1 -1
- package/dist/dispatch/operations-registry.d.ts.map +1 -1
- package/dist/dispatch/operations-registry.js +7 -1
- package/dist/dispatch/operations-registry.js.map +1 -1
- package/dist/docs/provenance.d.ts +6 -2
- package/dist/docs/provenance.d.ts.map +1 -1
- package/dist/docs/provenance.js +1 -0
- package/dist/docs/provenance.js.map +1 -1
- package/dist/docs/read.d.ts +148 -0
- package/dist/docs/read.d.ts.map +1 -0
- package/dist/docs/read.js +57 -0
- package/dist/docs/read.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/operations/docs.d.ts +13 -3
- package/dist/operations/docs.d.ts.map +1 -1
- package/dist/operations/docs.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
- package/src/db-recovery.ts +65 -0
- package/src/dispatch/operations-registry.ts +7 -1
- package/src/docs/provenance.ts +4 -1
- package/src/docs/read.ts +131 -0
- package/src/index.ts +10 -0
- package/src/operations/docs.ts +13 -3
package/src/db-recovery.ts
CHANGED
|
@@ -142,3 +142,68 @@ export interface BackupRecoverResult {
|
|
|
142
142
|
*/
|
|
143
143
|
dryRun: boolean;
|
|
144
144
|
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Per-role outcome of a `cleo doctor repair` pass (T11829 · DHQ-060).
|
|
148
|
+
*
|
|
149
|
+
* `doctor repair` first PROBES each role's live DB via `PRAGMA quick_check`
|
|
150
|
+
* (the same probe the recovery pipeline runs on snapshot candidates) and only
|
|
151
|
+
* invokes the {@link recoverMalformedDb} → quarantine → restore-from-snapshot →
|
|
152
|
+
* verify pipeline for roles that are actually malformed. This record captures
|
|
153
|
+
* what was observed and done for one role.
|
|
154
|
+
*
|
|
155
|
+
* @task T11829
|
|
156
|
+
* @epic T11833
|
|
157
|
+
* @saga T11242
|
|
158
|
+
* @public
|
|
159
|
+
*/
|
|
160
|
+
export interface DoctorRepairRoleResult {
|
|
161
|
+
/** Canonical role inspected. */
|
|
162
|
+
role: DbRole;
|
|
163
|
+
/** Absolute path to the role's live DB file (resolved from `DB_INVENTORY`). */
|
|
164
|
+
dbPath: string;
|
|
165
|
+
/** `true` when the live DB file exists on disk. */
|
|
166
|
+
present: boolean;
|
|
167
|
+
/**
|
|
168
|
+
* `true` when the live DB passed `PRAGMA quick_check` (or was absent) — i.e.
|
|
169
|
+
* NO repair was needed. `false` when corruption was detected.
|
|
170
|
+
*/
|
|
171
|
+
healthy: boolean;
|
|
172
|
+
/**
|
|
173
|
+
* `'skipped'` — healthy or absent, nothing done;
|
|
174
|
+
* `'would-repair'` — `--dry-run`, corruption detected, repair NOT performed;
|
|
175
|
+
* `'repaired'` — corruption detected and snapshot restore succeeded;
|
|
176
|
+
* `'failed'` — corruption detected but recovery could not complete.
|
|
177
|
+
*/
|
|
178
|
+
action: 'skipped' | 'would-repair' | 'repaired' | 'failed';
|
|
179
|
+
/** Absolute path to the snapshot restored (or that would be), when applicable. */
|
|
180
|
+
restoredFrom: string | null;
|
|
181
|
+
/** Absolute path to the quarantined corrupt DB, when a repair was performed. */
|
|
182
|
+
quarantinedTo: string | null;
|
|
183
|
+
/** Approximate data-loss window in hours, when a snapshot timestamp was parsed. */
|
|
184
|
+
dataLossWindowHours: number | null;
|
|
185
|
+
/** Human-readable detail (probe outcome, error reason, or restore summary). */
|
|
186
|
+
detail: string;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Aggregate result of a `cleo doctor repair` invocation across one or more roles
|
|
191
|
+
* (T11829 · DHQ-060).
|
|
192
|
+
*
|
|
193
|
+
* @task T11829
|
|
194
|
+
* @epic T11833
|
|
195
|
+
* @saga T11242
|
|
196
|
+
* @public
|
|
197
|
+
*/
|
|
198
|
+
export interface DoctorRepairResult {
|
|
199
|
+
/** `true` when the run was a `--dry-run` (no files mutated). */
|
|
200
|
+
dryRun: boolean;
|
|
201
|
+
/** Per-role outcomes. */
|
|
202
|
+
roles: DoctorRepairRoleResult[];
|
|
203
|
+
/** Count of roles found malformed (`!healthy`). */
|
|
204
|
+
malformedCount: number;
|
|
205
|
+
/** Count of roles actually repaired this run. */
|
|
206
|
+
repairedCount: number;
|
|
207
|
+
/** Count of roles where a needed repair FAILED. */
|
|
208
|
+
failedCount: number;
|
|
209
|
+
}
|
|
@@ -7117,7 +7117,7 @@ export const OPERATIONS: OperationDef[] = [
|
|
|
7117
7117
|
domain: 'docs',
|
|
7118
7118
|
operation: 'add',
|
|
7119
7119
|
description:
|
|
7120
|
-
'docs.add (mutate) — attach a local file or
|
|
7120
|
+
'docs.add (mutate) — attach a local file, URL, or inline content to a CLEO owner entity (task, session, observation)',
|
|
7121
7121
|
tier: 1,
|
|
7122
7122
|
idempotent: false,
|
|
7123
7123
|
sessionRequired: false,
|
|
@@ -7141,6 +7141,12 @@ export const OPERATIONS: OperationDef[] = [
|
|
|
7141
7141
|
required: false,
|
|
7142
7142
|
description: 'Remote URL to attach',
|
|
7143
7143
|
},
|
|
7144
|
+
{
|
|
7145
|
+
name: 'content',
|
|
7146
|
+
type: 'string' as const,
|
|
7147
|
+
required: false,
|
|
7148
|
+
description: 'Inline document body (T10965); mutually exclusive with file/url',
|
|
7149
|
+
},
|
|
7144
7150
|
{
|
|
7145
7151
|
name: 'desc',
|
|
7146
7152
|
type: 'string' as const,
|
package/src/docs/provenance.ts
CHANGED
|
@@ -74,6 +74,7 @@ export const PROVENANCE_NODE_KINDS = [
|
|
|
74
74
|
* - `related-task` — child task is related (non-parent) to parent
|
|
75
75
|
* - `linked-decision` — node references a BRAIN decision
|
|
76
76
|
* - `derived-from` — node was derived from a prior artifact
|
|
77
|
+
* - `shares-topic` — two docs co-occur on a canonical topic slug (T11826)
|
|
77
78
|
*
|
|
78
79
|
* @task T10166
|
|
79
80
|
*/
|
|
@@ -83,7 +84,8 @@ export type ProvenanceEdgeRelation =
|
|
|
83
84
|
| 'superseded-by'
|
|
84
85
|
| 'related-task'
|
|
85
86
|
| 'linked-decision'
|
|
86
|
-
| 'derived-from'
|
|
87
|
+
| 'derived-from'
|
|
88
|
+
| 'shares-topic';
|
|
87
89
|
|
|
88
90
|
/**
|
|
89
91
|
* Frozen const array backing {@link ProvenanceEdgeRelation} — used by the
|
|
@@ -98,6 +100,7 @@ export const PROVENANCE_EDGE_RELATIONS = [
|
|
|
98
100
|
'related-task',
|
|
99
101
|
'linked-decision',
|
|
100
102
|
'derived-from',
|
|
103
|
+
'shares-topic',
|
|
101
104
|
] as const satisfies ReadonlyArray<ProvenanceEdgeRelation>;
|
|
102
105
|
|
|
103
106
|
/**
|
package/src/docs/read.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `docs.read` core-SDK contract — the envelope-typed surface an external
|
|
3
|
+
* consumer (the Obsidian plugin, `cleo docs view`) calls to render a single
|
|
4
|
+
* CLEO doc directly from `cleo.db`, the SOLE doc authority (saga T11778).
|
|
5
|
+
*
|
|
6
|
+
* The response carries the doc **body** plus its full provenance **frontmatter**
|
|
7
|
+
* (slug, version anchors, supersession pointers, related tasks, topics). Binary
|
|
8
|
+
* or non-UTF-8 bodies are surfaced as base64 (T11825 AC2) so a consumer can
|
|
9
|
+
* render images/PDFs/files stored as blobs without a second round-trip.
|
|
10
|
+
*
|
|
11
|
+
* Part of the T10400 core-SDK API standard — this is a typed contract, not an
|
|
12
|
+
* ad-hoc shape (T11825 AC3).
|
|
13
|
+
*
|
|
14
|
+
* @task T11825 (Epic T11781 / Saga T11778)
|
|
15
|
+
* @adr ADR-078 — Docs SSoT as provenance graph
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Provenance frontmatter for a doc, sourced from the `attachments` columns that
|
|
22
|
+
* back the docs SSoT. Mirrors what a published doc's YAML frontmatter would
|
|
23
|
+
* carry, but read live from the DB rather than a file.
|
|
24
|
+
*
|
|
25
|
+
* @task T11825
|
|
26
|
+
*/
|
|
27
|
+
export interface DocFrontmatter {
|
|
28
|
+
/** Stable human-readable slug (`attachments.slug`). */
|
|
29
|
+
readonly slug: string;
|
|
30
|
+
/** Doc-kind taxonomy tag (e.g. `adr`, `spec`), or null for untyped blobs. */
|
|
31
|
+
readonly kind: string | null;
|
|
32
|
+
/** Display title (falls back to slug). */
|
|
33
|
+
readonly title: string | null;
|
|
34
|
+
/** One-sentence human summary, distinct from the body. */
|
|
35
|
+
readonly summary: string | null;
|
|
36
|
+
/** Document workflow state (`draft | active | superseded | …`). */
|
|
37
|
+
readonly lifecycleStatus: string;
|
|
38
|
+
/** Sequential doc-version counter for this slug (`attachments.doc_version`). */
|
|
39
|
+
readonly docVersion: number;
|
|
40
|
+
/** CLEO release version that wrote this row (`attachments.owner_version`). */
|
|
41
|
+
readonly ownerVersion: string | null;
|
|
42
|
+
/** Slug of the doc this one supersedes, or null. */
|
|
43
|
+
readonly supersedes: string | null;
|
|
44
|
+
/** Slug of the doc that supersedes this one, or null. */
|
|
45
|
+
readonly supersededBy: string | null;
|
|
46
|
+
/** Canonical topic slugs this doc carries (`attachments.topics`). */
|
|
47
|
+
readonly topics: readonly string[];
|
|
48
|
+
/** `T####` task ids this doc relates to (`attachments.related_tasks`). */
|
|
49
|
+
readonly relatedTasks: readonly string[];
|
|
50
|
+
/** SHA-256 content address of the body. */
|
|
51
|
+
readonly sha256: string;
|
|
52
|
+
/** ISO-8601 creation instant. */
|
|
53
|
+
readonly createdAt: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The body payload of a doc read.
|
|
58
|
+
*
|
|
59
|
+
* Exactly one of {@link text} / {@link base64} is populated: UTF-8-decodable
|
|
60
|
+
* content is surfaced as `text` with `encoding: 'utf-8'`; binary content is
|
|
61
|
+
* surfaced as `base64` with `encoding: 'base64'`.
|
|
62
|
+
*
|
|
63
|
+
* @task T11825
|
|
64
|
+
*/
|
|
65
|
+
export interface DocBody {
|
|
66
|
+
/** How {@link DocReadResponse.body} content is encoded. */
|
|
67
|
+
readonly encoding: 'utf-8' | 'base64';
|
|
68
|
+
/** UTF-8 body text, present when `encoding === 'utf-8'`. */
|
|
69
|
+
readonly text?: string;
|
|
70
|
+
/** Base64-encoded body bytes, present when `encoding === 'base64'`. */
|
|
71
|
+
readonly base64?: string;
|
|
72
|
+
/** Decoded byte length of the body. */
|
|
73
|
+
readonly sizeBytes: number;
|
|
74
|
+
/** IANA MIME type, when known. */
|
|
75
|
+
readonly mimeType: string | null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The full `docs.read` response envelope payload.
|
|
80
|
+
*
|
|
81
|
+
* @task T11825
|
|
82
|
+
*/
|
|
83
|
+
export interface DocReadResponse {
|
|
84
|
+
/** Provenance frontmatter. */
|
|
85
|
+
readonly frontmatter: DocFrontmatter;
|
|
86
|
+
/** Rendered body (text or base64). */
|
|
87
|
+
readonly body: DocBody;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Zod schema for {@link DocFrontmatter}. */
|
|
91
|
+
export const docFrontmatterSchema = z.object({
|
|
92
|
+
slug: z.string(),
|
|
93
|
+
kind: z.string().nullable(),
|
|
94
|
+
title: z.string().nullable(),
|
|
95
|
+
summary: z.string().nullable(),
|
|
96
|
+
lifecycleStatus: z.string(),
|
|
97
|
+
docVersion: z.number().int(),
|
|
98
|
+
ownerVersion: z.string().nullable(),
|
|
99
|
+
supersedes: z.string().nullable(),
|
|
100
|
+
supersededBy: z.string().nullable(),
|
|
101
|
+
topics: z.array(z.string()).readonly(),
|
|
102
|
+
relatedTasks: z.array(z.string()).readonly(),
|
|
103
|
+
sha256: z.string(),
|
|
104
|
+
createdAt: z.string(),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
/** Zod schema for {@link DocBody}. */
|
|
108
|
+
export const docBodySchema = z.object({
|
|
109
|
+
encoding: z.enum(['utf-8', 'base64']),
|
|
110
|
+
text: z.string().optional(),
|
|
111
|
+
base64: z.string().optional(),
|
|
112
|
+
sizeBytes: z.number().int().nonnegative(),
|
|
113
|
+
mimeType: z.string().nullable(),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
/** Zod schema for {@link DocReadResponse}. */
|
|
117
|
+
export const docReadResponseSchema = z.object({
|
|
118
|
+
frontmatter: docFrontmatterSchema,
|
|
119
|
+
body: docBodySchema,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Type guard for {@link DocReadResponse}.
|
|
124
|
+
*
|
|
125
|
+
* @param value - Unknown value to validate.
|
|
126
|
+
* @returns True when `value` conforms to the `docs.read` response schema.
|
|
127
|
+
* @task T11825
|
|
128
|
+
*/
|
|
129
|
+
export function isDocReadResponse(value: unknown): value is DocReadResponse {
|
|
130
|
+
return docReadResponseSchema.safeParse(value).success;
|
|
131
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -346,6 +346,8 @@ export type {
|
|
|
346
346
|
BackupRecoverResult,
|
|
347
347
|
DbRecoveredRowCounts,
|
|
348
348
|
DbRecoveryResult,
|
|
349
|
+
DoctorRepairResult,
|
|
350
|
+
DoctorRepairRoleResult,
|
|
349
351
|
} from './db-recovery.js';
|
|
350
352
|
// === Dependency Registry Contracts ===
|
|
351
353
|
export type {
|
|
@@ -398,6 +400,14 @@ export {
|
|
|
398
400
|
provenanceSessionNodeSchema,
|
|
399
401
|
provenanceTaskNodeSchema,
|
|
400
402
|
} from './docs/provenance.js';
|
|
403
|
+
// === docs.read core-SDK Contract (T11825) ===
|
|
404
|
+
export type { DocBody, DocFrontmatter, DocReadResponse } from './docs/read.js';
|
|
405
|
+
export {
|
|
406
|
+
docBodySchema,
|
|
407
|
+
docFrontmatterSchema,
|
|
408
|
+
docReadResponseSchema,
|
|
409
|
+
isDocReadResponse,
|
|
410
|
+
} from './docs/read.js';
|
|
401
411
|
// === DocsAccessor Contracts (T9063) ===
|
|
402
412
|
export type {
|
|
403
413
|
DocExportFormat,
|
package/src/operations/docs.ts
CHANGED
|
@@ -383,15 +383,25 @@ export interface DocsGenerateResult {
|
|
|
383
383
|
/**
|
|
384
384
|
* Parameters for `docs.add`.
|
|
385
385
|
*
|
|
386
|
-
* Exactly one of `file` or `
|
|
386
|
+
* Exactly one of `file`, `url`, or `content` must be provided.
|
|
387
387
|
*/
|
|
388
388
|
export interface DocsAddParams {
|
|
389
389
|
/** Owner entity ID (task, session, observation, etc.) — required. */
|
|
390
390
|
ownerId: string;
|
|
391
|
-
/** Local file path to attach (mutually exclusive with `url`). */
|
|
391
|
+
/** Local file path to attach (mutually exclusive with `url` and `content`). */
|
|
392
392
|
file?: string;
|
|
393
|
-
/** URL to attach as reference (mutually exclusive with `file`). */
|
|
393
|
+
/** URL to attach as reference (mutually exclusive with `file` and `content`). */
|
|
394
394
|
url?: string;
|
|
395
|
+
/**
|
|
396
|
+
* Inline document body authored directly on the command line or piped via
|
|
397
|
+
* stdin (`--content -`). Mutually exclusive with `file` and `url`. Stored
|
|
398
|
+
* as a `text/markdown` blob exactly as a file-sourced add would be —
|
|
399
|
+
* including the manifest.db mirror, slug reservation, memory observation,
|
|
400
|
+
* and audit trail — so a doc can be created without a pre-existing file.
|
|
401
|
+
*
|
|
402
|
+
* @task T10965
|
|
403
|
+
*/
|
|
404
|
+
content?: string;
|
|
395
405
|
/** Optional human-readable description. */
|
|
396
406
|
desc?: string;
|
|
397
407
|
/** Optional comma-separated labels for categorization. */
|