diff --git a/.yoi/workflow/ticket-intake-workflow.md b/.yoi/workflow/ticket-intake-workflow.md index 78c89766..4febbdbc 100644 --- a/.yoi/workflow/ticket-intake-workflow.md +++ b/.yoi/workflow/ticket-intake-workflow.md @@ -70,7 +70,7 @@ Intake は以下を行う。 - `TicketComment`: 既存 Ticket refinement / decision / plan の記録。 - `TicketDoctor`: 必要に応じた整合性確認。 -Intake は `TicketReview`, `TicketStatus`, `TicketClose` を通常使わない。review / state transition / close は Orchestrator または reviewer / maintainer workflow の責務である。 +Intake は `TicketReview`, `TicketWorkflowState`, `TicketClose` を通常使わない。review / state transition / close は Orchestrator または reviewer / maintainer workflow の責務である。 Ticket tools が利用できない環境では、勝手に file write で代替しない。ユーザーまたは Orchestrator に「Ticket tools がないため materialize できない」と報告し、必要なら `yoi ticket` を使える人間/親 workflow に戻す。 @@ -94,7 +94,7 @@ Ticket tools が利用できない環境では、勝手に file write で代替 確認観点: -- 同じ目的の open / pending Ticket がないか。 +- 同じ目的の未完了 Ticket がないか。 - closed Ticket の判断・resolution と矛盾しないか。 - 既存の umbrella/progress-container Ticket が、superseded/decomposed として退役できる状態か。 - 既存 concrete follow-up Ticket や Objective context で足りるか、新規 concrete Ticket が必要か。 diff --git a/.yoi/workflow/ticket-orchestrator-routing.md b/.yoi/workflow/ticket-orchestrator-routing.md index 124220f2..cc9df1c3 100644 --- a/.yoi/workflow/ticket-orchestrator-routing.md +++ b/.yoi/workflow/ticket-orchestrator-routing.md @@ -21,7 +21,7 @@ Panel Queue / queued notification は、人間が Orchestrator に routing を ```text TicketCreate / TicketComment -> Ticket Orchestrator Routing Workflow - -> planning return / requirements sync / spike / implementation / review / blocked / close / pending + -> planning return / requirements sync / spike / implementation / review / blocked / close -> 必要に応じて他 Workflow へ接続 ``` @@ -71,7 +71,6 @@ Orchestrator は以下を行う。 - `TicketList`: routing 候補や関連 Ticket の確認。 - `TicketShow`: 対象 Ticket の body / thread / artifacts / resolution 確認。 - `TicketComment`: routing decision / intent packet / blocked reason / next question の記録。 -- `TicketStatus`: pending/open などの状態整理が明示的に許可された場合だけ使う。 - `TicketWorkflowState`: `queued -> inprogress` acceptance、`inprogress -> done`、または concrete missing decision/information reason を伴う `ready|queued -> planning` に使う。 - `TicketOrchestrationPlanQuery`: 対象 Ticket や関連 Ticket の ordering / blocker / conflict / waiting-capacity / accepted-plan 記録を読む。queued acceptance 前に必ず確認する。 - `TicketOrchestrationPlanRecord`: Orchestrator が routing 中に project-relevant な ordering / dependency / conflict / capacity/waiting / accepted-plan decision を残す。これは queue reorder、自動起動、state 変更ではない。 @@ -88,7 +87,7 @@ Orchestrator は以下を行う。 - `before` / `after` / `blocked_by` / `blocks` / `conflicts_with` / `do_not_parallelize` / waiting-capacity 記録がある場合、それを acceptance 判断の入力にする。記録は自動 scheduler ではないため、実際に進めるかどうかは Orchestrator が読んだうえで明示的に決める。 - risk flags / risky domain がある場合は、IntentPacket に invariants / reviewer focus / escalation conditions を入れる。risk flag だけを `queued -> planning` の理由にしない。 - concrete missing decision / information がある場合: `TicketWorkflowState` で `queued -> planning` を記録し、reason/body と `TicketComment` に不足項目、checked context、なぜ coder の implementation latitude では解決できないか、次の planning question/action を残す。既存の claimed live/restorable Intake/Planning Pod があり、既存通知経路が使える場合は同じ理由を通知する。 -- external action 待ちなど planning では解決しない blocker の場合: concise な理由を Ticket thread に記録し、queued のまま待つか、既存の Ticket state/state mechanism で明示的に defer/block する。 +- external action 待ちなど planning では解決しない blocker の場合: concise な理由を Ticket thread に記録し、必要に応じて attention / action-required frontmatter や `TicketOrchestrationPlanRecord` の blocker/waiting-capacity 記録で明示する。lifecycle 外の storage bucket を routing target にしない。 Invariant: @@ -214,7 +213,7 @@ Action: - 必要な判断・外部 action を短く書く。 - `TicketComment` に blocked reason と next question を記録する。 -- 必要なら `TicketStatus` で pending に移す(許可がある場合だけ)。 +- 必要に応じて attention / action-required frontmatter や orchestration plan の blocker/waiting-capacity 記録で、待ち理由を current state とは別に表す。lifecycle 外の storage bucket へ移す route は使わない。 ### `close_ready` @@ -234,21 +233,6 @@ Action: - umbrella/progress-container Ticket を退役する close resolution では、関連作業がすべて完了したという意味ではなく container role を retired したことを明記し、完了済み concrete Ticket と残る follow-up Ticket / Objective を列挙する。 - close 権限がない場合は merge-ready / close-ready dossier を親/人間に提出する。 -### `defer_pending` - -今は進めないが blocked ではない。 - -条件: - -- 優先度・タイミングの理由で後回し。 -- 依存はあるが active blocker として扱うほどではない。 -- broad request が concrete implementable Ticket に分解され、Objective context や split decision record の作成待ちである。 - -Action: - -- defer reason を `TicketComment` に記録する。 -- 必要なら `TicketStatus` で pending に移す(許可がある場合だけ)。 - ### `closed_or_noop` routing 不要。 @@ -395,7 +379,6 @@ IntentPacket が短く書けない場合、`implementation_ready` ではなく ` - `review_needed` → reviewer Pod / review workflow - `blocked_action_required` → human / parent Orchestrator - `close_ready` → close workflow / maintainer decision -- `defer_pending` → pending / Objective or split-decision follow-up management ## 完了条件 diff --git a/crates/pod/src/feature/builtin/ticket.rs b/crates/pod/src/feature/builtin/ticket.rs index c36fe1f8..7e231650 100644 --- a/crates/pod/src/feature/builtin/ticket.rs +++ b/crates/pod/src/feature/builtin/ticket.rs @@ -110,14 +110,6 @@ impl TicketFeature { if !root.is_dir() { return Err("ticket backend root is not a directory".to_string()); } - for state_dir in ["open", "pending", "closed"] { - let dir = root.join(state_dir); - if !dir.is_dir() { - return Err(format!( - "ticket backend root is missing required {state_dir}/ directory" - )); - } - } Ok(root) } } @@ -194,9 +186,6 @@ fn tool_description(name: &str) -> &'static str { "TicketWorkflowState" => { "Transition Ticket state; queued -> inprogress is the accepted implementation start, so implementation side effects should happen only after that transition is accepted and recorded." } - "TicketStateCompat" => { - "Move a Ticket between open and pending; use TicketClose for closed." - } "TicketClose" => "Close a Ticket with a resolution through the typed local Ticket backend.", "TicketOrchestrationPlanRecord" => { "Append a durable typed Ticket orchestration plan record without changing state or starting work." @@ -228,9 +217,7 @@ mod tests { use tempfile::TempDir; fn make_ticket_root(root: &Path) { - std::fs::create_dir_all(root.join("open")).unwrap(); - std::fs::create_dir_all(root.join("pending")).unwrap(); - std::fs::create_dir_all(root.join("closed")).unwrap(); + std::fs::create_dir_all(root).unwrap(); } fn write_ticket_config(workspace: &Path, content: &str) { @@ -469,21 +456,21 @@ provider = "github" } #[test] - fn does_not_register_ticket_tools_when_root_lacks_state_dirs() { + fn registers_ticket_tools_for_flat_backend_root() { let temp = TempDir::new().unwrap(); - std::fs::create_dir_all(temp.path().join(DEFAULT_TICKET_BACKEND_RELATIVE_PATH)).unwrap(); + let root = temp.path().join(DEFAULT_TICKET_BACKEND_RELATIVE_PATH); + std::fs::create_dir_all(&root).unwrap(); let mut pending_tools = Vec::new(); let mut hooks = HookRegistryBuilder::default(); let report = FeatureRegistryBuilder::new() .with_module(ticket_tools_feature(temp.path())) .install_into_pending(&mut pending_tools, &mut hooks); - assert!(pending_tools.is_empty()); - assert!(report.reports[0].installed_tools.is_empty()); - assert!( - report.reports[0].diagnostics[0] - .message - .contains("missing required open/ directory") - ); + assert_eq!(pending_tools.len(), TICKET_TOOL_NAMES.len()); + assert_eq!(report.reports[0].installed_tools, TICKET_TOOL_NAMES); + assert!(report.reports[0].diagnostics.is_empty()); + assert!(!root.join("open").exists()); + assert!(!root.join("pending").exists()); + assert!(!root.join("closed").exists()); } } diff --git a/docs/development/work-items.md b/docs/development/work-items.md index 2e46ba5a..6632a131 100644 --- a/docs/development/work-items.md +++ b/docs/development/work-items.md @@ -2,7 +2,7 @@ Yoi project work is tracked through Tickets. For normal use, interact with Tickets through `yoi panel`, Ticket tools, the `yoi ticket ...` CLI, and Ticket workflows. Git history plus Ticket files remain the authoritative state-transition record behind those interfaces. -The current local backend stores Ticket files under `.yoi/tickets/`. That storage detail matters for maintainers and backend compatibility, but it is not the primary user-facing workflow. +The current local backend stores each Ticket in the flat `.yoi/tickets//` layout. The directory name is the canonical opaque Ticket id; slugs and frontmatter `id`/`slug` fields are not current-state authority. That storage detail matters for maintainers and backend compatibility, but it is not the primary user-facing workflow. Do not treat ad-hoc chat summaries, memory records, or Pod notifications as the final source of project state. Notifications are hints to inspect concrete state, not proof of completion. @@ -36,7 +36,7 @@ Pods with the Ticket built-in feature can use typed Ticket tools: - `TicketShow` - `TicketComment` - `TicketReview` -- `TicketStatus` +- `TicketWorkflowState` - `TicketClose` - `TicketDoctor` @@ -156,14 +156,13 @@ Routing classifications include: - `review_needed` - `blocked_action_required` - `close_ready` -- `defer_pending` - `closed_or_noop` Routing decisions should be recorded with `TicketComment` using `plan` or `decision` role. The decision should state the classification, evidence checked, reason, next action, and escalation conditions. For `return_to_planning`, the record must also state the concrete missing decision/information, context checked, why implementation latitude is insufficient, and the next planning question/action. ### 3. Planning/requirements sync -Use `ticket-preflight-workflow` only as a legacy compatibility canonical id for planning/requirements sync. Return `ready` or `queued` Tickets to `planning` only when the Orchestrator can name a concrete missing decision or information item after bounded project-context checks; risk flags and risky domains are context-lookup and reviewer-focus signals, not automatic stop gates. +Use `ticket-preflight-workflow` only as a legacy-compatible planning/requirements sync entry. Return `ready` or `queued` Tickets to `planning` only when the Orchestrator can name a concrete missing decision or information item after bounded project-context checks; risk flags and risky domains are context-lookup and reviewer-focus signals, not automatic stop gates. Planning sync should resolve or record: @@ -333,17 +332,17 @@ Do not store secrets, credentials, private prompt contents, or raw logs containi The product CLI exposes the typed Ticket backend for repository maintenance and validation. It operates on the configured `.yoi/tickets/` storage and is the preferred command-line surface when editing Tickets outside a Pod. ```sh -yoi ticket create --title "..." [--canonical id canonical id] [--kind task] [--priority P2] [--label a,b] +yoi ticket create --title "..." [--priority P2] yoi ticket list [--state planning|ready|queued|inprogress|done|closed|all] yoi ticket show yoi ticket comment [--role comment|plan|decision|implementation_report] [--file path] yoi ticket review --approve|--request-changes [--file path] -yoi ticket state open|pending +yoi ticket state yoi ticket close [--resolution text|--file path] yoi ticket doctor ``` -`yoi ticket state` intentionally does not close Tickets. Closing must use `yoi ticket close` so the backend writes the required `resolution.md` and passes `yoi ticket doctor`. +`yoi ticket state` records current lifecycle transitions among active states. Closing must use `yoi ticket close` so the backend writes the required `resolution.md` and passes `yoi ticket doctor`; `done` and `closed` remain distinct states. The current LocalTicketBackend stores records under: