/** * `cleo docs update ` — in-place blob replacement that preserves the slug. * * The slug is the stable, human-addressable handle. On update we keep the * slug pinned but rotate the bytes underneath: insert a NEW row for the new * sha256 content (without a slug), then atomically transfer the slug from * the old row to the new one inside a `BEGIN IMMEDIATE` transaction. * * The previous row stays reachable by attachment-id (and by raw sha256) for * version history — its slug column is cleared so the UNIQUE index does * not trip. No supersession edge is written (callers wanting an explicit * lineage edge should use `cleo docs supersede`). * * Each successful update appends one line to * `.cleo/audit/docs-versioning.jsonl`. When a second update for the same * slug arrives within a 5-minute window the audit line is squashed onto * the prior entry's `revisions[]` rather than written as a new line. * * @task T10161 (Epic T10157 / Saga T9855 — E12.C4) * @see packages/core/src/store/attachment-store.ts — `put()` write path * @see packages/core/src/store/schema/attachments.ts — `lifecycle_status` column */ import { type DocsLifecycleStatus, type DocsUpdateParams } from '@cleocode/contracts'; /** * 5-minute squash window for audit-log entries. A second update for the * same slug landing within this many milliseconds of the prior audit * entry's `firstAt` does NOT write a new line — it appends a revision * onto the existing line in place. * * @task T10161 */ export declare const DOCS_UPDATE_SQUASH_WINDOW_MS: number; /** * Project-root-relative path to the docs-versioning audit log. * * @task T10161 */ export declare const DOCS_VERSIONING_AUDIT_FILE = ".cleo/audit/docs-versioning.jsonl"; /** Pipe-delimited lifecycle status list for dispatch/CLI validation messages. */ export declare const DOCS_UPDATE_LIFECYCLE_STATUS_LIST: string; /** * Discriminated error for `updateDocBySlug` failures. * * Mirrors the LAFS error shape so callers (dispatch + tests) can map * directly onto an envelope code without bespoke translation. * * @task T10161 */ export type DocsUpdateError = { code: 'E_NOT_FOUND'; message: string; } | { code: 'E_INVALID_INPUT'; message: string; } | { code: 'E_DOC_SCHEMA_MISMATCH'; message: string; details?: Record; } | { code: 'E_INVALID_STATUS'; message: string; } | { code: 'E_FILE_ERROR'; message: string; }; /** * Successful update result returned by {@link updateDocBySlug}. * * Shape matches `DocsUpdateResult` from `@cleocode/contracts` — the * dispatch handler forwards it verbatim. `changed === false` indicates a * byte-identical noop (no new row inserted, no audit entry written). * * @task T10161 */ export interface DocsUpdateOk { slug: string; type: string | null; attachmentId: string; previousAttachmentId: string; sha256: string; previousSha256: string; changed: boolean; lifecycleStatus: DocsLifecycleStatus; updatedAt: string; /** @deprecated Legacy version field (audit-log-based). Use ownerVersion + docVersion instead (T11181). */ version: number; /** CLEO release version that wrote this row (canonical SSoT, T11181). */ ownerVersion: string; /** Sequential doc version counter for this slug (T11181). */ docVersion: number; squashed: boolean; /** * Human-readable summary of what happened to the slug. * Examples: "slug 'foo' was changed", "slug 'foo' was left untouched (content unchanged)". * @task T11055 */ summary: string; dryRun?: true; wouldWrite?: boolean; wouldChange?: boolean; } /** * Output of {@link updateDocBySlug} — either a success record or a * discriminated error envelope. * * @task T10161 */ export type UpdateDocBySlugResult = { ok: true; result: DocsUpdateOk; } | { ok: false; error: DocsUpdateError; }; /** * Detect MIME type from a file path. Mirrors the helper inside * `attachment-store.ts` — kept private to this module to avoid widening * the store's public surface for a single caller. * * @internal */ declare function mimeFromPath(filePath: string): string; /** * Validate a {@link DocsLifecycleStatus} value at runtime. * * Used by the dispatch handler before calling {@link updateDocBySlug} so * the core function can stay type-narrowed without a redundant check. * * @task T10161 */ export declare function isLifecycleStatus(raw: unknown): raw is DocsLifecycleStatus; /** * Update the attachment identified by `slug` to carry new bytes. * * Flow: * 1. Look up the existing row by slug → `E_NOT_FOUND` if missing. * 2. Hash the new bytes. If the hash matches the existing row's * sha256, the operation is a NOOP (return early with `changed: false`). * 3. Inside a `BEGIN IMMEDIATE` transaction: * a. Clear the slug column on the old row. * b. Insert (or upsert) a row for the new sha256 with the slug, * the prior row's type, and the requested lifecycle status. * c. Add an `attachment_refs` binding for each owner the prior row * had, so the slug stays addressable from the same owners. * d. Commit. Write the new blob to disk AFTER the transaction. * 4. Append (or squash) an audit-log entry. * * Signature follows ADR-057 L1: `(projectRoot, params): Promise`. * The dispatch handler is the sole call site; tests drive this via the * dispatch surface (or via the CLI E2E suite which spawns the compiled * binary against an isolated project root). * * @param projectRoot - Absolute path to the CLEO project root * @param params - {@link DocsUpdateParams} from the dispatch envelope * @returns Success record or discriminated error * * @task T10161 */ export declare function updateDocBySlug(projectRoot: string, params: DocsUpdateParams): Promise; export { mimeFromPath as docsUpdateMimeFromPath }; //# sourceMappingURL=docs-update.d.ts.map