fix(release): stop shipping FakeBackend and desktop E2E material in production artifacts - #3226
Conversation
📝 WalkthroughProblem solvedThis PR prevents It removes production It moves test-only code under Persisted Source of truth and solution scopeThe PR extends the existing Runtime Host composition. It keeps The test-only E2E entrypoint provides a separate launch path for test execution. It still uses the real Runtime Host. This is the smallest coherent solution shown by the diff. The added entrypoint and boundary checks are necessary to keep test-only code out of production artifacts. Simplification opportunitiesThe diff removes obsolete production options, CLI validation, backend registration, E2E lifecycle branches, and production imports. No further deletion is evident without weakening release-policy, module-graph, CLI, or E2E regression coverage. Validation and risksReported validation includes affected builds, Runtime Host tests, Desktop E2E tests, release-policy tests, lint, formatting, type checks, diff checks, and whitespace checks. CI passed packaging, Windows checks, recovery, and dependency-audit workflows. Windows packaging and release validation passed. Full local CLI and Electron packaging were not run. A local macOS Electron package build was not performed. Required-check status is unverified here. Concrete risks include:
Complexity deltaThe PR removes:
The PR adds:
Production authorities, branches, and artifact reachability decrease. The public surface changes through the renamed test-only export. Test-maintenance burden increases through boundary and release checks, but these checks enforce the stated packaging constraint. Overall maintenance complexity decreases. The added complexity is justified by release safety and regression coverage. Review-relevant risksThe current diff shows effects on persistence compatibility, public package exports, release artifact contents, and Desktop E2E startup behavior. Material changes in these protected areas require independent human review under repository policy. The person performing the merge reviews the final diff. A maintainer makes the final determination. WalkthroughDesktop E2E startup now uses a test-only entrypoint. Production runtime-host code no longer accepts ChangesRuntime and release isolation
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: ⚪ Minimal · up to The PR changes production packaging and backend composition, with the supplied build, test, release-policy, lint, format, typecheck, and E2E checks passing; no actionable merge-blocking risk remains, aside from a trivial bounded diagnostics-cleanup follow-up. Sequence Diagram(s)sequenceDiagram
participant RuntimeHostBoot
participant CandidateLauncher
participant CandidateEntry
participant RuntimeHostComposition
RuntimeHostBoot->>CandidateLauncher: start candidate with startup attempt ID
CandidateLauncher->>CandidateEntry: pass startup attempt ID
CandidateEntry->>RuntimeHostComposition: start candidate composition
RuntimeHostComposition-->>CandidateEntry: startup or lifecycle result
CandidateEntry-->>CandidateLauncher: report startup failure or election result
Possibly related issues
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
PR Summary by QodoExclude test-only backends and E2E bootstrap from release artifacts
AI Description
Diagram
High-Level Assessment
Files changed (39)
|
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: d9ca0244-c1cc-4b53-8670-26df1fc37589
📒 Files selected for processing (39)
apps/desktop/e2e/composer-inline-completion.spec.tsapps/desktop/e2e/slash-command-menu.spec.tsapps/desktop/e2e/streaming-remount.spec.tsapps/desktop/electron-builder.config.mjsapps/desktop/src/main/runtime-host-boot.tsapps/desktop/src/main/runtime-host-desktop-candidate.tspackages/runtime-host/package.jsonpackages/runtime-host/src/__tests__/candidate-cli.test.tspackages/runtime-host/src/__tests__/desktop-e2e-execution.test.tspackages/runtime-host/src/__tests__/execution-candidate-main.test.tspackages/runtime-host/src/__tests__/execution-composition.test.tspackages/runtime-host/src/__tests__/execution-host-message.test.tspackages/runtime-host/src/__tests__/execution-host-queue.test.tspackages/runtime-host/src/__tests__/execution-host-recovery.test.tspackages/runtime-host/src/__tests__/execution-host.test.tspackages/runtime-host/src/__tests__/fixtures/execution-host-suite.tspackages/runtime-host/src/__tests__/fixtures/execution-host.tspackages/runtime-host/src/__tests__/goal-root-authority.test.tspackages/runtime-host/src/__tests__/plan-two-client-uds.test.tspackages/runtime-host/src/__tests__/root-turn-coordinator.test.tspackages/runtime-host/src/__tests__/runtime-policy-coordinator.test.tspackages/runtime-host/src/__tests__/session-revision-two-client-uds.test.tspackages/runtime-host/src/candidate-cli.tspackages/runtime-host/src/client/connect-or-spawn.tspackages/runtime-host/src/client/launcher.tspackages/runtime-host/src/execution-candidate-main.tspackages/runtime-host/src/server/execution-composition.tspackages/runtime-host/src/test-only/desktop-e2e-execution.tspackages/runtime-host/src/test-only/execution-candidate-e2e-main.tspackages/runtime/README.mdpackages/runtime/package.jsonpackages/runtime/src/__tests__/fake-backend.test.tspackages/runtime/src/__tests__/runtime-continuation-crash.test.tspackages/runtime/src/__tests__/session-manager.test.tspackages/runtime/src/__tests__/stream-graph-coordinator.test.tspackages/runtime/src/test-only/fake-backend.tsscripts/release-cli-file-policy.mjsscripts/release-cli-file-policy.test.mjsscripts/release-cli-package.mjs
💤 Files with no reviewable changes (4)
- packages/runtime-host/src/client/launcher.ts
- packages/runtime-host/src/server/execution-composition.ts
- apps/desktop/src/main/runtime-host-desktop-candidate.ts
- packages/runtime-host/src/client/connect-or-spawn.ts
Included review availability: Your plan provides up to 3 included reviews per hour; 1 remains after this review.
Code Review by Qodo
1.
|
PR AssessmentWhat problem does this PR solve?This PR removes It also replaces the old production How does this PR solve the problem?
Is the problem correctly defined?Correct. The PR addresses both sides of the release-isolation problem: preventing test-only code from entering production artifacts and ensuring that removing the production fake backend does not degrade legacy persisted sessions into an internal registry error. Principle-based assessment
Review FindingsP1 BlockerNone. P2 Should FixNone. SuggestionNone. Verification
ConclusionCan merge. The PR satisfies the stated packaging boundary, preserves the real Runtime Host composition for E2E, handles legacy fake sessions with an intentional product-level refusal, and has passing local and CI verification. 点击展开中文PR 判断这个 PR 解决了什么问题?这个 PR 将 它还用独立的 test-only candidate entrypoint 替代了生产环境中的 这个 PR 如何解决这个问题?
这个问题定义得对吗?正确。 这个 PR 同时处理了发布隔离问题的两面:防止 test-only 代码进入生产产物,并确保删除生产 fake backend 后,旧的持久化 session 不会退化成内部 registry 错误。 原则性判断
Review FindingsP1 阻塞无。 P2 应该改无。 建议无。 验证
结论可以合入。 这个 PR 满足发布边界要求,保留了真实 Runtime Host composition 作为 E2E 路径,对旧 fake session 提供了明确且有意设计的产品级拒绝,并且本地和 CI 验证均通过。 |
…roduction artifacts FakeBackend is a test backend, but it was wired as a first-class production surface and left the build inside the release artifacts: the normal Runtime Host composition registered it unconditionally, and the production candidate entry statically imported the Desktop E2E composition, so both the CLI tarball and the packaged Electron app carried a test backend and the `--desktop-e2e` bootstrap. Move both behind a `test-only/` directory convention and cut every production import of them: - `FakeBackend` moves to `@maka/runtime/test-only/fake-backend`. - `DesktopE2eBackend` and its composition move to `packages/runtime-host/src/test-only/desktop-e2e-execution.ts`, with a new `test-only/execution-candidate-e2e-main.ts` entry beside it. - The production candidate entry loses its E2E import, and the `--desktop-e2e` flag disappears from the candidate CLI, the launcher, connect-or-spawn and the Desktop candidate manager: the entry module is now the switch, and `runtime-host-boot.ts` picks it from `isE2e`. - The production composition no longer registers a `fake` backend. Every deterministic-backend consumer — Desktop E2E, the in-process composition tests, and the forked `execution-host` test fixture — now goes through the existing `primaryBackendFactory` seam, so the E2E path still runs the real Runtime Host composition. Release packaging drops `test-only/` on both artifact paths: the CLI release copy filter and `isMakaDevelopmentArtifact` (which also fails the pack if such a file ever escapes), and the Electron `files` list beside the existing `!**/__tests__/**`. A new test walks the built module graph from the production candidate entry and fails if it can reach any `test-only/` module. `'fake'` remains in `BackendKind` for now; retiring the persisted value, the unknown-provider fallback in `llm-connections.ts`, and the product-layer special-cases is a follow-up so this change stays a packaging fix. Part of #3211 Generated-by: Claude Code
…odule graph
The production-entry reachability walk only matched static `import`/`export …
from` forms, so a `import('./test-only/…')` edge would have been invisible to
it — and the built `dist` already contains literal dynamic imports (for example
`@maka/runtime`'s `model-adapter.js` and `@maka/storage`'s
`foreign-session-store.js`). The guard could therefore have passed while a
production module reached test-only material.
Generated-by: Claude Code
…duct reason Dropping the `fake` registration left activation dispatching off a durable header value with no factory behind it, so a session or Automation persisted by an older build failed its next turn with `No backend factory registered for kind="fake"`. The Desktop composer does gate these rows — `sendBlocked` reads a destructive `sessionHealthNotice`, which `projectSessionSendOutcome` raises for `fake_backend` — but that is a renderer gate, not an authority: `maka run` resumes an existing session through a readiness check that only inspects the connection catalog, and a persisted Automation template still accepts `execution.backend: 'fake'`. Register an explicit refusal where the test backend used to be. It throws the canonical `NO_REAL_CONNECTION:fake_backend` error, which `parseNoRealConnectionError` already turns into the copy both surfaces show for these rows: the task came from the retired local simulation, add a real model and start a new one. Rewriting the durable header to `ai-sdk` on the read path was the alternative and is worse: it destroys the fact that drives that copy, and leaves a session that looks runnable while its `llmConnectionSlug` still points at nothing, so the failure would resurface later and less specifically. Generated-by: Claude Code
2f4bb27 to
2200d14
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/runtime-host/src/client/connect-or-spawn.ts (1)
235-241: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueClean up orphaned attempt diagnostics on terminal returns. Retention is bounded to 32 files for 24 hours, but
upgrade_requiredandblocked_by_residencycan leave recorded failures, and deadline timeouts leave unregistered late reports.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 08699108-348e-4194-9f62-fcc634a7c4ce
📒 Files selected for processing (15)
packages/runtime-host/src/__tests__/candidate-cli.test.tspackages/runtime-host/src/__tests__/desktop-e2e-execution.test.tspackages/runtime-host/src/__tests__/execution-candidate-main.test.tspackages/runtime-host/src/__tests__/execution-composition.test.tspackages/runtime-host/src/candidate-cli.tspackages/runtime-host/src/candidate-entry.tspackages/runtime-host/src/client/connect-or-spawn.tspackages/runtime-host/src/client/launcher.tspackages/runtime-host/src/execution-candidate-main.tspackages/runtime-host/src/server/execution-composition.tspackages/runtime-host/src/test-only/desktop-e2e-execution.tspackages/runtime-host/src/test-only/execution-candidate-e2e-main.tspackages/runtime/package.jsonpackages/runtime/src/__tests__/session-manager.test.tspackages/runtime/src/__tests__/stream-graph-coordinator.test.ts
Included review availability: Your plan provides up to 3 included reviews per hour; 2 remain after this review.
`BackendKind` was doing two jobs: naming the backend a build may select, and typing the backend value read back from durable state. The in-process FakeBackend stopped shipping in #3226, so the first job narrows to `'ai-sdk'`; the second becomes `PersistedBackendKind`, which still admits `'fake'` and now types the session header, the session-catalog wire projection, the run header, the Automation template, the workspace defaults, and the backend registry that dispatches off them. No data migration. Sessions, runs and Automations written by builds that shipped FakeBackend keep `'fake'` on disk: narrowing the decode guards would make those rows read back as malformed, and rewriting them to `'ai-sdk'` would make an unrunnable task look runnable, since their `llmConnectionSlug` still points at nothing. Activation already refuses them with the product's `fake_backend` reason. `backendKindOf` no longer answers `'fake'` for an unrecognized `providerType` — it throws. That fallback was the last live producer of the value, and there is no honest backend to name for a provider this build cannot describe; the non-throwing question ("can this connection be used?") is `isRealConnection` / `isConnectionReady`. With no provider declaring `'fake'`, the former `isFakeBackend` collapses to "is this providerType one the build knows", so it is named for what it tests. The send projection's dead `slug === 'fake'` branches go with it: a session carrying that slug is refused one line earlier by its backend, and a connection carrying it is refused by the readiness gate. Also deletes `apps/desktop/src/main/chat-readiness.ts`. Its send gate — `requireReadyConnection`, `assertSessionCanSend`, `ensureSessionCanSendOrRebind`, and a third copy of the Chinese connection error copy — had no caller left once the gate moved to Runtime Host; the only live import was the one-line `errorMessage` helper, now inlined at its single use site. Part of #3211 Generated-by: Claude Code
`BackendKind` was doing two jobs: naming the backend a build may select, and typing the backend value read back from durable state. The in-process FakeBackend stopped shipping in #3226, so the first job narrows to `'ai-sdk'`; the second becomes `PersistedBackendKind`, which still admits `'fake'` and now types the session header, the session-catalog wire projection, the run header, the Automation template, the workspace defaults, and the backend registry that dispatches off them. No data migration. Sessions, runs and Automations written by builds that shipped FakeBackend keep `'fake'` on disk: narrowing the decode guards would make those rows read back as malformed, and rewriting them to `'ai-sdk'` would make an unrunnable task look runnable, since their `llmConnectionSlug` still points at nothing. Activation already refuses them with the product's `fake_backend` reason. `backendKindOf` no longer answers `'fake'` for an unrecognized `providerType` — it throws. That fallback was the last live producer of the value, and there is no honest backend to name for a provider this build cannot describe; the non-throwing question ("can this connection be used?") is `isRealConnection` / `isConnectionReady`. With no provider declaring `'fake'`, the former `isFakeBackend` collapses to "is this providerType one the build knows", so it is named for what it tests. The send projection's dead `slug === 'fake'` branches go with it: a session carrying that slug is refused one line earlier by its backend, and a connection carrying it is refused by the readiness gate. Also deletes `apps/desktop/src/main/chat-readiness.ts`. Its send gate — `requireReadyConnection`, `assertSessionCanSend`, `ensureSessionCanSendOrRebind`, and a third copy of the Chinese connection error copy — had no caller left once the gate moved to Runtime Host; the only live import was the one-line `errorMessage` helper, now inlined at its single use site. Part of #3211 Generated-by: Claude Code
`BackendKind` was doing two jobs: naming the backend a build may select, and typing the backend value read back from durable state. The in-process FakeBackend stopped shipping in #3226, so the first job narrows to `'ai-sdk'`; the second becomes `PersistedBackendKind`, which still admits `'fake'` and now types the session header, the session-catalog wire projection, the run header, the Automation template, the workspace defaults, and the backend registry that dispatches off them. No data migration. Sessions, runs and Automations written by builds that shipped FakeBackend keep `'fake'` on disk: narrowing the decode guards would make those rows read back as malformed, and rewriting them to `'ai-sdk'` would make an unrunnable task look runnable, since their `llmConnectionSlug` still points at nothing. Activation already refuses them with the product's `fake_backend` reason. `backendKindOf` no longer answers `'fake'` for an unrecognized `providerType` — it throws. That fallback was the last live producer of the value, and there is no honest backend to name for a provider this build cannot describe; the non-throwing question ("can this connection be used?") is `isRealConnection` / `isConnectionReady`. With no provider declaring `'fake'`, the former `isFakeBackend` collapses to "is this providerType one the build knows", so it is named for what it tests. The send projection's dead `slug === 'fake'` branches go with it: a session carrying that slug is refused one line earlier by its backend, and a connection carrying it is refused by the readiness gate. Also deletes `apps/desktop/src/main/chat-readiness.ts`. Its send gate — `requireReadyConnection`, `assertSessionCanSend`, `ensureSessionCanSendOrRebind`, and a third copy of the Chinese connection error copy — had no caller left once the gate moved to Runtime Host; the only live import was the one-line `errorMessage` helper, now inlined at its single use site. Part of #3211 Generated-by: Claude Code
#3226 stopped shipping FakeBackend; this retires `'fake'` from the live surface. The value carried three jobs: a selector for a runnable backend, a literal in durable records, and a sentinel meaning "not real / not known". #3226 killed the first. The second is permanent. This removes the third, so `'fake'` now appears only in decode guards and in the one function translating it to the `fake_backend` product reason. - `BackendKind` narrows to `'ai-sdk'`; `PersistedBackendKind = BackendKind | 'fake'` types everything durable — session header and summary, catalog wire projection, run header, Automation template, workspace defaults, and the registry dispatching off them. - No caller chooses a backend. `CreateSessionInput.backend` is deleted rather than narrowed: a live build has exactly one backend, so the field carried no choice, only the chance of writing the retired value. The store stamps every new header, and a session derived from an older one no longer inherits its backend. - Two backend-kind guards go with it: the `sessions:create` IPC check, now unrepresentable, and the T1 tool-boundary gate on `header.backend` — that one was reachable, but all it withheld was the protocol marker on the initial event of a run that dies at `reserveRun` before any tool dispatch, and every marker consumer treats such a run the same as an unmarked one. - No data migration. Legacy rows keep `'fake'` on disk. Narrowing the decode guards makes them read back as malformed; rewriting them to `'ai-sdk'` makes an unrunnable task look runnable, since `llmConnectionSlug` still points at nothing. Activation refuses them with the product reason, as of #3226. - `backendKindOf` throws for an unrecognized `providerType` instead of answering `'fake'`. This changes `@maka/core`'s public contract; it has no in-repo caller. - Provider recognition gets one own-property-safe owner, so inherited object members such as `__proto__` no longer read as registered providers. Behavior change: `shouldRebindSessionToDefault` listed `fake_backend`, but nothing performs that rebind — activation dispatches off the header's own backend, so no connection swap could help. Removing it makes the projection answer `blocked`, lets the rail and the composer drop the workarounds that read `session.backend` directly, and surfaces the existing stale-task notice for unlocked rows. Follow-up tracked in #3306. Closes #3211 Generated-by: Claude Code
Summary
FakeBackendis a test backend, but it was wired as a first-class production surface and left the build inside the release artifacts. The normal Runtime Host composition registered it unconditionally (packages/runtime-host/src/server/execution-composition.ts:289), and the production candidate entry statically imported the Desktop E2E composition, so both the CLI tarball and the packaged Electron app carried a test backend plus the--desktop-e2ebootstrap.This PR is the packaging half of #3211: get the test backend and the Desktop E2E bootstrap out of the production build graph and out of both artifacts, without weakening the rule that E2E must run through the real Runtime Host composition.
What changed
FakeBackendmoves to@maka/runtime/test-only/fake-backend;DesktopE2eBackendand its composition move topackages/runtime-host/src/test-only/desktop-e2e-execution.ts, with a newtest-only/execution-candidate-e2e-main.tsentry beside it.--desktop-e2edisappears from the candidate CLI, the launcher,connect-or-spawnand the Desktop candidate manager. The entry module is now the switch:runtime-host-boot.tsresolves@maka/runtime-host/test-only/execution-candidate-e2e-mainwhenisE2e, and the production entry otherwise.fakebackend. Every deterministic-backend consumer — Desktop E2E, the in-process composition tests, and the forkedexecution-hosttest fixture — now goes through the existingprimaryBackendFactoryseam, which is exactly the seam test(release): FakeBackend is a first-class BackendKind and desktop E2E material ships in release artifacts #3211 asked to preserve.test-only/on both artifact paths, andpackages/runtime/README.mdrecords the convention.Design decisions this PR commits to (issue items 1 and 4)
Test-only compile entry. A
test-only/source directory plus a./test-only/*package export, extending the convention@maka/runtimealready used for./test-only/observation-text-reader. This was chosen over build-time exclusion or export conditions because it makes one fact — "no production module imports this" — checkable statically, from one directory name, on both artifact paths.Release-exclusion verification, without building a release. Three static checks, no packing required:
isMakaDevelopmentArtifactinscripts/release-cli-file-policy.mjsnow rejects anytest-only/segment, with unit tests inscripts/release-cli-file-policy.test.mjs(run bynpm run check:release).test-onlyis deliberately not added toDEVELOPMENT_DIRECTORIES, so a third-party package shipping a directory by that name is unaffected — there is a test for that too.validatePackedFilesinscripts/release-cli-package.mjs, so if the copy filter ever let such a file through, packing throws rather than shipping it.copyRuntimeDistdrops the directory at copy time.apps/desktop/electron-builder.config.mjsgains'!**/test-only/**'beside the existing'!**/__tests__/**'.The strongest guarantee is not a file filter, though: a new test in
packages/runtime-host/src/__tests__/execution-candidate-main.test.tswalks the built module graph from the production candidate entry across all bundled@maka/*packages and fails if it can reach anytest-only/module. Even a stray byte in an artifact would now be unreachable code.Design decided here, implemented in the follow-up (issue items 2 and 3)
'fake'stays inBackendKindin this PR. Removing it is a persistence and product-surface change, not a packaging one, and it is large enough to review separately. The decisions, so the follow-up does not re-litigate them:Persistence compatibility (item 2). Verified: a stored
backend: 'fake'is not tolerated by decode today.packages/storage/src/session-store.ts:1146(isBackendKind) is a hard validator — narrowing the union there turns every legacy fake session intoInvalid session header … malformed fields, i.e. an unreadable session, not a degraded one.packages/core/src/agent-run.ts:712andpackages/core/src/scheduled-task.ts:492have the same shape. So the follow-up must split the two jobs'fake'currently does: selecting a live backend factory, and tagging a retired legacy session.BackendKind(the live selector) narrows to'ai-sdk'; the persisted header keeps a wider'ai-sdk' | 'fake'type that decode still accepts and that resolves to no factory. No data migration — the product already treats these rows as retired (session-send-projection.ts:118→fake_backend,stale-sessions.ts:27, search exclusion inthread-search.ts:171), and rewriting them to'ai-sdk'would make dead sessions look sendable.Unknown-provider fallback (item 3).
packages/core/src/llm-connections.ts:375currently returns'fake'for an unknown or legacyproviderType. That is a behavior change to make explicitly:backendKindOfwill throw on an unrecognized provider rather than silently resolving a connection onto a test backend. Callers that need a non-throwing answer use the readiness projection, which already reports "not a real connection" without needing a backend kind. This is called out here so the follow-up PR carries it under its own heading.Behavior change
A session or Automation persisted with
backend: 'fake'no longer runs the test backend. The composition registers an explicit refusal at that kind instead, throwing the canonicalNO_REAL_CONNECTION:fake_backenderror, whichparseNoRealConnectionErroralready turns into the copy both surfaces show for these rows: the task came from the retired local simulation, add a real model and start a new one.An earlier revision of this PR left no factory at all there. That was wrong: the Desktop composer does gate these rows (
sendBlockedreads a destructivesessionHealthNotice, raised byprojectSessionSendOutcomeforfake_backend), but that is a renderer gate, not an authority.maka runresumes an existing session through a readiness check that only inspects the connection catalog and never reads the session header's backend, andscheduled-task.ts:492still acceptsexecution.backend: 'fake'in a persisted template — both reach activation directly, and would have surfaced a bareNo backend factory registered for kind="fake".Rewriting the durable header to
'ai-sdk'on the read path was the alternative and is worse: it destroys the fact that drives the correct copy, and leaves a session that looks runnable while itsllmConnectionSlugstill points at nothing, so the failure resurfaces later and less specifically.Not in scope
apps/desktop/src/main/e2e-fixture*(the dev screenshot fixture and@maka/core/e2e-fixtureschema) still ships in the asar. It is guarded —resolveE2eFixturethrows whenapp.isPackaged— and removing it from the build graph means makingmain.tsload it dynamically, which is a desktop-startup refactor, not a packaging filter.apps/desktop/src/main/chat-readiness.ts'sassertSessionCanSendfake-rejection has no production caller (onlyerrorMessageis imported, bymain-window.ts). It is residue, not an active guard; it is not treated as one here and is left for the follow-up's cleanup.Part of #3211
Verification
Built
@maka/code-mode→@maka/core→@maka/storage→@maka/mcp→@maka/runtime→@maka/runtime-host→@maka/ui→@maka/desktopfrom clean.@maka/runtime-hostfull workspace suite: 979 passed, 0 failed (node --test --test-concurrency=4 "dist/**/*.test.js").@maka/runtimeaffected files: 237 passed, 0 failed (fake-backend,session-manager,runtime-continuation-crash,stream-graph-coordinator).npm run check:release: 30 passed, 0 failed, including the newtest-onlyfile-policy tests.npm run lint,npm run format, and@maka/desktop's four-configtypecheckall clean.send-message,slash-command-menu,streaming-remount,composer-inline-completion,parent-session-deletion.slash-command-menu.spec.ts:34("compacts the active session") drivesDesktopE2eBackend.compactHistory, so it proves the new E2E entry still reaches the real composition throughprimaryBackendFactory.Module-graph evidence for the packaging claim, from the built
dist:622 modules reachable from
execution-candidate-main.js, none of them test-only; the E2E entry is itself test-only, as expected. This is the assertion the new test makes.The retired-backend refusal has a red-to-green regression test (
a legacy fake-backend session is refused with the product reason, not a registry error): removing the registration makes it fail onNo backend factory registered.On "fails without it": the
release-cli-file-policytests do fail onmain— the predicate did not rejecttest-only/paths. The module-graph test is a forward guard rather than a red-to-green test: onmainthe same modules exist under different names, so the assertion it encodes ("the production entry cannot reach test-only material") only becomes expressible once the directory convention exists.Not run locally: a full CLI
npm packand a fullelectron-builderpack. Both are CI-side, and the point of the file-policy unit tests plus the module-graph test is to catch a regression without them.AI use
Tool(s) and scope: Claude Code (Opus) — investigated the issue against current
main, drafted the design above, wrote the implementation and the new tests, and ran the verification listed. The human contributor of record reviews the final diff and owns accuracy and provenance.Checklist
Does this PR entail a change in behavior?