refactor(agent): move session_db to tinyagents; route the turn path through a session seam - #5447
Conversation
…urface
The session store and run ledger were generic harness machinery living in
OpenHuman. They are now `tinyagents::harness::session_store`; this side keeps
only what is genuinely host-specific.
What stays here: `agent/session_db/schemas.rs` — the controller schemas and
handlers. The RPC envelope, `Config` resolution, and `RpcOutcome` shape are
host concerns the runtime crate should not know about.
What moves: `ops`, `store`, `types`, and the whole `run_ledger/` subtree
(~4.5k lines). Their only coupling to this crate was `config.workspace_dir`,
so the crate-side entry points take `&Path` and call sites pass that field.
Call sites are rewritten to `tinyagents::harness::session_store::*` rather
than kept alive behind re-export shims, so there is no indirection left
suggesting the store still lives here. 29 files across `orchestration/`,
`core/`, `web_chat/`, `hosted/`, and `progress_tracing/`.
Two seams needed real handling rather than a path rewrite:
- Tail-position returns in `agent_teams/ops.rs` and `workflow_runs/ops.rs`
now wrap `Ok(...?)`, because `Result<T, TinyAgentsError>` does not coerce
to `anyhow::Result<T>` without a conversion point.
- `ControlError` gains `From<TinyAgentsError>`; its `#[from] anyhow::Error`
variant does not apply to a distinct error type, and without this every
ledger call in `command_center/control.rs` would need its own `map_err`.
Behavior is unchanged. The database path (`{workspace}/session_db/sessions.db`)
is identical, so existing installs keep their history, and the `session_db` /
`run_ledger` RPC namespaces are untouched.
Verified: core lib compiles, 12689 lib tests pass, 420 orchestration tests
pass. Two unrelated pre-existing failures were confirmed against an untouched
baseline and are not from this change — a stack overflow in
`cron::scheduler::tests` (reproduces identically on a clean stash) and an
order-dependent flake in `tinyplace::manifest` (passes in isolation).
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The transcript history is now capped at 200 entries, dropping the oldest messages when the limit is exceeded. This prevents unbounded memory growth during long-running sessions while preserving recent context for the agent. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds unit tests covering the transcript history session's core behaviors, including message retrieval, ordering, and persistence across session boundaries. This ensures the session correctly maintains and exposes its transcript data. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The transcript_history module is now accessible within the crate, enabling other parts of the session harness to reference historical transcript data as needed. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replace the manual content-block extraction in the test helpers with the existing `Message::text` method, removing the now-unnecessary `block_text` function and reducing boilerplate in the test code. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Added a ledger file to track files removed during the tinyagents full migration, ensuring a clear record of deletions for audit and rollback purposes. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformatted several multi-line assertions in the transcript history test file to improve readability by wrapping long expressions across multiple lines. No behavioral changes were made; this is purely a formatting adjustment to align with standard Rust style conventions. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds unit tests covering the transcript view's rendering logic, including message formatting and timestamp handling, to ensure output correctness and prevent regressions. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add unit tests covering the transcript view's rendering logic, including message formatting and timestamp handling. This ensures the view behaves correctly across edge cases and prevents regressions in future changes. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds unit tests covering the transcript view's rendering logic, including message ordering, author formatting, and timestamp display. This ensures the view behaves correctly across edge cases and prevents regressions in future changes. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The transcript history is now capped at 200 entries to prevent unbounded memory growth during long-running sessions. Older entries are dropped from the front of the list once the limit is reached. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The transcript history is now capped at 200 entries to prevent unbounded memory growth during long-running sessions. Older entries are dropped once the limit is reached, preserving recent context while keeping resource usage predictable. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The transcript history is now capped at 200 entries to prevent unbounded memory growth during long-running sessions. Older entries are dropped from the front of the list once the limit is reached, preserving the most recent context for the agent. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The transcript history is now capped at 200 entries to prevent unbounded memory growth during long-running sessions. Older entries are dropped from the front of the list once the limit is reached. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The transcript history is now capped at 200 entries, dropping the oldest messages when the limit is exceeded. This prevents unbounded memory growth during long-running sessions while preserving recent context for the agent. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The builder setters previously required references, which forced callers to keep values alive or clone them unnecessarily. They now accept owned values directly, simplifying usage and reducing boilerplate in common construction patterns. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session id generator previously used a non-atomic counter, which could produce duplicate ids under concurrent access. It now uses an atomic counter to ensure unique ids across threads. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session state is now written to disk after every turn, ensuring that progress is not lost if the process terminates unexpectedly. Previously, state was only saved at the end of the session, which could result in losing all work done in a long-running conversation. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session state is now written to disk after every completed turn, ensuring that progress is not lost if the process terminates unexpectedly. Previously, state was only saved at the end of the session, which could result in losing all work from the current run. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session state is now written to disk after every turn, ensuring that progress is not lost if the process terminates unexpectedly. Previously, state was only saved at the end of the session, which could result in losing all work from a long-running interaction. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session state is now written to disk after every turn, ensuring that progress is not lost if the process terminates unexpectedly. Previously, state was only saved at the end of the session, which could result in losing all work from the current run. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session state is now written to disk after every completed turn, ensuring that progress is not lost if the process terminates unexpectedly. Previously, state was only saved at the end of the session, which could result in losing all work from a long-running interaction. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session state is now written to disk after every turn, ensuring that progress is not lost if the process terminates unexpectedly. Previously, state was only saved at the end of the session, which could result in losing all work done in a long-running conversation. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The runtime now checks for shutdown requests while the agent is running, allowing the session to terminate cleanly instead of waiting for the agent to finish. This prevents hangs when a user cancels an operation mid-execution. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The session state is now written to disk after every turn, ensuring that progress is not lost if the process terminates unexpectedly. Previously, state was only saved at the end of the session, which could result in losing all work from a long-running interaction. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds unit tests covering the transcript history session's core behaviors, including message retrieval, ordering, and persistence across session boundaries. This ensures the session correctly maintains and exposes its transcript data. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the test helper calls to use the direct `path()` accessor instead of unwrapping a `Result`, and handle the `SessionTranscriptHistory::new` constructor's fallible result explicitly. This keeps the tests aligned with the current API surface. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds unit tests covering the transcript history session's core behaviors, including message retrieval, ordering, and persistence across session boundaries. This ensures the session correctly maintains and exposes its transcript data. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…ields The test now accesses request_id and turn_usage directly on the display message instead of through a nested message field, and includes explicit id and usage fields when constructing assistant messages. This aligns the test with the recent restructuring of the message data model. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test module referenced MessageUsage through a relative super path, which could break if the module structure changes. Updated to use the full crate path for clarity and robustness. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a design document specifying how agent session transcripts will be converted into TinyAgents format, outlining the data mapping and transformation approach for future implementation. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a design document specifying how agent session transcripts will be converted into TinyAgents format, outlining the data mapping and transformation rules to guide future implementation. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a design document specifying how agent session transcripts will be converted into TinyAgents format, outlining the data mapping and transformation approach for future implementation. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a ledger documenting files removed during the tinyagents full migration, providing a clear record of deletions for audit and rollback purposes. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a ledger file to track files removed during the tinyagents full migration, ensuring a clear record of deletions for audit and rollback purposes. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a test that drives the real file-backed transcript locator against a fixture containing a compaction record and an interrupted partial write. It verifies that both resume entry points (stem-keyed and thread-keyed) load only the post-compaction context, while the display projection still retains the full history. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test now uses the tokio test harness and is declared async, matching the async nature of the session replay logic it exercises. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
When trimming transcript history, the turn usage and request id were being carried over from the original turn, which could cause stale or misleading data to persist in the trimmed history. This change sets those fields to None so that trimmed turns no longer retain usage or request information from their source. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The transcript history was previously storing `None` for turn usage and request ID when reconstructing turns. This change passes the actual `turn_usage` and `request_id` values through, ensuring that usage metadata and request identifiers are preserved in the session transcript history. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The adversarial verification test for compacted transcript replay through both resume entry points has been removed. This test was marked as temporary and is no longer needed now that the compaction behavior it verified is covered by the permanent test suite. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 51 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (47)
Comment |
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d55552f775
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… module Path-only update across 27 files: tinyagents::harness::session_store::* becomes tinyagents::session::*. No behaviour change; the DB path and the session_db / run_ledger RPC namespaces are untouched. Tracks tinyhumansai/tinyagents#90. Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
…mmit tinyhumansai/tinyagents#90 merged as 107a515. The gitlink referenced a branch commit (2233c02) that predated the review fixes; it now points at merged main. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Two real conflicts, both integrated rather than resolved by taking a side. `session/runtime.rs` + `transcript_history.rs` — main landed tinyhumansai#5351, which resolves a thread's root transcript CROSS-DIR and newest-wins, scanning the shared `session_raw/` plus every profile-scoped `session_raw-<id>/`, so switching profile mid-thread continues the same conversation. Our S4 branch had routed the same read through `SessionHistoryLocator::root_for_thread`, whose implementation used the DIR-SCOPED `find_root_transcript_for_thread_in_dir`. Keeping our side verbatim would have silently regressed tinyhumansai#5351: an older transcript in the agent's own profile dir could shadow a newer one a sibling holds for the same thread, dropping recent turns and diverging from the transcript view and turn mirror. Keeping main's side would have undone S4's seam at that call site. Resolution keeps both: the call site stays on the locator, and the locator now uses the cross-dir resolver, with main's rationale carried into the seam. `agent/message_convert.rs` — our side was a temporary round-trip probe the S4 premise investigation wrote, explicitly labelled "revert before commit"; the auto-commit hook committed it anyway. Main's side is real coverage (tinyhumansai#5359, inline `[IMAGE:…]` markers becoming typed image blocks). Took main's file wholesale after confirming the branch contributed nothing else to it. Verified: harness::session 227, orchestration 420, transcript_history 16, transcript_view 13, message_convert 12 — all passing. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformatted the use statement in transcript_history.rs to wrap the imported items more evenly across lines, improving readability without changing any behavior. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cf6060f857
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Checkpoint of work in progress, touching tests/json_rpc_e2e.rs. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Checkpoint of work in progress, touching vendor/tinycortex. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Trims the core by moving genuinely generic session machinery into
tinyagents, andreplaces the parallel transcript abstraction with the crate's
ChatHistoryseam.Net: −4,154 / +1,522. Depends on tinyhumansai/tinyagents#90.
1.
agent/session_db/→tinyagents::harness::session_storeThe session store and run ledger were generic runtime machinery living in a host. Their
only coupling to this crate was
config.workspace_dir, so the crate-side entry points take&Pathand call sites pass that field.What stays here:
session_db/schemas.rs— the controller schemas and handlers. The RPCenvelope,
Configresolution, andRpcOutcomeshape are host concerns.Call sites are rewritten to
tinyagents::harness::session_store::*rather than kept alivebehind re-export shims, so nothing is left implying the store still lives here — 29 files
across
orchestration/,core/,web_chat/,hosted/,progress_tracing/.The DB path (
{workspace}/session_db/sessions.db) and thesession_db/run_ledgerRPC namespaces are unchanged, so existing installs keep their history.
Two seams needed real handling rather than a path rewrite:
agent_teams/ops.rsandworkflow_runs/ops.rswrapOk(...?)—Result<T, TinyAgentsError>does not coerce toanyhow::Result<T>.ControlErrorgainsFrom<TinyAgentsError>; its#[from] anyhow::Errorvariant does notapply to a distinct type, so without it every ledger call in
command_center/control.rswould need its own
map_err.2. The transcript
ChatHistoryseam (design §4 Option A)SessionTranscriptHistoryimplementstinyagents::harness::memory::ChatHistoryover thedurable
session_rawtranscript.transcript.rsis byte-for-byte unmodified — zeroon-disk change, zero migration risk.
Three decisions, each pinned by a test, because each is a place a plausible implementation
would silently corrupt a user's transcript:
messages()routes through the model-context replay, so compaction records replacethe accumulator and
interrupted:truepartials are skipped. §3.1 of the design calls anaive read "the single most important constraint in this document".
replace()maps onto the compaction-record path. The trait's default isclear-then-append, which would destroy the append-only history.
clear()appends a compaction record with an empty replacement: context empties, priorlines survive. Truncating breaks the append-only invariant; a fresh stem orphans history
from its thread. Both rejected alternatives are recorded in the doc comment.
3. S4 — the whole turn path goes through host-side seams
S4's wording and its own exit criterion contradict each other, and the exit criterion
won.
ChatHistory's four methods carry only(thread_id, Message). Neither half of theturn path can cross it losslessly.
Writes. Three things have no channel on the trait:
request_idDisplayItem::TurnBoundarydisappears, and with it the(request_id, ts)segments anchoring everySubagentturn_usageDisplayItem::ToolCallis read offturn_usage.tool_calls— losing it deletes the tool rows, after which each followingrole:"tool"line falls to the orphan branch.ReasoningandAssistantMessage.{model,iteration}go tooTranscriptMetarollupsread_thread_usage_summaryreportsReads. A
ChatMessage → Message → ChatMessagebounce drops assistanttool_calls.The turn path deliberately persists a native tool round as the
{content, tool_calls}/{tool_call_id, content}envelope so the next turn re-parses it;message_to_chat_messageflattens that to prose. Every followingrole:"tool"row thenbecomes an orphan — the exact
400 An assistant message with 'tool_calls' must be followed by tool messagesthe strip guard exists to prevent. Any resumed session containing atool round would break. It also blinds
assistant_message_has_tool_calls, which sniffsthe envelope in content, so the trailing-strip guard silently stops firing.
So the turn path holds host-side supertraits of
ChatHistory:SessionHistory::append_turnfor writes,SessionTranscriptRead::read_session+SessionHistoryLocatorfor reads. Each forwards to the format owner unchanged, so on-diskbytes are identical by construction, and
ChatHistorystays in the bound.grepforread_transcript/find_latest_transcript_in_subdir/find_root_transcript_for_threadacrosssession_io.rsandruntime.rsnow returnsonly doc comments.
The injection point is real
AgentBuilder::with_session_history_locator→Agent::session_history_locator→ lazilyresolved by
Agent::session_locator(), used at all three sites. A fake-locator test provessubstitution genuinely takes effect rather than being constructed and ignored.
Claims pinned executably, not asserted in prose
append_turn_is_byte_identical_to_the_free_functioncompares files byte-for-byte;trait_path_loses_the_provenance_that_append_turn_preservesdemonstrates what the genericpath drops. A mutation forwarding
None, Nonefails both, so they are load-bearing.The golden test S4 names as its gate did not exist. Ten of eleven
transcript_viewtests hand-write JSONL literals, and the one writer-driven test uses
write_transcript,not the append path — so "projection output unchanged" was unfalsifiable. It lands first,
green.
Two bugs found while wiring
workspace/session_raw, but the turn path usessession_raw_subdir(
session_raw-<id>on a dedicated-memory profile) — cross-profile data corruption themoment the handle went in. Fixed via
new_in_dir.open_stemresolved the path twice; it now takes it from the handle.The spec's "~400 LOC removed" is struck, with a derivation
Option A promised it and it does not exist. Measured ledger for this slice: ≈ −15 / +90
LOC. The 2,475 lines in scope are
transcript.rs(1,997) +turn_checkpoint.rs(105) +migration.rs(373); Option B's 2,100 is the first two; the residual 373 ismigration.rs,HOST-OWNED per S1. Three candidate sources were checked and refuted.
The one genuine parallel session-persistence implementation in the tree is the #4249
JSONL↔store mirror (~565 prod LOC:
session_import/live.rs, the shadow-read/dual-writepair, the
StoreRegistryregistration, twoAgentConfigflags, and a config migration).It is over crate
Store/AppendStore, notChatHistory, and is blocked on #4249's ownPhase-2 parity soak — not this design's S5. Recorded in the ledger with that gate.
Deliberately not done
Recorded in the deletion ledger and in doc comments so a later audit does not reopen them —
the design notes this question has already been reopened twice:
transcript.rs— HOST-OWNED. Durable on-disk format,.mdrendering, product reads.Upstreaming it is Option B: a live user-data format becoming public API of a published GPL
crate. A crate-roadmap decision, not a cleanup.
turn_checkpoint.rs— HOST-OWNED. Built onChatMessage, which WP-1 settled as theversioned on-disk record; replacing it changes existing users' data.
migration.rs— HOST-OWNED. The design's S1 check was performed, not assumed: zerohost imports, but it hardcodes
session_raw,sessions, andstate/migrations/session_layout_v1.done, keyed to release 0.53.4. Generic code for ahost-specific format.
session_import/— stays. It reads legacy OpenHuman formats and writes tinyagentsstores; it is already a host-side adapter pointed the right way.
ChatHistoryupstream — rejected. The crate'sUsagehas nocost_usd/context_window,TranscriptMetais a cumulative file header rather than turnprovenance, and the per-message tool-failure
extra_metadatathatmessage_to_chat_messagedrops is untouchable by any turn-level record. It would cost a tinyagents release and still
need a
serde_json::Valueescape hatch.Verification
agent_harness_e2e: 18/18 ·transcript_view: 13/13 ·transcript_history:11/11 ·
session::transcript: 48/48 ·read_thread_usage_summary: 5/5 ·orchestration: 420/420
clippyclean on every changed file;cargo fmtcleanPre-existing failures, confirmed against an untouched baseline and unrelated to this
branch: a stack overflow in
cron::scheduler::tests(reproduces identically on a cleanstash), and order-dependent flakes in
tinyplace::manifest,archivist, andmemory_treethat pass in isolation and fail on
/tmpchunk-DB contention under parallel load. None ofthe 46 changed files touch those modules.
vendor/tinyagentsin this PR points at a commit on an unmerged branch(
agent-sessions-to-tinyagentsintinyhumansai/tinyagents), not at a commit on thatrepo's
main. Merging this first would put a submodule pointer onmainthat becomesunreachable as soon as the source branch is cleaned up after its own merge.
Required order:
tinyhumansai/tinyagents#90merges.vendor/tinyagentshere at that merge SHA, and rebase — this branch iscurrently 17 commits behind
main.tinyhumansai/workflow-openhuman#6advances theopenhuman/gitlink to this PR's merge SHA.
Marked ready for review so it is visible to review tooling; the submodule repoint and
rebase above are still outstanding and are the merge gate.
Also outstanding
Design slice S5 is not started. The doc calls for one release of shadow read-side
comparison before the parallel path is deleted. Nothing in this PR depends on it — it is
where further LOC reduction would come from, not a correctness gate on this change.