Skip to main content

Store

Struct Store 

Source
pub struct Store { /* private fields */ }

Implementations§

Source§

impl Store

Source

pub fn session_budget( &self, session_id: &str, ) -> Result<SessionBudget, StoreError>

Compute the budget spent by the given session_id across every op currently reachable from any branch head. Returns (spent: 0, op_count: 0) for unknown sessions, with cap populated from policy.session_budgets.

Source

pub fn session_budget_cap( &self, session_id: &str, ) -> Result<Option<u64>, StoreError>

Resolve the budget cap configured for session_id from policy.json’s session_budgets (#292 slice 2). Returns None when no enforcement is configured.

Source

pub fn all_session_budgets(&self) -> Result<Vec<SessionBudget>, StoreError>

Compute per-session budget rollups across every branch. Returns one entry per distinct session that contributed at least one budget-bearing op. Sorted by session_id so the output is deterministic.

Source§

impl Store

Source

pub fn plan( &self, branch: &str, goal: &str, max_cost: Option<u64>, session_id: Option<&str>, ) -> Result<Plan, StoreError>

lex plan (#307). See module docs.

Source§

impl Store

Source

pub fn open(root: impl AsRef<Path>) -> Result<Self, StoreError>

Open or create a store rooted at root.

Source

pub fn rebuild_stage_index(&self) -> Result<usize, StoreError>

Build (or top up) the reverse index in one pass over every SigId in the store, rather than relying on lookup_lifecycle to discover entries one at a time. Safe to call at any time, including on a partially-built index (e.g. one left behind by an interrupted request that was populating it lazily): already- indexed stage_ids are skipped, so this only does the work that remains. Returns the number of newly-added entries.

Source

pub fn root(&self) -> &Path

Source

pub fn put_blob(&self, content: &str) -> Result<String, StoreError>

Content-address content and persist it under <root>/blobs/<sha>. Returns the sha. Idempotent: re-putting identical content is a no-op. Concurrency-safe — writes to a unique temp file then atomically renames onto the content-addressed path, so parallel writers of the same content can’t corrupt it.

Source

pub fn get_blob(&self, sha: &str) -> Result<String, StoreError>

Read a blob by its sha. UnknownBlob if absent.

Source

pub fn has_blob(&self, sha: &str) -> bool

Whether a blob with this sha exists.

Source

pub fn set_blob_ref( &self, namespace: &str, key: &str, sha: &str, ) -> Result<(), StoreError>

Bind key to a blob sha within namespace (e.g. namespace "loom/sprint-abc", key "build-node"). Overwrites an existing binding. The namespace may contain /; neither namespace nor key may contain a .. path component.

Source

pub fn get_blob_ref( &self, namespace: &str, key: &str, ) -> Result<String, StoreError>

Resolve namespace/key to a blob sha. UnknownBlobRef if unbound.

Source

pub fn list_blob_refs( &self, namespace: &str, ) -> Result<BTreeMap<String, String>, StoreError>

All key → sha bindings in a namespace (e.g. every artifact in a sprint). Empty map if the namespace has no bindings yet.

Source

pub fn publish(&self, stage: &Stage) -> Result<String, StoreError>

Publish a stage as Draft. Returns the StageId. Idempotent: republishing the same canonical AST returns the same StageId without writing duplicates.

Source

pub fn publish_signed( &self, stage: &Stage, signer: Option<&Keypair>, ) -> Result<String, StoreError>

Like Self::publish but optionally attaches an Ed25519 signature over the StageId (#227). When signer is Some, the persisted metadata gets a signature field that downstream consumers can verify via lex_vcs::verify_stage_id.

Idempotency: if a metadata file already exists the signature is not re-written. This preserves “republishing is a no-op” even across different signers — promoting a signed stage requires a fresh stage hash anyway, so a metadata overwrite would be the wrong primitive.

Source

pub fn activate(&self, stage_id: &str) -> Result<(), StoreError>

Source

pub fn deprecate( &self, stage_id: &str, reason: impl Into<String>, ) -> Result<(), StoreError>

Source

pub fn tombstone(&self, stage_id: &str) -> Result<(), StoreError>

Source

pub fn resolve_sig(&self, sig: &str) -> Result<Option<String>, StoreError>

The current Active StageId for a signature, or None.

Source

pub fn sig_history( &self, sig: &str, ) -> Result<Vec<StageHistoryEntry>, StoreError>

Per-stage history for a SigId, ordered chronologically by the last transition timestamp. Returns one entry per distinct StageId that has ever been published under sig. Ok(vec![]) if the SigId doesn’t exist in the store.

Used by lex blame to render “where does this fn come from”.

Source

pub fn get_ast(&self, stage_id: &str) -> Result<Stage, StoreError>

Source

pub fn get_asts_for_sigs_bulk( &self, pairs: &[(String, String)], ) -> Vec<Result<Stage, StoreError>>

Bulk AST fetch for callers that already know each stage’s signature — a branch head map, for instance, which is keyed by SigId and whose values are the StageIds it points at.

Prefer this over Self::get_asts_bulk whenever the SigId is in hand, because resolving a StageId back to a SigId is not reliable: a StageId hashes the structural signature plus the implementation, deliberately not the name (docs/INVARIANTS.md), so two functions that differ only in name share one StageId while having two distinct SigIds — and two separate ASTs, one under each sig directory. stage_index maps each StageId to a single sig, so get_ast/get_asts_bulk return whichever of those ASTs the index happens to name, i.e. the wrong name half the time (#826). Reading straight from the sig the caller already knows removes the ambiguity — and skips loading the index at all.

Returns results in the same order as pairs, Err for anything that fails to resolve (mirroring get_ast’s error semantics).

Source

pub fn get_asts_bulk( &self, stage_ids: &[String], ) -> Vec<Result<Stage, StoreError>>

Bulk variant of Self::get_ast for callers resolving many stage_ids at once (e.g. pkg_publish_handler’s old_head scan over every live function in a tenant, once per publish request). get_ast in a loop calls lookup_lifecycle once per stage_id, and lookup_lifecycle’s index-hit path reads and re-parses the entire stage_index.jsonl on every single call — fine for one call, but O(index size × N) for N calls in a row, which dominates once the index itself is large (#825’s follow-up: still correct and far better than the pre-index full-tenant-scan-per-call behavior, but the per-call reparse is itself a real, measured cost — 87.6s for 3,664 calls against a ~14k-line index on the alpibrusl tenant).

This loads the index once for the whole batch and keeps it in memory across all stage_ids, only touching disk again to append genuinely new entries (a positive backfill or a negative “not found anywhere” cache, same as the single-call path) — never to re-read what’s already loaded.

Returns results in the same order as stage_ids, Err for anything that fails to resolve (mirroring get_ast’s error semantics per call).

Source

pub fn get_metadata(&self, stage_id: &str) -> Result<Metadata, StoreError>

Source

pub fn get_status(&self, stage_id: &str) -> Result<StageStatus, StoreError>

Source

pub fn list_stages_by_name(&self, name: &str) -> Result<Vec<String>, StoreError>

Source

pub fn list_sigs(&self) -> Result<Vec<String>, StoreError>

Source

pub fn attach_test(&self, sig: &str, test: &Test) -> Result<String, StoreError>

Source

pub fn list_tests(&self, sig: &str) -> Result<Vec<Test>, StoreError>

Source

pub fn attach_spec(&self, sig: &str, spec: &Spec) -> Result<String, StoreError>

Source

pub fn list_specs(&self, sig: &str) -> Result<Vec<Spec>, StoreError>

Source

pub fn save_trace(&self, tree: &TraceTree) -> Result<String, StoreError>

Source

pub fn load_trace(&self, run_id: &str) -> Result<TraceTree, StoreError>

Source

pub fn list_traces(&self) -> Result<Vec<String>, StoreError>

Source

pub fn publish_program( &self, branch: &str, stages: &[Stage], diff: &DiffReport, new_imports: &ImportMap, activate: bool, ) -> Result<PublishOutcome, StoreError>

Apply a published program to a branch as a sequence of typed operations. Returns the ordered list of op_ids + the new head_op. The caller (lex publish CLI, lex serve’s HTTP handler) is responsible for computing the DiffReport against the current branch head — the diff infrastructure lives in lex-vcs::compute_diff (previously lex-cli) to keep this layer from owning diffing logic.

On success: every op in the returned list is durable in the op log and the branch’s head_op points at the last one. On a no-op (no diff): returns empty ops and the existing head_op unchanged.

Source

pub fn publish_program_signed( &self, branch: &str, stages: &[Stage], diff: &DiffReport, new_imports: &ImportMap, activate: bool, signer: Option<&Keypair>, ) -> Result<PublishOutcome, StoreError>

Signed variant of Self::publish_program (#227). Every stage written under this batch gets the same signer; per-stage keys aren’t supported because the agent identity model treats a publish as a single authorial act.

Source

pub fn derive_imports_from_oplog( &self, branch: &str, ) -> Result<ImportMap, StoreError>

Source

pub fn apply_operation_checked( &self, branch: &str, op: Operation, transition: StageTransition, candidate: &[Stage], ) -> Result<OpId, StoreError>

Apply an operation to a branch and advance its head_op.

The single advance path. Validates parents via lex_vcs::apply, persists the operation via the op log, then atomically advances the branch file’s head_op via set_branch_head_op.

Errors:

  • UnknownBranch: branch does not exist (no op is persisted).
  • Apply(ApplyError::StaleParent): the op’s parents don’t match the branch head — head is unchanged. Callers that want retry-on-stale (e.g. lex publish re-running against a moved head) match on this variant explicitly.
  • Apply(ApplyError::UnknownMergeParent): a merge op’s second parent isn’t in the log.
  • Io: filesystem error during persist or branch advance.

Crash recovery: between op persist and branch advance, a crash can leave an orphan op record in the log with no branch pointing at it. The op is content-addressed and cheap to re-derive from the same source. See Apply a single op against branch, gated on the candidate program typechecking. The per-op variant of #130’s write-time gate — counterpart to Self::publish_program’s batch-mode check.

candidate is the sequence of Stages that would exist on this branch after the op is applied. Caller’s responsibility: today neither lex-store nor lex-vcs reconstruct the candidate from the op + branch state on behalf of the caller. The natural callers (HTTP POST /v1/publish for a single op; agent harnesses driving merges via the future #134 API) already have the candidate in memory.

On rejection: branch head unchanged, no op record persisted. Same atomicity guarantee as the publish path.

§Why a separate method, not a flag on apply_operation

apply_operation accepting Option<&[Stage]> and silently skipping the gate on None is exactly the kind of “secretly opt-out” path #130 is trying to remove. The honest split: apply_operation for the one caller that already typechecked its input up front (publish_program), apply_operation_checked for callers holding the candidate, Self::apply_operation_gated for single-parent callers that hold only the transition (/v1/patch), and Self::apply_merge_op_gated for merge commits (#833).

Source

pub fn candidate_program_for( &self, branch: &str, transition: &StageTransition, ) -> Result<Vec<Stage>, StoreError>

The program that would exist on branch after transition is applied: the branch head (snapshot-cached) with the transition replayed over it, every resulting (sig, stage) bulk-loaded. Exact for a single-parent transition — the candidate Self::apply_operation_gated wants. Not valid for a merge: a StageTransition::Merge records only the delta relative to dst, while the op-DAG replay that computes a merge’s real head walks both parents (#833).

Source

pub fn apply_operation_gated( &self, branch: &str, op: Operation, transition: StageTransition, ) -> Result<OpId, StoreError>

Self::apply_operation_checked for a single-parent op where the caller holds only the transition: assembles the candidate via Self::candidate_program_for and runs the gate. Same rejection semantics — TypeError, a RepairHint attestation, head unchanged, nothing persisted. This is the write path for /v1/patch (#833). Merge ops must not use it (see candidate_program_for); they go through Self::apply_merge_op_gated.

Source

pub fn apply_merge_op_gated( &self, branch: &str, op: Operation, transition: StageTransition, ) -> Result<OpId, StoreError>

The gated write path for merge commits (commit_merge, POST /v1/merge/<id>/commit, lex merge commit).

A StageTransition::Merge records only the delta relative to dst; the sig->stage map every consumer reads is recomputed by replaying the op DAG, which for a merge walks both parents and can surface sigs the delta never mentions. So the only way to know the true post-merge program is to replay it — land the op and read branch_head. This lands the merge op, type-checks the resulting head, and on a failure rolls the head back and returns TypeError.

Before #833 the merge paths landed through the ungated apply_operation, so a merge whose result didn’t compose (e.g. dst still calls helper, an agent-supplied resolution dropped it) advanced the head with nothing to catch it.

Rollback leaves the rejected merge op as an unreachable record (reclaimed by lex op gc, the same orphan crash-recovery already tolerates). A stage the merge names that was never published surfaces as the underlying StoreError from the bulk read — the “never advance onto content that can’t be loaded” invariant from the other side.

Source

pub fn typecheck_merge_projection( &self, branch: &str, delta: &BTreeMap<String, Option<String>>, ) -> Result<(), StoreError>

Type-check the program that would result from overlaying a merge delta onto branch’s current head — without moving the head (#834). delta maps sig_id -> Some(stage) to set that sig to stage, or sig_id -> None to remove it, exactly the entries a StageTransition::Merge records.

This is the read-only, resolve-time counterpart of apply_merge_op_gated’s commit-time gate: it lets a merge session tell an agent which resolution broke type-checking the moment it is submitted, instead of only after a failed commit. Ok(()) means the projected program composes; a type failure is Err(StoreError::TypeError(..)); a read failure is the corresponding StoreError I/O variant.

Source

pub fn try_semantic_body_merge( &self, dst_branch: &str, sig_id: &str, base: &str, ours: &str, theirs: &str, ) -> Result<Option<String>, StoreError>

#838: attempt a typed three-way merge of a single sig’s body for a ModifyModify conflict — the intra-function, better-than-git case where two agents edited disjoint subtrees of the same function (different match arms, different let bindings).

base / ours (the dst side) / theirs (the src side) are the three stage ids the merge engine surfaced for sig_id. Loads the three FnDecls, structurally merges the bodies (lex_vcs::merge_bodies), and accepts the result only if the merged function also type-checks against dst_branch’s head — a body that composes syntactically but not by type is still a conflict (#838). On success the merged stage is published (content-addressed, idempotent; orphaned and GC-reclaimable if the merge is never committed) and its id returned; None means “fall back to a whole-function conflict.”

Deliberately narrow for this slice: only pure body divergence is merged. If the two sides disagree on anything but the body (examples, type params — the signature is identical by construction, since all three share sig_id), or either stage isn’t a function, it falls back to a conflict.

Source

pub fn recompute_producer_trust( &self, tool_id: &str, window: usize, granted_by: &str, ) -> Result<Option<AttestationId>, StoreError>

Open the attestation log rooted at this store. The log lives under <root>/attestations/; opening is idempotent and cheap (fs::create_dir_all). Exposed publicly so consumers — lex blame --with-evidence, GET /v1/stage/<id>/attestations — can read what the store gate emitted without round-tripping through this crate’s API surface. Recompute a producer’s trust score from its recent attestation history and emit a fresh ProducerTrust attestation (#293). Score = `passed / (passed + failed

  • inconclusive)over the lastwindowattestations produced bytool_id, expressed in thousandths (0..=1000`).

Refuses to grant trust when the tool has an active ProducerBlock — the block wins as a hard veto. Returns Ok(None) for “no attestations to score” (a brand-new producer); the caller can choose how to handle it (typically: skip the publish until evidence accrues).

granted_by is the identity of the actor running the recompute (typically the human admin, or “lex-ci-bot” for an automated nightly).

Source

pub fn live_producer_trust_scores( &self, ) -> Result<BTreeMap<String, u32>, StoreError>

The latest live ProducerTrust score (thousandths, 0..=1000) for every producer that currently has trust: the newest score per tool by timestamp, excluding any tool under an active ProducerBlock (a block is a hard veto over trust, matching recompute_producer_trust).

Used to export a capsule trusted-keys keyring from earned trust — the producer id doubles as the publisher’s signing key downstream, so this turns track record into the allowlist capsule install consumes.

Source

pub fn attestation_log(&self) -> Result<AttestationLog, StoreError>

Source

pub fn record_examples_passed( &self, stage_id: &str, op_id: &OpId, count: usize, ) -> Result<(), StoreError>

Emit an Examples::Passed attestation for a published stage whose behavioral examples {} block was run and passed (#835, Tier 1). Mirrors Self::record_typecheck_passed. The behavioral run itself happens one layer up (lex-api / lex-cli) because it needs the bytecode compiler + VM, which this crate deliberately doesn’t depend on; the store only records the verdict. file_hash uses the stage id — the stage fully determines its own examples.

Source

pub fn record_review( &self, stage_id: &str, op_id: Option<OpId>, reviewer: &str, verdict: ReviewVerdict, notes: Option<String>, ) -> Result<AttestationId, StoreError>

Record a structured Review verdict on a stage (#836 G4). The verdict maps onto the attestation result so existing result-based tooling reads it: Approve->Passed, Reject->Failed, RequestChanges->Inconclusive.

Source

pub fn latest_review_verdict( &self, stage_id: &str, ) -> Result<Option<ReviewVerdict>, StoreError>

The latest Review verdict recorded on a stage, if any (#836 G4). “Latest” is by attestation timestamp; ties keep the last one seen. Used by promote_candidate to honor a standing Reject.

Source

pub fn record_op_trace( &self, run_id: &str, root_target: &str, op_id: &OpId, result: AttestationResult, producer: ProducerDescriptor, ) -> Result<usize, StoreError>

Emit Trace attestations linking an already-committed op to the run that produced it (#257). One attestation per produced stage (matching the TypeCheck emission contract — see Self::apply_operation_checked) with op_id: Some(op_id) set, so lex trace --op <op_id> surfaces the run.

Returns the number of attestations emitted (zero for ops that produce no attestable stage, e.g. Remove / ImportOnly).

Idempotent: re-emitting for the same (run_id, root_target, op_id, stage_id, producer, result) tuple dedups via content addressing.

op_id must already exist in the op log — an unknown op surfaces as StoreError::UnknownOp.

Source

pub fn record_run_committed_ops_since( &self, run_id: &str, root_target: &str, branch: &str, base: Option<&OpId>, result: AttestationResult, producer: ProducerDescriptor, ) -> Result<usize, StoreError>

Walk ops_since(branch_head, base) and emit per-stage Trace attestations for each new op, linking them to the run that produced them (#257). Used by lex run --trace after the VM exits: snapshot base = branch_head before the run, then call this with the post-run head.

base = None means “every op currently reachable from the branch head” — generally not what you want for a single run; pass the pre-run head.

Returns the total number of attestations emitted across every new op. Zero is the common case (the run committed no ops).

Idempotent on the per-op level via Self::record_op_trace.

Source

pub fn apply_replace_match_arm( &self, branch: &str, from_stage_id: &str, match_node: &NodeId, arm_index: usize, new_body: CExpr, ) -> Result<OpId, StoreError>

Apply a typed ReplaceMatchArm transform (#280) and emit a OperationKind::ReplaceMatchArm op that records the semantic shape of the edit, not just the byte effect.

Steps:

  1. Load the source stage’s canonical bytes (delta-aware).
  2. Run lex_ast::replace_match_arm to produce the new Stage. Pure function, no I/O.
  3. Publish the new stage. Idempotent on the content-addressed to_stage_id.
  4. Assemble the candidate program (every active stage on the branch, with the rewritten one swapped in) and call Self::apply_operation_checked — re-typechecks and runs every existing gate (TypeCheck attestation, required_attestations, producer-block walk-back).

Failure modes:

  • StoreError::TransformError — transform didn’t apply. The branch is unchanged; no stage published.
  • StoreError::TypeError — transform produced an ill-typed program. The new stage is on disk (idempotent on its content hash) but the branch is unchanged. Same “publish without advance” semantics as #245.
  • Everything else from apply_operation_checked.
Source

pub fn apply_rename_local( &self, branch: &str, from_stage_id: &str, let_node: &NodeId, new_name: &str, ) -> Result<OpId, StoreError>

Apply a typed RenameLocal transform (#280) — rename a let-bound local within a fn body and emit a matching OperationKind::RenameLocal. Same end-to-end shape as Self::apply_replace_match_arm; see that method for the failure-mode taxonomy.

Source

pub fn apply_inline_let( &self, branch: &str, from_stage_id: &str, let_node: &NodeId, ) -> Result<OpId, StoreError>

Apply a typed InlineLet transform (#280) — eliminate a let x := v; body by substituting v for every unshadowed x in body, then replacing the Let node with the substituted body. Same end-to-end shape as Self::apply_replace_match_arm.

Source

pub fn apply_extract_function( &self, branch: &str, from_stage_id: &str, expr_node: &NodeId, spec: ExtractFnSpec, ) -> Result<(OpId, OpId), StoreError>

Apply a typed ExtractFunction transform (#280 slice 4) — extract a sub-expression of from_stage_id’s body into a new top-level fn defined by spec, and emit two ops tied together by a shared synthetic Intent so lex op log --intent <id> groups them.

The two ops:

  1. AddFunction { sig_id: <new_fn_sig>, stage_id: <new_fn_stage> }
  2. ModifyBody { sig_id: <source_sig>, from_stage_id, to_stage_id: <modified> }

The shared Intent’s prompt is structured (extract_function: <new_fn_name> plus the source identity) so downstream tooling can recover the typed-transform shape from the op-log + intent-log join.

Returns (add_fn_op_id, modify_body_op_id).

Source

pub fn propose_candidate( &self, branch: &str, new_stage: &Stage, intent_id: &IntentId, ) -> Result<OpId, StoreError>

Propose a stage for sig_id without advancing the branch head (#294). Multiple agents can call this concurrently for the same sig — every call lands a fresh Candidate op chained off the current head_op. The branch head stays where it was; a later Self::promote_candidate picks the winner.

The caller is responsible for typechecking new_stage against whatever program context they consider valid — propose_candidate doesn’t run the gate. Type errors surface at promotion time, where the candidate is composed back into a candidate program via the standard apply_operation_checked path.

The stage is published (idempotent on content hash). The intent_id is required so downstream consumers can distinguish proposals by author.

Source

pub fn list_candidates( &self, sig_id: &str, ) -> Result<Vec<CandidateInfo>, StoreError>

List every live Candidate op for sig_id — i.e. those not yet referenced by any Promote op (either as the winner or in the supersedes set). Used by lex stage candidates. Results are sorted by op_id for reproducibility.

Source

pub fn promote_candidate( &self, branch: &str, candidate_op_id: &OpId, ) -> Result<OpId, StoreError>

Promote a previously-landed Candidate op as the new branch head for its sig (#294). Emits a Promote op listing every other live Candidate for the same sig in its supersedes field. After this lands, Self::list_candidates returns an empty set for the sig.

Re-typechecks the candidate program (winner stage + the rest of the branch) through apply_operation_checked, so a candidate that doesn’t compose with the current branch state surfaces as StoreError::TypeError.

Source

pub fn apply_operation( &self, branch: &str, op: Operation, transition: StageTransition, ) -> Result<OpId, StoreError>

set_branch_head_op for the durability story on the branch file itself.

Source§

impl Store

Source

pub fn current_branch(&self) -> String

Source

pub fn set_current_branch(&self, name: &str) -> Result<(), StoreError>

Source

pub fn list_branches(&self) -> Result<Vec<String>, StoreError>

Source

pub fn get_branch(&self, name: &str) -> Result<Option<Branch>, StoreError>

Source

pub fn branch_head( &self, name: &str, ) -> Result<BTreeMap<String, String>, StoreError>

Computed view: walk the op log from the branch head and replay each transition into a SigId → StageId map.

Backed by a persisted snapshot (<branch>.head_snapshot.json) keyed on the head it was computed for. Steady state — this call’s head_op matches the last call’s — replays only the ops since the snapshot instead of the whole history: O(ops since the last call) instead of O(total branch history). Falls back to a full walk (and refreshes the snapshot) whenever there’s no snapshot yet, or the snapshot’s op isn’t actually an ancestor of the new head (a branch reset, or history reordered by a merge) — see OpLog::walk_forward_since’s own doc comment.

This existed as a genuine, measured bottleneck before the snapshot: a single call over a tenant with 110k+ accumulated ops took on the order of an hour, dominated by one disk read per ancestor op in the full BFS walk (alpibrusl/lex-lang#813’s follow-up). Every consumer that used to call this once per file in a multi-file publish (fixed separately, also #813) now calls it once per publish request — but “once” was still a full walk over the entire history every time, since nothing persisted the result between calls.

Source

pub fn branch_log(&self, name: &str) -> Result<Vec<MergeRecord>, StoreError>

Source

pub fn create_branch(&self, name: &str, from: &str) -> Result<(), StoreError>

Snapshot the source branch’s head_op into a new named branch.

Source

pub fn create_predicate_branch( &self, name: &str, predicate: Value, ) -> Result<(), StoreError>

Create a predicate-defined branch (#133). The branch’s content is the set of ops matching predicate; head_op stays None and is materialized lazily by callers when they need a single point to apply ops against. Cheap to create and discard — it’s a saved query, not a snapshot.

Source

pub fn delete_branch(&self, name: &str) -> Result<(), StoreError>

Source

pub fn invalidate_gate_checkpoints(&self) -> Result<usize, StoreError>

Invalidate every branch’s last_gate_checkpoint (#256). Run when a new ProducerBlock attestation lands so the next branch advance walks back from genesis once and re-verifies the full chain. Returns the number of branches whose checkpoint changed.

Source§

impl Store

Source

pub fn merge(&self, src: &str, dst: &str) -> Result<MergeReport, StoreError>

Source

pub fn commit_merge( &self, dst: &str, report: &MergeReport, ) -> Result<(), StoreError>

Source§

impl Store

Source

pub fn plan_gc(&self, cli_retain: &[Predicate]) -> Result<GcPlan, StoreError>

Build a GcPlan from the store’s current state plus an optional list of additional retention predicates from the CLI (lex op gc --retain ...). The policy file’s gc_retention.retain entries are appended to those.

Returns StoreError::Io(InvalidData, ...) if a predicate in policy.json fails to parse.

Source

pub fn apply_gc(&self, plan: &GcPlan) -> Result<usize, StoreError>

Apply a GcPlan — actually delete every op in plan.to_delete. Idempotent: running again on the same store after a successful apply yields a plan with an empty deletion set.

Returns the number of op records actually removed (loose files deleted + packed ops dropped during pack rewrites).

Auto Trait Implementations§

§

impl Freeze for Store

§

impl RefUnwindSafe for Store

§

impl Send for Store

§

impl Sync for Store

§

impl Unpin for Store

§

impl UnsafeUnpin for Store

§

impl UnwindSafe for Store

Blanket Implementations§

Source§

impl<T> Allocation for T
where T: RefUnwindSafe + Send + Sync,

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.