The pal keeps 文 and 受信 live under the shut socket the chrome holds them for #71
Labels
No labels
needs-info
needs-triage
ready-for-agent
ready-for-human
v2
wayfinder:grilling
wayfinder:map
wayfinder:prototype
wayfinder:research
wayfinder:task
wayfinder:ticket
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
aiko/denpa#71
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-07-31 by the cross-seam review of the
#62–#68batch, not by a failing test.What is true
#65gave the window chrome a rule: a shut socket holds the keys that cannot work under it.receiver-chrome.tsxderivessocketHeldfromfault.kind === 'socket-closed'and threads it intoDock,TransmissionBandandKeyRow, where 送信, ハンズフリー and 文 go disabled.PalBarnever got the rule. It carries the same 文 and 受信 keys and takes the same fault (pal-bar.tsx'sfault?: PalFaultKindprop), but 文 has nodisabledat all and 受信 isdisabled={micHeld}only. So in pet mode and in the Android overlay both keys stay pressable under the very socket the bar is drawing as 不通, and pressing 送信's equivalent raises the error#65was filed to stop.Both surfaces are deciding from the same fact.
two-hop.ts'spalFaultForreturns'local-down'exactly whenlocal.mark === 'down', andfault-spec.tsreturns'socket-closed'on the identical test. The disagreement is not about what is true; it is that only one surface was taught what to do about it.Why it is worth its own ticket
The gap is
#65's alone —pal-bar.tsx,pal-fault.tsandfault-spec.tswere untouched by that batch, and the two fault taxonomies predate it. What the batch added is reachability:#64wired なぜ tosetMode('window'), so the operator now walks from the pal, where the keys are live, straight into the window, where the same keys are dead under the same fault. Two surfaces disagreeing is a bug; two surfaces disagreeing with a button between them is one you will actually hit.The fix, and the one to avoid
One shared predicate over
TwoHopStatus— something shaped likestopsTransmission(fault)— consumed by both surfaces.Not a
socketHeldprop added toPalBarby hand. That is a third place deriving the same rule from the same fact, and the next fault kind added has to remember all three. The two fault vocabularies (ReceiverFaultKind = 'socket-closed'vsPalFaultKind = 'local-down') already cost#64a cross-walk in its own test to prove なぜ always has a screen behind it; a shared predicate is the thing that stops that list growing.Acceptance
TwoHopStatus, so they cannot drift again#65did for the chrome — her failing to answer is not the socket failingRelated
denpa#65,denpa#64,denpa#62,denpa#59Merged to
main.Fix.
stopsTransmission(status: TwoHopStatus)intwo-hop.tsis the one place the rule lives.use-receiver-chrome.tsreads it for the chrome,pal-container.tsxandoverlay-pal-container.tsxfor the two pals. The chrome no longer derivesfault.kind === 'socket-closed'— that was the second copy this issue named.5a answered deliberately, not by omission, the way
#65answered it for the chrome: with both hops reachable and a turn unanswered, the socket is fine and the send is legitimate. Her failing to answer is not the socket failing. CONNECTING is not a fault either — the reconnect backoff sits there for most of every retry.Acceptance, all five:
keys-under-shut-socket.test.tsxrenders the chrome, the desktop pal and the Android overlay from oneTwoHopStatus, so a change that holds one and not the others fails whichever one it isOne thing this issue did not ask for, found by review.
pal-composer-panel.tsxheld the 文 key but not the composer's own 送信, so a composer already open when the socket dropped could still send — raising the exact error#65was filed to stop. The send and its ⏎ are held now. The panel stays open and the draft is kept: closing it would throw away what the operator typed, and 不通 on the bar already says why it will not go.Known trade.
transmissionHeldis a prop that must agree with thefaultprop beside it; passingTwoHopStatusinto each surface would make disagreement structurally impossible. Kept as a boolean because the chrome is otherwise a pure render, there are three real call sites, and all three are pinned by the cross-surface test. Reviewed and flagged as a risk rather than a bug.Gate: 66 test files, 878 tests,
npm run typecheckexits 0.