Contents

Interlace Policy Lowering

This note records implementation-owned lowering mechanics. Numbered specs define observable semantics.

Authority boundary

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.

Self-contained compilation and policy composition

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.

Resource-limit ownership

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.

Candidate-specialized Interlang helpers

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.

Scoped facets and generated outputs

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.

Request-only advertisement validation

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.

Exposure and origin-aware resolution

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.

Plan-derived advertisement schema

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:

It 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.

Frozen transfer commitments

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.

Plan ownership and canonical transcript

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.

Compiled executable

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:

  1. keeps the rules reachable from 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;
  2. eliminates copy rules;
  3. gives every nonzero 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;
  4. stratifies the result with the ordinary dependency analysis;
  5. splits the strata into two phases: a stratum reading Advertised, AdvertisedField, or a relation of such a stratum belongs to the request phase, every other stratum to the advertisement phase;
  6. compiles every rule’s join plans; and
  7. chooses each relation’s indexes, one per bound-argument mask a plan or 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.

Rule elements and join plans

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:

  1. the changed element;
  2. fully bound elements, as membership probes;
  3. advertisement and session-authority atoms;
  4. record groups with a bound owner or an indexed bound argument;
  5. derived atoms with a bound argument, through their index;
  6. the rest in body order, unindexed record groups last.

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.

Compiled TextMatch residuals

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.

Session state

The session evaluator lives on the endpoint for its logical session or direct pair; replacement and retirement drop it with the endpoint. It owns:

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.

Rounds

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.

Maintenance

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.

Work and limits

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 and retention evaluation

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.

Open wake relation

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.