Skip to content

Drive agent work from GitHub issues #8

Closed

Description

@tannerpolley

Start after the upstream sync (#5) lands. It builds on the ported Issues browser (#4), model roles, the delegate_task worktree option and pull-request linking.

Goal

Drive agent work directly from GitHub issues, with the issue and the agent threads staying in sync.

Required behavior

  1. Start an agent from an issue. An action on an issue (the Issues page and the issue side panel) opens a thread whose task is the issue.

    • It chooses the model through the user's model roles.
    • It can use a worktree.
    • The issue is linked to the thread the same way pull requests are linked today.
    • This replaces or extends today's "Start in worktree".
  2. An orchestrator works through a milestone. Point an orchestrator thread at a milestone.

    • It starts workers on each open issue that isn't blocked.
    • It respects the worker cap. The global rules set about 4, and heavy checks run through agent-heavy.
    • It tracks the workers and reports per issue.
    • It honors native "blocked by" dependencies and sub-issues.
  3. Issues show agent status. An issue shows which thread or worker is on it and its live state: working, waiting on the user, or done. A worker's closeout posts back to the issue.

  4. Agents keep issues current. As part of orchestration, agents can:

    • create and split issues;
    • change milestones;
    • update issue bodies.

    Approval rules decide which of these need the user's go: closing issues, editing user-written text, and anything else the user chooses.

Agent structure (user requirements, 2026-10-05)

  1. One main orchestrator. Each repository has exactly one main orchestrator thread, and it is in charge of every milestone and all orchestration for that repository. The app doesn't start or allow a second top-level orchestrator for the same repository.
  2. Every subagent is tied to one issue.
    • Each subagent works on exactly one issue, and its thread name says which, e.g. #243 Diagnose two-liquid feed-scale conditioning.
    • The issue-to-thread link is durable, as in part 3.
  3. Worktree icon. A subagent working in its own worktree shows the worktree icon from Worktree icon on child threads in their own worktree #7.
  4. The thread tree mirrors the issue tree.
    • A subagent can own a parent issue. The workers for that issue's sub-issues are then that subagent's own subagents, not the main orchestrator's.
    • A leaf issue's worker reports to the subagent that owns its parent issue. That subagent reports to the main orchestrator.
    • This uses the existing delegation lineage. Results flow up only through final-message delivery: children never message their parents mid-task (Sync fork onto upstream's released orchestrator v2 #5 enforces this).

Open questions to settle before building

  • Where the issue-to-thread link lives: thread metadata like pull-request linking, plus a marker on the issue.
  • How the app recognizes "the" main orchestrator for a repository (a thread flag set when it is created or handed off?) and what handoff looks like.
  • Whether milestone orchestration is a new orchestrator mode or a skill or instruction set on top of existing tools.
  • Which parts need the user's approval in-app, and which are covered by standing rules.

Activity

  1. tannerpolley commented on Oct 5, 2026

    @tannerpolley
    OwnerAuthor

    Part of this work (user, 2026-10-05): "Browse all issues" opens the full list in the side panel, not the main panel. That is tracked in #6, which should land first or alongside, so starting agents from issues happens in the side panel next to the thread.

  2. tannerpolley commented on Oct 5, 2026

    @tannerpolley
    OwnerAuthor

    Design pass started 2026-10-05: a read-only GPT-6 Astra xhigh proposal that answers the three open questions and proposes 3–5 shippable slices, each marked interface-only or server. The user decides before any building.

  3. tannerpolley commented on Oct 5, 2026

    @tannerpolley
    OwnerAuthor

    Design proposal (GPT-6 Astra xhigh, read-only, 2026-10-05)

    Status: proposal awaiting the user's decisions. Mobile is out of scope for this fork for now.

    I recommend one ordinary orchestrator thread per repository, backed by a small server-owned ownership record, with issue orchestration instructions layered on the existing delegation tools. The server should enforce identity, ancestry, capacity, and approvals; the agent should decide how to do the work.

    The durable issue link belongs in thread metadata. GitHub gets a managed status comment and closeout comments. Routine model handoff should keep the same app thread, preserving its children and pending results.

    This proposal covers all eight requirements in #8. It builds on #6’s repository-scoped side panel and #7’s worktree indicator. It proposes no new general workflow engine, separate milestone conversations, or independent messaging protocol.

    I read the issues, comments, and checkout on sync/upstream-v2. The checkout was clean when inspected, at b6ae77a737. I made no edits, commits, checkouts, stashes, GitHub writes, or test runs.

    What the code already provides

    These are verified facts; the recommendations that follow are proposals.

    Existing behavior Evidence and design consequence
    delegate_task creates an app-owned child under the calling thread, requires an active parent run, and uses stable command IDs for retries. OrchestratorMcpService.ts:1567. Extend this path; do not introduce a second worker launcher.
    Delegation already creates and prepares worktrees. Implementation and test roles default to worktrees; explicit worktree requests fail when the checkout cannot support one. OrchestratorMcpService.ts:1063. Reuse this behavior and #7’s indicator.
    Thread lineage stores parentThreadId, relationship, and root. Completion delivery also depends on the parent’s task node and run. orchestrationV2.ts:103, Orchestrator.ts:9190. Changing only parentThreadId would not safely transfer a live task.
    Completion distinguishes a finished turn from work still waiting on children. SubagentProjection.ts:210. Use this distinction for issue status and closeout.
    Children cannot send messages to ancestors through the guarded MCP paths. The server separately sends parent notifications about pending child questions. OrchestratorMcpService.ts:427, Orchestrator.ts:10311. Preserve final-result delivery and the question path; add no progress-message exception.
    Model roles contain ordered targets. Capabilities expose those targets with availability reasons; the delegation role field is a separate task classification. settings.ts:1289, orchestratorMcp.ts:517, T3OrchestrationInstructions.ts:42. Resolve configured model-role targets rather than hard-coding models or overloading role.
    PR linking has both the compatibility linkedPullRequest field and newer multiple-link contracts. link_pull_request dispatches a durable command; a server reactor watches linked PRs. orchestrationV2.ts:383, pullRequests/handlers.ts:305, PullRequestWatchReactor.ts:64. Reuse the command/reactor pattern, while keeping the issue link singular.
    The Issues service exposes only repositories, list, and detail. Its contracts have no issue writes, parent/sub-issue relations, or blockers. The current worktree action opens an unsent draft. IssueService.ts:611, issue.ts:146, IssueDetailPanel.tsx:231. “Start agent” requires server work, not merely a new button.
    Scheduled tasks can launch a fresh thread or queue a message into an existing thread. ScheduledTaskService.ts:518. Repository schedules must resolve the registered owner and queue there.
    Automatic title refresh skips user titles; first-message generation has its own guard. ThreadTitleRefreshWorker.ts:39, Orchestrator.ts:4523. Protect issue titles in both paths and when generation results arrive.

    The three open questions

    1. Put the authoritative link on the thread; put a visible backlink and status on GitHub.

    Add one nullable linkedIssue to the app thread and its shell projection. It contains the existing issue reference—host, repository, number—plus the URL and GitHub’s stable repository/issue IDs. Numbers remain the human-facing identity; stable IDs prevent a repository rename from creating a second owner accidentally.

    Every newly created repository subagent must have exactly one issue before its run starts. Historical threads remain readable without fabricated links. One issue may retain several historical threads, but only one current owner at a time. A later implementation or review attempt can take ownership after the previous attempt finishes.

    On GitHub:

    • Maintain one T3-owned status comment per issue, identified by a hidden marker and persisted comment ID. It names the current thread, model, worktree branch when applicable, state, and last update.
    • Append one closeout comment per completed attempt, containing the worker’s final outcome, evidence/PR links, and remaining gates.
    • Keep issue bodies, labels, and assignees out of this synchronization mechanism. They remain planning data with their own approval rules.
    • Include a thread link only when a usable application origin is known. Otherwise show the environment/thread reference. Never publish a pairing token or assume localhost is reachable.

    The existing thread route already includes both environment and thread IDs: agentAwareness.ts:33. Turning that into a public GitHub backlink needs an explicitly configured, usable origin.

    The comment is a mirror, not the ownership lock. Deleting it must not release the issue or cause another worker to start.

    2. Use a mix: ordinary agent instructions plus a small amount of enforced server state.

    There should be no new provider interaction mode and no second orchestration runtime.

    Inject a short repository-orchestration instruction block into the registered root and its issue workers, following the existing model-role instruction mechanism. It tells the agent to:

    • Read the issue and current dependencies.
    • Choose models from configured roles.
    • Delegate eligible work through delegate_task.
    • Use asynchronous delegation, then finish its turn while children work.
    • Ask questions through the existing question tool.
    • Report upward through its final message.
    • Keep issue planning changes within the approved policy.

    The server, rather than those instructions, must enforce:

    • One registered root per repository.
    • One issue per child and one current owner per issue.
    • Correct parent ownership.
    • Shared worker admission.
    • Exact, durable approval of GitHub mutations.
    • Idempotent launches and writebacks.

    The agent chooses priorities, decomposition, implementation, and whether an issue’s acceptance criteria have been met. GitHub remains authoritative for issue text, milestones, native hierarchy, blockers, and open/closed state.

    3. Identify the main orchestrator through a repository record, not its title, model, or a loose thread flag.

    Create one record keyed by GitHub host and stable repository ID, containing the owning thread, selected project/workspace, ownership revision, paused/running state, worker cap, selected milestones, and approved policy.

    A unique database key prevents two roots for the same repository in the owning environment—even when several local projects or worktrees point to it. Thread badges derive from this record. Renaming, forking, changing models, or pinning a thread never grants ownership.

    “Start agent” and “Work through milestone” look up this record. The first action creates the root atomically if necessary; later actions route to it. They never silently create another top-level orchestrator.

    For routine handoff, change the provider/model on the same app thread. Existing provider-switch planning operates on the current thread and its provider history, and t3_thread_configure already exposes selection changes: ProviderSwitchService.ts:69, thread/tools.ts:190. The root ID, children, approvals, and issue ownership stay put.

    For a genuinely new app thread:

    1. Pause new admissions under the old root.
    2. Let its active issue subtree finish and deliver results through the existing lineage.
    3. Prepare the new thread with a summary and server-generated inventory of milestones, completed attempts, remaining issues, and pending changes.
    4. Atomically replace the root pointer using the expected ownership revision.
    5. Reject subsequent orchestration requests from the retired root; retain its history.

    Do not transfer live children by editing their lineage. Initially, fresh-thread handoff should wait for the subtree to drain. A user who needs an immediate model change can use same-thread handoff.

    One boundary needs to be explicit: a repository has one authoritative T3 environment. Connected clients route work there; another environment does not automatically take over when it is offline. SQLite cannot guarantee exclusivity across unrelated, independently configured T3 installations. Global ownership across those installations would require a shared coordinator and is outside this minimal design. A GitHub comment is not a sufficient substitute.

    How issue work would run

    The issue action becomes Start agent, with a model-role choice, workspace choice, and the effective approval policy visible before starting. Preserve an optional “Prepare draft” action for the current unsent-composer behavior.

    The start service resolves the selected issue repository and a matching project, reads the issue, then queues a start request to the registered root. The root delegates through the existing active-run path. The server validates the issue and parent at delegation time.

    Repository resolution must use the issue’s selected repository. The current thread Issues helper intentionally prefers the fork’s origin repository over the upstream PR identity: ThreadDetailsIssueRows.tsx:42. Preserve that distinction.

    For nested issues, the active tree is:

    flowchart TD
        R["Repository orchestrator — all milestones"]
        R --> P["#120 Parent issue owner"]
        P --> A["#121 Sub-issue worker"]
        P --> B["#122 Sub-issue worker"]
        R --> C["#130 Independent issue worker"]
    
    Loading

    Milestones select work; they do not add artificial thread levels.

    A caller may delegate an issue only if it is:

    • The repository root, for an issue with no parent; or
    • The current owner of that issue’s immediate GitHub parent.

    If a selected sub-issue has no parent owner, start the required ancestor issue owners first. Do not silently attach it to the root. If this would enter an unapproved milestone or another repository, present that expansion for approval.

    A reviewer is also an issue worker. Sequential review can become the issue’s next attempt under the same proper parent. Parallel independent review needs its own genuine sub-issue; do not create untracked review children or attach a same-issue child beneath itself.

    Read native GitHub blockers and hierarchy. GitHub supplies APIs for issue dependencies and sub-issues and parents; the current T3 IssueService needs those additions.

    An issue is eligible only when it is open, authorized, unclaimed, correctly parented, and all known blockers are closed. Missing permissions, incomplete pagination, or failed dependency reads mean eligibility unknown, not “unblocked.” Check again before launch. A parent issue does not implicitly block its children; hierarchy and dependencies serve different purposes.

    Capacity should mean active work across the whole repository tree.

    Default to four worker slots shared across all milestones and descendant levels. The repository root is the coordinator and is outside those four; a parent-issue agent occupies a slot while actively working.

    Apply admission when a worker run starts, including resumed runs and completion follow-ups—not just when a child is first created. A child may be queued without starting its provider. Worktree preparation should also be bounded.

    A live worker waiting on user input retains its slot. An issue owner whose turn has ended and is waiting on children does not. Managed delegation uses mode="async" so an ancestor does not hold a blocking tool call while waiting for children that cannot get a slot.

    This needs one repository-level admission rule shared by initial starts and resumes. Counting four children separately under every parent would violate the requested cap.

    Keep agent-heavy in the worker instructions for heavy checks. Worker admission and command memory limits address different resources.

    Status and writeback

    Derive live status from existing runs, pending requests, delegated progress, and background work:

    Display Meaning
    Queued / preparing Accepted, awaiting capacity or workspace setup.
    Working An active worker run.
    Waiting on you A pending input or approval request.
    Waiting on sub-issues No active turn, but child work or result delivery remains outstanding.
    Done A successful delegated result is available and no owned work remains.
    Failed / interrupted / cancelled Preserve the actual outcome.
    Paused / unavailable Repository work is paused, or eligibility/access cannot be established.

    Agent done and GitHub issue closed are separate states. A completed implementation can still need review, a push, a PR, or merge approval. Do not close an issue simply because a provider turn ended.

    Publish closeouts from the existing delegated-result milestone, after delegatedTaskProgress reports result_available. This avoids an early parent closeout while its children are still running.

    Use the existing event/outbox boundary for GitHub writes. It already commits command receipts, events, projections, and effects together: EventSink.ts:515. Give every outgoing closeout a stable key derived from its issue, thread, and result event.

    GitHub calls can have ambiguous outcomes after network failures. Persist returned IDs, and reconcile the marker before retrying an uncertain create. Promise recoverable, deduplicated delivery—not an impossible cross-system transaction.

    The app gets live status through existing server events. Coalesce GitHub status-comment updates; do not write for every token or tool call. Display “GitHub update pending” when publishing fails.

    Minimal data, contracts, and migrations

    Keep the new state limited to three responsibilities:

    Record Minimal contents and purpose
    Thread issue binding linkedIssue: stable identity, current repository/number, URL. Included in thread and shell projections. Historical links remain on their threads.
    Issue work record Keyed by host + stable issue ID. Current owning thread/attempt, status-comment ID, and publishing bookkeeping. Runtime status is derived rather than maintained as another agent state machine.
    Repository orchestration record Repository identity, owner thread, workspace/project, revision, paused/running state, worker cap, selected milestones, approved rules.
    Pending issue change Added with issue upkeep: immutable proposed operation, exact payload/diff, expected source state, requesting thread, approval decision, and execution result.

    The issue work pointer answers “who owns this now?” Thread bindings answer “what was this thread about?” Those are distinct responsibilities, not competing sources for the same fact.

    Contract changes:

    • Add optional/defaulted linkedIssue to thread, shell, launch/delegation, MCP read, and client representations.
    • Add an issue argument to delegate_task; require it for new repository-managed children. Persist it in the same command that creates the child.
    • Extend issue reads with stable IDs, parent/sub-issues, blockers, and completeness flags.
    • Add typed start, link/unlink, work-status, repository-control, and issue-change operations.
    • Add capability flags so new clients retain read-only behavior against older servers.
    • Keep existing task IDs, completion delivery, worktree contracts, and model selection.

    Suggested migration allocation:

    • 059 — ThreadIssueWork: issue-work projection/indexes and synchronization bookkeeping.
    • 060 — RepositoryOrchestration: unique repository-owner record and its projection.
    • 061 — IssueChanges: durable proposals, approvals, and mutation results.

    Thread JSON gains optional fields decoded as absent/null for old data. Do not infer issue links from titles or issue URLs in old messages. Replay must reconstruct the new projections from durable events.

    The current manifest ends at 058 and explicitly requires new migrations for future V2 changes: Migrations.ts:141. These numbers are proposed reservations; recheck them when implementation begins.

    Keep issue numbering in the actual title.

    Store #243 Diagnose two-liquid feed-scale conditioning as the title. Do not add a decorative prefix only in the sidebar.

    Reuse titleSource:

    • generated: suffix comes from the issue title.
    • user: suffix was explicitly renamed by the user.

    A linked issue owns the numeric prefix in either case. A single server helper should normalize it on creation, linking, and renaming. Linked threads bypass first-message and periodic AI retitling; stale title-generation results must also be rejected. “Use issue title” restores the generated suffix. No third title-source value is necessary.

    Service, MCP, and UI boundaries

    Extend IssueService for GitHub reads and writes, credential verification, pagination, and rate limits. Add one domain service, IssueWorkService, for starting/linking work, repository ownership, eligibility checks, approval policy, and writeback coordination. Keep pure invariants in the orchestration command path and external I/O in effects.

    Expose thin MCP tools such as:

    • link_issue, unlink_issue, and get_issue_work.
    • start_issue_work and get_repository_orchestration.
    • Repository pause/resume and handoff requests.
    • propose_issue_change, with a typed operation for create, update, milestone, hierarchy, or close/reopen.

    Extend delegate_task; do not add a competing delegate_issue_task. Where useful, expose the same methods through WebSocket RPC and scheduled tasks.

    Existing issue RPCs have read scope, while PR writes and schedule mutations use operate scope: RpcAuthorization.ts:95. New starts and mutations need operate scope. Approval-policy changes and mutation approval must additionally verify that the actor is the user; an agent cannot acquire that authority by setting a payload field.

    The UI owns presentation and input:

    • Shared Start agent action in the full Issues page and issue side panel.
    • Linked issue in thread details; current worker/status in issue rows and details.
    • Repository owner, milestone selection, pause/resume, capacity, and handoff controls.
    • Exact change previews and approval cards.
    • Reuse Worktree icon on child threads in their own worktree #7’s workspace comparison and icon on sidebar, lineage, and Agents surfaces.

    Follow the service boundary already documented in the repo: handlers decode, call a service, and map errors.

    Approval rules

    Starting work is explicit authorization for the selected scope and displayed publishing policy. It should not require another approval for each eligible worker.

    Action Recommended default
    Read issues, blockers, milestones, or worker state Allowed.
    Start eligible workers inside an approved issue/milestone scope and cap Covered by the start authorization.
    Publish T3 status and worker closeout comments Covered by the publishing policy accepted when work starts.
    Create or split issues; move them between milestones Ask initially. The user may grant a bounded standing rule for the repository/milestones.
    Edit an issue body Ask with the exact diff. Standing permission may cover T3-authored text that has not subsequently changed.
    Close/reopen issues or change parent/dependency relationships Ask unless explicitly covered by standing rules.
    Expand scope, change ownership environment, or transfer to a new root thread Explicit in-app approval.
    Push, create a PR, merge, delete worktrees, or delete issues Retain their separate existing authority requirements. Issue orchestration grants none automatically.

    Do not identify “agent-written” text by GitHub author login: the agent and user may share an account. Track the baseline of bodies written through this service; if it no longer matches, treat the content as externally edited.

    Use t3_request_user_input for questions about scope or choices. Its definition explicitly says it never approves a permission request: thread/tools.ts:136.

    GitHub mutations need a durable application approval tied to the exact proposal. The tool returns a pending-change reference; the user approves in-app; the server executes and records the result. Parent agents may help resolve ordinary questions, but cannot approve a protected mutation on the user’s behalf.

    Re-read the affected issue before execution. If its relevant content differs from the approved baseline, return a conflict for a new preview. This detects stale proposals; it is not a claim that GitHub provides atomic compare-and-swap for every mutation.

    Failure and recovery

    Case Proposed behavior
    Root provider dies Retain ownership. Show the root as interrupted; restart or change models on the same thread. Do not create a replacement root because of a timeout. Existing workers and durable final deliveries remain attached.
    T3 server restarts Recover existing runs and pending effects, rebuild admission from durable state, reconcile accepted starts by command ID, then resume eligible work. Never replay a launch just because its original response was lost.
    Root is archived/deleted while it owns work Require pause/handoff or cancellation first. Existing delivery can dispose results when the parent is archived/deleted, so allowing this silently would lose the expected wake: Orchestrator.ts:9000.
    Issue closes mid-work Stop new admissions for it, interrupt its current work through the normal task API, retain workspace/results, and request a decision about resume or closeout. Do not reopen automatically.
    Issue disappears or becomes inaccessible Mark unavailable and pause dependent work. A missing issue may be an access failure, not deletion. Preserve its link and local result.
    Issue is re-parented mid-work Pause the affected branch. Keep the old task’s lineage immutable while it finishes or cancels and delivers its final result. Start a successor under the new parent, using that final result and retained workspace. Historical attempts remain visibly historical.
    New blocker appears Pause new work; interrupt affected active work when detected. A running local process cannot be synchronized instantaneously with arbitrary GitHub edits.
    Duplicate Start clicks or agent retries Return the existing accepted operation/current owner. One issue claim and stable command IDs prevent duplicate workers.
    GitHub write fails or is rate-limited Keep the local result and durable pending effect; retry with backoff. Do not rerun the worker to reproduce a comment.
    Split partially succeeds Record each created issue ID and completed step. Resume only missing steps; do not recreate successful issues or attempt destructive rollback.
    Four slots are occupied Queue further runs. Parents finish their turns while waiting for children; all resumed work uses the same admission rule.
    Milestone has no eligible work Report whether it is complete, blocked, awaiting approval, or unreadable. Wake on relevant changes rather than repeatedly asking the model to inspect an unchanged list.

    For external changes, use a narrow server reconciliation pass registered with the existing Scheduler, following the PR reactor’s stale-read guards and failure reporting. Poll only active repository plans and relevant dependencies. The Scheduler already prevents a source from overlapping itself: Scheduler.ts:24.

    Recurring user schedules should target repository ownership dynamically, so fresh-thread handoff does not leave schedules dispatching to the retired root. Do not automatically create a persistent user schedule merely to implement milestone progress.

    Surface and provider coverage

    The full Issues page remains all-repositories; #6’s side-panel list must remain scoped to the repository from which it was opened. Starting an agent should preserve the adjacent issue view.

    Web and desktop share the main UI implementation. Mobile needs issue links, status, start controls, and approval responses through the same contracts. Shared selectors belong in packages/client-runtime. Existing mobile approval cards are tied to live provider approvals, so durable issue-change approval needs explicit client support rather than assuming the current card works unchanged: PendingApprovalCard.tsx:27.

    Use one Start action implementation for issue details, lists, command-palette access, and any keybinding. Supply reverse actions: pause/resume, unlink where allowed, cancel/retry, and visible handoff history. An active issue worker cannot silently unlink or switch issues.

    For Codex, Claude, Cursor, Grok, OpenCode, and Antigravity, the proposed route is the same app-owned MCP delegation path. Provider-native untracked spawning must not bypass issue assignment, ancestry, or the cap. In managed issue sessions, disable native delegation where supported and advertise guaranteed issue orchestration only for adapters where that restriction can be enforced.

    I did not verify native-spawn suppression for all six adapters. That is an implementation prerequisite, not an established capability. Instructions alone cannot guarantee “every subagent has one issue.”

    Remote, relay, and tunnel clients use the owning server’s credentials and services. No client executes gh, assumes a local checkout, or embeds a localhost origin.

    Five independently shippable slices

    #6 and #7 are sibling prerequisites to reuse, not work to duplicate inside #8.

    Slice Deliverable and acceptance boundary Loading requirement
    1. Single-issue work end to end Minimum repository-owner record; Start agent; model-role/workspace choice; issue argument on delegation; durable link; enforced #number title; working/waiting/done status; recoverable status and closeout comments. No automatic milestone sweep. This delivers 1, 3, and 6 early. Needs a server restart. Migrations 059/060 and contracts.
    2. Complete the client surfaces Full-page and side-panel parity; linked issue navigation; mobile start/status; command-palette entry; shared #7 indicators; clear queued/publishing/error states. Uses slice 1’s APIs. Interface-only. Reopen web/desktop. Mobile requires the corresponding client update.
    3. Milestone execution and nested ownership Complete blocker/hierarchy reads, ancestor owners, parent validation, four-slot admission across the tree, event-driven continuation, external-change reconciliation, pause/resume, and repository-targeted scheduled work. Needs a server restart.
    4. Issue upkeep with durable approval Create/split, body changes, milestone changes, close/reopen, policy settings, exact previews, conflict detection, and recovery from partially completed writes. Needs a server restart. Migration 061 plus client approval UI.
    5. Fresh-thread ownership transfer Drain-and-transfer handoff, ownership revision checks, retired-root rejection, schedule rerouting, and recovery inventory. Routine same-thread model handoff is already usable before this slice. Needs a server restart.

    The first implementation proof should be deliberately narrow: select one issue, start one properly named worker in a worktree, observe waiting and completion, see its closeout on GitHub, and retry the accepted start/writeback without producing duplicates.

    Focused later checks should cover concurrent ownership claims, nested capacity, title-refresh races, pending approvals surviving restart, stale body edits, and re-parenting without misdelivered results. No repo-wide test run is necessary to establish those behaviors.

    Risks and scope cuts

    The largest correctness risks are live lineage changes, counting capacity only at initial spawn, treating a finished turn as completed issue work, and claiming global ownership with an environment-local lock. The design above gives each an explicit boundary.

    The main operational risks are GitHub rate limits, ambiguous mutation responses, external edits, and native provider tools bypassing T3’s managed delegation. Keep synchronization narrow and capability claims honest.

    If scope must shrink, defer fresh-thread transfer first: same-thread model handoff covers the frequent use case. Next defer automatic splitting/body editing, retaining reviewed proposals; then defer recurring schedules and richer GitHub status-comment updates. Preserve the durable issue link, numbered titles, status, final closeouts, parent guard, singleton owner, and shared admission rule. Those are the behaviors that make issue-driven work dependable.

  4. tannerpolley commented on Oct 5, 2026

    @tannerpolley
    OwnerAuthor

    User decisions (2026-10-05):

    • The design direction is approved. Slice 1 (single-issue work end to end) is starting now.
    • GitHub writes allowed automatically once work starts: one coalesced T3 status comment per issue and one closeout comment per finished attempt. Bodies, labels, milestones and open/closed state need approval.
    • Default approval for issue upkeep (slice 4): agents may create and split issues and move them between milestones without asking. Body edits and close/reopen still ask, with an exact preview.
    • Mobile is out of scope for now.
  5. tannerpolley commented on Oct 6, 2026

    @tannerpolley
    OwnerAuthor

    Slice 1 (single-issue agent work) is merged into sync/upstream-v2 and ships in fork build v2.9.3 (staged; goes live on the next server restart).

    • Start agent on issue detail creates one root orchestrator per repository plus one worker per issue; repeated starts reuse the existing work. Workers are titled #N <title> and protected from automatic retitling.
    • One updating GitHub status comment per issue, plus one closeout comment per finished attempt.
    • Native Codex/Claude subagent tools are disabled in managed threads; delegation goes through T3.
    • Migrations 059 ThreadIssueWork and 060 RepositoryOrchestration.

    Review fixes (bb1e80c, 466acd8):

    • Status refresh no longer reads thread history inside the event write. While a worker is busy it reads only its runtime requests.
    • GitHub comments run in their own effect lane with bounded retries, so a failing comment can't block a provider start or interrupt.
    • A worker resumed directly still gets its closeout comment.
    • Deleting a worker publishes Unavailable.

    Not yet tried in a real client. Slices 2–5 and mobile are still open.

  6. tannerpolley commented on Oct 6, 2026

    @tannerpolley
    OwnerAuthor

    Steps 2–5 are built into fork build v2.10.0 (sync/upstream-v2 @ 4034fa3). It installs when the app next quits; database backup is taken first. Migrations 061 RepositoryScheduledWork and 062 IssueChanges.

    • Step 2 (interface): Start agent on issue detail in the full page and side panel, plus a command-palette entry; linked-issue row in thread details; worker name, live status and worktree icon on issues.
    • Step 3 (milestones): Work through milestone; native sub-issue, parent and blocked-by reads (unknown never counts as unblocked); thread tree mirrors issue tree; 4 worker slots across the repository tree, including resumes; pause/resume; reconciliation for closed, blocked, re-parented and unreadable issues; schedules that target the repository's orchestrator. Managed work runs on Codex and Claude only.
    • Step 4 (issue upkeep): propose_issue_change. Create, split and milestone moves run automatically unless Settings → Customizations → "Ask before agents create, split or move issues" is on. Body edits and close/reopen always ask, with an exact preview. Durable, conflict-checked and recoverable (no duplicate issues).
    • Step 5 (handoff): hand the main orchestrator to a fresh thread. Agents can only request it; you approve. Existing work drains first; the new root gets a recovery inventory; the retired root is rejected afterwards. Archive and delete guards.

    Each step had a read-only GPT-6.1 Sol review: 8, 4 and 6 defects respectively, all fixed with regression tests. Combined branch: 2,219 tests pass across the touched suites. The one failure is MessagesTimeline.test.tsx, which already fails to load before this work (node:sea import). Typechecks are clean.

    Not yet tried in a real client. Mobile is out of scope.

  7. tannerpolley commented on Oct 13, 2026

    @tannerpolley
    OwnerAuthor

    Checked in the fork sandbox on 2026-10-13 (in-app browser, test repo tannerpolley/t3code-sandbox).

    Works:

    Bug found and fixed (a04508d703 on sync/upstream-v2, not released yet): supportsManagedIssueWork compared the driver with "claude", but Claude's driver kind is "claudeAgent". So Start agent failed whenever the model role chose Claude, and restoring a Claude lead (the ePC-SAFT recovery) would have been rejected. The test fixture used the same wrong name. A new test is tied to the real drivers, and the Start error now includes the rejected target's reason.

    Smaller things:

    Close after the fix ships in v2.11.1.

  8. tannerpolley commented on Oct 13, 2026

    @tannerpolley
    OwnerAuthor

    Shipped in v2.11.1 (tag release/v2.11.1, now on main):

    • Managed issue work accepts the real Codex and Claude drivers.
    • Start agent, milestone orchestration and issue upkeep work.
    • A stuck repository root can be recovered: Cancel start request and Use another thread as orchestrator on the Issues page, plus the MCP tools cancel_issue_request and restore_repository_root.

    Tested in the sandbox in the in-app browser on 2026-10-13. Follow-ups have their own issues (#13 roadmap, #29 Project chips, #30).

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions