perf(semantic): drop eager member byte hashing from lifecycle authority - #548
Conversation
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
AdmittedProjectionArtifactV1::from_lifecycle_install read and SHA-256 hashed all five artifact members - including the whole model file - on every authority construction, then discarded the bytes. Session open repeats the identical read-and-verify when it consumes the bytes, so every scheduled projection's artifact load and every serving restore attempt paid a redundant full model read. Construction now checks each member's structural pin (declared entry, normalized relative path, regular non-symlink file, exact length) without reading bytes, matching the artifact-store authority whose admission also defers digest checks to reads. Byte digests remain verified by read_member_bytes at every session open, so digest- mismatched bytes still can never open a session or serve a vector. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
|
A successfully parsed LCM JSON payload whose object keys carry credential material was collapsed into StructuredSanitizationError::SanitizerUnavailable and then mapped to DetectionError::Receipt, so the sanitizer's own fail-closed quarantine surfaced as 'privacy sanitizer receipt construction failed' in every projection-drain refusal. Give quarantine findings their own typed state (CredentialKeyQuarantine -> StructuredQuarantine), keep SanitizerUnavailable for detector initialization failure (-> Initialization, with InvalidLimits), and reserve Receipt for real canonical/receipt construction faults (new CanonicalEncoding). Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
💡 Codex ReviewThe release path in When an installed member is corrupted without changing its length, this structural check now admits it, and the AGENTS.md reference: AGENTS.md:L98-L100 Lines 72 to 74 in fe90ba8 In an existing checkout where ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
ScriptedAlchemy
left a comment
There was a problem hiding this comment.
td query cross-review. Crate-only fastembed_adapter.rs change. I did not edit the crate, merge, or flatten the stack.
Holds from this side
- Incomplete stays degraded: no census/retention/degraded-status rewrite. Cheaper path is successful authority construction, not a faster fail.
- Construction is no longer a digest verdict.
prepare_restorestillwarm_query_session()(open/read/hash) before commit, so a same-length digest-mismatched model cannot become current/serving. Queryretrieve_semanticstill cannot see that as Complete. - Semantic miss/BudgetExceeded still must not take down exact/lexical/graph. Untouched here.
- DeadlineExceeded / Failed mapping untouched.
Nits only — inline. Stay draft until you want CI. #545 is up for your review.
Seat retries and the seat/reconcile duplicate publication of one sealed generation rebuilt every graph entity and relation per attempt, with each edge serialized and hashed three times; interactive adjacency hydrated the far endpoint of every edge, including kinds the query excluded, and re-read shared endpoints once per incident edge. - memoize the complete published-graph manifest on the immutable generation, keyed by projection identity and projector revision; only a fully successful build is recorded, so a deadline mid-build memoizes nothing and a memo hit still refuses an expired request - derive each edge/symbol/chunk/file/import stable identity exactly once per projection build and reuse it across the entity and its relations - stage semantic adjacency hydration: excluded edge kinds stop at the validated edge payload without reading their far endpoint, and each surviving unique endpoint hydrates once per batch Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
begin_maintenance_drain is only for run_maintenance_checkpoint and crate tests. A public unchecked drain would close admission with no permit. Admission stays closed after Truncate; Database will not call this on the live submit path.
Codex P2: key quarantine no longer shares the "ambiguous structured document" message. Parse ambiguity stays StructuredQuarantine.
Materializing a foreign/WAL read-snapshot copy previously ran a full rusqlite backup of the scratch copy into a second standalone file, rewriting every main-database page after the family was already copied. Leave WAL journal mode on the exclusively owned copy instead: that checkpoints only the WAL-resident pages in place, drops the second file, and keeps peak scratch near one family copy. Isolated fixture (38 MiB main + 4 MiB WAL, no reflink): 79,918,280 bytes written on the previous tip vs 46,338,756 bytes now, with the same readable snapshot rows. DirectImmutable stays no-copy. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Lifecycle authority construction is structural-only, so an install path that never opens a session could mark a same-length digest-mismatched model Current. schedule_generation's AlreadyPublished resume installed the pointer without warming, and a resume reporting every batch already committed embedded nothing and installed the same way. Both install paths now warm one pooled candidate session before staging publication, exactly as prepare_restore warms before commit: a cold open reads and digest-verifies every member, so corrupt bytes stay Failed/degraded and never become Current or serving. After real embedding the warm reuses the idle pooled session without re-reading the artifact. Tests prove a digest-mismatched model never becomes Current after an AlreadyPublished resume (publication staging never runs) or restore. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
The credential-key quarantine test asserts variant equality directly, which requires PartialEq on the public error enum. Matches the derive set already carried by StructuredSanitizationError. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…nce-checkpoint-port-2ce6' into codex/pr541-review-fix
ScriptedAlchemy
left a comment
There was a problem hiding this comment.
td query re-check on 32b5c8f. The construction≠serving hole is closed: AlreadyPublished and committed-batch now warm_candidate_for_install before stage_projection. Digest-mismatch stays Failed/Runtime, current() stays None, staging does not run. Query cannot see that as Complete. Incomplete stays degraded. No edit, no merge, no flatten. Not touching #545's base.
ScriptedAlchemy
left a comment
There was a problem hiding this comment.
td query deep review of #548 @ 32b5c8f (not #545). No crate edits, no restack/relink/flatten, no base change.
Lock holds. Construction is structural-only. prepare_restore, AlreadyPublished, and the all-batches-committed resume all warm_query_session before install. Featured open_session → fastembed_model → required_member_bytes → digest. Digest-mismatch stays Failed, current() stays None, stage_projection does not run. Query cannot treat that as Complete. Exact/lexical/graph stay unblocked (warm is spawn_blocking on the scheduler task). Incomplete stays degraded.
No serving bug found. Residuals only, inline. Ready watch stays #538. Left #509 alone.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fd6ada3c30
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ArtifactMemberRoleV1::TokenizerConfig, | ||
| ] { | ||
| lifecycle_install.read_member_bytes(role)?; | ||
| lifecycle_install.member_pin_path(role)?; |
There was a problem hiding this comment.
Restore digest verification for generation inspection
When an installed model or tokenizer is corrupted without changing its length, this structural-only check now lets LoadedSemanticArtifactV1::from_lifecycle_projection succeed. ProductionSemanticRuntimeV1::inspect_generation then uses that constructor at crates/tracedecay-usecases/src/semantic_runtime/production.rs:2123-2132 and unconditionally emits SemanticExecutableGenerationV1 with cold_offline_ready and rollback_executable set to true at lines 2164-2169; the configuration backend trusts that evidence before committing activation and rollback. Thus a digest-mismatched artifact can be certified as byte-exact and rollback-ready even though a subsequent session open will reject it. Preserve the constructor optimization if needed, but make this production inspection path explicitly read and verify every pinned member before issuing executable evidence.
AGENTS.md reference: AGENTS.md:L119-L121
Useful? React with 👍 / 👎.
…t-ea75' into cursor/mount-lexical-budget-725a Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…' into cursor/semantic-cheap-authority-construction-36ae Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…raph-db-hydrate-digest-off-gate-13bf Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Materializing a foreign/WAL read-snapshot copy previously ran a full rusqlite backup of the scratch copy into a second standalone file, rewriting every main-database page after the family was already copied. Leave WAL journal mode on the exclusively owned copy instead: that checkpoints only the WAL-resident pages in place, drops the second file, and keeps peak scratch near one family copy. Isolated fixture (38 MiB main + 4 MiB WAL, no reflink): 79,918,280 bytes written on the previous tip vs 46,338,756 bytes now, with the same readable snapshot rows. DirectImmutable stays no-copy. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
The projected memory-graph read and reconciliation paths loaded the full canonical owner source twice, re-parsing every payload_json and event_json a second time only to rehash the same ID-derived watermark. load_source now captures the highest lineage event_sequence inside the same read snapshot; because the lineage table is append-only and every source-affecting mutation records a lineage event in the same transaction on both fact write paths, an unchanged stamp proves the watermark is unchanged. A moved or absent stamp falls back to the full reload and watermark compare, preserving exact conflict semantics. A settled reconciliation pass over eight 16KiB payload facts drops from 48 source rows / 278656 bytes loaded to 24 rows / 139328 bytes. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Drop the cfg(test) ports load_source_for_test and finish_reconciliation_watermark_for_test. The 2-to-1 source-load claim now baselines against an unmounted publication pass, which records exactly one canonical source load before failing at the graph mount, and compares a settled publish_project_memory_graph_after_write pass against it through the same reconciliation telemetry. The stale-stamp conflict invariant is proven by parking a publication inside the verified-graph reconcile, committing a fact mid-pass, and releasing it: the finish reloads the mutated source, surfaces GraphConflict with nothing pending, and the publisher schedules exactly one republication that settles with one further load. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
The cheaper-construction test compared std::time::Instant around a 16MiB read+hash baseline, a host-clock dependency that can flake under CI load and measures nothing a virtual clock could pause. Replace the timing comparison with a strictly-less-work proof: one verification pass must return every member's exact pinned bytes (impossible without reading all of them), and construction must still succeed after member read permission is revoked, so any construction-time byte read fails the constructor and the test. The revocation is itself proven binding because the read path fails on the same authority. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…-13bf Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…ff-gate-13bf' into cursor/graph-db-hydrate-digest-off-gate-13bf Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…ink-6b34' into td/merge-down
Materializing a foreign/WAL read-snapshot copy previously ran a full rusqlite backup of the scratch copy into a second standalone file, rewriting every main-database page after the family was already copied. Leave WAL journal mode on the exclusively owned copy instead: that checkpoints only the WAL-resident pages in place, drops the second file, and keeps peak scratch near one family copy. Isolated fixture (38 MiB main + 4 MiB WAL, no reflink): 79,918,280 bytes written on the previous tip vs 46,338,756 bytes now, with the same readable snapshot rows. DirectImmutable stays no-copy. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…ce-load-once-8e36
…cm-display-token-cache-ba84
…a84' into td/carry-b # Conflicts: # crates/tracedecay-runtime-core/src/sqlite_snapshot_materialize.rs
…hot-checkpoint-f76e
…digest-off-gate-13bf
Crate-only change in
crates/tracedecay-semanticmaking semantic serving/census-adjacent work cheaper on the successful path, plus the coordinator-required serving guard: a same-length digest-mismatched model can never become Current/serving. Stacked onorigin/codex/tracedecay-total-redesign-plan(tip2accc616).1. Files + symbols changed
crates/tracedecay-semantic/src/fastembed_adapter.rs—AdmittedProjectionArtifactV1::from_lifecycle_install, with the shared structural check factored intoLifecycleInstallArtifactV1::member_pin_path(used by both construction andread_member_bytes).crates/tracedecay-semantic/src/lib.rs—DaemonSemanticRuntimeHandleV1::schedule_generationinstall paths now call a newwarm_candidate_for_installbefore staging publication (see §2b).2a. Why it is cheaper
from_lifecycle_installeagerly read and SHA-256 hashed all five artifact members — including the entire model file (default ceiling 700 MiB) — on every authority construction, then discarded the bytes. Its own comment admitted the redundancy: "Each future session open repeats the same check when it reads the bytes." Session open (open_session→required_member_bytes→read_member_bytes) re-reads and digest-verifies every member before any byte reaches ORT.This constructor runs via
LoadedSemanticArtifactV1::from_lifecycle/from_lifecycle_projectionon the hot serving paths behind the live degraded reasons:schedule_generation(load_artifactper saved-edit projection request), andprepare_restorevia the daemon'sprepare_restore_current) — exactly the retry loop that runs while "no code generation is currently serving", where each attempt previously paid the full model read + hash twice (once at construction, once at thewarm_query_sessioncold open).Construction now performs only the byte-free structural pin checks per member (declared pin, normalized relative path, regular non-symlink file, exact length via one
symlink_metadatacall) — O(members) metadata instead of O(model bytes) read + hash. This matches the artifact-store authority (AdmittedProjectionArtifactV1::admit), whose admission already defers digest checks to reads. Plan 31 length AND SHA-256 both remain enforced: length eagerly at construction, digest at every session open, where the bytes are consumed. Install-time trust is unchanged (model_lifecycle/acquisition.rsverifies downloaded bytes against catalog pins).Investigated and discarded the labeled hypothesis: first-publish
schedule_generationdoes not re-embed — embedding is batch-checkpointed andcompleted_batch_offsetskips committed batches;current/prepare_current_observation/query_factoryare already pointer-cheap.2b. Must-fix: warm before the serving pointer installs (review r3817700311)
With structural-only construction,
schedule_generation'sAlreadyPublishedresume installed the serving pointer viainstall_candidate_on_successwithout ever opening a session — so a same-length digest-mismatched model could have become Current (and downstreammark_ready). The resume that reports every batch already committed had the same hole: the embed loop runs zero iterations, so no session opens before install.Both install paths now call
warm_candidate_for_install(aspawn_blockingwarm_query_session) before publication staging, exactly asprepare_restorewarms before commit. A cold open reads and digest-verifies every member; failure maps toSemanticRuntimeScheduleFailureV1::Runtime→Failed/degraded — never Current, never serving, never census_complete. After real embedding the warm reuses the idle pooled session with no re-read, so the cheaper-serving win is preserved.3. Isolated test evidence
All tests are in-crate, hermetic (tempdir lifecycle-install fixtures), no live profile, no network model.
cargo test -p tracedecay-semantic: 170 passed, 0 failed (169 lib + 1 integration). With--features semantic-fastembed(production FastEmbed/ORT runtime): 178 passed, 0 failed (177 lib + 1 integration, 2 pre-existing ignored).Less work (operation-count proof — zero member byte reads at construction):
fastembed_adapter::tests::lifecycle_authority_construction_reads_no_member_bytes— the fixture's model member has the pinned length but not the pinned digest. Construction succeeds, which is only possible if it read zero member bytes (a digest verdict requires reading every byte). On the same authority,required_member_bytes(Model)and (undersemantic-fastembed)open_sessionfail with typedCorruptArtifact.Less time (baseline vs treatment on the same fixture):
fastembed_adapter::tests::lifecycle_authority_construction_is_cheaper_than_member_byte_verification— on one 16 MiB fixture, the baseline is a singleread_member_bytespass over all five members (byte-identical to the work the old constructor performed on top of what the new one does; still the per-session-open verification). The assert isconstruction < baseline, andconstructionis already the elapsed total of the 10-loop (review r3817700318). Ten constructions cheaper than one read+hash. Measured:176.7 µs for the ten-construction total vs 17.2 ms for one baseline pass.
Serving guard (must-fix) — digest-mismatched model never becomes Current:
scheduling_tests::already_published_resume_with_digest_mismatched_model_never_becomes_current—AlreadyPublishedresume over the mismatched authority endsFailed { reason: Runtime, prior_generation: None },current()staysNone, and the publication-staging callback provably never runs. Falsified against the hole: with the warm removed, this test fails undersemantic-fastembed(the pointer becomes Current) — verified locally, then the fix restored.scheduling_tests::restore_with_digest_mismatched_model_never_becomes_current—prepare_restorereturnsErr(Runtime); status staysUnavailable,current()staysNone.Fail-closed retained:
lifecycle_authority_construction_rejects_structural_pin_violations— missing member, length-pin mismatch, and (unix) symlinked member still fail construction eagerly with typedCorruptArtifact.lifecycle_install_authority_verifies_member_bytes_at_read— valid pins construct; corrupt bytes fail at read; production runtime admits the install andopen_sessionfails typed on non-ONNX fixture bytes.4. Plan lock confirmation
schedule_generationremains enqueue-only; exact/lexical/graph search stay non-blocking.store_maintenance/code_index_schedulerlog lines are unmodified. Not a faster degrade — the cheaper path is the successful authority construction.deadline_authorityintegration test green. Query embed keeps one-in-flight admission, non-blocking acquire, and deadline honoring (all existingruntime_query/session_pooltests pass unchanged).