fix: align ticket flat-state surfaces

This commit is contained in:
Keisuke Hirata 2026-06-09 13:27:02 +09:00
parent 21114fdd6f
commit 8fe4b822ee
No known key found for this signature in database
4 changed files with 21 additions and 52 deletions

View File

@ -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 が必要か。

View File

@ -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
## 完了条件

View File

@ -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());
}
}

View File

@ -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/<ticket-id>/` 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 <id>
yoi ticket comment <id> [--role comment|plan|decision|implementation_report] [--file path]
yoi ticket review <id> --approve|--request-changes [--file path]
yoi ticket state <id> open|pending
yoi ticket state <id> <planning|ready|queued|inprogress|done>
yoi ticket close <id> [--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: