Skip to content

fix(cli): t3 thread list and show sign in read-only with a short-lived session - #36055

Open
juliusmarminge wants to merge 2 commits into
mainfrom
factory/issue-36050
Open

juliusmarminge wants to merge 2 commits into
mainfrom
factory/issue-36050

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Nov 13, 2026 •

Copy link
Copy Markdown
Member

Cause: withThreadTools in apps/server/src/cli/thread.ts issued every t3 thread session with access "full-access" and the 30-day MCP_CLIENT_SESSION_TTL, and relied only on acquireRelease to revoke it, which does not run when the CLI is killed. Fix: withThreadTools now takes the access level and whether the command waits. list and show request "read-only" (orchestration:read only). start and send keep "full-access" because they launch and message threads. EnvironmentAuth.issueMcpClientSession gained an optional ttl (default unchanged, so McpOAuth is unaffected), and the CLI passes 10 minutes for every command that does not --wait. A killed list, show, start or send now leaves at most a 10-minute session, which is read-only for reads. Commands with --wait keep the default lifetime because a wait has no upper bound and sessions cannot be renewed. Reproduced first: the repro branch's test failed with both commands requesting full-access. I extended that test with an assertion that list and show get a 10-minute session. No new regexes or parsers.

Fixes #36050

Reproduction on main

On origin/main a2be070, I ran node apps/server/src/bin.ts serve --base-dir <fresh dir> --port 37991 with a fresh database, then t3 project add <dir> and t3 thread list --project <dir> --base-dir <same>. Read-only queries of auth_sessions showed a 't3 thread cli' row for each thread list call (including one that failed with 'No T3 Code project matches'). Each row had scopes [orchestration:read, orchestration:operate, settings:write, source-control:write] and expires_at 30 days after issued_at, and was revoked about 100ms later. I then started t3 thread list in the background, waited for its session row, and sent SIGKILL to the captured PID (exit 137). t3 auth session list --json still listed that session as active, with the same four scopes and expiresAt 2026-12-13 (issued 2026-11-13). A unit test on branch factory/repro-36050 shows that both list and show request access 'full-access' instead of 'read-only'.

Evidence

Run identical commands against fresh real servers: register a synthetic project; list its threads; list a missing project; show a missing thread; SIGKILL another list command immediately after session issuance; inspect active sessions immediately and after 10 real minutes. Linux, Node CLI, isolated databases, loopback servers. Captures render recorded CLI output; video pauses are compressed and elapsed timestamps retained.

Before After
a2be070f30 b27d61095c
Before: four scopes, 30-day lifetime, killed session remains active After: read-only scope, 10-minute lifetime, killed session expires
Before recording After recording
Before transcript After transcript
Before session fields After session fields

Base's killed session expires December 13 and remained active after the comparison interval. Head's killed session expired November 13 at 07:24:14 UTC and was absent from the active list at 07:24:20 UTC. Neither session was revoked.

Reproduction driver · Capture renderer

Not shown:

  • Successful show of a populated thread was not exercised; show was verified through a real missing-thread request that issued a session.
  • Mutating commands and --wait session lifetimes were outside this reproduction and were not exercised.
  • Base's full 30-day expiry was read from its persisted timestamp; only head's 10-minute expiry was observed in real time.

Tests

vp test run src/cli/thread.test.ts (repro branch, before the fix): 1 failed, 13 passed, failing on full-access vs read-only. After the fix: vp test run src/cli/thread.test.ts src/auth/EnvironmentAuth.test.ts: 39 passed. vp run check:changed: format, lint, knip, graphql, typecheck (3 packages) and tests (3 files, 41 passed) all pass.

Not in this change

Who did the work

  • Written by Claude Opus 5.5, in Claude Code
  • Reviewed by GPT-6.1 Sol, in Codex, and by Claude Opus 5.5, each on its own
  • Proof captured by GPT-6.1 Sol and checked by Claude Opus 5.5

Run by the t3code factory in T3 Code.

juliusmarminge and others added 2 commits November 13, 2026 07:00
Fails on main: both issue a full-access MCP client session (#36050).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d session

`t3 thread` issued a full-access MCP session with the 30-day MCP TTL for every
subcommand, reads included, and revoked it only on a clean exit. A killed CLI
left a write-capable credential active for a month.

list and show now ask for read-only access. Sessions for commands that do not
--wait expire after 10 minutes, so one the CLI never revokes lapses soon after.

Fixes #36050

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted Author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Nov 13, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — The change narrows list/show sessions to read-only and limits non-waiting CLI sessions to 10 minutes, while preserving cleanup and the existing lifetime for waiting commands. These alter existing product defaults and require confirmation of the intended permission and expiration behavior.

You can add or adjust custom eligibility rules. Learn more.

@github-actions

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 21.5 KiB 21.5 KiB 0 B (0.0%) 24.4 KiB ✅
Codex Live turn messages 1 1 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.9 KiB 21.9 KiB 0 B (0.0%) 24.4 KiB ✅
Claude Live turn messages 1 1 0 (0.0%) 8 ✅

Other scenarios

All 64 scenario measurements match main.

All 64 scenario measurements
Provider Scenario Metric Main baseline This PR Impact PR ceiling
Codex short turn resume WebSocket decoded 10.5 KiB 10.5 KiB 0 B (0.0%) 12.7 KiB ✅
Codex short turn resume WebSocket messages 2 2 0 (0.0%) 2 ✅
Codex long turn running, cold open HTTP decoded 281.7 KiB 281.7 KiB 0 B (0.0%) 302.7 KiB ✅
Codex long turn running, cold open rows 75 75 0 (0.0%) 75 ✅
Codex long turn running, history page HTTP decoded 92.4 KiB 92.4 KiB 0 B (0.0%) 107.4 KiB ✅
Codex long turn running, history page rows 75 75 0 (0.0%) 75 ✅
Codex long turn finished, cold open HTTP decoded 381.5 KiB 381.5 KiB 0 B (0.0%) 410.2 KiB ✅
Codex long turn finished, cold open rows 151 151 0 (0.0%) 151 ✅
Codex long turn finished, socket cold open WebSocket decoded 381.6 KiB 381.6 KiB 0 B (0.0%) 410.2 KiB ✅
Codex long turn finished, socket cold open WebSocket messages 2 2 0 (0.0%) 2 ✅
Codex long turn finished, socket cold open rows 151 151 0 (0.0%) 151 ✅
Codex long turn finished, work page HTTP decoded 186.2 KiB 186.2 KiB 0 B (0.0%) 200.2 KiB ✅
Codex long turn finished, work page rows 150 150 0 (0.0%) 150 ✅
Codex idle thread resume WebSocket decoded 65 B 65 B 0 B (0.0%) 200 B ✅
Codex idle thread resume WebSocket messages 1 1 0 (0.0%) 1 ✅
Codex fleet shell snapshot HTTP decoded 42.4 KiB 42.4 KiB 0 B (0.0%) 53.7 KiB ✅
Codex fleet shell snapshot rows 27 27 0 (0.0%) 27 ✅
Codex fleet shell subscribe WebSocket decoded 65 B 65 B 0 B (0.0%) 200 B ✅
Codex fleet shell subscribe WebSocket messages 1 1 0 (0.0%) 1 ✅
Codex fleet shell resume check HTTP decoded 18 B 18 B 0 B (0.0%) 100 B ✅
Codex fleet shell resume check rows 0 0 0 0 ✅
Codex stale shell reload HTTP decoded 42.4 KiB 42.4 KiB 0 B (0.0%) 53.7 KiB ✅
Codex stale shell reload rows 27 27 0 (0.0%) 27 ✅
Codex stale shell resume WebSocket decoded 140 B 140 B 0 B (0.0%) 400 B ✅
Codex stale shell resume WebSocket messages 2 2 0 (0.0%) 2 ✅
Codex stale todo list cold open HTTP decoded 72.4 KiB 72.4 KiB 0 B (0.0%) 83.0 KiB ✅
Codex stale todo list cold open rows 75 75 0 (0.0%) 75 ✅
Codex stale todo list cold open nodes 2 2 0 (0.0%) 4 ✅
Codex stale todo list cold open subagents 2 2 0 (0.0%) 4 ✅
Codex stale todo list cold open plans 1 1 0 (0.0%) 2 ✅
Codex stale todo list cold open providerTurns 1 1 0 (0.0%) 2 ✅
Codex stale todo list cold open checkpoints 1 1 0 (0.0%) 2 ✅
Claude short turn resume WebSocket decoded 10.6 KiB 10.6 KiB 0 B (0.0%) 12.7 KiB ✅
Claude short turn resume WebSocket messages 2 2 0 (0.0%) 2 ✅
Claude long turn running, cold open HTTP decoded 283.3 KiB 283.3 KiB 0 B (0.0%) 302.7 KiB ✅
Claude long turn running, cold open rows 75 75 0 (0.0%) 75 ✅
Claude long turn running, history page HTTP decoded 93.4 KiB 93.4 KiB 0 B (0.0%) 107.4 KiB ✅
Claude long turn running, history page rows 75 75 0 (0.0%) 75 ✅
Claude long turn finished, cold open HTTP decoded 384.2 KiB 384.2 KiB 0 B (0.0%) 410.2 KiB ✅
Claude long turn finished, cold open rows 151 151 0 (0.0%) 151 ✅
Claude long turn finished, socket cold open WebSocket decoded 384.3 KiB 384.3 KiB 0 B (0.0%) 410.2 KiB ✅
Claude long turn finished, socket cold open WebSocket messages 2 2 0 (0.0%) 2 ✅
Claude long turn finished, socket cold open rows 151 151 0 (0.0%) 151 ✅
Claude long turn finished, work page HTTP decoded 188.1 KiB 188.1 KiB 0 B (0.0%) 200.2 KiB ✅
Claude long turn finished, work page rows 150 150 0 (0.0%) 150 ✅
Claude idle thread resume WebSocket decoded 66 B 66 B 0 B (0.0%) 200 B ✅
Claude idle thread resume WebSocket messages 1 1 0 (0.0%) 1 ✅
Claude fleet shell snapshot HTTP decoded 43.0 KiB 43.0 KiB 0 B (0.0%) 53.7 KiB ✅
Claude fleet shell snapshot rows 27 27 0 (0.0%) 27 ✅
Claude fleet shell subscribe WebSocket decoded 66 B 66 B 0 B (0.0%) 200 B ✅
Claude fleet shell subscribe WebSocket messages 1 1 0 (0.0%) 1 ✅
Claude fleet shell resume check HTTP decoded 18 B 18 B 0 B (0.0%) 100 B ✅
Claude fleet shell resume check rows 0 0 0 0 ✅
Claude stale shell reload HTTP decoded 43.0 KiB 43.0 KiB 0 B (0.0%) 53.7 KiB ✅
Claude stale shell reload rows 27 27 0 (0.0%) 27 ✅
Claude stale shell resume WebSocket decoded 140 B 140 B 0 B (0.0%) 400 B ✅
Claude stale shell resume WebSocket messages 2 2 0 (0.0%) 2 ✅
Claude stale todo list cold open HTTP decoded 73.4 KiB 73.4 KiB 0 B (0.0%) 83.0 KiB ✅
Claude stale todo list cold open rows 75 75 0 (0.0%) 75 ✅
Claude stale todo list cold open nodes 2 2 0 (0.0%) 4 ✅
Claude stale todo list cold open subagents 2 2 0 (0.0%) 4 ✅
Claude stale todo list cold open plans 1 1 0 (0.0%) 2 ✅
Claude stale todo list cold open providerTurns 1 1 0 (0.0%) 2 ✅
Claude stale todo list cold open checkpoints 1 1 0 (0.0%) 2 ✅

Baseline: 5f85768 · PR result: b27d610 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@coderabbitai

coderabbitai Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

Thread CLI commands now select MCP session access and expiry options. list and show request read-only sessions with a 10-minute TTL. start and send request full-access sessions and pass their --wait setting. The 30-day default remains when no TTL is supplied.

Changes: Thread CLI session access and expiry

Layer / File(s) Summary
Optional session TTL
apps/server/src/auth/EnvironmentAuth.ts
issueMcpClientSession accepts an optional TTL. Session creation uses that TTL when supplied and retains the 30-day default otherwise.
Thread command session policy and tests
apps/server/src/cli/thread.ts, apps/server/src/cli/thread.test.ts
withThreadTools now takes an access level and wait setting. list and show request read-only sessions and a 10-minute TTL. start and send request full access and pass their --wait setting; waiting commands omit the TTL. Tests verify the access and TTL for list and show.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Low

Possibly related issues

  • pingdotgg/t3code#27117 — This change shortens the lifetime of sessions for commands that do not wait and makes list and show read-only; the issue’s session-row pruning concern is not addressed here.

Possibly related PRs

  • pingdotgg/t3code#34249 — Both PRs use the access and TTL options of EnvironmentAuth.issueMcpClientSession.

Suggested labels: bugfix

Suggested reviewers: t3dotgg, jo2234, maria-rcks

Merge Risk: Minimal · up to b27d6

The change makes thread list and show use read-only, short-lived sessions, while start and send keep full access. No merge-blocking risk was found.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title names the main change: t3 thread list and show now sign in with read-only access and a short-lived session. It follows the conventional commit format and matches the changeset. It is spe…
Linked Issues check Passed Scope: #36050 is the only active directly linked issue. Its coding requirements are (1) list and show should use a read-only session, and (2) a session left active by a killed CLI should expire within…
Out of Scope Changes check Passed The whole-PR diff (a2be070 to b27d610) touches three source files, and each change serves #36050:
- apps/server/src/auth/EnvironmentAuth.ts: adds optional ttl to issueMcpClientSession, defaulting to t…
Description check Passed The description follows the repository template closely and covers all required sections. Problem: withThreadTools issued every t3 thread session with full-access and the 30-day TTL, and revocation re…
Full details: Title check

Explanation

The title names the main change: t3 thread list and show now sign in with read-only access and a short-lived session. It follows the conventional commit format and matches the changeset. It is specific and accurate, so it passes. The title is 78 characters, which is long but acceptable.

Full details: Linked Issues check

Explanation

Scope: #36050 is the only active directly linked issue. Its coding requirements are (1) list and show should use a read-only session, and (2) a session left active by a killed CLI should expire within minutes, not 30 days.

Evidence at head b27d610 (diff against merge base a2be070):

  • Requirement 1 is met. threadListCommand and threadShowCommand call withThreadTools with { access: "read-only", waits: false }. The issued session therefore uses mcpClientScopes("read-only"), which the issue identifies as [orchestration:read].
  • Requirement 2 is met for list and show. withThreadTools passes ttl: CLI_SESSION_TTL (Duration.minutes(10)) whenever waits is false. EnvironmentAuth.issueMcpClientSession now accepts an optional ttl and uses input.ttl ?? MCP_CLIENT_SESSION_TTL, so the default 30-day lifetime is unchanged for other callers.
  • Start and send keep full-access, since they launch and message threads. With --wait, they keep the 30-day default. The PR states that a wait has no upper bound and sessions cannot be renewed, so this is a disclosed residual gap, not an omission. The issue's expected-behavior wording ('a CLI session that is never revoked does not outlive the command by much') is not fully met for start --wait and send --wait.

The author reports a test assertion for the 10-minute read-only session for list and show, and a failing repro-branch test before the fix. I did not inspect the test file; this relies on the author's report.

Full details: Out of Scope Changes check

Explanation

The whole-PR diff (a2be070 to b27d610) touches three source files, and each change serves #36050:

  • apps/server/src/auth/EnvironmentAuth.ts: adds optional ttl to issueMcpClientSession, defaulting to the existing MCP_CLIENT_SESSION_TTL. This supplies the 10-minute lifetime and keeps other callers unchanged.
  • apps/server/src/cli/thread.ts: adds the access and waits parameters to withThreadTools, sets CLI_SESSION_TTL, and passes access per command. Changes are limited to the thread CLI session path.
  • apps/server/src/cli/thread.test.ts: test coverage for the new access and TTL behavior, per the author's report.

The PR does not change pruning of session rows (#27117). The PR description lists this as excluded, and the diff does not touch it. The Evidence section in the PR description is a placeholder and carries no code. No unrelated code changes were found.

Full details: Description check

Explanation

The description follows the repository template closely and covers all required sections. Problem: withThreadTools issued every t3 thread session with full-access and the 30-day TTL, and revocation relied on acquireRelease, which does not run when the CLI is killed. Change: list and show now request read-only access, and the CLI passes a 10-minute TTL for commands without --wait; EnvironmentAuth.issueMcpClientSession gained an optional ttl with the default unchanged. Scope: the issue #36050 is linked and the change is a bug fix. Verification: a reproduction on main with before/after captures, a failing-then-passing unit test, and listed vp test and check runs. Limitations are stated (unexercised show of populated threads, mutating commands, --wait lifetimes). The author also names follow-up items that are out of scope (--wait sessions, #27117 row pruning). The model and harness are identified as the template requests.

Finishing Touches

Generate unit tests (beta)

  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 5f85768 and b27d610.

Files selected for processing: 3

  • apps/server/src/auth/EnvironmentAuth.ts
  • apps/server/src/cli/thread.test.ts
  • apps/server/src/cli/thread.ts

Limit details: You’ve used all 10 included reviews currently available.

This branch has not been deployed

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

Labels

size:S 10-29 changed lines (additions + deletions). vouch:trusted Author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: t3 thread list|show issue a full-access session for a read, and a killed CLI leaves it active for 30 days

1 participant