Repository navigation
fix: pass over a crashed T3 home whose runtime file names the running server's port - #97
Conversation
… server's port (#96) The T3 button's lookup took the first T3 home whose recorded origin accepted a TCP connection. Every T3 home defaults to port 3773 and a crash leaves server-runtime.json behind, so a crashed ~/.t3 or configured t3_home shadowed the running ~/.t3-server and linked its stale threads and environment. Server::probe now asks the server at the origin for T3's environment descriptor (GET /.well-known/t3/environment, which t3 pair reads too) and counts the home as running only when its environmentId is the home's environment-id. A closed port is still "not running"; anything else that answers becomes a reason. find_in_homes_with and find_with take the probe so tests can stand in for it, and each home is probed once per lookup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@sfl review |
There was a problem hiding this comment.
✅ Verdict: PASS • Round 1
Confidence: 8/10
Review done by claude-opus-5-5 (same family: same-family: codex gated: codex Weekly 167% SUS 60 🔴 pause, defer)
The PR replaces the bare TCP is_listening check with Server::probe. The probe sends GET /.well-known/t3/environment and treats a home as running only when the reported environmentId matches that home's environment-id, so a crashed home sharing port 3773 no longer hides the live server. The PR also adds injectable find_in_homes_with and find_with and probes each home once per lookup. I found no Critical, High or Medium defect: the logic, fallback reason, size and redirect limits, and README claims match exact-SHA code and upstream T3 source. The only finding is a Low test gap.
🔨 Full-Spectrum Review Summary
Effort: high
Review time: 2m 31s
Token count: Unavailable
| Pass | 🔴 Critical | 🟠 High | 🟡 Medium | ⚪ Low |
|---|---|---|---|---|
| 🔒 Security | 0 | 0 | 0 | 0 |
| ✅ Accuracy | 0 | 0 | 0 | 0 |
| 🧰 Quality & Maintainability | 0 | 0 | 0 | 1 |
| Total | 0 | 0 | 0 | 1 |
Key Findings
- ⚪ LOW — Quality & Maintainability: Timeout and non-connect error path of `probe` is untested.
crates/dashboard-core/src/t3.rs:173: Trigger: a server accepts the connection but sends no response withinIDENTITY_TIMEOUT(2s), or the request fails after connecting. The newErr(_)branch then returnsLiveness::Other("... didn't say which T3 Code server it is"), so the button is disabled with anUnreadablereason rather thanNotRunning. That differs from the old TCP check, which reported a slow server as running. The new tests cover connection refused, non-2xx statuses, bad bodies and oversized bodies, but no test covers a stalled server. AGENTS.md asks for failure-path coverage, and a regression in howis_connectversus timeout errors are classified would go unnoticed. Impact is limited to test coverage. The behavior looks intended. (non-blocking)
Coverage
- Files reviewed:
README.mdcrates/dashboard-core/src/t3.rscrates/dashboard-core/tests/t3.rs
- Context read:
AGENTS.mdCargo.tomlcrates/dashboard-core/Cargo.tomlrustfmt.tomlcrates/dashboard-app/src/main.rscrates/dashboard-app/src/board_view.rscrates/dashboard-app/src/demo.rscrates/dashboard-core/src/pmd.rscrates/dashboard-core/tests/no_child_process.rscrates/dashboard-core/tests/support/http.rspingdotgg/t3code packages/contracts/src/environmentHttp.tspingdotgg/t3code apps/server/src/environment/ServerEnvironment.tspingdotgg/t3code apps/server/src/config.tspingdotgg/t3code apps/server/src/cli/pair.ts
- Verified:
- Compare
base_commit.shaandmerge_base_commit.shaequala6ce1c8e0bdbfcd3c04bf7da0428d6dddac791d3; the only commit,commits[-1].sha, is4baa07093b57cc5cca2c28eaff177ad57edf0375; PR 97 head and base match - The workspace
reqwest0.12 dependency enablesblockingandrustls-tls, anddashboard-corealready depends on it ThreadFinder::findruns on GPUIbackground_executor, not inside a tokio runtime, so a blocking reqwest client is safe there;pmd.rsalready uses the same blocking client withredirect::Policy::none()andno_proxy()- Upstream T3 defines
/.well-known/t3/environmentin theEnvironmentMetadataHttpApigroup with no auth headers or middleware, and its descriptorenvironmentIdis read fromenvironment-idin the same state dir asserver-runtime.json - Body read is capped by
take(DESCRIPTOR_LIMIT + 1)with a length check; redirects are off, no proxy is used, and no credentials are sent - When no home is running,
find_in_homes_withreuses the storedLivenessfor theresolve_userdatapick; homes wherelocatefails are never probed; covered byeach_home_is_asked_once_and_its_answer_decides find_withmapsRunning,StoppedandOtherto lookups,NotRunningandUnreadablerespectively, matching the README's new tooltip reasons- Tests cover the shared-port stale home for a default and a configured
t3_home, a stale home alone, non-T3 answers (404, 500, HTML, empty, missing, numeric and oversized ID) and malformed runtime files - No new child process or
Commanduse, sono_child_process.rsstill holds
- Compare
- Not verified:
- Runtime behavior against a real T3 server; upstream T3 source was read at its default branch, not the deployed version
- Whether
cargo fmt, clippy and the tests pass: therustandcoreCI checks were still in progress at review time; onlyadvisorieshad succeeded - Whether reqwest 0.12 classifies every connect-phase timeout and DNS failure as
is_connect, which is inferred from library behavior, not checked in source - Lookup latency with a slow server, up to about 2.3s per probed home, was not measured
- Tests run: None. Reviewer execution is disabled.
HemSoft full-spectrum review • 3 evidence-based passes • Reviewed head 4baa07093b57cc5cca2c28eaff177ad57edf0375 • SFL v0
…ver (#96) The identity check adds a tooltip reason, so the T3 button needs current-head UI evidence. ui_tests.rs gains t3_homes: synthetic T3 homes (state database, runtime file, environment ID) and a loopback thread answering T3's environment descriptor, behind a ThreadFinder that runs the production find_in_homes. the_t3_button_links_only_the_home_whose_own_server_answers checks both states in a test window; captures_t3_home_identity renders them, with the tooltip, through CI's existing Screenshots step. rusqlite becomes a dev-dependency of dashboard-app to write those databases; it is already locked. tests/t3.rs covers a server that accepts the connection but never answers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Risk level: 2/10 (project-dashboard)
Worked on by: claude-opus-5-5 on laptop, for Chief dispatch b9abf22d (claim
chief-dispatch-b9abf22d-dashboard-launch-maintenance)Summary
The T3 button's lookup took the first T3 home whose recorded origin accepted a TCP connection. Every T3 home defaults to port 3773, and a crash leaves
server-runtime.jsonbehind. A crashed~/.t3or configuredt3_hometherefore shadowed the running~/.t3-serverand linked its stale thread under its stale environment.Server::probereplacesServer::is_listening. It asks the server at the recorded origin for T3's environment descriptor (GET /.well-known/t3/environment, the unauthenticated endpoint T3's ownt3 pairreads). A home counts as running only when the descriptor'senvironmentIdis that home'senvironment-id. The result is aLiveness:Running,Stopped(connection refused or timed out, reported as "T3 Code isn't running" as before), orOther(reason).Othercovers another environment, a non-2xx answer, a body that isn't a descriptor, or no answer within 2 s, and surfaces as the existingUnreadablereason.find_in_homesandfindkeep their signatures and behavior for real servers.find_in_homes_withandfind_withtake the probe so tests can inject one. The fallback reason reuses the probe of the homeresolve_userdatapicks instead of probing it twice.pidis not used. A live PID can belong to an unrelated process after reuse, and checking it would need new per-OS process APIs. The descriptor's environment ID is what identifies the server. Both the descriptor endpoint and thepidfield are present in T3v0.1.0,v0.1.1and currentmain(83a3674).Commits:
4baa070fix:: the production change above (t3.rs,tests/t3.rs, README).9850a25test:: test and capture evidence only. No production source or README change. It touchesdashboard-app/src/ui_tests.rs(t3_homesfixture plus one UI test),dashboard-app/src/captures.rs(one capture) anddashboard-core/tests/t3.rs(stalled-server test). It also addsrusqlite.workspace = trueas adashboard-appdev-dependency to write the synthetic state databases. That puts one line inCargo.lock("rusqlite"indashboard-app's list); no new crate or version.Acceptance Criteria
tests/t3.rsa_stale_home_on_the_running_servers_port_is_passed_over. Also through the board inui_tests::the_t3_button_links_only_the_home_whose_own_server_answers, and in the screenshots below.t3_home:a_stale_configured_t3_home_on_the_running_servers_port_is_passed_over(built throughcandidate_homes).a_stale_home_is_passed_over_for_the_running_server.T3 Code isn't running. Same test after the fake server stops, andfind_reports_why_for_every_checkout_when_t3_is_missing_or_stopped.origin, a non-JSON one, and an empty or missingenvironment-ideach give a reason (a_runtime_file_without_the_servers_identity_is_a_reason). A descriptor missingenvironmentId, with a numeric one, non-JSON, empty, oversized, or HTTP 404/500 each give a reason (something_that_isnt_a_t3_server_on_the_port_is_a_reason). A server that accepts but never answers gives a reason within the probe's timeout (a_server_that_accepts_but_never_answers_is_a_reason). Another environment on a lone stale home's port gives a reason naming the origin (a_stale_home_alone_with_another_environment_on_its_port_links_nothing).tests/no_child_process.rspasses;reading_never_writes_to_t3s_databasepasses. The probe is an HTTP GET.Definition of Done
Fix in
dashboard-core/src/t3.rs;find_in_homes' doc comment matches the behavior.tests/t3.rscovers these cases, each backed byFakeT3, an in-process loopback HTTP thread that stops when dropped (60 s cap):t3_home;Runtime fixtures carry T3's current fields (
version,pid,port,origin,startedAt,serverVersion).The board's T3 button runs the production lookup over synthetic homes (
ui_tests::t3_homes::Homescallsdashboard_core::t3::find_in_homes), in a Windows-run UI test and in Linux captures.README's T3 button section describes the identity check and the new tooltip reason.
CI on Ubuntu and Windows: all five required checks pass on
9850a25(run 42366244188).Review gate: SFL R1 PASS on production head
4baa070is carried to9850a25under the Chief review rule. The parent independently verified the five changed files are tests/capture fixtures, a dev-only dependency and its lock entry. Production source and dependencies are unchanged; all five required checks pass. No new review was requested.Validation
T3 button screenshots (current head
9850a25)These come from the existing CI Screenshots step:
CAPTURE_DIR="$GITHUB_WORKSPACE/captures" cargo test -p dashboard-app --locked captures -- --ignored. That rancaptures_t3_home_identityonubuntu-latest: CI run 42366244188, jobrust (ubuntu-latest)128384829157, artifactcaptures13571888536 (sha256:ffe38f4cd6cdd6ca1159b74d797f999e8493baabf79bf9a2cda1667d15436cde).The scenario is rendered offscreen by GPUI's wgpu renderer in a 1100×420 window at 2× scale; no desktop window was opened. Two synthetic homes have project rows for
/demo/codexbar:.t3: a crashed home, environmentenv-stale, thread "Stale session from a crashed home"..t3-server: the running home, environmentenv-live, thread "Live session".Both runtime files record one loopback descriptor server that answers as
env-live. The board'sThreadFinderis the productiont3::find_in_homes. Tooltips come from a syntheticwindow.hover(("t3", 0))in the test window; there is no OS input. No real T3 install, profile or credentials are involved.Wrong environment, button disabled. Homes: the crashed
.t3only. The T3 session cell is greyed out and links nothing. Before this fix it would have linked "Stale session from a crashed home".Wrong environment, hover tooltip. The new reason: "Can't read T3 Code's threads: the server at http://127.0.0.1:42081 is another T3 Code environment, not the one whose files were found".
Shared port, button enabled. Homes: the crashed
.t3first, then the running.t3-server, on the same port. The button names "Live session".Shared port, hover tooltip. "Open the T3 Code thread: Live session" and
http://127.0.0.1:42081/env-live/t-live, the running home's environment and thread.No recording. Each state is a static outcome of what is on disk and what answers at the recorded port. The only interaction is hovering one button, shown above as before and after stills. GPUI's headless renderer in this harness writes PNG frames and has no video encoder, so a recording would show nothing the four stills don't.
Commands and results
On Windows (laptop):
4baa0709850a25cargo fmt --all --checkcargo clippy --workspace --all-targets --locked -- -D warningscargo test --workspace --lockedt322t323,no_child_process1, 22 result lines ok, 0 failedcargo deny --locked check advisories(0.20.2)advisories okadvisories okcargo check/cargo test -p dashboard-core --locked, separate target dirsWindows clippy doesn't compile
captures.rs(Linux and macOS only); CI'srust (ubuntu-latest)does, and passed.Focused tests at
9850a25:cargo test -p dashboard-core --locked --test t3gives 23 passed.cargo test -p dashboard-app --locked the_t3_buttongives 2 passed, includingthe_t3_button_links_only_the_home_whose_own_server_answers, which checks both board states on Windows. It asserts the disabled button, the exact reason, that no URL opens on click, and that the enabled click opens…/env-live/t-live.Old-source control: the new
tests/t3.rsat4baa070, minus the two tests that call the newfind_in_homes_with, run againsta6ce1c8exported withgit archive. 16 pass and 4 fail:a_stale_home_on_the_running_servers_port_is_passed_overa_stale_configured_t3_home_on_the_running_servers_port_is_passed_overa_stale_home_alone_with_another_environment_on_its_port_links_nothingsomething_that_isnt_a_t3_server_on_the_port_is_a_reasonCI on
9850a25(run 42366244188):advisories,core (ubuntu-latest),core (windows-latest),rust (ubuntu-latest)andrust (windows-latest)all pass.Review
SFL R1 on
4baa070: PASS (review), with one Low, non-blocking note. The note: the probe's accepted-connection-then-timeout branch had no stalled-server test.9850a25addsa_server_that_accepts_but_never_answers_is_a_reason(test-only); it asserts the exact reason and that the lookup returns within the probe's bound. There are 0 unresolved review threads.Parent verification: the test-only carry decision above applies to
9850a2533f922916a8834b014ae620c6b0e8fd7d, not a newly posted SFL review of that SHA. The parent downloaded and visually inspected all four current-head attachment images, verified HTTP 200 and each PNG hash against the reported artifact, and confirmed the disabled wrong-environment state and correct enabled live-thread state. The one Low note is covered by the new stalled-server test.Risks and deferred work
environment-id(a copied folder) are still indistinguishable. Out of scope.Closes #96
🤖 Generated with Claude Code