rm review notes

This commit is contained in:
Jinjing
2026-08-02 17:20:35 -07:00
parent 0da6075c32
commit 92c18ccba6
57 changed files with 0 additions and 3619 deletions
@@ -1,263 +0,0 @@
# LLM Counsel Report — stop-keep-reconnecting-to-remote @ 0ae9174408
- Run: `20260802-121457`
- Base: `0ae91744086a3195e9a92e585dae6d814bedf808` (equals HEAD — **all work is uncommitted**, including 2 untracked budget files)
- Seats: grok | codex gpt-5.6-sol high | claude opus high | claude fable high
- Date: 2026-08-02T19:31:36Z
- Mandate: **0 functional or performance regressions**
## Executive verdict
**Do not ship as-is.** Four independent reviewers and a coordinator spot-check agree this branch does **not** deliver “zero functional or performance regression,” and it only **partially** delivers the branch claim (stop endless SSH auto-reconnect).
The core idea is sound and well-tested for the **desktop Electron IPC happy path after a live connection drops**: a **target-scoped** 60s wall-clock budget survives `SshConnectionManager` connection replacement, parks zero-cost on exhausted auto connects, and no longer re-arms on pane remount when `initiator: 'auto'` reaches main.
But multi-seat agreement (plus code verification) shows **merge-blocking functional regressions and claim holes**:
1. **Paired/web runtime RPC drops `initiator`**, so auto remounts re-arm the budget as `'user'` and can restart connect storms (claim falsified off pure desktop IPC).
2. **Cold / never-connected targets never open the budget window** — only `scheduleReconnect` opens it — so remount-driven dead-host storms remain unbounded in the most common “host was never up this session” case.
3. **60s wall-clock vs 30s connect timeouts ≈ ~2 black-hole retries**, then a **pause that never self-expires** until a human clicks Connect — this is a deliberate policy shift, but it is a large functional change vs the prior 9-step ladder and breaks unattended recovery (automations, overnight sleep, host reboots >60s).
4. **Sleep/wake**: budget burns wall-clock during suspend, and resume hard-skips exhausted targets — undoing #7773-style wake recovery when the outage brackets sleep.
**Ship risk: High.** Recommend fix consensus High items before merge; treat parameter/policy (60s + never-expire) as an explicit product decision if retained.
## Consensus findings
### C1 — High — Runtime RPC / web preload drops `initiator` → auto reconnect re-arms budget
| | |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Severity** | High (functionality / claim falsified for paired clients) |
| **Files** | `src/renderer/src/web/web-preload-api.ts:3238-3245`, `src/main/runtime/rpc/methods/ssh.ts:11-37`, `src/main/ipc/ssh.ts:99-104,982-993` |
| **Agreeing seats** | grok, codex, claude-fable (all High); claude-opus noted related path but under-weighted web adapter vs desktop |
| **Coordinator** | **Verified.** Web `connect` sends only `{ targetId }`; RPC schema is `{ targetId }` only; `connectRegisteredSshTarget(targetId)` omits initiator → defaults to `'user'` → `sshAutoReconnectBudget.reset`. |
**Impact:** Shared renderer paths that correctly pass `initiator: 'auto'` (pane remount, automation, startup) still re-arm the budget when they run over web/runtime RPC. Desktop park can be overwritten by a paired client’s full connect storm.
**Smallest fix:** Plumb `initiator` through RPC schema + web preload + `connectRegisteredSshTarget`; default only true user UI to `'user'`.
---
### C2 — High — Initial / cold `connect()` never opens the budget window
| | |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Severity** | High (claim only half-delivered; endless reconnect remains) |
| **Files** | `src/main/ssh/ssh-connection.ts:605-656` (`connect`), `:1287` (only production `deadlineFor`), `src/main/ipc/ssh.ts:991-1008` |
| **Agreeing seats** | All four (High) |
| **Coordinator** | **Verified.** `deadlineFor` is only called from `scheduleReconnect`. Failed initial `connect()` throws and never opens a window; `isExhausted` stays false forever for never-connected targets. IPC park tests hand-synthesize the window with `deadlineFor(..., now - budget)`. |
**Impact:** Dead host from app start / restored tab never connected: each auto remount can still run up to `INITIAL_RETRY_ATTEMPTS=5` × `CONNECT_TIMEOUT_MS=30s` (~160s SSH work per call), forever.
**Smallest fix:** Open the window on first `'auto'` admission in `connectTarget` (and/or on terminal failure of `connect()`), then enforce `isExhausted` on subsequent auto connects.
---
### C3 — High — 60s budget ≈ ~2 timeout attempts; pause never self-heals
| | |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Severity** | High (functional behavior change / recovery regression) |
| **Files** | `src/main/ssh/ssh-auto-reconnect-budget.ts:10,34-37`, `src/main/ssh/ssh-connection-utils.ts:35-38`, `src/main/ssh/ssh-connection.ts:1277-1307` |
| **Agreeing seats** | grok, claude-opus, claude-fable (High); codex focused on related flap/boundary but not this product recovery frame |
| **Coordinator** | **Verified.** `CONNECT_TIMEOUT_MS=30_000`, `AUTO_RECONNECT_BUDGET_MS=60_000`, ladder delays sum to 103s. Black-hole trace ≈ 2 attempts then permanent pause. Test suite pins pause does not expire. |
**Impact:** Host reboot / VPN / Wi-Fi flap >60s permanently parks the target until a human clicks Connect. Unattended automations (`useAutomationDispatchEvents` with `initiator: 'auto'`) stay skipped forever after one blip. Prior ladder allowed 9 failed handshakes.
**Note: If product explicitly wants “stop after ~1 minute and require manual Connect,” document it -> yes let's go for this one.** Under the mandate **0 functional regression**, this is still a regression vs prior recovery behavior.
**Smallest fix options (pick product intent):** (a) raise budget ≥ full ladder (~5+ min), and/or (b) slow self-heal (re-open window every N minutes / on network or resume), and/or (c) pin attempt-count tests so future timeout bumps don’t silently go to 1 attempt.
---
### C4 — High — Sleep/wake: wall-clock budget + resume hard-skip undoes wake recovery
| | |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Severity** | High (functionality regression vs #7773 wake path) |
| **Files** | `src/main/ssh/ssh-auto-reconnect-budget.ts:34-37`, `src/main/ipc/ssh.ts:576-580`, `src/main/ssh/ssh-connection.ts:1283-1285` |
| **Agreeing seats** | claude-opus, claude-fable (High); grok framed resume skip as holding; codex did not elevate |
| **Coordinator** | **Verified.** `isExhausted` uses raw `Date.now()`; resume handler `continue`s when exhausted and never runs `isRelayLinkAliveAfterResume` / `manager.reconnect`. |
**Impact:** Outage starts → window opens → laptop sleeps → wakes on a healthy network: budget already exhausted from sleep time; resume path skips; SSH workspaces stay parked needing manual Connect.
**Smallest fix:** On `powerMonitor` resume, re-open or reset budget for targets (or measure only awake/active retry time) before the liveness probe.
---
### C5 — Medium — Successful handshake immediately resets budget (flap storms unbounded)
| | |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Severity** | Medium (claim incompleteness; not a pure new regression vs old ladder) |
| **Files** | `src/main/ssh/ssh-connection.ts:615-620,1310-1322`; contrast `SshReconnectLadder` / `STABLE_CONNECTION_MS` |
| **Agreeing seats** | grok, codex (High severity framing); peers noted pre-branch ladder also never stopped flaps |
| **Coordinator** | **Verified reset-on-handshake.** Severity demoted to Medium: old ladder already continued flaps; this is incomplete bounding of the branch claim, not a newly introduced endless loop vs main. |
**Smallest fix:** Reset budget only after stable connection (align with `STABLE_CONNECTION_MS`), not on every handshake.
---
### C6 — Medium — Stale exhausted window survives clean Disconnect / dispose
| | |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| **Severity** | Medium |
| **Files** | `disconnect` / removal paths; budget only reset on success handshake or `'user'` connect |
| **Agreeing seats** | claude-fable (F4); peers elevated as unique strong finding |
| **Coordinator** | **Verified directionally.** Clean disconnect does not clear the map entry; later auto connect can park with “host unreachable” without probing. |
**Smallest fix:** `sshAutoReconnectBudget.reset(targetId)` on explicit disconnect / removeTarget / intentional user stop.
---
### C7 — Medium — Parked state is broadcast-only; UI may not show pause message
| | |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Severity** | Medium (UX / state consistency) |
| **Files** | `src/main/ipc/ssh.ts` early park return + `broadcastSshState`; `getPublicSshState` / overlay copy paths |
| **Agreeing seats** | claude-opus (M2), grok/fable partial |
| **Coordinator** | **Plausible / partially verified.** Park returns a synthetic state without writing into connection manager; later `getState` can disagree. Overlay message path needs product confirmation. |
---
### C8 — Low/Medium — Deadline clamp can start one attempt at exact exhaustion
| | |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| **Severity** | Low–Medium (one tail attempt, not a loop) |
| **Files** | `src/main/ssh/ssh-connection.ts:1280-1307` |
| **Agreeing seats** | codex (Medium); others: tail attempt / no loop |
| **Coordinator** | **Verified.** Timer callback does not re-check `isExhausted`; can dial after nominal deadline for up to one connect timeout. |
**Smallest fix:** Re-check `isExhausted` at the start of `runReconnectAttempt` / timer fire.
## High-signal unique findings
| Finding | Seat | Status |
| -------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----------------------------------------------------------------------------------------------- |
| User Connect `reset()`s **before** success → failed user Connect leaves no open window → next auto remount unbounded again | grok (scenario 2B) | **Verified** in code (`reset` on initiator user before attempt). Important interaction with C2. |
| Near-exhaustion auto connect can still enter full initial-retry loop | grok | Related to C2/C8; credible |
| `SSH_CONNECT_FAILED_STATUSES` gate may newly abort PTY reattach on relay-error races | claude-opus | **Unverified** — needs targeted test; mark residual |
| Paired storm overwrites desktop parked broadcast UI | claude-fable | Credible second-order of C1 |
| Reset Relay / non-Connect recovery actions don’t re-arm | claude-fable | Credible product gap |
## Performance surface
**Checked:** reconnect timers, connect retry loops, IPC short-circuit, relay-lost backoff (separate), renderer remount / automation call sites, power resume.
| Agreed | Residual |
| ----------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| Desktop park path is zero-cost once window exhausted and `initiator: 'auto'` reaches main | Web/RPC path undoes that (C1) → full connect cost storms |
| Cold path still allows multi-minute SSH work per remount (C2) | Hot path: flap handshake resets keep churn unbounded (C5) |
| 60s cap reduces ladder work after drop (good for dead-host CPU/network) | But only after drop; initial connect path not capped |
**No multi-seat evidence of render-loop or unbounded buffer regressions** from this diff. Performance risk is **SSH reconnect storms**, not UI frame cost.
## Functionality / regression surface
| Agreed breaks / claim holes | Disputed / demoted |
| -------------------------------------------- | ---------------------------------------------------------- |
| C1 web/RPC re-arm | Flap reset severity (claim gap vs new regression) |
| C2 cold connect unbounded | Deadline clamp as High (only one tail attempt) |
| C3 permanent pause after ~2 timeout attempts | claude-opus “park healthy live session” overstated (peers) |
| C4 sleep/wake recovery regression | |
| Automation permanently skipped after park | |
**Residual gaps:** no long-running integration against real sleep/wake; no paired-client e2e for initiator; vitest for renderer/main passed on some seats (opus/fable: 181 main SSH + 538 pty tests) but not re-run by coordinator.
## Peer ratings (blind)
**Peer ratings were blind (anonymized labels A–D).** Mapping was coordinator-only:
| Label | Seat |
| ----- | ------------ |
| A | grok |
| B | claude-opus |
| C | codex |
| D | claude-fable |
### Aggregate mean scores (unblinded)
Overall = mean of (evidence, regression_catch, false_positive_risk, actionability).
`false_positive_risk`: **5 = trustworthy / low FP**.
| Seat | evidence | regression_catch | false_positive_risk | actionability | **overall** |
| ---------------- | --------: | ----------------: | -------------------: | -------------: | -----------: |
| **claude-fable** | 5.00 | 4.75 | 4.50 | 4.75 | **4.75** |
| **claude-opus** | 5.00 | 4.75 | 4.00 | 5.00 | **4.69** |
| **grok** | 5.00 | 4.25 | 4.50 | 4.50 | **4.56** |
| **codex** | 4.50 | 3.25 | 4.25 | 3.25 | **3.81** |
### Trust callouts
- **Most trusted this run:** claude-fable (tight FP discipline, C1 second-order UI overwrite, F4 disconnect lifecycle) and claude-opus (deepest recovery regressions: sleep/wake, 60s math, permanent pause).
- **Strong evidence, slightly narrower product frame:** grok (best on user-Connect-before-success hole; missed sleep/wake as High).
- **Most rigorous per-finding but narrowest coverage / least actionability:** codex (excellent C1 test pin + deadline clamp; missed sleep/wake and permanent-pause product framing; no fix directions).
## Seat scorecards
| Seat | Strengths | Blind spots | Trust weight |
| ---------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------ |
| **grok** | Concrete code cites; user-Connect reset-before-success; web/RPC + cold path | Missed sleep/wake as High; some severity on pre-existing flaps | High |
| **codex** | Sharpest C1 evidence (existing web test pins lossy payload); deadline clamp; flap vs STABLE_CONNECTION_MS | Narrow (4 findings); under-weights product recovery / never-expire; no fix directions; couldn’t run vitest in env | Medium |
| **claude-opus** | Best timelines; sleep/wake; 60s≈2 attempts; automation unattended; ran full test gates | Under-weighted live web adapter (treated as more latent); a few Medium edges slightly overstated | High |
| **claude-fable** | Claim falsified for paired clients; disconnect stale window; gates + typecheck; clear fix directions | Missed overlay dropping pause message; some future-caller speculation | Highest |
## Recommended next actions
Ordered smallest-first; **no drive-by refactors**.
1. **Plumb `initiator` end-to-end** through web preload + runtime RPC + `connectRegisteredSshTarget` (fixes C1).
2. **Open budget window on first auto connect / failed cold connect** so remount storms actually stop (fixes C2; interacts with user-reset-before-success).
3. **Product decision on 60s + never-expire** (C3): either raise budget / add slow self-heal, or document intentional permanent park + manual Connect; pin attempt-count tests either way.
4. **Resume path:** re-arm or extend budget on wake before #7773 probe (fixes C4).
5. **Reset budget on explicit Disconnect / removeTarget** (fixes C6).
6. **Re-check exhausted at timer fire / `runReconnectAttempt`** (fixes C8).
7. Optional: reset budget only after stable connection (C5); make park state authoritative + show pause message (C7).
**Do not merge** until at least (1)+(2) land if the claim is “stop keep reconnecting.” Treat (3)+(4) as required for **zero functional regression** under the counsel mandate.
## Artifacts
| Path | Contents |
| ------------------------------------------------------------------- | ---------------------------------------------------------- |
| `.llm-counsel/20260802-121457/COUNSEL-REPORT.md` | This report |
| `.llm-counsel/20260802-121457/reviews/` | `grok.md`, `codex.md`, `claude-opus.md`, `claude-fable.md` |
| `.llm-counsel/20260802-121457/reviews-blind/` | Anonymized A–D copies |
| `.llm-counsel/20260802-121457/ratings-blind/` | Raw peer ratings (letter keys) |
| `.llm-counsel/20260802-121457/ratings/` | Coordinator-unblinded rating copies |
| `.llm-counsel/20260802-121457/blind-map.json` | Coordinator-only label→seat map |
| `.llm-counsel/20260802-121457/base.txt` / `head.txt` / `branch.txt` | Scope pins |
-1
View File
@@ -1 +0,0 @@
0ae91744086a3195e9a92e585dae6d814bedf808
@@ -1,14 +0,0 @@
{
"label_to_seat": {
"A": "grok",
"C": "codex",
"B": "claude-opus",
"D": "claude-fable"
},
"seat_to_label": {
"grok": "A",
"codex": "C",
"claude-opus": "B",
"claude-fable": "D"
}
}
-1
View File
@@ -1 +0,0 @@
stop-keep-reconnecting-to-remote
@@ -1,77 +0,0 @@
{
"id": "7ace5846-b3c1-4711-a9fb-8d14b7301422",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_12ee9e0d2ed9",
"messages": [
{
"id": "msg_c8c0e04e2632",
"run_id": "run_bd00c31afa11",
"from_handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_1c70db5688a7\",\"dispatchId\":\"ctx_52a53956018e\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6774,
"created_at": "2026-08-02T19:16:25Z",
"delivered_at": null,
"sender_pane_key": "02ff22b9-11e8-46c0-8481-ca669d3e74f8:c2add700-f7e0-48a3-beb8-0db87c8e4a08",
"delivery_contract": "current_delivery"
},
{
"id": "msg_809457e3f376",
"run_id": "run_bd00c31afa11",
"from_handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_84cc9b5d5f7d\",\"dispatchId\":\"ctx_7a4145086c54\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6776,
"created_at": "2026-08-02T19:16:44Z",
"delivered_at": null,
"sender_pane_key": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d:0f751890-e4e0-4c87-b1b3-20311379a42a",
"delivery_contract": "current_delivery"
},
{
"id": "msg_a09626e4945e",
"run_id": "run_bd00c31afa11",
"from_handle": "term_de79d121-b74d-45e5-be1c-448261d04d6f",
"to_handle": "run:run_bd00c31afa11",
"subject": "Review complete: 7 findings, top High",
"body": "Reviewed the SSH auto-reconnect budget branch (new budget module + connection/IPC/preload/renderer wiring) for functional and performance regressions. Found 7 proven issues: High—web/RPC always re-arms budget by dropping initiator; High—budget only opens post-drop so cold and post-user-fail auto remounts stay unbounded; High—60s wall-clock with 30s timeouts cuts retries to ~2 vs prior 9/multi-minute ladder. Full report at the path below; no production code edited.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_2cda5d06c526\",\"dispatchId\":\"ctx_12af4bb5bd4c\",\"outcome\":\"succeeded\",\"reportPath\":\".llm-counsel/20260802-121457/reviews/grok.md\"}",
"read": 0,
"sequence": 6781,
"created_at": "2026-08-02T19:21:32Z",
"delivered_at": null,
"sender_pane_key": "33259e5a-eaee-48fc-a94f-e5b68a3bbada:5931d416-1958-4ea3-89f2-a84541215773",
"delivery_contract": "current_delivery"
}
],
"count": 3,
"replayed": true,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "c243cc91-3f83-43c2-89ac-e76199d79653",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,77 +0,0 @@
{
"id": "852a5163-cd32-417d-8506-d8624f4270c1",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_12ee9e0d2ed9",
"messages": [
{
"id": "msg_c8c0e04e2632",
"run_id": "run_bd00c31afa11",
"from_handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_1c70db5688a7\",\"dispatchId\":\"ctx_52a53956018e\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6774,
"created_at": "2026-08-02T19:16:25Z",
"delivered_at": null,
"sender_pane_key": "02ff22b9-11e8-46c0-8481-ca669d3e74f8:c2add700-f7e0-48a3-beb8-0db87c8e4a08",
"delivery_contract": "current_delivery"
},
{
"id": "msg_809457e3f376",
"run_id": "run_bd00c31afa11",
"from_handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_84cc9b5d5f7d\",\"dispatchId\":\"ctx_7a4145086c54\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6776,
"created_at": "2026-08-02T19:16:44Z",
"delivered_at": null,
"sender_pane_key": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d:0f751890-e4e0-4c87-b1b3-20311379a42a",
"delivery_contract": "current_delivery"
},
{
"id": "msg_a09626e4945e",
"run_id": "run_bd00c31afa11",
"from_handle": "term_de79d121-b74d-45e5-be1c-448261d04d6f",
"to_handle": "run:run_bd00c31afa11",
"subject": "Review complete: 7 findings, top High",
"body": "Reviewed the SSH auto-reconnect budget branch (new budget module + connection/IPC/preload/renderer wiring) for functional and performance regressions. Found 7 proven issues: High—web/RPC always re-arms budget by dropping initiator; High—budget only opens post-drop so cold and post-user-fail auto remounts stay unbounded; High—60s wall-clock with 30s timeouts cuts retries to ~2 vs prior 9/multi-minute ladder. Full report at the path below; no production code edited.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_2cda5d06c526\",\"dispatchId\":\"ctx_12af4bb5bd4c\",\"outcome\":\"succeeded\",\"reportPath\":\".llm-counsel/20260802-121457/reviews/grok.md\"}",
"read": 0,
"sequence": 6781,
"created_at": "2026-08-02T19:21:32Z",
"delivered_at": null,
"sender_pane_key": "33259e5a-eaee-48fc-a94f-e5b68a3bbada:5931d416-1958-4ea3-89f2-a84541215773",
"delivery_contract": "current_delivery"
}
],
"count": 3,
"replayed": true,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "135bf256-805b-4e2f-88e6-35e4628de75e",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,59 +0,0 @@
{
"id": "d6f5a6bb-8175-4805-a0e7-9595f360e14f",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_b8a730471cbf",
"messages": [
{
"id": "msg_62216dc868a8",
"run_id": "run_bd00c31afa11",
"from_handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_84cc9b5d5f7d\",\"dispatchId\":\"ctx_7a4145086c54\",\"phase\":\"reviewing\"}",
"read": 0,
"sequence": 6782,
"created_at": "2026-08-02T19:21:51Z",
"delivered_at": null,
"sender_pane_key": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d:0f751890-e4e0-4c87-b1b3-20311379a42a",
"delivery_contract": "current_delivery"
},
{
"id": "msg_aa6400472f7b",
"run_id": "run_bd00c31afa11",
"from_handle": "term_2eaba6da-e77c-49d4-9e47-393e19fa1381",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_40172447da9b\",\"dispatchId\":\"ctx_ecd84e7d782e\",\"phase\":\"reviewing\"}",
"read": 0,
"sequence": 6783,
"created_at": "2026-08-02T19:22:23Z",
"delivered_at": null,
"sender_pane_key": "66edb724-33f6-40be-bb2a-7f4ee1b37325:2a90f35f-9446-4764-8c3a-b199337c77b0",
"delivery_contract": "current_delivery"
}
],
"count": 2,
"replayed": true,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "348c6040-9ede-44bb-bb5b-9e674cc99eb4",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,59 +0,0 @@
{
"id": "8687f1f1-96af-48e7-a7ff-48a38d38ad18",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_45f3edb60491",
"messages": [
{
"id": "msg_e5e52d5aa05f",
"run_id": "run_bd00c31afa11",
"from_handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_1c70db5688a7\",\"dispatchId\":\"ctx_52a53956018e\",\"phase\":\"reviewing\"}",
"read": 0,
"sequence": 6785,
"created_at": "2026-08-02T19:23:38Z",
"delivered_at": null,
"sender_pane_key": "02ff22b9-11e8-46c0-8481-ca669d3e74f8:c2add700-f7e0-48a3-beb8-0db87c8e4a08",
"delivery_contract": "current_delivery"
},
{
"id": "msg_9a08ad362ceb",
"run_id": "run_bd00c31afa11",
"from_handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"to_handle": "run:run_bd00c31afa11",
"subject": "Review found four reconnect-budget regressions",
"body": "I reviewed the full branch and uncommitted scope and wrote the evidence-backed report to .llm-counsel/20260802-121457/reviews/codex.md. I found four regressions: three High-severity budget bypasses affecting web clients, cold-start failures, and rapid flaps, plus one Medium deadline overrun. No production code was changed; the remaining work is to propagate initiator through web RPC, charge initial failures, require stable recovery before resetting, and re-check exhaustion before dialing.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_84cc9b5d5f7d\",\"dispatchId\":\"ctx_7a4145086c54\",\"outcome\":\"succeeded\",\"filesModified\":[],\"reportPath\":\"/Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews/codex.md\",\"findingsCount\":4,\"topSeverity\":\"High\"}",
"read": 0,
"sequence": 6786,
"created_at": "2026-08-02T19:24:05Z",
"delivered_at": null,
"sender_pane_key": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d:0f751890-e4e0-4c87-b1b3-20311379a42a",
"delivery_contract": "current_delivery"
}
],
"count": 2,
"replayed": false,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "09696544-c58c-4b24-855c-bd075c23bc0a",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,41 +0,0 @@
{
"id": "5cc0ac13-87f9-4437-8d7b-2f604d895a0f",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_0da2600a4a09",
"messages": [
{
"id": "msg_c0e4691b308a",
"run_id": "run_bd00c31afa11",
"from_handle": "term_2eaba6da-e77c-49d4-9e47-393e19fa1381",
"to_handle": "run:run_bd00c31afa11",
"subject": "12 findings (4 High): branch claim only half delivered",
"body": "Reviewed the full branch (0ae9174408..HEAD is empty; all 10 modified + 2 untracked files are uncommitted) and ran the gates read-only: 181 main-process SSH tests, 538 pty-connection tests, and oxfmt --check all pass. Found 12 proven findings (4 High / 4 Medium / 4 Low): the initial-connect path never opens a budget window at all (SshConnection.connect() throws without calling scheduleReconnect, and deadlineFor has exactly one production call site), so the headline 'dead host + pane remounts' case is still an unbounded ~160s-per-call storm; the 60s wall-clock budget only fits TWO attempts against a black-holed host (CONNECT_TIMEOUT_MS=30s) versus nine before, never re-arms itself, and burns while the process is suspended, so any 61s outage — host reboot, VPN reconnect, or a lid-close where the drop was seen before sleep — permanently parks every SSH workspace until a human clicks Connect, with the new powerMonitor guard also skipping the #7773 wake-recovery path. Remaining work is the author's: fix H4 (charge the window on failed auto connects), H3 (reset on resume), H1/H2 (raise or slow-re-arm the budget), and M2b (the overlay drops AUTO_RECONNECT_PAUSED_MESSAGE, so the user is never told Orca stopped trying); full evidence with file:line in the report.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_40172447da9b\",\"dispatchId\":\"ctx_ecd84e7d782e\",\"outcome\":\"succeeded\",\"reportPath\":\".llm-counsel/20260802-121457/reviews/claude-opus.md\"}",
"read": 0,
"sequence": 6787,
"created_at": "2026-08-02T19:27:07Z",
"delivered_at": null,
"sender_pane_key": "66edb724-33f6-40be-bb2a-7f4ee1b37325:2a90f35f-9446-4764-8c3a-b199337c77b0",
"delivery_contract": "current_delivery"
}
],
"count": 1,
"replayed": false,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "ca83659d-b216-48bd-8dfa-bd937c68abc9",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,41 +0,0 @@
{
"id": "8c21ea07-ac2f-4395-8ba1-7377cd1fe924",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_66f1222663e3",
"messages": [
{
"id": "msg_4a46a9308487",
"run_id": "run_bd00c31afa11",
"from_handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"to_handle": "run:run_bd00c31afa11",
"subject": "Review done: 7 findings, 3 High — claim falsified for paired clients",
"body": "Reviewed the auto-reconnect-budget branch (all 10 modified + 2 untracked files), traced every ssh.connect caller across desktop/web/RPC, and ran the touched suites (181+538 tests) plus full typecheck — all green. Core desktop mechanism is sound, but I found 7 issues: High — (F1) the runtime RPC drops the initiator field so paired web/mobile clients default to 'user', resetting the budget and re-launching 5x30s connect storms on every pane remount, which falsifies the branch claim; (F2) the wall-clock budget counts system-sleep time and the resume handler hard-skips exhausted targets, regressing #7773 wake recovery; (F3) 60s budget vs 30s connect timeout gives a black-holing host ~2 attempts and the never-expiring pause means any >60s blip permanently breaks unattended automation dispatch until a human clicks Connect. Remaining: Medium F4 (stale exhausted window survives clean disconnect → false 'host unreachable' hours later) and F5 (budget enforced only at the IPC gate; connect()/reconnect() and never-connected hosts bypass it), plus two Lows — full evidence with file:line in the report.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_1c70db5688a7\",\"dispatchId\":\"ctx_52a53956018e\",\"outcome\":\"succeeded\",\"reportPath\":\".llm-counsel/20260802-121457/reviews/claude-fable.md\"}",
"read": 0,
"sequence": 6788,
"created_at": "2026-08-02T19:28:21Z",
"delivered_at": null,
"sender_pane_key": "02ff22b9-11e8-46c0-8481-ca669d3e74f8:c2add700-f7e0-48a3-beb8-0db87c8e4a08",
"delivery_contract": "current_delivery"
}
],
"count": 1,
"replayed": false,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "4a730049-db2c-42f9-af66-f093822bf8da",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,97 +0,0 @@
{"_keepalive":true,"_heartbeat":true,"elapsedMs":15002,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":30004,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":45007,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":60008,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":75009,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":90010,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":105011,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":120012,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":135013,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":150014,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":165015,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":180017,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":195018,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":210020,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":225020,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":240023,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":255024,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":270026,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":285029,"deadlineMs":900000}
{"_keepalive":true,"_heartbeat":true,"elapsedMs":300033,"deadlineMs":900000}
{
"id": "08ad1bf7-cd71-45b7-a39f-c6c506611b47",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_12ee9e0d2ed9",
"messages": [
{
"id": "msg_c8c0e04e2632",
"run_id": "run_bd00c31afa11",
"from_handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_1c70db5688a7\",\"dispatchId\":\"ctx_52a53956018e\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6774,
"created_at": "2026-08-02T19:16:25Z",
"delivered_at": null,
"sender_pane_key": "02ff22b9-11e8-46c0-8481-ca669d3e74f8:c2add700-f7e0-48a3-beb8-0db87c8e4a08",
"delivery_contract": "current_delivery"
},
{
"id": "msg_809457e3f376",
"run_id": "run_bd00c31afa11",
"from_handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_84cc9b5d5f7d\",\"dispatchId\":\"ctx_7a4145086c54\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6776,
"created_at": "2026-08-02T19:16:44Z",
"delivered_at": null,
"sender_pane_key": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d:0f751890-e4e0-4c87-b1b3-20311379a42a",
"delivery_contract": "current_delivery"
},
{
"id": "msg_a09626e4945e",
"run_id": "run_bd00c31afa11",
"from_handle": "term_de79d121-b74d-45e5-be1c-448261d04d6f",
"to_handle": "run:run_bd00c31afa11",
"subject": "Review complete: 7 findings, top High",
"body": "Reviewed the SSH auto-reconnect budget branch (new budget module + connection/IPC/preload/renderer wiring) for functional and performance regressions. Found 7 proven issues: High—web/RPC always re-arms budget by dropping initiator; High—budget only opens post-drop so cold and post-user-fail auto remounts stay unbounded; High—60s wall-clock with 30s timeouts cuts retries to ~2 vs prior 9/multi-minute ladder. Full report at the path below; no production code edited.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_2cda5d06c526\",\"dispatchId\":\"ctx_12af4bb5bd4c\",\"outcome\":\"succeeded\",\"reportPath\":\".llm-counsel/20260802-121457/reviews/grok.md\"}",
"read": 0,
"sequence": 6781,
"created_at": "2026-08-02T19:21:32Z",
"delivered_at": null,
"sender_pane_key": "33259e5a-eaee-48fc-a94f-e5b68a3bbada:5931d416-1958-4ea3-89f2-a84541215773",
"delivery_contract": "current_delivery"
}
],
"count": 3,
"replayed": false,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "000328fa-8062-4306-9251-10e171c17621",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,77 +0,0 @@
{
"id": "1b4f2cc8-e97a-4149-be8d-fe3a3bdc2c02",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_12ee9e0d2ed9",
"messages": [
{
"id": "msg_c8c0e04e2632",
"run_id": "run_bd00c31afa11",
"from_handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_1c70db5688a7\",\"dispatchId\":\"ctx_52a53956018e\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6774,
"created_at": "2026-08-02T19:16:25Z",
"delivered_at": null,
"sender_pane_key": "02ff22b9-11e8-46c0-8481-ca669d3e74f8:c2add700-f7e0-48a3-beb8-0db87c8e4a08",
"delivery_contract": "current_delivery"
},
{
"id": "msg_809457e3f376",
"run_id": "run_bd00c31afa11",
"from_handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_84cc9b5d5f7d\",\"dispatchId\":\"ctx_7a4145086c54\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6776,
"created_at": "2026-08-02T19:16:44Z",
"delivered_at": null,
"sender_pane_key": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d:0f751890-e4e0-4c87-b1b3-20311379a42a",
"delivery_contract": "current_delivery"
},
{
"id": "msg_a09626e4945e",
"run_id": "run_bd00c31afa11",
"from_handle": "term_de79d121-b74d-45e5-be1c-448261d04d6f",
"to_handle": "run:run_bd00c31afa11",
"subject": "Review complete: 7 findings, top High",
"body": "Reviewed the SSH auto-reconnect budget branch (new budget module + connection/IPC/preload/renderer wiring) for functional and performance regressions. Found 7 proven issues: High—web/RPC always re-arms budget by dropping initiator; High—budget only opens post-drop so cold and post-user-fail auto remounts stay unbounded; High—60s wall-clock with 30s timeouts cuts retries to ~2 vs prior 9/multi-minute ladder. Full report at the path below; no production code edited.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_2cda5d06c526\",\"dispatchId\":\"ctx_12af4bb5bd4c\",\"outcome\":\"succeeded\",\"reportPath\":\".llm-counsel/20260802-121457/reviews/grok.md\"}",
"read": 0,
"sequence": 6781,
"created_at": "2026-08-02T19:21:32Z",
"delivered_at": null,
"sender_pane_key": "33259e5a-eaee-48fc-a94f-e5b68a3bbada:5931d416-1958-4ea3-89f2-a84541215773",
"delivery_contract": "current_delivery"
}
],
"count": 3,
"replayed": true,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "e1a0059b-b1ff-444b-9a3f-2e66d6b3a473",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,77 +0,0 @@
{
"id": "e611f5cb-8940-4f33-b147-855a4c426c4d",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_12ee9e0d2ed9",
"messages": [
{
"id": "msg_c8c0e04e2632",
"run_id": "run_bd00c31afa11",
"from_handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_1c70db5688a7\",\"dispatchId\":\"ctx_52a53956018e\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6774,
"created_at": "2026-08-02T19:16:25Z",
"delivered_at": null,
"sender_pane_key": "02ff22b9-11e8-46c0-8481-ca669d3e74f8:c2add700-f7e0-48a3-beb8-0db87c8e4a08",
"delivery_contract": "current_delivery"
},
{
"id": "msg_809457e3f376",
"run_id": "run_bd00c31afa11",
"from_handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_84cc9b5d5f7d\",\"dispatchId\":\"ctx_7a4145086c54\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6776,
"created_at": "2026-08-02T19:16:44Z",
"delivered_at": null,
"sender_pane_key": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d:0f751890-e4e0-4c87-b1b3-20311379a42a",
"delivery_contract": "current_delivery"
},
{
"id": "msg_a09626e4945e",
"run_id": "run_bd00c31afa11",
"from_handle": "term_de79d121-b74d-45e5-be1c-448261d04d6f",
"to_handle": "run:run_bd00c31afa11",
"subject": "Review complete: 7 findings, top High",
"body": "Reviewed the SSH auto-reconnect budget branch (new budget module + connection/IPC/preload/renderer wiring) for functional and performance regressions. Found 7 proven issues: High—web/RPC always re-arms budget by dropping initiator; High—budget only opens post-drop so cold and post-user-fail auto remounts stay unbounded; High—60s wall-clock with 30s timeouts cuts retries to ~2 vs prior 9/multi-minute ladder. Full report at the path below; no production code edited.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_2cda5d06c526\",\"dispatchId\":\"ctx_12af4bb5bd4c\",\"outcome\":\"succeeded\",\"reportPath\":\".llm-counsel/20260802-121457/reviews/grok.md\"}",
"read": 0,
"sequence": 6781,
"created_at": "2026-08-02T19:21:32Z",
"delivered_at": null,
"sender_pane_key": "33259e5a-eaee-48fc-a94f-e5b68a3bbada:5931d416-1958-4ea3-89f2-a84541215773",
"delivery_contract": "current_delivery"
}
],
"count": 3,
"replayed": true,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "95804d1c-3a6c-46b9-8901-c6b8efeb5da8",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,77 +0,0 @@
{
"id": "93c0ad40-d273-4dd4-b20e-6016617f27eb",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_12ee9e0d2ed9",
"messages": [
{
"id": "msg_c8c0e04e2632",
"run_id": "run_bd00c31afa11",
"from_handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_1c70db5688a7\",\"dispatchId\":\"ctx_52a53956018e\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6774,
"created_at": "2026-08-02T19:16:25Z",
"delivered_at": null,
"sender_pane_key": "02ff22b9-11e8-46c0-8481-ca669d3e74f8:c2add700-f7e0-48a3-beb8-0db87c8e4a08",
"delivery_contract": "current_delivery"
},
{
"id": "msg_809457e3f376",
"run_id": "run_bd00c31afa11",
"from_handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_84cc9b5d5f7d\",\"dispatchId\":\"ctx_7a4145086c54\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6776,
"created_at": "2026-08-02T19:16:44Z",
"delivered_at": null,
"sender_pane_key": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d:0f751890-e4e0-4c87-b1b3-20311379a42a",
"delivery_contract": "current_delivery"
},
{
"id": "msg_a09626e4945e",
"run_id": "run_bd00c31afa11",
"from_handle": "term_de79d121-b74d-45e5-be1c-448261d04d6f",
"to_handle": "run:run_bd00c31afa11",
"subject": "Review complete: 7 findings, top High",
"body": "Reviewed the SSH auto-reconnect budget branch (new budget module + connection/IPC/preload/renderer wiring) for functional and performance regressions. Found 7 proven issues: High—web/RPC always re-arms budget by dropping initiator; High—budget only opens post-drop so cold and post-user-fail auto remounts stay unbounded; High—60s wall-clock with 30s timeouts cuts retries to ~2 vs prior 9/multi-minute ladder. Full report at the path below; no production code edited.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_2cda5d06c526\",\"dispatchId\":\"ctx_12af4bb5bd4c\",\"outcome\":\"succeeded\",\"reportPath\":\".llm-counsel/20260802-121457/reviews/grok.md\"}",
"read": 0,
"sequence": 6781,
"created_at": "2026-08-02T19:21:32Z",
"delivered_at": null,
"sender_pane_key": "33259e5a-eaee-48fc-a94f-e5b68a3bbada:5931d416-1958-4ea3-89f2-a84541215773",
"delivery_contract": "current_delivery"
}
],
"count": 3,
"replayed": true,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "c0fc7ff0-ca23-4f71-8dc9-427ce7295747",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,77 +0,0 @@
{
"id": "f4f622c9-64d9-49ee-ad2a-f13679740d79",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_12ee9e0d2ed9",
"messages": [
{
"id": "msg_c8c0e04e2632",
"run_id": "run_bd00c31afa11",
"from_handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_1c70db5688a7\",\"dispatchId\":\"ctx_52a53956018e\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6774,
"created_at": "2026-08-02T19:16:25Z",
"delivered_at": null,
"sender_pane_key": "02ff22b9-11e8-46c0-8481-ca669d3e74f8:c2add700-f7e0-48a3-beb8-0db87c8e4a08",
"delivery_contract": "current_delivery"
},
{
"id": "msg_809457e3f376",
"run_id": "run_bd00c31afa11",
"from_handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_84cc9b5d5f7d\",\"dispatchId\":\"ctx_7a4145086c54\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6776,
"created_at": "2026-08-02T19:16:44Z",
"delivered_at": null,
"sender_pane_key": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d:0f751890-e4e0-4c87-b1b3-20311379a42a",
"delivery_contract": "current_delivery"
},
{
"id": "msg_a09626e4945e",
"run_id": "run_bd00c31afa11",
"from_handle": "term_de79d121-b74d-45e5-be1c-448261d04d6f",
"to_handle": "run:run_bd00c31afa11",
"subject": "Review complete: 7 findings, top High",
"body": "Reviewed the SSH auto-reconnect budget branch (new budget module + connection/IPC/preload/renderer wiring) for functional and performance regressions. Found 7 proven issues: High—web/RPC always re-arms budget by dropping initiator; High—budget only opens post-drop so cold and post-user-fail auto remounts stay unbounded; High—60s wall-clock with 30s timeouts cuts retries to ~2 vs prior 9/multi-minute ladder. Full report at the path below; no production code edited.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_2cda5d06c526\",\"dispatchId\":\"ctx_12af4bb5bd4c\",\"outcome\":\"succeeded\",\"reportPath\":\".llm-counsel/20260802-121457/reviews/grok.md\"}",
"read": 0,
"sequence": 6781,
"created_at": "2026-08-02T19:21:32Z",
"delivered_at": null,
"sender_pane_key": "33259e5a-eaee-48fc-a94f-e5b68a3bbada:5931d416-1958-4ea3-89f2-a84541215773",
"delivery_contract": "current_delivery"
}
],
"count": 3,
"replayed": true,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "78a95bc3-c2b4-46e3-9a5f-45245643be08",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,77 +0,0 @@
{
"id": "bb22021d-2b85-463d-8e69-048282e62285",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_12ee9e0d2ed9",
"messages": [
{
"id": "msg_c8c0e04e2632",
"run_id": "run_bd00c31afa11",
"from_handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_1c70db5688a7\",\"dispatchId\":\"ctx_52a53956018e\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6774,
"created_at": "2026-08-02T19:16:25Z",
"delivered_at": null,
"sender_pane_key": "02ff22b9-11e8-46c0-8481-ca669d3e74f8:c2add700-f7e0-48a3-beb8-0db87c8e4a08",
"delivery_contract": "current_delivery"
},
{
"id": "msg_809457e3f376",
"run_id": "run_bd00c31afa11",
"from_handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_84cc9b5d5f7d\",\"dispatchId\":\"ctx_7a4145086c54\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6776,
"created_at": "2026-08-02T19:16:44Z",
"delivered_at": null,
"sender_pane_key": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d:0f751890-e4e0-4c87-b1b3-20311379a42a",
"delivery_contract": "current_delivery"
},
{
"id": "msg_a09626e4945e",
"run_id": "run_bd00c31afa11",
"from_handle": "term_de79d121-b74d-45e5-be1c-448261d04d6f",
"to_handle": "run:run_bd00c31afa11",
"subject": "Review complete: 7 findings, top High",
"body": "Reviewed the SSH auto-reconnect budget branch (new budget module + connection/IPC/preload/renderer wiring) for functional and performance regressions. Found 7 proven issues: High—web/RPC always re-arms budget by dropping initiator; High—budget only opens post-drop so cold and post-user-fail auto remounts stay unbounded; High—60s wall-clock with 30s timeouts cuts retries to ~2 vs prior 9/multi-minute ladder. Full report at the path below; no production code edited.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_2cda5d06c526\",\"dispatchId\":\"ctx_12af4bb5bd4c\",\"outcome\":\"succeeded\",\"reportPath\":\".llm-counsel/20260802-121457/reviews/grok.md\"}",
"read": 0,
"sequence": 6781,
"created_at": "2026-08-02T19:21:32Z",
"delivered_at": null,
"sender_pane_key": "33259e5a-eaee-48fc-a94f-e5b68a3bbada:5931d416-1958-4ea3-89f2-a84541215773",
"delivery_contract": "current_delivery"
}
],
"count": 3,
"replayed": true,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "48fad382-4ff3-4b3a-8b5e-a6ff6d5a09af",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,77 +0,0 @@
{
"id": "d168d223-c2db-4af2-b649-609c95a6353e",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_12ee9e0d2ed9",
"messages": [
{
"id": "msg_c8c0e04e2632",
"run_id": "run_bd00c31afa11",
"from_handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_1c70db5688a7\",\"dispatchId\":\"ctx_52a53956018e\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6774,
"created_at": "2026-08-02T19:16:25Z",
"delivered_at": null,
"sender_pane_key": "02ff22b9-11e8-46c0-8481-ca669d3e74f8:c2add700-f7e0-48a3-beb8-0db87c8e4a08",
"delivery_contract": "current_delivery"
},
{
"id": "msg_809457e3f376",
"run_id": "run_bd00c31afa11",
"from_handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_84cc9b5d5f7d\",\"dispatchId\":\"ctx_7a4145086c54\",\"phase\":\"investigating\"}",
"read": 0,
"sequence": 6776,
"created_at": "2026-08-02T19:16:44Z",
"delivered_at": null,
"sender_pane_key": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d:0f751890-e4e0-4c87-b1b3-20311379a42a",
"delivery_contract": "current_delivery"
},
{
"id": "msg_a09626e4945e",
"run_id": "run_bd00c31afa11",
"from_handle": "term_de79d121-b74d-45e5-be1c-448261d04d6f",
"to_handle": "run:run_bd00c31afa11",
"subject": "Review complete: 7 findings, top High",
"body": "Reviewed the SSH auto-reconnect budget branch (new budget module + connection/IPC/preload/renderer wiring) for functional and performance regressions. Found 7 proven issues: High—web/RPC always re-arms budget by dropping initiator; High—budget only opens post-drop so cold and post-user-fail auto remounts stay unbounded; High—60s wall-clock with 30s timeouts cuts retries to ~2 vs prior 9/multi-minute ladder. Full report at the path below; no production code edited.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_2cda5d06c526\",\"dispatchId\":\"ctx_12af4bb5bd4c\",\"outcome\":\"succeeded\",\"reportPath\":\".llm-counsel/20260802-121457/reviews/grok.md\"}",
"read": 0,
"sequence": 6781,
"created_at": "2026-08-02T19:21:32Z",
"delivered_at": null,
"sender_pane_key": "33259e5a-eaee-48fc-a94f-e5b68a3bbada:5931d416-1958-4ea3-89f2-a84541215773",
"delivery_contract": "current_delivery"
}
],
"count": 3,
"replayed": true,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "d871337b-96b0-489d-8c32-bd9c4861c922",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,36 +0,0 @@
{
"id": "e4dc229c-0483-408d-81b8-6213a835f8ad",
"ok": true,
"result": {
"dispatch": {
"id": "ctx_52a53956018e",
"task_id": "task_1c70db5688a7",
"assignee_handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"assignee_pane_key": "02ff22b9-11e8-46c0-8481-ca669d3e74f8:c2add700-f7e0-48a3-beb8-0db87c8e4a08",
"status": "dispatched",
"failure_count": 0,
"last_failure": null,
"dispatched_at": "2026-08-02 19:16:12",
"completed_at": null,
"created_at": "2026-08-02 19:16:12",
"last_heartbeat_at": null,
"requested_agent": null,
"base_agent": null,
"agent_launch_failure": null,
"run_id": "run_bd00c31afa11",
"capability_hash": null,
"process_incarnation": null,
"capability_revoked_at": null,
"contract_version": 1,
"launch_token_hash": null
},
"injected": true,
"mutation": {
"requestId": "6fa636c8-61a5-403c-b97b-b5a32bec5ab1",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,36 +0,0 @@
{
"id": "b353aa17-fa46-4dcc-afc4-121324661f29",
"ok": true,
"result": {
"dispatch": {
"id": "ctx_ecd84e7d782e",
"task_id": "task_40172447da9b",
"assignee_handle": "term_2eaba6da-e77c-49d4-9e47-393e19fa1381",
"assignee_pane_key": "66edb724-33f6-40be-bb2a-7f4ee1b37325:2a90f35f-9446-4764-8c3a-b199337c77b0",
"status": "dispatched",
"failure_count": 0,
"last_failure": null,
"dispatched_at": "2026-08-02 19:16:09",
"completed_at": null,
"created_at": "2026-08-02 19:16:09",
"last_heartbeat_at": null,
"requested_agent": null,
"base_agent": null,
"agent_launch_failure": null,
"run_id": "run_bd00c31afa11",
"capability_hash": null,
"process_incarnation": null,
"capability_revoked_at": null,
"contract_version": 1,
"launch_token_hash": null
},
"injected": true,
"mutation": {
"requestId": "e3f4562f-24eb-40cb-b68d-d5ebcf798daa",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,36 +0,0 @@
{
"id": "69960a15-53d6-41cd-9397-5ad4da2b2b63",
"ok": true,
"result": {
"dispatch": {
"id": "ctx_7a4145086c54",
"task_id": "task_84cc9b5d5f7d",
"assignee_handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"assignee_pane_key": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d:0f751890-e4e0-4c87-b1b3-20311379a42a",
"status": "dispatched",
"failure_count": 0,
"last_failure": null,
"dispatched_at": "2026-08-02 19:16:05",
"completed_at": null,
"created_at": "2026-08-02 19:16:05",
"last_heartbeat_at": null,
"requested_agent": null,
"base_agent": null,
"agent_launch_failure": null,
"run_id": "run_bd00c31afa11",
"capability_hash": null,
"process_incarnation": null,
"capability_revoked_at": null,
"contract_version": 1,
"launch_token_hash": null
},
"injected": true,
"mutation": {
"requestId": "dbeae111-64ee-4ca8-87fc-7ff713c6330a",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,36 +0,0 @@
{
"id": "dd5a5d2b-3e86-4a92-b20e-d492ba6f77e2",
"ok": true,
"result": {
"dispatch": {
"id": "ctx_12af4bb5bd4c",
"task_id": "task_2cda5d06c526",
"assignee_handle": "term_de79d121-b74d-45e5-be1c-448261d04d6f",
"assignee_pane_key": "33259e5a-eaee-48fc-a94f-e5b68a3bbada:5931d416-1958-4ea3-89f2-a84541215773",
"status": "dispatched",
"failure_count": 0,
"last_failure": null,
"dispatched_at": "2026-08-02 19:15:58",
"completed_at": null,
"created_at": "2026-08-02 19:15:58",
"last_heartbeat_at": null,
"requested_agent": null,
"base_agent": null,
"agent_launch_failure": null,
"run_id": "run_bd00c31afa11",
"capability_hash": null,
"process_incarnation": null,
"capability_revoked_at": null,
"contract_version": 1,
"launch_token_hash": null
},
"injected": true,
"mutation": {
"requestId": "a29ea5ee-5395-49f1-8e33-4ecc48f51fbd",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,6 +0,0 @@
{
"grok": "term_de79d121-b74d-45e5-be1c-448261d04d6f",
"codex": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"claude-opus": "term_2eaba6da-e77c-49d4-9e47-393e19fa1381",
"claude-fable": "term_8016c85a-9e49-4bf1-aa84-b162016357df"
}
-1
View File
@@ -1 +0,0 @@
0ae91744086a3195e9a92e585dae6d814bedf808
@@ -1,6 +0,0 @@
{
"rater-1": "grok",
"rater-2": "codex",
"rater-3": "claude-opus",
"rater-4": "claude-fable"
}
@@ -1,59 +0,0 @@
{
"id": "23ac0766-72d8-4be5-b8c5-e3862cb6c08d",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_76a843dcfdbd",
"messages": [
{
"id": "msg_fce49bf2f2c4",
"run_id": "run_bd00c31afa11",
"from_handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"to_handle": "run:run_bd00c31afa11",
"subject": "alive",
"body": "",
"type": "heartbeat",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_c6d6b86c3e5c\",\"dispatchId\":\"ctx_abac11c1a90e\",\"phase\":\"reviewing\"}",
"read": 0,
"sequence": 6790,
"created_at": "2026-08-02T19:29:27Z",
"delivered_at": null,
"sender_pane_key": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d:0f751890-e4e0-4c87-b1b3-20311379a42a",
"delivery_contract": "current_delivery"
},
{
"id": "msg_554f0cf6edeb",
"run_id": "run_bd00c31afa11",
"from_handle": "term_de79d121-b74d-45e5-be1c-448261d04d6f",
"to_handle": "run:run_bd00c31afa11",
"subject": "Blind ratings complete for A–D",
"body": "Read anonymized reports A–D and the blind packet only; scored evidence, regression catch, FP risk, and actionability for each. B and D lead on regression catch (sleep/wake + permanent pause); A is strong on cold-path/post-user-fail; C is accurate but thin. Ratings written; no branch re-diff.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_9152aeee3fbf\",\"dispatchId\":\"ctx_3d365e21631f\",\"outcome\":\"succeeded\",\"reportPath\":\".llm-counsel/20260802-121457/ratings-blind/rater-1.md\"}",
"read": 0,
"sequence": 6792,
"created_at": "2026-08-02T19:30:42Z",
"delivered_at": null,
"sender_pane_key": "33259e5a-eaee-48fc-a94f-e5b68a3bbada:5931d416-1958-4ea3-89f2-a84541215773",
"delivery_contract": "current_delivery"
}
],
"count": 2,
"replayed": false,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "31cf9f13-a348-48c3-a391-1ebd7eec0e4d",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,41 +0,0 @@
{
"id": "318b59d9-ce31-4d3a-9cdd-4b8bd00b1183",
"ok": true,
"result": {
"runId": "run_bd00c31afa11",
"deliveryId": "delivery_40bf60fb8525",
"messages": [
{
"id": "msg_4cc0d0299fed",
"run_id": "run_bd00c31afa11",
"from_handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"to_handle": "run:run_bd00c31afa11",
"subject": "Blind ratings done: B strongest, C weakest coverage",
"body": "Read all four anonymized reports (plus the packet) and rated each on evidence, regression catch, false-positive risk, and actionability with a best-unique finding and one overreach per letter. B is the standout (5/5/5/5 — only report covering RPC initiator-drop, cold-path, sleep/wake, never-expiring pause, AND the overlay dropping the paused message, with verified negatives); A and D are close peers at mostly 4-5s with disjoint blind spots (A missed sleep/wake, D missed the flap gap and the overlay message loss), while C is accurate but narrow (missed sleep/wake and the too-short/never-expires family, no fix directions, couldn't run tests). Nothing left — ratings table plus 5 notes written to the ratings-blind file; the four-way consensus findings (RPC drops initiator; initial-connect path never opens a budget window) deserve merge-blocker treatment.",
"type": "worker_done",
"priority": "normal",
"thread_id": null,
"payload": "{\"taskId\":\"task_930a97650ad0\",\"dispatchId\":\"ctx_746916d6a74b\",\"outcome\":\"succeeded\",\"filesModified\":[\".llm-counsel/20260802-121457/ratings-blind/rater-4.md\"],\"reportPath\":\".llm-counsel/20260802-121457/ratings-blind/rater-4.md\"}",
"read": 0,
"sequence": 6793,
"created_at": "2026-08-02T19:31:00Z",
"delivered_at": null,
"sender_pane_key": "02ff22b9-11e8-46c0-8481-ca669d3e74f8:c2add700-f7e0-48a3-beb8-0db87c8e4a08",
"delivery_contract": "current_delivery"
}
],
"count": 1,
"replayed": false,
"acknowledged": null,
"timedOut": false,
"cancelled": false,
"connectionLost": false,
"mutation": {
"requestId": "83d19dbc-5db7-4cd7-b593-c417005d9ecd",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,27 +0,0 @@
{
"id": "747e2bf8-22b1-4595-81ec-234815be7cc5",
"ok": true,
"result": {
"task": {
"id": "task_9152aeee3fbf",
"parent_id": null,
"spec": "You are a peer rater for an anonymized multi-reviewer counsel. Do NOT re-read the full branch diff.\n\nRead ONLY these anonymized reports (and the packet if provided):\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/A.md\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/B.md\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/C.md\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/D.md\n- optional: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/ratings-packet-blind.md\n\nDo not guess which model wrote which letter. Do not use model names as row keys.\n\nRate EVERY letter A–D (no self-labeling — you do not know which is yours).\nIntegers 1–5 for each:\n- evidence_quality (file/line + concrete impact)\n- regression_catch (perf + functionality bugs that look real)\n- false_positive_risk (5 = low FP / trustworthy; 1 = lots of speculation)\n- actionability (clear smallest fix)\n\nAlso for each letter:\n- best_unique finding (or \"none\")\n- one overreach/false positive if any (or \"none\")\n\nWrite ONLY: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/ratings-blind/rater-1.md\n\nTable required:\n| report | evidence | regression_catch | false_positive_risk | actionability | best_unique | overreach |\n\nRows: A, B, C, D only.\nThen ≤5 lines of notes. worker_done with reportPath pointing at the ratings-blind file.",
"status": "ready",
"deps": "[]",
"result": null,
"created_at": "2026-08-02 19:29:00",
"completed_at": null,
"created_by_terminal_handle": "term_0d47b7fc-993b-4b4f-93f3-593adf918663",
"task_title": "You are a peer rater for an anonymized multi-reviewer counsel. Do NOT re-read...",
"display_name": "You are a peer rater for an anonymized multi-reviewer counsel. Do NOT re-read...",
"run_id": "run_bd00c31afa11"
},
"mutation": {
"requestId": "000170ad-8fb7-48ee-b3a5-a4a149251b76",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,27 +0,0 @@
{
"id": "fa80f27c-51fd-4138-b6cc-012edc2af783",
"ok": true,
"result": {
"task": {
"id": "task_c6d6b86c3e5c",
"parent_id": null,
"spec": "You are a peer rater for an anonymized multi-reviewer counsel. Do NOT re-read the full branch diff.\n\nRead ONLY these anonymized reports (and the packet if provided):\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/A.md\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/B.md\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/C.md\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/D.md\n- optional: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/ratings-packet-blind.md\n\nDo not guess which model wrote which letter. Do not use model names as row keys.\n\nRate EVERY letter A–D (no self-labeling — you do not know which is yours).\nIntegers 1–5 for each:\n- evidence_quality (file/line + concrete impact)\n- regression_catch (perf + functionality bugs that look real)\n- false_positive_risk (5 = low FP / trustworthy; 1 = lots of speculation)\n- actionability (clear smallest fix)\n\nAlso for each letter:\n- best_unique finding (or \"none\")\n- one overreach/false positive if any (or \"none\")\n\nWrite ONLY: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/ratings-blind/rater-2.md\n\nTable required:\n| report | evidence | regression_catch | false_positive_risk | actionability | best_unique | overreach |\n\nRows: A, B, C, D only.\nThen ≤5 lines of notes. worker_done with reportPath pointing at the ratings-blind file.",
"status": "ready",
"deps": "[]",
"result": null,
"created_at": "2026-08-02 19:29:01",
"completed_at": null,
"created_by_terminal_handle": "term_0d47b7fc-993b-4b4f-93f3-593adf918663",
"task_title": "You are a peer rater for an anonymized multi-reviewer counsel. Do NOT re-read...",
"display_name": "You are a peer rater for an anonymized multi-reviewer counsel. Do NOT re-read...",
"run_id": "run_bd00c31afa11"
},
"mutation": {
"requestId": "e0400079-09f5-4de8-ad25-985b389abc7d",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,27 +0,0 @@
{
"id": "3848d58c-42fa-4dd5-a8ec-8bc0eb723914",
"ok": true,
"result": {
"task": {
"id": "task_b5d084d93ecd",
"parent_id": null,
"spec": "You are a peer rater for an anonymized multi-reviewer counsel. Do NOT re-read the full branch diff.\n\nRead ONLY these anonymized reports (and the packet if provided):\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/A.md\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/B.md\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/C.md\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/D.md\n- optional: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/ratings-packet-blind.md\n\nDo not guess which model wrote which letter. Do not use model names as row keys.\n\nRate EVERY letter A–D (no self-labeling — you do not know which is yours).\nIntegers 1–5 for each:\n- evidence_quality (file/line + concrete impact)\n- regression_catch (perf + functionality bugs that look real)\n- false_positive_risk (5 = low FP / trustworthy; 1 = lots of speculation)\n- actionability (clear smallest fix)\n\nAlso for each letter:\n- best_unique finding (or \"none\")\n- one overreach/false positive if any (or \"none\")\n\nWrite ONLY: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/ratings-blind/rater-3.md\n\nTable required:\n| report | evidence | regression_catch | false_positive_risk | actionability | best_unique | overreach |\n\nRows: A, B, C, D only.\nThen ≤5 lines of notes. worker_done with reportPath pointing at the ratings-blind file.",
"status": "ready",
"deps": "[]",
"result": null,
"created_at": "2026-08-02 19:29:04",
"completed_at": null,
"created_by_terminal_handle": "term_0d47b7fc-993b-4b4f-93f3-593adf918663",
"task_title": "You are a peer rater for an anonymized multi-reviewer counsel. Do NOT re-read...",
"display_name": "You are a peer rater for an anonymized multi-reviewer counsel. Do NOT re-read...",
"run_id": "run_bd00c31afa11"
},
"mutation": {
"requestId": "862886e1-71bd-4e70-bc33-c353582120a9",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,27 +0,0 @@
{
"id": "36057c0a-fad3-487d-8909-565d6c95723b",
"ok": true,
"result": {
"task": {
"id": "task_930a97650ad0",
"parent_id": null,
"spec": "You are a peer rater for an anonymized multi-reviewer counsel. Do NOT re-read the full branch diff.\n\nRead ONLY these anonymized reports (and the packet if provided):\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/A.md\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/B.md\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/C.md\n- /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews-blind/D.md\n- optional: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/ratings-packet-blind.md\n\nDo not guess which model wrote which letter. Do not use model names as row keys.\n\nRate EVERY letter A–D (no self-labeling — you do not know which is yours).\nIntegers 1–5 for each:\n- evidence_quality (file/line + concrete impact)\n- regression_catch (perf + functionality bugs that look real)\n- false_positive_risk (5 = low FP / trustworthy; 1 = lots of speculation)\n- actionability (clear smallest fix)\n\nAlso for each letter:\n- best_unique finding (or \"none\")\n- one overreach/false positive if any (or \"none\")\n\nWrite ONLY: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/ratings-blind/rater-4.md\n\nTable required:\n| report | evidence | regression_catch | false_positive_risk | actionability | best_unique | overreach |\n\nRows: A, B, C, D only.\nThen ≤5 lines of notes. worker_done with reportPath pointing at the ratings-blind file.",
"status": "ready",
"deps": "[]",
"result": null,
"created_at": "2026-08-02 19:29:07",
"completed_at": null,
"created_by_terminal_handle": "term_0d47b7fc-993b-4b4f-93f3-593adf918663",
"task_title": "You are a peer rater for an anonymized multi-reviewer counsel. Do NOT re-read...",
"display_name": "You are a peer rater for an anonymized multi-reviewer counsel. Do NOT re-read...",
"run_id": "run_bd00c31afa11"
},
"mutation": {
"requestId": "ce21a1f5-3ce6-4365-ac28-247268b9027e",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,17 +0,0 @@
# Blind peer ratings (rater-1)
Scale 1–5. `false_positive_risk`: 5 = low FP / trustworthy; 1 = speculative.
| report | evidence | regression_catch | false_positive_risk | actionability | best_unique | overreach |
|--------|----------|------------------|---------------------|---------------|-------------|-----------|
| A | 5 | 4 | 4 | 4 | Post–user-Connect fail clears budget window and never reopens it → unbounded auto remounts resume (finding 2B) | Web/RPC initiator drop as High without noting current paired mobile callers are still user-driven (latent vs live) |
| B | 5 | 5 | 4 | 5 | Sleep burns wall-clock budget + resume hard-skip defeats #7773 wake recovery (H3) | M3 “stale window parks a healthy live session” is thinner edge-case (needs exhausted window while still connected) |
| C | 4 | 3 | 4 | 4 | Flap reset vs existing `STABLE_CONNECTION_MS` / flap test proving budget no-ops on sustained flaps | Elevating pre-existing flap-never-gives-up behavior to High new regression |
| D | 5 | 5 | 5 | 5 | Clean Disconnect leaves exhausted window → false “host unreachable” park hours later with no probe (F4) | F7 i18n note is low-value noise vs the real pause-UX issue (overlay ignoring `state.error`) |
## Notes
- Consensus core (A/B/C/D): cold `connect()` never opens the window; web/RPC drops `initiator`; 60s ≈ ~2 timeout attempts vs old 9.
- B and D uniquely catch sleep/wake budget accounting + resume skip; A and C miss that high-value regression.
- B is deepest (timelines, test gates, clean list) but slightly over-severities a few medium edges; D is tightest on FP discipline with F4 lifecycle accounting.
- C is accurate but narrow (4 findings) and under-catches product recovery regressions.
@@ -1,11 +0,0 @@
| report | evidence | regression_catch | false_positive_risk | actionability | best_unique | overreach |
|---|---:|---:|---:|---:|---|---|
| A | 5 | 5 | 4 | 5 | Near-exhaustion auto connect can enter the full initial-retry loop after the nominal deadline. | Runtime-owned target budget retention is weak because its connect path is user-classified and resets the entry. |
| B | 5 | 5 | 3 | 5 | Broad failed-status gating can newly abort PTY reattach on a relay-error race, beyond the paused state. | The claim that an exhausted check can park a healthy live session relies on speculative stale-window paths and overstates relay-state clearing effects. |
| C | 5 | 4 | 4 | 4 | The deadline clamp schedules a final handshake exactly at exhaustion, extending SSH work by up to the connect timeout. | Rapid-flap handling is plausibly incomplete, but High severity is debatable because successful handshakes are explicitly treated as new outage epochs. |
| D | 5 | 5 | 4 | 5 | Explicit disconnect can leave an aging budget that later falsely parks an automatic reconnect without probing. | The Reset Relay recovery claim is not fully traced through that handler's own reconnect lifecycle. |
Notes:
B is the broadest and best verified, but its long tail contains several low-value or speculative findings.
C is the most concise and trustworthy, with one particularly sharp boundary-timer catch.
A and D both identify the headline web/RPC and cold-connect bypasses with strong concrete evidence.
@@ -1,15 +0,0 @@
# Blind peer ratings — rater-3
| report | evidence | regression_catch | false_positive_risk | actionability | best_unique | overreach |
|---|---|---|---|---|---|---|
| A | 5 | 4 | 5 | 5 | Post-user-Connect hole: a user connect `reset()`s the window *before* success, so Connect→fail→next `'auto'` remount runs unbounded again with no window open (finding #2 scenario B) | #5 flap-storm framed as a branch failure while conceding the old ladder never stopped flaps either — scope creep, but honestly caveated |
| B | 5 | 4 | 4 | 5 | L4: the new `SSH_CONNECT_FAILED_STATUSES` gate is asymmetric — a racing relay `'error'` override now aborts a reattach that previously proceeded, while a relay-lost `'reconnecting'` override still reattaches mid-redeploy | M3 claims a stale window can park a *healthy live* session, but its own preconditions (superseded-attempt race) don't support that framing; D/F4 makes the sober version. Its "found clean" preload-contract audit is stated as verified while missing the web adapter |
| C | 5 | 3 | 5 | 3 | Cites the *existing* flap test (12 post-handshake drops at 45s spacing, expected to keep reconnecting) as proof the budget cannot bound flap storms — plus `web-preload-api.test.ts:3417` pinning the lossy `{ targetId }` RPC payload | Medium on the deadline-clamp boundary attempt; it is one tail attempt (both B and D traced the same clamp and found no loop), so Low is the honest severity |
| D | 5 | 5 | 5 | 5 | F1's second-order effect: the paired client's storm overwrites the desktop's parked broadcast with `'connecting'`, flipping the desktop UI out of the paused state the branch guarantees. F4 (clean user Disconnect leaves the window armed → false "host is unreachable" hours later, no probe) and F6 (reset-relay doesn't re-arm) are also unique | F5 leans on future callers ("tomorrow it is whatever new internal caller someone adds") rather than a present defect, and largely restates F1/F3 |
Notes:
1. Consensus majors: (1) web/runtime RPC drops `initiator`, (2) initial-connect never opens a window, (3) 60s ≈ 2 attempts + pause never expires, (4) sleep/wake, (5) flap resets budget, (6) stale window after disconnect. Coverage: D 5/6, A 5/6 (misses sleep/wake), B 4/6 (misses web adapter + flaps), C 3/6.
2. #1 is the strongest finding in the set — A, C and D independently cite `web-preload-api.ts:3238-3244` with C adding a pinned test. B alone rates it Medium/"latent (mobile caller is user-driven)", which looks like a false negative: the shared renderer already sends `initiator:'auto'` through that same adapter, so it is live, not latent.
3. D is the most complete and best-calibrated: broadest real coverage, gates actually run (incl. web typecheck), fix direction on every finding, and an accurate "checked and found sound" section.
4. C is the most rigorous per finding but the narrowest — it never engages the "ZERO functional regressions" half of the mandate (no 60s-shortening, never-expiring-pause, or unattended-automation analysis) and gives no fix directions.
5. Two disputes worth resolving before synthesis: flaps (A/C say the budget can't bound them; B/D don't raise it) and the deadline-clamp severity (C Medium vs B/D "one tail attempt, no loop").
@@ -1,16 +0,0 @@
# Rater 4 — blind ratings (reports A–D)
| report | evidence | regression_catch | false_positive_risk | actionability | best_unique | overreach |
|--------|----------|------------------|---------------------|---------------|-------------|-----------|
| A | 5 | 4 | 5 | 4 | User Connect resets the budget *before* success, so a failed user reconnect leaves no open window and unbounded auto remounts resume (finding #2, scenario B) | none material — #6 (runtime-owned id inherits exhausted window) is lightly evidenced but plausible and marked Low |
| B | 5 | 5 | 5 | 5 | Parked state is broadcast-only/never authoritative (later `ssh:getState` disagrees) **and** the overlay derives copy from `status` alone, so `AUTO_RECONNECT_PAUSED_MESSAGE` never reaches the user (M2); also the proof that the IPC park test must hand-synthesize the window because no production path opens it cold (H4) | H3 step 1 states the woken pre-sleep timer attempt fails "before the network stack is up" as near-certain; that ordering is environment-dependent, though the conclusion survives without it |
| C | 4 | 3 | 4 | 2 | `web-preload-api.test.ts:3417` already pins the lossy `{ targetId }` RPC payload — sharpest evidence for the initiator-drop finding; plus the deepest flap-storm analysis tied to `STABLE_CONNECTION_MS` semantics | Rating the flap-reset gap High as something "this change claims to bound": the pre-branch ladder behaved identically on flaps (C's own cited test predates the branch), so it's a claim-completeness gap, not a High regression |
| D | 5 | 4 | 4 | 4 | Stale exhausted window survives a clean user **Disconnect** → hours later an `'auto'` connect parks with a false "host unreachable" and fails automation dispatch without a single probe (F4); also F1's note that paired-client storms flip the desktop's parked UI state back | F7 assumes the paused message "rides into renderer overlays" — per A#7/B M2(b) the pane overlay ignores `state.error`, so the message is actually dropped; D missed that and built a Low on a partly wrong premise |
## Notes
1. Strong four-way consensus on the two load-bearing findings — runtime-RPC drops `initiator` (re-arms budget from paired/web clients) and the cold/initial-connect path never opens a window — so both should be treated as confirmed merge blockers.
2. B is the most complete: only report with all of {RPC drop, cold path, sleep/wake, 60s/never-expires, overlay message loss, authoritative-state gap}, plus verified negatives (clamp can't hit 0ms, relay loop terminates) and a prioritized pre-merge list.
3. A and D are close peers with disjoint blind spots: A missed sleep/wake entirely (framed the resume guard as "holds"), D missed the flap-reset gap and the overlay dropping the paused message.
4. C is accurate and adds unique evidence but has the narrowest regression coverage (no sleep/wake, no 60s-too-short/never-expires) and offers no fix directions; it also could not run tests (missing vitest binary, disclosed honestly).
5. FP discipline is good across all four; disagreements are severity framing, not fabricated facts.
@@ -1,421 +0,0 @@
# Blind ratings packet
Rate reports A–D only. Do not guess authors.
## Report: A
# Review: stop-keep-reconnecting-to-remote (SSH auto-reconnect budget)
**Seat:** [reviewer]
**Scope:** `0ae91744086a3195e9a92e585dae6d814bedf808`..HEAD + all uncommitted/untracked changes
**Claim:** Stop endless SSH auto-reconnect to remote via a target-scoped wall-clock auto-reconnect budget
**Mode:** Review-only (no production edits)
## Executive summary
The change introduces a process-wide, **target-scoped** 60s wall-clock budget that (a) caps `SshConnection.scheduleReconnect` loops and (b) parks `initiator: 'auto'` `ssh:connect` calls once exhausted. That correctly addresses the core desktop failure mode where `SshConnectionManager.connect()` discards a non-connected connection and a pane remount restarts a fresh ladder forever.
However, the stop is **not complete across all auto-connect surfaces**, and the 60s budget **materially shortens** prior multi-minute retry behavior. Highest risks: web/runtime/RPC paths always default to `initiator: 'user'` (re-arming the budget), budget windows never open on cold/failed `connect()` paths (so remount storms can resume after a failed user Connect), and slow hosts get only ~2 timeout-sized attempts before permanent pause.
## What changed (map)
| Area | Change |
|------|--------|
| `ssh-auto-reconnect-budget.ts` (new) | Singleton `Map<targetId, startedAt>`; 60s budget; reset/clear |
| `ssh-connection.ts` | Open/check budget in `scheduleReconnect`; reset on successful handshake |
| `ipc/ssh.ts` | `connectTarget(targetId, initiator)`; auto parks when exhausted; power-monitor skip; test clear |
| Preload types/API | Optional `initiator?: 'user' \| 'auto'` (default `'user'`) |
| Renderer auto call sites | App startup, `pty-connection`, automation dispatch pass `initiator: 'auto'` |
| Tests | Budget unit tests; connection give-up rewritten; IPC park/re-arm tests |
## Branch claim — what holds
1. **Target-scoped budget survives connection replacement**
`SshConnectionManager.connect()` disconnects non-connected instances and builds a new `SshConnection` (fresh ladder). Budget lives on `sshAutoReconnectBudget` keyed by `targetId`, not on the connection object. Covered by test “keeps the give-up after the connection object is replaced”.
2. **Desktop auto IPC park after exhaustion does zero SSH work**
`connectTarget(..., 'auto')` short-circuits before `doConnect` / `connectionManager.connect` when exhausted. IPC test asserts `mockConnectionManager.connect` is not called.
3. **Pane remount no longer re-arms budget (desktop Electron)**
`pty-connection.waitForSshConnection` and App startup pass `initiator: 'auto'`. User UI Connect paths omit initiator → default `'user'` → re-arm. Correct split for those surfaces.
4. **False “connected” on parked auto connect is fixed**
Park returns `{ status: 'reconnection-failed' }` without throwing. `waitForSshConnection` now treats failed statuses as `connected: false` instead of reattaching a PTY to a dead host.
5. **Power resume respects exhaustion**
Resume path skips `manager.reconnect` when budget exhausted (wake is not a user ask).
---
## Findings (proven)
### 1. High — Web / runtime RPC always re-arms budget (initiator dropped)
**Impact:** On web client and any runtime RPC `ssh.connect`, every connect is treated as **user-initiated**, so `sshAutoReconnectBudget.reset(targetId)` runs and the give-up can never stick for those clients. Undermines the branch claim outside pure desktop Electron IPC.
**Evidence:**
```3238:3243:src/renderer/src/web/web-preload-api.ts
connect: async (args) => {
const { state } = await callRuntimeResult<{ state: SshConnectionState | null }>(
'ssh.connect',
{ targetId: args.targetId } // initiator stripped
)
```
```27:37:src/main/runtime/rpc/methods/ssh.ts
defineMethod({
name: 'ssh.connect',
params: SshTarget, // only { targetId }
handler: async (params) => {
try {
return { state: getPublicSshState(await connectRegisteredSshTarget(params.targetId)) }
```
```99:103:src/main/ipc/ssh.ts
export async function connectRegisteredSshTarget(targetId: string): Promise<SshConnectionState> {
...
return registeredConnectSshTarget(targetId) // second arg omitted → initiator defaults to 'user'
}
```
```991:993:src/main/ipc/ssh.ts
if (initiator === 'user') {
sshAutoReconnectBudget.reset(targetId)
```
`connectRuntimeEnvironmentSshTarget` also invokes RPC with only `{ targetId }` (overlay “Connect” may be intentional user; other auto runtime traffic still hits the same default).
**Failure scenario:** Dead remote host, budget exhausts on desktop main process, mobile/web/runtime auto-connect arrives via RPC → reset → new full connect / reconnect cycle resumes.
**Suggested fix direction (not applied):** Plumb `initiator` through RPC schema + web preload + `connectRegisteredSshTarget`; default RPC callers that are machine-driven to `'auto'`.
---
### 2. High — Budget window only opens in `scheduleReconnect`; cold auto-connect and post–user-fail remounts remain unbounded
**Impact:** The wall-clock stop only applies **after a successful connection has dropped** and entered the reconnect ladder. Cold failures and “user clicked Connect, still dead” do not open a budget window, so `initiator: 'auto'` remounts keep running full `connect()` / `INITIAL_RETRY` forever.
**Evidence:**
- Window open site is only `deadlineFor` inside `scheduleReconnect`:
```1280:1288:src/main/ssh/ssh-connection.ts
const nowMs = Date.now()
if (sshAutoReconnectBudget.isExhausted(this.target.id, nowMs)) {
this.setState('reconnection-failed', AUTO_RECONNECT_PAUSED_MESSAGE)
return
}
const deadlineMs = sshAutoReconnectBudget.deadlineFor(this.target.id, nowMs)
```
- `isExhausted` is **false** when no window is open:
```34:37:src/main/ssh/ssh-auto-reconnect-budget.ts
isExhausted(targetId: string, nowMs: number): boolean {
const startedAt = this.windowStartedAtMs.get(targetId)
return startedAt !== undefined && nowMs - startedAt >= this.budgetMs
}
```
- Initial `connect()` does 5 transient retries (`INITIAL_RETRY_ATTEMPTS=5`, `INITIAL_RETRY_DELAY_MS=2000`, `CONNECT_TIMEOUT_MS=30_000`) and on failure sets `error` / throws — **never** calls `deadlineFor` / `scheduleReconnect` (reconnect is only wired after a live transport drops).
- User Connect **clears** the window **before** success:
```991:993:src/main/ipc/ssh.ts
if (initiator === 'user') {
...(truncated if longer)...
## Report: B
# Branch review — `stop-keep-reconnecting-to-remote` (seat: [reviewer])
**Scope reviewed:** `0ae9174408..HEAD` (no commits on branch) **plus all uncommitted + untracked changes**.
10 modified files, 2 new untracked files (`src/main/ssh/ssh-auto-reconnect-budget.{ts,test.ts}`).
**Gates run (read-only):**
- `vitest run src/main/ssh/ssh-auto-reconnect-budget.test.ts src/main/ssh/ssh-connection.test.ts src/main/ipc/ssh.test.ts` → **181 passed / 3 files passed**
- `vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-connection.test.ts` → **538 passed**
- `oxfmt --check -c .oxfmtrc.json <9 changed files>` → clean
**Verdict:** the core mechanism is sound and correctly target-scoped (the branch's central insight — that a connection-scoped bound can never stick because `SshConnectionManager.connect()` discards and rebuilds the connection — is right and well-tested). But the branch **does not fully deliver its claim** (the initial-connect path is entirely unbounded, H4) and it **introduces four high-severity recovery regressions**: a 61-second outage permanently disables SSH auto-reconnect for the life of the process, only two attempts survive on the exact failure mode being targeted, and sleep/wake recovery (#7773) is defeated.
**Findings: 4 High, 4 Medium, 4 Low. Top severity: High.**
---
## Key constants (used throughout)
| Constant | Value | Source |
|---|---|---|
| `AUTO_RECONNECT_BUDGET_MS` | **60_000** | `ssh-auto-reconnect-budget.ts:10` |
| `CONNECT_TIMEOUT_MS` | **30_000** | `ssh-connection-utils.ts:38` |
| `INITIAL_RETRY_ATTEMPTS` / delay | **5** / 2_000 | `ssh-connection-utils.ts:35-36` |
| `RECONNECT_BACKOFF_MS` | `[1s,2s,5s,5s,10s,10s,10s,30s,30s]` (9 entries) | `ssh-connection-utils.ts:37` |
| `DEFAULT_BOUNDED_SSH_RELAY_GRACE_PERIOD_SECONDS` | **24 h** | `shared/ssh-types.ts:6` |
---
## H1 — A 61-second outage permanently disables SSH auto-reconnect for the process lifetime (High)
**Files:** `src/main/ssh/ssh-auto-reconnect-budget.ts:34-37`, `src/main/ssh/ssh-connection.ts:1283-1285`, `src/main/ipc/ssh.ts:993-1008`
`isExhausted()` has no expiry, and the only two things that clear the window are (a) a successful `attemptConnect` and (b) a `'user'`-initiated `ssh:connect`. Once paused, **nothing in the product ever retries**, so (a) can never happen. The branch's own test pins this as intentional (`ssh-auto-reconnect-budget.test.ts:46-52`, "does not expire the pause on its own", asserted at 24 hours).
**Failure scenario (routine, not exotic):** a remote host reboots (typically 30–120 s), sshd is restarted, a VPN re-establishes, or Wi-Fi roams between APs. At T+60 s Orca gives up. The host is back at T+90 s. Every SSH workspace, terminal, agent pane and automation on that host stays dead until a human opens the desktop app and clicks Connect — **for as long as the app runs** (Orca desktop sessions run for days/weeks).
**Why this is a regression, not just a policy choice:** it contradicts the surrounding system's own design assumptions.
- The remote relay defaults to a **24-hour** bounded grace (`shared/ssh-types.ts:6`) precisely so a user can lose the link and come back to live remote PTYs. Orca now stops trying 60 seconds into an outage the remote side is prepared to survive for a day.
- `SshReconnectLadder` deliberately caps flap delays at `FLAP_DELAY_CAP_MS` so a reconnect lands *inside* relay grace (`ssh-reconnect-ladder.ts:12-20`). That machinery is now moot past 60 s.
- Previously the ladder gave 9 handshake failures before `reconnection-failed`.
**Suggested fix:** keep the pause (it is the right idea) but re-arm it on a slow schedule — e.g. a single long-interval, zero-cost TCP/`connect`-probe every 5–15 min, or expire the window after N minutes. That preserves "no busy loop" while not requiring a human to be sitting at the machine.
---
## H2 — Only **two** reconnect attempts survive on the exact failure mode this targets (High)
**Files:** `src/main/ssh/ssh-connection.ts:1277-1307` (budget check + delay clamp), `ssh-connection-utils.ts:37-38`
An unreachable host (packet black hole — `ETIMEDOUT`, the case named in the branch's own test at `ssh-connection.test.ts:572`) fails each attempt only after `CONNECT_TIMEOUT_MS` = 30 s. Trace against a 60 s budget:
```
t=0 drop → scheduleReconnect → window opens, deadline = 60_000; delay = min(1000, 60000) = 1000
t=1_000 attempt #1 → times out at t≈31_000 → not exhausted (31k<60k); delay = min(2000, 29_000) = 2000
t=33_000 attempt #2 → times out at t≈63_000 → 63k ≥ 60k → EXHAUSTED → paused permanently
```
**Two** retries, versus **nine** before. Anything that takes longer than ~63 s to come back is never auto-recovered. (For a *fast*-failing host — instant `ECONNREFUSED` — ~8 attempts still fit, so the effective retry count now silently depends on how the host fails.)
**Evidence of lost coverage:** the branch replaced the exact-count assertion with an inequality:
```diff
- expect(connectAttempts).toBe(1 + RECONNECT_BACKOFF_MS.length)
+ expect(connectAttempts).toBeLessThan(1 + RECONNECT_BACKOFF_MS.length)
```
(`ssh-connection.test.ts:582-590`). The old assertion's comment — "Counting a failure twice, or giving up early, would strand a user on a flaky link" — was the guard against exactly this. Nothing now pins how many attempts a user actually gets; a future `CONNECT_TIMEOUT_MS` bump to 40 s would silently reduce this to **one** attempt with all tests still green.
---
## H3 — Sleep/wake: the budget burns while the process is suspended, and the resume handler now skips reconnect entirely (High)
**Files:** `src/main/ipc/ssh.ts:576-579`, `src/main/ssh/ssh-connection.ts:1280-1285`
`isExhausted` compares raw `Date.now()` deltas, so **wall-clock time in which Orca made zero attempts still spends the budget** — including time the process is suspended by the OS.
**Failure scenario:** Wi-Fi drops as the lid closes → the `close`/`error` handler (`ssh-connection.ts:1259-1274`) calls `scheduleReconnect()`, which **opens the window at T** and arms a 1 s timer. The machine suspends. It resumes 8 hours later:
1. The pending `setTimeout` fires immediately on wake; `runReconnectAttempt` runs before the network stack is up and fails — which is exactly why `RESUME_PROBE_ATTEMPTS = 2` with a retry exists (`ssh.ts:546-547`, #7773). `scheduleReconnect` then sees 8 hours of "spent budget" → **permanent pause**.
2. The new guard at `ssh.ts:576-579` makes the `powerMonitor` `'resume'` handler `continue` for that target, so `isRelayLinkAliveAfterResume()` + `manager.reconnect(targetId)` — the whole #7773 wake-recovery path — never runs either.
Net effect: **after any overnight/lunch sleep where the drop was noticed before suspend, every SSH workspace is parked and needs a manual click.** The comment on the guard ("a laptop that wakes near a dead host does no SSH work at all") is true, but so is the inverse: a laptop that wakes near a *healthy* host also does no SSH work.
**Suggested fix:** treat a wake as a new outage epoch — call `sshAutoReconnectBudget.reset(targetId)` in `onResume` before the liveness probe (or measure attempts / active retry time rather than raw `Date.now()` deltas).
---
## H4 — The initial-connect path is completely unbounded: the branch claim is only half delivered (High)
**Files:** `src/main/ssh/ssh-connection.ts:604-655` (`connect()`), `:1287` (`deadlineFor` — the only production call site), `src/main/ipc/ssh.ts:991-1008`
The budget window is opened **only** by `scheduleReconnect()`. `SshConnection.connect()` fails by *throwing* (`ssh-connection.ts:653-655`) and never calls `scheduleReconnect()`; there is no `setupDisconnectHandler` on a client that never reached `ready`. Therefore **a target that has never connected in this app session never opens a budget window, and `isExhausted` is permanently `false` for it.**
**Failure scenario — the most common "Orca keeps reconnecting to a dead remote":** the remote host is gone (VM deleted, laptop off the corp network, host decommissioned) and Orca is restarted, or the target simply never came up this session. Then:
- App startup auto-reconnect (`App.tsx:1024-1029`) → `connectTarget(..., 'auto')` → not exhausted → full `doConnect`.
- `connectionManager.connect()` → `SshConnection.connect()` → `INITIAL_RETRY_ATTEMPTS = 5` attempts, each up to `CONNECT_TIMEOUT_MS = 30 s`, plus 2 s sleeps ≈ **160 s of SSH work per call**.
- Every SSH pane remount (`pty-connection.ts:852-856`), every automation dispatch (`useAutomationDispatchEvents.ts:171`), every renderer reload repeats it, forever. `connectInFlight` dedupes only *concurrent* calls.
**Evidence:** the new IPC test cannot reach the parked branch through product behavior — it has to synthesize the window by hand:
```ts
sshAutoReconnectBudget.deadlineFor('ssh-1', Date.now() - AUTO_RECONNECT_BUDGET_MS)
```
(`ssh.test.ts:598`, `:624`). No production path opens a window on a failed connect.
**Suggested fix:** charge the window on failed connects too — e.g. call `sshAutoReconnectBudget.deadlineFor(targetId, Date.now())` at the top of the `initiator === 'auto'` branch in `connectTarget` (opening the window on the first automatic connect), and/or in `SshConnection.connect()`'s terminal failure path.
---
## M1 — Unattended automations become permanently unavailable with no re-arm path (Medium)
**File:** `src/renderer/src/hooks/useAutomationDispatchEvents.ts:168-186`
The dispatch connect is now marked `'auto'`, so after a give-up it returns the paused state and the handler throws `'SSH target is unavailable.'` → `markDispatchResult({ status: 'skipped_unavailable' })`. **Nothing in the automation path can ever re-arm the budget** (only a desktop `'user'` connect or a successful connection can, and per H1 no connection will be attempted). A 61-second blip at 03:00 kills every remaining scheduled run against that host until a human opens the app. Combined with H1/H3 this is the most user-visible consequence of the branch.
At minimum this is a behavior change that should be called out in the PR body; ideally scheduled automations get their own re-arm (a scheduled run is arguably closer to "the user asking" than a pane remount is).
---
## M2 — The paused state is broadcast-only, and its message never reaches the user (Medium)
**Files:** `src/main/ipc/ssh.ts:993-1008`, `src/main/ipc/ssh.ts:392-395`, `src/renderer/src/components/terminal-pane/TerminalSshReconnectOverlay.tsx:38-53`
(a) **Not authoritative.** The early return `broadcastSshState(...)`s a `reconnection-failed` state but never writes it into `connectionManager`. A subsequent `ssh:getState` / `getPublicSshState` returns whatever the connection object holds — which is `'disconnected'`, or `undefined` if the manager deleted the connection after a failed connect (`ssh-connection-manager.ts:50-53`). A renderer that re-reads state (new window, paired client sync, `useAutomationDispatchEvents.ts:167`) sees a different status than the one that was pushed.
...(truncated if longer)...
## Report: C
# [reviewer] review: SSH auto-reconnect budget
Reviewed `0ae91744086a3195e9a92e585dae6d814bedf808..HEAD` plus all uncommitted and untracked changes, with emphasis on reconnect admission, timers, connection replacement, renderer/RPC contracts, SSH flaps, and automation dispatch.
## Findings
### High — Paired/web clients discard `initiator: 'auto'`, so their automatic reconnects re-arm the budget
- Files/lines: `src/renderer/src/web/web-preload-api.ts:3238-3242`, `src/main/runtime/rpc/methods/ssh.ts:11-13,28-32`, `src/main/ipc/ssh.ts:99-103,982-994`; affected new callers include `src/renderer/src/App.tsx:1024-1028`, `src/renderer/src/components/terminal-pane/pty-connection.ts:850-857`, and `src/renderer/src/hooks/useAutomationDispatchEvents.ts:168-174`.
- Impact: automatic startup reconnect, pane remount, and automation dispatch in the web/paired renderer can continue creating SSH work after the target's budget should be exhausted. The branch claim holds only for the Electron preload path, not for another supported renderer using the same `PreloadApi` contract.
- Evidence: the changed callers pass `initiator: 'auto'`, but `createSshApi().connect` forwards only `{ targetId: args.targetId }`. The runtime RPC schema contains only `targetId`, and its handler calls `connectRegisteredSshTarget(targetId)` without an initiator. That reaches `connectTarget` through a wrapper whose omitted second argument takes the new default `'user'`; line 993 then deletes the target's budget on every web automatic connect. The existing web preload test at `src/renderer/src/web/web-preload-api.test.ts:3417` even pins the lossy `{ targetId }` RPC payload, so the new native-preload tests cannot catch this path.
- Concrete scenario: a paired/web session remounts an SSH pane for a dead host. `waitForSshConnection` labels the call automatic, the web adapter drops that label, main resets the budget as a user connect, and the next remount repeats indefinitely.
### High — Initial automatic connection failures never open the budget window
- Files/lines: `src/main/ipc/ssh.ts:982-1009`, `src/main/ssh/ssh-connection.ts:605-655`, `src/main/ssh/ssh-connection-manager.ts:27-55`, `src/renderer/src/components/terminal-pane/pty-connection.ts:840-878`.
- Impact: if Orca has no established connection object (the common cold-start/dead-host case), startup or pane-driven automatic connects can run the five-attempt initial retry loop on every remount forever. This directly preserves the endless reconnect behavior the branch is meant to stop, and each round may perform up to five 30-second connection attempts plus retry sleeps.
- Evidence: `connectTarget(..., 'auto')` only asks `isExhausted`; it never calls `deadlineFor`, so a target with no existing window is admitted. `SshConnection.connect()` performs `INITIAL_RETRY_ATTEMPTS` and, on terminal failure, does not open or charge the global budget. `SshConnectionManager.connect()` then removes the failed connection object. The renderer deletes its per-target promise in `finally`, so the next pane remount creates a fresh automatic call. The only production call to `deadlineFor` is `scheduleReconnect()` at `src/main/ssh/ssh-connection.ts:1287`, a path reached after an already-connected transport drops, not after an initial connect fails.
- Concrete scenario: Orca restores a tab while its host is offline. The App startup call times out/deferred-connects, main eventually exhausts its initial attempts and deletes the connection, and focusing/remounting that tab starts a new five-attempt sequence because `isExhausted` remains false forever.
### High — Every momentary SSH handshake resets the target budget, so a rapid flap storm is still endless
- Files/lines: `src/main/ssh/ssh-connection.ts:615-620,1255-1272,1310-1322`; existing stability semantics are in `src/main/ssh/ssh-reconnect-ladder.ts:4-5,37-43,63-66` and the still-present flap test at `src/main/ssh/ssh-connection.test.ts:638-659`.
- Impact: a host/proxy that repeatedly completes SSH setup and immediately closes can reconnect forever, retaining the timer/SSH/relay churn this change claims to bound. This is not a hypothetical classification gap: the existing code already distinguishes a momentary handshake from a stable 60-second connection when deciding whether an outage is over.
- Evidence: after `attemptConnect()` resolves, `runReconnectAttempt()` immediately calls `sshAutoReconnectBudget.reset()`. If the socket then emits `end`, `close`, or `error`, `setupDisconnectHandler()` opens a brand-new 60-second window. Repeating that sequence always resets before exhaustion. By contrast, `SshReconnectLadder` only treats a connection as stable after `STABLE_CONNECTION_MS`; its test drives 12 post-handshake drops spaced 45 seconds apart and expects reconnecting to continue, which demonstrates the new budget currently has no effect on sustained flapping.
- Concrete scenario: an SSH server accepts authentication but a proxy, policy, or broken relay-side transport closes the channel a few seconds later. Every handshake is counted as a full recovery even though none survives the existing stability threshold, so Orca retries without end.
### Medium — The deadline clamp schedules one more SSH attempt at the exact exhaustion boundary
- Files/lines: `src/main/ssh/ssh-connection.ts:1280-1307`; timeout bound at `src/main/ssh/ssh-connection-utils.ts:35-38`.
- Impact: even on the failed-handshake path that does exhaust, Orca performs SSH work after the advertised 60-second bound. The final attempt can remain active for up to the 30-second connection timeout; after suspend/resume, an already-armed timer can likewise dial a dead host despite the new resume admission guard.
- Evidence: `delayMs` is clamped to `deadlineMs - nowMs`, which makes the last timer fire exactly at the deadline. Its callback checks only `disposed` and unconditionally calls `runReconnectAttempt()`; it does not re-check `isExhausted`. With fast failures, the delays reach 43 seconds and the next 30-second ladder step is clamped to 17 seconds, causing a new handshake to start at 60 seconds. The changed test at `src/main/ssh/ssh-connection.test.ts:559-592` only asserts fewer than all nine ladder attempts, so this extra boundary attempt still satisfies the test.
## Verification
- `git diff --check` passed.
- Targeted Vitest execution could not start because this worktree's installed dependencies do not contain the `vitest` binary (`pnpm exec vitest` returned `Command "vitest" not found`). Findings above are based on traced production call paths and the existing tests/contracts cited inline.
- No production or test files were edited; only this review report was created.
...(truncated if longer)...
## Report: D
# Review: stop endless SSH auto-reconnect (auto-reconnect budget)
## Verdict
The core mechanism is sound and well-tested for the desktop happy path: the budget is
correctly target-scoped (so it survives `SshConnectionManager` rebuilding the connection —
the exact hole that made the old ladder give-up unreachable), it resets on every successful
handshake and on user-initiated connects, the IPC-layer park is genuinely zero-cost, and the
relay-lost loop keeps its own separate bound. All 181 main-process SSH tests, all 538
pty-connection tests, and `typecheck` (node/cli/web projects) pass.
However, the branch claim ("stop keep reconnecting") is **falsified for paired/web clients**
(F1: the runtime RPC drops `initiator`, so every remote pane remount re-arms the budget and
launches a full connect storm), and two design consequences look like real functional
regressions against today's behavior: sleep/wake recovery is lost whenever an outage brackets
a sleep (F2), and the pause never self-heals after the host recovers, which permanently breaks
unattended flows like automation dispatch until a human clicks Connect (F3/F4).
## Findings
### F1 — HIGH (functionality regression / claim falsified): runtime RPC drops `initiator`, paired clients reset the budget and re-launch full connect storms
- **Files:** `src/renderer/src/web/web-preload-api.ts:3238-3244`, `src/main/runtime/rpc/methods/ssh.ts:28-37`, `src/main/ipc/ssh.ts:99-104`, `src/main/ipc/ssh.ts:982-993`
- **Evidence:** The web client's `window.api.ssh.connect` implementation forwards only
`{ targetId: args.targetId }` over runtime RPC — the `initiator` field the shared renderer
code now sends (`pty-connection.ts:854`, `useAutomationDispatchEvents.ts:171`) is silently
dropped. The RPC schema (`z.object({ targetId })`) can't carry it either, and the handler
calls `connectRegisteredSshTarget(targetId)` → `connectTarget(targetId)` whose default is
`'user'` → `sshAutoReconnectBudget.reset(targetId)` at `ssh.ts:993`.
- **Failure scenario:** Host goes down; desktop correctly parks after 60s. A paired web/mobile
client is open on the same workspace. Its SSH pane remount calls `ssh.connect` with
`initiator: 'auto'` → arrives at the hub as `'user'` → budget reset → full
`SshConnection.connect()` with `INITIAL_RETRY_ATTEMPTS = 5` × `CONNECT_TIMEOUT_MS = 30s`
against the dead host, every remount, forever. The desktop's parked
`'reconnection-failed'` broadcast is also overwritten by the new `'connecting'` states, so
the desktop UI flips out of the paused state the branch is supposed to guarantee. The
endless-reconnect loop this branch exists to kill is fully alive whenever any paired client
is attached.
- **Fix direction:** add `initiator` to the RPC schema + `connectRegisteredSshTarget`
signature, and thread it in `web-preload-api.ts`. Internal main-process callers
(`ephemeral-vm-runtime-ssh.ts:31`) should pass an explicit value too.
### F2 — HIGH (functionality regression): wall-clock budget counts time asleep; resume handler hard-skips, reversing #7773 wake recovery
- **Files:** `src/main/ssh/ssh-auto-reconnect-budget.ts:34-37`, `src/main/ipc/ssh.ts:576-580`, `src/main/ssh/ssh-connection.ts:1283-1285`
- **Evidence:** `isExhausted` compares raw `Date.now()` deltas, so time during which no retry
could possibly run (system sleep) consumes the budget. The new resume-handler guard
(`ssh.ts:578`) then `continue`s without even running `isRelayLinkAliveAfterResume`, and any
pre-sleep `reconnectTimer` that fires at wake gets exactly one attempt
(`runReconnectAttempt` has no budget check) racing NIC bring-up before
`scheduleReconnect` parks at `ssh-connection.ts:1283`.
- **Failure scenario:** Wifi drops (or the user closes the lid mid-outage) → `close` fires →
`scheduleReconnect` opens the 60s window → machine sleeps 10 minutes → wake on a working
network. Budget reads exhausted purely from sleep time; the resume path skips the target;
the one leftover timer attempt typically fails because wifi hasn't re-associated yet; every
SSH workspace is parked with "The SSH host is unreachable" even though the host is fine.
Before this branch, the #7773 resume probe + ladder reconnected automatically. Same shape:
budget exhausted pre-sleep, host recovers during sleep → wake does nothing at all.
- **Fix direction:** clock the budget in "awake time" (e.g. re-open/extend the window on
`powerMonitor` resume), or treat resume as a budget reset — waking near a *recovered* host
is precisely when one cheap probe is worth it.
### F3 — HIGH (stopping too early + never self-healing; product-level): 60s budget yields ~2 real attempts for a black-holing host, and the pause never expires
- **Files:** `src/main/ssh/ssh-auto-reconnect-budget.ts:10`, `src/main/ssh/ssh-connection-utils.ts:35-38`
- **Evidence:** `AUTO_RECONNECT_BUDGET_MS = 60_000` while `CONNECT_TIMEOUT_MS = 30_000` and
the ladder's delays alone sum to 103s. For an ETIMEDOUT-style outage: attempt 1 at t≈1s
times out at t≈31s, attempt 2 starts t≈33s and times out past the deadline → parked after
**2** attempts (the old behavior ran 9). The budget test suite pins that the pause never
expires (`'does not expire the pause on its own'`), and the only resets are a successful
handshake or a `'user'` connect.
- **Failure scenario:** Any blip longer than 60s — router reboot, VPN re-auth, ISP flap,
remote host restart — permanently converts every SSH workspace on that target into a manual
Connect click. Previously the (buggy but user-visible) remount-driven retry meant Orca
always eventually reconnected once the host returned. Unattended setups are hit hardest:
overnight scheduled automations against a host that blipped once at 2am fail until a human
intervenes (`useAutomationDispatchEvents.ts:171-177` now throws
"SSH target is unavailable." from the parked state without any SSH work — even when the
host has long since recovered).
- **Fix direction:** this is the intended trade, but the parameters undermine it: budget
should exceed one full ladder pass (≥ ~5 min), and/or decay the pause (re-open a window
after N minutes, or on network-change/resume events) so recovery doesn't require a human.
### F4 — MEDIUM (wrong budget accounting across connection lifecycle): stale exhausted window survives disconnect/removal
- **Files:** `src/main/ssh/ssh-connection.ts:1426-1453` (`disconnect()` — no budget reset), `src/main/ipc/ssh.ts:993` (only `'user'` *connects* reset)
- **Evidence:** Nothing clears a target's window on explicit disconnect, target removal, or
dispose; the only resets are a successful handshake and a `'user'` connect. `deadlineFor`
opened the window at the first `scheduleReconnect` of an outage.
- **Failure scenario:** Outage opens the window at t0; user clicks **Disconnect** at t0+30s
(a clean, deliberate stop — no give-up ever happened); host recovers. Hours later an
automation dispatch or a reopened workspace's pane remount issues an `'auto'` connect →
`isExhausted` is true (the 60s elapsed ages ago) → parked with "The SSH host is
unreachable. Automatic reconnect is paused" — a false statement produced without a single
probe, and the dispatch fails. The paused broadcast also paints `'reconnection-failed'`
over a target whose real state is `'disconnected'`, while a later `ssh:getState`
(`getPublicSshState`, `ssh.ts:391-394`) reports the connection-manager state again —
the two disagree.
- **Fix direction:** `sshAutoReconnectBudget.reset(targetId)` in `ssh:disconnect`,
`removeTarget`, and runtime-owned target teardown.
### F5 — MEDIUM (defense-in-depth / claim only holds at the IPC boundary): `reconnect()` and initial `connect()` never consult the budget
- **Files:** `src/main/ssh/ssh-connection.ts:868-882` (`reconnect()`), `src/main/ssh/ssh-connection.ts:605-656` (`connect()`)
- **Evidence:** `reconnect()` calls `runReconnectAttempt()` with no exhausted check — the
branch's own test pins "one failed try, then parked" for a replacement connection.
`connect()` runs up to `INITIAL_RETRY_ATTEMPTS = 5` transient retries (each up to 30s) with
no budget consult and never opens a window (windows only open in `scheduleReconnect`). The
zero-cost park exists solely in `connectTarget` (`ssh.ts:994`).
- **Failure scenario:** Any caller that reaches the manager without going through
`connectTarget`'s auto-gate gets the full storm on an exhausted target — today that is the
runtime RPC path (F1) and `connectRuntimeOwnedSshTarget`; tomorrow it is whatever new
internal caller someone adds. Also, for a host that is down *from the start* (never
handshakes), the window never opens at all, so repeated auto connects each burn up to
5 × 30s of SSH work indefinitely — the "stop" only covers the established-then-dropped
case.
- **Fix direction:** consult/park inside `SshConnection.connect()`/`reconnect()` (or in
`SshConnectionManager.connect`) so the bound holds regardless of entry point, and open the
window on failed initial connects too.
### F6 — LOW (UX): user recovery actions other than Connect don't re-arm
- **Files:** `src/main/ipc/ssh.ts:1323-1330` (`ssh:resetRelay`), `src/main/ipc/ssh.ts:1218` (`ssh:disconnect`)
- **Evidence:** Only `ssh:connect` with default initiator resets the budget. "Reset relay" —
an explicit user recovery gesture on the target — leaves the target parked; the follow-up
...(truncated if longer)...
@@ -1,17 +0,0 @@
# Rater 4 — blind ratings (reports A–D)
| report | evidence | regression_catch | false_positive_risk | actionability | best_unique | overreach |
|--------|----------|------------------|---------------------|---------------|-------------|-----------|
| A=grok | 5 | 4 | 5 | 4 | User Connect resets the budget *before* success, so a failed user reconnect leaves no open window and unbounded auto remounts resume (finding #2, scenario B) | none material — #6 (runtime-owned id inherits exhausted window) is lightly evidenced but plausible and marked Low |
| B=claude-opus | 5 | 5 | 5 | 5 | Parked state is broadcast-only/never authoritative (later `ssh:getState` disagrees) **and** the overlay derives copy from `status` alone, so `AUTO_RECONNECT_PAUSED_MESSAGE` never reaches the user (M2); also the proof that the IPC park test must hand-synthesize the window because no production path opens it cold (H4) | H3 step 1 states the woken pre-sleep timer attempt fails "before the network stack is up" as near-certain; that ordering is environment-dependent, though the conclusion survives without it |
| C=codex | 4 | 3 | 4 | 2 | `web-preload-api.test.ts:3417` already pins the lossy `{ targetId }` RPC payload — sharpest evidence for the initiator-drop finding; plus the deepest flap-storm analysis tied to `STABLE_CONNECTION_MS` semantics | Rating the flap-reset gap High as something "this change claims to bound": the pre-branch ladder behaved identically on flaps (C's own cited test predates the branch), so it's a claim-completeness gap, not a High regression |
| D=claude-fable | 5 | 4 | 4 | 4 | Stale exhausted window survives a clean user **Disconnect** → hours later an `'auto'` connect parks with a false "host unreachable" and fails automation dispatch without a single probe (F4); also F1's note that paired-client storms flip the desktop's parked UI state back | F7 assumes the paused message "rides into renderer overlays" — per A#7/B M2(b) the pane overlay ignores `state.error`, so the message is actually dropped; D missed that and built a Low on a partly wrong premise |
## Notes
1. Strong four-way consensus on the two load-bearing findings — runtime-RPC drops `initiator` (re-arms budget from paired/web clients) and the cold/initial-connect path never opens a window — so both should be treated as confirmed merge blockers.
2. B is the most complete: only report with all of {RPC drop, cold path, sleep/wake, 60s/never-expires, overlay message loss, authoritative-state gap}, plus verified negatives (clamp can't hit 0ms, relay loop terminates) and a prioritized pre-merge list.
3. A and D are close peers with disjoint blind spots: A missed sleep/wake entirely (framed the resume guard as "holds"), D missed the flap-reset gap and the overlay dropping the paused message.
4. C is accurate and adds unique evidence but has the narrowest regression coverage (no sleep/wake, no 60s-too-short/never-expires) and offers no fix directions; it also could not run tests (missing vitest binary, disclosed honestly).
5. FP discipline is good across all four; disagreements are severity framing, not fabricated facts.
# Unblinded by coordinator
@@ -1,16 +0,0 @@
# Blind peer ratings — rater-3
| report | evidence | regression_catch | false_positive_risk | actionability | best_unique | overreach |
|---|---|---|---|---|---|---|
| A=grok | 5 | 4 | 5 | 5 | Post-user-Connect hole: a user connect `reset()`s the window *before* success, so Connect→fail→next `'auto'` remount runs unbounded again with no window open (finding #2 scenario B) | #5 flap-storm framed as a branch failure while conceding the old ladder never stopped flaps either — scope creep, but honestly caveated |
| B=claude-opus | 5 | 4 | 4 | 5 | L4: the new `SSH_CONNECT_FAILED_STATUSES` gate is asymmetric — a racing relay `'error'` override now aborts a reattach that previously proceeded, while a relay-lost `'reconnecting'` override still reattaches mid-redeploy | M3 claims a stale window can park a *healthy live* session, but its own preconditions (superseded-attempt race) don't support that framing; D/F4 makes the sober version. Its "found clean" preload-contract audit is stated as verified while missing the web adapter |
| C=codex | 5 | 3 | 5 | 3 | Cites the *existing* flap test (12 post-handshake drops at 45s spacing, expected to keep reconnecting) as proof the budget cannot bound flap storms — plus `web-preload-api.test.ts:3417` pinning the lossy `{ targetId }` RPC payload | Medium on the deadline-clamp boundary attempt; it is one tail attempt (both B and D traced the same clamp and found no loop), so Low is the honest severity |
| D=claude-fable | 5 | 5 | 5 | 5 | F1's second-order effect: the paired client's storm overwrites the desktop's parked broadcast with `'connecting'`, flipping the desktop UI out of the paused state the branch guarantees. F4 (clean user Disconnect leaves the window armed → false "host is unreachable" hours later, no probe) and F6 (reset-relay doesn't re-arm) are also unique | F5 leans on future callers ("tomorrow it is whatever new internal caller someone adds") rather than a present defect, and largely restates F1/F3 |
Notes:
1. Consensus majors: (1) web/runtime RPC drops `initiator`, (2) initial-connect never opens a window, (3) 60s ≈ 2 attempts + pause never expires, (4) sleep/wake, (5) flap resets budget, (6) stale window after disconnect. Coverage: D 5/6, A 5/6 (misses sleep/wake), B 4/6 (misses web adapter + flaps), C 3/6.
2. #1 is the strongest finding in the set — A, C and D independently cite `web-preload-api.ts:3238-3244` with C adding a pinned test. B alone rates it Medium/"latent (mobile caller is user-driven)", which looks like a false negative: the shared renderer already sends `initiator:'auto'` through that same adapter, so it is live, not latent.
3. D is the most complete and best-calibrated: broadest real coverage, gates actually run (incl. web typecheck), fix direction on every finding, and an accurate "checked and found sound" section.
4. C is the most rigorous per finding but the narrowest — it never engages the "ZERO functional regressions" half of the mandate (no 60s-shortening, never-expiring-pause, or unattended-automation analysis) and gives no fix directions.
5. Two disputes worth resolving before synthesis: flaps (A/C say the budget can't bound them; B/D don't raise it) and the deadline-clamp severity (C Medium vs B/D "one tail attempt, no loop").
# Unblinded by coordinator
@@ -1,12 +0,0 @@
| report | evidence | regression_catch | false_positive_risk | actionability | best_unique | overreach |
|---|---:|---:|---:|---:|---|---|
| A=grok | 5 | 5 | 4 | 5 | Near-exhaustion auto connect can enter the full initial-retry loop after the nominal deadline. | Runtime-owned target budget retention is weak because its connect path is user-classified and resets the entry. |
| B=claude-opus | 5 | 5 | 3 | 5 | Broad failed-status gating can newly abort PTY reattach on a relay-error race, beyond the paused state. | The claim that an exhausted check can park a healthy live session relies on speculative stale-window paths and overstates relay-state clearing effects. |
| C=codex | 5 | 4 | 4 | 4 | The deadline clamp schedules a final handshake exactly at exhaustion, extending SSH work by up to the connect timeout. | Rapid-flap handling is plausibly incomplete, but High severity is debatable because successful handshakes are explicitly treated as new outage epochs. |
| D=claude-fable | 5 | 5 | 4 | 5 | Explicit disconnect can leave an aging budget that later falsely parks an automatic reconnect without probing. | The Reset Relay recovery claim is not fully traced through that handler's own reconnect lifecycle. |
Notes:
B is the broadest and best verified, but its long tail contains several low-value or speculative findings.
C is the most concise and trustworthy, with one particularly sharp boundary-timer catch.
A and D both identify the headline web/RPC and cold-connect bypasses with strong concrete evidence.
# Unblinded by coordinator
@@ -1,18 +0,0 @@
# Blind peer ratings (rater-1)
Scale 1–5. `false_positive_risk`: 5 = low FP / trustworthy; 1 = speculative.
| report | evidence | regression_catch | false_positive_risk | actionability | best_unique | overreach |
|--------|----------|------------------|---------------------|---------------|-------------|-----------|
| A=grok | 5 | 4 | 4 | 4 | Post–user-Connect fail clears budget window and never reopens it → unbounded auto remounts resume (finding 2B) | Web/RPC initiator drop as High without noting current paired mobile callers are still user-driven (latent vs live) |
| B=claude-opus | 5 | 5 | 4 | 5 | Sleep burns wall-clock budget + resume hard-skip defeats #7773 wake recovery (H3) | M3 “stale window parks a healthy live session” is thinner edge-case (needs exhausted window while still connected) |
| C=codex | 4 | 3 | 4 | 4 | Flap reset vs existing `STABLE_CONNECTION_MS` / flap test proving budget no-ops on sustained flaps | Elevating pre-existing flap-never-gives-up behavior to High new regression |
| D=claude-fable | 5 | 5 | 5 | 5 | Clean Disconnect leaves exhausted window → false “host unreachable” park hours later with no probe (F4) | F7 i18n note is low-value noise vs the real pause-UX issue (overlay ignoring `state.error`) |
## Notes
- Consensus core (A/B/C/D): cold `connect()` never opens the window; web/RPC drops `initiator`; 60s ≈ ~2 timeout attempts vs old 9.
- B and D uniquely catch sleep/wake budget accounting + resume skip; A and C miss that high-value regression.
- B is deepest (timelines, test gates, clean list) but slightly over-severities a few medium edges; D is tightest on FP discipline with F4 lifecycle accounting.
- C is accurate but narrow (4 findings) and under-catches product recovery regressions.
# Unblinded by coordinator
@@ -1,251 +0,0 @@
# Anonymized report A
# Review: stop-keep-reconnecting-to-remote (SSH auto-reconnect budget)
**Seat:** [reviewer]
**Scope:** `0ae91744086a3195e9a92e585dae6d814bedf808`..HEAD + all uncommitted/untracked changes
**Claim:** Stop endless SSH auto-reconnect to remote via a target-scoped wall-clock auto-reconnect budget
**Mode:** Review-only (no production edits)
## Executive summary
The change introduces a process-wide, **target-scoped** 60s wall-clock budget that (a) caps `SshConnection.scheduleReconnect` loops and (b) parks `initiator: 'auto'` `ssh:connect` calls once exhausted. That correctly addresses the core desktop failure mode where `SshConnectionManager.connect()` discards a non-connected connection and a pane remount restarts a fresh ladder forever.
However, the stop is **not complete across all auto-connect surfaces**, and the 60s budget **materially shortens** prior multi-minute retry behavior. Highest risks: web/runtime/RPC paths always default to `initiator: 'user'` (re-arming the budget), budget windows never open on cold/failed `connect()` paths (so remount storms can resume after a failed user Connect), and slow hosts get only ~2 timeout-sized attempts before permanent pause.
## What changed (map)
| Area | Change |
|------|--------|
| `ssh-auto-reconnect-budget.ts` (new) | Singleton `Map<targetId, startedAt>`; 60s budget; reset/clear |
| `ssh-connection.ts` | Open/check budget in `scheduleReconnect`; reset on successful handshake |
| `ipc/ssh.ts` | `connectTarget(targetId, initiator)`; auto parks when exhausted; power-monitor skip; test clear |
| Preload types/API | Optional `initiator?: 'user' \| 'auto'` (default `'user'`) |
| Renderer auto call sites | App startup, `pty-connection`, automation dispatch pass `initiator: 'auto'` |
| Tests | Budget unit tests; connection give-up rewritten; IPC park/re-arm tests |
## Branch claim — what holds
1. **Target-scoped budget survives connection replacement**
`SshConnectionManager.connect()` disconnects non-connected instances and builds a new `SshConnection` (fresh ladder). Budget lives on `sshAutoReconnectBudget` keyed by `targetId`, not on the connection object. Covered by test “keeps the give-up after the connection object is replaced”.
2. **Desktop auto IPC park after exhaustion does zero SSH work**
`connectTarget(..., 'auto')` short-circuits before `doConnect` / `connectionManager.connect` when exhausted. IPC test asserts `mockConnectionManager.connect` is not called.
3. **Pane remount no longer re-arms budget (desktop Electron)**
`pty-connection.waitForSshConnection` and App startup pass `initiator: 'auto'`. User UI Connect paths omit initiator → default `'user'` → re-arm. Correct split for those surfaces.
4. **False “connected” on parked auto connect is fixed**
Park returns `{ status: 'reconnection-failed' }` without throwing. `waitForSshConnection` now treats failed statuses as `connected: false` instead of reattaching a PTY to a dead host.
5. **Power resume respects exhaustion**
Resume path skips `manager.reconnect` when budget exhausted (wake is not a user ask).
---
## Findings (proven)
### 1. High — Web / runtime RPC always re-arms budget (initiator dropped)
**Impact:** On web client and any runtime RPC `ssh.connect`, every connect is treated as **user-initiated**, so `sshAutoReconnectBudget.reset(targetId)` runs and the give-up can never stick for those clients. Undermines the branch claim outside pure desktop Electron IPC.
**Evidence:**
```3238:3243:src/renderer/src/web/web-preload-api.ts
connect: async (args) => {
const { state } = await callRuntimeResult<{ state: SshConnectionState | null }>(
'ssh.connect',
{ targetId: args.targetId } // initiator stripped
)
```
```27:37:src/main/runtime/rpc/methods/ssh.ts
defineMethod({
name: 'ssh.connect',
params: SshTarget, // only { targetId }
handler: async (params) => {
try {
return { state: getPublicSshState(await connectRegisteredSshTarget(params.targetId)) }
```
```99:103:src/main/ipc/ssh.ts
export async function connectRegisteredSshTarget(targetId: string): Promise<SshConnectionState> {
...
return registeredConnectSshTarget(targetId) // second arg omitted → initiator defaults to 'user'
}
```
```991:993:src/main/ipc/ssh.ts
if (initiator === 'user') {
sshAutoReconnectBudget.reset(targetId)
```
`connectRuntimeEnvironmentSshTarget` also invokes RPC with only `{ targetId }` (overlay “Connect” may be intentional user; other auto runtime traffic still hits the same default).
**Failure scenario:** Dead remote host, budget exhausts on desktop main process, mobile/web/runtime auto-connect arrives via RPC → reset → new full connect / reconnect cycle resumes.
**Suggested fix direction (not applied):** Plumb `initiator` through RPC schema + web preload + `connectRegisteredSshTarget`; default RPC callers that are machine-driven to `'auto'`.
---
### 2. High — Budget window only opens in `scheduleReconnect`; cold auto-connect and post–user-fail remounts remain unbounded
**Impact:** The wall-clock stop only applies **after a successful connection has dropped** and entered the reconnect ladder. Cold failures and “user clicked Connect, still dead” do not open a budget window, so `initiator: 'auto'` remounts keep running full `connect()` / `INITIAL_RETRY` forever.
**Evidence:**
- Window open site is only `deadlineFor` inside `scheduleReconnect`:
```1280:1288:src/main/ssh/ssh-connection.ts
const nowMs = Date.now()
if (sshAutoReconnectBudget.isExhausted(this.target.id, nowMs)) {
this.setState('reconnection-failed', AUTO_RECONNECT_PAUSED_MESSAGE)
return
}
const deadlineMs = sshAutoReconnectBudget.deadlineFor(this.target.id, nowMs)
```
- `isExhausted` is **false** when no window is open:
```34:37:src/main/ssh/ssh-auto-reconnect-budget.ts
isExhausted(targetId: string, nowMs: number): boolean {
const startedAt = this.windowStartedAtMs.get(targetId)
return startedAt !== undefined && nowMs - startedAt >= this.budgetMs
}
```
- Initial `connect()` does 5 transient retries (`INITIAL_RETRY_ATTEMPTS=5`, `INITIAL_RETRY_DELAY_MS=2000`, `CONNECT_TIMEOUT_MS=30_000`) and on failure sets `error` / throws — **never** calls `deadlineFor` / `scheduleReconnect` (reconnect is only wired after a live transport drops).
- User Connect **clears** the window **before** success:
```991:993:src/main/ipc/ssh.ts
if (initiator === 'user') {
sshAutoReconnectBudget.reset(targetId)
```
**Failure scenario A (cold host):** Host never answers. Pane remount → `initiator: 'auto'` → not exhausted → `doConnect` → up to ~5×30s timeouts. Repeat forever. Budget never starts.
**Failure scenario B (user re-arm then fail):** Host dropped, budget exhausted, parked. User clicks Connect → `reset`. Connect fails → manager deletes connection, state `error`. Next remount auto-connects again with **no open window** → unbounded retries resume.
This actively falsifies a full reading of “stop keep reconnecting”: the stop is **post-drop ladder only**, not “all automatic SSH dial attempts.”
**Suggested fix direction:** Open the budget window on first auto-initiated failure (or on first auto `connectTarget` entry), and/or treat failed user reconnect as reopening a bounded auto window rather than a fully cleared map entry until success.
---
### 3. High — 60s wall-clock budget is a functional regression vs prior ~9-handshake / multi-minute ladder for slow hosts
**Impact:** Under the mandate “ZERO functional regressions first,” permanent pause after ~60s of wall clock is a deliberate but **user-visible behavior change**. Hosts with 30s connect timeouts get ~**two** handshake attempts, then permanent pause until manual Connect. Prior code guaranteed **9** failed handshakes before `reconnection-failed` (sum of delays alone 103s; with timeouts on order of several minutes).
**Evidence:**
```10:10:src/main/ssh/ssh-auto-reconnect-budget.ts
export const AUTO_RECONNECT_BUDGET_MS = 60_000
```
```35:38:src/main/ssh/ssh-connection-utils.ts
export const INITIAL_RETRY_ATTEMPTS = 5
export const INITIAL_RETRY_DELAY_MS = 2000
export const RECONNECT_BACKOFF_MS = [1000, 2000, 5000, 5000, 10000, 10000, 10000, 30000, 30000]
export const CONNECT_TIMEOUT_MS = 30_000
```
Test rewrite removed the pin that protected long retry:
- Old: `expect(connectAttempts).toBe(1 + RECONNECT_BACKOFF_MS.length)` with comment *“giving up early would strand a user on a flaky link”*
- New: stops on budget with `expect(connectAttempts).toBeLessThan(1 + RECONNECT_BACKOFF_MS.length)`
**Failure scenario:** Wi‑Fi / VPN blip of 70–90s. Old ladder kept trying. New budget marks `reconnection-failed` with paused message; auto recovery never resumes even after the network is healthy. User must notice overlay and click Connect.
**Note:** Fast `ECONNREFUSED`-style failures can still pack many attempts into 60s; the regression is sharpest for **timeout-class** unreachability (the common “remote asleep / path dead” case).
---
### 4. Medium — Near-exhaustion auto `ssh:connect` can still spend minutes of SSH work
**Impact:** Park path promises “zero SSH work” only when **already** exhausted. If budget has milliseconds left (or none open — finding #2), auto connect enters `doConnect` → `connectionManager.connect` → full `INITIAL_RETRY` with no mid-flight budget checks.
**Evidence:** Exhaustion checked only at `connectTarget` entry (`ipc/ssh.ts` ~994–1008). `SshConnection.connect()` loop has no `sshAutoReconnectBudget` reads. `runReconnectAttempt` also does not re-check budget before `attemptConnect` (only next `scheduleReconnect` does).
**Failure scenario:** Budget opened at T0. At T0+59s, pane remount fires auto connect → not exhausted → starts 5×30s attempts while the “60s budget” has already conceptually ended mid-flight.
---
### 5. Medium — Successful flap handshakes reset budget forever (claim incomplete for flap storms)
**Impact:** Not a pure regression vs the old ladder (flaps never incremented `consecutiveFailedAttempts` to give-up), but the new budget **does not** stop hosts that briefly complete handshake then drop.
**Evidence:**
```1319:1321:src/main/ssh/ssh-connection.ts
this.reconnectLadder.markConnected(Date.now())
sshAutoReconnectBudget.reset(this.target.id)
```
Same reset on initial successful `connect()` (~618–619).
**Failure scenario:** Host accepts TCP/SSH every few seconds then dies. Each success resets the 60s window → continuous reconnect CPU/network; power-monitor and remount keep participating.
---
### 6. Low — Runtime-owned target IDs can retain exhausted budget across lifecycle
**Impact:** `removeTarget` / disconnect do not call `sshAutoReconnectBudget.reset/clear` for that id. Runtime-owned IDs are stable (`getRuntimeOwnedSshTargetId(runtimeId)`). A later re-upsert of the same runtime id may inherit an exhausted window until a user-classified connect resets it. Manual targets mint new ids on add (`ssh-${Date.now()}-...`), so less exposed.
**Evidence:** `sshAutoReconnectBudget.clear()` only in `resetSshHandlerStateForTests`; production resets only on user connect / successful handshake / full test clear. `removeRegisteredSshTarget` does not touch the budget map.
---
### 7. Low — User-facing overlay may not show the paused message body
**Impact:** Main sets `error: AUTO_RECONNECT_PAUSED_MESSAGE`, but `TerminalSshReconnectOverlay.messageForStatus` uses generic translated copy for `reconnection-failed`, not `state.error`. User may not learn that Connect is required to re-arm auto retry (status bar / other surfaces may still show raw error depending on wiring).
**Evidence:** Overlay switch for `reconnection-failed` → fixed translate string; park message constant is more specific.
---
## Correctness / lifecycle checks (no issue or pre-existing)
| Check | Result |
|-------|--------|
| Dispose + timer cleanup | `disconnect` clears `reconnectTimer`; timer callback bails if disposed |
| Double `scheduleReconnect` | Guard `if (this.reconnectTimer) return` retained |
| Delay clamp past deadline | `delayMs = min(decision.delayMs, max(0, deadlineMs - nowMs))` avoids sleeping past budget; last attempt may still fire at boundary (acceptable) |
| User Connect re-arm | Default initiator + explicit reset; overlay Connect omits initiator → user |
| Automation dispatch | Uses `initiator: 'auto'`; non-connected after park → `skipped_unavailable` |
| Relay override mask on park | Clears relay lost backoff + override before broadcasting paused state |
| Performance (hot path) | O(1) Map ops; no new per-byte stream work; no extra listeners; timers only existing reconnect path |
| Unbounded buffers | Budget map grows with distinct target ids only (small); `clear()` in tests only |
## Performance
- **Win:** After exhaustion, desktop auto remounts and power-resume skip SSH work (primary goal).
- **Risk:** Finding #2/#4 still allow long INITIAL_RETRY storms (CPU + remote sshd load) when the window is not open or not yet exhausted.
- **No new render/stream path cost** beyond one extra status check in `waitForSshConnection`.
## Test gaps
1. No test that cold auto `ssh:connect` (never connected / never dropped) eventually stops.
2. No test that user Connect → fail → auto remount remains bounded.
3. No test that RPC/web connect preserves `initiator: 'auto'`.
4. Removed exact 9-attempt guarantee; no assertion documenting intended max attempts under 30s timeouts (~2).
5. No integration test that power-monitor + exhausted budget leaves connection untouched (unit-level only via code read).
## Verdict on branch claim
| Claim piece | Verdict |
|-------------|---------|
| Stop endless ladder after drop (desktop) | **Mostly true** (60s hard stop; survives connection replace) |
| Stop endless pane-remount restart after give-up (desktop Electron) | **True** when window already open/exhausted |
| Stop all automatic remote reconnect forever | **False** — web/RPC re-arm (#1); cold/post-user-fail paths (#2); flaps (#5) |
| Zero functional regression | **False** — shorter retry window (#3) |
**Top severity: High**
**Findings count: 7** (3 High, 2 Medium, 2 Low)
## Recommended priority order (for implementer, not this seat)
1. Plumb `initiator` through RPC + web preload + `connectRegisteredSshTarget`.
2. Open budget on auto connect failures (not only post-drop `scheduleReconnect`); define semantics after user-reset failure.
3. Revisit 60s vs CONNECT_TIMEOUT (e.g. budget that counts failed attempts, or ≥ ladder delay sum + N timeouts) if “no functional regression” is hard requirement.
4. Optionally re-check budget inside `connect()` / `runReconnectAttempt` before each attempt.
@@ -1,186 +0,0 @@
# Anonymized report B
# Branch review — `stop-keep-reconnecting-to-remote` (seat: [reviewer])
**Scope reviewed:** `0ae9174408..HEAD` (no commits on branch) **plus all uncommitted + untracked changes**.
10 modified files, 2 new untracked files (`src/main/ssh/ssh-auto-reconnect-budget.{ts,test.ts}`).
**Gates run (read-only):**
- `vitest run src/main/ssh/ssh-auto-reconnect-budget.test.ts src/main/ssh/ssh-connection.test.ts src/main/ipc/ssh.test.ts` → **181 passed / 3 files passed**
- `vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-connection.test.ts` → **538 passed**
- `oxfmt --check -c .oxfmtrc.json <9 changed files>` → clean
**Verdict:** the core mechanism is sound and correctly target-scoped (the branch's central insight — that a connection-scoped bound can never stick because `SshConnectionManager.connect()` discards and rebuilds the connection — is right and well-tested). But the branch **does not fully deliver its claim** (the initial-connect path is entirely unbounded, H4) and it **introduces four high-severity recovery regressions**: a 61-second outage permanently disables SSH auto-reconnect for the life of the process, only two attempts survive on the exact failure mode being targeted, and sleep/wake recovery (#7773) is defeated.
**Findings: 4 High, 4 Medium, 4 Low. Top severity: High.**
---
## Key constants (used throughout)
| Constant | Value | Source |
|---|---|---|
| `AUTO_RECONNECT_BUDGET_MS` | **60_000** | `ssh-auto-reconnect-budget.ts:10` |
| `CONNECT_TIMEOUT_MS` | **30_000** | `ssh-connection-utils.ts:38` |
| `INITIAL_RETRY_ATTEMPTS` / delay | **5** / 2_000 | `ssh-connection-utils.ts:35-36` |
| `RECONNECT_BACKOFF_MS` | `[1s,2s,5s,5s,10s,10s,10s,30s,30s]` (9 entries) | `ssh-connection-utils.ts:37` |
| `DEFAULT_BOUNDED_SSH_RELAY_GRACE_PERIOD_SECONDS` | **24 h** | `shared/ssh-types.ts:6` |
---
## H1 — A 61-second outage permanently disables SSH auto-reconnect for the process lifetime (High)
**Files:** `src/main/ssh/ssh-auto-reconnect-budget.ts:34-37`, `src/main/ssh/ssh-connection.ts:1283-1285`, `src/main/ipc/ssh.ts:993-1008`
`isExhausted()` has no expiry, and the only two things that clear the window are (a) a successful `attemptConnect` and (b) a `'user'`-initiated `ssh:connect`. Once paused, **nothing in the product ever retries**, so (a) can never happen. The branch's own test pins this as intentional (`ssh-auto-reconnect-budget.test.ts:46-52`, "does not expire the pause on its own", asserted at 24 hours).
**Failure scenario (routine, not exotic):** a remote host reboots (typically 30–120 s), sshd is restarted, a VPN re-establishes, or Wi-Fi roams between APs. At T+60 s Orca gives up. The host is back at T+90 s. Every SSH workspace, terminal, agent pane and automation on that host stays dead until a human opens the desktop app and clicks Connect — **for as long as the app runs** (Orca desktop sessions run for days/weeks).
**Why this is a regression, not just a policy choice:** it contradicts the surrounding system's own design assumptions.
- The remote relay defaults to a **24-hour** bounded grace (`shared/ssh-types.ts:6`) precisely so a user can lose the link and come back to live remote PTYs. Orca now stops trying 60 seconds into an outage the remote side is prepared to survive for a day.
- `SshReconnectLadder` deliberately caps flap delays at `FLAP_DELAY_CAP_MS` so a reconnect lands *inside* relay grace (`ssh-reconnect-ladder.ts:12-20`). That machinery is now moot past 60 s.
- Previously the ladder gave 9 handshake failures before `reconnection-failed`.
**Suggested fix:** keep the pause (it is the right idea) but re-arm it on a slow schedule — e.g. a single long-interval, zero-cost TCP/`connect`-probe every 5–15 min, or expire the window after N minutes. That preserves "no busy loop" while not requiring a human to be sitting at the machine.
---
## H2 — Only **two** reconnect attempts survive on the exact failure mode this targets (High)
**Files:** `src/main/ssh/ssh-connection.ts:1277-1307` (budget check + delay clamp), `ssh-connection-utils.ts:37-38`
An unreachable host (packet black hole — `ETIMEDOUT`, the case named in the branch's own test at `ssh-connection.test.ts:572`) fails each attempt only after `CONNECT_TIMEOUT_MS` = 30 s. Trace against a 60 s budget:
```
t=0 drop → scheduleReconnect → window opens, deadline = 60_000; delay = min(1000, 60000) = 1000
t=1_000 attempt #1 → times out at t≈31_000 → not exhausted (31k<60k); delay = min(2000, 29_000) = 2000
t=33_000 attempt #2 → times out at t≈63_000 → 63k ≥ 60k → EXHAUSTED → paused permanently
```
**Two** retries, versus **nine** before. Anything that takes longer than ~63 s to come back is never auto-recovered. (For a *fast*-failing host — instant `ECONNREFUSED` — ~8 attempts still fit, so the effective retry count now silently depends on how the host fails.)
**Evidence of lost coverage:** the branch replaced the exact-count assertion with an inequality:
```diff
- expect(connectAttempts).toBe(1 + RECONNECT_BACKOFF_MS.length)
+ expect(connectAttempts).toBeLessThan(1 + RECONNECT_BACKOFF_MS.length)
```
(`ssh-connection.test.ts:582-590`). The old assertion's comment — "Counting a failure twice, or giving up early, would strand a user on a flaky link" — was the guard against exactly this. Nothing now pins how many attempts a user actually gets; a future `CONNECT_TIMEOUT_MS` bump to 40 s would silently reduce this to **one** attempt with all tests still green.
---
## H3 — Sleep/wake: the budget burns while the process is suspended, and the resume handler now skips reconnect entirely (High)
**Files:** `src/main/ipc/ssh.ts:576-579`, `src/main/ssh/ssh-connection.ts:1280-1285`
`isExhausted` compares raw `Date.now()` deltas, so **wall-clock time in which Orca made zero attempts still spends the budget** — including time the process is suspended by the OS.
**Failure scenario:** Wi-Fi drops as the lid closes → the `close`/`error` handler (`ssh-connection.ts:1259-1274`) calls `scheduleReconnect()`, which **opens the window at T** and arms a 1 s timer. The machine suspends. It resumes 8 hours later:
1. The pending `setTimeout` fires immediately on wake; `runReconnectAttempt` runs before the network stack is up and fails — which is exactly why `RESUME_PROBE_ATTEMPTS = 2` with a retry exists (`ssh.ts:546-547`, #7773). `scheduleReconnect` then sees 8 hours of "spent budget" → **permanent pause**.
2. The new guard at `ssh.ts:576-579` makes the `powerMonitor` `'resume'` handler `continue` for that target, so `isRelayLinkAliveAfterResume()` + `manager.reconnect(targetId)` — the whole #7773 wake-recovery path — never runs either.
Net effect: **after any overnight/lunch sleep where the drop was noticed before suspend, every SSH workspace is parked and needs a manual click.** The comment on the guard ("a laptop that wakes near a dead host does no SSH work at all") is true, but so is the inverse: a laptop that wakes near a *healthy* host also does no SSH work.
**Suggested fix:** treat a wake as a new outage epoch — call `sshAutoReconnectBudget.reset(targetId)` in `onResume` before the liveness probe (or measure attempts / active retry time rather than raw `Date.now()` deltas).
---
## H4 — The initial-connect path is completely unbounded: the branch claim is only half delivered (High)
**Files:** `src/main/ssh/ssh-connection.ts:604-655` (`connect()`), `:1287` (`deadlineFor` — the only production call site), `src/main/ipc/ssh.ts:991-1008`
The budget window is opened **only** by `scheduleReconnect()`. `SshConnection.connect()` fails by *throwing* (`ssh-connection.ts:653-655`) and never calls `scheduleReconnect()`; there is no `setupDisconnectHandler` on a client that never reached `ready`. Therefore **a target that has never connected in this app session never opens a budget window, and `isExhausted` is permanently `false` for it.**
**Failure scenario — the most common "Orca keeps reconnecting to a dead remote":** the remote host is gone (VM deleted, laptop off the corp network, host decommissioned) and Orca is restarted, or the target simply never came up this session. Then:
- App startup auto-reconnect (`App.tsx:1024-1029`) → `connectTarget(..., 'auto')` → not exhausted → full `doConnect`.
- `connectionManager.connect()` → `SshConnection.connect()` → `INITIAL_RETRY_ATTEMPTS = 5` attempts, each up to `CONNECT_TIMEOUT_MS = 30 s`, plus 2 s sleeps ≈ **160 s of SSH work per call**.
- Every SSH pane remount (`pty-connection.ts:852-856`), every automation dispatch (`useAutomationDispatchEvents.ts:171`), every renderer reload repeats it, forever. `connectInFlight` dedupes only *concurrent* calls.
**Evidence:** the new IPC test cannot reach the parked branch through product behavior — it has to synthesize the window by hand:
```ts
sshAutoReconnectBudget.deadlineFor('ssh-1', Date.now() - AUTO_RECONNECT_BUDGET_MS)
```
(`ssh.test.ts:598`, `:624`). No production path opens a window on a failed connect.
**Suggested fix:** charge the window on failed connects too — e.g. call `sshAutoReconnectBudget.deadlineFor(targetId, Date.now())` at the top of the `initiator === 'auto'` branch in `connectTarget` (opening the window on the first automatic connect), and/or in `SshConnection.connect()`'s terminal failure path.
---
## M1 — Unattended automations become permanently unavailable with no re-arm path (Medium)
**File:** `src/renderer/src/hooks/useAutomationDispatchEvents.ts:168-186`
The dispatch connect is now marked `'auto'`, so after a give-up it returns the paused state and the handler throws `'SSH target is unavailable.'` → `markDispatchResult({ status: 'skipped_unavailable' })`. **Nothing in the automation path can ever re-arm the budget** (only a desktop `'user'` connect or a successful connection can, and per H1 no connection will be attempted). A 61-second blip at 03:00 kills every remaining scheduled run against that host until a human opens the app. Combined with H1/H3 this is the most user-visible consequence of the branch.
At minimum this is a behavior change that should be called out in the PR body; ideally scheduled automations get their own re-arm (a scheduled run is arguably closer to "the user asking" than a pane remount is).
---
## M2 — The paused state is broadcast-only, and its message never reaches the user (Medium)
**Files:** `src/main/ipc/ssh.ts:993-1008`, `src/main/ipc/ssh.ts:392-395`, `src/renderer/src/components/terminal-pane/TerminalSshReconnectOverlay.tsx:38-53`
(a) **Not authoritative.** The early return `broadcastSshState(...)`s a `reconnection-failed` state but never writes it into `connectionManager`. A subsequent `ssh:getState` / `getPublicSshState` returns whatever the connection object holds — which is `'disconnected'`, or `undefined` if the manager deleted the connection after a failed connect (`ssh-connection-manager.ts:50-53`). A renderer that re-reads state (new window, paired client sync, `useAutomationDispatchEvents.ts:167`) sees a different status than the one that was pushed.
(b) **Message is dropped by the pane UI.** `TerminalSshReconnectOverlay` derives its copy purely from `status` and ignores `state.error` entirely (`:47-52`): a paused target renders *"The SSH connection to X failed. Connect again to continue this terminal session."* — identical to any other error, with no hint that Orca has **stopped trying**. `AUTO_RECONNECT_PAUSED_MESSAGE` is the branch's whole UX premise and it never reaches the primary surface.
---
## M3 — The exhausted check runs before the live-connection fast path, so a stale window can park a healthy session (Medium)
**File:** `src/main/ipc/ssh.ts:991-1008` (runs before `awaitTargetLifecycle` and before `doConnect`'s live-session fast path at `:1078-1090`)
The parked branch fires purely on `isExhausted`, with no check that the target is actually down. It also actively `clearRelayLostBackoff` + `clearRelayStateOverride` and publishes `reconnection-failed`, so a false positive doesn't just fail a connect — it tears the pane's reattach out from under a live relay and wipes the relay's own recovery state.
The budget is only cleared at two sites (`ssh-connection.ts:619`, `:1321`), both of which require a successful `attemptConnect`. Paths that leave a **stale open window**:
- `runReconnectAttempt` succeeds but is superseded — `:1315-1317` returns **before** `markConnected`/`reset`.
- `SshConnection.disconnect()` (`:1425-1450`) resets the ladder but **not** the budget; likewise `ssh:resetRelay` and `ssh:terminateSessions`. A user-driven reset while a window is open leaves it armed; 60 s later an automatic pane remount is parked.
Cheap defenses: return early only when there is no live `connected` session for the target, and call `sshAutoReconnectBudget.reset()` from the disconnect/reset-relay paths.
---
## M4 — Contract gap: paired clients (mobile, `orca serve`, runtime environments) cannot mark a connect as `'auto'` (Medium)
**Files:** `src/main/runtime/rpc/methods/ssh.ts:28-38`, `src/main/ipc/ssh.ts:99-104`, `src/renderer/src/runtime/runtime-environment-ssh-state.ts:178-196`
`connectRegisteredSshTarget(targetId)` takes no initiator and therefore uses the `'user'` default — it **resets the budget**. Every `ssh.connect` RPC from a paired mobile client, a `orca serve` web client, or a nested runtime environment re-arms automatic retries. Today the only mobile caller is user-driven (`mobile/src/components/NewWorktreeModal.tsx:536`), so this is latent rather than live — but the preload contract grew an `initiator` param while the RPC schema (`params: SshTarget`) did not, so the give-up is silently defeatable on paired setups and will drift the first time a client adds an automatic reconnect.
---
## L1 — The ladder's give-up arm is now dead code (Low)
`ssh-connection.ts:1288-1292` (`'Max reconnection attempts reached'`) is unreachable with a 60 s budget: nine consecutive failed handshakes cannot fit (the delay table alone sums to 103 s). `SshReconnectLadder`'s documented contract — "`reconnection-failed` stays reachable exactly after `RECONNECT_BACKOFF_MS.length` failed handshakes" (`ssh-reconnect-ladder.ts:26-31`) — is no longer exercised end to end. Either delete the arm or state that it is a lower bound retained for a configurable budget.
## L2 — Unthrottled broadcast fan-out on the "costs nothing" path (Low)
`ssh.ts:1006` broadcasts on **every** parked auto-connect: `webContents.send('ssh:state-changed')` plus `currentRuntime?.notifySshStateChanged` to every paired client, each going through `withSshRemotePlatform` (authority + platform lookups). A window restore or layout change that remounts N SSH panes emits N identical `reconnection-failed` pushes. Negligible next to the SSH work it replaces, but the branch's stated goal for this path is "zero cost" (`ssh.test.ts:606`), and this is the one thing it still does per call. A cheap dedupe (skip the broadcast if the last published state for the target is already this paused state) would make that claim literal.
## L3 — Budget entries are never removed on target deletion (Low)
`ssh:removeTarget` does not call `sshAutoReconnectBudget.reset(targetId)`, so `windowStartedAtMs` retains an entry per target id ever seen (trivial memory, but unbounded across a long session with many ephemeral/runtime-owned targets). More concretely: a target removed while paused and later re-adopted under the same id (`ssh:importConfig({ reAdopt: true })`) comes back **already paused**.
## L4 — The new pane-side failure gate changes behavior beyond the branch's scope, asymmetrically (Low)
`pty-connection.ts:815-819` + `:857-863`. `doConnect` returns `getPublicSshState(targetId)` (`ssh.ts:1214`), which **prefers `relayStateOverrides`**. `setOnTerminalRelayError` publishes status `'error'` (`ssh.ts:717-727`) and can land between the `connected` broadcast and that return. In that race the pane now aborts the reattach (`reportError`) instead of proceeding as it always has. Conversely a relay-lost `'reconnecting'` override is *not* in `SSH_CONNECT_FAILED_STATUSES`, so the pane still reattaches into a relay that is mid-redeploy. If the intent is only to catch the new paused state, gating on `status === 'reconnection-failed' && error === AUTO_RECONNECT_PAUSED_MESSAGE` (or a dedicated field) would avoid changing the relay-error path at all.
---
## What I checked and found clean
- **No new timers, listeners or unbounded buffers.** The budget is one `Map<string, number>` with one entry per target; `reset()` deletes on success. No new `setTimeout`/`setInterval`, no new IPC channels, no listener registration.
- **No busy reconnect loop from the delay clamp.** `Math.min(decision.delayMs, Math.max(0, deadlineMs - nowMs))` (`ssh-connection.ts:1295`) cannot produce a 0 ms delay: `isExhausted` uses `>=`, so a non-exhausted window guarantees `deadline - now ≥ 1` ms, and the clamp only ever *lowers* the delay. Worst case is one final attempt fired up to 30 s early — one extra attempt, not a loop.
- **No double-reconnect storm.** The parked path returns before `connectInFlight`/`awaitTargetLifecycle`, so it cannot interleave with an in-flight connect; the `'user'` reset happens before any await, so it cannot be lost to a race.
- **Dispose path unchanged.** `scheduleReconnect`'s `this.disposed || this.reconnectTimer` guard, the timer-clear in `disconnect()`, and generation fencing are untouched; the budget check is a pure read added ahead of them.
- **Relay-lost loop still terminates.** `'reconnection-failed'` is already in `TRANSPORT_TERMINAL_STATUSES` (`ssh.ts:322-327`), so a paused transport clears the relay redeploy loop rather than leaving it re-arming at `RELAY_LOST_MAX_DELAY_MS`.
- **Preload/IPC contract is backward compatible.** `initiator` is optional at every layer and defaults to `'user'` (`ssh.ts:1060`, `preload/index.ts:4398-4404`, `api-types.ts:3409-3413`), so unmarked callers keep the pre-existing behavior. All 11 renderer `ssh.connect` call sites were checked: the 9 left unmarked are genuinely user-initiated (Connect buttons, add-repo, composer send, workspace create, settings pane, status row, disconnect dialog) and correctly re-arm the budget; the recovery affordance exists for `reconnection-failed` (`TerminalSshReconnectOverlay.tsx:35`, `SshDisconnectedDialog.tsx:76`).
- **Test-isolation hazard handled.** The process-wide singleton is cleared in both `resetSshHandlerStateForTests` (`ssh.ts:1537`) and `ssh-connection.test.ts:352-354`; without those, one test's exhausted window would suppress reconnects in every later test on the same target id. Good catch by the author.
- **Formatting/tests:** `oxfmt --check` clean on all 9 changed files; 181 + 538 tests pass. Two unrelated formatting-only hunks in `ssh-connection.test.ts:1853-1915` are noise but harmless.
---
## Recommended minimum before merge
1. **H4** — open the budget window on failed automatic connects, or the branch does not fix its headline case (dead host + app restart / pane remounts).
2. **H3** — `reset()` on `powerMonitor` `'resume'`; do not count suspended wall-clock as spent budget.
3. **H1/H2** — either raise the budget above `CONNECT_TIMEOUT_MS × 3` (so the "unreachable host" case gets more than two tries) or add a slow re-arm so the pause is recoverable without a human. As written, a 61-second outage is a permanent, human-only-recoverable failure.
4. **M2(b)** — surface `AUTO_RECONNECT_PAUSED_MESSAGE` in `TerminalSshReconnectOverlay`; otherwise the user is never told that Orca stopped trying.
@@ -1,40 +0,0 @@
# Anonymized report C
# [reviewer] review: SSH auto-reconnect budget
Reviewed `0ae91744086a3195e9a92e585dae6d814bedf808..HEAD` plus all uncommitted and untracked changes, with emphasis on reconnect admission, timers, connection replacement, renderer/RPC contracts, SSH flaps, and automation dispatch.
## Findings
### High — Paired/web clients discard `initiator: 'auto'`, so their automatic reconnects re-arm the budget
- Files/lines: `src/renderer/src/web/web-preload-api.ts:3238-3242`, `src/main/runtime/rpc/methods/ssh.ts:11-13,28-32`, `src/main/ipc/ssh.ts:99-103,982-994`; affected new callers include `src/renderer/src/App.tsx:1024-1028`, `src/renderer/src/components/terminal-pane/pty-connection.ts:850-857`, and `src/renderer/src/hooks/useAutomationDispatchEvents.ts:168-174`.
- Impact: automatic startup reconnect, pane remount, and automation dispatch in the web/paired renderer can continue creating SSH work after the target's budget should be exhausted. The branch claim holds only for the Electron preload path, not for another supported renderer using the same `PreloadApi` contract.
- Evidence: the changed callers pass `initiator: 'auto'`, but `createSshApi().connect` forwards only `{ targetId: args.targetId }`. The runtime RPC schema contains only `targetId`, and its handler calls `connectRegisteredSshTarget(targetId)` without an initiator. That reaches `connectTarget` through a wrapper whose omitted second argument takes the new default `'user'`; line 993 then deletes the target's budget on every web automatic connect. The existing web preload test at `src/renderer/src/web/web-preload-api.test.ts:3417` even pins the lossy `{ targetId }` RPC payload, so the new native-preload tests cannot catch this path.
- Concrete scenario: a paired/web session remounts an SSH pane for a dead host. `waitForSshConnection` labels the call automatic, the web adapter drops that label, main resets the budget as a user connect, and the next remount repeats indefinitely.
### High — Initial automatic connection failures never open the budget window
- Files/lines: `src/main/ipc/ssh.ts:982-1009`, `src/main/ssh/ssh-connection.ts:605-655`, `src/main/ssh/ssh-connection-manager.ts:27-55`, `src/renderer/src/components/terminal-pane/pty-connection.ts:840-878`.
- Impact: if Orca has no established connection object (the common cold-start/dead-host case), startup or pane-driven automatic connects can run the five-attempt initial retry loop on every remount forever. This directly preserves the endless reconnect behavior the branch is meant to stop, and each round may perform up to five 30-second connection attempts plus retry sleeps.
- Evidence: `connectTarget(..., 'auto')` only asks `isExhausted`; it never calls `deadlineFor`, so a target with no existing window is admitted. `SshConnection.connect()` performs `INITIAL_RETRY_ATTEMPTS` and, on terminal failure, does not open or charge the global budget. `SshConnectionManager.connect()` then removes the failed connection object. The renderer deletes its per-target promise in `finally`, so the next pane remount creates a fresh automatic call. The only production call to `deadlineFor` is `scheduleReconnect()` at `src/main/ssh/ssh-connection.ts:1287`, a path reached after an already-connected transport drops, not after an initial connect fails.
- Concrete scenario: Orca restores a tab while its host is offline. The App startup call times out/deferred-connects, main eventually exhausts its initial attempts and deletes the connection, and focusing/remounting that tab starts a new five-attempt sequence because `isExhausted` remains false forever.
### High — Every momentary SSH handshake resets the target budget, so a rapid flap storm is still endless
- Files/lines: `src/main/ssh/ssh-connection.ts:615-620,1255-1272,1310-1322`; existing stability semantics are in `src/main/ssh/ssh-reconnect-ladder.ts:4-5,37-43,63-66` and the still-present flap test at `src/main/ssh/ssh-connection.test.ts:638-659`.
- Impact: a host/proxy that repeatedly completes SSH setup and immediately closes can reconnect forever, retaining the timer/SSH/relay churn this change claims to bound. This is not a hypothetical classification gap: the existing code already distinguishes a momentary handshake from a stable 60-second connection when deciding whether an outage is over.
- Evidence: after `attemptConnect()` resolves, `runReconnectAttempt()` immediately calls `sshAutoReconnectBudget.reset()`. If the socket then emits `end`, `close`, or `error`, `setupDisconnectHandler()` opens a brand-new 60-second window. Repeating that sequence always resets before exhaustion. By contrast, `SshReconnectLadder` only treats a connection as stable after `STABLE_CONNECTION_MS`; its test drives 12 post-handshake drops spaced 45 seconds apart and expects reconnecting to continue, which demonstrates the new budget currently has no effect on sustained flapping.
- Concrete scenario: an SSH server accepts authentication but a proxy, policy, or broken relay-side transport closes the channel a few seconds later. Every handshake is counted as a full recovery even though none survives the existing stability threshold, so Orca retries without end.
### Medium — The deadline clamp schedules one more SSH attempt at the exact exhaustion boundary
- Files/lines: `src/main/ssh/ssh-connection.ts:1280-1307`; timeout bound at `src/main/ssh/ssh-connection-utils.ts:35-38`.
- Impact: even on the failed-handshake path that does exhaust, Orca performs SSH work after the advertised 60-second bound. The final attempt can remain active for up to the 30-second connection timeout; after suspend/resume, an already-armed timer can likewise dial a dead host despite the new resume admission guard.
- Evidence: `delayMs` is clamped to `deadlineMs - nowMs`, which makes the last timer fire exactly at the deadline. Its callback checks only `disposed` and unconditionally calls `runReconnectAttempt()`; it does not re-check `isExhausted`. With fast failures, the delays reach 43 seconds and the next 30-second ladder step is clamped to 17 seconds, causing a new handshake to start at 60 seconds. The changed test at `src/main/ssh/ssh-connection.test.ts:559-592` only asserts fewer than all nine ladder attempts, so this extra boundary attempt still satisfies the test.
## Verification
- `git diff --check` passed.
- Targeted Vitest execution could not start because this worktree's installed dependencies do not contain the `vitest` binary (`pnpm exec vitest` returned `Command "vitest" not found`). Findings above are based on traced production call paths and the existing tests/contracts cited inline.
- No production or test files were edited; only this review report was created.
@@ -1,176 +0,0 @@
# Anonymized report D
# Review: stop endless SSH auto-reconnect (auto-reconnect budget)
## Verdict
The core mechanism is sound and well-tested for the desktop happy path: the budget is
correctly target-scoped (so it survives `SshConnectionManager` rebuilding the connection —
the exact hole that made the old ladder give-up unreachable), it resets on every successful
handshake and on user-initiated connects, the IPC-layer park is genuinely zero-cost, and the
relay-lost loop keeps its own separate bound. All 181 main-process SSH tests, all 538
pty-connection tests, and `typecheck` (node/cli/web projects) pass.
However, the branch claim ("stop keep reconnecting") is **falsified for paired/web clients**
(F1: the runtime RPC drops `initiator`, so every remote pane remount re-arms the budget and
launches a full connect storm), and two design consequences look like real functional
regressions against today's behavior: sleep/wake recovery is lost whenever an outage brackets
a sleep (F2), and the pause never self-heals after the host recovers, which permanently breaks
unattended flows like automation dispatch until a human clicks Connect (F3/F4).
## Findings
### F1 — HIGH (functionality regression / claim falsified): runtime RPC drops `initiator`, paired clients reset the budget and re-launch full connect storms
- **Files:** `src/renderer/src/web/web-preload-api.ts:3238-3244`, `src/main/runtime/rpc/methods/ssh.ts:28-37`, `src/main/ipc/ssh.ts:99-104`, `src/main/ipc/ssh.ts:982-993`
- **Evidence:** The web client's `window.api.ssh.connect` implementation forwards only
`{ targetId: args.targetId }` over runtime RPC — the `initiator` field the shared renderer
code now sends (`pty-connection.ts:854`, `useAutomationDispatchEvents.ts:171`) is silently
dropped. The RPC schema (`z.object({ targetId })`) can't carry it either, and the handler
calls `connectRegisteredSshTarget(targetId)` → `connectTarget(targetId)` whose default is
`'user'` → `sshAutoReconnectBudget.reset(targetId)` at `ssh.ts:993`.
- **Failure scenario:** Host goes down; desktop correctly parks after 60s. A paired web/mobile
client is open on the same workspace. Its SSH pane remount calls `ssh.connect` with
`initiator: 'auto'` → arrives at the hub as `'user'` → budget reset → full
`SshConnection.connect()` with `INITIAL_RETRY_ATTEMPTS = 5` × `CONNECT_TIMEOUT_MS = 30s`
against the dead host, every remount, forever. The desktop's parked
`'reconnection-failed'` broadcast is also overwritten by the new `'connecting'` states, so
the desktop UI flips out of the paused state the branch is supposed to guarantee. The
endless-reconnect loop this branch exists to kill is fully alive whenever any paired client
is attached.
- **Fix direction:** add `initiator` to the RPC schema + `connectRegisteredSshTarget`
signature, and thread it in `web-preload-api.ts`. Internal main-process callers
(`ephemeral-vm-runtime-ssh.ts:31`) should pass an explicit value too.
### F2 — HIGH (functionality regression): wall-clock budget counts time asleep; resume handler hard-skips, reversing #7773 wake recovery
- **Files:** `src/main/ssh/ssh-auto-reconnect-budget.ts:34-37`, `src/main/ipc/ssh.ts:576-580`, `src/main/ssh/ssh-connection.ts:1283-1285`
- **Evidence:** `isExhausted` compares raw `Date.now()` deltas, so time during which no retry
could possibly run (system sleep) consumes the budget. The new resume-handler guard
(`ssh.ts:578`) then `continue`s without even running `isRelayLinkAliveAfterResume`, and any
pre-sleep `reconnectTimer` that fires at wake gets exactly one attempt
(`runReconnectAttempt` has no budget check) racing NIC bring-up before
`scheduleReconnect` parks at `ssh-connection.ts:1283`.
- **Failure scenario:** Wifi drops (or the user closes the lid mid-outage) → `close` fires →
`scheduleReconnect` opens the 60s window → machine sleeps 10 minutes → wake on a working
network. Budget reads exhausted purely from sleep time; the resume path skips the target;
the one leftover timer attempt typically fails because wifi hasn't re-associated yet; every
SSH workspace is parked with "The SSH host is unreachable" even though the host is fine.
Before this branch, the #7773 resume probe + ladder reconnected automatically. Same shape:
budget exhausted pre-sleep, host recovers during sleep → wake does nothing at all.
- **Fix direction:** clock the budget in "awake time" (e.g. re-open/extend the window on
`powerMonitor` resume), or treat resume as a budget reset — waking near a *recovered* host
is precisely when one cheap probe is worth it.
### F3 — HIGH (stopping too early + never self-healing; product-level): 60s budget yields ~2 real attempts for a black-holing host, and the pause never expires
- **Files:** `src/main/ssh/ssh-auto-reconnect-budget.ts:10`, `src/main/ssh/ssh-connection-utils.ts:35-38`
- **Evidence:** `AUTO_RECONNECT_BUDGET_MS = 60_000` while `CONNECT_TIMEOUT_MS = 30_000` and
the ladder's delays alone sum to 103s. For an ETIMEDOUT-style outage: attempt 1 at t≈1s
times out at t≈31s, attempt 2 starts t≈33s and times out past the deadline → parked after
**2** attempts (the old behavior ran 9). The budget test suite pins that the pause never
expires (`'does not expire the pause on its own'`), and the only resets are a successful
handshake or a `'user'` connect.
- **Failure scenario:** Any blip longer than 60s — router reboot, VPN re-auth, ISP flap,
remote host restart — permanently converts every SSH workspace on that target into a manual
Connect click. Previously the (buggy but user-visible) remount-driven retry meant Orca
always eventually reconnected once the host returned. Unattended setups are hit hardest:
overnight scheduled automations against a host that blipped once at 2am fail until a human
intervenes (`useAutomationDispatchEvents.ts:171-177` now throws
"SSH target is unavailable." from the parked state without any SSH work — even when the
host has long since recovered).
- **Fix direction:** this is the intended trade, but the parameters undermine it: budget
should exceed one full ladder pass (≥ ~5 min), and/or decay the pause (re-open a window
after N minutes, or on network-change/resume events) so recovery doesn't require a human.
### F4 — MEDIUM (wrong budget accounting across connection lifecycle): stale exhausted window survives disconnect/removal
- **Files:** `src/main/ssh/ssh-connection.ts:1426-1453` (`disconnect()` — no budget reset), `src/main/ipc/ssh.ts:993` (only `'user'` *connects* reset)
- **Evidence:** Nothing clears a target's window on explicit disconnect, target removal, or
dispose; the only resets are a successful handshake and a `'user'` connect. `deadlineFor`
opened the window at the first `scheduleReconnect` of an outage.
- **Failure scenario:** Outage opens the window at t0; user clicks **Disconnect** at t0+30s
(a clean, deliberate stop — no give-up ever happened); host recovers. Hours later an
automation dispatch or a reopened workspace's pane remount issues an `'auto'` connect →
`isExhausted` is true (the 60s elapsed ages ago) → parked with "The SSH host is
unreachable. Automatic reconnect is paused" — a false statement produced without a single
probe, and the dispatch fails. The paused broadcast also paints `'reconnection-failed'`
over a target whose real state is `'disconnected'`, while a later `ssh:getState`
(`getPublicSshState`, `ssh.ts:391-394`) reports the connection-manager state again —
the two disagree.
- **Fix direction:** `sshAutoReconnectBudget.reset(targetId)` in `ssh:disconnect`,
`removeTarget`, and runtime-owned target teardown.
### F5 — MEDIUM (defense-in-depth / claim only holds at the IPC boundary): `reconnect()` and initial `connect()` never consult the budget
- **Files:** `src/main/ssh/ssh-connection.ts:868-882` (`reconnect()`), `src/main/ssh/ssh-connection.ts:605-656` (`connect()`)
- **Evidence:** `reconnect()` calls `runReconnectAttempt()` with no exhausted check — the
branch's own test pins "one failed try, then parked" for a replacement connection.
`connect()` runs up to `INITIAL_RETRY_ATTEMPTS = 5` transient retries (each up to 30s) with
no budget consult and never opens a window (windows only open in `scheduleReconnect`). The
zero-cost park exists solely in `connectTarget` (`ssh.ts:994`).
- **Failure scenario:** Any caller that reaches the manager without going through
`connectTarget`'s auto-gate gets the full storm on an exhausted target — today that is the
runtime RPC path (F1) and `connectRuntimeOwnedSshTarget`; tomorrow it is whatever new
internal caller someone adds. Also, for a host that is down *from the start* (never
handshakes), the window never opens at all, so repeated auto connects each burn up to
5 × 30s of SSH work indefinitely — the "stop" only covers the established-then-dropped
case.
- **Fix direction:** consult/park inside `SshConnection.connect()`/`reconnect()` (or in
`SshConnectionManager.connect`) so the bound holds regardless of entry point, and open the
window on failed initial connects too.
### F6 — LOW (UX): user recovery actions other than Connect don't re-arm
- **Files:** `src/main/ipc/ssh.ts:1323-1330` (`ssh:resetRelay`), `src/main/ipc/ssh.ts:1218` (`ssh:disconnect`)
- **Evidence:** Only `ssh:connect` with default initiator resets the budget. "Reset relay" —
an explicit user recovery gesture on the target — leaves the target parked; the follow-up
auto connect from the pane still returns the paused state. Overlaps F4's fix.
### F7 — LOW (message/i18n consistency): `AUTO_RECONNECT_PAUSED_MESSAGE` is raw English in the state channel
- **File:** `src/main/ssh/ssh-auto-reconnect-budget.ts:12-13`
- **Evidence:** The string rides `SshConnectionState.error` into renderer overlays and
automation errors untranslated. Pre-existing errors on this channel ("Max reconnection
attempts reached") have the same shape, so this matches local convention — noting only
because this one is a *directive* ("use Connect to retry") the UI surfaces prominently,
including on clients (mobile/web) that may not render a Connect affordance for hub targets.
## Explicitly checked and found sound
- **Park is zero-cost at the IPC layer:** exhausted auto `connectTarget` returns before
`awaitTargetLifecycle`/`connectInFlight`/`doConnect`; no manager call, no session churn
(`ssh.ts:994-1009`). The ipc test asserts `connect` is never called.
- **Budget resets on both success paths** (`connect()` at `ssh-connection.ts:619`,
`runReconnectAttempt` at `:1321`) and reset placement is after a real handshake, so the
system-ssh probe's cosmetic 'connected' can't re-earn budget. `connectViaSystemSsh` skips
the reset but has no production callers.
- **No double-reconnect storms:** single `reconnectTimer` guard unchanged; delay clamp
(`Math.min(decision.delayMs, remaining)`) can shorten only the final wait, producing at
most one tail attempt — no busy loop at the deadline (verified against fast-failing
ECONNREFUSED: ~8 bounded attempts, then park).
- **Relay-lost loop independence:** the relay redeploy loop keeps its own bounded
attempts/backoff and calls `session.reconnect`, never `connectTarget`, so it neither
bypasses nor re-arms the budget; the parked path clears its backoff + override so the
paused state isn't masked (`ssh.ts:999-1000`, matching the `getPublicSshState` precedence).
- **All user-facing Connect surfaces default to `'user'`** (reconnect overlay, SshPane,
status row, disconnected dialog, composer, automations-page source connect) — correct
re-arm semantics; renderer callers that are genuinely automatic (startup reconnect,
pane remount, automation dispatch) are marked `'auto'`.
- **Renderer failed-status handling:** `ssh:connect` rejects on real connect failures
(`doConnect` throws), so the new `SSH_CONNECT_FAILED_STATUSES` check in
`waitForSshConnection` only intercepts resolved terminal states (chiefly the parked one) —
in-flight `'connecting'`/`'deploying-relay'` still resolve to a reattach as before, and the
connect promise is deleted in `finally`, so a later user Connect lets panes retry.
- **Test isolation:** singleton cleared in `resetSshHandlerStateForTests` and in the
connection suite's `beforeEach`; ipc suite resets in `beforeEach` (line 344).
- **No leaks/timers added:** no new listeners; one extra Map lookup + a longer `console.warn`
per schedule — negligible.
- **Gates:** `vitest` — ssh-auto-reconnect-budget + ipc/ssh + ssh-connection: 181 passed;
pty-connection (project config): 538 passed. `npm run typecheck` (node, cli, web): clean.
## Summary ranking
| # | Severity | One-liner |
|---|----------|-----------|
| F1 | High | Runtime RPC drops `initiator`; paired clients re-arm the budget + full connect storm (claim falsified) |
| F2 | High | Budget counts sleep time; resume handler skips → wake recovery regressed vs #7773 |
| F3 | High | 60s budget ≈ 2 attempts vs old 9; pause never expires → any >60s blip needs a human, breaks unattended automations |
| F4 | Medium | Exhausted window survives clean disconnect/removal → false "host unreachable" parks hours later |
| F5 | Medium | Budget enforced only at the IPC gate; `connect()`/`reconnect()` and never-connected hosts bypass it |
| F6 | Low | Reset-relay/disconnect don't re-arm; only Connect does |
| F7 | Low | Paused directive string untranslated / shown on clients without a Connect affordance |
@@ -1,175 +0,0 @@
# Review: stop endless SSH auto-reconnect (auto-reconnect budget)
Seat: claude-fable · Base: 0ae9174408..HEAD + uncommitted/untracked · Date: 2026-08-02
## Verdict
The core mechanism is sound and well-tested for the desktop happy path: the budget is
correctly target-scoped (so it survives `SshConnectionManager` rebuilding the connection —
the exact hole that made the old ladder give-up unreachable), it resets on every successful
handshake and on user-initiated connects, the IPC-layer park is genuinely zero-cost, and the
relay-lost loop keeps its own separate bound. All 181 main-process SSH tests, all 538
pty-connection tests, and `typecheck` (node/cli/web projects) pass.
However, the branch claim ("stop keep reconnecting") is **falsified for paired/web clients**
(F1: the runtime RPC drops `initiator`, so every remote pane remount re-arms the budget and
launches a full connect storm), and two design consequences look like real functional
regressions against today's behavior: sleep/wake recovery is lost whenever an outage brackets
a sleep (F2), and the pause never self-heals after the host recovers, which permanently breaks
unattended flows like automation dispatch until a human clicks Connect (F3/F4).
## Findings
### F1 — HIGH (functionality regression / claim falsified): runtime RPC drops `initiator`, paired clients reset the budget and re-launch full connect storms
- **Files:** `src/renderer/src/web/web-preload-api.ts:3238-3244`, `src/main/runtime/rpc/methods/ssh.ts:28-37`, `src/main/ipc/ssh.ts:99-104`, `src/main/ipc/ssh.ts:982-993`
- **Evidence:** The web client's `window.api.ssh.connect` implementation forwards only
`{ targetId: args.targetId }` over runtime RPC — the `initiator` field the shared renderer
code now sends (`pty-connection.ts:854`, `useAutomationDispatchEvents.ts:171`) is silently
dropped. The RPC schema (`z.object({ targetId })`) can't carry it either, and the handler
calls `connectRegisteredSshTarget(targetId)` → `connectTarget(targetId)` whose default is
`'user'` → `sshAutoReconnectBudget.reset(targetId)` at `ssh.ts:993`.
- **Failure scenario:** Host goes down; desktop correctly parks after 60s. A paired web/mobile
client is open on the same workspace. Its SSH pane remount calls `ssh.connect` with
`initiator: 'auto'` → arrives at the hub as `'user'` → budget reset → full
`SshConnection.connect()` with `INITIAL_RETRY_ATTEMPTS = 5` × `CONNECT_TIMEOUT_MS = 30s`
against the dead host, every remount, forever. The desktop's parked
`'reconnection-failed'` broadcast is also overwritten by the new `'connecting'` states, so
the desktop UI flips out of the paused state the branch is supposed to guarantee. The
endless-reconnect loop this branch exists to kill is fully alive whenever any paired client
is attached.
- **Fix direction:** add `initiator` to the RPC schema + `connectRegisteredSshTarget`
signature, and thread it in `web-preload-api.ts`. Internal main-process callers
(`ephemeral-vm-runtime-ssh.ts:31`) should pass an explicit value too.
### F2 — HIGH (functionality regression): wall-clock budget counts time asleep; resume handler hard-skips, reversing #7773 wake recovery
- **Files:** `src/main/ssh/ssh-auto-reconnect-budget.ts:34-37`, `src/main/ipc/ssh.ts:576-580`, `src/main/ssh/ssh-connection.ts:1283-1285`
- **Evidence:** `isExhausted` compares raw `Date.now()` deltas, so time during which no retry
could possibly run (system sleep) consumes the budget. The new resume-handler guard
(`ssh.ts:578`) then `continue`s without even running `isRelayLinkAliveAfterResume`, and any
pre-sleep `reconnectTimer` that fires at wake gets exactly one attempt
(`runReconnectAttempt` has no budget check) racing NIC bring-up before
`scheduleReconnect` parks at `ssh-connection.ts:1283`.
- **Failure scenario:** Wifi drops (or the user closes the lid mid-outage) → `close` fires →
`scheduleReconnect` opens the 60s window → machine sleeps 10 minutes → wake on a working
network. Budget reads exhausted purely from sleep time; the resume path skips the target;
the one leftover timer attempt typically fails because wifi hasn't re-associated yet; every
SSH workspace is parked with "The SSH host is unreachable" even though the host is fine.
Before this branch, the #7773 resume probe + ladder reconnected automatically. Same shape:
budget exhausted pre-sleep, host recovers during sleep → wake does nothing at all.
- **Fix direction:** clock the budget in "awake time" (e.g. re-open/extend the window on
`powerMonitor` resume), or treat resume as a budget reset — waking near a *recovered* host
is precisely when one cheap probe is worth it.
### F3 — HIGH (stopping too early + never self-healing; product-level): 60s budget yields ~2 real attempts for a black-holing host, and the pause never expires
- **Files:** `src/main/ssh/ssh-auto-reconnect-budget.ts:10`, `src/main/ssh/ssh-connection-utils.ts:35-38`
- **Evidence:** `AUTO_RECONNECT_BUDGET_MS = 60_000` while `CONNECT_TIMEOUT_MS = 30_000` and
the ladder's delays alone sum to 103s. For an ETIMEDOUT-style outage: attempt 1 at t≈1s
times out at t≈31s, attempt 2 starts t≈33s and times out past the deadline → parked after
**2** attempts (the old behavior ran 9). The budget test suite pins that the pause never
expires (`'does not expire the pause on its own'`), and the only resets are a successful
handshake or a `'user'` connect.
- **Failure scenario:** Any blip longer than 60s — router reboot, VPN re-auth, ISP flap,
remote host restart — permanently converts every SSH workspace on that target into a manual
Connect click. Previously the (buggy but user-visible) remount-driven retry meant Orca
always eventually reconnected once the host returned. Unattended setups are hit hardest:
overnight scheduled automations against a host that blipped once at 2am fail until a human
intervenes (`useAutomationDispatchEvents.ts:171-177` now throws
"SSH target is unavailable." from the parked state without any SSH work — even when the
host has long since recovered).
- **Fix direction:** this is the intended trade, but the parameters undermine it: budget
should exceed one full ladder pass (≥ ~5 min), and/or decay the pause (re-open a window
after N minutes, or on network-change/resume events) so recovery doesn't require a human.
### F4 — MEDIUM (wrong budget accounting across connection lifecycle): stale exhausted window survives disconnect/removal
- **Files:** `src/main/ssh/ssh-connection.ts:1426-1453` (`disconnect()` — no budget reset), `src/main/ipc/ssh.ts:993` (only `'user'` *connects* reset)
- **Evidence:** Nothing clears a target's window on explicit disconnect, target removal, or
dispose; the only resets are a successful handshake and a `'user'` connect. `deadlineFor`
opened the window at the first `scheduleReconnect` of an outage.
- **Failure scenario:** Outage opens the window at t0; user clicks **Disconnect** at t0+30s
(a clean, deliberate stop — no give-up ever happened); host recovers. Hours later an
automation dispatch or a reopened workspace's pane remount issues an `'auto'` connect →
`isExhausted` is true (the 60s elapsed ages ago) → parked with "The SSH host is
unreachable. Automatic reconnect is paused" — a false statement produced without a single
probe, and the dispatch fails. The paused broadcast also paints `'reconnection-failed'`
over a target whose real state is `'disconnected'`, while a later `ssh:getState`
(`getPublicSshState`, `ssh.ts:391-394`) reports the connection-manager state again —
the two disagree.
- **Fix direction:** `sshAutoReconnectBudget.reset(targetId)` in `ssh:disconnect`,
`removeTarget`, and runtime-owned target teardown.
### F5 — MEDIUM (defense-in-depth / claim only holds at the IPC boundary): `reconnect()` and initial `connect()` never consult the budget
- **Files:** `src/main/ssh/ssh-connection.ts:868-882` (`reconnect()`), `src/main/ssh/ssh-connection.ts:605-656` (`connect()`)
- **Evidence:** `reconnect()` calls `runReconnectAttempt()` with no exhausted check — the
branch's own test pins "one failed try, then parked" for a replacement connection.
`connect()` runs up to `INITIAL_RETRY_ATTEMPTS = 5` transient retries (each up to 30s) with
no budget consult and never opens a window (windows only open in `scheduleReconnect`). The
zero-cost park exists solely in `connectTarget` (`ssh.ts:994`).
- **Failure scenario:** Any caller that reaches the manager without going through
`connectTarget`'s auto-gate gets the full storm on an exhausted target — today that is the
runtime RPC path (F1) and `connectRuntimeOwnedSshTarget`; tomorrow it is whatever new
internal caller someone adds. Also, for a host that is down *from the start* (never
handshakes), the window never opens at all, so repeated auto connects each burn up to
5 × 30s of SSH work indefinitely — the "stop" only covers the established-then-dropped
case.
- **Fix direction:** consult/park inside `SshConnection.connect()`/`reconnect()` (or in
`SshConnectionManager.connect`) so the bound holds regardless of entry point, and open the
window on failed initial connects too.
### F6 — LOW (UX): user recovery actions other than Connect don't re-arm
- **Files:** `src/main/ipc/ssh.ts:1323-1330` (`ssh:resetRelay`), `src/main/ipc/ssh.ts:1218` (`ssh:disconnect`)
- **Evidence:** Only `ssh:connect` with default initiator resets the budget. "Reset relay" —
an explicit user recovery gesture on the target — leaves the target parked; the follow-up
auto connect from the pane still returns the paused state. Overlaps F4's fix.
### F7 — LOW (message/i18n consistency): `AUTO_RECONNECT_PAUSED_MESSAGE` is raw English in the state channel
- **File:** `src/main/ssh/ssh-auto-reconnect-budget.ts:12-13`
- **Evidence:** The string rides `SshConnectionState.error` into renderer overlays and
automation errors untranslated. Pre-existing errors on this channel ("Max reconnection
attempts reached") have the same shape, so this matches local convention — noting only
because this one is a *directive* ("use Connect to retry") the UI surfaces prominently,
including on clients (mobile/web) that may not render a Connect affordance for hub targets.
## Explicitly checked and found sound
- **Park is zero-cost at the IPC layer:** exhausted auto `connectTarget` returns before
`awaitTargetLifecycle`/`connectInFlight`/`doConnect`; no manager call, no session churn
(`ssh.ts:994-1009`). The ipc test asserts `connect` is never called.
- **Budget resets on both success paths** (`connect()` at `ssh-connection.ts:619`,
`runReconnectAttempt` at `:1321`) and reset placement is after a real handshake, so the
system-ssh probe's cosmetic 'connected' can't re-earn budget. `connectViaSystemSsh` skips
the reset but has no production callers.
- **No double-reconnect storms:** single `reconnectTimer` guard unchanged; delay clamp
(`Math.min(decision.delayMs, remaining)`) can shorten only the final wait, producing at
most one tail attempt — no busy loop at the deadline (verified against fast-failing
ECONNREFUSED: ~8 bounded attempts, then park).
- **Relay-lost loop independence:** the relay redeploy loop keeps its own bounded
attempts/backoff and calls `session.reconnect`, never `connectTarget`, so it neither
bypasses nor re-arms the budget; the parked path clears its backoff + override so the
paused state isn't masked (`ssh.ts:999-1000`, matching the `getPublicSshState` precedence).
- **All user-facing Connect surfaces default to `'user'`** (reconnect overlay, SshPane,
status row, disconnected dialog, composer, automations-page source connect) — correct
re-arm semantics; renderer callers that are genuinely automatic (startup reconnect,
pane remount, automation dispatch) are marked `'auto'`.
- **Renderer failed-status handling:** `ssh:connect` rejects on real connect failures
(`doConnect` throws), so the new `SSH_CONNECT_FAILED_STATUSES` check in
`waitForSshConnection` only intercepts resolved terminal states (chiefly the parked one) —
in-flight `'connecting'`/`'deploying-relay'` still resolve to a reattach as before, and the
connect promise is deleted in `finally`, so a later user Connect lets panes retry.
- **Test isolation:** singleton cleared in `resetSshHandlerStateForTests` and in the
connection suite's `beforeEach`; ipc suite resets in `beforeEach` (line 344).
- **No leaks/timers added:** no new listeners; one extra Map lookup + a longer `console.warn`
per schedule — negligible.
- **Gates:** `vitest` — ssh-auto-reconnect-budget + ipc/ssh + ssh-connection: 181 passed;
pty-connection (project config): 538 passed. `npm run typecheck` (node, cli, web): clean.
## Summary ranking
| # | Severity | One-liner |
|---|----------|-----------|
| F1 | High | Runtime RPC drops `initiator`; paired clients re-arm the budget + full connect storm (claim falsified) |
| F2 | High | Budget counts sleep time; resume handler skips → wake recovery regressed vs #7773 |
| F3 | High | 60s budget ≈ 2 attempts vs old 9; pause never expires → any >60s blip needs a human, breaks unattended automations |
| F4 | Medium | Exhausted window survives clean disconnect/removal → false "host unreachable" parks hours later |
| F5 | Medium | Budget enforced only at the IPC gate; `connect()`/`reconnect()` and never-connected hosts bypass it |
| F6 | Low | Reset-relay/disconnect don't re-arm; only Connect does |
| F7 | Low | Paused directive string untranslated / shown on clients without a Connect affordance |
@@ -1,184 +0,0 @@
# Branch review — `stop-keep-reconnecting-to-remote` (seat: claude-opus)
**Scope reviewed:** `0ae9174408..HEAD` (no commits on branch) **plus all uncommitted + untracked changes**.
10 modified files, 2 new untracked files (`src/main/ssh/ssh-auto-reconnect-budget.{ts,test.ts}`).
**Gates run (read-only):**
- `vitest run src/main/ssh/ssh-auto-reconnect-budget.test.ts src/main/ssh/ssh-connection.test.ts src/main/ipc/ssh.test.ts` → **181 passed / 3 files passed**
- `vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-connection.test.ts` → **538 passed**
- `oxfmt --check -c .oxfmtrc.json <9 changed files>` → clean
**Verdict:** the core mechanism is sound and correctly target-scoped (the branch's central insight — that a connection-scoped bound can never stick because `SshConnectionManager.connect()` discards and rebuilds the connection — is right and well-tested). But the branch **does not fully deliver its claim** (the initial-connect path is entirely unbounded, H4) and it **introduces four high-severity recovery regressions**: a 61-second outage permanently disables SSH auto-reconnect for the life of the process, only two attempts survive on the exact failure mode being targeted, and sleep/wake recovery (#7773) is defeated.
**Findings: 4 High, 4 Medium, 4 Low. Top severity: High.**
---
## Key constants (used throughout)
| Constant | Value | Source |
|---|---|---|
| `AUTO_RECONNECT_BUDGET_MS` | **60_000** | `ssh-auto-reconnect-budget.ts:10` |
| `CONNECT_TIMEOUT_MS` | **30_000** | `ssh-connection-utils.ts:38` |
| `INITIAL_RETRY_ATTEMPTS` / delay | **5** / 2_000 | `ssh-connection-utils.ts:35-36` |
| `RECONNECT_BACKOFF_MS` | `[1s,2s,5s,5s,10s,10s,10s,30s,30s]` (9 entries) | `ssh-connection-utils.ts:37` |
| `DEFAULT_BOUNDED_SSH_RELAY_GRACE_PERIOD_SECONDS` | **24 h** | `shared/ssh-types.ts:6` |
---
## H1 — A 61-second outage permanently disables SSH auto-reconnect for the process lifetime (High)
**Files:** `src/main/ssh/ssh-auto-reconnect-budget.ts:34-37`, `src/main/ssh/ssh-connection.ts:1283-1285`, `src/main/ipc/ssh.ts:993-1008`
`isExhausted()` has no expiry, and the only two things that clear the window are (a) a successful `attemptConnect` and (b) a `'user'`-initiated `ssh:connect`. Once paused, **nothing in the product ever retries**, so (a) can never happen. The branch's own test pins this as intentional (`ssh-auto-reconnect-budget.test.ts:46-52`, "does not expire the pause on its own", asserted at 24 hours).
**Failure scenario (routine, not exotic):** a remote host reboots (typically 30–120 s), sshd is restarted, a VPN re-establishes, or Wi-Fi roams between APs. At T+60 s Orca gives up. The host is back at T+90 s. Every SSH workspace, terminal, agent pane and automation on that host stays dead until a human opens the desktop app and clicks Connect — **for as long as the app runs** (Orca desktop sessions run for days/weeks).
**Why this is a regression, not just a policy choice:** it contradicts the surrounding system's own design assumptions.
- The remote relay defaults to a **24-hour** bounded grace (`shared/ssh-types.ts:6`) precisely so a user can lose the link and come back to live remote PTYs. Orca now stops trying 60 seconds into an outage the remote side is prepared to survive for a day.
- `SshReconnectLadder` deliberately caps flap delays at `FLAP_DELAY_CAP_MS` so a reconnect lands *inside* relay grace (`ssh-reconnect-ladder.ts:12-20`). That machinery is now moot past 60 s.
- Previously the ladder gave 9 handshake failures before `reconnection-failed`.
**Suggested fix:** keep the pause (it is the right idea) but re-arm it on a slow schedule — e.g. a single long-interval, zero-cost TCP/`connect`-probe every 5–15 min, or expire the window after N minutes. That preserves "no busy loop" while not requiring a human to be sitting at the machine.
---
## H2 — Only **two** reconnect attempts survive on the exact failure mode this targets (High)
**Files:** `src/main/ssh/ssh-connection.ts:1277-1307` (budget check + delay clamp), `ssh-connection-utils.ts:37-38`
An unreachable host (packet black hole — `ETIMEDOUT`, the case named in the branch's own test at `ssh-connection.test.ts:572`) fails each attempt only after `CONNECT_TIMEOUT_MS` = 30 s. Trace against a 60 s budget:
```
t=0 drop → scheduleReconnect → window opens, deadline = 60_000; delay = min(1000, 60000) = 1000
t=1_000 attempt #1 → times out at t≈31_000 → not exhausted (31k<60k); delay = min(2000, 29_000) = 2000
t=33_000 attempt #2 → times out at t≈63_000 → 63k ≥ 60k → EXHAUSTED → paused permanently
```
**Two** retries, versus **nine** before. Anything that takes longer than ~63 s to come back is never auto-recovered. (For a *fast*-failing host — instant `ECONNREFUSED` — ~8 attempts still fit, so the effective retry count now silently depends on how the host fails.)
**Evidence of lost coverage:** the branch replaced the exact-count assertion with an inequality:
```diff
- expect(connectAttempts).toBe(1 + RECONNECT_BACKOFF_MS.length)
+ expect(connectAttempts).toBeLessThan(1 + RECONNECT_BACKOFF_MS.length)
```
(`ssh-connection.test.ts:582-590`). The old assertion's comment — "Counting a failure twice, or giving up early, would strand a user on a flaky link" — was the guard against exactly this. Nothing now pins how many attempts a user actually gets; a future `CONNECT_TIMEOUT_MS` bump to 40 s would silently reduce this to **one** attempt with all tests still green.
---
## H3 — Sleep/wake: the budget burns while the process is suspended, and the resume handler now skips reconnect entirely (High)
**Files:** `src/main/ipc/ssh.ts:576-579`, `src/main/ssh/ssh-connection.ts:1280-1285`
`isExhausted` compares raw `Date.now()` deltas, so **wall-clock time in which Orca made zero attempts still spends the budget** — including time the process is suspended by the OS.
**Failure scenario:** Wi-Fi drops as the lid closes → the `close`/`error` handler (`ssh-connection.ts:1259-1274`) calls `scheduleReconnect()`, which **opens the window at T** and arms a 1 s timer. The machine suspends. It resumes 8 hours later:
1. The pending `setTimeout` fires immediately on wake; `runReconnectAttempt` runs before the network stack is up and fails — which is exactly why `RESUME_PROBE_ATTEMPTS = 2` with a retry exists (`ssh.ts:546-547`, #7773). `scheduleReconnect` then sees 8 hours of "spent budget" → **permanent pause**.
2. The new guard at `ssh.ts:576-579` makes the `powerMonitor` `'resume'` handler `continue` for that target, so `isRelayLinkAliveAfterResume()` + `manager.reconnect(targetId)` — the whole #7773 wake-recovery path — never runs either.
Net effect: **after any overnight/lunch sleep where the drop was noticed before suspend, every SSH workspace is parked and needs a manual click.** The comment on the guard ("a laptop that wakes near a dead host does no SSH work at all") is true, but so is the inverse: a laptop that wakes near a *healthy* host also does no SSH work.
**Suggested fix:** treat a wake as a new outage epoch — call `sshAutoReconnectBudget.reset(targetId)` in `onResume` before the liveness probe (or measure attempts / active retry time rather than raw `Date.now()` deltas).
---
## H4 — The initial-connect path is completely unbounded: the branch claim is only half delivered (High)
**Files:** `src/main/ssh/ssh-connection.ts:604-655` (`connect()`), `:1287` (`deadlineFor` — the only production call site), `src/main/ipc/ssh.ts:991-1008`
The budget window is opened **only** by `scheduleReconnect()`. `SshConnection.connect()` fails by *throwing* (`ssh-connection.ts:653-655`) and never calls `scheduleReconnect()`; there is no `setupDisconnectHandler` on a client that never reached `ready`. Therefore **a target that has never connected in this app session never opens a budget window, and `isExhausted` is permanently `false` for it.**
**Failure scenario — the most common "Orca keeps reconnecting to a dead remote":** the remote host is gone (VM deleted, laptop off the corp network, host decommissioned) and Orca is restarted, or the target simply never came up this session. Then:
- App startup auto-reconnect (`App.tsx:1024-1029`) → `connectTarget(..., 'auto')` → not exhausted → full `doConnect`.
- `connectionManager.connect()` → `SshConnection.connect()` → `INITIAL_RETRY_ATTEMPTS = 5` attempts, each up to `CONNECT_TIMEOUT_MS = 30 s`, plus 2 s sleeps ≈ **160 s of SSH work per call**.
- Every SSH pane remount (`pty-connection.ts:852-856`), every automation dispatch (`useAutomationDispatchEvents.ts:171`), every renderer reload repeats it, forever. `connectInFlight` dedupes only *concurrent* calls.
**Evidence:** the new IPC test cannot reach the parked branch through product behavior — it has to synthesize the window by hand:
```ts
sshAutoReconnectBudget.deadlineFor('ssh-1', Date.now() - AUTO_RECONNECT_BUDGET_MS)
```
(`ssh.test.ts:598`, `:624`). No production path opens a window on a failed connect.
**Suggested fix:** charge the window on failed connects too — e.g. call `sshAutoReconnectBudget.deadlineFor(targetId, Date.now())` at the top of the `initiator === 'auto'` branch in `connectTarget` (opening the window on the first automatic connect), and/or in `SshConnection.connect()`'s terminal failure path.
---
## M1 — Unattended automations become permanently unavailable with no re-arm path (Medium)
**File:** `src/renderer/src/hooks/useAutomationDispatchEvents.ts:168-186`
The dispatch connect is now marked `'auto'`, so after a give-up it returns the paused state and the handler throws `'SSH target is unavailable.'` → `markDispatchResult({ status: 'skipped_unavailable' })`. **Nothing in the automation path can ever re-arm the budget** (only a desktop `'user'` connect or a successful connection can, and per H1 no connection will be attempted). A 61-second blip at 03:00 kills every remaining scheduled run against that host until a human opens the app. Combined with H1/H3 this is the most user-visible consequence of the branch.
At minimum this is a behavior change that should be called out in the PR body; ideally scheduled automations get their own re-arm (a scheduled run is arguably closer to "the user asking" than a pane remount is).
---
## M2 — The paused state is broadcast-only, and its message never reaches the user (Medium)
**Files:** `src/main/ipc/ssh.ts:993-1008`, `src/main/ipc/ssh.ts:392-395`, `src/renderer/src/components/terminal-pane/TerminalSshReconnectOverlay.tsx:38-53`
(a) **Not authoritative.** The early return `broadcastSshState(...)`s a `reconnection-failed` state but never writes it into `connectionManager`. A subsequent `ssh:getState` / `getPublicSshState` returns whatever the connection object holds — which is `'disconnected'`, or `undefined` if the manager deleted the connection after a failed connect (`ssh-connection-manager.ts:50-53`). A renderer that re-reads state (new window, paired client sync, `useAutomationDispatchEvents.ts:167`) sees a different status than the one that was pushed.
(b) **Message is dropped by the pane UI.** `TerminalSshReconnectOverlay` derives its copy purely from `status` and ignores `state.error` entirely (`:47-52`): a paused target renders *"The SSH connection to X failed. Connect again to continue this terminal session."* — identical to any other error, with no hint that Orca has **stopped trying**. `AUTO_RECONNECT_PAUSED_MESSAGE` is the branch's whole UX premise and it never reaches the primary surface.
---
## M3 — The exhausted check runs before the live-connection fast path, so a stale window can park a healthy session (Medium)
**File:** `src/main/ipc/ssh.ts:991-1008` (runs before `awaitTargetLifecycle` and before `doConnect`'s live-session fast path at `:1078-1090`)
The parked branch fires purely on `isExhausted`, with no check that the target is actually down. It also actively `clearRelayLostBackoff` + `clearRelayStateOverride` and publishes `reconnection-failed`, so a false positive doesn't just fail a connect — it tears the pane's reattach out from under a live relay and wipes the relay's own recovery state.
The budget is only cleared at two sites (`ssh-connection.ts:619`, `:1321`), both of which require a successful `attemptConnect`. Paths that leave a **stale open window**:
- `runReconnectAttempt` succeeds but is superseded — `:1315-1317` returns **before** `markConnected`/`reset`.
- `SshConnection.disconnect()` (`:1425-1450`) resets the ladder but **not** the budget; likewise `ssh:resetRelay` and `ssh:terminateSessions`. A user-driven reset while a window is open leaves it armed; 60 s later an automatic pane remount is parked.
Cheap defenses: return early only when there is no live `connected` session for the target, and call `sshAutoReconnectBudget.reset()` from the disconnect/reset-relay paths.
---
## M4 — Contract gap: paired clients (mobile, `orca serve`, runtime environments) cannot mark a connect as `'auto'` (Medium)
**Files:** `src/main/runtime/rpc/methods/ssh.ts:28-38`, `src/main/ipc/ssh.ts:99-104`, `src/renderer/src/runtime/runtime-environment-ssh-state.ts:178-196`
`connectRegisteredSshTarget(targetId)` takes no initiator and therefore uses the `'user'` default — it **resets the budget**. Every `ssh.connect` RPC from a paired mobile client, a `orca serve` web client, or a nested runtime environment re-arms automatic retries. Today the only mobile caller is user-driven (`mobile/src/components/NewWorktreeModal.tsx:536`), so this is latent rather than live — but the preload contract grew an `initiator` param while the RPC schema (`params: SshTarget`) did not, so the give-up is silently defeatable on paired setups and will drift the first time a client adds an automatic reconnect.
---
## L1 — The ladder's give-up arm is now dead code (Low)
`ssh-connection.ts:1288-1292` (`'Max reconnection attempts reached'`) is unreachable with a 60 s budget: nine consecutive failed handshakes cannot fit (the delay table alone sums to 103 s). `SshReconnectLadder`'s documented contract — "`reconnection-failed` stays reachable exactly after `RECONNECT_BACKOFF_MS.length` failed handshakes" (`ssh-reconnect-ladder.ts:26-31`) — is no longer exercised end to end. Either delete the arm or state that it is a lower bound retained for a configurable budget.
## L2 — Unthrottled broadcast fan-out on the "costs nothing" path (Low)
`ssh.ts:1006` broadcasts on **every** parked auto-connect: `webContents.send('ssh:state-changed')` plus `currentRuntime?.notifySshStateChanged` to every paired client, each going through `withSshRemotePlatform` (authority + platform lookups). A window restore or layout change that remounts N SSH panes emits N identical `reconnection-failed` pushes. Negligible next to the SSH work it replaces, but the branch's stated goal for this path is "zero cost" (`ssh.test.ts:606`), and this is the one thing it still does per call. A cheap dedupe (skip the broadcast if the last published state for the target is already this paused state) would make that claim literal.
## L3 — Budget entries are never removed on target deletion (Low)
`ssh:removeTarget` does not call `sshAutoReconnectBudget.reset(targetId)`, so `windowStartedAtMs` retains an entry per target id ever seen (trivial memory, but unbounded across a long session with many ephemeral/runtime-owned targets). More concretely: a target removed while paused and later re-adopted under the same id (`ssh:importConfig({ reAdopt: true })`) comes back **already paused**.
## L4 — The new pane-side failure gate changes behavior beyond the branch's scope, asymmetrically (Low)
`pty-connection.ts:815-819` + `:857-863`. `doConnect` returns `getPublicSshState(targetId)` (`ssh.ts:1214`), which **prefers `relayStateOverrides`**. `setOnTerminalRelayError` publishes status `'error'` (`ssh.ts:717-727`) and can land between the `connected` broadcast and that return. In that race the pane now aborts the reattach (`reportError`) instead of proceeding as it always has. Conversely a relay-lost `'reconnecting'` override is *not* in `SSH_CONNECT_FAILED_STATUSES`, so the pane still reattaches into a relay that is mid-redeploy. If the intent is only to catch the new paused state, gating on `status === 'reconnection-failed' && error === AUTO_RECONNECT_PAUSED_MESSAGE` (or a dedicated field) would avoid changing the relay-error path at all.
---
## What I checked and found clean
- **No new timers, listeners or unbounded buffers.** The budget is one `Map<string, number>` with one entry per target; `reset()` deletes on success. No new `setTimeout`/`setInterval`, no new IPC channels, no listener registration.
- **No busy reconnect loop from the delay clamp.** `Math.min(decision.delayMs, Math.max(0, deadlineMs - nowMs))` (`ssh-connection.ts:1295`) cannot produce a 0 ms delay: `isExhausted` uses `>=`, so a non-exhausted window guarantees `deadline - now ≥ 1` ms, and the clamp only ever *lowers* the delay. Worst case is one final attempt fired up to 30 s early — one extra attempt, not a loop.
- **No double-reconnect storm.** The parked path returns before `connectInFlight`/`awaitTargetLifecycle`, so it cannot interleave with an in-flight connect; the `'user'` reset happens before any await, so it cannot be lost to a race.
- **Dispose path unchanged.** `scheduleReconnect`'s `this.disposed || this.reconnectTimer` guard, the timer-clear in `disconnect()`, and generation fencing are untouched; the budget check is a pure read added ahead of them.
- **Relay-lost loop still terminates.** `'reconnection-failed'` is already in `TRANSPORT_TERMINAL_STATUSES` (`ssh.ts:322-327`), so a paused transport clears the relay redeploy loop rather than leaving it re-arming at `RELAY_LOST_MAX_DELAY_MS`.
- **Preload/IPC contract is backward compatible.** `initiator` is optional at every layer and defaults to `'user'` (`ssh.ts:1060`, `preload/index.ts:4398-4404`, `api-types.ts:3409-3413`), so unmarked callers keep the pre-existing behavior. All 11 renderer `ssh.connect` call sites were checked: the 9 left unmarked are genuinely user-initiated (Connect buttons, add-repo, composer send, workspace create, settings pane, status row, disconnect dialog) and correctly re-arm the budget; the recovery affordance exists for `reconnection-failed` (`TerminalSshReconnectOverlay.tsx:35`, `SshDisconnectedDialog.tsx:76`).
- **Test-isolation hazard handled.** The process-wide singleton is cleared in both `resetSshHandlerStateForTests` (`ssh.ts:1537`) and `ssh-connection.test.ts:352-354`; without those, one test's exhausted window would suppress reconnects in every later test on the same target id. Good catch by the author.
- **Formatting/tests:** `oxfmt --check` clean on all 9 changed files; 181 + 538 tests pass. Two unrelated formatting-only hunks in `ssh-connection.test.ts:1853-1915` are noise but harmless.
---
## Recommended minimum before merge
1. **H4** — open the budget window on failed automatic connects, or the branch does not fix its headline case (dead host + app restart / pane remounts).
2. **H3** — `reset()` on `powerMonitor` `'resume'`; do not count suspended wall-clock as spent budget.
3. **H1/H2** — either raise the budget above `CONNECT_TIMEOUT_MS × 3` (so the "unreachable host" case gets more than two tries) or add a slow re-arm so the pause is recoverable without a human. As written, a 61-second outage is a permanent, human-only-recoverable failure.
4. **M2(b)** — surface `AUTO_RECONNECT_PAUSED_MESSAGE` in `TerminalSshReconnectOverlay`; otherwise the user is never told that Orca stopped trying.
@@ -1,38 +0,0 @@
# Codex review: SSH auto-reconnect budget
Reviewed `0ae91744086a3195e9a92e585dae6d814bedf808..HEAD` plus all uncommitted and untracked changes, with emphasis on reconnect admission, timers, connection replacement, renderer/RPC contracts, SSH flaps, and automation dispatch.
## Findings
### High — Paired/web clients discard `initiator: 'auto'`, so their automatic reconnects re-arm the budget
- Files/lines: `src/renderer/src/web/web-preload-api.ts:3238-3242`, `src/main/runtime/rpc/methods/ssh.ts:11-13,28-32`, `src/main/ipc/ssh.ts:99-103,982-994`; affected new callers include `src/renderer/src/App.tsx:1024-1028`, `src/renderer/src/components/terminal-pane/pty-connection.ts:850-857`, and `src/renderer/src/hooks/useAutomationDispatchEvents.ts:168-174`.
- Impact: automatic startup reconnect, pane remount, and automation dispatch in the web/paired renderer can continue creating SSH work after the target's budget should be exhausted. The branch claim holds only for the Electron preload path, not for another supported renderer using the same `PreloadApi` contract.
- Evidence: the changed callers pass `initiator: 'auto'`, but `createSshApi().connect` forwards only `{ targetId: args.targetId }`. The runtime RPC schema contains only `targetId`, and its handler calls `connectRegisteredSshTarget(targetId)` without an initiator. That reaches `connectTarget` through a wrapper whose omitted second argument takes the new default `'user'`; line 993 then deletes the target's budget on every web automatic connect. The existing web preload test at `src/renderer/src/web/web-preload-api.test.ts:3417` even pins the lossy `{ targetId }` RPC payload, so the new native-preload tests cannot catch this path.
- Concrete scenario: a paired/web session remounts an SSH pane for a dead host. `waitForSshConnection` labels the call automatic, the web adapter drops that label, main resets the budget as a user connect, and the next remount repeats indefinitely.
### High — Initial automatic connection failures never open the budget window
- Files/lines: `src/main/ipc/ssh.ts:982-1009`, `src/main/ssh/ssh-connection.ts:605-655`, `src/main/ssh/ssh-connection-manager.ts:27-55`, `src/renderer/src/components/terminal-pane/pty-connection.ts:840-878`.
- Impact: if Orca has no established connection object (the common cold-start/dead-host case), startup or pane-driven automatic connects can run the five-attempt initial retry loop on every remount forever. This directly preserves the endless reconnect behavior the branch is meant to stop, and each round may perform up to five 30-second connection attempts plus retry sleeps.
- Evidence: `connectTarget(..., 'auto')` only asks `isExhausted`; it never calls `deadlineFor`, so a target with no existing window is admitted. `SshConnection.connect()` performs `INITIAL_RETRY_ATTEMPTS` and, on terminal failure, does not open or charge the global budget. `SshConnectionManager.connect()` then removes the failed connection object. The renderer deletes its per-target promise in `finally`, so the next pane remount creates a fresh automatic call. The only production call to `deadlineFor` is `scheduleReconnect()` at `src/main/ssh/ssh-connection.ts:1287`, a path reached after an already-connected transport drops, not after an initial connect fails.
- Concrete scenario: Orca restores a tab while its host is offline. The App startup call times out/deferred-connects, main eventually exhausts its initial attempts and deletes the connection, and focusing/remounting that tab starts a new five-attempt sequence because `isExhausted` remains false forever.
### High — Every momentary SSH handshake resets the target budget, so a rapid flap storm is still endless
- Files/lines: `src/main/ssh/ssh-connection.ts:615-620,1255-1272,1310-1322`; existing stability semantics are in `src/main/ssh/ssh-reconnect-ladder.ts:4-5,37-43,63-66` and the still-present flap test at `src/main/ssh/ssh-connection.test.ts:638-659`.
- Impact: a host/proxy that repeatedly completes SSH setup and immediately closes can reconnect forever, retaining the timer/SSH/relay churn this change claims to bound. This is not a hypothetical classification gap: the existing code already distinguishes a momentary handshake from a stable 60-second connection when deciding whether an outage is over.
- Evidence: after `attemptConnect()` resolves, `runReconnectAttempt()` immediately calls `sshAutoReconnectBudget.reset()`. If the socket then emits `end`, `close`, or `error`, `setupDisconnectHandler()` opens a brand-new 60-second window. Repeating that sequence always resets before exhaustion. By contrast, `SshReconnectLadder` only treats a connection as stable after `STABLE_CONNECTION_MS`; its test drives 12 post-handshake drops spaced 45 seconds apart and expects reconnecting to continue, which demonstrates the new budget currently has no effect on sustained flapping.
- Concrete scenario: an SSH server accepts authentication but a proxy, policy, or broken relay-side transport closes the channel a few seconds later. Every handshake is counted as a full recovery even though none survives the existing stability threshold, so Orca retries without end.
### Medium — The deadline clamp schedules one more SSH attempt at the exact exhaustion boundary
- Files/lines: `src/main/ssh/ssh-connection.ts:1280-1307`; timeout bound at `src/main/ssh/ssh-connection-utils.ts:35-38`.
- Impact: even on the failed-handshake path that does exhaust, Orca performs SSH work after the advertised 60-second bound. The final attempt can remain active for up to the 30-second connection timeout; after suspend/resume, an already-armed timer can likewise dial a dead host despite the new resume admission guard.
- Evidence: `delayMs` is clamped to `deadlineMs - nowMs`, which makes the last timer fire exactly at the deadline. Its callback checks only `disposed` and unconditionally calls `runReconnectAttempt()`; it does not re-check `isExhausted`. With fast failures, the delays reach 43 seconds and the next 30-second ladder step is clamped to 17 seconds, causing a new handshake to start at 60 seconds. The changed test at `src/main/ssh/ssh-connection.test.ts:559-592` only asserts fewer than all nine ladder attempts, so this extra boundary attempt still satisfies the test.
## Verification
- `git diff --check` passed.
- Targeted Vitest execution could not start because this worktree's installed dependencies do not contain the `vitest` binary (`pnpm exec vitest` returned `Command "vitest" not found`). Findings above are based on traced production call paths and the existing tests/contracts cited inline.
- No production or test files were edited; only this review report was created.
@@ -1,249 +0,0 @@
# Review: stop-keep-reconnecting-to-remote (SSH auto-reconnect budget)
**Seat:** grok
**Scope:** `0ae91744086a3195e9a92e585dae6d814bedf808`..HEAD + all uncommitted/untracked changes
**Claim:** Stop endless SSH auto-reconnect to remote via a target-scoped wall-clock auto-reconnect budget
**Mode:** Review-only (no production edits)
## Executive summary
The change introduces a process-wide, **target-scoped** 60s wall-clock budget that (a) caps `SshConnection.scheduleReconnect` loops and (b) parks `initiator: 'auto'` `ssh:connect` calls once exhausted. That correctly addresses the core desktop failure mode where `SshConnectionManager.connect()` discards a non-connected connection and a pane remount restarts a fresh ladder forever.
However, the stop is **not complete across all auto-connect surfaces**, and the 60s budget **materially shortens** prior multi-minute retry behavior. Highest risks: web/runtime/RPC paths always default to `initiator: 'user'` (re-arming the budget), budget windows never open on cold/failed `connect()` paths (so remount storms can resume after a failed user Connect), and slow hosts get only ~2 timeout-sized attempts before permanent pause.
## What changed (map)
| Area | Change |
|------|--------|
| `ssh-auto-reconnect-budget.ts` (new) | Singleton `Map<targetId, startedAt>`; 60s budget; reset/clear |
| `ssh-connection.ts` | Open/check budget in `scheduleReconnect`; reset on successful handshake |
| `ipc/ssh.ts` | `connectTarget(targetId, initiator)`; auto parks when exhausted; power-monitor skip; test clear |
| Preload types/API | Optional `initiator?: 'user' \| 'auto'` (default `'user'`) |
| Renderer auto call sites | App startup, `pty-connection`, automation dispatch pass `initiator: 'auto'` |
| Tests | Budget unit tests; connection give-up rewritten; IPC park/re-arm tests |
## Branch claim — what holds
1. **Target-scoped budget survives connection replacement**
`SshConnectionManager.connect()` disconnects non-connected instances and builds a new `SshConnection` (fresh ladder). Budget lives on `sshAutoReconnectBudget` keyed by `targetId`, not on the connection object. Covered by test “keeps the give-up after the connection object is replaced”.
2. **Desktop auto IPC park after exhaustion does zero SSH work**
`connectTarget(..., 'auto')` short-circuits before `doConnect` / `connectionManager.connect` when exhausted. IPC test asserts `mockConnectionManager.connect` is not called.
3. **Pane remount no longer re-arms budget (desktop Electron)**
`pty-connection.waitForSshConnection` and App startup pass `initiator: 'auto'`. User UI Connect paths omit initiator → default `'user'` → re-arm. Correct split for those surfaces.
4. **False “connected” on parked auto connect is fixed**
Park returns `{ status: 'reconnection-failed' }` without throwing. `waitForSshConnection` now treats failed statuses as `connected: false` instead of reattaching a PTY to a dead host.
5. **Power resume respects exhaustion**
Resume path skips `manager.reconnect` when budget exhausted (wake is not a user ask).
---
## Findings (proven)
### 1. High — Web / runtime RPC always re-arms budget (initiator dropped)
**Impact:** On web client and any runtime RPC `ssh.connect`, every connect is treated as **user-initiated**, so `sshAutoReconnectBudget.reset(targetId)` runs and the give-up can never stick for those clients. Undermines the branch claim outside pure desktop Electron IPC.
**Evidence:**
```3238:3243:src/renderer/src/web/web-preload-api.ts
connect: async (args) => {
const { state } = await callRuntimeResult<{ state: SshConnectionState | null }>(
'ssh.connect',
{ targetId: args.targetId } // initiator stripped
)
```
```27:37:src/main/runtime/rpc/methods/ssh.ts
defineMethod({
name: 'ssh.connect',
params: SshTarget, // only { targetId }
handler: async (params) => {
try {
return { state: getPublicSshState(await connectRegisteredSshTarget(params.targetId)) }
```
```99:103:src/main/ipc/ssh.ts
export async function connectRegisteredSshTarget(targetId: string): Promise<SshConnectionState> {
...
return registeredConnectSshTarget(targetId) // second arg omitted → initiator defaults to 'user'
}
```
```991:993:src/main/ipc/ssh.ts
if (initiator === 'user') {
sshAutoReconnectBudget.reset(targetId)
```
`connectRuntimeEnvironmentSshTarget` also invokes RPC with only `{ targetId }` (overlay “Connect” may be intentional user; other auto runtime traffic still hits the same default).
**Failure scenario:** Dead remote host, budget exhausts on desktop main process, mobile/web/runtime auto-connect arrives via RPC → reset → new full connect / reconnect cycle resumes.
**Suggested fix direction (not applied):** Plumb `initiator` through RPC schema + web preload + `connectRegisteredSshTarget`; default RPC callers that are machine-driven to `'auto'`.
---
### 2. High — Budget window only opens in `scheduleReconnect`; cold auto-connect and post–user-fail remounts remain unbounded
**Impact:** The wall-clock stop only applies **after a successful connection has dropped** and entered the reconnect ladder. Cold failures and “user clicked Connect, still dead” do not open a budget window, so `initiator: 'auto'` remounts keep running full `connect()` / `INITIAL_RETRY` forever.
**Evidence:**
- Window open site is only `deadlineFor` inside `scheduleReconnect`:
```1280:1288:src/main/ssh/ssh-connection.ts
const nowMs = Date.now()
if (sshAutoReconnectBudget.isExhausted(this.target.id, nowMs)) {
this.setState('reconnection-failed', AUTO_RECONNECT_PAUSED_MESSAGE)
return
}
const deadlineMs = sshAutoReconnectBudget.deadlineFor(this.target.id, nowMs)
```
- `isExhausted` is **false** when no window is open:
```34:37:src/main/ssh/ssh-auto-reconnect-budget.ts
isExhausted(targetId: string, nowMs: number): boolean {
const startedAt = this.windowStartedAtMs.get(targetId)
return startedAt !== undefined && nowMs - startedAt >= this.budgetMs
}
```
- Initial `connect()` does 5 transient retries (`INITIAL_RETRY_ATTEMPTS=5`, `INITIAL_RETRY_DELAY_MS=2000`, `CONNECT_TIMEOUT_MS=30_000`) and on failure sets `error` / throws — **never** calls `deadlineFor` / `scheduleReconnect` (reconnect is only wired after a live transport drops).
- User Connect **clears** the window **before** success:
```991:993:src/main/ipc/ssh.ts
if (initiator === 'user') {
sshAutoReconnectBudget.reset(targetId)
```
**Failure scenario A (cold host):** Host never answers. Pane remount → `initiator: 'auto'` → not exhausted → `doConnect` → up to ~5×30s timeouts. Repeat forever. Budget never starts.
**Failure scenario B (user re-arm then fail):** Host dropped, budget exhausted, parked. User clicks Connect → `reset`. Connect fails → manager deletes connection, state `error`. Next remount auto-connects again with **no open window** → unbounded retries resume.
This actively falsifies a full reading of “stop keep reconnecting”: the stop is **post-drop ladder only**, not “all automatic SSH dial attempts.”
**Suggested fix direction:** Open the budget window on first auto-initiated failure (or on first auto `connectTarget` entry), and/or treat failed user reconnect as reopening a bounded auto window rather than a fully cleared map entry until success.
---
### 3. High — 60s wall-clock budget is a functional regression vs prior ~9-handshake / multi-minute ladder for slow hosts
**Impact:** Under the mandate “ZERO functional regressions first,” permanent pause after ~60s of wall clock is a deliberate but **user-visible behavior change**. Hosts with 30s connect timeouts get ~**two** handshake attempts, then permanent pause until manual Connect. Prior code guaranteed **9** failed handshakes before `reconnection-failed` (sum of delays alone 103s; with timeouts on order of several minutes).
**Evidence:**
```10:10:src/main/ssh/ssh-auto-reconnect-budget.ts
export const AUTO_RECONNECT_BUDGET_MS = 60_000
```
```35:38:src/main/ssh/ssh-connection-utils.ts
export const INITIAL_RETRY_ATTEMPTS = 5
export const INITIAL_RETRY_DELAY_MS = 2000
export const RECONNECT_BACKOFF_MS = [1000, 2000, 5000, 5000, 10000, 10000, 10000, 30000, 30000]
export const CONNECT_TIMEOUT_MS = 30_000
```
Test rewrite removed the pin that protected long retry:
- Old: `expect(connectAttempts).toBe(1 + RECONNECT_BACKOFF_MS.length)` with comment *“giving up early would strand a user on a flaky link”*
- New: stops on budget with `expect(connectAttempts).toBeLessThan(1 + RECONNECT_BACKOFF_MS.length)`
**Failure scenario:** Wi‑Fi / VPN blip of 70–90s. Old ladder kept trying. New budget marks `reconnection-failed` with paused message; auto recovery never resumes even after the network is healthy. User must notice overlay and click Connect.
**Note:** Fast `ECONNREFUSED`-style failures can still pack many attempts into 60s; the regression is sharpest for **timeout-class** unreachability (the common “remote asleep / path dead” case).
---
### 4. Medium — Near-exhaustion auto `ssh:connect` can still spend minutes of SSH work
**Impact:** Park path promises “zero SSH work” only when **already** exhausted. If budget has milliseconds left (or none open — finding #2), auto connect enters `doConnect` → `connectionManager.connect` → full `INITIAL_RETRY` with no mid-flight budget checks.
**Evidence:** Exhaustion checked only at `connectTarget` entry (`ipc/ssh.ts` ~994–1008). `SshConnection.connect()` loop has no `sshAutoReconnectBudget` reads. `runReconnectAttempt` also does not re-check budget before `attemptConnect` (only next `scheduleReconnect` does).
**Failure scenario:** Budget opened at T0. At T0+59s, pane remount fires auto connect → not exhausted → starts 5×30s attempts while the “60s budget” has already conceptually ended mid-flight.
---
### 5. Medium — Successful flap handshakes reset budget forever (claim incomplete for flap storms)
**Impact:** Not a pure regression vs the old ladder (flaps never incremented `consecutiveFailedAttempts` to give-up), but the new budget **does not** stop hosts that briefly complete handshake then drop.
**Evidence:**
```1319:1321:src/main/ssh/ssh-connection.ts
this.reconnectLadder.markConnected(Date.now())
sshAutoReconnectBudget.reset(this.target.id)
```
Same reset on initial successful `connect()` (~618–619).
**Failure scenario:** Host accepts TCP/SSH every few seconds then dies. Each success resets the 60s window → continuous reconnect CPU/network; power-monitor and remount keep participating.
---
### 6. Low — Runtime-owned target IDs can retain exhausted budget across lifecycle
**Impact:** `removeTarget` / disconnect do not call `sshAutoReconnectBudget.reset/clear` for that id. Runtime-owned IDs are stable (`getRuntimeOwnedSshTargetId(runtimeId)`). A later re-upsert of the same runtime id may inherit an exhausted window until a user-classified connect resets it. Manual targets mint new ids on add (`ssh-${Date.now()}-...`), so less exposed.
**Evidence:** `sshAutoReconnectBudget.clear()` only in `resetSshHandlerStateForTests`; production resets only on user connect / successful handshake / full test clear. `removeRegisteredSshTarget` does not touch the budget map.
---
### 7. Low — User-facing overlay may not show the paused message body
**Impact:** Main sets `error: AUTO_RECONNECT_PAUSED_MESSAGE`, but `TerminalSshReconnectOverlay.messageForStatus` uses generic translated copy for `reconnection-failed`, not `state.error`. User may not learn that Connect is required to re-arm auto retry (status bar / other surfaces may still show raw error depending on wiring).
**Evidence:** Overlay switch for `reconnection-failed` → fixed translate string; park message constant is more specific.
---
## Correctness / lifecycle checks (no issue or pre-existing)
| Check | Result |
|-------|--------|
| Dispose + timer cleanup | `disconnect` clears `reconnectTimer`; timer callback bails if disposed |
| Double `scheduleReconnect` | Guard `if (this.reconnectTimer) return` retained |
| Delay clamp past deadline | `delayMs = min(decision.delayMs, max(0, deadlineMs - nowMs))` avoids sleeping past budget; last attempt may still fire at boundary (acceptable) |
| User Connect re-arm | Default initiator + explicit reset; overlay Connect omits initiator → user |
| Automation dispatch | Uses `initiator: 'auto'`; non-connected after park → `skipped_unavailable` |
| Relay override mask on park | Clears relay lost backoff + override before broadcasting paused state |
| Performance (hot path) | O(1) Map ops; no new per-byte stream work; no extra listeners; timers only existing reconnect path |
| Unbounded buffers | Budget map grows with distinct target ids only (small); `clear()` in tests only |
## Performance
- **Win:** After exhaustion, desktop auto remounts and power-resume skip SSH work (primary goal).
- **Risk:** Finding #2/#4 still allow long INITIAL_RETRY storms (CPU + remote sshd load) when the window is not open or not yet exhausted.
- **No new render/stream path cost** beyond one extra status check in `waitForSshConnection`.
## Test gaps
1. No test that cold auto `ssh:connect` (never connected / never dropped) eventually stops.
2. No test that user Connect → fail → auto remount remains bounded.
3. No test that RPC/web connect preserves `initiator: 'auto'`.
4. Removed exact 9-attempt guarantee; no assertion documenting intended max attempts under 30s timeouts (~2).
5. No integration test that power-monitor + exhausted budget leaves connection untouched (unit-level only via code read).
## Verdict on branch claim
| Claim piece | Verdict |
|-------------|---------|
| Stop endless ladder after drop (desktop) | **Mostly true** (60s hard stop; survives connection replace) |
| Stop endless pane-remount restart after give-up (desktop Electron) | **True** when window already open/exhausted |
| Stop all automatic remote reconnect forever | **False** — web/RPC re-arm (#1); cold/post-user-fail paths (#2); flaps (#5) |
| Zero functional regression | **False** — shorter retry window (#3) |
**Top severity: High**
**Findings count: 7** (3 High, 2 Medium, 2 Low)
## Recommended priority order (for implementer, not this seat)
1. Plumb `initiator` through RPC + web preload + `connectRegisteredSshTarget`.
2. Open budget on auto connect failures (not only post-drop `scheduleReconnect`); define semantics after user-reset failure.
3. Revisit 60s vs CONNECT_TIMEOUT (e.g. budget that counts failed attempts, or ≥ ladder delay sum + N timeouts) if “no functional regression” is hard requirement.
4. Optionally re-check budget inside `connect()` / `runReconnectAttempt` before each attempt.
-1
View File
@@ -1 +0,0 @@
run_bd00c31afa11
@@ -1,37 +0,0 @@
Review the shit out of the current branch and find performance / functionality regressions.
Scope:
- Worktree: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote
- Base: 0ae91744086a3195e9a92e585dae6d814bedf808 .. HEAD plus ALL uncommitted changes (including untracked files)
- Branch claim: stop endless SSH auto-reconnect to remote (auto-reconnect budget / stop keep reconnecting)
- Focus: ZERO functional or performance regressions first; then correctness/lifecycle risks
What to review (all of these; some are untracked):
- src/main/ssh/ssh-auto-reconnect-budget.ts (NEW untracked)
- src/main/ssh/ssh-auto-reconnect-budget.test.ts (NEW untracked)
- src/main/ssh/ssh-connection.ts
- src/main/ssh/ssh-connection.test.ts
- src/main/ipc/ssh.ts
- src/main/ipc/ssh.test.ts
- src/preload/api-types.ts
- src/preload/index.ts
- src/renderer/src/App.tsx
- src/renderer/src/components/terminal-pane/pty-connection.ts
- src/renderer/src/components/terminal-pane/pty-connection.test.ts
- src/renderer/src/hooks/useAutomationDispatchEvents.ts
Commands to inspect scope:
git status --short
git diff HEAD
# also read untracked files directly
Rules:
- Review-only. Do not edit production code.
- Stay in this worktree. Do not follow absolute paths into other checkouts.
- Proven findings only: file + line, severity (Critical/High/Medium/Low), impact, evidence.
- Actively try to falsify the branch claim. Prefer concrete failure scenarios over style nits.
- Cover hot paths touched by the diff (render, stream, send, timers, listeners, unbounded buffers, RPC, SSH reconnect loops).
- Check for: broken reconnect after transient blips, stopping too early, never stopping, double reconnect storms, race conditions on dispose, wrong budget accounting across connections, UI/IPC contract breaks, automation dispatch regressions, performance (extra timers, listeners not cleaned, busy reconnect loops).
- Write your full report to: REPORT_PATH_PLACEHOLDER
- Also put a 3-sentence executive summary in worker_done body.
- In worker_done payload include: taskId, dispatchId, reportPath, filesModified:[], findingsCount, topSeverity
@@ -1,27 +0,0 @@
{
"id": "5324bf66-b54a-4c6a-bb1a-d1557b05e4c0",
"ok": true,
"result": {
"task": {
"id": "task_1c70db5688a7",
"parent_id": null,
"spec": "Review the shit out of the current branch and find performance / functionality regressions.\n\nScope:\n- Worktree: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote\n- Base: 0ae91744086a3195e9a92e585dae6d814bedf808 .. HEAD plus ALL uncommitted changes (including untracked files)\n- Branch claim: stop endless SSH auto-reconnect to remote (auto-reconnect budget / stop keep reconnecting)\n- Focus: ZERO functional or performance regressions first; then correctness/lifecycle risks\n\nWhat to review (all of these; some are untracked):\n- src/main/ssh/ssh-auto-reconnect-budget.ts (NEW untracked)\n- src/main/ssh/ssh-auto-reconnect-budget.test.ts (NEW untracked)\n- src/main/ssh/ssh-connection.ts\n- src/main/ssh/ssh-connection.test.ts\n- src/main/ipc/ssh.ts\n- src/main/ipc/ssh.test.ts\n- src/preload/api-types.ts\n- src/preload/index.ts\n- src/renderer/src/App.tsx\n- src/renderer/src/components/terminal-pane/pty-connection.ts\n- src/renderer/src/components/terminal-pane/pty-connection.test.ts\n- src/renderer/src/hooks/useAutomationDispatchEvents.ts\n\nCommands to inspect scope:\n git status --short\n git diff HEAD\n # also read untracked files directly\n\nRules:\n- Review-only. Do not edit production code.\n- Stay in this worktree. Do not follow absolute paths into other checkouts.\n- Proven findings only: file + line, severity (Critical/High/Medium/Low), impact, evidence.\n- Actively try to falsify the branch claim. Prefer concrete failure scenarios over style nits.\n- Cover hot paths touched by the diff (render, stream, send, timers, listeners, unbounded buffers, RPC, SSH reconnect loops).\n- Check for: broken reconnect after transient blips, stopping too early, never stopping, double reconnect storms, race conditions on dispose, wrong budget accounting across connections, UI/IPC contract breaks, automation dispatch regressions, performance (extra timers, listeners not cleaned, busy reconnect loops).\n- Write your full report to: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews/claude-fable.md\n- Also put a 3-sentence executive summary in worker_done body.\n- In worker_done payload include: taskId, dispatchId, reportPath, filesModified:[], findingsCount, topSeverity\n\nSEAT: claude-fable\nWrite your full report to: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews/claude-fable.md",
"status": "ready",
"deps": "[]",
"result": null,
"created_at": "2026-08-02 19:16:12",
"completed_at": null,
"created_by_terminal_handle": "term_0d47b7fc-993b-4b4f-93f3-593adf918663",
"task_title": "Review the shit out of the current branch and find performance / functionalit...",
"display_name": "Review the shit out of the current branch and find performance / functionalit...",
"run_id": "run_bd00c31afa11"
},
"mutation": {
"requestId": "380dec02-80a3-4034-8585-9203f3ec2acc",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,27 +0,0 @@
{
"id": "962d99e6-ff2b-41f6-96d4-96029a06d586",
"ok": true,
"result": {
"task": {
"id": "task_40172447da9b",
"parent_id": null,
"spec": "Review the shit out of the current branch and find performance / functionality regressions.\n\nScope:\n- Worktree: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote\n- Base: 0ae91744086a3195e9a92e585dae6d814bedf808 .. HEAD plus ALL uncommitted changes (including untracked files)\n- Branch claim: stop endless SSH auto-reconnect to remote (auto-reconnect budget / stop keep reconnecting)\n- Focus: ZERO functional or performance regressions first; then correctness/lifecycle risks\n\nWhat to review (all of these; some are untracked):\n- src/main/ssh/ssh-auto-reconnect-budget.ts (NEW untracked)\n- src/main/ssh/ssh-auto-reconnect-budget.test.ts (NEW untracked)\n- src/main/ssh/ssh-connection.ts\n- src/main/ssh/ssh-connection.test.ts\n- src/main/ipc/ssh.ts\n- src/main/ipc/ssh.test.ts\n- src/preload/api-types.ts\n- src/preload/index.ts\n- src/renderer/src/App.tsx\n- src/renderer/src/components/terminal-pane/pty-connection.ts\n- src/renderer/src/components/terminal-pane/pty-connection.test.ts\n- src/renderer/src/hooks/useAutomationDispatchEvents.ts\n\nCommands to inspect scope:\n git status --short\n git diff HEAD\n # also read untracked files directly\n\nRules:\n- Review-only. Do not edit production code.\n- Stay in this worktree. Do not follow absolute paths into other checkouts.\n- Proven findings only: file + line, severity (Critical/High/Medium/Low), impact, evidence.\n- Actively try to falsify the branch claim. Prefer concrete failure scenarios over style nits.\n- Cover hot paths touched by the diff (render, stream, send, timers, listeners, unbounded buffers, RPC, SSH reconnect loops).\n- Check for: broken reconnect after transient blips, stopping too early, never stopping, double reconnect storms, race conditions on dispose, wrong budget accounting across connections, UI/IPC contract breaks, automation dispatch regressions, performance (extra timers, listeners not cleaned, busy reconnect loops).\n- Write your full report to: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews/claude-opus.md\n- Also put a 3-sentence executive summary in worker_done body.\n- In worker_done payload include: taskId, dispatchId, reportPath, filesModified:[], findingsCount, topSeverity\n\nSEAT: claude-opus\nWrite your full report to: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews/claude-opus.md",
"status": "ready",
"deps": "[]",
"result": null,
"created_at": "2026-08-02 19:16:07",
"completed_at": null,
"created_by_terminal_handle": "term_0d47b7fc-993b-4b4f-93f3-593adf918663",
"task_title": "Review the shit out of the current branch and find performance / functionalit...",
"display_name": "Review the shit out of the current branch and find performance / functionalit...",
"run_id": "run_bd00c31afa11"
},
"mutation": {
"requestId": "e40a564d-15ab-40e5-8fef-2cc37b0c67b3",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,27 +0,0 @@
{
"id": "cb08f2ef-0a81-4eff-80a8-5fa2d39e82d0",
"ok": true,
"result": {
"task": {
"id": "task_84cc9b5d5f7d",
"parent_id": null,
"spec": "Review the shit out of the current branch and find performance / functionality regressions.\n\nScope:\n- Worktree: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote\n- Base: 0ae91744086a3195e9a92e585dae6d814bedf808 .. HEAD plus ALL uncommitted changes (including untracked files)\n- Branch claim: stop endless SSH auto-reconnect to remote (auto-reconnect budget / stop keep reconnecting)\n- Focus: ZERO functional or performance regressions first; then correctness/lifecycle risks\n\nWhat to review (all of these; some are untracked):\n- src/main/ssh/ssh-auto-reconnect-budget.ts (NEW untracked)\n- src/main/ssh/ssh-auto-reconnect-budget.test.ts (NEW untracked)\n- src/main/ssh/ssh-connection.ts\n- src/main/ssh/ssh-connection.test.ts\n- src/main/ipc/ssh.ts\n- src/main/ipc/ssh.test.ts\n- src/preload/api-types.ts\n- src/preload/index.ts\n- src/renderer/src/App.tsx\n- src/renderer/src/components/terminal-pane/pty-connection.ts\n- src/renderer/src/components/terminal-pane/pty-connection.test.ts\n- src/renderer/src/hooks/useAutomationDispatchEvents.ts\n\nCommands to inspect scope:\n git status --short\n git diff HEAD\n # also read untracked files directly\n\nRules:\n- Review-only. Do not edit production code.\n- Stay in this worktree. Do not follow absolute paths into other checkouts.\n- Proven findings only: file + line, severity (Critical/High/Medium/Low), impact, evidence.\n- Actively try to falsify the branch claim. Prefer concrete failure scenarios over style nits.\n- Cover hot paths touched by the diff (render, stream, send, timers, listeners, unbounded buffers, RPC, SSH reconnect loops).\n- Check for: broken reconnect after transient blips, stopping too early, never stopping, double reconnect storms, race conditions on dispose, wrong budget accounting across connections, UI/IPC contract breaks, automation dispatch regressions, performance (extra timers, listeners not cleaned, busy reconnect loops).\n- Write your full report to: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews/codex.md\n- Also put a 3-sentence executive summary in worker_done body.\n- In worker_done payload include: taskId, dispatchId, reportPath, filesModified:[], findingsCount, topSeverity\n\nSEAT: codex\nWrite your full report to: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews/codex.md",
"status": "ready",
"deps": "[]",
"result": null,
"created_at": "2026-08-02 19:16:02",
"completed_at": null,
"created_by_terminal_handle": "term_0d47b7fc-993b-4b4f-93f3-593adf918663",
"task_title": "Review the shit out of the current branch and find performance / functionalit...",
"display_name": "Review the shit out of the current branch and find performance / functionalit...",
"run_id": "run_bd00c31afa11"
},
"mutation": {
"requestId": "5521ea3a-ead3-475c-be0a-edb6fac3573a",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,27 +0,0 @@
{
"id": "50ed3aab-6388-41b9-b84c-4632e4565171",
"ok": true,
"result": {
"task": {
"id": "task_2cda5d06c526",
"parent_id": null,
"spec": "Review the shit out of the current branch and find performance / functionality regressions.\n\nScope:\n- Worktree: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote\n- Base: 0ae91744086a3195e9a92e585dae6d814bedf808 .. HEAD plus ALL uncommitted changes (including untracked files)\n- Branch claim: stop endless SSH auto-reconnect to remote (auto-reconnect budget / stop keep reconnecting)\n- Focus: ZERO functional or performance regressions first; then correctness/lifecycle risks\n\nWhat to review (all of these; some are untracked):\n- src/main/ssh/ssh-auto-reconnect-budget.ts (NEW untracked)\n- src/main/ssh/ssh-auto-reconnect-budget.test.ts (NEW untracked)\n- src/main/ssh/ssh-connection.ts\n- src/main/ssh/ssh-connection.test.ts\n- src/main/ipc/ssh.ts\n- src/main/ipc/ssh.test.ts\n- src/preload/api-types.ts\n- src/preload/index.ts\n- src/renderer/src/App.tsx\n- src/renderer/src/components/terminal-pane/pty-connection.ts\n- src/renderer/src/components/terminal-pane/pty-connection.test.ts\n- src/renderer/src/hooks/useAutomationDispatchEvents.ts\n\nCommands to inspect scope:\n git status --short\n git diff HEAD\n # also read untracked files directly\n\nRules:\n- Review-only. Do not edit production code.\n- Stay in this worktree. Do not follow absolute paths into other checkouts.\n- Proven findings only: file + line, severity (Critical/High/Medium/Low), impact, evidence.\n- Actively try to falsify the branch claim. Prefer concrete failure scenarios over style nits.\n- Cover hot paths touched by the diff (render, stream, send, timers, listeners, unbounded buffers, RPC, SSH reconnect loops).\n- Check for: broken reconnect after transient blips, stopping too early, never stopping, double reconnect storms, race conditions on dispose, wrong budget accounting across connections, UI/IPC contract breaks, automation dispatch regressions, performance (extra timers, listeners not cleaned, busy reconnect loops).\n- Write your full report to: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews/grok.md\n- Also put a 3-sentence executive summary in worker_done body.\n- In worker_done payload include: taskId, dispatchId, reportPath, filesModified:[], findingsCount, topSeverity\n\nSEAT: grok\nWrite your full report to: /Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote/.llm-counsel/20260802-121457/reviews/grok.md",
"status": "ready",
"deps": "[]",
"result": null,
"created_at": "2026-08-02 19:15:51",
"completed_at": null,
"created_by_terminal_handle": "term_0d47b7fc-993b-4b4f-93f3-593adf918663",
"task_title": "Review the shit out of the current branch and find performance / functionalit...",
"display_name": "Review the shit out of the current branch and find performance / functionalit...",
"run_id": "run_bd00c31afa11"
},
"mutation": {
"requestId": "db6b1bfa-9bff-4185-b4a7-ef47487f955b",
"replayed": false
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,18 +0,0 @@
{
"id": "2ef4b435-3ff4-4659-acfa-aa1b02d25d12",
"ok": true,
"result": {
"terminal": {
"handle": "term_8016c85a-9e49-4bf1-aa84-b162016357df",
"tabId": "02ff22b9-11e8-46c0-8481-ca669d3e74f8",
"worktreeId": "48e366bb-2436-46a9-bb37-10ace25ada5f::/Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote",
"title": "counsel-claude-fable",
"executionHostId": "local",
"hostPlatform": "darwin",
"surface": "visible"
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,18 +0,0 @@
{
"id": "5d877049-4c39-4fe1-b654-4d3e8724b2d9",
"ok": true,
"result": {
"terminal": {
"handle": "term_2eaba6da-e77c-49d4-9e47-393e19fa1381",
"tabId": "66edb724-33f6-40be-bb2a-7f4ee1b37325",
"worktreeId": "48e366bb-2436-46a9-bb37-10ace25ada5f::/Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote",
"title": "counsel-claude-opus",
"executionHostId": "local",
"hostPlatform": "darwin",
"surface": "visible"
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,18 +0,0 @@
{
"id": "85e90266-68a4-4de0-a2c4-bf1c66e70c59",
"ok": true,
"result": {
"terminal": {
"handle": "term_40bfaceb-179e-440f-8876-f1fbd0b51e9c",
"tabId": "d138bfa2-0ca8-4719-a0c6-74ce96436c1d",
"worktreeId": "48e366bb-2436-46a9-bb37-10ace25ada5f::/Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote",
"title": "counsel-codex",
"executionHostId": "local",
"hostPlatform": "darwin",
"surface": "visible"
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}
@@ -1,20 +0,0 @@
{
"id": "dde67ce8-07f8-4ceb-8194-961f04ad01e7",
"ok": true,
"result": {
"terminal": {
"handle": "term_de79d121-b74d-45e5-be1c-448261d04d6f",
"tabId": "33259e5a-eaee-48fc-a94f-e5b68a3bbada",
"paneKey": "33259e5a-eaee-48fc-a94f-e5b68a3bbada:5931d416-1958-4ea3-89f2-a84541215773",
"ptyId": "48e366bb-2436-46a9-bb37-10ace25ada5f::/Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote@@5e24c15a",
"worktreeId": "48e366bb-2436-46a9-bb37-10ace25ada5f::/Users/jinjingliang/Documents/projects/orca/stop-keep-reconnecting-to-remote",
"title": "counsel-grok",
"executionHostId": "local",
"hostPlatform": "darwin",
"surface": "visible"
}
},
"_meta": {
"runtimeId": "0ff81073-c9cc-4119-8520-0d78345794e0"
}
}