feat: typed command results, batch 2 (home/back/rotate/app-switcher) — Phase 2#934
Merged
Merged
Conversation
…— Phase 2
Wire four more commands into the CommandResultMap spine, narrowing their public
client return types from the open DaemonResponseData bag to closed shapes.
Each shape is grounded in a re-read of the dispatch handler's literal return
(src/core/dispatch.ts DISPATCH_HANDLERS) — a fixed `action` discriminant plus
the always-present successText `message`:
- home -> { action: 'home'; message }
- back -> { action: 'back'; mode: BackMode; message }
- rotate -> { action: 'rotate'; orientation: DeviceRotation; message }
- app-switcher -> { action: 'app-switcher'; message }
The handlers spread nothing else, so the shapes are closed (consistent with the
viewport contract, the generic-dispatch Android dialog-recovery `warning`
annotation is intentionally not part of the contract). The result types move
from the client-types.ts mirror into a new src/contracts/navigation.ts; the
now-unused CommandActionResult helper is deleted. Public export names are
preserved (re-exported via client-types.ts -> index.ts), so no API break.
The exact-equality parity test pins CommandResult<name> === the contract type
for all ten migrated commands; the public-root export test gains back/rotate
samples.
Verified: tsc --noEmit, oxfmt + oxlint --deny-warnings, fallow audit clean,
Layering Guard empty, 791 tests across core/contracts/client/commands pass
(the lone failure was the known-flaky daemon-client mid-stream-abort test, which
passes in isolation).
Size Report
Startup median (7 runs, lower is better):
Top changed chunks: no changes in the largest emitted chunks. |
Member
Author
|
Reviewed current head 6fcae4a. I checked the #913 typed-results context, the diff, the dispatch returns for back/home/rotate/app-switcher, and daemon routing/Android dialog recovery behavior. No actionable blockers found: public export names are preserved, back/home/rotate map to fixed generic dispatch returns, app-switcher has no daemon-route traits that would add Android dialog warnings, and all 21 checks are green. Residual risk is type-surface only; runtime behavior is unchanged. Marking ready-for-human. |
|
This was referenced Jun 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Phase 2 (typed results) batch 2: wire four more commands into the
CommandResultMapspine, narrowing their public client return types from the openDaemonResponseDatabag to closed shapes. Continues #913 (batch 1: boot/shutdown/viewport).Shapes (each grounded in the handler's literal return)
Re-read of
src/core/dispatch.tsDISPATCH_HANDLERS— a fixedactiondiscriminant plus the always-presentsuccessTextmessage:home{ action: 'home'; message }back{ action: 'back'; mode: BackMode; message }rotate{ action: 'rotate'; orientation: DeviceRotation; message }app-switcher{ action: 'app-switcher'; message }The handlers spread nothing else, so the shapes are closed. Consistent with the
viewportcontract, the generic-dispatch Android dialog-recoverywarningannotation is intentionally not part of the contract (it's a rare runtime annotation, not the command's result shape).Mechanics (same template as batch 1)
src/contracts/navigation.tsholds the four closed types (moved out of theclient-types.tsmirror); the now-unusedCommandActionResulthelper is deleted.CommandResultMapgainshome/back/rotate/'app-switcher'; the client methods returnCommandResult<'name'>.HomeCommandResult, … re-exported viaclient-types.ts→index.ts), so no API break.CommandResult<name>=== its contract type for all 10 migrated commands; the public-root export test gainsback/rotatesamples.Why these four / not the rest yet
clipboardis also closeable (a discriminated union) but its CLI formatter passes the result straight into aRecord<string, unknown>param, so it needs a careful follow-up.keyboard/alert/app-state/waitare genuinely platform-/action-conditional (open) — they stay as the existing mirror until modeled as unions in later batches. This keeps each batch grounded and low-risk, per the plan's per-command discipline.Verification
tsc --noEmitexit 0oxfmt+oxlint --deny-warningscleanfallow audit --base origin/maincleanvitestcore/contracts/client/commands → 104 files / 791 tests pass (the one failure was the known-flakydaemon-clientmid-stream-abort test — passes 30/30 in isolation, unrelated to types)Remaining for Phase 2
~50 commands still on the
Recorddefault (most genuinely dynamic and deliberately left open per the spine's doctrine);clipboardnext; then the platform-union commands; then (b) theTypedErrorgraft and (c) deleting theclient-types.tsresult-type mirror once enough is migrated.