import { V as Node } from "./nodes.js"; import { c as DisplayDiff, d as FieldChange, f as diffDisplayLists, g as collapseReplacer, h as serializeDisplayList, l as DlSnapshot, m as parseDisplaySnapshot, o as CommandDelta, p as formatDisplayDiff, r as ViolationDetail, s as DL_SNAPSHOT_VERSION, u as DlSnapshotError } from "./guards.js"; import { i as Scene } from "./scene.js"; import { a as DiffOptions, c as diff, i as DiffInput, n as ChangeOp, o as NodeRef, r as ChangeSet, s as Region, t as Change } from "./diff.js"; import { CoverageReport, FontMode, FontUsage, Timeline, ValueTypeId } from "@glissade/core"; //#region src/cacheColdAudit.d.ts interface CacheColdResult { ok: boolean; /** id of the first node whose isolated emit() diverged (set only when !ok). */ node?: string; /** * The FIRST command-level delta of the divergent node's isolated emit (set only * when a specific leaf diverged — never for a Group fallback or a missing node). * The WHOLE `CommandDelta` is embedded — index, kind, opA/opB, and every * field change — so a multi-field divergence isn't flattened away. `gs * verify-determinism --bisect` consumes this to name the (frame, node, op). */ delta?: CommandDelta; /** * `true` when the two `createScene()` builds returned SHARED node instances * (identity-equal) rather than independent ones — so the twice-eval is * INCONCLUSIVE: a shared impure signal is evaluated once and memoized, making * the two DisplayLists identical by construction even for an impure node. The * audit can neither confirm purity nor localize a divergence. Set alongside * `ok:true` (nothing diverged, but the probe was defeated). The caller must * surface this LOUDLY, never read it as "pure". Root cause is almost always a * scene-frame helper that captures its `children` once and reuses them across * calls; the fix is to rebuild children per `createScene()`. */ sharedInstances?: boolean; } /** * Evaluate two fresh scenes from `createScene` at `t` and confirm the * DisplayLists are byte-identical. Returns `{ ok: true }` for a pure scene, or * `{ ok: false, node }` naming the first divergent node. DEV-only — never on * the render hot path. */ declare function auditCacheCold(createScene: () => Scene, doc: Timeline, t: number): CacheColdResult; /** * Adapt {@link auditCacheCold} into a {@link ViolationLocator} payload for * `withDeterminismGuards('throw', fn, locate)`: name the first node whose cold * re-eval disagrees (plus its first command-level delta). Returns a `reason` * (no node) when the twice-eval was DEFEATED by shared node instances — so the * throw says out loud WHY it couldn't localize instead of silently degrading to * a bare violation (the gap that let long frame-helper episodes get no culprit). * Returns `undefined` only when the probe ran with independent builds and still * agreed (a rare timing-only impurity the cold probe can't reproduce — the bare * throw then stands). DEV-only — re-evaluates two FRESH scenes, throw branch only. */ declare function locateViolation(createScene: () => Scene, doc: Timeline, t: number): ViolationDetail | undefined; //#endregion //#region src/fontUsage.d.ts /** Walk `scene` for Text nodes; one usage per node carrying its full text. */ declare function collectTextUsages(scene: Scene): FontUsage[]; /** * Collect font usages from the POST-localize document's STRING tracks (FIX 3, * 0.14 canary). For every `'string'` track whose target node is a Text node, * emit one usage per distinct localized KEY VALUE under that node's fontFamily — * so a localized CJK message bound to a Latin-only font surfaces as an uncovered * glyph. `collectTextUsages` only sees the authored BASE `node.text()`, which is * resolved BEFORE the localized string tracks bind, so it misses this. */ declare function collectLocalizedTextUsages(scene: Scene, doc: Timeline): FontUsage[]; /** * Caller-supplied I/O: fetch the raw bytes for a font face URL (the export * paths read a file / fetch a URL; this keeps core pure). Returning undefined * means "could not load" — that family contributes no coverage, surfacing as * missing glyphs (strict) / a dev warning, never a hang. */ type FontByteLoader = (url: string) => Promise; interface ValidateSceneFontsOptions { mode?: FontMode; /** OS-installed families to treat as registered (case-insensitive). */ osFamilies?: ReadonlySet | undefined; /** * Additional usages to validate alongside the scene's authored Text (FIX 3): * the POST-localize document's localized string-track values, which the * scene-walk can't see (they bind AFTER `node.text()` is read). Build them * with `collectLocalizedTextUsages(scene, localizedDoc)`. */ extraUsages?: readonly FontUsage[] | undefined; } /** * Run §3.6 font validation for a scene + its timeline document. Builds the * registry from `doc.assets`, loads each registered face's cmap via `loadBytes` * (once per family — the first face's URL is enough for coverage), and runs the * pure `validateFonts`. Strict mode throws FontValidationError; dev warns. */ declare function validateSceneFonts(scene: Scene, doc: Timeline, loadBytes: FontByteLoader, options?: ValidateSceneFontsOptions): Promise; //#endregion //#region src/validate.d.ts /** * Bumped ONLY on a breaking change to the diagnostic shape. New CODES and new * OPTIONAL fields are additive and do NOT bump it — a consumer keys on `code` * and tolerates unknown ones. */ declare const DIAGNOSTIC_SCHEMA_VERSION: 1; /** Closed severity ladder. `error` = a build error (unbound target); `warning` * = a probable-mistake (position of a flow child); `info` = a valid-but-notable * observation (estimating measurer). */ type DiagnosticSeverity = 'error' | 'warning' | 'info'; /** * Stable, ADDITIVE-ONLY diagnostic codes (never renamed/removed — the wire * contract). Chosen with BOTH `validateScene` and the future * `gs parity --semantic` surface in mind. * * This enum is the shared diagnostic VOCABULARY — NOT "everything validateScene * emits." Each code maps to a distinct ENFORCEMENT POINT: * - `UNKNOWN_TARGET` — EMITTED by validateScene: a track targets an id/prop that * resolves to no signal. * - `MEASURER_FALLBACK` — EMITTED by validateScene: the scene carries Text but no * real measurer is injected, so layout uses the rough per-character estimate. * - `YOGA_CHILD_POSITION` — EMITTED by validateScene: a track drives * `position`/`position.*` of a FLOWABLE child of a Layout, whose flex slot * overrides/confounds that position. * - `OFF_CANVAS` — RESERVED for `critique()` (0.60): a node's RENDERED box lands * fully outside the viewport. It is a composed-geometry check (needs ancestor * Group world transforms from the DisplayList), so validateScene — which reads * only static LOCAL positions — does NOT emit it (a nested child would * false-positive). Kept in the enum as the additive-only wire contract so 0.60 * critique() can emit it without a schema bump. * - `ID_COLLISION` — ENFORCED at `createScene()` (throws `DuplicateNodeIdError`): * a built Scene structurally cannot contain a duplicate id, so validateScene * never reaches this case. Kept for the shared contract / `gs parity` surface. */ type DiagnosticCode = 'UNKNOWN_TARGET' | 'ID_COLLISION' | 'OFF_CANVAS' | 'YOGA_CHILD_POSITION' | 'MEASURER_FALLBACK' | 'TEXT_OVERFLOW' | 'OCCLUSION' | 'CAPTION_COLLISION' | 'OUT_OF_BOUNDS' | 'MISALIGNED' | 'UNEVEN_SPACING' | 'LAYOUT_OVERFLOW' | 'RENDER_ONLY_EXPORT' | 'LOTTIE_DROP' | 'LOTTIE_APPROXIMATE' | 'ANCHOR_RECENTER' | 'UNEXPLAINED_RESIDUAL' | 'BACKEND_DIVERGE'; /** * 0.60: which enforcement SURFACE produced a diagnostic — distinguishes a CERTAIN * static fact (`validateScene`) from a HEURISTIC rendered judgment (`critique`, * which CAN false-positive) from a perceptual parity result (`parity`). Additive, * optional (a pre-0.60 consumer ignores it). Certification reads it to keep "zero * static errors" and "zero critique false-positives" as distinct claims. */ type DiagnosticSource = 'validateScene' | 'critique' | 'parity'; /** One diagnostic. The `{schemaVersion, code, severity, message, node?, track?}` * core shape is PINNED; future fields (`source`/`detail`, 0.60) are ADDITIVE only. */ interface SceneDiagnostic { schemaVersion: typeof DIAGNOSTIC_SCHEMA_VERSION; code: DiagnosticCode; severity: DiagnosticSeverity; message: string; /** The node id the diagnostic concerns, when applicable. */ node?: string; /** The track target string the diagnostic concerns, when applicable. */ track?: string; /** 0.60: the enforcement surface that produced this diagnostic. */ source?: DiagnosticSource; /** 0.60: structured evidence (e.g. `{ measured, threshold }` for a critique * code), so a consumer digs into the numbers without parsing the message. */ detail?: Record; } /** `validateScene` result — the CLI-lint `{ hasErrors, diagnostics }` shape, * plus a top-level `schemaVersion`. */ interface ValidateSceneResult { schemaVersion: typeof DIAGNOSTIC_SCHEMA_VERSION; /** true iff any diagnostic has severity `error`. */ hasErrors: boolean; /** Every diagnostic found — AGGREGATED, never throw-on-first, stable order. */ diagnostics: SceneDiagnostic[]; } /** Classic edit distance (iterative two-row DP). Small strings only (ids). */ declare function levenshtein(a: string, b: string): number; /** * The nearest candidate to `name` within a reasonable edit budget (≤ 2, or a * third of the length for longer names), or undefined if none is close enough — * so a wildly-different typo doesn't get a misleading "did you mean" tail. */ declare function nearestId(name: string, candidates: Iterable): string | undefined; /** * Eagerly validate a scene (+ optional timeline) and AGGREGATE every problem — * the static belt that surfaces at the AUTHORING site what the render-time * `UnboundTargetError` backstop only shows one-at-a-time from deep in the render * loop. Pure read (see the module header): calling it never changes a subsequent * render's bytes. * * With a `doc`, every track target is walked through the existing * `scene.resolveTarget`; an unresolved one becomes an `UNKNOWN_TARGET` error * with a Levenshtein nearest-id / nearest-prop suggestion, and a * `position`/`position.*` track on a flowable Layout child becomes a * `YOGA_CHILD_POSITION` warning. The scene-only MEASURER_FALLBACK check runs * regardless. validateScene emits exactly three codes — UNKNOWN_TARGET, * MEASURER_FALLBACK, YOGA_CHILD_POSITION; OFF_CANVAS/ID_COLLISION are reserved * (see the DiagnosticCode doc for their enforcement points). */ declare function validateScene(scene: Scene, doc?: Timeline): ValidateSceneResult; /** * Read a node's RESOLVED prop value at time `t` — the always-truthful read, the * anti-false-conclusion primitive for inspection tooling (and load-bearing for * 0.60 `critique()`). A BOUND prop returns its REAL bound value at `t` (not the * misleading static default); an unbound prop returns its static value at any * `t`; an unresolvable target returns `undefined`. * * Thin wrapper over the existing `scene.resolveTarget` + core's `evaluateAt` * (read inside a read phase). NOTE: the scene must be BOUND (`bindScene`/ * `evaluate` already ran for the doc) for a track-driven value to appear — * `resolveAt` reads the live signal, it does not itself bind. Render-neutral: * the scene playhead is restored after the read. */ declare function resolveAt(scene: Scene, target: string, t: number): unknown; /** One prop's live binding state on a SPECIFIC node instance. */ interface InstancePropState { /** The track-target path (e.g. `position`, `opacity`). */ path: string; /** The §2.2 value type(s) the prop accepts. */ expects: ValueTypeId | readonly ValueTypeId[] | undefined; /** * TRUE when THIS instance's signal currently has a bound source (a timeline * track OR a computed `() => …` initializer) — so a static read of it is a * LIE; use `resolveAt` to read its real value over time. This is the * anti-false-conclusion guard (the cursorFill trap): type-level "bindable" * says the prop CAN be animated; this says it currently IS. */ bound: boolean; /** * 0.65 — TRUE when the target is DERIVED / read-only: it exposes a computed value * for inspection (read via `resolveAt`) but rejects a `set`/track bind (e.g. a * Camera's `resolvedCenter`, derived from `centerOn`). An author must NOT attempt * to drive it. Absent (⇒ a normal settable prop) for every other target. */ derived?: boolean; } /** * Announce which props are CURRENTLY bound on THIS node instance (not just * type-level bindable). Reads `signal.isBound` per registered target — a pure * inspection read. Pair with `resolveAt` to read a bound prop's real value. A * DERIVED, read-only target (a Camera's `resolvedCenter`) is flagged `derived:true`. */ declare function instanceProps(node: Node): InstancePropState[]; //#endregion //#region src/critique.d.ts /** * 0.64 — a RESERVED region (e.g. the bottom caption band): a FILL-zone for its * OWNER node and a FORBIDDEN-zone for everything else. A thin wrapper over the * shared {@link Region} (kept pure — never a render input, only a CRITIQUE input), * so every golden stays byte-identical. * * `owner` is the node id ALLOWED to fill the region — it AND its whole subtree are * subtree-matched as exempt (a caption plus its word/line split children never * self-collide). A Text node that OWNS a SafeArea also gets the band's height * (`bounds.maxY - bounds.minY`) as its EFFECTIVE height-box for the existing * TEXT_OVERFLOW-height check (critique-only — no render `box.h` is ever set). */ interface SafeArea { /** The reserved band in device px (integer bounds; the shared diff `Region`). */ bounds: Region; /** The node id allowed to FILL the region (subtree-matched: the owner + its * descendants are exempt from CAPTION_COLLISION). */ owner?: string; } /** * 0.77 — a keep-WITHIN box: node `node`'s rendered composed box must stay INSIDE * `within`. The INVERSE of a {@link SafeArea} (keep-OUT band) and a first-class named * type paralleling it. `node` is a node id (resolved the same way `SafeArea.owner` is); * `within` is the shared integer {@link Region} (ingested through validateRegion — * quantize-or-fail-loud — so a hand-built box and a describe().types Region reach the * OUT_OF_BOUNDS check byte-identically). A PURE critique input — never a render input, * so every golden stays byte-identical. */ interface ContainBound { /** The node id whose composed box must stay inside {@link within}. */ node: string; /** The keep-within box in device px (integer bounds; the shared diff {@link Region}). */ within: Region; } /** * Cut 2 — an author-declared alignment GROUP: a set of sibling node ids whose * rendered boxes are EXPECTED to align (share a cross-axis center) and be evenly * spaced along a main axis. The EXPLICIT-declaration form (auto-inference of groups * is deferred): the author lists the ids they intend to read as a row/column, and * critique() checks MISALIGNED / UNEVEN_SPACING against the members' INTEGER device * boxes at the group's SETTLED frame. A PURE critique input — never a render input, * so every golden stays byte-identical. */ interface AlignGroup { /** Optional label used in the diagnostic message + `detail.group` (else the * member id list is used). */ id?: string; /** The node ids whose boxes should align / be evenly spaced (>= 2). Every id must * resolve to a node in the scene (fail-loud on a typo). */ members: string[]; /** The main axis. Omitted ⇒ INFERRED from the members' geometry at the settled * frame (the axis with the larger spread of box centers; a tie prefers 'row'). */ axis?: 'row' | 'column'; } /** * 0.77 — thrown when a critique() input cannot be resolved, the fail-loud twin of * validateRegion's {@link RegionError} on a malformed box. `containBounds` fails loud * when its `node` id does NOT resolve to a node with its own rendered box — a typo'd * id (matches nothing) or a container Group (no own box) — rather than silently * guarding nothing. A declared keep-within box that silently no-ops is the * confident-wrong-by-omission the critique suite exists to prevent: an author who ADDED * a guard would be worse off than one who knew they had none. (The box half already * fails loud via validateRegion; the node half now matches.) instanceof-catchable so a * no-build author can `catch (e) { if (e instanceof glissade.CritiqueError) … }`. */ declare class CritiqueError extends Error { constructor(message: string); } interface CritiqueOptions { /** * frames-per-second for the sampling grid. Default: the timeline's own fps, * else 60 (aligning critique verdicts with what `gs render` produces). Kept an * override for tooling; a fixed INTEGER-frame grid is the determinism contract. */ fps?: number; /** * Author-declared INTENTIONALLY off-stage node ids — the OFF_CANVAS opt-out. * A node is exempt from OFF_CANVAS iff its id is in this list OR ANY of its * ancestors' ids is (SUBTREE match): list the parked GROUP id * (`'sd1-drawer'`) and its whole subtree — current children AND any it later * gains — is suppressed, while sibling groups stay fully checked. This lets an * author silence the true-positive-but-intentional off-stage art (wing-parked * drawers, hidden placeholder cards) without muting OFF_CANVAS wholesale. A * PURE emission filter — determinism-neutral (it never changes the sampled * geometry, only which off-frame nodes are reported). Same param-seam shape as * a future `safeAreas`; a per-node `offstage:true` marker is a planned * fast-follow, not this mechanism. */ offstage?: readonly string[]; /** * 0.63.1 — the legibility FLOOR (px) the geometry `fontSize` auto-fix must not * sink below. A TEXT_OVERFLOW offers the `fontSize` geometry lever ONLY when the * shrink-to-fit lands ≥ this floor (else the overflow escalates instead of * auto-shrinking to an unreadable caption). Gates BOTH the width- and * height-overflow feasibility. Default 6 (mirrors `fitText({ minPx })`) — omitting * it is byte-identical to prior behaviour. A PURE feasibility-partition param: it * never touches the sampled geometry / render path, so every golden stays * byte-identical. Raise it for a stricter legibility bar, lower it (e.g. 1) to let * the fix shrink text further. */ minLegiblePx?: number; /** * 0.64 — RESERVED regions (e.g. the caption band). A non-owner node whose * on-stage composed box intrudes one for its whole on-stage span raises * CAPTION_COLLISION; the band OWNER (and its subtree) FILL it and are exempt. A * caption OWNING a band also gets the band height as its effective height-box for * the TEXT_OVERFLOW-height check, and a resize (box.h/width grow) that would push * a non-owner into a band is infeasible. A PURE critique input — never a render * input (no node gets a `box.h`), so every golden stays byte-identical. Build one * with `captionSafeArea(size)` from @glissade/narrate. */ safeAreas?: readonly SafeArea[]; /** * 0.77 — keep-WITHIN boxes: the INVERSE of {@link safeAreas} (keep-OUT bands). Each * entry declares that node `node`'s rendered composed box must stay INSIDE `within`. * A node whose device box is NOT fully inside its declared box for its WHOLE on-stage * span raises OUT_OF_BOUNDS (the persistent-drift discipline OFF_CANVAS/CAPTION_COLLISION * use — a transient overshoot during animation does not fire). Only nodes with a * declared box participate (no cost for others). Each `within` is ingested through the * SHARED validateRegion (integer-quantize + fail-loud on a bad region), so a hand-built * box and a describe().types Region reach the check byte-identically. A PURE critique * input — never a render input, so every golden stays byte-identical. */ containBounds?: readonly ContainBound[]; /** * Cut 2 — EXPLICIT sibling-alignment groups (MISALIGNED + UNEVEN_SPACING). Each * group lists >= 2 member node ids (fail-loud on an unknown id) whose rendered * boxes are read at the group's SETTLED frame (the max grid frame where every * member is present AND at rest — its integer bbox equals the next frame's, so * entrance/exit/rotation transients are excluded). A member group that never * simultaneously settles fails loud. Empty ⇒ byte-identical behaviour (no group is * checked). Auto-inference of groups is deferred — declare the group explicitly. */ alignGroups?: readonly AlignGroup[]; /** * Cut 2 — the cross-axis alignment slack (integer px, default 2). A group whose * members' cross-axis centers span MORE than this raises MISALIGNED. Must be a * finite integer >= 0 (fail-loud otherwise, mirroring validateRegion's integer * discipline). */ alignTolerance?: number; /** * Cut 2 — the inter-member spacing slack (integer px, default 2). A group whose * main-axis gaps span MORE than this raises UNEVEN_SPACING. Must be a finite * integer >= 0 (fail-loud otherwise). */ gapTolerance?: number; } /** * 0.63 — the MEANING-PRESERVATION veto class of a single fix LEVER. A `'geometry'` * lever changes only layout/pose/size (reflow, resize box, move, widen, restack) — * the loop may AUTO-apply it. A `'content'` lever changes MEANING (truncate/reword * a caption — verified dialog) — the loop must NEVER auto-apply it; it escalates to * a human. Per-LEVER (not per-diagnostic) so one diagnostic can offer BOTH: any * geometry lever ⇒ still auto-fixable, all-content ⇒ escalate. */ type FixClass = 'geometry' | 'content'; /** One decidable way to resolve a diagnostic, tagged with its meaning-preservation * {@link FixClass}. `lever` is a stable machine token (the prop to touch); * `hint` is the human prose. A diagnostic carries a LIST of these in * `detail.fixHints` (additive to the pinned schema — no version bump). */ interface FixHint { /** The stable lever token — the prop/dimension to adjust (e.g. `'fontSize'`, `'width'`, `'position'`, `'zIndex'`, `'text'`). */ lever: string; /** geometry (auto-fixable) vs content (escalate — never auto-applied). */ fixClass: FixClass; /** Human-readable description of applying this lever. */ hint: string; } interface CritiqueResult { schemaVersion: typeof DIAGNOSTIC_SCHEMA_VERSION; /** true iff any diagnostic has severity `error` (always a static error — the * rendered pass emits only warnings/info). */ hasErrors: boolean; /** FLAT merged, CANONICALLY-SORTED diagnostics (static + rendered). */ diagnostics: SceneDiagnostic[]; /** true when the rendered pass was skipped because static validation errored. */ renderedSkipped: boolean; /** why the rendered pass was skipped (present iff `renderedSkipped`). */ renderedSkipReason?: string; /** how many integer-frame grid samples the rendered pass took (0 if skipped). */ sampledFrames: number; } /** * Rendered-geometric diagnostics for `(scene, timeline)`. Runs `validateScene` * first; short-circuits the rendered pass on any static error. Otherwise binds the * scene, samples a fixed integer-frame grid, and emits OFF_CANVAS / TEXT_OVERFLOW * / OCCLUSION where a span check fires. PURE READ — never changes evaluate() or a * render path; canonically-sorted (frame, then code, then node-id) output. */ declare function critique(scene: Scene, timeline: Timeline, opts?: CritiqueOptions): CritiqueResult; /** * CANONICAL SORT — by frame (detail.frame, undefined last), then code, then * node-id. An unordered diagnostic array is golden-unstable; this is the HARD emit * requirement (assert sort-invariance: shuffle-then-sort ≡ emitted order). Stable * for equal keys (the static-then-rendered concat order is deterministic). */ declare function sortDiagnostics(diags: SceneDiagnostic[]): SceneDiagnostic[]; //#endregion //#region src/fidelity.d.ts /** exportFidelity result — the CLI-lint `{ hasErrors, diagnostics }` shape. */ interface ExportFidelityResult { schemaVersion: typeof DIAGNOSTIC_SCHEMA_VERSION; /** true iff any diagnostic is severity `error` — always false here (render-only * use is a warning, never a build error), kept for shape-parity with the family. */ hasErrors: boolean; /** every RENDER_ONLY_EXPORT finding, canonically sorted. */ diagnostics: SceneDiagnostic[]; } /** * Statically scan `scene` (+ optional `timeline`, for reveal-track detection) for * render-only features that won't survive Lottie export, aggregating one * RENDER_ONLY_EXPORT warning per affected node. Pure read; a scene with no * render-only feature returns an EMPTY diagnostics list. */ declare function exportFidelity(scene: Scene, timeline?: Timeline): ExportFidelityResult; //#endregion //#region src/canonicalScene.d.ts /** * Bind the scene against its doc at t=0 (EXACTLY as diff does) so resolved values * are live, then serialize the SEMANTIC scene structure canonically: every * id-bearing node, sorted by id, as `{ id, type, parent, props }` where `props` * is the sorted list of NON-animated resolved target values. Construction-order * invariant (sorted by id + path); mirrors diff's node/prop comparison exactly. */ declare function canonicalSceneForm(scene: Scene, timeline?: Timeline): string; /** * Serialize the timeline's TRACK set canonically: every track as * `{ target, sig }` sorted by target, where `sig` is the target-independent * value signature (keys/type/expr). Mirrors diff's track comparison (add/remove/ * retarget/keys-change) exactly — a retarget re-keys `target`, a keys-change * re-keys `sig`. Labels/markers/audio/assets/fps are NOT part of this semantic * address (diff ignores them too); they are separate render-cert determinants. */ declare function canonicalTimelineForm(timeline?: Timeline): string; /** SHA-256 of a byte array → 64-char lowercase hex. */ declare function sha256Bytes(msg: Uint8Array): string; /** SHA-256 of a UTF-8 string → 64-char lowercase hex. */ declare function sha256(s: string): string; /** SHA-256 of the canonical scene form — the scene half of the semantic address. */ declare function sceneHash(scene: Scene, timeline?: Timeline): string; /** SHA-256 of the canonical timeline (track) form — the timeline half. */ declare function timelineHash(timeline?: Timeline): string; /** * `certKey(scene, timeline?)` — the PURE semantic content-address of a * (scene, timeline) pair: `sha256(sceneHash · timelineHash)`. NO raster render * (it binds at t=0 to read resolved values, exactly as diff does, then hashes). * * The "will this render be a cache hit?" primitive an author queries BEFORE * spending the farm. Consistent with `diff` by construction (shared * canonicalization here): `certKey(A) === certKey(B) ⟺ diff(A,B).empty`. * * It is the SEMANTIC half of the per-frame render certificate — the full * `certHash` additionally folds frameKey / fontDigest / backendHash / toolchain / * renderConfig (the CLI owns those). Two scenes with the same certKey still render * to different bytes under different fonts/fps — that is why the cache keys on the * fuller certHash, not on certKey. */ declare function certKey(scene: Scene, timeline?: Timeline): string; //#endregion //#region src/assess.d.ts interface AssessOptions extends CritiqueOptions { /** * ALSO fold in the static render-only export-fidelity scan (exportFidelity) — * for an EXPORT-BOUND scene, a render-only feature (motionBlur/echo/…) the Lottie * exporter drops is part of the verdict. Default false (a realtime-only scene * needn't see it). Its findings are warnings, never geometry-fixable, so they * ESCALATE (report up) rather than block `clean`. */ exportBound?: boolean; /** * A PREVIOUS state to diff against — assess folds in the blast-radius (`diff( * previous, current)`) so an author sees "exactly what my edit changed." Purely * informational: it NEVER affects `clean` (a change is neither right nor wrong). */ previous?: DiffInput; /** * KNOWINGLY-ACCEPTED diagnostics (scoped-intent, like critique's `offstage`) — a * deliberate render-only export drop, an intentional brand-contrast. Each entry * matches a diagnostic by its `code`, its `node` id (SUBTREE match — an ancestor * id suppresses its whole subtree), or the combined `'@'` form. A * matched diagnostic is removed from the FIXABLE set (so `clean` can be true with * an accepted residual) but still appears in `diagnostics` + `accepted`. */ accepted?: readonly string[]; } interface AssessResult { schemaVersion: typeof DIAGNOSTIC_SCHEMA_VERSION; /** * TRUE iff nothing MECHANICAL remains: no error-severity diagnostic AND no * geometry-fixable warning — after accepted diagnostics are removed and * content-only ones escalated. `clean` is the loop's termination signal. */ clean: boolean; /** true iff any (non-accepted) diagnostic is severity `error`. */ hasErrors: boolean; /** The UNIFIED, deduped, PRIORITIZED diagnostics (severity, then canonical sort). */ diagnostics: SceneDiagnostic[]; /** * The loop's WORK QUEUE — non-accepted warnings that expose a GEOMETRY lever (so * the agent can auto-apply a fix). Prioritized; `fixable[0]` is the top target. */ fixable: SceneDiagnostic[]; /** * Non-accepted WARNINGS the loop cannot mechanically auto-close — the * mechanical/human boundary (the meaning-preservation veto's ESCALATE half). * Two ways in: a diagnostic whose only levers are content-class (all-content → * changing them touches MEANING, e.g. "shorten a caption"), OR one with no * mechanical lever at all (a pure human-judgment fidelity/acceptance call, e.g. * RENDER_ONLY_EXPORT: accept the export-fidelity loss or restructure the scene). * The loop reports these UP and never auto-applies them; they do NOT block * `clean` (the loop has done all it mechanically can — the human owns the rest). */ escalated: SceneDiagnostic[]; /** Diagnostics matched by `opts.accepted` — the knowingly-accepted residual. */ accepted: SceneDiagnostic[]; /** The pure semantic content-address — the TRUST HANDLE keyed by the certify layer. */ certKey: string; /** * A stable signature of the diagnostic SET — the loop's CONVERGENCE detector. If * this round's `signature` equals last round's, the fix made NO progress (stuck) → * terminate. Lets the loop detect no-progress from the IIFE with no extra export. */ signature: string; /** The blast-radius vs `opts.previous`, present iff a previous state was given. */ blastRadius?: ChangeSet; } /** The structured fix-hint list a critique diagnostic carries in `detail.fixHints` * (empty for diagnostics without decidable levers — static errors, parity notes). */ declare function fixHintsOf(d: SceneDiagnostic): readonly FixHint[]; /** TRUE iff the diagnostic offers ANY geometry lever — the agent may AUTO-fix it * (pick a geometry lever, never a content one). The veto's positive half. */ declare function isGeometryFixable(d: SceneDiagnostic): boolean; /** TRUE iff the diagnostic has levers but ALL are content-class — its only * resolution touches MEANING, so it must ESCALATE (never auto-apply). */ declare function isContentOnly(d: SceneDiagnostic): boolean; declare function assess(scene: Scene, timeline: Timeline, opts?: AssessOptions): AssessResult; /** * A stable signature of a diagnostic SET — the loop compares this round's signature * to last round's; EQUAL ⇒ no progress ⇒ STUCK ⇒ terminate (never a silent infinite * loop). Deterministic: built from the already-canonically-sorted `diagnostics`. */ declare function diagnosticsSignature(diagnostics: readonly SceneDiagnostic[]): string; /** TRUE iff two assess rounds produced the SAME diagnostic set (no progress). */ declare function sameDiagnostics(a: readonly SceneDiagnostic[], b: readonly SceneDiagnostic[]): boolean; //#endregion export { type AlignGroup, type AssessOptions, type AssessResult, type CacheColdResult, type Change, type ChangeOp, type ChangeSet, type CommandDelta, type ContainBound, CritiqueError, type CritiqueOptions, type CritiqueResult, DIAGNOSTIC_SCHEMA_VERSION, DL_SNAPSHOT_VERSION, type DiagnosticCode, type DiagnosticSeverity, type DiagnosticSource, type DiffInput, type DiffOptions, type DisplayDiff, type DlSnapshot, DlSnapshotError, type ExportFidelityResult, type FieldChange, type FixClass, type FixHint, type FontByteLoader, type InstancePropState, type NodeRef, type Region, type SafeArea, type SceneDiagnostic, type ValidateSceneFontsOptions, type ValidateSceneResult, assess, auditCacheCold, canonicalSceneForm, canonicalTimelineForm, certKey, sha256 as certSha256, sha256Bytes as certSha256Bytes, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, critique, diagnosticsSignature, diff, diffDisplayLists, exportFidelity, fixHintsOf, formatDisplayDiff, instanceProps, isContentOnly, isGeometryFixable, levenshtein, locateViolation, nearestId, parseDisplaySnapshot, resolveAt, sameDiagnostics, sceneHash, serializeDisplayList, sortDiagnostics, timelineHash, validateScene, validateSceneFonts };