Skip to content

Owner decisions as one-click actions in the Issues and PR panes #46

Open

Description

@tannerpolley

Owner request, 2026-10-15, from the ePC-SAFT orchestrator thread. Related: #43 and #44 (Issues pane), and the issue-upkeep approval flow behind propose_issue_change.

Problem

Repository orchestrators regularly reach decisions only the owner can make:

  • adopt a change whose evidence is mixed;
  • close an issue on the agent's recommendation;
  • confirm an allowance the lead set while the owner was away.

Today those decisions live in chat or in issue comments, and there are four ways the owner can lose them:

  • The question tool (AskUserQuestion / request_user_input) shows buttons, but when the owner is away it auto-answers "choose the recommended option". That answer is not an owner decision, so the decision is lost or has to be asked again.
  • GitHub's PR Approve button can't be used: agents open PRs under the owner's account, and GitHub doesn't let authors approve their own PRs.
  • Checkboxes in an issue body change the body, and the T3 issue inbox doesn't track body edits, so the agent never hears the answer.
  • Issue-upkeep approval mode makes every agent issue edit wait for approval. It applies to routine status updates too, which is too noisy, and it doesn't cover merges.

Wanted

  1. A decision request an agent can post on an issue or PR. It carries a short question, two to four options, the agent's recommendation and its evidence link, and what happens after each choice.
  2. A Decisions view, or a filter in the Issues and PR panes, listing every open decision request across projects, with the option buttons inline. One click records the owner's choice.
  3. The choice is durable and owner-authored. It can't come from away-mode or agent answers, and it is recorded on GitHub as a comment or label for history.
  4. The requesting orchestrator is woken with the choice, through the issue inbox or a task notification, and acts on it.
  5. Optional: a badge on the thread and project when decisions are waiting, and a hover card on the decision row showing the evidence summary (see Issues pane as a modular extension of upstream's pull request pane: gather ideas, borrow its patterns #44's hover-card ideas).

Example

Waiting at the time of writing on tannerpolley/ePC-SAFT:

Interim workaround

Until this exists, the agent can tag such issues decision-needed and act on a decision:approve or decision:decline label, or a one-word comment. The inbox does track label changes and comments.

Activity

  1. tannerpolley commented on Oct 20, 2026

    @tannerpolley
    OwnerAuthor

    Orchestrator review (2026-10-20): still relevant; it starts with a design for the owner's approval.

    Nothing in the code implements decision requests yet. The question tool's away-mode problem is unchanged; that setting is now called Ask mode (#59).

    Stage 1, when this issue's queue notice arrives (owner decision 2026-10-20): its worker posts a short design here, covering:

    • how a decision request is stored and shown, in the Issues and PR panes and across projects;
    • the GitHub comment and label format that records the owner's choice;
    • how the choice wakes the requesting orchestrator, reusing the issue inbox where possible;
    • how it stays owner-authored, so away-mode or agent answers can't count as a decision.

    The owner approves the design, then the same issue gets built. Until then, the interim decision-needed / decision:approve labels keep working through the inbox.

  2. tannerpolley commented on Oct 20, 2026

    @tannerpolley
    OwnerAuthor

    T3 Code status: Done

    Issue: #46 Owner decisions as one-click actions in the Issues and PR panes

    Thread: thread:delegated-task:command%3Amcp%3A667e5306-5428-4b55-a9f6-4ae3fcb3609b%3Adelegate-task%3Apr72-fix-1

    Model: gpt-6.1-sol

    Branch: issue-46-owner-decisions-as-one-click-actions-in-4

    Updated: 2026-10-21T00:35:33.730Z

  3. tannerpolley commented on Oct 20, 2026

    @tannerpolley
    OwnerAuthor

    T3 Code closeout: done

    Issue: #46 Owner decisions as one-click actions in the Issues and PR panes
    Thread: thread:delegated-task:command%3Amcp%3A667e5306-5428-4b55-a9f6-4ae3fcb3609b%3Adelegate-task%3Aissue-46-design-1
    Model: gpt-6.1-sol
    Branch: issue-46-owner-decisions-as-one-click-actions-in
    Completed: 2026-10-20T23:59:11.156Z

    Recommendation

    Use a small server-side decision record, mirrored to GitHub comments. GitHub preserves the history; the server proves which choice came through T3’s owner interface and reliably wakes the requester.

    This design follows #46 and its comments. No product code changed, servers started, live userdata accessed, or GitHub writes made.

    1. Request and storage

    Each immutable request contains:

    • A question and 2–4 options, each with a stable identifier, label, and description of what follows that choice.
    • The recommended option, a short reason, and an evidence link.
    • The target host, repository, issue or PR identifier.
    • Requesting thread and project, derived from the authenticated tool caller.
    • A server-generated decision identifier and caller-supplied retry identifier.

    A fork migration adds one authoritative table holding that request, its GitHub comment reference, state, owner choice, authenticated owner identity, timestamps, and pending publication/delivery steps. States distinguish publishing, awaiting choice, answered, withdrawn, and failed publication.

    GitHub-only storage cannot establish owner authorship because agents share the owner’s login. This constraint already appears in IssueInboxRules.ts; durable records and delivery receipts have precedents in migration 015 and migration 006.

    2. Agent tools

    Add three MCP tools:

    • request_owner_decision: publish a request; retries return the same request.
    • list_owner_decisions: list requests and their current state within the caller’s permitted scope.
    • withdraw_owner_decision: withdraw the caller’s unanswered request, with a reason.

    There is no agent tool for choosing. Changing options requires withdrawal and a new request.

    Reuse caller identity and GitHub posting helpers from IssueInboxMcpTools.ts, without making arbitrary t3_issue_post comments into trusted requests.

    propose_issue_change remains the owner of exact issue mutations. A decision can link its preview, but answering does not approve that separate proposal or bypass its gates. Do not reuse its automatic approval policy for owner decisions. See IssueChangeService.ts.

    3. Owner interface

    Add a Decisions filter with a count in the shared IssueBrowser.tsx, covering both the Issues page and thread-side Issues pane.

    Rows show the full question, target, requesting project/thread, recommendation, evidence link, and inline option buttons. Each option exposes its consequence before selection; the recommendation is marked but never preselected. Clicking records the choice, disables competing buttons, and shows publication status.

    The all-project view collects records from connected environments, with project/repository filters and visible offline warnings. Writes return to the environment owning the record.

    A fork-owned component also appears above issue detail content, beside existing change cards. Later, reuse it in the PR list filter, thread PR pane, and PR detail panel. Reuse PullRequestListRow.tsx and PullRequestDetailLayout.tsx, following #44. Thread/project count badges and evidence hover cards are optional later additions.

    4. Durable owner choices

    Publish a readable request comment plus a versioned structured marker:

    <!-- t3-decision-request:v1 id=D42 -->

    The comment includes the complete question, options, consequences, recommendation, evidence, and requester.

    A choice appends another readable comment: “Owner chose Adopt for D42,” with the consequence, timestamp, and request link, plus:

    <!-- t3-decision-choice:v1 id=D42 option=adopt -->

    The server accepts choices only through an authenticated owner-client command, using the user/scopes checks demonstrated in ForkWsRpc.ts. Choice and publication work commit together; concurrent clicks accept one choice. Retries reconcile comment markers before reposting.

    These records never become runtime questions. Today DontAskModeWorker.ts selects the first option and accepts ordinary approvals when Ask mode is off; it cannot touch decisions.

    This enforces T3’s user/agent credential boundary. GitHub markers alone—and unrestricted access to owner credentials—cannot prove a physical human acted.

    5. Wake-up

    Reuse the inbox’s durable queued-message delivery pattern, with a stable command identifier. After the choice comment is confirmed, send:

    Owner decision D42 on owner/repo#123: Adopt. Next: [declared consequence]. Evidence: [link]. Owner record: [comment link].

    Delivery retries after restart. An unavailable requester leaves visible undelivered work; repository handoff resolves the destination.

    Do not depend on ordinary polling: IssueInboxWatcher.ts excludes PR comments, while IssueInbox.ts digests tagged activity. Keep decision responses separate from issue admission in IssueQueueCommands.ts.

    6. Labels and batches

    Keep the manual workaround. Managed requests use decision-needed; remove it only when no managed requests remain. Legacy decision:approve / decision:decline labels remain inbox signals, never verified owner receipts.

    Estimated files, including focused tests:

    • Batch A: useful Issues workflow: 20–26 files across contracts, migration/service, MCP, client state, and web. Approximately 5–8 upstream-owned integration files; feature logic stays fork-owned.
    • Batch B: PR surfaces: 6–10 files, approximately 2–4 upstream-owned hooks; reuse Batch A’s server and tools.
    • Optional polish: 4–7 files for badges and hover content.

    Follow fork.md and #24: small hooks, fork-owned modules. Web and desktop apply; mobile is outside this fork’s scope.

    7. Owner decisions before implementation

    • Storage: approve server records plus GitHub audit comments? Recommended.
    • Delivery: direct requester notification using the inbox pattern? Recommended.
    • Approval meaning: keep decision answers separate from exact issue-change approval initially? Recommended.
    • Identity guarantee: authenticated-owner boundary for v1, or stronger human verification? Recommend the existing boundary, explicitly documented.
    • Order: Issues first, then PRs; optional badges later? Recommended.

    No tests or UI verification were run for this read-only design. The identity guarantee needs explicit agreement before implementation.

  4. tannerpolley commented on Oct 21, 2026

    @tannerpolley
    OwnerAuthor

    Stage 1 design (2026-10-20). Waiting for owner approval; nothing is built. Read-only research by GPT-6.1 Sol (high) against main.

    Recommendation

    A small server-side decision record, mirrored to GitHub comments. GitHub keeps the history. The server proves the choice came through T3's owner interface, and wakes the thread that asked.

    GitHub alone can't prove the owner chose: agents post under the owner's login (see apps/server/src/issues/IssueInboxRules.ts).

    1. Request and storage

    Each request is immutable. It holds:

    • a question and 2–4 options, each with a stable id, a label and what happens after that choice;
    • the recommended option, with a short reason and an evidence link;
    • the target issue or PR;
    • the requesting thread and project, taken from the authenticated tool caller;
    • a server id, plus a retry id from the caller.

    A fork migration adds one table for the request, its GitHub comment, its state, the owner's choice and identity, timestamps, and the publish and delivery steps. The states are publishing, awaiting choice, answered, withdrawn and publication failed. The same pattern is used in migrations 015 (issue inbox) and 006 (issue changes).

    2. Agent tools

    • request_owner_decision: publishes a request. A retry returns the same request.
    • list_owner_decisions: lists requests within the caller's scope.
    • withdraw_owner_decision: withdraws the caller's unanswered request, with a reason.

    There is no agent tool for choosing. To change the options, withdraw the request and post a new one. propose_issue_change keeps owning exact issue edits: a decision may link one, but answering a decision doesn't approve it.

    3. Owner interface

    • Where: a Decisions filter with a count in the shared IssueBrowser.tsx, covering both the Issues page and the thread's Issues pane.
    • Rows: the question, the target, the requesting project and thread, the recommendation and the evidence link, with one button per option. Each option shows what happens before you click it. The recommendation is marked but never preselected. A click records the choice, disables the other buttons and shows publishing status.
    • All projects: the view gathers decisions across connected environments, with filters and offline warnings.
    • Issue detail: a fork-owned card above the issue detail, next to the existing change cards.
    • Later: the PR list, the PR pane and PR detail, reusing PullRequestListRow.tsx and PullRequestDetailLayout.tsx (see Issues pane as a modular extension of upstream's pull request pane: gather ideas, borrow its patterns #44).

    4. Durable, owner-authored choices

    • The request comment is readable and carries the marker <!-- t3-decision-request:v1 id=D42 -->.
    • The choice is a second comment, for example "Owner chose Adopt for D42", with the consequence, the time and a link to the request. It carries <!-- t3-decision-choice:v1 id=D42 option=adopt -->.
    • Owner-only: the server accepts a choice only through an authenticated owner-client command (the user and scope checks in ForkWsRpc.ts). Concurrent clicks accept one choice.
    • Never an auto-answer: decisions are never runtime questions, so away mode, which today picks the first option (DontAskModeWorker.ts), can't answer them.
    • The limit: this enforces T3's boundary between user and agent credentials. It can't prove a physical person clicked if someone has the owner's credentials.

    5. Wake-up

    The inbox's durable delivery sends the requester a message, for example "Owner decision D42 on owner/repo#123: Adopt. Next: … Evidence: … Owner record: …". It's retried after a restart. It doesn't rely on inbox polling, which skips PR comments, and it stays separate from issue admission.

    6. Labels and batches

    • Labels: managed requests add decision-needed, which is removed when none remain. decision:approve and decision:decline stay inbox signals, never verified choices.
    • Batch A, Issues workflow: about 20–26 files (contracts, migration and service, MCP, client state, web), including about 5–8 upstream-owned hook points.
    • Batch B, PR surfaces: about 6–10 files.
    • Optional: badges and hover content, about 4–7 files.

    Questions for the owner (recommendation in bold)

    1. Storage: server records plus GitHub audit comments.
    2. Delivery: a direct notification to the requester, using the inbox pattern.
    3. Approval meaning: keep decision answers separate from issue-change approval.
    4. Identity guarantee: T3's authenticated-owner boundary for v1, documented, rather than stronger human verification.
    5. Order: Issues first, then PRs, with badges optional later.
  5. tannerpolley commented on Oct 21, 2026

    @tannerpolley
    OwnerAuthor

    Approval status (2026-10-20): the question to approve this design was answered while the owner was away (Don't ask mode). Per this issue's own rule, an away-mode answer isn't an owner decision, so it doesn't count as approval.

    Batch A is being built as designed, with the recommended answers to all five questions, so the worker slot isn't idle. It won't merge until the owner approves the design in person.

  6. tannerpolley commented on Oct 21, 2026

    @tannerpolley
    OwnerAuthor

    T3 Code closeout: done

    Issue: #46 Owner decisions as one-click actions in the Issues and PR panes
    Thread: thread:delegated-task:command%3Amcp%3A667e5306-5428-4b55-a9f6-4ae3fcb3609b%3Adelegate-task%3Aissue-46-batch-a-1
    Model: gpt-6.1-sol
    Branch: issue-46-owner-decisions-as-one-click-actions-in-2
    Completed: 2026-10-21T00:20:57.337Z

    Batch A is implemented and pushed. The worktree is clean.

    • Branch: issue-46-owner-decisions-as-one-click-actions-in
    • Commit: dff447f58f — feat(issues): add durable owner decisions
    • Migration: 020_OwnerDecisions, adding the authoritative decision table and pending-work indexes.
    • MCP tools: request_owner_decision, list_owner_decisions, and withdraw_owner_decision, with presentation labels. There is no agent tool for choosing.
    • RPCs: issues.decisions.list, issues.decisions.choose, and issues.decisions.subscribe.
    • States: publishing, awaiting_choice, answered, withdrawn, and publication_failed.

    The choice RPC takes identity and scopes from the authenticated client session. The service requires a user actor and orchestration:operate; the client uses createEnvironmentRpcCommand and its permission atom. A transactional compare-and-set accepts one winning choice and commits its publication and delivery work together.

    GitHub publication reconciles request and choice markers before reposting. Confirmed choices notify the requester through a system message.dispatch using a stable command ID and queue_after_active. Failed delivery remains visible and retries after restart. Decisions never enter runtime-question storage, so Ask mode cannot answer them.

    The shared Issues browser has the Decisions filter and count, project/repository filtering, cross-environment gathering, and offline warnings. Issue details show decision cards with consequences visible before selection. Writes target the environment owning each record. The user documentation includes the identity limit.

    Validation passed:

    • Type checks for contracts, server, web, and shared client runtime.
    • Focused service, MCP core, authorization, migration, inbox, Ask-mode, and restart-delivery tests.
    • All 25 client permission tests.
    • Formatting and lint for every new feature file.

    Full changed-file lint still fails on existing errors in IssueBrowser.tsx and IssueDetailPanel.tsx. I reproduced those errors on their pre-change versions; the evidence is in the baseline lint log.

    No PR was opened. The branch is ready for orchestrator review and PR creation; merge still requires the owner’s in-person design approval. Browser verification was not run. Batch B, optional badges, and mobile remain outside this delivery.

  7. tannerpolley commented on Oct 21, 2026

    @tannerpolley
    OwnerAuthor

    T3 Code closeout: done

    Issue: #46 Owner decisions as one-click actions in the Issues and PR panes
    Thread: thread:delegated-task:command%3Amcp%3A667e5306-5428-4b55-a9f6-4ae3fcb3609b%3Adelegate-task%3Apr72-review-1
    Model: gpt-6.1-sol
    Branch: issue-46-owner-decisions-as-one-click-actions-in-3
    Completed: 2026-10-21T00:25:52.273Z

    Found three P2 defects in draft PR #72 at dff447f58f:

    1. Decision delivery ignores repository handoff. OwnerDecisions.ts:465
      If root A requests a decision, hands ownership to B, and the owner then answers, delivery still targets A. It never follows retiredToThreadId or resolves the current repository owner. An unarchived A receives the message and the record is marked delivered; an archived A leaves it retrying indefinitely. B never receives the decision. Resolve and durably record the delivery destination through the handoff chain while preserving duplicate-delivery protection.

    2. Reconciliation accepts altered or forged comments as the published owner record. OwnerDecisions.ts:365
      Reconciliation checks only whether a comment contains the marker. For example, a choice comment is posted, its response is lost, and its readable text is edited while its marker remains. The retry accepts that altered comment, marks publication complete, and wakes the requester with its URL—even if the text contradicts the recorded choice or consequence. A preexisting comment quoting the marker produces the same result. Validate the expected audit content before accepting a matching comment; surface a mismatch instead of confirming publication.

    3. One environment can remove another environment’s decision-needed label. OwnerDecisions.ts:431
      Two connected environments can each publish a request on the same GitHub issue. When the final local request in environment B is answered or withdrawn, B counts only its own database rows and deletes the shared label, although environment A still has an unanswered request. A has already cleared pending_label, so it does not restore the label. Label removal needs to account for outstanding managed requests from other environments.

    The requested checks passed: 97 server tests across eight files, all 25 client permission tests, and server/web/contracts type checks.

    Read-only review completed; the checkout remains clean. Browser verification was excluded as requested. These defects and the owner’s in-person design approval remain open gates.

  8. tannerpolley commented on Oct 21, 2026

    @tannerpolley
    OwnerAuthor

    T3 Code closeout: done

    Issue: #46 Owner decisions as one-click actions in the Issues and PR panes
    Thread: thread:delegated-task:command%3Amcp%3A667e5306-5428-4b55-a9f6-4ae3fcb3609b%3Adelegate-task%3Apr72-fix-1
    Model: gpt-6.1-sol
    Branch: issue-46-owner-decisions-as-one-click-actions-in-4
    Completed: 2026-10-21T00:35:33.182Z

    Committed and pushed afdb99c698 to issue-46-owner-decisions-as-one-click-actions-in for draft PR #72. The requested co-author trailer is present, and the worktree is clean.

    • Handoff delivery: follows retiredToThreadId chains or the repository owner lookup, then persists deliveryThreadId before dispatch. Retries retain that destination and the original command ID. The restart test verifies one persisted message despite a lost delivery receipt. Deleted or archived requesters without a successor now enter visible delivery_failed, stopping delivery retries. Migration 021_OwnerDecisionDelivery adds the stored destination.
    • Comment integrity: request, choice, and withdrawal markers now include a SHA-256 digest of the exact readable body. Reconciliation checks both the body’s digest and the expected server-generated content. Altered, quoted, or forged content enters visible publication_mismatch, with a reason. I chose terminal failure: publication and delivery work are cleared, so it cannot loop through retries or replacement posts.
    • Shared label safety: withdrawal publishes a digest-protected marker before cleanup. Before removing decision-needed, the service checks GitHub comments for unresolved requests from any environment. Altered withdrawal comments cannot close a request.

    Validation completed:

    • All eight server test files touched by the PR passed: 109 tests.
    • After the final additions, the three affected suites passed again: 44 tests, including 21 OwnerDecisions tests.
    • Server, web, and contracts type checks passed.
    • Formatting and whitespace checks passed.
    • Changed-file lint reports only five pre-existing prefer-effect-fn errors in IssueInbox.ts, reproduced on the original file. Evidence: current lint log, baseline lint log.

    The label check reads at most 100 comments; if more pages exist, it conservatively retains the label. Browser verification and repo-wide checks were not run. #46 remains open.

  9. tannerpolley commented on Oct 21, 2026

    @tannerpolley
    OwnerAuthor

    Migration numbering for the batch A draft (PR #72): #80 takes fork migration 020 (020_MarkedIssueInbox) because it merges first. Before #72 lands, renumber its 020_OwnerDecisions and 021_OwnerDecisionDelivery to 021 and 022, in the files and in ForkMigrations.ts and its test. The fork migrator skips any id below the highest one already applied, so a lower number would never run on an upgraded database.

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions