Skip to content

feat(t3): T3 Code as a third harness (#253) - #272

Merged
mergify[bot] merged 7 commits into
mainfrom
feat/253-t3-harness
Nov 6, 2026
Merged

mergify[bot] merged 7 commits into
mainfrom
feat/253-t3-harness

Conversation

@ericlitman

Copy link
Copy Markdown
Owner

Why

Eric decided on 2026-11-06 that T3 Code becomes a third harness next to Claude Code and Codex. In a T3 session, T3 owns starting, isolating and tracking agents, and pstack's dispatch rules conflicted with that. This adds a T3 route at the existing dispatch boundary, starting from fikaventures' PR #254 (first commit authored by Aiden Lee). Closes #253.

Summary

parent records once: orchestration harness (claude | codex | t3) + model runtime (claude | codex)
T3 parent, per lane:
  T3 offers and enables the provider (canRunChildTask, no constraint, model + effort)
      -> delegate_task  (claude->claudeAgent, codex, cursor, grok, opencode, agy->antigravity)
  else -> the route a standalone parent of that runtime uses (native subagent or external runner)
  else -> named dropout

Eric's rule, quoted in #253: "any lane for which there is a delegate provider available and configured should go through that delegate provider, otherwise it should go through the external runners."

  • T3 result record: capability snapshot, descriptor, request, raw responses. Evidence class is T3 launch configuration, not a served-model report.
  • Completion: provider, model and effort match on required settings; status: completed, workState: result_available, no pending children, question or approval; non-empty summary. A wait timeout means still running.
  • Setup on T3 probes T3-routed families through delegate_task and the rest as before. Fork's blanket setup bypass deleted.
  • AGENTS.md, UPSTREAM.md, .agents/triage-backlog.md, README and reference name three harnesses.
  • Verifier: a t3 transport runs a run-owned disposable T3 server from the operator's installed T3 binary, with the candidate plugin passed through the Claude provider's launchArgs, and reads the session transcript. Recipe t3:dispatch.

Evidence

  • t3:dispatch, real run against a disposable T3 0.1.7 server, before this rebase (nothing published): poteto-mode loaded from the candidate path; exactly one delegate_task with claudeAgent / claude-haiku-5-5 / {effort: low}; settled per the completion rules; a per-run marker absent from the prompt returned. ~/.t3/userdata/settings.json unchanged (hash and mtime).
  • Probe: a T3 session loads the exact candidate plugin through launchArgs --plugin-dir, and a child it delegates to loads it too.
  • Live gate: pending; features t3:dispatch, claude:setup, codex:skill-invocation:poteto-mode, with --self-test.

Merge Danger

Door: two-way

Blast Radius: T3 sessions

Standalone Claude Code and Codex routing is unchanged outside the ## T3 host section. In T3, lanes for providers T3 enables move from the runner to delegate_task.

Acceptance criteria

  1. "provider-dispatch.md has a T3 parent route… A lane whose provider T3 offers and has enabled goes through T3's delegate_task; any other lane goes through the existing external runner… A named dropout only when neither route can run it." Done (## T3 host); the runtime's own provider falls back to its native subagent, since the runner refuses same-provider lanes.
  2. "/setup-pstack works from a T3 session." Text done (steps 1, 5, 7, 9); not run live in T3 (see Not verified).
  3. "AGENTS.md, UPSTREAM.md and .agents/triage-backlog.md name three harnesses…" Done.
  4. "verify-open-pstack can run a live lane from a real T3 session, and the live gate for this PR covers T3, Claude Code and Codex." Transport and recipe done; live gate pending.
  5. Gates: below.

Gate results

On 30ba229: bun run test 0, bun run typecheck 0, bun run static 0, claude plugin validate plugins/pstack 0, verifier bun run test 0 (205 pass). no-comments: no comments added.

Not verified

  • /setup-pstack run live from a T3 session.
  • The Codex runtime under T3 (reasoningEffort spelling) and a T3 lane that falls back to the runner: no recipe yet.
  • Providers other than Claude and Codex through T3: disabled in this T3.
  • The transport reads two T3 0.1.7 internals (orchestration_v2_projection_provider_threads, server-runtime.json); a T3 change fails the case loudly.
  • Each T3 run leaves one Claude transcript folder in the operator's ~/.claude/projects/.

🤖 Generated with Claude Code

Aiden Lee and others added 7 commits November 6, 2026 11:33
Squashed net diff of fikaventures' PR #254 at 7bb8f55, applied to main
2d92c53. The only conflict was CHANGES.md placement; the fork's entry sits
above the 1.17.0 section.

Refs #253

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Corrections to the ported T3 host section, per the #253 design:

- The parent records the orchestration harness and the model runtime once;
  T3 is detected from the session's instructions and working tools, with
  T3CODE_THREAD_ID only as corroboration.
- A lane goes through delegate_task when T3 offers its provider, enabled,
  with the model and effort; otherwise it takes the standalone route of the
  same runtime (native or the external runner with --parent <runtime>); a
  named dropout only when neither can run it.
- inherit-parent and auto go through delegate_task with no target.
- Alias resolution stays inside the provider and the catalog's revision
  form, compares revisions numerically and ignores suffixes such as -high.
- Worktree children need a committed base or fixture inputs; no commits
  of unrelated changes on their behalf.
- Advisory read-only only where the caller allows it; enforced read-only
  takes the standalone route.
- T3 results are T3 launch configuration evidence with explicit
  completion, timeout and dropout rules.

Refs #253

Co-authored-by: Aiden Lee <aiden@fika.vc>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Step 1 records the orchestration harness and the runtime, and the runtime
selects the config home, sheet and integration. Step 5 reads T3's
capabilities once and routes each family by the T3 host order: T3-routed
families probe through delegate_task, standalone-routed families keep the
runtime's checks and probes. Step 7 shows each family's route and step 9
runs the smoke panel through the same routes. The fork's blanket setup
bypass in provider-dispatch.md is deleted.

Refs #253

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

AGENTS.md, UPSTREAM.md, README.md and docs/reference.md name Claude Code,
Codex and T3 Code; T3 owns orchestration while its runtime owns
configuration and skill installation. The triage rule that routed a new
tool only as an external provider now distinguishes model providers from
parent harnesses. UPSTREAM.md records the T3 routing adaptation, and
CHANGES.md carries the entry under Unreleased.

Refs #253

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
T3 names Claude `claudeAgent` and Antigravity `antigravity`, where pstack
says `claude` and `agy`. One table in the T3 host section now maps each
pstack provider to the `driverKind` a live `orchestrator_capabilities`
call lists; a provider without a T3 driver takes the standalone route.

Refs #253

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The verifier can now run a live lane from a real T3 Code session. The
`t3` transport starts a run-owned disposable T3 server from the
operator's installed service binary, gives its Claude provider the same
session-only candidate plugin flags as the Claude harness, runs each
case with `t3 thread start --wait --json`, stops the server, and parses
the parent's Claude transcript, located through the run-owned
`statev2.sqlite`, with the Claude stream parser.

`t3:dispatch` has the installed poteto-mode dispatch one fixture-sheet
role (`claude:claude-haiku-5-5@low`) and asserts the candidate skill
path, the exact `delegate_task` request, the settled response and a
per-run marker the child read from a fixture file.

Classification reaches `t3:dispatch` only from provider-dispatch.md,
codex-tools.md, setup-pstack/SKILL.md and recipes.ts, never by default;
self-test adds no T3 session, and the doctor checks the T3 binary only
when a `t3:` feature is named.

Refs #253

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #253

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mergify

mergify Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 All 2 merge protections satisfied — ready to merge.

Show 2 satisfied protections

🟢 Queue ready pull requests

Automatically queue non-draft pull requests targeting main after verify and live-gate pass on the head and every review thread is resolved.

  • #review-threads-unresolved = 0
  • -draft
  • check-success=live-gate
  • check-success=verify

🟢 🚦 Auto-queue

When all merge protections are satisfied and these conditions match, this pull request will be queued automatically.

  • -files~=^\.github/
  • base = main

@ericlitman

Copy link
Copy Markdown
Owner Author

Live evidence: Open Pstack

  • Candidate SHA: 30ba22997b0f6c36e1a763117be97da3191ae189
  • Base SHA: b411250b31dda88136a5a559b55e8c00c9c0e2af
  • Result: PASSED — named features verified
  • Self-test: completed in both harnesses
  • Planned sessions: 6 (limit 12)
  • Publisher revision: 30ba22997b0f6c36e1a763117be97da3191ae189
  • Evidence-set SHA-256: 3ff70b94a10caaeaafe6e17d5b580b643afb3ae54a686d00128574f585e76d7a
  • Installation (claude): CLI 2.1.318 (Claude Code), plugin 1.19.0, candidate 30ba22997b0f6c36e1a763117be97da3191ae189, tree a93afad618df4a91f08a78484c0ae27f564be7016821b94992891c98e03f4684.
  • Installation (codex): CLI codex-cli 0.167.0, plugin 1.19.0, candidate 30ba22997b0f6c36e1a763117be97da3191ae189, tree a93afad618df4a91f08a78484c0ae27f564be7016821b94992891c98e03f4684.
  • Installation (t3): CLI t3 v0.1.7, plugin 1.19.0, candidate 30ba22997b0f6c36e1a763117be97da3191ae189, tree a93afad618df4a91f08a78484c0ae27f564be7016821b94992891c98e03f4684.
  • Observation (claude / skill-invocation:poteto-mode): surface claude -p stream-json with the candidate plugin-dir; action recipe skill-invocation:poteto-mode: invoke; result passed: invoke:exit-0 invoke:skill-loaded invoke:file:result.md; 3 assertions machine-checked; transcript SHA-256 c1798ba44859de155fdbcd5f961ce56632ecabedcfb01afb779196eb624f91d1; artifact-set SHA-256 2ace1a6a1f2b364dc6977cfb5cab72691e2e49958eb9a6a3d4da066988858d4f.
  • Observation (claude / project-skill): surface claude -p stream-json with the candidate plugin-dir; action recipe project-skill: doctor; result passed: doctor:exit-0 doctor:skill-loaded doctor:doctor-ran doctor:doctor-canonical-pass; 4 assertions machine-checked; transcript SHA-256 0110c5cf36aa9200ad07ea7a6cb463c2f0a386bd3e5ac096c4d89debf379e6e2; artifact-set SHA-256 642bf32a12d722fd38f05d0a4f9e9d30b485b88abd7ed2944585fdf7f66dd1ef.
  • Observation (codex / catalog): surface codex exec --json with the run-owned candidate installation; action recipe catalog: consume-sheet, consume-sheet-bounded; result passed 24 assertions; ids retained privately; 24 assertions machine-checked; transcript SHA-256 a937a4e97e89691a65b374774a784d1d55f4e4dd646896749e749cc69c26be4f; artifact-set SHA-256 e05395bfc9b5b17e2dc1c3e3f8c0ce93463e5600231f6c295a971712cb5beb72.
  • Observation (codex / project-skill): surface codex exec --json with the run-owned candidate installation; action recipe project-skill: doctor; result passed: doctor:exit-0 doctor:skill-loaded doctor:doctor-ran doctor:doctor-canonical-pass; 4 assertions machine-checked; transcript SHA-256 5759ce4d82b35c27554920b975a295dbad74877966284419eab540f6af73cdb9; artifact-set SHA-256 05f7a8d5a1e7a943856643b765268f04233e03a6b1c81c3bcef195355e564929.
  • Observation (t3 / dispatch): surface t3 thread start on a run-owned T3 server whose Claude provider loads the candidate plugin-dir; action recipe dispatch: dispatch; result passed: dispatch:exit-0 dispatch:skill-loaded-from-candidate dispatch:delegate-request dispatch:delegate-settled dispatch:marker-returned; 5 assertions machine-checked; transcript SHA-256 b16263e1caae91d722cd239803d81173315f15122602c7c3c0ec3e4b0196944b; artifact-set SHA-256 a88c1b9f89ebab2583992fbffb185883f40c7982cfdfda38b0ef3505fdd532d0.
  • Policy: every new head requires a fresh run; this verifier does not change PR state, queue, or merge.

@mergify
mergify Bot merged commit 5650d79 into main Nov 6, 2026
6 of 7 checks passed
@mergify

mergify Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

✅ Merged into main as 5650d79 at 16:42 UTC

Less than a minute in the queue, with no time running CI.

So far

  • ✅ Entered the queue Fri 6 Nov, 16:42 · rule default · triggered by merge protections
  • ✅ Checks skipped · PR is already up-to-date
  • ✅ Merged 16:42 · 5650d79 · merge
8 merge conditions met
  • github-review-approved · required by GitHub repository ruleset rule Protect main
  • #review-threads-unresolved = 0 · required by Merge Protections rule Queue ready pull requests
  • -draft · required by Merge Protections rule Queue ready pull requests
  • check-success=live-gate · required by Merge Protections rule Queue ready pull requests
  • check-success=verify · required by Merge Protections rule Queue ready pull requests
  • any of · required by GitHub repository ruleset rule Protect main:
    • check-success = @github-actions/verify
    • check-neutral = @github-actions/verify
    • check-skipped = @github-actions/verify
  • any of · required by GitHub repository ruleset rule Protect main:
    • check-success = live-gate
    • check-neutral = live-gate
    • check-skipped = live-gate
  • any of · required by GitHub repository ruleset rule Review gate:
    • check-success = @mergify/Mergify Merge Protections
    • check-neutral = @mergify/Mergify Merge Protections
    • check-skipped = @mergify/Mergify Merge Protections

Times in UTC · dashboard · how the merge queue works

@greptile-apps

greptile-apps Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileTier: plus

[Medium impact] The new dispatch behavior is limited to T3 sessions, with separate changes to the operator verifier.

Fix the two T3 verifier failures before merging.

Findings

  1. P1 Completed tasks fail verification ▶
  2. P1 Apostrophes break plugin loading ▶

Summary

Adds T3 Code as a third parent harness while keeping Claude Code and Codex responsible for configuration and skill loading.

  • T3 sends supported model lanes through its own task delegation.
  • The verifier can run a live T3 dispatch check.

Acknowledged by ericlitman: live T3 setup, the Codex runtime under T3, runner fallback, and other T3 providers remain unverified. The PR also records its reliance on T3 internals and retained Claude transcripts. These were not reported as defects.

Reviews (1) · Last reviewed commit: "chore: release T3 Code as a third harnes..." · Reviewed by Greptile

@unfret-eal

unfret-eal Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

🛑 10 blocking findings

🔴 Critical · T3 route ignores the writer-sandbox setting for confined writers

🔴 High · Map the T3 transport itself to `t3:dispatch`

🟡 Medium · Startup cancellation leaves T3 running

🟡 Medium · Handle Claude's hashed transcript folders for long paths

🟡 Medium · Escape apostrophes in the T3 plugin path

🟡 Medium · Empty Claude config overrides break transcript collection

🟡 Medium · Resolve T3 routes before rejecting writer assignments

🟡 Medium · Deferred completion is judged from the initial response

🟡 Medium · The server stops before deferred delegation completes

🟡 Medium · T3 server readiness loop never exits if the server dies by signal

Walkthrough

Adds T3 Code as a third parent harness next to Claude Code and Codex. In a T3 session, pstack still chooses each role's provider, model and effort. A lane whose provider T3 offers and has enabled goes through T3's `delegate_task`. Any other lane takes the route a standalone Claude Code or Codex parent of the same runtime would use. A lane neither route can run is a named dropout. The PR also teaches `/setup-pstack` to probe and smoke-test through the same routes. It adds a `t3` transport and a `t3:dispatch` recipe to the verifier, updates the repo docs to name three harnesses, and bumps the version to 1.19.0. The author reports one real `t3:dispatch` run against T3 0.1.7; the live gate is still pending, and `/setup-pstack` has not been run live in T3.

Area What changes Review notes
Dispatch rules `provider-dispatch.md` gains a `T3 host` section. It covers the T3 route, then the standalone route, then a named dropout. It includes a provider-to-driverKind table, target and effort options, alias resolution, worktree isolation, and advisory read-only. A T3 lane completes only when status is `completed`, workState is `result_available`, nothing is pending, and the summary is non-empty. `codex-tools.md` points to the new section. The T3 route does not honour the writer-sandbox setting for confined writers. T3 route ignores the writer-sandbox setting for confined writers
Setup `/setup-pstack` records the orchestration harness and the model runtime. It reads `orchestrator_capabilities` and probes T3-routed families with `delegate_task`, keeping T3 result records separate from native and external results. The smoke panel runs through each family's route. Writer assignments may be rejected before T3 routes are resolved. Resolve T3 routes before rejecting writer assignments
Verifier T3 transport Adds a `t3` harness. It starts a disposable T3 server from the installed binary, with the candidate plugin passed in `launchArgs`. It then runs `t3 thread start --wait` and maps the thread through `statev2.sqlite` to read the parent's Claude transcript. Several transport findings are open: - Server lifecycle: the server can be left running if startup is cancelled; the readiness loop never exits if the server dies by signal; and the server stops before deferred delegation completes. - Completion check: deferred completion is judged from the initial response. - Transcript lookup: hashed transcript folders for long paths and empty `CLAUDE_CONFIG_DIR` overrides break transcript collection. - Plugin path: apostrophes in the path are not escaped. Startup cancellation leaves T3 running
T3 server readiness loop never exits if the server dies by signal
The server stops before deferred delegation completes
Deferred completion is judged from the initial response
Handle Claude's hashed transcript folders for long paths
Empty Claude config overrides break transcript collection
Escape apostrophes in the T3 plugin path
Verifier classification and recipe Changes to the dispatch, codex-tools, setup and recipes files now select `t3:dispatch`. Self-test never adds a T3 session. Adds tests for the classification and the required harnesses. The T3 transport code itself (`harness.ts`) is not mapped to `t3:dispatch`. Map the T3 transport itself to `t3:dispatch`
Docs and version `AGENTS.md`, `UPSTREAM.md`, the triage backlog, the README, the reference and `CHANGES.md` now name three harnesses. Manifests are bumped to 1.19.0.
Since the last review
  • New: Startup cancellation leaves T3 running
  • New: Handle Claude's hashed transcript folders for long paths
  • New: Escape apostrophes in the T3 plugin path
  • New: Empty Claude config overrides break transcript collection
  • New: T3 route ignores the writer-sandbox setting for confined writers
  • New: Resolve T3 routes before rejecting writer assignments
  • New: Deferred completion is judged from the initial response
  • New: The server stops before deferred delegation completes
  • New: Map the T3 transport itself to `t3:dispatch`
  • New: T3 server readiness loop never exits if the server dies by signal
Repair prompt

Read the Unfret check run for ericlitman/open-pstack#272 at head 30ba22997b0f6c36e1a763117be97da3191ae189 (run panel:30ba22997b0f6c36e1a763117be97da3191ae189:ipSjs9Pu9Uh:lsAk9-FnxLU, check). Use the check's App id and GET /repos/ericlitman/open-pstack/check-runs/128017142697; its output.text contains the structured result. Compare it with the current code and head, verify that each listed mandatory finding still applies, repair only those that do, honor the repository constraints, and validate the changes. Leave advisories and accepted risks alone. A manually resolved thread is not a waiver.

Details
Lane / stage Model Effort Outcome Duration
broad-gpt-5.6-sol-xhigh / review openai/gpt-5.6-sol → gpt-5.6-sol · primary xhigh succeeded 8 min 27 s
broad-opus-5.5-max / review anthropic/claude-opus-5-5 → claude-opus-5-5 · primary max succeeded 12 min 51 s
focal-gpt-5.6-sol-xhigh copy / review openai/gpt-6.1-sol → gpt-6.1-sol · primary xhigh succeeded 13 min 58 s
focal-gpt-5.6-sol-xhigh copy 2 / review openai/gpt-6.1-sol → gpt-6.1-sol · primary xhigh succeeded 16 min 55 s
judge / judge openai/gpt-6-astra → gpt-6-astra · primary high succeeded 5 min 51 s
confirmation / confirmation anthropic/claude-opus-5-5 → claude-opus-5-5 · primary max succeeded 4 min 43 s

First review of this pull request.
4 reviewers finished.
Commit 30ba229.
Check run.
Run: GET /unfret/run/panel:30ba22997b0f6c36e1a763117be97da3191ae189:ipSjs9Pu9Uh:lsAk9-FnxLU.

@unfret review · @unfret status · @unfret help

@unfret-eal unfret-eal Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 10 blocking findings
🔴 Critical · T3 route ignores the writer-sandbox setting for confined writers
🔴 High · Map the T3 transport itself to `t3:dispatch`
🟡 Medium · Startup cancellation leaves T3 running
🟡 Medium · Handle Claude's hashed transcript folders for long paths
🟡 Medium · Escape apostrophes in the T3 plugin path
🟡 Medium · Empty Claude config overrides break transcript collection
🟡 Medium · Resolve T3 routes before rejecting writer assignments
🟡 Medium · Deferred completion is judged from the initial response
🟡 Medium · The server stops before deferred delegation completes
🟡 Medium · T3 server readiness loop never exits if the server dies by signal
Check

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support T3-owned delegation in pstack provider dispatch

1 participant