/** * T13727 — backfill `conduit_attachment_versions` for docs whose history * predates the conduit adoption. * * The section write path (`section-write-path.ts`) has projected every docs * update through the conduit history family since the adoption release, but * updates from BEFORE the adoption wrote no conduit rows — the T13360 * migration projected only then-current content. A doc at `docVersion` 3 can * therefore hold a single conduit version row (the first post-adoption * update, minted as local display ordinal 1), with versions 1..N−1 known * only to the legacy surfaces. This module is the historical complement to * that projection: it reconstructs the missing rows from the honest * pre-adoption sources and inserts them through the canonical accessor. * * Honest-metadata contract (Council condition for migrated history): * * - The version CHAIN comes from the docs-versioning audit log * (`.cleo/audit/docs-versioning.jsonl`): each changed revision records * the real timestamp (`ts`), the real author (`attachedBy`) and the real * content address (`sha256`). Version 1 anchors at the first revision's * `previousSha256`; its creation instant is read from the matching * `docs_attachments` row (historical rows keep their `created_at` after * an update re-homes the slug), and its author is unknown — recorded as * {@link CONDUIT_BACKFILL_AUTHOR}, never invented. * - `changeType` is the schema's honest `'migrated'` (via * {@link importMigratedConduitVersion}); `origin` is `'backfill-t13727'` * so repair-synthesized rows never pose as T13360 imports. * - `contentHash` addresses the REAL historical bytes. When the bytes are * unrecoverable (neither the blob store nor the legacy attachment tree * holds them), the position is recorded as a GAP and skipped — content * is never fabricated. The parent chain links across a gap to the * nearest present ancestor and the report counts the gap. * - Existing conduit rows are NEVER updated or renumbered (append-only). * Matching is by CONTENT HASH, never by ordinal or tail position * (review-2253): a chain position whose hash already has a row is done; * an existing row whose hash is not in the chain refuses the doc * (reported for manual review); complete means every recoverable * position is present, and unrecoverable positions stay reported as * gaps rather than being silently dropped. * * Idempotency: a doc whose chain positions all have rows (by content hash) * is skipped; per-row natural uids (doc uid, content hash, createdAt) make a * partial re-run a verified no-op via `ON CONFLICT DO NOTHING`. * * The parent link from the last backfilled row to the first EXISTING row is * implicit (content alignment + display ordinals); existing rows' * `parent_version_uid` is history and stays untouched. * * @module * @task T13727 * @epic T13340 * @saga T13339 */ /** * Honest author label for backfilled rows whose real author is unrecoverable * (the original creation predates the docs-versioning audit trail's author * tracking on revisions, and `docs_attachments` records no author). * * @task T13727 */ export declare const CONDUIT_BACKFILL_AUTHOR = "cleo:backfill-t13727"; /** * Import provenance written to `conduit_attachment_versions.origin` — keeps * repair-synthesized rows distinguishable from T13360 `'migrated'` imports * and from native (`NULL`) rows. * * @task T13727 */ export declare const CONDUIT_BACKFILL_ORIGIN = "backfill-t13727"; /** One content state in a doc's reconstructed pre-adoption version chain. */ export interface ConduitBackfillChainState { /** 1-based position in the doc's version chain (aligns with docVersion). */ readonly position: number; /** Real content address of this version's bytes. */ readonly sha256: string; /** * Real creation instant. For chain position 1 this is the * `docs_attachments` row's `created_at` when the row survives; when it * does not, the first revision's `ts` is used and the change summary * discloses that the original creation instant is unrecoverable. */ readonly createdAt: string; /** Real revision author, or {@link CONDUIT_BACKFILL_AUTHOR} when unknown. */ readonly authorAgentId: string; /** Honest change summary (never claims to be a native edit). */ readonly changeSummary: string; } /** One version row the apply pass would insert. */ export interface ConduitBackfillVersion extends ConduitBackfillChainState { /** Measured byte length of the verified historical bytes. */ readonly sizeBytes: number; /** Measured token count of the verified historical bytes. */ readonly tokens: number; /** * The parent version's row uid for the DAG link — the uid of the nearest * lower chain position that has (or will have, via this plan) a row, or * null for the chain root. Precomputed at plan time: a present parent's * uid is read from its existing row; a backfilled parent's uid is the * natural row uid {@link importMigratedConduitVersion} derives, so the * link is stable across a re-run. */ readonly parentVersionUid: string | null; } /** One chain position whose historical bytes are unrecoverable. */ export interface ConduitBackfillGap { /** Chain position skipped. */ readonly position: number; /** The real content address that has no recoverable bytes. */ readonly sha256: string; } /** * Per-doc backfill plan (the dry-run artifact — pure with respect to the * database and the blob store; the only I/O is reading). */ export interface ConduitDocBackfillPlan { /** The doc's live slug. */ readonly slug: string; /** The doc's immutable uid (`conduit_attachments.uid`). */ readonly docUid: string; /** Content states reconstructable from the honest sources. */ readonly expectedVersions: number; /** `conduit_attachment_versions` rows already present. */ readonly existingVersions: number; /** Chain positions whose content_hash already has a row. */ readonly presentVersions: number; /** Rows the apply pass would insert (bytes verified present). */ readonly toBackfill: readonly ConduitBackfillVersion[]; /** Chain positions with unrecoverable bytes — counted, never fabricated. */ readonly gaps: readonly ConduitBackfillGap[]; /** * True when every recoverable chain position is present — nothing left to * insert. A doc with unrecoverable gaps can be complete AND still carry * {@link gaps}; the gaps stay reported (they are real, unhealed drift), * only the false "conflict" is gone. */ readonly complete: boolean; /** * True when the existing conduit rows contradict the reconstructed chain * (content-hash mismatch at a tail position). The doc is refused and * reported for manual review; nothing is written. */ readonly conflict: boolean; /** * `docs_attachments.doc_version` minus the provable chain length when * positive — versions the audit trail cannot account for (e.g. a doc whose * updates predate the versioning audit file). Informational; those rows * stay unrecoverable rather than being invented. */ readonly auditGapVersions: number; } /** Outcome of applying one {@link ConduitDocBackfillPlan}. */ export interface ConduitDocBackfillResult { /** The doc's live slug. */ readonly slug: string; /** The doc's immutable uid. */ readonly docUid: string; /** Rows actually inserted (0 on a skipped/refused re-run). */ readonly inserted: number; /** Chain positions left as unrecoverable gaps. */ readonly gaps: number; } /** * Plan the conduit-history backfill for every adopted doc. * * A doc is in scope when the docs registry (`docs_attachments`) knows its * slug AND a `conduit_attachments` row exists for it — the doc was adopted * (or migrated) but its pre-adoption updates wrote no conduit rows. Pure * with respect to the database and the blob store: safe in dry-run planning. * * Matching model (review-2253): matching is by CONTENT HASH, never by * ordinal or tail position — after a gapped apply the existing set mixes * backfilled low positions with minted rows, so a positional tail check * would false-conflict forever. The rules: * * - a chain position whose content_hash already has a row is done; * - a position with no row and no verified bytes is a gap (counted); * - an existing row whose content_hash is not in the chain is a conflict * (the doc is refused and reported, never overwritten); * - complete means every recoverable position is present — a doc with * permanent gaps can be complete AND still report them. * * Existing rows keep their minted local display ordinals (rows are * append-only); backfilled rows carry their chain position as * `version_number`, so ordinals may repeat across the seam — the schema * defines `version_number` as a local display ordinal that is never a key, * and `docs versions` assigns read-time display ordinals instead. * * @param projectRoot - Absolute path to the CLEO project root. * @returns One plan per in-scope doc (complete, backfill, gap or conflict). * @task T13727 */ export declare function planConduitHistoryBackfill(projectRoot: string): Promise; /** * Execute one {@link ConduitDocBackfillPlan}: insert the synthesized history * rows through the canonical accessor, one transaction per doc. * * Every statement inside the transaction resolves without a real I/O yield * (bytes were read and measured during planning; {@link withConduitDocWriteTransaction} * brackets the inserts), so a partial backfill cannot leave a forked chain. * Per-row natural uids make a re-run a verified no-op; a doc whose count * already equals the chain length was filtered by the planner * (`complete: true`) and inserts nothing here. * * @param projectRoot - Absolute path to the CLEO project root. * @param plan - One doc's plan from {@link planConduitHistoryBackfill}. * @returns Inserted row count and unrecoverable gap count for the doc. * @task T13727 */ export declare function applyConduitHistoryBackfill(projectRoot: string, plan: ConduitDocBackfillPlan): Promise; //# sourceMappingURL=conduit-history-backfill.d.ts.map