mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 06:59:42 +02:00
fix(session): persist an exit retraction, and let tests reach the watcher
Ten findings from a two-model review of this branch. Both reviewers cleared the
detection logic itself; everything here is a gap around it.
A route that starts a command in a pane now PERSISTS as well as broadcasts.
`/interactive` and `/shell` did neither before, and the pane-exit watcher cannot
cover for them: its next tick finds `paneExit` already cleared in memory,
reports no change and writes nothing, so `state.json` kept saying the agent had
exited for as long as the session stayed quiet. Nothing reads that record for a
decision yet, which is exactly why it had to be fixed now — part 2 is designed
to read it. The `clearPaneExitForNewPane()` docstring claimed its callers
already persisted; that claim was false for these two, and now says what the
caller owes instead.
The watcher's four guards were unreachable by any test. `refreshPaneExits()`
opened with `if (IS_TEST_MODE) return;`, so the read gate, the in-flight
suppression, the generation counter and the empty-read rule could each be
deleted with the whole suite green. The tmux call moves into `readPaneRows()`,
which a test subclass overrides — the shape `runRemoteReconnectTick` already
uses in this file for the same reason — and the test-mode gate moves with it, so
what a test cannot do is spawn a process rather than exercise the bookkeeping.
Each of the four guards now has a test that fails when it is deleted.
The muted status dot turned out to be a specificity fight on three surfaces, not
two. `.tab-status.error` was not excluded, so a session whose agent exited and
whose PTY-exit breaker then tripped lost its red dot to the mute — the state the
browser answers with a "restart it?" confirm, and a needs-you colour by the same
argument that protects the two alert classes. And mobile.css gives a `busy` dot
a 9px size and a green glow with `!important`, while `status` stays `busy` for a
pane whose agent died mid-turn, so a phone rendered a grey dot still wearing the
green halo beside a badge reading "exited". Both measured against the real
stylesheets, both now excluded, and the CSS test reads mobile.css too instead of
being structurally blind to half the problem.
Six comments said things that were not true. Two named the stats collector as
what replaces a restored reading, which is the opposite of the design. The
interval constant argued that 2000 ms keeps a read inside a tick, when the
5000 ms exec timeout means it cannot — which is why the in-flight guard exists.
`MuxSession.discovered` did not say the flag is permanent, though `saveSessions()`
serializes it. The empty-read docstring claimed a distinction that `|| true`
makes impossible. The invariants doc promised more than its drift test delivers.
And CLAUDE.md had no pointer at all, leaving its two hardest prohibitions
("never set `status: 'error'`", "never null the pid") only in the file it is
meant to route people to.
Refs Ark0N/Codeman#446.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
90fd0a5a15
commit
9c286eeddf
+10
-5
@@ -895,8 +895,9 @@ export class Session extends EventEmitter {
|
||||
// because the scoping reads `_remote`, `_docker` and the mux fields, all of
|
||||
// which are set by now. It is a claim about a pane this process has not
|
||||
// looked at yet, so every path that starts or re-attaches a pane drops it
|
||||
// (see `_setupOrAttachMuxSession`) and the stats tick replaces it with a
|
||||
// first-hand reading.
|
||||
// (see `_setupOrAttachMuxSession`) and the pane-exit watcher's own tick
|
||||
// replaces it with a first-hand reading. NOT the stats collector, which a
|
||||
// browser panel arms and disarms — see `startPaneExitWatcher`.
|
||||
this.setPaneExit(config.paneExit);
|
||||
// Never self-parent: a session pointing at itself would draw a zero-length
|
||||
// lineage arc under its own tab. Only reachable via the recovery path, where
|
||||
@@ -1156,9 +1157,13 @@ export class Session extends EventEmitter {
|
||||
* Every path that starts or relaunches a command in the pane calls it, and
|
||||
* the mux half also invalidates a pane read already in flight.
|
||||
*
|
||||
* It does not persist or broadcast by itself. Each caller is already followed
|
||||
* by the route's or recovery's own persist, and the pane-exit watcher would
|
||||
* reach the same answer within one interval regardless.
|
||||
* It does not persist or broadcast by itself; the caller owns both. ⚠ That
|
||||
* caller MUST persist, and the pane-exit watcher is not a fallback for it:
|
||||
* the watcher's next tick reads UNKNOWN, finds this field already cleared,
|
||||
* reports no change and therefore writes nothing, so a caller that only
|
||||
* broadcasts leaves `state.json` saying the agent exited for as long as the
|
||||
* session stays quiet. `/interactive` and `/shell` did exactly that until
|
||||
* Ark0N/Codeman#446 review; both now persist on their success path.
|
||||
*/
|
||||
private clearPaneExitForNewPane(): void {
|
||||
this.setPaneExit(undefined);
|
||||
|
||||
Reference in New Issue
Block a user