feat(ticket): expose case tags and independent assignment handling

This commit is contained in:
2026-10-12 07:33:11 +09:00
parent 56795fb92c
commit 9b0d42fdbc
79 changed files with 9326 additions and 713 deletions
@@ -45,7 +45,11 @@ Unfinished dependencies remain query/relation diagnostics and do not disable man
Action projection permits delegation independently of existing Worker occupancy; actual Worker start eligibility still excludes unfinished Worker work and closed cases. `can_start_manual_worker` no longer includes an unresolved-dependency predicate. `can_assign_orchestrator` permits an explicit fresh Assign/reissue operation even when a current row exists; callers can supply the observed expected assignment for CAS. `can_unassign_orchestrator` uses the retained current pointer, not active-work or case-state filtering. These authoring actions are not Worker transfer. Final browser controls must use these Backend decisions rather than recreating old phase/dependency gates.
Delivery diagnostics are available through the typed Store scan/reservation/ack records and structured `ticket_assignment_delivery_pending` warning (`assignment_id`, `input_event_id`, `delivery_id`, recipient and error category, never content). Existing `ticket_notification_delivery_failed` remains for recipient failures. No browser acknowledgement/status-edit endpoint is introduced: successful delivery is not a user handling update. T-743 can consume the operational query independently of handling without inventing a third status in tracking.
Delivery diagnostics are available through the typed Store scan/reservation/ack records and structured `ticket_assignment_delivery_pending` warning (`assignment_id`, `input_event_id`, `delivery_id`, recipient and error category, never content). Existing `ticket_notification_delivery_failed` remains for recipient failures. No browser acknowledgement/status-edit endpoint is introduced: successful delivery is not a user handling update. At the T-743 base `56795fb9`, `openapi/server-api.json` exposes assignment tracking and start fences but no assignment delivery query/receipt endpoint. The Store scan is an internal Rust API, not an authenticated browser/normal Tool/WIP route. Consumers must report this concrete API insufficiency for the owning Server/API handoff, not claim they can query delivery or infer it from handling/Worker state. Saved Assign plus delivery failure remains a saved delegation, never a reason to reassign. Any future diagnostics route must keep delivery, acceptance and actual start evidence separate from self-reported handling, and preserve bounded Workspace/assignment-scoped read authority.
## T-743 public handoff following the base API gap
The accepted T-743 public API adds canonical/browser `assignment-diagnostics`, `assignment-tracking/assignments` and fenced `assignment-answer` routes over the existing assignment/origin/delivery/start/receipt authority. The corresponding normal Tools/native WIP use shared typed Workspace forwarding. Diagnostics takes assignment identity and independent input/start cursors, bounds each page, and retains unknown/history distinctions; it does not expose saved rendered content or binding credentials, attest actual model processing, or mutate handling. Continuing answer atomically fences authenticated source/current assignment/observed event and saves the input/receipt, then triggers committed-input notification; it creates neither a new row nor automatic inprogress. These definitions supersede the base's unavailable public read/input surfaces, not the underlying delivery ledger or T-748 transaction ownership. See [assignment tracking](ticket-assignment-tracking.md) for ordinary Tools/WIP names and CAS/retry contracts. Combined route/generated/runtime validation is separately required; source definitions alone do not prove environment rollout.
## DB caller/transaction handoff for T-748 and protocol boundaries
@@ -30,7 +30,9 @@ Canonical Workspace endpoints:
- `POST` on the same path: self-report update with event CAS and exact retry key.
- `GET .../assignment-tracking/history`: assignment-scoped handling audit, with `assignment_id`, optional `after_event_id`, and `limit` (1–100). The returned `next_event_id` continues that history.
Normal tools and route-bound WIP operations expose `TicketAssignmentTrackingQuery`, `TicketAssignmentTrackingHistory`, and `TicketAssignmentTrackingUpdate`. Ticket query/detail and model projections include current assignment tracking. Read access does not grant mutation authority; Backend checks every update.
Normal tools and route-bound WIP operations expose `TicketAssignmentTrackingQuery`, `TicketAssignmentTrackingHistory`, and `TicketAssignmentTrackingUpdate`. T-743's public handoff adds `TicketAssignmentsQuery` for bounded retained Orchestrator rows (`GET .../assignment-tracking/assignments`, `after_assignment_id`, limit 1–100, `next_assignment_id`), `TicketAssignmentDiagnostics` for sanitized independent delivery/start pages (`GET .../assignment-diagnostics`, assignment ID, separate input/start cursors), and `TicketAssignmentAnswer` (`POST .../assignment-answer`). Answer uses the existing authenticated operation receipts with assignment/event CAS and bounded body; it continues waiting/inprogress on the same row and does not change handling. Done requires explicit reissue instead.
`TicketOrchestratorAssign` / `TicketOrchestratorClear` use the existing role-assignment routes with a target principal, observed assignment identity and stable `operation_id`; explicit nullable `expected_assignment_id` is mandatory in Assign Tool/WIP input. These tools target responsibility, not Worker execution/lifecycle, and never derive the actor from model role text. Query first; retry exact full input with the original identity, never automatically generate a fresh key on stale/unknown outcome. Typed forwarding is shared `WorkspaceClient::execute_server_operation`; it does not introduce another ledger or direct HTTP/storage fallback. Read-only/review access includes retained rows and diagnostics, not Assign/clear/answer/self-report mutation. Ticket query/detail and model projections include current assignment tracking; `QueryTicket` also exposes exact current `assigned_principal` and self-reported `handling_status` filters. Read access does not grant mutation authority; Backend checks every update.
Read a fresh projection before a new update. Do not treat a replayed receipt's last-event identity as the latest observed value. Retained-assignment query exposes Assign attribution and release/replacement independently; the history endpoint pages initial/migrated handling and subsequent self-reports, not delivery acknowledgements.
+14 -6
View File
@@ -6,7 +6,9 @@ Workspace Server control-plane records, append-only Ticket events, and any linke
## Concepts
- `Ticket`: durable work record containing intent, requirements, decisions, useful results/evidence, and resolution history.
- `Ticket`: durable work record containing intent, requirements, decisions, useful results/evidence, and immutable closure history. Its case state is only `open` or `closed`; open is not readiness, and closed is not necessarily fulfillment.
- `Classification tag`: Workspace-owned stable identity with a mutable display name. Multiple tags classify/filter Tickets; they never substitute for closure reasons, delegation or start authority.
- `Orchestrator handling`: self-reported `waiting` / `inprogress` / `done` on the current assignment row, with waiting reason, progress, next action and result. These are not case states, Worker states, MR judgments or delivery status.
- `Objective`: a first-class medium-term goal with background, strategy, success criteria, and canonical Ticket context links. It neither schedules nor authorizes Ticket work.
- `Task`: session-local progress tracking, not the project record.
- `Assignment`: an authorized delegation of Ticket work to a Worker, independent from its Profile or Flow.
@@ -25,13 +27,17 @@ The Server resolves Workspace access, active configuration, and immutable Profil
`SpawnTicketWorker` starts and atomically assigns a generic Worker with an `initial_request`. Its registered `profile` is optional (omitted/null selects `builtin:ticket-worker`); `flow` and alias-keyed `workdir_attachments` are optional. No repository or Flow is required for non-code work. Choose an explicit Profile when its tools and instructions fit the task; assignment does not depend on choosing the Coder role. Backend validates claims, derives repository attachment access from declared Ticket targets, and preserves explicit ExternalGrant ceilings. Callers cannot request capabilities through attachment selection.
The guarded start records acceptance only after spawn, initial input, assignment, and attachment finalization. Do not pre-write `inprogress` or mistake a state update for a launch. On a failed or unknown outcome, reread durable operation evidence before retrying or spawning a replacement. After acceptance, reread Ticket state and current Worker responsibility.
Orchestrator Assign delegates handling and persists initial waiting with the assignment/audit/receipt; no separate ready/Queue operation is required. The stable `workspace_agent:workspace-orchestrator` principal is distinct from the concrete registered Worker. Answers during waiting/ongoing work continue the same assignment; status is explicitly self-reported, never automatically set on delivery or acceptance. An explicit reissue after done creates a new assignment row even for the same principal, preserving prior result/release/replacement history. Reopen and Worker exchange alone create neither a fresh delegation nor execution rights. See [assignment tracking](ticket-assignment-tracking.md) and [delivery/start fences](ticket-assignment-delivery.md).
The guarded start records acceptance only after spawn, initial input, assignment, and attachment finalization. Current Orchestrator starts (including generic Worker spawn and existing-Worker assignment) must carry the observed `orchestrator_assignment_id`. Do not pre-write handling `inprogress` or mistake a case/status update for a launch. On a failed or unknown outcome, reread durable operation evidence before retrying the same operation; never silently spawn a replacement. After acceptance, reread current Worker responsibility and authoritative unfinished work, not merely `current_worker`. Unfinished dependencies are diagnostics for partial work/waiting, not a blanket start refusal, and are not implicitly started; actual authentication, case closure, resource/claim and recovery guards still apply.
Results can be returned in conversation, optionally recorded in a `TicketComment`, optionally published as a Drive document/artifact when that surface is available, or written/published to an authorized repository. Neither comments nor Drive artifacts are universally required. Preserve concise evidence useful to the user without fabricating work, tests, or conclusions. `implementation_report` is optional historical/audit context, not integration readiness or completion authority.
The return choice is independent of result format. Examples:
- Research with review not required: answer the question and optionally record a useful summary; no MR or approval is required for an authorized completion decision.
- Research returned to the human: record handling done with a bounded result; leave the case open when no closure was requested. No MR or universal approval is required.
- Explicit user confirmation before continuing: record waiting with the specific reason and next action. An answer continues the same assignment; receipt alone is not an inprogress self-report.
- Whole request fulfilled and closure requested: separately record handling and a reasoned case close with `completed`. Other closure reasons (not planned, duplicate, unknown, or custom) do not claim fulfillment.
- Code requiring independent review before merge: use the trusted review path and guarded MR integration. A Profile, Flow transition, tool invocation, or prose approval is not the review judgment.
- Return without conclusion: report the result/limitations and leave the Ticket unconcluded. A finished turn, an artifact, or an approved MR does not itself decide completion.
@@ -41,9 +47,11 @@ These are instruction/tool contracts, not a claim that a mechanism proves real a
`QueryTicket` provides bounded discovery/filtering; `ShowTicket` provides authoritative item content and `content_digest`, paged thread, relations, Objectives, and any current MR context. Read the relevant Ticket before implementation, routing, review, state, or conclusion decisions. Check potential duplicates before creation or material rescope. Use `QueryObjective` and `ShowObjective` for broader context without replacing the Ticket read.
Useful mutation surfaces include `TicketCreate`, `TicketComment`, relation/plan tools, and authorized `TicketWorkflowState`, `CompleteTicket`, and `TicketClose` decisions. Exposure depends on the effective Profile/Feature configuration. Decision tools use current `content_digest` as `expected_content_digest`, expected state, a reason/resolution, stable operation key, and optional supporting references. Backend checks actor authority, CAS, replay, and resource boundaries. It does not enforce a universal state sequence, MR/approval gate, or natural-language satisfaction.
Useful mutation surfaces include `TicketCreate`, `TicketComment`, classification and relation/plan tools, `TicketOrchestratorAssign` (explicit delegation or reissue), `TicketOrchestratorClear`, fenced `TicketAssignmentAnswer`, `TicketAssignmentTrackingUpdate`, and `TicketCaseUpdate`. Read current tracking first; use `TicketAssignmentsQuery` for bounded retained rows and `TicketAssignmentDiagnostics` for sanitized separate input/start evidence. `QueryTicket` supports exact current principal and self-reported handling filters. Exposure depends on the effective Profile/Feature configuration and does not grant authority. Ready, Queue, six-state transitions and `CompleteTicket`/`TicketClose` are not current Worker operations; old events remain historical evidence.
`CompleteTicket` records completion; `TicketClose` records closure. Neither integrates an MR or expands resource grants. Ticket progress/conclusion can occur without commits, MRs, or approvals when consistent with the request. Conversely, code's default publication/review practices must not be erased by confusing an independent Ticket decision with merge authority. Do not claim an unmerged result was integrated. Reread after conflicts and replay a recorded operation only with the same fingerprint.
Assign/reissue uses target principal, explicitly observed nullable `expected_assignment_id`, and stable `operation_id`; clear uses the observed assignment_id and operation_id. These role operations do not start/stop Workers. Answers use the same current assignment/event CAS and stable operation_key with bounded body, never an unfenced comment to manufacture continuation. Case judgments use current `content_digest` as `expected_content_digest`, expected open/closed state, explicitly observed nullable `closure_operation_key`, typed close/reopen/correct-closure action, explanation, stable operation key and optional references. Tracking updates use immutable `assignment_id`, observed `last_event_id` as `expected_last_event_id`, stable `operation_key` and the complete handling payload. Tag rename/delete mutations use stable tag identity and observed `expected_last_operation_key`; Ticket tag-set updates compare the observed full `expected_tag_ids` set and send the complete desired `tag_ids` set, never a display name or status-only comparison. Exact retry keeps the same key and entire input; changed input under that key is rejected. Replayed receipts are not fresh observations. After conflict, reread and reconcile, never silently overwrite or retry as a new operation against another assignment. Unknown dispatch outcomes are not success or failure. Saved Assign plus failed notification is not failed delegation and must not prompt another Assign. Do not infer missing delivery/acceptance diagnostics from Worker or handling state; report the concrete unavailable endpoint.
`TicketCaseUpdate` records an authenticated close, reopen or correction judgment with immutable reason/actor/history. It neither integrates an MR nor expands grants nor changes handling status. Handling done and case closed do not imply one another; done does not require unassignment. Reopen alone is not reissue or old Worker revival. Generic closure can occur without commits, MRs or approvals when consistent with the request, while explicit user approval conditions must be respected. Backend checks actor authority, CAS, replay and resource boundaries, not natural-language satisfaction. Code's guarded MR integration remains separate: do not claim an unmerged result was integrated.
Relations remain metadata, not a scheduling mechanism: record actual project dependencies, but do not encode capacity, Worker/worktree ownership, or execution ordering as relations. Use orchestration/session records for those concerns and inspect current Backend claim rules rather than inferring launch eligibility from a relation alone.
@@ -80,7 +88,7 @@ Intake clarifies ambiguous intent, checks duplicates and relevant context, and r
A useful Ticket records background/motivation, requirements, acceptance criteria, binding decisions/invariants, implementation latitude, and relevant open questions/risks. Include useful result evidence and final resolution when appropriate. Separate confirmed facts, user claims, hypotheses, and undecided questions. Do not copy Worker role boundaries, a temporary plan, or a decision to stop after authoring into the Ticket as a product prohibition. Record an approval gate only when the user asks for it; do not prematurely prescribe implementation tactics.
Risk flags prompt context checks and reviewer focus, not automatic stop gates. Return to planning only for a named missing decision/information after bounded checks; escalate beyond-scope decisions without inventing new constraints.
Risk flags prompt context checks and reviewer focus, not automatic stop gates. Record a waiting reason and next action only for a named missing decision/information after bounded checks; escalate beyond-scope decisions without inventing new constraints or moving the case to a planning state.
Do not create new umbrella/progress-container Tickets. Split broad work into concrete useful outcomes, record the split in Ticket/Objective context, and use Objectives for enduring medium-term goals. Do not replace umbrellas with parent/child, sub-ticket, part-of, contains, or other hierarchy relations. A concrete planning/design/investigation Ticket is valid when requested. Existing umbrellas can be retired as superseded/decomposed without rewriting history; distinguish container retirement from completion of future work.