Session rename on one device doesn't reach the account's other open clients #293

Closed
opened 2026-11-01 15:36:58 +00:00 by kayg · 13 comments
Owner

Owner field report, 2026-11-01, production (https://ssh.kayg.org; the build is this morning's dev, without #250).

Repro

  1. Open the canvas on device A and on device B, both signed in as the owner. Both tabs stay active and in the foreground.
  2. On A, rename the session on ssh (for example to arnotsrnaotiearsnoetars). A's window pill and session list update.
  3. B never shows the new name: not in its window pill, not in its session list, not after waiting. A refresh was not tried.

Expected: a rename reaches every open client of that account live, within the same budget as other inventory changes. The rename pins the name on every device.

Notes

  • The client rename path is apps/tracer/src/canvas/windows/session-ui.on-demand.ts:490, which calls inventory.rename(...).
  • Windows resync names from inventory changes (session-hooks.ts, lifecycle.ts:411).
  • Suspects: the server doesn't broadcast the rename as an inventory event to the account's other connections, or B's inventory subscription ignores name-only changes.
  • First check whether current dev (37bbda455) still reproduces this. Then add a two-client production-build Playwright test, failing first.
Owner field report, 2026-11-01, production (https://ssh.kayg.org; the build is this morning's dev, without #250). **Repro** 1. Open the canvas on device A and on device B, both signed in as the owner. Both tabs stay active and in the foreground. 2. On A, rename the session on `ssh` (for example to `arnotsrnaotiearsnoetars`). A's window pill and session list update. 3. B never shows the new name: not in its window pill, not in its session list, not after waiting. A refresh was not tried. **Expected:** a rename reaches every open client of that account live, within the same budget as other inventory changes. The rename pins the name on every device. **Notes** - The client rename path is `apps/tracer/src/canvas/windows/session-ui.on-demand.ts:490`, which calls `inventory.rename(...)`. - Windows resync names from inventory changes (`session-hooks.ts`, `lifecycle.ts:411`). - Suspects: the server doesn't broadcast the rename as an inventory event to the account's other connections, or B's inventory subscription ignores name-only changes. - First check whether current dev (37bbda455) still reproduces this. Then add a two-client production-build Playwright test, failing first.
Author
Owner

J71 started. Branch fix-rename-sync-293, base a26615c50 (current origin/dev). Plan: (1) reproduce with two same-account clients against a production build, with a third-account isolation assertion; (2) trace rename through inventory broadcast and client subscription, fix only the drop; (3) rerun browser regression, fast gate on dev-02, final x86 gate on compute01; commit and push this branch. Estimate: 90 minutes plus shared build-slot queue time. No deployment or merge.

J71 started. Branch `fix-rename-sync-293`, base `a26615c50` (current origin/dev). Plan: (1) reproduce with two same-account clients against a production build, with a third-account isolation assertion; (2) trace rename through inventory broadcast and client subscription, fix only the drop; (3) rerun browser regression, fast gate on dev-02, final x86 gate on compute01; commit and push this branch. Estimate: 90 minutes plus shared build-slot queue time. No deployment or merge.
Author
Owner

J71 baseline finding on current origin/dev a26615c50: the reported stale text no longer reproduces. The two-client production-build test receives the new name in B’s session list and window pill via canvas.layout.operation, but FAILS because B never gets the pin (.cl-sx-pin absent). Focused live-inventory test also fails: expected pinned=true, received false. The live rename already uses account-scoped canvas target updates, not terminal.sessions (which carries liveness IDs). Root cause of the remaining requirement failure: session targets carry name but no pin metadata, and setLayout updates name only. Minimal fix: carry an optional pin flag alongside the existing target name through the same scoped operation and hydrate the inventory/runtime model. No new event topic, polling, or transport. Evidence: dev-02 ~/.cache/codex-scratch/j71/before/session-rename-sync; log ~/worktrees/j71-before3.log.

J71 baseline finding on current origin/dev a26615c50: the reported stale text no longer reproduces. The two-client production-build test receives the new name in B’s session list and window pill via canvas.layout.operation, but FAILS because B never gets the pin (`.cl-sx-pin` absent). Focused live-inventory test also fails: expected pinned=true, received false. The live rename already uses account-scoped canvas target updates, not terminal.sessions (which carries liveness IDs). Root cause of the remaining requirement failure: session targets carry `name` but no pin metadata, and setLayout updates name only. Minimal fix: carry an optional pin flag alongside the existing target name through the same scoped operation and hydrate the inventory/runtime model. No new event topic, polling, or transport. Evidence: dev-02 ~/.cache/codex-scratch/j71/before/session-rename-sync; log ~/worktrees/j71-before3.log.
Author
Owner

J71 fix validation: production browser regression PASS (1 passed, 8.3s). B receives name + pin in 16ms; pin survives reload; the third account receives neither the rename nor a canvas mutation. Focused tests: 26 pass, 0 fail; tracer typecheck passes. Optional pinned metadata follows the existing account-scoped canvas target operation; runtime/inventory hydration keeps it across paints and reloads. Before/after screenshots attached. Fast gates pending on dev-02 and compute01; logs ~/worktrees/j71-fast.log on each machine. Exactly one target directory per machine: ~/worktrees/j71/target; will remove at finish.

J71 fix validation: production browser regression PASS (1 passed, 8.3s). B receives name + pin in 16ms; pin survives reload; the third account receives neither the rename nor a canvas mutation. Focused tests: 26 pass, 0 fail; tracer typecheck passes. Optional `pinned` metadata follows the existing account-scoped canvas target operation; runtime/inventory hydration keeps it across paints and reloads. Before/after screenshots attached. Fast gates pending on dev-02 and compute01; logs ~/worktrees/j71-fast.log on each machine. Exactly one target directory per machine: ~/worktrees/j71/target; will remove at finish.
Author
Owner

J71 crash-safety checkpoint: atomic implementation pushed, head bf6d4cd527 on fix-rename-sync-293. Production-build browser regression and 26 focused tests pass; broad fast gates are still running on dev-02 and compute01 (~/worktrees/j71-fast.log). Source is unchanged from those gate inputs. After the final gate: optimized-static browser proof, D243 size delta versus starting dev, restore source, and remove both job target dirs. No merge or deployment.

J71 crash-safety checkpoint: atomic implementation pushed, head bf6d4cd52700c6250199ee835b22ba7de355e667 on fix-rename-sync-293. Production-build browser regression and 26 focused tests pass; broad fast gates are still running on dev-02 and compute01 (~/worktrees/j71-fast.log). Source is unchanged from those gate inputs. After the final gate: optimized-static browser proof, D243 size delta versus starting dev, restore source, and remove both job target dirs. No merge or deployment.
Author
Owner

Gate finding: dev-02 workspace test bare_infraternal_is_the_overview_and_points_to_help fails at cli_envelope.rs:211, expected exit 0, got 5. The VM has a stopped user infraternal-preview.service; the CLI correctly reports component_down. The same binary returns 0 with XDG_CONFIG_HOME pointed at an empty private job directory. This is outside the rename path. Preserving the raw gate log, then proving the failure on unmodified starting dev and rerunning with private XDG test config. No repository CLI changes and no changes to the VM service.

Gate finding: dev-02 workspace test bare_infraternal_is_the_overview_and_points_to_help fails at cli_envelope.rs:211, expected exit 0, got 5. The VM has a stopped user infraternal-preview.service; the CLI correctly reports component_down. The same binary returns 0 with XDG_CONFIG_HOME pointed at an empty private job directory. This is outside the rename path. Preserving the raw gate log, then proving the failure on unmodified starting dev and rerunning with private XDG test config. No repository CLI changes and no changes to the VM service.
Author
Owner

J71 baseline gate proof: the exact CLI overview test also fails on unmodified starting origin/dev a26615c50 (expected exit 0, got 5; test exit 101). Log: dev-02 ~/.cache/codex-scratch/j71/baseline-cli.log. Source restored; full fast gate rerunning with a private empty XDG_CONFIG_HOME so the stopped VM preview service does not affect this fixture. Final compute01 gate remains running. No source changes since pushed bf6d4cd527.

J71 baseline gate proof: the exact CLI overview test also fails on unmodified starting origin/dev a26615c50 (expected exit 0, got 5; test exit 101). Log: dev-02 ~/.cache/codex-scratch/j71/baseline-cli.log. Source restored; full fast gate rerunning with a private empty XDG_CONFIG_HOME so the stopped VM preview service does not affect this fixture. Final compute01 gate remains running. No source changes since pushed bf6d4cd52700c6250199ee835b22ba7de355e667.
Author
Owner

J71 checkpoint: pushed head remains bf6d4cd527; no source changes. Both broad fast gates are progressing (compute01 has completed the workspace test step; dev-02 isolated run is in terminal tests). The raw dev-02 CLI fixture failure is proven on baseline and recorded above. Remaining: final static builds/frontend gate, optimized browser before/after and static size delta, attach logs, remove both owned target directories. Target paths remain ~/worktrees/j71/target on each VM.

J71 checkpoint: pushed head remains bf6d4cd52700c6250199ee835b22ba7de355e667; no source changes. Both broad fast gates are progressing (compute01 has completed the workspace test step; dev-02 isolated run is in terminal tests). The raw dev-02 CLI fixture failure is proven on baseline and recorded above. Remaining: final static builds/frontend gate, optimized browser before/after and static size delta, attach logs, remove both owned target directories. Target paths remain ~/worktrees/j71/target on each VM.
Author
Owner

J71 dev-02 isolated scripts/verify fast PASS (XDG_CONFIG_HOME is the private empty job directory). Source hashes match committed head bf6d4cd527. Full log attached. Target directory deleted and no owned runtime processes remain on dev-02. Final compute01 gate remains running.

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| static builds             | SKIP (x86)   |
+---------------------------+--------------+

39 pass
 0 fail
 59970 expect() calls
Ran 39 tests across 10 files. [2.34s]
FAST_GATE_EXIT=0
J71 dev-02 isolated scripts/verify fast PASS (XDG_CONFIG_HOME is the private empty job directory). Source hashes match committed head bf6d4cd52700c6250199ee835b22ba7de355e667. Full log attached. Target directory deleted and no owned runtime processes remain on dev-02. Final compute01 gate remains running. ```text +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | static builds | SKIP (x86) | +---------------------------+--------------+ 39 pass 0 fail 59970 expect() calls Ran 39 tests across 10 files. [2.34s] FAST_GATE_EXIT=0 ```
Author
Owner

J71 checkpoint: head remains bf6d4cd527, unchanged and pushed. Dev-02 isolated full fast gate passed (verbatim summary/full log attached above), its source hashes match and target is removed. Compute01 final fast gate passed workspace tests and is now on the second optimized static target; the first finished in 13m47s. Remaining estimate: about 20–30 minutes for final static/frontend results, optimized browser proof, baseline rebuild size deltas, evidence attachment and compute01 cleanup. No additional implementation work or scope expansion.

J71 checkpoint: head remains bf6d4cd52700c6250199ee835b22ba7de355e667, unchanged and pushed. Dev-02 isolated full fast gate passed (verbatim summary/full log attached above), its source hashes match and target is removed. Compute01 final fast gate passed workspace tests and is now on the second optimized static target; the first finished in 13m47s. Remaining estimate: about 20–30 minutes for final static/frontend results, optimized browser proof, baseline rebuild size deltas, evidence attachment and compute01 cleanup. No additional implementation work or scope expansion.
Author
Owner

J71 final compute01 scripts/verify fast PASS. Full log attached. Source unchanged from pushed head bf6d4cd527. Optimized before/after browser proof and baseline static size comparison are queued through build-slot (position 6); no implementation changes pending.

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+

39 pass
 0 fail
 59970 expect() calls
Ran 39 tests across 10 files. [6.67s]
J71 final compute01 scripts/verify fast PASS. Full log attached. Source unchanged from pushed head bf6d4cd52700c6250199ee835b22ba7de355e667. Optimized before/after browser proof and baseline static size comparison are queued through build-slot (position 6); no implementation changes pending. ```text +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | static builds | PASS | +---------------------------+--------------+ 39 pass 0 fail 59970 expect() calls Ran 39 tests across 10 files. [6.67s] ```
Author
Owner

J71 optimized comparison was admitted after ~24 minutes in the shared queue, but the browser could not launch: compute01 lacks Chromium host libraries (first missing libnspr4.so), before the test body ran. No product failure and no source changes. Private Chromium copy now loads with cached Nix libraries and matching loader; shared cache/system packages unchanged. Rerunning the comparison with that scoped runtime. Both full fast gates remain PASS, logs attached above. Head bf6d4cd527.

J71 optimized comparison was admitted after ~24 minutes in the shared queue, but the browser could not launch: compute01 lacks Chromium host libraries (first missing libnspr4.so), before the test body ran. No product failure and no source changes. Private Chromium copy now loads with cached Nix libraries and matching loader; shared cache/system packages unchanged. Rerunning the comparison with that scoped runtime. Both full fast gates remain PASS, logs attached above. Head bf6d4cd52700c6250199ee835b22ba7de355e667.
Author
Owner

J71 finished. Branch fix-rename-sync-293, head bf6d4cd52700c6250199ee835b22ba7de355e667 (pushed; no merge or deployment).

Current starting dev a26615c50 already propagates the name text through the account-scoped canvas layout stream. The reproducible remaining bug is loss of the manual-name pin: B sees the renamed text but no pin, including after reload. The minimal fix carries optional boolean pinned beside the existing target name and preserves it through serialization, reconciliation and inventory hydration. Same-name manual renames also pin the automatic name. No new event topic or polling.

Before/after against optimized static binaries and production frontend:

  • BEFORE: 1 failed, .cl-sx-pin absent on B; both name-text checks pass. BASELINE_BROWSER_EXIT=1.
  • AFTER: 1 passed (10.7s); name + pin observed on B in 91 ms; pin survives reload; third account receives no matching rename event and its canvas remains unchanged.
  • Initial dev-02 production frontend proof also failed first and passed after (8.3s; observed 16 ms). Focused tests: 26 pass, 0 fail.

Static sizes vs starting dev, identical build configuration:

target baseline bytes head bytes delta
x86_64 musl 21,642,320 21,642,416 +96
aarch64 musl 19,794,072 19,794,088 +16

Both full scripts/verify fast gates PASS: dev-02 with private empty XDG_CONFIG_HOME, compute01 with both static targets. The first raw dev-02 gate failed the CLI overview fixture because that VM has a stopped preview service; identical expected-0/actual-5 failure was proven on unmodified starting dev. No CLI or VM service changes. Full gate logs are attached to the earlier gate comments.

Final compute01 gate summary, verbatim:

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+

39 pass
 0 fail
 59970 expect() calls
Ran 39 tests across 10 files. [6.67s]

Source restored after baseline measurement; all 13 changed-file hashes match committed head on both VMs. Both owned target directories and the private compute01 browser copy removed; no owned test servers, browsers or runs remain.

How to test:

  1. Open two active canvases signed into the same account.
  2. Rename a session on A, including choosing its current automatic name.
  3. B updates its pill/list name and pin live; reload preserves the pin. A different account sees no rename.

Optimized proof log, before/after screenshots and baseline CLI failure log attached. Raw evidence also retained outside the checkout at ~/.cache/codex-scratch/j71/evidence on the control host and ~/.cache/codex-scratch/j71/{before-static,after-static} on compute01. No owner decision needed. Issue left open.

J71 finished. Branch `fix-rename-sync-293`, head `bf6d4cd52700c6250199ee835b22ba7de355e667` (pushed; no merge or deployment). Current starting dev `a26615c50` already propagates the name text through the account-scoped canvas layout stream. The reproducible remaining bug is loss of the manual-name pin: B sees the renamed text but no pin, including after reload. The minimal fix carries optional boolean `pinned` beside the existing target name and preserves it through serialization, reconciliation and inventory hydration. Same-name manual renames also pin the automatic name. No new event topic or polling. Before/after against optimized static binaries and production frontend: - BEFORE: `1 failed`, `.cl-sx-pin` absent on B; both name-text checks pass. `BASELINE_BROWSER_EXIT=1`. - AFTER: `1 passed (10.7s)`; name + pin observed on B in 91 ms; pin survives reload; third account receives no matching rename event and its canvas remains unchanged. - Initial dev-02 production frontend proof also failed first and passed after (8.3s; observed 16 ms). Focused tests: 26 pass, 0 fail. Static sizes vs starting dev, identical build configuration: | target | baseline bytes | head bytes | delta | |---|---:|---:|---:| | x86_64 musl | 21,642,320 | 21,642,416 | +96 | | aarch64 musl | 19,794,072 | 19,794,088 | +16 | Both full `scripts/verify fast` gates PASS: dev-02 with private empty XDG_CONFIG_HOME, compute01 with both static targets. The first raw dev-02 gate failed the CLI overview fixture because that VM has a stopped preview service; identical expected-0/actual-5 failure was proven on unmodified starting dev. No CLI or VM service changes. Full gate logs are attached to the earlier gate comments. Final compute01 gate summary, verbatim: ```text +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | static builds | PASS | +---------------------------+--------------+ 39 pass 0 fail 59970 expect() calls Ran 39 tests across 10 files. [6.67s] ``` Source restored after baseline measurement; all 13 changed-file hashes match committed head on both VMs. Both owned target directories and the private compute01 browser copy removed; no owned test servers, browsers or runs remain. How to test: 1. Open two active canvases signed into the same account. 2. Rename a session on A, including choosing its current automatic name. 3. B updates its pill/list name and pin live; reload preserves the pin. A different account sees no rename. Optimized proof log, before/after screenshots and baseline CLI failure log attached. Raw evidence also retained outside the checkout at `~/.cache/codex-scratch/j71/evidence` on the control host and `~/.cache/codex-scratch/j71/{before-static,after-static}` on compute01. No owner decision needed. Issue left open.
Author
Owner

J210 owner-authorized housekeeping: closing this completed scope because its work is merged on dev in 65d986191. Verified the merge commit is an ancestor of current origin/dev. The implementation reports and evidence remain in this issue.

J210 owner-authorized housekeeping: closing this completed scope because its work is merged on dev in [65d986191](https://git.kayg.org/kayg/infraternal/commit/65d9861914e8cd4a734d533d64ed1f4ea5c34119). Verified the merge commit is an ancestor of current origin/dev. The implementation reports and evidence remain in this issue.
kayg closed this issue 2026-11-04 17:04:41 +00:00
Sign in to join this conversation.
No labels
launch-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/infraternal#293
No description provided.