Skip to content
Omena

This page documents a preview surface. Its contract may still change.

Workspace snapshot bindings

Fixed inputs, owner identity, transfer integrity, and stale-write protection for bound workspace requests.

Edit on GitHubAuthored guidance with generated contracts.

A bound workspace request carries the complete OmenaWorkspaceSnapshotBindingV0: the workspace root, OmenaWorkspaceSnapshotIdV0 revision, and an input commitment. The numeric revision remains available to existing SDK requests. It identifies a revision within its owner; it is not sufficient to compare two hosts.

The commitment is a derived integrity digest. It covers the ordered style and source inputs, source language and admitted provider evidence, actual source syntax indexes, manifests, resolver inputs, settings, external SIF facts and their admitted trust records, and source-corpus completeness. Length-framed, domain-separated serialization preserves meaningful list order. The digest does not issue module identities or authenticate a remote publisher.

Configuration here means the admitted effective inputs: diagnostic severity and deep-analysis mode, the five LSP feature flags, package manifest text, resolver path mappings and disk identities, and the external SIF cache fingerprint. It does not promise the raw bytes of every configuration file or scheduler/cache settings that the query does not read. Utility configuration is independently loaded during source admission and its resulting source facts are committed. The optional configContentDigest is an additional caller input; the LSP omits it. It is not evidence that a receiver read or authenticated a configuration file.

Reading an exported snapshot

The version 1 LSP omena/sdkWorkflow envelope can export a binding and the inputs needed to admit it. The CLI SDK transport and daemon bound handshake carry that same binding. Each receiver reconstructs source facts from actual text and language, resolves imports, admits provider results against parsed expression IDs and exact spans, and recomputes the commitment. Serialized SourceSyntaxIndex values and transported SIF trust assertions are not admitted. Local external SIFs are independently regenerated through the existing bridge and trust path; a lock claim is not equivalent to that admission.

External SIF inputs are scoped to the root's reachable dependency closure, including forwarded interfaces and dependency entries. Owner exports select already admitted resolved targets without reading current disk contents. Each receiver independently admits its own local bridge/trust results and applies the same deterministic projection. Unrelated workspaces' SIFs and trust records do not enter the root's commitment. Equal relative import spellings retain their document context; ambiguous convenience aliases yield to the consumer's existing contextual resolver while all resolved target facts remain present.

The commitment also includes independently admitted contextual resolution edges: each editor document and import spelling identifies the actual bridge backing URL, canonical SIF URL, and full artifact digest. A disk SIF origin includes its backing URL and artifact digest, keeping its transitive imports distinct from an unsaved editor buffer at the same URI. Package transitive resolution uses that physical backing URL. Exports project these existing records without disk reads; receivers produce their own records through admission. Swapping two importers' targets changes the commitment even when the unordered SIF/trust set is identical. The existing SIF input carrier retains this admission metadata for query consumers. A resolved SIF reference carries its selected importer edge through forward and dependency traversal; an admitted corpus cannot fall back to a legacy global alias when that context is missing or mismatched. Bound reads verify that attached consumer metadata equals the committed edge set. Existing Salsa input equality includes the metadata, and the disk diagnostics environment fingerprint commits it explicitly, including mappings to corpus members.

These records provide integrity provenance. The legacy serialized admission result omits the Rust metadata; Rust callers supply the actual admitted context to preserve consumer resolution and input commitment consistency.

The resulting SDK view is immutable for a read request. Read clones retain a reader handle and cannot publish another revision. The destination session's existing owner performs supported mutations against an expected current binding, then publishes reconstructed source and provider facts with the changed styles.

The scope of freshness is explicit:

  • LSP bound requests must match the current LSP owner.
  • Daemon bound requests must match that daemon session's current owner. Reconnects cannot replace an existing different binding by importing another claim.
  • A CLI invocation admits an immutable input set. It cannot discover a remote owner's subsequent mutations from the commitment alone.

Consequently, equal complete inputs and revision can have equal portable bindings. A coherent claim admitted by a fresh receiver is not proof that a different process still considers it current. Cross-process current-write authority is not conveyed by this envelope.

Protecting a workspace edit

A snapshot-backed transaction derives expected file digests from its admitted read view. It retains the same actual destination owner's reader handle and requires its current native write guard across staging, precondition checks, journal validation, renames, and rollback. An equal portable binding from a different owner does not satisfy this provenance check. A metadata-only owner advance makes an older transaction stale even when the file bytes are unchanged.

Imported origin is permanent for destination writes. A local in-memory mutation and republication of an imported snapshot does not promote it to native file write authority. Native disk-only CLI writes remain a separate unbound route with their existing transaction safety conditions. Bound daemon format and lint requests currently refuse the unadapted disk route; they require integration with the actual resident document owner before they can succeed.

Wire compatibility and verification

Version 1 wraps existing workflow payloads in an explicit bound envelope. Legacy requests retain their version 0 payloads. A binding without the explicit bound version is refused instead of silently becoming an unbound request. A bound request's inner numeric revision must agree with its full envelope.

The snapshot contract verification probe takes already-built LSP, daemon, and CLI binaries. It captures the actual LSP export, independently admits it through TCP and CLI, compares complete diagnostics and explain payload bytes, and checks input and stale-owner refusals. This contract probe does not establish resident parse reuse, worker cancellation, or memory and latency budgets.

Contextual SIF admission retains the verdict actually returned by the existing bridge for that full artifact. Identical package bytes can be admitted under different root verdict directories. The selected snapshot trust frame therefore uses its admitted importer, specifier, backing URL, canonical URL and artifact digest as a context key, paired with the actual bridge verdict. The input validator checks that this frame agrees with the attached consumer edges; a global canonical-URL map cannot replace a contextual verdict.

Forward and dependency edges retain the initiating document as well as the disk SIF origin. Repeated targets in one admission retain the actual bridge result. A supplied legacy SIF without a current-context verdict must be admitted before it can supply contextual trust; generic non-context seed APIs retain their existing unbound behavior. Export performs no filesystem reconstruction, and untrusted transfers cannot supply this admission metadata.

These Rust fields are omitted from legacy SIF JSON and transport schemas. They intentionally participate in commitments, existing memo input equality and the disk-cache context frame. Rust callers constructing admission edges must provide both their actual verdict and initiating document context. A change to either alters the bound input commitment and the diagnostics cache environment key. The LSP build continues to refuse recorded verdicts when attestation verification is unavailable; metadata does not change verification policy or grant an imported snapshot native write authority.

Local bound imports and subsequent in-memory mutations select recorded verdicts from the importing workspace's .cache/omena verdict directory. A shared package file does not cause another workspace's verdict directory to be used. The LSP keeps its configured regenerable-cache location; SDK imports use the process cache policy for their explicit local root. Recorded verdicts remain scoped to that root even when no regenerable disk cache is available. Cache memory hits still verify the actual root's verdict under the existing verification policy.

OmenaBridgeExternalSifStorageV0::workspace_cache_root() returns Option<&Path> to represent a disabled regenerable disk cache. Existing constructors that take a cache root return Some; the optional-cache constructor accepts a separate recorded-verdict directory. The process-root factory accepts absolute native paths and file URIs using the bridge's existing URI conversion.

Rust caller migration

This snapshot slice contributes to the declared pre-1.0 minor breaking release. OmenaQueryExternalSifInputV0.admitted_resolution_edges and OmenaQueryBridgeExternalSifTrustedResolutionV1.resolution_edges are new public fields. Rust struct literals must supply them, and exhaustive patterns must name them or use ... An empty vector is appropriate for a genuinely legacy, unbound input. A caller retaining admitted inputs must carry the actual bridge-produced edges, artifact digests and verdicts; replacing that provenance with an empty vector changes the input's meaning.

Both fields use serde(skip). The input carrier implements both Serialize and Deserialize: legacy JSON omits its edges, deserialization defaults them to empty, and even a supplied admittedResolutionEdges property is ignored. A nonempty admitted input therefore loses provenance and equality on a JSON roundtrip. Eq includes the edge vector even though the legacy JSON does not. The trusted resolution result implements Serialize, Default and Eq, but not Deserialize; its equal legacy JSON likewise does not imply equal Rust values. These omitted fields do not add wire or IDL properties.

The loss can also change selection. Once any corpus member carries admission metadata, lookup uses the importer and specifier, validates the complete artifact and conflicting contextual records, and refuses a missing or mismatched edge without borrowing a legacy alias. If a JSON roundtrip removes all those edges, the remaining inputs again use the legacy unbound lookup. A serialized legacy SIF is not a portable substitute for the admitted snapshot transfer and re-admission path.

OmenaSdkWorkspaceV0::replace_style_resolution_inputs now returns Result<OmenaSdkSnapshotResponseV0, OmenaError>. A caller that previously read workspace.replace_style_resolution_inputs(inputs).snapshot_id must handle the result first, for example workspace.replace_style_resolution_inputs(inputs)?.snapshot_id. replace_style_sources already returned Result. Both methods now check revision addition instead of saturating: changed unbound inputs at u64::MAX return workspace.snapshot-revision-exhausted before storing inputs. Unchanged no-ops remain successful at that revision and return the same complete snapshot.

Bound resolver changes require replacement of the complete admitted source-fact view and return workspace.snapshot-full-replacement-required, including at the maximum revision. Supported bound style changes require a current owner and its admitted utility inputs, reconstruct and admit the next view, then publish it. A current request clone cannot publish; a stale style request fails the live binding check even for a no-op. Resolver no-ops retain the existing behavior of returning that workspace clone's snapshot without a new publication. Callers must still validate freshness for a subsequent bound read. Refusals preserve the stored inputs, revision and binding.

The NAPI Workspace and CachedWorkspace and the WASM Workspace retain their existing JS replacement methods. Success and no-op responses preserve the full snapshot payload. Native errors carry the serialized typed envelope, and WASM throws that typed envelope. Maximum-revision and bound-owner error paths are validated through private adapter fixtures using existing Rust constructors; production JS constructors do not expose revision or owner seeding.

At the existing JS number boundary, WASM's JSON-compatible serializer cannot encode a revision above Number.MAX_SAFE_INTEGER. A Rust no-op at u64::MAX still succeeds internally, but its WASM response returns the typed sdk.response-serialization error. A changed input at that revision reaches the revision-exhaustion error first. This slice preserves that serializer behavior; normal-revision adapter no-ops return the complete successful snapshot.

On this page