@cleocode/core 2026.10.12 → 2026.10.13-rc.ci.682
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/build-provenance.json +2 -2
- package/dist/cloud/nexus-auth.d.ts.map +1 -1
- package/dist/cloud/nexus-auth.js +11 -2
- package/dist/cloud/nexus-auth.js.map +1 -1
- package/dist/cloud/nexus-cloud-status.d.ts.map +1 -1
- package/dist/cloud/nexus-cloud-status.js +18 -5
- package/dist/cloud/nexus-cloud-status.js.map +1 -1
- package/dist/cloud/nexus-credentials.d.ts +3 -2
- package/dist/cloud/nexus-credentials.d.ts.map +1 -1
- package/dist/cloud/nexus-credentials.js +58 -14
- package/dist/cloud/nexus-credentials.js.map +1 -1
- package/dist/cloud/nexus-logout.d.ts.map +1 -1
- package/dist/cloud/nexus-logout.js +12 -2
- package/dist/cloud/nexus-logout.js.map +1 -1
- package/dist/cloud/nexus-vault-state.d.ts.map +1 -1
- package/dist/cloud/nexus-vault-state.js +6 -26
- package/dist/cloud/nexus-vault-state.js.map +1 -1
- package/dist/config/registry.d.ts +21 -0
- package/dist/config/registry.d.ts.map +1 -1
- package/dist/config/registry.js +41 -0
- package/dist/config/registry.js.map +1 -1
- package/dist/dispatch/mvi-projection.d.ts.map +1 -1
- package/dist/dispatch/mvi-projection.js +2 -0
- package/dist/dispatch/mvi-projection.js.map +1 -1
- package/dist/docs/docs-ops.d.ts +15 -14
- package/dist/docs/docs-ops.d.ts.map +1 -1
- package/dist/docs/docs-ops.js +32 -96
- package/dist/docs/docs-ops.js.map +1 -1
- package/dist/docs/docs-read-model.d.ts +19 -10
- package/dist/docs/docs-read-model.d.ts.map +1 -1
- package/dist/docs/docs-read-model.js +31 -47
- package/dist/docs/docs-read-model.js.map +1 -1
- package/dist/docs/docs-update.d.ts +51 -1
- package/dist/docs/docs-update.d.ts.map +1 -1
- package/dist/docs/docs-update.js +235 -13
- package/dist/docs/docs-update.js.map +1 -1
- package/dist/docs/doctor.d.ts +26 -4
- package/dist/docs/doctor.d.ts.map +1 -1
- package/dist/docs/doctor.js +260 -102
- package/dist/docs/doctor.js.map +1 -1
- package/dist/docs/fetch-result.d.ts +30 -10
- package/dist/docs/fetch-result.d.ts.map +1 -1
- package/dist/docs/fetch-result.js +100 -15
- package/dist/docs/fetch-result.js.map +1 -1
- package/dist/docs/index.d.ts +1 -0
- package/dist/docs/index.d.ts.map +1 -1
- package/dist/docs/index.js +1 -0
- package/dist/docs/index.js.map +1 -1
- package/dist/docs/legacy-doc-migration.d.ts +182 -0
- package/dist/docs/legacy-doc-migration.d.ts.map +1 -0
- package/dist/docs/legacy-doc-migration.js +480 -0
- package/dist/docs/legacy-doc-migration.js.map +1 -0
- package/dist/docs/publications-store.d.ts +67 -0
- package/dist/docs/publications-store.d.ts.map +1 -0
- package/dist/docs/publications-store.js +193 -0
- package/dist/docs/publications-store.js.map +1 -0
- package/dist/docs/publish-pr.d.ts.map +1 -1
- package/dist/docs/publish-pr.js +31 -18
- package/dist/docs/publish-pr.js.map +1 -1
- package/dist/docs/section-leases.d.ts +233 -0
- package/dist/docs/section-leases.d.ts.map +1 -0
- package/dist/docs/section-leases.js +344 -0
- package/dist/docs/section-leases.js.map +1 -0
- package/dist/docs/section-merge.d.ts +159 -0
- package/dist/docs/section-merge.d.ts.map +1 -0
- package/dist/docs/section-merge.js +215 -0
- package/dist/docs/section-merge.js.map +1 -0
- package/dist/docs/section-write-path.d.ts +191 -0
- package/dist/docs/section-write-path.d.ts.map +1 -0
- package/dist/docs/section-write-path.js +423 -0
- package/dist/docs/section-write-path.js.map +1 -0
- package/dist/docs/stats.d.ts +56 -0
- package/dist/docs/stats.d.ts.map +1 -0
- package/dist/docs/stats.js +119 -0
- package/dist/docs/stats.js.map +1 -0
- package/dist/gc/cleanup.d.ts +1 -1
- package/dist/gc/cleanup.d.ts.map +1 -1
- package/dist/gc/cleanup.js +10 -1
- package/dist/gc/cleanup.js.map +1 -1
- package/dist/gc/janitor.js +1 -1
- package/dist/gc/janitor.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/memory/pipeline-manifest-sqlite.d.ts +34 -0
- package/dist/memory/pipeline-manifest-sqlite.d.ts.map +1 -1
- package/dist/memory/pipeline-manifest-sqlite.js +114 -0
- package/dist/memory/pipeline-manifest-sqlite.js.map +1 -1
- package/dist/nexus/analyze-orchestrator.d.ts +5 -1
- package/dist/nexus/analyze-orchestrator.d.ts.map +1 -1
- package/dist/nexus/analyze-orchestrator.js +25 -13
- package/dist/nexus/analyze-orchestrator.js.map +1 -1
- package/dist/nexus/assessment-store.d.ts +8 -4
- package/dist/nexus/assessment-store.d.ts.map +1 -1
- package/dist/nexus/assessment-store.js +18 -27
- package/dist/nexus/assessment-store.js.map +1 -1
- package/dist/orchestration/index.d.ts.map +1 -1
- package/dist/orchestration/index.js +6 -0
- package/dist/orchestration/index.js.map +1 -1
- package/dist/orchestration/lean-change.d.ts +104 -5
- package/dist/orchestration/lean-change.d.ts.map +1 -1
- package/dist/orchestration/lean-change.js +162 -6
- package/dist/orchestration/lean-change.js.map +1 -1
- package/dist/orchestration/spawn-prompt.d.ts +6 -1
- package/dist/orchestration/spawn-prompt.d.ts.map +1 -1
- package/dist/orchestration/spawn-prompt.js +3 -1
- package/dist/orchestration/spawn-prompt.js.map +1 -1
- package/dist/orchestration/spawn.d.ts.map +1 -1
- package/dist/orchestration/spawn.js +6 -0
- package/dist/orchestration/spawn.js.map +1 -1
- package/dist/release/open.d.ts.map +1 -1
- package/dist/release/open.js +6 -2
- package/dist/release/open.js.map +1 -1
- package/dist/release/plan.d.ts.map +1 -1
- package/dist/release/plan.js +33 -2
- package/dist/release/plan.js.map +1 -1
- package/dist/resources/admission-ledger.d.ts +5 -0
- package/dist/resources/admission-ledger.d.ts.map +1 -1
- package/dist/resources/admission-ledger.js +1 -1
- package/dist/resources/admission-ledger.js.map +1 -1
- package/dist/resources/run-admission.d.ts.map +1 -1
- package/dist/resources/run-admission.js +4 -1
- package/dist/resources/run-admission.js.map +1 -1
- package/dist/resources/run-governed.d.ts +29 -4
- package/dist/resources/run-governed.d.ts.map +1 -1
- package/dist/resources/run-governed.js +113 -73
- package/dist/resources/run-governed.js.map +1 -1
- package/dist/sentient/worktree-dispatch.d.ts +12 -1
- package/dist/sentient/worktree-dispatch.d.ts.map +1 -1
- package/dist/sentient/worktree-dispatch.js +19 -0
- package/dist/sentient/worktree-dispatch.js.map +1 -1
- package/dist/spawn/branch-lock.d.ts +11 -1
- package/dist/spawn/branch-lock.d.ts.map +1 -1
- package/dist/spawn/branch-lock.js +54 -4
- package/dist/spawn/branch-lock.js.map +1 -1
- package/dist/sticky/archive.d.ts +1 -0
- package/dist/sticky/archive.d.ts.map +1 -1
- package/dist/sticky/archive.js +13 -0
- package/dist/sticky/archive.js.map +1 -1
- package/dist/sticky/engine-ops.d.ts.map +1 -1
- package/dist/sticky/engine-ops.js +7 -0
- package/dist/sticky/engine-ops.js.map +1 -1
- package/dist/store/attachment-repair.d.ts +10 -4
- package/dist/store/attachment-repair.d.ts.map +1 -1
- package/dist/store/attachment-repair.js +52 -25
- package/dist/store/attachment-repair.js.map +1 -1
- package/dist/store/attachment-store.d.ts.map +1 -1
- package/dist/store/attachment-store.js +27 -18
- package/dist/store/attachment-store.js.map +1 -1
- package/dist/store/blob-keep.d.ts +83 -0
- package/dist/store/blob-keep.d.ts.map +1 -1
- package/dist/store/blob-keep.js +139 -0
- package/dist/store/blob-keep.js.map +1 -1
- package/dist/store/blob-ops.d.ts +24 -0
- package/dist/store/blob-ops.d.ts.map +1 -1
- package/dist/store/blob-ops.js +47 -0
- package/dist/store/blob-ops.js.map +1 -1
- package/dist/store/conduit-doc-history.d.ts +209 -0
- package/dist/store/conduit-doc-history.d.ts.map +1 -1
- package/dist/store/conduit-doc-history.js +315 -3
- package/dist/store/conduit-doc-history.js.map +1 -1
- package/dist/store/decision-adr-links.d.ts +11 -0
- package/dist/store/decision-adr-links.d.ts.map +1 -1
- package/dist/store/decision-adr-links.js +20 -1
- package/dist/store/decision-adr-links.js.map +1 -1
- package/dist/store/display-id-alias.d.ts +8 -2
- package/dist/store/display-id-alias.d.ts.map +1 -1
- package/dist/store/display-id-alias.js +8 -3
- package/dist/store/display-id-alias.js.map +1 -1
- package/dist/store/lock.d.ts +18 -0
- package/dist/store/lock.d.ts.map +1 -1
- package/dist/store/lock.js +41 -0
- package/dist/store/lock.js.map +1 -1
- package/dist/store/row-identity-registry.d.ts.map +1 -1
- package/dist/store/row-identity-registry.js +17 -0
- package/dist/store/row-identity-registry.js.map +1 -1
- package/dist/store/schema/cleo-project/conduit.d.ts +16 -0
- package/dist/store/schema/cleo-project/conduit.d.ts.map +1 -1
- package/dist/store/schema/cleo-project/conduit.js +8 -0
- package/dist/store/schema/cleo-project/conduit.js.map +1 -1
- package/dist/store/schema/cleo-project/docs.d.ts +294 -0
- package/dist/store/schema/cleo-project/docs.d.ts.map +1 -1
- package/dist/store/schema/cleo-project/docs.js +82 -0
- package/dist/store/schema/cleo-project/docs.js.map +1 -1
- package/dist/store/sync/hlc.d.ts +9 -0
- package/dist/store/sync/hlc.d.ts.map +1 -1
- package/dist/store/sync/hlc.js +9 -0
- package/dist/store/sync/hlc.js.map +1 -1
- package/dist/store/sync/merge/engine.d.ts.map +1 -1
- package/dist/store/sync/merge/engine.js +4 -2
- package/dist/store/sync/merge/engine.js.map +1 -1
- package/dist/store/sync/merge/rules.d.ts +12 -1
- package/dist/store/sync/merge/rules.d.ts.map +1 -1
- package/dist/store/sync/merge/rules.js +76 -1
- package/dist/store/sync/merge/rules.js.map +1 -1
- package/dist/store/sync/replica-registry.d.ts +6 -2
- package/dist/store/sync/replica-registry.d.ts.map +1 -1
- package/dist/store/sync/replica-registry.js +21 -9
- package/dist/store/sync/replica-registry.js.map +1 -1
- package/dist/store/sync/timestamp-canon.d.ts.map +1 -1
- package/dist/store/sync/timestamp-canon.js +6 -0
- package/dist/store/sync/timestamp-canon.js.map +1 -1
- package/dist/store/table-classification.d.ts +58 -0
- package/dist/store/table-classification.d.ts.map +1 -1
- package/dist/store/table-classification.js +72 -0
- package/dist/store/table-classification.js.map +1 -1
- package/dist/store/twin-collapse.d.ts.map +1 -1
- package/dist/store/twin-collapse.js +4 -1
- package/dist/store/twin-collapse.js.map +1 -1
- package/dist/store/vault-manifest.d.ts +10 -1
- package/dist/store/vault-manifest.d.ts.map +1 -1
- package/dist/store/vault-manifest.js +59 -1
- package/dist/store/vault-manifest.js.map +1 -1
- package/dist/tasks/affected-scope.d.ts +11 -0
- package/dist/tasks/affected-scope.d.ts.map +1 -1
- package/dist/tasks/affected-scope.js +30 -11
- package/dist/tasks/affected-scope.js.map +1 -1
- package/dist/tasks/cancel-ops.d.ts.map +1 -1
- package/dist/tasks/cancel-ops.js +7 -2
- package/dist/tasks/cancel-ops.js.map +1 -1
- package/dist/tasks/change-set.d.ts +22 -0
- package/dist/tasks/change-set.d.ts.map +1 -1
- package/dist/tasks/change-set.js +119 -2
- package/dist/tasks/change-set.js.map +1 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/dist/tasks/complete.js +7 -0
- package/dist/tasks/complete.js.map +1 -1
- package/dist/tasks/done-plan.d.ts +5 -0
- package/dist/tasks/done-plan.d.ts.map +1 -1
- package/dist/tasks/done-plan.js +54 -19
- package/dist/tasks/done-plan.js.map +1 -1
- package/dist/tasks/engine-wrap.d.ts +4 -0
- package/dist/tasks/engine-wrap.d.ts.map +1 -1
- package/dist/tasks/engine-wrap.js +20 -4
- package/dist/tasks/engine-wrap.js.map +1 -1
- package/dist/tasks/evidence-paths.d.ts +11 -0
- package/dist/tasks/evidence-paths.d.ts.map +1 -1
- package/dist/tasks/evidence-paths.js +12 -0
- package/dist/tasks/evidence-paths.js.map +1 -1
- package/dist/tools/fs.d.ts +7 -3
- package/dist/tools/fs.d.ts.map +1 -1
- package/dist/tools/fs.js +23 -8
- package/dist/tools/fs.js.map +1 -1
- package/dist/worktree/list.d.ts.map +1 -1
- package/dist/worktree/list.js +9 -0
- package/dist/worktree/list.js.map +1 -1
- package/dist/worktree/prune.d.ts.map +1 -1
- package/dist/worktree/prune.js +52 -1
- package/dist/worktree/prune.js.map +1 -1
- package/migrations/drizzle-cleo-project/20261013000000_t13360-migrated-doc-import/migration.sql +61 -0
- package/migrations/drizzle-cleo-project/20261014000000_t13362-docs-publications-ledger/migration.sql +45 -0
- package/package.json +13 -13
- package/templates/CLEO-INJECTION.md +1 -1
- package/templates/CLEO-REFERENCE.md +2 -2
|
@@ -30,6 +30,32 @@
|
|
|
30
30
|
* @saga T13339
|
|
31
31
|
*/
|
|
32
32
|
import { conduitAttachmentApprovals, conduitAttachmentVersions, conduitDocHeads, sectionCrdtStates } from './schema/cleo-project/conduit.js';
|
|
33
|
+
import type { ConduitDocMigrationLedgerRow } from './schema/cleo-project/docs.js';
|
|
34
|
+
/**
|
|
35
|
+
* Run one docs-projection write batch atomically (T13583).
|
|
36
|
+
*
|
|
37
|
+
* The section write path composes several accessors — section-state writes,
|
|
38
|
+
* the version append, the head upsert, the rebase and the derived
|
|
39
|
+
* contributors rebuild — and a failure halfway must not leave a non-head
|
|
40
|
+
* version behind for the next update to fork onto. The drizzle handle from
|
|
41
|
+
* {@link getDb} and the raw handle from {@link getNativeDb} are the same
|
|
42
|
+
* underlying `DatabaseSync`, so a `BEGIN IMMEDIATE` on the raw handle
|
|
43
|
+
* brackets every statement the accessors issue (the same pattern
|
|
44
|
+
* `docs-update.ts` uses for its slug transfer).
|
|
45
|
+
*
|
|
46
|
+
* Constraint (T13583): every statement inside `fn` must resolve without a
|
|
47
|
+
* real I/O yield — the docs projection satisfies this (sync-driver
|
|
48
|
+
* accessors, sync `node:fs` blob reads/writes), so no other async writer can
|
|
49
|
+
* interleave on this connection mid-transaction. A caller that awaits real
|
|
50
|
+
* I/O inside `fn` breaks that guarantee; per-handle writer serialization is
|
|
51
|
+
* the follow-up if such a caller appears.
|
|
52
|
+
*
|
|
53
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
54
|
+
* @param fn - The write batch; awaited between statements on this one
|
|
55
|
+
* connection, so no other writer can interleave inside the transaction.
|
|
56
|
+
* @returns What `fn` returned.
|
|
57
|
+
*/
|
|
58
|
+
export declare function withConduitDocWriteTransaction<T>(projectRoot: string, fn: () => Promise<T>): Promise<T>;
|
|
33
59
|
/** Row shape of `conduit_attachment_versions` as read back. */
|
|
34
60
|
export type ConduitAttachmentVersionRow = typeof conduitAttachmentVersions.$inferSelect;
|
|
35
61
|
/** Row shape of `conduit_attachment_approvals` as read back. */
|
|
@@ -46,6 +72,12 @@ export interface ConduitDocHeadConflict {
|
|
|
46
72
|
readonly hlc: string;
|
|
47
73
|
/** ISO-8601 instant the conflict was recorded. */
|
|
48
74
|
readonly recordedAt: string;
|
|
75
|
+
/**
|
|
76
|
+
* The winning head's version uid at recording time — both sides of the
|
|
77
|
+
* conflict are named on the row (head `head_version_uid` + this).
|
|
78
|
+
* (T13595)
|
|
79
|
+
*/
|
|
80
|
+
readonly winnerVersionUid?: string;
|
|
49
81
|
}
|
|
50
82
|
/**
|
|
51
83
|
* Resolve a document slug to its immutable row uid.
|
|
@@ -59,6 +91,38 @@ export interface ConduitDocHeadConflict {
|
|
|
59
91
|
* @returns The doc's `conduit_attachments.uid`, or `null` when unknown.
|
|
60
92
|
*/
|
|
61
93
|
export declare function resolveConduitDocUid(projectRoot: string, slug: string): Promise<string | null>;
|
|
94
|
+
/**
|
|
95
|
+
* Adopt a document into the conduit family on first sight (T13355).
|
|
96
|
+
*
|
|
97
|
+
* The docs write path (`docs/section-write-path.ts`) projects every doc
|
|
98
|
+
* update through the section CRDT layer, whose history tables are keyed on
|
|
99
|
+
* `conduit_attachments.uid`; a doc that only exists in the legacy
|
|
100
|
+
* `attachments` registry has no conduit row yet, so this inserts one. The
|
|
101
|
+
* row is a thin identity shell: the bytes live in the blob store, so
|
|
102
|
+
* `content` carries an empty buffer and the sizes/hash describe nothing —
|
|
103
|
+
* the version rows carry the real content addressing.
|
|
104
|
+
*
|
|
105
|
+
* `conversation_id` is the NOT NULL transport owner; docs adopted here use
|
|
106
|
+
* the `cleodocs` sentinel (no FK — soft reference by design). Idempotent:
|
|
107
|
+
* an existing live row (or alias-resolved uid) is returned with
|
|
108
|
+
* `adopted: false`.
|
|
109
|
+
*
|
|
110
|
+
* The adopted row's uid is the schema's random UUIDv7 default — a
|
|
111
|
+
* single-device mint. Two devices adopting the same legacy slug concurrently
|
|
112
|
+
* mint two uids for one doc; deduping that is the filed follow-up (T13586).
|
|
113
|
+
*
|
|
114
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
115
|
+
* @param input - Slug, author and current full text of the doc.
|
|
116
|
+
* @returns The doc's immutable uid and whether this call created the row.
|
|
117
|
+
*/
|
|
118
|
+
export declare function ensureConduitDoc(projectRoot: string, input: {
|
|
119
|
+
slug: string;
|
|
120
|
+
authorAgentId: string;
|
|
121
|
+
content: Buffer;
|
|
122
|
+
}): Promise<{
|
|
123
|
+
uid: string;
|
|
124
|
+
adopted: boolean;
|
|
125
|
+
}>;
|
|
62
126
|
/**
|
|
63
127
|
* Record an old-slug → doc-uid alias (append-only).
|
|
64
128
|
*
|
|
@@ -118,6 +182,29 @@ export interface AppendConduitAttachmentVersionInput {
|
|
|
118
182
|
* @returns The inserted row.
|
|
119
183
|
*/
|
|
120
184
|
export declare function appendConduitAttachmentVersion(projectRoot: string, input: AppendConduitAttachmentVersionInput): Promise<ConduitAttachmentVersionRow>;
|
|
185
|
+
/**
|
|
186
|
+
* The set of content hashes the conduit doc-history family references.
|
|
187
|
+
*
|
|
188
|
+
* The blob sweep (`attachment-repair.ts`) only consulted the legacy
|
|
189
|
+
* attachments tables, the publications ledger and the blobs manifest, so a
|
|
190
|
+
* version/merged-text blob this family references could be garbage-collected
|
|
191
|
+
* as "unreferenced" (T13596). The sweep callers consult this set; it covers
|
|
192
|
+
* the version rows' `content_hash` (the merged-text blobs) and the adopted
|
|
193
|
+
* docs' `content_hash`.
|
|
194
|
+
*
|
|
195
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
196
|
+
* @returns Every `content_hash` named by `conduit_attachment_versions` or
|
|
197
|
+
* `conduit_attachments`.
|
|
198
|
+
*/
|
|
199
|
+
export declare function listConduitHistorySha256Set(projectRoot: string): Promise<Set<string>>;
|
|
200
|
+
/**
|
|
201
|
+
* Read one version row by its wire identity (`uid`).
|
|
202
|
+
*
|
|
203
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
204
|
+
* @param versionUid - The version's `uid`.
|
|
205
|
+
* @returns The version row, or `null` when unknown.
|
|
206
|
+
*/
|
|
207
|
+
export declare function getConduitAttachmentVersion(projectRoot: string, versionUid: string): Promise<ConduitAttachmentVersionRow | null>;
|
|
121
208
|
/**
|
|
122
209
|
* List a document's versions, newest display ordinal first.
|
|
123
210
|
*
|
|
@@ -262,4 +349,126 @@ export declare function writeSectionCrdtState(projectRoot: string, input: {
|
|
|
262
349
|
* @param baseVersionUid - The landed version's uid.
|
|
263
350
|
*/
|
|
264
351
|
export declare function rebaseSectionCrdtStates(projectRoot: string, attachmentUid: string, baseVersionUid: string): Promise<void>;
|
|
352
|
+
/** Input for {@link importMigratedConduitDoc}. */
|
|
353
|
+
export interface ImportMigratedConduitDocInput {
|
|
354
|
+
/** Doc slug (the pre-rekey primary key; still the table PK). */
|
|
355
|
+
readonly slug: string;
|
|
356
|
+
/** Real provenance author when known; 'system:migration' when none. */
|
|
357
|
+
readonly fromAgentId: string;
|
|
358
|
+
/** Captured content bytes (stored uncompressed; sizes equal). */
|
|
359
|
+
readonly content: Buffer;
|
|
360
|
+
/** SHA-256 hex of `content`. */
|
|
361
|
+
readonly contentHash: string;
|
|
362
|
+
/** Content format tag ('markdown', 'text', …). */
|
|
363
|
+
readonly format: string;
|
|
364
|
+
/** Optional title. */
|
|
365
|
+
readonly title?: string;
|
|
366
|
+
/** Token count (measured, never invented). */
|
|
367
|
+
readonly tokens: number;
|
|
368
|
+
/** ISO-8601 doc creation instant from real provenance. */
|
|
369
|
+
readonly createdAt: string;
|
|
370
|
+
/** Imported history length (also written to the version_count columns). */
|
|
371
|
+
readonly versionCount: number;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Import one legacy doc as a `conduit_attachments` row (content-captured).
|
|
375
|
+
*
|
|
376
|
+
* The row uid is the natural identity over the slug: re-runs coalesce.
|
|
377
|
+
* `conversation_id` is the honest system value 'docs:migration' — migrated
|
|
378
|
+
* docs belong to no conduit conversation.
|
|
379
|
+
*
|
|
380
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
381
|
+
* @param input - The captured doc facts.
|
|
382
|
+
* @returns The doc uid and whether this call inserted the row.
|
|
383
|
+
* @task T13360
|
|
384
|
+
*/
|
|
385
|
+
export declare function importMigratedConduitDoc(projectRoot: string, input: ImportMigratedConduitDocInput): Promise<{
|
|
386
|
+
uid: string;
|
|
387
|
+
inserted: boolean;
|
|
388
|
+
}>;
|
|
389
|
+
/** Input for {@link importMigratedConduitVersion}. */
|
|
390
|
+
export interface ImportMigratedConduitVersionInput {
|
|
391
|
+
/** Immutable doc id (`conduit_attachments.uid`). */
|
|
392
|
+
readonly attachmentUid: string;
|
|
393
|
+
/** Parent version uid in the DAG (NULL for the root import). */
|
|
394
|
+
readonly parentVersionUid: string | null;
|
|
395
|
+
/** Real provenance author (audit-log actor); 'system:migration' when none. */
|
|
396
|
+
readonly authorAgentId: string;
|
|
397
|
+
/** Content-addressed ref to the captured bytes. */
|
|
398
|
+
readonly storageKey: string;
|
|
399
|
+
/** SHA-256 hex of the version body. */
|
|
400
|
+
readonly contentHash: string;
|
|
401
|
+
/** Body size in bytes. */
|
|
402
|
+
readonly sizeBytes: number;
|
|
403
|
+
/** Measured token count. */
|
|
404
|
+
readonly tokens: number;
|
|
405
|
+
/** Display ordinal within the doc (derived from the real event order). */
|
|
406
|
+
readonly versionNumber: number;
|
|
407
|
+
/** ISO-8601 instant from real provenance (the audit event's ts). */
|
|
408
|
+
readonly createdAt: string;
|
|
409
|
+
/** Honest summary of what this row is. */
|
|
410
|
+
readonly changeSummary: string;
|
|
411
|
+
/** Pre-rekey display slug (legacy carry column). */
|
|
412
|
+
readonly slug: string;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Import one history row for a migrated doc.
|
|
416
|
+
*
|
|
417
|
+
* Every imported row carries `origin: 'migrated'` (spec §5 — no imported row
|
|
418
|
+
* poses as native) and a natural uid over (doc uid, content hash, event
|
|
419
|
+
* timestamp) — a pure function of the version's real facts (T13638). The
|
|
420
|
+
* derivation never touches the device-local `docs-audit.jsonl` row position,
|
|
421
|
+
* so two devices migrating the same synced legacy doc derive the SAME uid
|
|
422
|
+
* for the same content whatever their local audit logs hold, and two
|
|
423
|
+
* different contents (different hashes) can never collide on one uid. The
|
|
424
|
+
* insert is conflict-free on re-run and history can never be duplicated
|
|
425
|
+
* (AC4).
|
|
426
|
+
*
|
|
427
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
428
|
+
* @param input - The version facts.
|
|
429
|
+
* @returns The version uid and whether this call inserted the row.
|
|
430
|
+
* @task T13360
|
|
431
|
+
* @task T13638 — content-derived natural uid
|
|
432
|
+
*/
|
|
433
|
+
export declare function importMigratedConduitVersion(projectRoot: string, input: ImportMigratedConduitVersionInput): Promise<{
|
|
434
|
+
uid: string;
|
|
435
|
+
inserted: boolean;
|
|
436
|
+
}>;
|
|
437
|
+
/** Input for {@link recordConduitDocMigration}. */
|
|
438
|
+
export interface RecordConduitDocMigrationInput {
|
|
439
|
+
/** The migrated doc's slug (one receipt per slug). */
|
|
440
|
+
readonly slug: string;
|
|
441
|
+
/** Captured doc uid (NULL when the doc is dangling). */
|
|
442
|
+
readonly docUid: string | null;
|
|
443
|
+
/** 'captured' or 'dangling'. */
|
|
444
|
+
readonly status: 'captured' | 'dangling';
|
|
445
|
+
/** Why (e.g. 'path-missing', 'path-ephemeral:worktree'). */
|
|
446
|
+
readonly reason: string;
|
|
447
|
+
/** Legacy pointer path when one existed. */
|
|
448
|
+
readonly legacyPath?: string;
|
|
449
|
+
/** Legacy content sha256 when known. */
|
|
450
|
+
readonly legacySha256?: string;
|
|
451
|
+
/** How many history rows the import created. */
|
|
452
|
+
readonly versionsImported: number;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Record the migration receipt for one doc (append-only, idempotent).
|
|
456
|
+
*
|
|
457
|
+
* This IS the auditable record for the dangling verdict (AC3): a dangling
|
|
458
|
+
* doc leaves a row with status 'dangling', the legacy pointer and the
|
|
459
|
+
* reason — never a silent import. The natural uid over the slug makes a
|
|
460
|
+
* re-run coalesce onto the same row.
|
|
461
|
+
*
|
|
462
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
463
|
+
* @param input - The receipt facts.
|
|
464
|
+
* @task T13360
|
|
465
|
+
*/
|
|
466
|
+
export declare function recordConduitDocMigration(projectRoot: string, input: RecordConduitDocMigrationInput): Promise<{
|
|
467
|
+
uid: string;
|
|
468
|
+
inserted: boolean;
|
|
469
|
+
}>;
|
|
470
|
+
/** The migration receipt for one doc, or null when not migrated. */
|
|
471
|
+
export declare function findConduitDocMigration(projectRoot: string, slug: string): Promise<ConduitDocMigrationLedgerRow | null>;
|
|
472
|
+
/** All migration receipts, slug ascending (the audit view). */
|
|
473
|
+
export declare function listConduitDocMigrations(projectRoot: string): Promise<ConduitDocMigrationLedgerRow[]>;
|
|
265
474
|
//# sourceMappingURL=conduit-doc-history.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conduit-doc-history.d.ts","sourceRoot":"","sources":["../../src/store/conduit-doc-history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAOH,OAAO,EACL,0BAA0B,EAE1B,yBAAyB,EACzB,eAAe,EAEf,iBAAiB,EAClB,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"conduit-doc-history.d.ts","sourceRoot":"","sources":["../../src/store/conduit-doc-history.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,13 +29,62 @@
|
|
|
29
29
|
* @epic T13340
|
|
30
30
|
* @saga T13339
|
|
31
31
|
*/
|
|
32
|
-
import { randomUUID } from 'node:crypto';
|
|
32
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
33
33
|
import { ExitCode } from '@cleocode/contracts/exit-codes.js';
|
|
34
|
-
import { and, desc, eq, sql } from 'drizzle-orm';
|
|
34
|
+
import { and, asc, desc, eq, sql } from 'drizzle-orm';
|
|
35
35
|
import { CleoError } from '../errors.js';
|
|
36
36
|
import { naturalRowUid } from './row-identity.js';
|
|
37
37
|
import { conduitAttachmentApprovals, conduitAttachments, conduitAttachmentVersions, conduitDocHeads, conduitDocSlugAliases, sectionCrdtStates, } from './schema/cleo-project/conduit.js';
|
|
38
|
-
import {
|
|
38
|
+
import { conduitDocMigrationLedger } from './schema/cleo-project/docs.js';
|
|
39
|
+
import { getDb, getNativeDb } from './sqlite.js';
|
|
40
|
+
/**
|
|
41
|
+
* Run one docs-projection write batch atomically (T13583).
|
|
42
|
+
*
|
|
43
|
+
* The section write path composes several accessors — section-state writes,
|
|
44
|
+
* the version append, the head upsert, the rebase and the derived
|
|
45
|
+
* contributors rebuild — and a failure halfway must not leave a non-head
|
|
46
|
+
* version behind for the next update to fork onto. The drizzle handle from
|
|
47
|
+
* {@link getDb} and the raw handle from {@link getNativeDb} are the same
|
|
48
|
+
* underlying `DatabaseSync`, so a `BEGIN IMMEDIATE` on the raw handle
|
|
49
|
+
* brackets every statement the accessors issue (the same pattern
|
|
50
|
+
* `docs-update.ts` uses for its slug transfer).
|
|
51
|
+
*
|
|
52
|
+
* Constraint (T13583): every statement inside `fn` must resolve without a
|
|
53
|
+
* real I/O yield — the docs projection satisfies this (sync-driver
|
|
54
|
+
* accessors, sync `node:fs` blob reads/writes), so no other async writer can
|
|
55
|
+
* interleave on this connection mid-transaction. A caller that awaits real
|
|
56
|
+
* I/O inside `fn` breaks that guarantee; per-handle writer serialization is
|
|
57
|
+
* the follow-up if such a caller appears.
|
|
58
|
+
*
|
|
59
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
60
|
+
* @param fn - The write batch; awaited between statements on this one
|
|
61
|
+
* connection, so no other writer can interleave inside the transaction.
|
|
62
|
+
* @returns What `fn` returned.
|
|
63
|
+
*/
|
|
64
|
+
// @sync-invariant docs.head.max-hlc the version append, the head upsert and the rebase commit or roll back as one causal step; a half-landed projection cannot fork the next update
|
|
65
|
+
export async function withConduitDocWriteTransaction(projectRoot, fn) {
|
|
66
|
+
await getDb(projectRoot);
|
|
67
|
+
const native = getNativeDb(projectRoot);
|
|
68
|
+
if (!native) {
|
|
69
|
+
// @sync-invariant none:input-shape the projection requires an open project store; getDb above just opened it, so this is a broken environment, not row data — nothing was written
|
|
70
|
+
throw new CleoError(ExitCode.GENERAL_ERROR, 'conduit doc write transaction requires an initialised project store');
|
|
71
|
+
}
|
|
72
|
+
native.prepare('BEGIN IMMEDIATE').run();
|
|
73
|
+
try {
|
|
74
|
+
const out = await fn();
|
|
75
|
+
native.prepare('COMMIT').run();
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
try {
|
|
80
|
+
native.prepare('ROLLBACK').run();
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
/* Auto-rolled back by SQLite on most errors. */
|
|
84
|
+
}
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
39
88
|
/**
|
|
40
89
|
* Refuse an empty identifier at the write boundary.
|
|
41
90
|
*
|
|
@@ -77,6 +126,68 @@ export async function resolveConduitDocUid(projectRoot, slug) {
|
|
|
77
126
|
.get();
|
|
78
127
|
return alias?.docUid ?? null;
|
|
79
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Adopt a document into the conduit family on first sight (T13355).
|
|
131
|
+
*
|
|
132
|
+
* The docs write path (`docs/section-write-path.ts`) projects every doc
|
|
133
|
+
* update through the section CRDT layer, whose history tables are keyed on
|
|
134
|
+
* `conduit_attachments.uid`; a doc that only exists in the legacy
|
|
135
|
+
* `attachments` registry has no conduit row yet, so this inserts one. The
|
|
136
|
+
* row is a thin identity shell: the bytes live in the blob store, so
|
|
137
|
+
* `content` carries an empty buffer and the sizes/hash describe nothing —
|
|
138
|
+
* the version rows carry the real content addressing.
|
|
139
|
+
*
|
|
140
|
+
* `conversation_id` is the NOT NULL transport owner; docs adopted here use
|
|
141
|
+
* the `cleodocs` sentinel (no FK — soft reference by design). Idempotent:
|
|
142
|
+
* an existing live row (or alias-resolved uid) is returned with
|
|
143
|
+
* `adopted: false`.
|
|
144
|
+
*
|
|
145
|
+
* The adopted row's uid is the schema's random UUIDv7 default — a
|
|
146
|
+
* single-device mint. Two devices adopting the same legacy slug concurrently
|
|
147
|
+
* mint two uids for one doc; deduping that is the filed follow-up (T13586).
|
|
148
|
+
*
|
|
149
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
150
|
+
* @param input - Slug, author and current full text of the doc.
|
|
151
|
+
* @returns The doc's immutable uid and whether this call created the row.
|
|
152
|
+
*/
|
|
153
|
+
// @sync-invariant docs.doc.adopt-once the existence probe and the identity-shell insert are one causal step; the uid default makes a re-adopt conflict-free
|
|
154
|
+
export async function ensureConduitDoc(projectRoot, input) {
|
|
155
|
+
requireNonEmpty('slug', input.slug);
|
|
156
|
+
requireNonEmpty('authorAgentId', input.authorAgentId);
|
|
157
|
+
const existing = await resolveConduitDocUid(projectRoot, input.slug);
|
|
158
|
+
if (existing !== null)
|
|
159
|
+
return { uid: existing, adopted: false };
|
|
160
|
+
const db = await getDb(projectRoot);
|
|
161
|
+
await db
|
|
162
|
+
.insert(conduitAttachments)
|
|
163
|
+
.values({
|
|
164
|
+
slug: input.slug,
|
|
165
|
+
conversationId: 'cleodocs',
|
|
166
|
+
fromAgentId: input.authorAgentId,
|
|
167
|
+
content: Buffer.alloc(0),
|
|
168
|
+
originalSize: input.content.length,
|
|
169
|
+
compressedSize: input.content.length,
|
|
170
|
+
contentHash: createHash('sha256').update(input.content).digest('hex'),
|
|
171
|
+
format: 'text',
|
|
172
|
+
title: input.slug,
|
|
173
|
+
tokens: input.content
|
|
174
|
+
.toString('utf-8')
|
|
175
|
+
.split(/\s+/)
|
|
176
|
+
.filter((w) => w.length > 0).length,
|
|
177
|
+
})
|
|
178
|
+
.onConflictDoNothing()
|
|
179
|
+
.run();
|
|
180
|
+
const row = await db
|
|
181
|
+
.select({ uid: conduitAttachments.uid })
|
|
182
|
+
.from(conduitAttachments)
|
|
183
|
+
.where(eq(conduitAttachments.slug, input.slug))
|
|
184
|
+
.get();
|
|
185
|
+
if (!row?.uid) {
|
|
186
|
+
// @sync-invariant docs.doc.adopt-once an adopt that cannot be read back means the store lost the write; fail loudly instead of returning a phantom
|
|
187
|
+
throw new CleoError(ExitCode.GENERAL_ERROR, `conduit doc adopt for slug '${input.slug}' could not be read back`);
|
|
188
|
+
}
|
|
189
|
+
return { uid: row.uid, adopted: true };
|
|
190
|
+
}
|
|
80
191
|
/**
|
|
81
192
|
* Record an old-slug → doc-uid alias (append-only).
|
|
82
193
|
*
|
|
@@ -165,6 +276,54 @@ export async function appendConduitAttachmentVersion(projectRoot, input) {
|
|
|
165
276
|
}
|
|
166
277
|
return row;
|
|
167
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* The set of content hashes the conduit doc-history family references.
|
|
281
|
+
*
|
|
282
|
+
* The blob sweep (`attachment-repair.ts`) only consulted the legacy
|
|
283
|
+
* attachments tables, the publications ledger and the blobs manifest, so a
|
|
284
|
+
* version/merged-text blob this family references could be garbage-collected
|
|
285
|
+
* as "unreferenced" (T13596). The sweep callers consult this set; it covers
|
|
286
|
+
* the version rows' `content_hash` (the merged-text blobs) and the adopted
|
|
287
|
+
* docs' `content_hash`.
|
|
288
|
+
*
|
|
289
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
290
|
+
* @returns Every `content_hash` named by `conduit_attachment_versions` or
|
|
291
|
+
* `conduit_attachments`.
|
|
292
|
+
*/
|
|
293
|
+
export async function listConduitHistorySha256Set(projectRoot) {
|
|
294
|
+
const db = await getDb(projectRoot);
|
|
295
|
+
const versionRows = await db
|
|
296
|
+
.select({ contentHash: conduitAttachmentVersions.contentHash })
|
|
297
|
+
.from(conduitAttachmentVersions)
|
|
298
|
+
.all();
|
|
299
|
+
const docRows = await db
|
|
300
|
+
.select({ contentHash: conduitAttachments.contentHash })
|
|
301
|
+
.from(conduitAttachments)
|
|
302
|
+
.all();
|
|
303
|
+
const set = new Set();
|
|
304
|
+
for (const row of versionRows)
|
|
305
|
+
set.add(row.contentHash);
|
|
306
|
+
for (const row of docRows)
|
|
307
|
+
set.add(row.contentHash);
|
|
308
|
+
return set;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Read one version row by its wire identity (`uid`).
|
|
312
|
+
*
|
|
313
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
314
|
+
* @param versionUid - The version's `uid`.
|
|
315
|
+
* @returns The version row, or `null` when unknown.
|
|
316
|
+
*/
|
|
317
|
+
export async function getConduitAttachmentVersion(projectRoot, versionUid) {
|
|
318
|
+
requireNonEmpty('versionUid', versionUid);
|
|
319
|
+
const db = await getDb(projectRoot);
|
|
320
|
+
const row = await db
|
|
321
|
+
.select()
|
|
322
|
+
.from(conduitAttachmentVersions)
|
|
323
|
+
.where(eq(conduitAttachmentVersions.uid, versionUid))
|
|
324
|
+
.get();
|
|
325
|
+
return row ?? null;
|
|
326
|
+
}
|
|
168
327
|
/**
|
|
169
328
|
* List a document's versions, newest display ordinal first.
|
|
170
329
|
*
|
|
@@ -350,6 +509,7 @@ export async function upsertConduitDocHead(projectRoot, input) {
|
|
|
350
509
|
versionUid: input.headVersionUid,
|
|
351
510
|
hlc: input.headHlc,
|
|
352
511
|
recordedAt: now,
|
|
512
|
+
winnerVersionUid: existing.headVersionUid,
|
|
353
513
|
};
|
|
354
514
|
await db
|
|
355
515
|
.update(conduitDocHeads)
|
|
@@ -465,4 +625,156 @@ export async function rebaseSectionCrdtStates(projectRoot, attachmentUid, baseVe
|
|
|
465
625
|
.where(eq(sectionCrdtStates.attachmentUid, attachmentUid))
|
|
466
626
|
.run();
|
|
467
627
|
}
|
|
628
|
+
/**
|
|
629
|
+
* Import one legacy doc as a `conduit_attachments` row (content-captured).
|
|
630
|
+
*
|
|
631
|
+
* The row uid is the natural identity over the slug: re-runs coalesce.
|
|
632
|
+
* `conversation_id` is the honest system value 'docs:migration' — migrated
|
|
633
|
+
* docs belong to no conduit conversation.
|
|
634
|
+
*
|
|
635
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
636
|
+
* @param input - The captured doc facts.
|
|
637
|
+
* @returns The doc uid and whether this call inserted the row.
|
|
638
|
+
* @task T13360
|
|
639
|
+
*/
|
|
640
|
+
// @sync-invariant docs.docs-migration.idempotent-import the natural uid over the slug makes a re-run a verified no-op via ON CONFLICT DO NOTHING; nothing is updated or deleted
|
|
641
|
+
export async function importMigratedConduitDoc(projectRoot, input) {
|
|
642
|
+
requireNonEmpty('slug', input.slug);
|
|
643
|
+
requireNonEmpty('fromAgentId', input.fromAgentId);
|
|
644
|
+
requireNonEmpty('contentHash', input.contentHash);
|
|
645
|
+
const uid = naturalRowUid('project', 'conduit_attachments', [input.slug]);
|
|
646
|
+
const db = await getDb(projectRoot);
|
|
647
|
+
const result = await db
|
|
648
|
+
.insert(conduitAttachments)
|
|
649
|
+
.values({
|
|
650
|
+
slug: input.slug,
|
|
651
|
+
conversationId: 'docs:migration',
|
|
652
|
+
fromAgentId: input.fromAgentId,
|
|
653
|
+
content: input.content,
|
|
654
|
+
originalSize: input.content.length,
|
|
655
|
+
compressedSize: input.content.length,
|
|
656
|
+
contentHash: input.contentHash,
|
|
657
|
+
format: input.format,
|
|
658
|
+
...(input.title !== undefined ? { title: input.title } : {}),
|
|
659
|
+
tokens: input.tokens,
|
|
660
|
+
versionCount: input.versionCount,
|
|
661
|
+
currentVersion: input.versionCount,
|
|
662
|
+
createdAt: input.createdAt,
|
|
663
|
+
uid,
|
|
664
|
+
})
|
|
665
|
+
.onConflictDoNothing()
|
|
666
|
+
.run();
|
|
667
|
+
return { uid, inserted: (result.changes ?? 0) > 0 };
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Import one history row for a migrated doc.
|
|
671
|
+
*
|
|
672
|
+
* Every imported row carries `origin: 'migrated'` (spec §5 — no imported row
|
|
673
|
+
* poses as native) and a natural uid over (doc uid, content hash, event
|
|
674
|
+
* timestamp) — a pure function of the version's real facts (T13638). The
|
|
675
|
+
* derivation never touches the device-local `docs-audit.jsonl` row position,
|
|
676
|
+
* so two devices migrating the same synced legacy doc derive the SAME uid
|
|
677
|
+
* for the same content whatever their local audit logs hold, and two
|
|
678
|
+
* different contents (different hashes) can never collide on one uid. The
|
|
679
|
+
* insert is conflict-free on re-run and history can never be duplicated
|
|
680
|
+
* (AC4).
|
|
681
|
+
*
|
|
682
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
683
|
+
* @param input - The version facts.
|
|
684
|
+
* @returns The version uid and whether this call inserted the row.
|
|
685
|
+
* @task T13360
|
|
686
|
+
* @task T13638 — content-derived natural uid
|
|
687
|
+
*/
|
|
688
|
+
// @sync-invariant docs.versions.append-only the natural uid over (doc uid, content hash, event timestamp) makes a re-run a verified no-op via ON CONFLICT DO NOTHING; imported history is never updated or deleted
|
|
689
|
+
export async function importMigratedConduitVersion(projectRoot, input) {
|
|
690
|
+
requireNonEmpty('attachmentUid', input.attachmentUid);
|
|
691
|
+
requireNonEmpty('contentHash', input.contentHash);
|
|
692
|
+
requireNonEmpty('createdAt', input.createdAt);
|
|
693
|
+
requireNonEmpty('authorAgentId', input.authorAgentId);
|
|
694
|
+
const uid = naturalRowUid('project', 'conduit_attachment_versions', [
|
|
695
|
+
input.attachmentUid,
|
|
696
|
+
input.contentHash,
|
|
697
|
+
input.createdAt,
|
|
698
|
+
]);
|
|
699
|
+
const db = await getDb(projectRoot);
|
|
700
|
+
const result = await db
|
|
701
|
+
.insert(conduitAttachmentVersions)
|
|
702
|
+
.values({
|
|
703
|
+
id: randomUUID(),
|
|
704
|
+
uid,
|
|
705
|
+
attachmentUid: input.attachmentUid,
|
|
706
|
+
parentVersionUid: input.parentVersionUid,
|
|
707
|
+
slug: input.slug,
|
|
708
|
+
versionNumber: input.versionNumber,
|
|
709
|
+
authorAgentId: input.authorAgentId,
|
|
710
|
+
changeType: 'migrated',
|
|
711
|
+
storageKey: input.storageKey,
|
|
712
|
+
contentHash: input.contentHash,
|
|
713
|
+
originalSize: input.sizeBytes,
|
|
714
|
+
compressedSize: input.sizeBytes,
|
|
715
|
+
tokens: input.tokens,
|
|
716
|
+
changeSummary: input.changeSummary,
|
|
717
|
+
sectionsModified: '[]',
|
|
718
|
+
tokensAdded: 0,
|
|
719
|
+
tokensRemoved: 0,
|
|
720
|
+
createdAt: input.createdAt,
|
|
721
|
+
origin: 'migrated',
|
|
722
|
+
})
|
|
723
|
+
.onConflictDoNothing()
|
|
724
|
+
.run();
|
|
725
|
+
return { uid, inserted: (result.changes ?? 0) > 0 };
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* Record the migration receipt for one doc (append-only, idempotent).
|
|
729
|
+
*
|
|
730
|
+
* This IS the auditable record for the dangling verdict (AC3): a dangling
|
|
731
|
+
* doc leaves a row with status 'dangling', the legacy pointer and the
|
|
732
|
+
* reason — never a silent import. The natural uid over the slug makes a
|
|
733
|
+
* re-run coalesce onto the same row.
|
|
734
|
+
*
|
|
735
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
736
|
+
* @param input - The receipt facts.
|
|
737
|
+
* @task T13360
|
|
738
|
+
*/
|
|
739
|
+
// @sync-invariant docs.docs-migration.idempotent-import the natural uid over the slug makes a re-run a verified no-op via ON CONFLICT DO NOTHING; receipts are never updated or deleted
|
|
740
|
+
export async function recordConduitDocMigration(projectRoot, input) {
|
|
741
|
+
requireNonEmpty('slug', input.slug);
|
|
742
|
+
const uid = naturalRowUid('project', 'conduit_doc_migration_ledger', [input.slug]);
|
|
743
|
+
const db = await getDb(projectRoot);
|
|
744
|
+
const result = await db
|
|
745
|
+
.insert(conduitDocMigrationLedger)
|
|
746
|
+
.values({
|
|
747
|
+
slug: input.slug,
|
|
748
|
+
docUid: input.docUid,
|
|
749
|
+
status: input.status,
|
|
750
|
+
reason: input.reason,
|
|
751
|
+
...(input.legacyPath !== undefined ? { legacyPath: input.legacyPath } : {}),
|
|
752
|
+
...(input.legacySha256 !== undefined ? { legacySha256: input.legacySha256 } : {}),
|
|
753
|
+
versionsImported: input.versionsImported,
|
|
754
|
+
migratedAt: new Date().toISOString(),
|
|
755
|
+
uid,
|
|
756
|
+
})
|
|
757
|
+
.onConflictDoNothing()
|
|
758
|
+
.run();
|
|
759
|
+
return { uid, inserted: (result.changes ?? 0) > 0 };
|
|
760
|
+
}
|
|
761
|
+
/** The migration receipt for one doc, or null when not migrated. */
|
|
762
|
+
export async function findConduitDocMigration(projectRoot, slug) {
|
|
763
|
+
requireNonEmpty('slug', slug);
|
|
764
|
+
const db = await getDb(projectRoot);
|
|
765
|
+
return ((await db
|
|
766
|
+
.select()
|
|
767
|
+
.from(conduitDocMigrationLedger)
|
|
768
|
+
.where(eq(conduitDocMigrationLedger.slug, slug))
|
|
769
|
+
.get()) ?? null);
|
|
770
|
+
}
|
|
771
|
+
/** All migration receipts, slug ascending (the audit view). */
|
|
772
|
+
export async function listConduitDocMigrations(projectRoot) {
|
|
773
|
+
const db = await getDb(projectRoot);
|
|
774
|
+
return db
|
|
775
|
+
.select()
|
|
776
|
+
.from(conduitDocMigrationLedger)
|
|
777
|
+
.orderBy(asc(conduitDocMigrationLedger.slug))
|
|
778
|
+
.all();
|
|
779
|
+
}
|
|
468
780
|
//# sourceMappingURL=conduit-doc-history.js.map
|