This note records implementation-owned lowering mechanics. Numbered specs define observable semantics.
Public exchange inputs are exactly two canonical side-agnostic
interlace modules. One opaque immutable compiled plan privately owns
those modules in ordered slots 0 and 1.
Operand position is the sole authority for slot, origin, and hygienic
lowering scope; the plan does not store parallel per-operand copies of
those derived values or fixed facet metadata.
The fixed profile derives one symmetric advertisement schema and each local executable from that ordered pair. A compiled-plan value plus a typed local slot is the complete executable provenance for one logical session. Plans remain immutable; hard replacement consumes the complete session and constructs another plan from another complete pair. Lowered names, compiled access strategies and evaluator state are local implementation state and do not enter plan identity.
The merged stored and advertised selections are intersections of the
two modules. On each endpoint, the local module’s
ExposeRecord/1 output is the complete gate for the peer
module’s access to local record facts and an additional gate on sending
selected records. No host approval, route option, extra plan input, or
third policy source participates.
lace-compiler owns Lace-120/130 parsing, local candidate
specialization, grounding proofs, and deterministic local naming. Its
public functions compile one complete source into a facet-bearing
InterlaceModule. The parser and lowerer are private; there
is no public AST or partial-program entry point. Friendly and advanced
definitions share one local symbol table. Frontend provenance exists
only for candidate-specialization and grounding checks.
Compilation depends on lace-sans-io for ordinary parsed
programs, canonical validation, and rendering. The core has no reverse
compiler dependency and retains its one Datalog evaluator and fixed
Lace-040 exchange lowering. Runtime/FFI authoring entry points call the
compiler; precompiled execution calls only the core. Feature defaults
and binding availability are owned by runtime-provider-architecture.md.
Validate every authored rule and dependency before execution pruning, including unused rules and zero-bound aggregate paths. Source bytes arrive complete; compilation owns no IO, resolver, evaluator, executor, or source-update watch. Applications own ordinary file/code loading and retain source files or canonical artifacts when needed.
The core’s prepare_facet_constraints takes an n-ary
authoring collection and an n-ary guest collection of complete canonical
policies. Every input has at least one standard facet, including on the
singleton fast path. It rejects an empty authoring collection and
helper-only inputs; veto-only inputs are valid. Raw canonical program
validation remains separate from this stricter policy precondition.
The composer deduplicates exact canonical inputs, orders them deterministically, isolates every private predicate structurally, intersects each selection/exposure facet across inputs defining it, and unions disabled facets. Absent facets add no local constraint; present-but-empty facets constrain the result to empty. The complete result is validated against the same operand source bounds. Renaming covers positive, negative, counted, and aggregate references; base predicates keep their meaning in authoring inputs.
Guest inputs pass through the same confinement function as peer-slot
lowering, with the intersection of authoring ExposeRecord
facets as the gate: record facts become gated wrappers and
AdmitOrder comparisons and nonzero TopK
rankings gain exposure gates. The composer emits only the wrappers a
guest references, as ordinary canonical rules inside the one
operand.
Field exclusions stay inline negated atoms over the facet variable,
appended to each composed facet rule and to the guest-view rule
(not AdvertisedField in request selection,
not Field elsewhere). Evaluation binds the candidate from
the intersected facet atoms first and then probes that one record, so
work does not grow with excluded or unrelated records. A separate denial
relation (not denied(Record)) would materialize every
matching record, and an inlined
Have(Record), not Field(...) constraint would visit every
record; both forms were measured to scale with store contents.
Exclusions force the wrapper form even for a singleton input.
Runtime porcelain and public composition use this one composer. Foreign bindings pass both complete collections through one call, not a binary fold. Nested composition is legal but can retain extra wrappers, change canonical bytes, and consume more work or rule allowance. No expression tree or source graph is retained to flatten later calls. This is pre-plan local composition, not a third policy input, a new authority boundary, or a generated-source accounting exemption.
DatalogLimits owns generic preparation and finite-input
evaluation bounds. A prepared Lacegram retains that value,
and the session program compiled from it retains it for the logical
session. Parsing, canonicalization, Interlang emission, and evaluation
share rule/arity/TopK checks; generic evaluation also checks complete
finite inputs once before executing rules. The 1024-byte fact value
bound remains fixed.
The public generic evaluator accepts finite
RecordSnapshot, runtime, and explicit tuple inputs. A
record snapshot counts its complete projected facts at construction
without retaining tuples. Runtime facts count as both runtime and base
inputs. Unused inputs count; each built relation counts its distinct
complete facts and each TopK occurrence its retained facts.
Exchange evaluation reads records through store requests and uses 050
logical work instead of a whole-store count.
Compilation bounds one complete source by 130’s
max_source_bytes, defaulting to 1 MiB. Runtime preparation
uses fixed 040 bounds for each complete contribution and the final
composed operand. Per-output authoring rules, advertised
specializations, and local composition wrappers count toward those
bounds. Generated exchange executables are checked without reapplying a
source-rule allowance to hygienic copies, guards, or merges; their
arity, aggregate, derived-fact, and iteration bounds remain fixed.
Output specialization preserves those limits. No generic option enters a
runtime operation, operand identity, or exchange-plan transcript.
Interlang lowering emits separate unary facet rules for each explicit selected output. It retains authored helpers, not a compiler-created output-tuple relation. Output permutations normalize before emission. Each output receives its own candidate analysis; the other outputs remain checked support. By-hash outputs have independent exposure-admission and advertisement guards rather than one combined guard; stored selection names the supported hash without a target read.
Automatic candidate flow stays within local friendly helpers. A marked candidate reaching an opaque advanced helper is rejected; unmarked advanced helpers remain ordinary calls after complete dependency validation. Checked lowering has one predicate per friendly helper; advertised variants are keyed by helper, arity, and candidate positions.
A finite worklist propagates candidate positions by record-variable identity through helper definitions, unions, recursion, negation, cardinality, and Interlang TopK candidate calls. Repeated arguments mark each matching position. Generated names come from a deterministic allocator over local predicate symbols and preserve uniqueness of emitted helpers.
TopK is a binding aggregate node, not a boolean builtin. Safety
binding follows its selected candidate facts, while field-discovery
grounding and checked-support by-hash binding are proven separately
through the candidate helper graph. For an advertised variant, marked
candidate positions select the exact specialized candidate relation; no
marked positions retain checked aggregate support. Lowering preserves
candidate argument order, partition order, ordering form, direction, and
limit. Candidate RecordLink and candidate
AdmitOrder remain unprojectable; admission order over
unmarked checked support remains valid.
Inside an advertised variant, candidate Have and
Field accesses become Advertised and
AdvertisedField. Checked support-record facts and builtins
remain checked. Candidate RecordLink, candidate
AdmitOrder, and traversal into opaque inline-Datalog graphs
are rejected when no faithful advertised projection exists. A checked
support record may bind a candidate through a record-link target.
include Target by hash lowers stored selection to the
checked support body alone, exposure adds Have(Target), and
requests add Advertised(Target). A constant-bound output
substitutes its hash in those rules. Support policy cannot inspect
missing target facts. The sender’s own exposure gate still requires
independent exposure of the target before transfer.
Every generated advertisement dependency is reachable only from
SelectAdvertisedCandidate/1. Checked helper graphs used by
stored selection or exposure contain no advertisement predicates.
The fixed profile’s predicate vocabulary (020 record facts, session authority, advertisement facts and the five facets, with arities and argument kinds) is one core table read by validation, composition, lowering, bindings and the compiler. Generated executable predicates are not profile rows. Lowering emits ordinary lowercase names, for example:
op0_facet_selectstoredrecord
op1_facet_txdisabled
op1_helper_<name>
op1_exposed_field
lace_may_send
lace_may_request
Lowering rescopes every source predicate by operand position, so a
source helper spelled like a generated name cannot collide with it.
Ordinary 030 validation therefore needs no reserved generated names:
capitalized heads are valid only for declared facets. The runtime reads
its two outputs by name from the program it generated. These names are
local state and never enter plan identity. ILTP carries frozen request
hashes in a 060 ::want item, not predicate rows.
For local side S and peer side P:
MaySend = merged stored selection, S.ExposeRecord,
not S.TxDisabled, not P.RxDisabled
MayRequest = merged advertised-candidate selection,
Advertised, missing,
not P.TxDisabled, not S.RxDisabled
Each output is one rule joining both operand facets with its gates
and vetoes. There is no intermediate merged-selection relation: ordinary
copy elimination can therefore deduplicate exposure and selection when
both reduce to Have, without recognizing a policy-specific
pattern.
Absent disabled facets contribute no veto. A statically true disabled
facet may compile the gated output to Empty. Dynamic facets
remain ordinary stratified Datalog. The result is frozen with the output
it gates; no transport direction metadata duplicates it.
The core canonical policy validator checks facet reachability before
plan construction, independently of authoring compilation. An
Advertised/1 or AdvertisedField/4 dependency
is valid only when every path from an exported facet begins at
SelectAdvertisedCandidate/1. Validation rejects a
dependency reachable from SelectStoredRecord/1,
ExposeRecord/1, TxDisabled/0, or
RxDisabled/0.
Reachability follows positive calls, negation, cardinality counted
atoms, and TopK candidate predicates. A helper containing
an advertisement dependency is rejected when shared by request selection
and a forbidden facet. Helpers with no advertisement dependency may be
shared normally.
Only request-phase strata read the advertisement relations, which receive each round’s listing change when the request phase starts. The advertisement phase therefore cannot resolve the predicates to an empty relation; a forbidden dependency is a preparation error.
Local-origin record facts use the full local view. Peer-origin record
facts use scoped wrappers constrained by the local operand’s
ExposeRecord/1 result. For local side0, an explanatory
containment wrapper is:
op1:ContainsRecord(Outer,Contained) :-
ContainsRecord(Outer,Contained), op0:ExposeRecord(Outer).
The lowerer emits a peer-view wrapper for each of Have,
Field, RecordLink, and
ContainsRecord that the peer operand references. Each
wrapper rule is the base record-fact atom followed by the local exposure
atom over its owning record, the first head term. The wrapper has one
rule per distinct constant pattern of its references:
Field(R,'Gate',_,v) needs
op1_exposed_field(Record,'Gate',index,value), so evaluation
projects only matching fields rather than every field of every exposed
record. A reference without constants needs the general rule, which then
replaces the others. Every row a reference can match satisfies its own
pattern, so positive, negated, counted and aggregate references keep
their results. The composer’s guest wrappers stay one general rule per
fact, because composed rules are canonical and count toward the
operand’s rule allowance. The evaluator needs no wrapper recognition. In
any rule, including composed guest wrappers, a record group whose owner
is unbound and named by a unary positive relation atom uses that
relation as its gate. Without a selective bound argument (a field value
or prefix, a link target, or a link name and data) the gate binds the
owners first and the group probes them. With one, the store index
supplies candidates and the gate filters each before candidate work;
once the step has read one record more than the gate holds rows, it
serves the bindings still owed from the gate’s owners instead. Only
visible admitted owners reach projection and logical work accounting,
and a gated step reads at most one record more than its gate holds. This
access path changes no result, so no set of wrapper heads accompanies
the program.
Probed owners resolve through positional identity batches at the
round boundary and the session id map. Bound and exposed
Have use identities without loading bytes; other
projections batch-load held owners by identity. Loaded views are checked
against the expected hash/id association. Indexed stores avoid unrelated
records; scan-oriented stores receive a hash batch rather than one scan
per owner. Stores receive no viewer, exposure, policy, or containment
constraint.
Peer admission-order lowering inspects parsed ordering expressions,
not rendered text. Compare adds exposure gates for its ordering records.
A nonzero TopK uses a generated candidate relation requiring
peer-visible Have for its ordering record before ranking.
The relation preserves the complete original tuple; constants, repeated
variables, anonymous components, and partitions retain their aggregate
meaning. A zero bound keeps the original candidate graph without
introducing eligibility work or an ordering lookup.
Exposing an outer discloses its projected contained hashes but does not admit or independently expose them. This metadata remains local compiled state and never enters source, protocol data, plan identity, or public diagnostics.
After request-only reachability validation, the plan analyzer follows
every reachable AdvertisedField/4 dependency from
SelectAdvertisedCandidate/1 in both complete operands. It
includes helpers, negation, cardinality, and TopK
candidates.
A static name requires that exact field. A dynamic name requires all
fields. Advertised/1 and PartitionBy(...) add
no field requirement. The union becomes one symmetric schema in the
canonical plan and is used in both directions.
The fixed profile’s all-fields form exactly follows the complete 020
Field projection, including structural By and
Mark occurrences when present. No host or local executable
supplies an additional schema. There is no endpoint capability
intersection, wildcard capability, post-plan expansion, or host schema
approval. The schema changes only the exact projection attached to
mutually selected send candidates; it cannot add records to selection or
expose local record facts.
When advertisement evaluation begins, the endpoint freezes one
coherent local StorePosition read boundary for the round.
Each advertisement output freezes:
(StoreRecordId, RecordHash) identities;
andIt has no advertisement fact source. The listing is one structured machine-owned value. Encoding may retain one current byte buffer for partial transport writes, but does not create another semantic listing or cursor-owned tuple copy.
Each endpoint keeps one local listing for its logical session. Once
MaySend is up to date, the listing changes in place by the
round’s held MaySend change: rows that appeared for held
records or whose records became held are added, and rows that
disappeared for records held at the previous boundary are withdrawn.
Only added records load their projections, by hash in batches of 1,024.
The changed listing stays frozen as the round’s commitments until the
next round’s MaySend is ready. A round’s work on the
listing is therefore proportional to its change, apart from the
protocol’s complete first listing.
The listing travels with its change. A direct peer receives both; a
connected adapter encodes the change as the Lace-060
::offers-changed item, or runs Lace-061 in the
session’s first round. A change after an empty listing adds every entry,
so no round builds a separate collection of the whole listing.
After its local listing is accepted and its complete peer listing is received, each endpoint atomically publishes the peer listing and starts request evaluation over the existing round-local read boundary. The endpoint validates the schema of the entries the peer listing’s change adds; the other entries were validated when they were added. That evaluation freezes the peer listing, checked facts, missing results, disabled facets, and one complete request listing. Each endpoint progresses independently; both request outputs freeze before transfer starts. Freezing the request releases the endpoint’s share of the peer listing, so its owner, the direct sibling’s local listing or a connected adapter’s previous peer listing, changes in place in the next round.
A selected advertisement row retains local identity, hash, and
projection. A transfer request loads that identity and validates the
store association. It does not reevaluate MaySend,
exposure, or disabled facets.
Missing or mismatched bytes are integration failure. Policy fact changes affect a later round or another logical session; an active transfer remains bound to its frozen advertisement commitment until that session settles or is retired by hard replacement.
Compilation or constraint composition returns one self-contained module whose canonical text determines its derived hash and wire identity and whose parsed validated program is private local state. Plan construction consumes exactly two such modules and moves them into the plan’s private ordered array. Public fields cannot mutate modules, schema, origin, or executable state after construction. Runtime replacement preparation produces the same canonical module type before staging; canonical module identity is the equality check, with no duplicate policy-generation field.
The canonical plan transcript contains only:
ExchangePlanProfile('lace-040-exchange-plan-v13')
ExchangePlanOperand('0','<module-0-id>')
ExchangePlanOperand('1','<module-1-id>')
ExchangePlanRequireAdvertisedField('<name>')
ExchangePlanRequireAllAdvertisedFields()
The schema uses zero or more unique exact-field rows or one all-fields row, never both. The profile row is the domain separator. The transcript does not repeat derived origins, fixed facets, runtime-fact declarations, lowering metadata, or output declarations. Its hash is a derived local accessor, not a separately stored provenance field or wire confirmation.
Peer and public raw module text is always parsed and validated. The
compiled plan and transcript remain local derived artifacts. After its
nonce preface pair, a connected logical session settles role-ordered v3
proofs (required for a bound context, optional for unbound IO) and then
one canonical operand per role, announced by Blob hash and delivered
only when missing, freezes Here and
PeerConfidential when present, and constructs the plan.
Proof authority changes runtime facts only; it never enters plan
identity or adds a policy input. A direct pair assigns both modules and
explicit final authorities by array position before round work.
Each endpoint compiles its side-local executable once per logical session or direct pair into one session program holding both outputs, so helpers that both read are derived and stored once. Compilation:
lace_may_send and
lace_may_request through every positive, negated,
cardinality, and aggregate dependency; an output’s graph does not
include unused generated record-fact wrappers;TopK over record facts, or ranked
by AdmitOrder, a generated candidate relation of complete
facts, which joins the ordering record’s Have when ranked
by AdmitOrder;Advertised, AdvertisedField, or a relation of
such a stratum belongs to the request phase, every other stratum to the
advertisement phase;TopK reads.A phase is a set of strata, not a prefix: request strata may precede advertisement strata in stratum order. Each phase runs its strata in order, which is sound because no advertisement stratum reads a request stratum. Static validation covers the complete source; compilation never prunes a candidate relation because an aggregate limit is zero or a runtime binding is absent.
A retained predicate whose only rule is a copy,
p(X1, ..., Xn) :- q(X1, ..., Xn). with distinct variables,
is replaced by q in every positive, negated and cardinality
atom; repeated body atoms and rules are dropped. The output and
TopK candidate relations stay, since a TopK
with k = 0 still derives its candidate. Every remaining
relation keeps its extension, so outputs are unchanged, while composed
and generated wrappers no longer derive one relation under several
names.
A rule body becomes elements: relation atoms (derived,
TopK retained, advertisement, and session-authority
relations), record groups, negations, cardinality tests, and builtins. A
record group collects the positive record-fact atoms sharing one owning
record term. Compare over AdmitOrder adds a
Have group for each operand and compares their session
identities; a held constant anchor also narrows the owner group’s store
read range. A negated or counted record atom over the owner of a
positive group in the same rule is a filter inside that group: records
are immutable, so its truth changes only when the owner becomes
visible.
Every rule has one plan without a changed element and one plan per element that can change. Plans order elements by fixed rules, with no runtime cost model:
Negations, builtins, and cardinality tests run as soon as their variables are bound. A record group gated by a unary relation counts as indexed only through a selective argument, as described under exposure above.
A record group whose owner the plan without a changed element binds before reaching it is a probe group; every other group is a scan group. A probe whose owner a constant or a relation row binds watches the owners it finds missing, and its change is the watched owners that became held. Every other group’s change is the records admitted since the previous round.
A plan runs as a pipeline of blocks of at most 1,024 bindings and 16 KiB of values, one block per step, so intermediate memory is bounded by the plan’s length. Every step keeps its cursor, so evaluation suspends at a record request and resumes exactly there.
The strict matcher is internal to the core. Complete static validation checks all authored patterns, including public parsed programs and unused or zero-bound aggregate dependencies, before pruning. Executable construction compiles each retained syntactic occurrence once into its ordinary immutable compiled owner. Join-plan variants share that occurrence rather than compiling per plan or binding. Composition, relation renaming, advertised specialization, exposure lowering, and retention preserve the builtin structurally; none adds a fact source or broadens an exposure view.
TextMatch tracks its input term as a planner dependency
and runs only when bound. It remains a residual filter, with no index
extraction, store operation, or rewrite of TextShape. The
existing indexed TextShape prefix path remains unchanged.
Generic, exchange, and retention execution apply the 050 charge; the
test-only reference engine preserves the same boolean semantics.
Preparation merges literal runs into a shared decoded pool, keeps atom counts unexpanded, compiles ASCII classes into two 64-bit bitmap words, and computes per-branch byte bounds. The optional two literal alternatives share surrounding pieces; there are no duplicate programs or factoring inside alternatives. Outer literal prefix and suffix runs use byte comparisons.
For each of at most two branches, matching checks byte bounds and consumes fixed pieces before the variable repeat forwards and fixed pieces after it backwards. That branch’s actual alternative width determines its consumption. All remaining text then belongs to the one variable atom and is checked against its scalar bounds. There is no split search. Without a variable repeat, a terminal exact repeat can check the remainder; otherwise fixed pieces consume forwards and require exhaustion.
ASCII literals and positive classes use byte paths. Unicode literal repeats check width and scalar equality. Dot counts scalars; negated ASCII classes check forbidden ASCII bytes while counting scalar starts. Fixed dot and negated-class atoms consume scalars from the appropriate end. Slicing stays checked and Unicode-correct.
Matching uses O(pattern pieces + text bytes) work across at most two branches, O(1) auxiliary stack, and no match-time allocation. Preparation storage is source-bounded and repeats never expand. There is no backtracking, recursion, NFA, DP, fallback matcher, global cache, registry, persisted matcher format, or runtime callback. Compiled metadata retains decoded source byte length and syntactic branch count even when optimizations remove pieces, so fast paths cannot alter the protocol charge. The existing budget is charged once before matching, including its base builtin unit.
The session evaluator lives on the endpoint for its logical session or direct pair; replacement and retirement drop it with the endpoint. It owns:
u32, with
text in a paged arena and one reference per stored occurrence; values
without references are reclaimed between rounds;TopK retained, advertisement,
and session-authority predicate: arity × u32 rows in 16 KiB
pages, signed derivation counts beside them, a deduplication table, and
the compiled indexes; dead rows stay tombstones until tombstones
outnumber live rows; andStoreRecordId
or missing.Record facts are never materialized: records loaded for a block are
dropped after it. Tables are sharded so no allocation exceeds 64 KiB.
Fixed hashing and insertion-ordered pages keep store-call sequences and
work reproducible. A suspended evaluation keeps its continuation in the
evaluator; the machine’s StoreCall carries only the pending
request.
Each round has one record boundary B_r, the
advertisement phase’s CurrentStorePosition. Every record
request in the round reads through B_r; the evaluator never
runs ahead of it, including when other operations commit between
Advertising and Requesting.
The advertisement phase first settles the store’s growth since
B_{r-1}: it resolves the watched values again at
B_r, or reads the records admitted in
(B_{r-1}, B_r] when they are fewer than the watched values.
It then brings the advertisement strata up to date and reads
MaySend. The request phase applies the peer listing’s
change since its previous complete listing: all entries after 061
reconciliation, the parsed 060 delta on later connected rounds, and the
sender’s listing change beside a direct pair’s complete listing. It then
brings the request strata up to date and reads MayRequest.
Every round runs both phases, also when an output has no rules.
Relations keep the round’s old view (its start) and new view, so
advertisement-phase changes remain visible to the request phase.
A round’s outputs equal a full evaluation at B_r with
that round’s complete peer listing. The first round is the same update
from the empty state, running every rule’s plan without a changed
element once. There is no demand (magic-set) rewrite: a first round
computes and sends a complete listing, so it is a full pass over the
selection anyway, and the sessions that pay it repeatedly are many short
ones against large stores.
A non-recursive stratum maintains derivation counts. Each plan reads
every element in the round’s old or new view so that the terms of one
change telescope; a fact lives while its count is positive, and counts
settle non-negative when the stratum ends. This covers
not Have of newly held records, listing withdrawals,
negation over advertisements, and reach exclusions over newly admitted
records.
A recursive stratum maintains sets: insertions continue semi-naively from the changed inputs. A change that can delete one of its facts, such as a lower relation losing rows, a negated relation gaining rows, a negated record or counted input changing, or a listing withdrawal, recomputes the stratum from its lower strata. Recursion over store facts alone only grows and stays incremental.
A TopK occurrence ranks again every partition its
candidate’s changes touch, from the candidate’s partition index with a
bounded heap, and updates its retained relation. An out-of-domain
Int value is charged as a candidate and excluded; a missing
AdmitOrder excludes its candidate; k = 0
retains nothing while its candidate is still derived. A
Cardinality test recounts each outer binding a change
touches, in the old and the new view, from the counted relation’s index
or a record query; no outcome relation is stored.
Final send projection reuses identities in the session id map,
resolves the selected rows it does not know in positional batches of
1,024 at B_r, and removes missing targets. It loads no
target bytes and never borrows an outer record’s identity for a
contained or linked target. MayRequest retains missing
targets.
Logical work counts each candidate record and each fact visited with
its projected bytes, each relation row read by a join, probe or
aggregate, each derived fact produced, each builtin and filter test, and
each hash in an identity batch. Each output has a budget of
max_evaluation_work per round; the first round charges the
complete evaluation, later rounds their update. The advertisement
phase’s work, including store growth settlement and strata that only
MayRequest reads, is charged to MaySend’s
budget, also when MaySend has no rules. Executed work
survives a yield, and resumption charges nothing again. Host store
requests, pages, allocations, and scheduling are not work.
Each built relation, including generated candidate and
TopK retained relations, holds at most 2^18 facts when its
stratum settles; a counting stratum fails early once a relation exceeds
twice that. Each stratum iterates at most 1000 times per update. A limit
or store failure fails the session, and its evaluator is dropped rather
than reused half-updated.
Generic finite evaluation compiles every rule, without pruning or
copy elimination, and runs one round over a RecordSnapshot,
explicit tuples, and runtime facts; any derived relation can be read
afterwards. The retention sweep runs each window’s lowered program, with
one stored-selection output, as one round at its boundary. Its windowing
and helper clones (redirect_calls) are a retention
mechanism owned by store-compaction.md, not
an evaluator feature.
Compiled policy does not classify open commits. At fixed point the
machine reports the terminal round’s shared local read-through; any
later Lace StorePosition advance wakes the endpoint for an
ordinary round, whose evaluation updates the session state with what
changed. Complex and simple policy use the same broad mechanism. See open-store-wakes.md.