pub struct Store { /* private fields */ }Implementations§
Source§impl Store
impl Store
Sourcepub fn open(root: impl AsRef<Path>) -> Result<Self, StoreError>
pub fn open(root: impl AsRef<Path>) -> Result<Self, StoreError>
Open or create a store rooted at root.
pub fn root(&self) -> &Path
Sourcepub fn publish(&self, stage: &Stage) -> Result<String, StoreError>
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.
Sourcepub fn publish_signed(
&self,
stage: &Stage,
signer: Option<&Keypair>,
) -> Result<String, StoreError>
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.
pub fn activate(&self, stage_id: &str) -> Result<(), StoreError>
pub fn deprecate( &self, stage_id: &str, reason: impl Into<String>, ) -> Result<(), StoreError>
pub fn tombstone(&self, stage_id: &str) -> Result<(), StoreError>
Sourcepub fn resolve_sig(&self, sig: &str) -> Result<Option<String>, StoreError>
pub fn resolve_sig(&self, sig: &str) -> Result<Option<String>, StoreError>
The current Active StageId for a signature, or None.
Sourcepub fn sig_history(
&self,
sig: &str,
) -> Result<Vec<StageHistoryEntry>, StoreError>
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”.
pub fn get_ast(&self, stage_id: &str) -> Result<Stage, StoreError>
pub fn get_metadata(&self, stage_id: &str) -> Result<Metadata, StoreError>
pub fn get_status(&self, stage_id: &str) -> Result<StageStatus, StoreError>
pub fn list_stages_by_name(&self, name: &str) -> Result<Vec<String>, StoreError>
pub fn list_sigs(&self) -> Result<Vec<String>, StoreError>
pub fn attach_test(&self, sig: &str, test: &Test) -> Result<String, StoreError>
pub fn list_tests(&self, sig: &str) -> Result<Vec<Test>, StoreError>
pub fn attach_spec(&self, sig: &str, spec: &Spec) -> Result<String, StoreError>
pub fn list_specs(&self, sig: &str) -> Result<Vec<Spec>, StoreError>
pub fn save_trace(&self, tree: &TraceTree) -> Result<String, StoreError>
pub fn load_trace(&self, run_id: &str) -> Result<TraceTree, StoreError>
pub fn list_traces(&self) -> Result<Vec<String>, StoreError>
Sourcepub fn publish_program(
&self,
branch: &str,
stages: &[Stage],
diff: &DiffReport,
new_imports: &ImportMap,
activate: bool,
) -> Result<PublishOutcome, StoreError>
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.
Sourcepub fn publish_program_signed(
&self,
branch: &str,
stages: &[Stage],
diff: &DiffReport,
new_imports: &ImportMap,
activate: bool,
signer: Option<&Keypair>,
) -> Result<PublishOutcome, StoreError>
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.
pub fn derive_imports_from_oplog( &self, branch: &str, ) -> Result<ImportMap, StoreError>
Sourcepub fn apply_operation_checked(
&self,
branch: &str,
op: Operation,
transition: StageTransition,
candidate: &[Stage],
) -> Result<OpId, StoreError>
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 publishre-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
The merge engine in lex-vcs::merge calls
Store::apply_operation directly to land merge ops, and at
merge time the resolved program isn’t a single Vec<Stage>
the way it is on the publish path — it’s a per-sig
resolution map. Forcing a candidate through apply_operation
would either require the merge engine to assemble one (slow,
every active stage off disk) or accept Option<&[Stage]>
and silently skip the gate — the second is exactly the kind
of “secretly opt-out” path #130 is trying to remove. The
honest split is two methods: apply_operation for callers
that already typecheck their inputs (or don’t need to —
rare, but the merge-resolve case), apply_operation_checked
for everyone else.
Sourcepub fn attestation_log(&self) -> Result<AttestationLog, StoreError>
pub fn attestation_log(&self) -> Result<AttestationLog, 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.
Sourcepub fn apply_operation(
&self,
branch: &str,
op: Operation,
transition: StageTransition,
) -> Result<OpId, StoreError>
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
impl Store
pub fn current_branch(&self) -> String
pub fn set_current_branch(&self, name: &str) -> Result<(), StoreError>
pub fn list_branches(&self) -> Result<Vec<String>, StoreError>
pub fn get_branch(&self, name: &str) -> Result<Option<Branch>, StoreError>
Sourcepub fn branch_head(
&self,
name: &str,
) -> Result<BTreeMap<String, String>, StoreError>
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.
PERF: O(N) per call where N is the number of ops on this
branch’s history. Each call: re-opens the op log (a mkdir -p ops/ syscall), BFS-walks the full ancestor set, allocates
a BTreeSet<OpId> + Vec<OperationRecord> + BTreeMap,
reverses, then linearly replays. No memoization. Tier-1 size
(a few hundred ops per branch) makes this acceptable; if
hotter consumers land (e.g. an HTTP-served branch_head),
memoize per-(branch_name, head_op) — the head_op tail of the
cache key is a content-addressed hash, so cache invalidation
is free.
pub fn branch_log(&self, name: &str) -> Result<Vec<MergeRecord>, StoreError>
Sourcepub fn create_branch(&self, name: &str, from: &str) -> Result<(), StoreError>
pub fn create_branch(&self, name: &str, from: &str) -> Result<(), StoreError>
Snapshot the source branch’s head_op into a new named branch.
Sourcepub fn create_predicate_branch(
&self,
name: &str,
predicate: Value,
) -> Result<(), StoreError>
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.