Skip to content

The switch-failure work of #2637 is half covered: a guard that reddens a comment, and a catch no test walks #2771

Closed

Description

@pm25coder

Two defects in the test coverage around switching sessions, both found by driving the code in both directions rather than by reading it. They share one subject: the switch path has two entry points, and only one of them is measured.

1. A guard reddens a comment

emrg/gui/test/switch-session.test.js:42-50 asserts that the emrg:switchSession handler in emrg/gui/main.js does not refuse on ownStream:

const region = handlerRegion(source, "switchSession");
assert.ok(region.length > 500, `the switch region must be the real handler, got ${region.length} chars`);
assert.ok(
  !region.includes("ownStream"),
  "the switch handler must not refuse on ownStream any more: switching leaves the previous " +
    "connection open, so a running turn is unaffected by moving the window to another session",
);

The region is a slice of the handler's text and the predicate is a plain substring test.

Measured, mutating main.js and then restoring it byte-exact (blob 823311d3cd5b7f8ff6438d311240c9dac1948667):

  • control: 4 passed, rc 0.
  • arm: two comment lines inserted inside the handler, naming the field and reading nothing from it:
      // ARM22: the refusal removed in issue #2637 read connManager.get(prevSid).ownStream before
      // opening any connection. This line names that field and reads nothing from it.

→ rc 1, not ok 1, message the switch handler must not refuse on ownStream any more …; the other three tests stay green.

So a change that adds a comment explaining why the old refusal was removed is rejected, and the failure message accuses code that refuses on nothing and reads nothing. The property the guard states — the handler does not read or refuse on that field — is not what it measures: it measures whether the characters ownStream occur anywhere in a slice of the file.

The predicate also cannot tell the field from a longer name that begins with it. Measured on the strings themselves:

"this.ownStreamRequestId = rid".includes("ownStream") -> true
"conn._releaseOwnStream()".includes("ownStream")      -> false

(the release helper spells it OwnStream, so that one is not a collision).

2. A catch no test walks

emrg/gui/renderer/src/components/Shell.tsx:326 — selectProjectSession — is one of two functions that surface a rejected switch:

    try {
      await b.switchSession({ sessionId: sid });
    } catch (e) {
      setSwitchError((e as Error)?.message ?? String(e));
      return;
    }

The other is openTaskSession (Shell.tsx:344), reached from the task panel's "Open session", and it is covered twice in Shell.test.tsx — :688 and :704 — each asserting the banner's text and that the panel did not move.

selectProjectSession is reached from the agents panel's project-sessions sub-view: agent-session-row → onSelectAgentSession → Shell.tsx:830. Three of the four links are tested: WorkspaceView.test.tsx:287-291 proves the row's click calls the prop with (project, sid), and Shell.test.tsx:755-762 proves Shell renders the row from listAgentSessions. But no test clicks that row, so the catch is never executed.

Measured, mutating Shell.tsx and then restoring it byte-exact (blob a9a8d828c05e26d429dbfce144ab916d81c38130):

  • control: Shell.test.tsx — 1 file passed, 52 tests passed, 12.95s.
  • arm: the try/catch removed, leaving a bare await b.switchSession({ sessionId: sid }); → still 52 passed, rc 0.

Deleting the branch that shows the failure to the person who clicked leaves every test green.

What the fix must satisfy

  1. The guard measures the property it states, in both directions. With comments (// and block) removed from the region before matching: a comment naming the field must be silent, and the real refusal put back into the handler must still be red, naming the file and the assertion. Stripping comments alone does not settle the ownStreamRequestId collision — matching the field as a whole token in code (rather than as a substring of the text) does, and the arm above must still redden after it is matched that way.
  2. A test drives agent-session-row. With switchSession rejecting, the banner must appear carrying the rejection's message, and the view must not have moved. Its control arm: deleting that catch must turn the new test red — the arm that stays green today.

Both changes belong in one PR: they are one subject, and the second fixes the shape the first has to leave room for.

Activity

  1. pm25coder commented on Nov 9, 2026

    @pm25coder
    CollaboratorAuthor

    Handled by #2773

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions