refactor: extract daemon replay behind one application facade - #2166
Conversation
Size Report
npm unpacked components
Startup median (7 runs, lower is better):
Top changed chunks:
Top changed packed files
|
Code-quality reviewI read the whole diff against What follows is about the enforcement the PR adds, not about what it broke. Six findings; none blocking, but 1–4 are worth fixing before merge because they are what pins the extraction in place. 1. The store-authority gate is now vacuous, and its rationale comment is false
test('the remaining divergence-report chain holds SessionStore only as a type', () => {
for (const file of DIVERGENCE_CHAIN_FILES) {
if (!SESSION_STORE_TYPE_ONLY_ALLOWED.has(file)) continue;
for (const site of collectImportSites(file)) {
if (site.target !== SESSION_STORE_MODULE) continue;
...After this PR, none of the five So the inner The header comment is now wrong too:
There is no longer any distinction: all five have none. This PR earned the stronger ratchet and then left the weaker one in place, so the state it achieved is unpinned and can silently regrow. Fix: delete 2. The least-authority claim — the stated justification for the +68 production lines — is not enforcedThe PR body argues the net growth buys "keeping replay internals from acquiring close/record authority", and the new policy encodes exactly two forbidden targets ( forbiddenTargetRoots: [
'src/daemon/handlers/session-close.ts',
'src/daemon/handlers/record-runtime.ts',
],But the authority that matters is
import { SessionStore } from '../../session-store.ts'; // value import…used once, at line 317, for So today the Fix (two lines):
That converts the PR's central argument from prose into a gate, which is the whole point of the ownership scripts. 3. New type-import cycle
|
| pointer | in |
|---|---|
session-replay-repair-hint.ts |
packages/contracts/src/replay-divergence.ts:221 |
session-replay-target-classification.ts |
packages/ad-script/src/internal/target-annotation-identity.ts:6,202, …/target-annotation-classification.ts:13 |
session-replay-target-verification.ts |
packages/ad-replay/src/internal/target-verification.ts:3, …/target-annotation-identity.ts:53 |
Plus three dangling symbol references to runReplayScriptSource in packages/ad-replay/src/internal/{inspect,step-loop}.ts and its test. In a repo that leans this hard on comments as navigation, a pointer into a path that no longer exists costs a reader (or an agent) a full-tree search. docs/adr/** is a historical record and can stay as-is; the packages/** ones should follow the move.
Nits
- Test names now describe the fixture, not the behavior. A bulk rename turned 40 tests from
runReplayScriptSource …intorunReplayForTest …— naming the local test helper rather than the production entry point. They should readrunReplayCommand …;sed -i '' 's/runReplayForTest /runReplayCommand /'overreplay/internal/__tests__/. index.tscarries no header. It is the artifact the whole PR exists to create, and it is 8 lines of re-exports with no statement of what the module owns or what stays outside it. Every module it re-exports has one.- Two of the six pinned façade exports have no production consumer.
ReplayCommandandReplayTestCommandare used only byreplay/__tests__/, which sits inside the module and can import./internal/command-types.tsdirectly. Pinning them widens the façade for test convenience. daemon-replayis declared twice —LOGICAL_MODULE_POLICIES(rootsrc/daemon/replay/) andDAEMON_REPLAY_FACADE(rootsrc/daemon/replay/index.ts) — with nothing tying them together. Afacadefield on the policy would make the module and its façade undriftable.command: paramsatsession-replay-maestro-runtime.ts:136,144hands the error/success builders an object that also carriesbundle,startedAtand the mutablestatein a slot typedReplayCommand. Structurally fine, but it un-narrows the type the parameter exists to narrow.
Summary: the move itself is clean and I could not fault its behavior. The gap is that the enforcement shipped alongside it is weaker than the PR body claims — one gate asserts nothing (1), the central least-authority argument has no gate at all and is already breached once inside the tree (2), a new cycle slips under a ratchet that measures only the largest one (3), and the one test that guarded the capability threading was deleted in the same change that introduced the threading (4). Items 1, 2 and 5 are each a handful of lines.
|
One actionable finding blocks readiness at
The replay facade/least-authority extraction and move-adjusted +68 production-line rationale otherwise look coherent; no smaller design finding. Android Smoke currently failed in an unrelated alert-dismiss scenario and should be rerun on this exact head, but it is separate from the confirmed code finding. No |
93b8012 to
c5ed604
Compare
|
Addressed in
Validation after the rebase: focused replay/repair/resume tests passed (54 files, 474 tests), the merged application contract passed, the coordinator structural gate passed (7 tests), layering passed (172 tests plus guard), fallow passed for 117 changed files, and the affected non-Vitest checks passed. The affected Vitest lane had only the existing 5-second iOS lifecycle/gesture-admission timeout pair under host load; gesture passed when isolated, and the independent behavior review found no PR regression when comparing PR/base. No wire, progress, artifact, error-ordering, repair/resume, lifecycle, or single-coordinator behavior changed. |
|
Reviewed exact head |
c5ed604 to
66e02f0
Compare
|
Rebased and pushed the PR onto The reported conflict in I also refreshed the PR description with the final review fixes and measurements: bound read/mutation/observation capabilities instead of exposing keyed session operations, no replay imports of the concrete Validation on this exact head:
The PR remains unmerged; GitHub exact-head checks are now running for the new commit. |
|
Reviewed exact head 66e02f0. The semantic rebase conflict is correctly resolved: replayed open actions retain current main's tenant-aware resolveSessionScope. The replay facade remains narrow and session-bound, with no concrete SessionStore authority or second coordinator construction exposed. Code review is clean and all completed exact-head checks are green; iOS Smoke Tests is the only remaining in-progress job. Ready for human review now, and merge-ready once that exact-head iOS job passes. |
|
Summary
Extract daemon replay and replay-test application orchestration behind the named
src/daemon/replay/index.tsfacade.runReplayCommandandrunReplayTestCommandentry points with aReplaySessionaggregate and narrow replay-owned capabilities.src/daemon/replay/internal, while keeping close, recording, session storage, publication, script writing, transaction, and request-binding ownership in their existing modules.Closes #2131
Dependencies and base
mainatcfed1ae56745a81ef659d8adcebd67ea8a015ad1(including the latest merged default-branch changes). The only rebase conflict was insrc/daemon/replay/internal/session-replay-action-runtime.ts; it was resolved by preserving the current-main tenant-awareresolveSessionScopeimport and replayed-openresolvedSessionScopepropagation.caa3dc23f9f3f6a829e91e8c5f00a1e4a3084f53.dd988847c8f5a5a21dc9315e829d228c025cfc4d.Validation
pnpm check:quick— pass.pnpm check:affected --run— all non-Vitest selected checks passed: format, lint, typecheck, layering, DI seams, fallow, MCP metadata, build, package verification, Node integration, and macOS coverage. The Vitest-related lane completed 536/537 files and 4,531/4,532 tests; the single failure was the 5-second iOS Settings provider timeout under host load. The replay, repair, resume, routing, and structural tests passed. An independent behavior review compared the timeout on the PR and its base and found no regression; no production timeout or behavior change was made.1538source files, zero forbidden logical-module imports).Touched files and size
src/daemon/replay/index.ts,src/daemon/replay/internal/**, andsrc/daemon/replay/**/__tests__/**: named facade, private implementation, and relocated replay/test coverage.src/daemon/handlers/session-command-input.ts,src/daemon/handlers/session-replay-command.ts,src/daemon/handlers/session-replay-video-owner.ts, andsrc/daemon/handlers/session.ts: handler composition, command types, and existing owner adapters.src/daemon/session-replay-coordinator.ts,src/daemon/internal-observation.ts,src/daemon/handlers/session-runtime.ts, andsrc/daemon/session-action-recorder.ts: named capability and moved-path seams.scripts/layering/**,src/daemon/__tests__/replay-coordinator-ownership.test.ts,fallow-baselines/health.json,oxlint.config.ts, andscripts/__tests__/test-file-size-ratchet.test.ts: structural enforcement, import scanning, and baselines.packages/ad-replay/**,packages/ad-script/**,packages/contracts/**, andpackages/replay-test/**: current replay module paths and contract/test imports.Rename-aware diff against
origin/main...HEAD: 121 files, gross+2217/-1593. For productionsrc/**excluding__tests__and*.test.ts, changed-line accounting is+1515/-1337, a move-adjusted net+178lines.The net production growth is deliberate and independently adversarially reviewed: it is the cost of the named facade, the session command-input seam, bound read/mutation/observation capabilities, and the existing-owner adapters needed to keep replay internals from acquiring close/record authority. The extraction removes the null router, duplicated session-handler decomposition, and scheduler-side owner plumbing; further shrinking by passing the broad
SessionCommandInput/SessionStoreor by moving close/record implementations into replay would violate the issue's least-authority and ownership constraints. No parallel mutable state or compatibility shim was added.Residual risks
66e02f04bb4c0e98ed052e61c2f09735e834f81d.