Live Lane Awareness (Epic #15100, graduated from Discussion #15090) is producer-complete and consumer-empty. All four truth surfaces are built or in review — the typed route (#15087, merged), the two historical Bird Views (#14435, #15088, shipped), the current-state landscape (#15234, in review), and the lifecycle frontier (#15267, PR #15295) — and not one of them reaches a hook. ADR 0035 §2.6 specifies the writer that would carry them. It has no ticket.
This is the missing critical-path leaf of its own epic. Epic #15100 names it directly: "The only stateful adapter is an expiring projection writer with enforced single-writer lease semantics and atomic old-complete/new-complete transport", and OQ5/OQ6 record enforced single-writer atomic transport as a resolved open question.
Filed rather than assumed: a producer whose output nothing consumes is unwired substrate that decays silently — the surrounding release pain (peers hand-surveying lifecycle obligations every hook fire) is not relieved by a producer, only by a projection a hook can read.
The Problem
Hook projections today render fixture or policy-only rows (#15265), and the canonical route stays trapped behind rendered Markdown. Wave 1's whole thesis is that hooks become pure bounded renderers of a published projection. Without the writer there is nothing to render, so every consumer-side leaf (§2.11 Ph3–4 reader cutover) stays blocked behind this one.
The hard part is not writing a file — it is that multiple MCP/service processes can race one target, and a stale holder must not be able to mutate the resource after takeover. An atomic rename alone was explicitly rejected by the epic: it prevents torn reads but not multi-writer lost updates.
The Architectural Reality
ADR 0035 §2.6 decides ownership and the storage contract; this ticket implements a decided contract and does not reopen one. The exact class/file still runs structural pre-flight.
Owner: the Memory Core operational service boundary (§2.6). Every local resident already authenticates to it; its SQLite/WAL store is process-shared so multiple processes can arbitrate one target; it has an operational SummarizationJobs lease-table precedent; and keeping the writer next to the broker lets the resource validate the fencing token immediately before rename. A remote lease plus an unguarded local write would not actually fence stale writers.
Two operational tables in the shared SQLite store — explicitly not Native Edge Graph nodes, creating no ontology: HookProjectionChannels(target_id, channel, source_watermark, envelope_json, captured_at, expires_at, updated_at, PK(target_id, channel)) and HookProjectionLeases(target_id PK, fencing_epoch, holder_token_hash, holder_instance_digest, acquired_at, expires_at, state).
target_id is server-derived from the attested tuple {schemaVersion, capability, agentId, harnessType, instanceKeyDigest, workspaceKeyDigest, projectionKind}; session id and raw paths excluded. Output path is derived under the Memory-Core-owned runtime root (<runtime-root>/hook-projections/<target_id>/current.json). A producer can submit an envelope for an admitted target/channel; it cannot choose a filesystem path.
Producer submission (§2.6.1): each producer submits only its own typed channel + watermark + expiry; a transaction validates schema, target admission, producer identity, and monotonic watermark. Regressed watermark rejected; equal-watermark identical replay idempotent; equal-watermark different payload is a source conflict and degrades that channel. Producers never read/write current.json and never merge another producer's channel.
Resource-side fencing (§2.6.2) — the load-bearing part: immediately before touching current.json the winner opens a serialized SQLite write transaction, revalidates {target_id, token hash, fencing_epoch, not-expired} against the broker's clock, reads the latest committed channel rows, and holds that same transaction while creating a unique temp sibling, flushing, and atomically renaming. No takeover can commit between token revalidation and resource mutation. An epoch in JSON is diagnostic; the transaction around the rename is what enforces the property.
Wave 1 lease shape: a bounded single-publication lease with no renewal. If one bounded render/write cannot finish before expiry it aborts — adding renewal requires ADR revalidation.
leaseTtlMs is required from the Memory-Core configuration boundary; the primitive has no hidden default (ADR-0019 discipline).
Fail-closed: missing SQLite, busy timeout, unreadable schema, or lease-service failure ⇒ no projection update. Normal contention returns held/retry metadata and performs no write.
Release is conditional on token hash AND epoch — a stale holder can neither release nor overwrite a successor; success and failure paths both use token+epoch-checked release.
Producer input contract already landed: lifecycle-frontier.v1 (#15267) and computed-route.v1 (#15087) are typed, frozen, notAuthority envelopes carrying their own watermark/expiry.
Structure map: Memory Core operational service helpers; SummarizationJobs is the cited lease precedent.
Consumed as submitted channels; never merged or re-derived by a producer
A channel absent/expired is honestly absent, never synthesized
Producer JSDoc
per-channel independence spec
Decision Record impact
aligned-with ADR 0035 §2.6 (writer ownership + storage contract) and §2.6.1–2.6.3 (submission, fencing, crash/stale behavior). No amendment — this leaf implements the decided contract. Any move to a renewable lease is an ADR revalidation, not an implementation choice.
Decision Record
Required authority: ADR 0035 (graduated from Discussion #15090, Cycle-7), §2.6.
Acceptance Criteria
HookProjectionChannels + HookProjectionLeases exist as operational tables in the shared Memory Core SQLite store — not Native Edge Graph nodes, creating no ontology.
Lease acquisition runs in a serialized write transaction; succeeds only when the target row is unheld or expired; increments fencing_epoch monotonically; returns a raw token while storing only its hash.
The fencing property, pinned by a concurrency test, not by inspection: a stale holder cannot mutate current.json after takeover. Token+epoch revalidation and the rename occur inside the SAME serialized transaction; no takeover commits between them.
Release/clear is conditional on token hash AND epoch — a stale holder can neither release nor overwrite a successor; success and failure paths both use token+epoch-checked release.
Producer submission validates schema, target admission, producer identity, and monotonic watermark: regressed watermark rejected; equal-watermark identical replay idempotent; equal-watermark different payload degrades that channel as a source conflict.
target_id is server-derived from the attested tuple; session id and raw paths excluded. A producer cannot supply a filesystem path — pinned by spec.
leaseTtlMs is required from the Memory-Core config boundary and fails loud when absent — no hidden default, not even a literal.
Wave-1 lease is bounded single-publication with no renewal: a render/write that cannot finish before expiry aborts rather than extending.
Fail-closed on missing SQLite, busy timeout, unreadable schema, or lease-service failure: no projection update, never a partial write.
Reader-observable atomicity: a concurrent reader sees old-complete or new-complete, never a torn payload.
Cross-family review challenges the fencing proof and the crash/takeover path specifically.
Out of Scope
Hook rendering / reader cutover (§2.11 Ph3–4) — hooks are pure bounded renderers of the published projection; this ticket publishes, it does not render.
Lease renewal / heartbeat. Wave 1 is bounded single-publication by ADR decision; renewal requires ADR revalidation, so proposing it here is out of scope by construction.
New producers.#15267 and #15087 are the Wave-1 inputs; this consumes them.
Ranking, scoring, or curation. The composition layer may concatenate produced envelopes preserving provenance/freshness/emptiness/degradation/citations; it may not read content to rank, weight, infer, or curate per turn.
Quarantine/migration of the legacy unprovenanced lifecycle-state file — a separate §2.11 concern.
Avoided Traps
Atomic rename without singleton enforcement — explicitly rejected by Epic #15100: it prevents torn reads but not multi-writer lost updates. The rename is necessary and not sufficient; the transaction around it is the actual property.
Fencing epoch in the JSON payload as the guard. It is diagnostic only. A reader-visible epoch cannot stop an old holder from mutating the resource — only the serialized transaction wrapping the rename can.
A remote lease + an unguarded local write. Looks correct, fences nothing. This is precisely why §2.6 puts the writer next to the broker.
A default leaseTtlMs. A primitive-local default (even a literal) silently converts a config gap into a plausible-but-wrong lease window (ADR-0019; and the live snapshot-drift trap in #15294 makes an absent leaf a realistic state, not a hypothetical).
Renewal "because a render might be slow." Wave 1 chose bounded-no-renewal deliberately; a slow render must abort loudly rather than quietly hold a target.
Related
Parent epic: #15100 (Live Lane Awareness — Wave-1 composition). This is its missing critical-path leaf.
Blocked consumers: §2.11 Ph3–4 reader cutover; #15265 (hook advisory serves fixture rows) is the live symptom of having no published projection.
Authority: ADR 0035 §2.6 (learn/agentos/decisions/0035-live-lane-awareness-composition.md), graduated from Discussion #15090.
Config-boundary trap to avoid at wiring time: #15294.
Live latest-open sweep: checked latest 20 open issues at 2026-07-16T18:32:51Z; no equivalent found (searched additionally for projection writer lease, single-writer, fenced lease projection, ADR 0035 writer across open+closed — only the epic and its producers match). A2A in-flight [lane-claim] sweep at 2026-07-16T18:32Z across the last 12 claims: no overlapping claim.
Context
Live Lane Awareness (Epic #15100, graduated from Discussion #15090) is producer-complete and consumer-empty. All four truth surfaces are built or in review — the typed route (#15087, merged), the two historical Bird Views (#14435, #15088, shipped), the current-state landscape (#15234, in review), and the lifecycle frontier (#15267, PR #15295) — and not one of them reaches a hook. ADR 0035 §2.6 specifies the writer that would carry them. It has no ticket.
This is the missing critical-path leaf of its own epic. Epic #15100 names it directly: "The only stateful adapter is an expiring projection writer with enforced single-writer lease semantics and atomic old-complete/new-complete transport", and OQ5/OQ6 record enforced single-writer atomic transport as a resolved open question.
Filed rather than assumed: a producer whose output nothing consumes is unwired substrate that decays silently — the surrounding release pain (peers hand-surveying lifecycle obligations every hook fire) is not relieved by a producer, only by a projection a hook can read.
The Problem
Hook projections today render fixture or policy-only rows (
#15265), and the canonical route stays trapped behind rendered Markdown. Wave 1's whole thesis is that hooks become pure bounded renderers of a published projection. Without the writer there is nothing to render, so every consumer-side leaf (§2.11 Ph3–4 reader cutover) stays blocked behind this one.The hard part is not writing a file — it is that multiple MCP/service processes can race one target, and a stale holder must not be able to mutate the resource after takeover. An atomic rename alone was explicitly rejected by the epic: it prevents torn reads but not multi-writer lost updates.
The Architectural Reality
ADR 0035 §2.6 decides ownership and the storage contract; this ticket implements a decided contract and does not reopen one. The exact class/file still runs structural pre-flight.
SummarizationJobslease-table precedent; and keeping the writer next to the broker lets the resource validate the fencing token immediately before rename. A remote lease plus an unguarded local write would not actually fence stale writers.HookProjectionChannels(target_id, channel, source_watermark, envelope_json, captured_at, expires_at, updated_at, PK(target_id, channel))andHookProjectionLeases(target_id PK, fencing_epoch, holder_token_hash, holder_instance_digest, acquired_at, expires_at, state).target_idis server-derived from the attested tuple{schemaVersion, capability, agentId, harnessType, instanceKeyDigest, workspaceKeyDigest, projectionKind}; session id and raw paths excluded. Output path is derived under the Memory-Core-owned runtime root (<runtime-root>/hook-projections/<target_id>/current.json). A producer can submit an envelope for an admitted target/channel; it cannot choose a filesystem path.current.jsonand never merge another producer's channel.current.jsonthe winner opens a serialized SQLite write transaction, revalidates{target_id, token hash, fencing_epoch, not-expired}against the broker's clock, reads the latest committed channel rows, and holds that same transaction while creating a unique temp sibling, flushing, and atomically renaming. No takeover can commit between token revalidation and resource mutation. An epoch in JSON is diagnostic; the transaction around the rename is what enforces the property.leaseTtlMsis required from the Memory-Core configuration boundary; the primitive has no hidden default (ADR-0019 discipline).held/retry metadata and performs no write.lifecycle-frontier.v1(#15267) andcomputed-route.v1(#15087) are typed, frozen,notAuthorityenvelopes carrying their own watermark/expiry.SummarizationJobsis the cited lease precedent.Contract Ledger Matrix
HookProjectionChannelstable(target_id, channel); monotonic watermark enforced in-transactionHookProjectionLeasestablefencing_epoch; only the token HASH storedheld+ retry metadata, no writecurrent.jsonleaseTtlMsconfig leafconfig.template.mjsJSDoclifecycle-frontier.v1/computed-route.v1producersDecision Record impact
aligned-with ADR 0035 §2.6(writer ownership + storage contract) and§2.6.1–2.6.3(submission, fencing, crash/stale behavior). No amendment — this leaf implements the decided contract. Any move to a renewable lease is an ADR revalidation, not an implementation choice.Decision Record
Required authority: ADR 0035 (graduated from Discussion #15090, Cycle-7), §2.6.
Acceptance Criteria
HookProjectionChannels+HookProjectionLeasesexist as operational tables in the shared Memory Core SQLite store — not Native Edge Graph nodes, creating no ontology.fencing_epochmonotonically; returns a raw token while storing only its hash.current.jsonafter takeover. Token+epoch revalidation and the rename occur inside the SAME serialized transaction; no takeover commits between them.target_idis server-derived from the attested tuple; session id and raw paths excluded. A producer cannot supply a filesystem path — pinned by spec.leaseTtlMsis required from the Memory-Core config boundary and fails loud when absent — no hidden default, not even a literal.Out of Scope
Avoided Traps
leaseTtlMs. A primitive-local default (even a literal) silently converts a config gap into a plausible-but-wrong lease window (ADR-0019; and the live snapshot-drift trap in #15294 makes an absent leaf a realistic state, not a hypothetical).Related
learn/agentos/decisions/0035-live-lane-awareness-composition.md), graduated from Discussion #15090.Live latest-open sweep: checked latest 20 open issues at
2026-07-16T18:32:51Z; no equivalent found (searched additionally forprojection writer lease,single-writer,fenced lease projection,ADR 0035 writeracross open+closed — only the epic and its producers match). A2A in-flight[lane-claim]sweep at2026-07-16T18:32Zacross the last 12 claims: no overlapping claim.Retrieval Hint:
hook projection writer fencing epoch lease serialized transaction rename current.jsonOrigin Session ID:
ad475320-6bdc-4555-ba3f-b78d51de0b17