merge: remove fixed investigator ticket role

This commit is contained in:
Keisuke Hirata 2026-06-08 20:54:46 +09:00
commit b552372d76
No known key found for this signature in database
6 changed files with 57 additions and 34 deletions

View File

@ -22,7 +22,7 @@ User request / conversation
- `Ticket` は durable orchestration record。 - `Ticket` は durable orchestration record。
- `Task` は session-local progress tracking。 - `Task` は session-local progress tracking。
- `Assignment` は Orchestrator から coder / reviewer / investigator Pod への具体的委譲。 - `Assignment` は Orchestrator から coder / reviewer Pod、または task-specific helper Pod への具体的委譲。
- `IntentPacket` は Ticket から抽出して Assignment に渡す短い実装・レビュー契約。 - `IntentPacket` は Ticket から抽出して Assignment に渡す短い実装・レビュー契約。
Intake は、要件同期と Ticket 化を担当する。実装の起動・worktree 作成・review 委譲・merge 判断は Orchestrator 側の責務である。`ready` は Orchestrator が routing できる状態を意味し、実装戦術がすべて事前固定されていることを意味しない。 Intake は、要件同期と Ticket 化を担当する。実装の起動・worktree 作成・review 委譲・merge 判断は Orchestrator 側の責務である。`ready` は Orchestrator が routing できる状態を意味し、実装戦術がすべて事前固定されていることを意味しない。
@ -47,7 +47,7 @@ Intake は以下を行う。
## Intake がしないこと ## Intake がしないこと
- coder / reviewer / investigator Pod を起動しない。 - coder / reviewer Pod や read-only investigation helper Pod を起動しない。
- worktree を作らない。 - worktree を作らない。
- merge / close / branch cleanup をしない。 - merge / close / branch cleanup をしない。
- implementation-ready でない Ticket を実装に投げない。 - implementation-ready でない Ticket を実装に投げない。

View File

@ -50,7 +50,7 @@ Orchestrator は以下を行う。
- 自動 scheduler として unattended に実行しない。 - 自動 scheduler として unattended に実行しない。
- Panel Queue / queued notification だけを unattended scheduler trigger として扱わない。 - Panel Queue / queued notification だけを unattended scheduler trigger として扱わない。
- `queued -> inprogress` acceptance なしに worktree 作成、implementation Pod `SpawnPod`、coder/reviewer routing を行わない。 - `queued -> inprogress` acceptance なしに worktree 作成、implementation Pod `SpawnPod`、coder/reviewer routing を行わない。
- 人間/上位 Orchestrator の許可または明示的な routing acceptance なしに coder / reviewer / investigator Pod を起動しない。 - 人間/上位 Orchestrator の許可または明示的な routing acceptance なしに coder / reviewer Pod や read-only investigation helper Pod を起動しない。
- 設計境界の未決定を勝手に implementation-ready として固定しない。 - 設計境界の未決定を勝手に implementation-ready として固定しない。
- merge / close / cleanup 権限を持たない場面で勝手に完了処理しない。 - merge / close / cleanup 権限を持たない場面で勝手に完了処理しない。
- Ticket tools があるからといって arbitrary filesystem write を行わない。 - Ticket tools があるからといって arbitrary filesystem write を行わない。
@ -139,7 +139,7 @@ Action:
Action: Action:
- read-only investigation を提案する。 - read-only investigation を提案する。
- 許可があれば investigator Pod を read-only scope で起動できる。 - 許可があれば task-specific read-only helper Pod を普通の scoped Pod として起動できる。
- `TicketComment` に調査問い・scope・完了条件を記録する。 - `TicketComment` に調査問い・scope・完了条件を記録する。
- 実装 worktree はまだ作らない。 - 実装 worktree はまだ作らない。

View File

@ -553,7 +553,7 @@ fn append_role_execution_guidance(out: &mut String, role: TicketRole) {
TicketRole::Orchestrator => append_orchestrator_agent_routing_guidance(out), TicketRole::Orchestrator => append_orchestrator_agent_routing_guidance(out),
TicketRole::Coder => append_coder_agent_routing_guidance(out), TicketRole::Coder => append_coder_agent_routing_guidance(out),
TicketRole::Reviewer => append_reviewer_agent_routing_guidance(out), TicketRole::Reviewer => append_reviewer_agent_routing_guidance(out),
TicketRole::Intake | TicketRole::Investigator => {} TicketRole::Intake => {}
} }
} }

View File

@ -194,25 +194,26 @@ pub enum TicketRole {
Orchestrator, Orchestrator,
Coder, Coder,
Reviewer, Reviewer,
Investigator,
} }
impl TicketRole { impl TicketRole {
pub const ALL: [TicketRole; 5] = [ pub const ALL: [TicketRole; 4] = [
TicketRole::Intake, TicketRole::Intake,
TicketRole::Orchestrator, TicketRole::Orchestrator,
TicketRole::Coder, TicketRole::Coder,
TicketRole::Reviewer, TicketRole::Reviewer,
TicketRole::Investigator,
]; ];
pub fn supported_names() -> Vec<&'static str> {
Self::ALL.iter().map(|role| role.as_str()).collect()
}
pub fn as_str(self) -> &'static str { pub fn as_str(self) -> &'static str {
match self { match self {
Self::Intake => "intake", Self::Intake => "intake",
Self::Orchestrator => "orchestrator", Self::Orchestrator => "orchestrator",
Self::Coder => "coder", Self::Coder => "coder",
Self::Reviewer => "reviewer", Self::Reviewer => "reviewer",
Self::Investigator => "investigator",
} }
} }
@ -222,7 +223,6 @@ impl TicketRole {
"orchestrator" => Some(Self::Orchestrator), "orchestrator" => Some(Self::Orchestrator),
"coder" => Some(Self::Coder), "coder" => Some(Self::Coder),
"reviewer" => Some(Self::Reviewer), "reviewer" => Some(Self::Reviewer),
"investigator" => Some(Self::Investigator),
_ => None, _ => None,
} }
} }
@ -232,7 +232,6 @@ impl TicketRole {
Self::Intake => "ticket-intake-workflow", Self::Intake => "ticket-intake-workflow",
Self::Orchestrator => "ticket-orchestrator-routing", Self::Orchestrator => "ticket-orchestrator-routing",
Self::Coder | Self::Reviewer => "multi-agent-workflow", Self::Coder | Self::Reviewer => "multi-agent-workflow",
Self::Investigator => "ticket-orchestrator-routing",
} }
} }
} }
@ -550,7 +549,10 @@ impl RawTicketConfig {
for (name, raw_role) in self.roles { for (name, raw_role) in self.roles {
let role = TicketRole::parse(&name).ok_or_else(|| TicketConfigError::Invalid { let role = TicketRole::parse(&name).ok_or_else(|| TicketConfigError::Invalid {
path: path.to_path_buf(), path: path.to_path_buf(),
message: format!("unknown Ticket role `{name}`"), message: format!(
"unsupported Ticket role `{name}`; supported fixed roles: {}",
TicketRole::supported_names().join(", ")
),
})?; })?;
let profile_configured = raw_role.profile.is_some(); let profile_configured = raw_role.profile.is_some();
roles.inner.insert(role, raw_role.resolve(role)); roles.inner.insert(role, raw_role.resolve(role));
@ -711,11 +713,6 @@ workflow = "multi-agent-workflow"
profile = "project:reviewer" profile = "project:reviewer"
launch_prompt = "$workspace/ticket/reviewer/launch" launch_prompt = "$workspace/ticket/reviewer/launch"
workflow = "multi-agent-workflow" workflow = "multi-agent-workflow"
[roles.investigator]
profile = "default"
launch_prompt = "$workspace/ticket/investigator/launch"
workflow = "ticket-orchestrator-routing"
"#, "#,
); );
@ -738,8 +735,8 @@ workflow = "ticket-orchestrator-routing"
"$workspace/ticket/reviewer/launch" "$workspace/ticket/reviewer/launch"
); );
assert_eq!( assert_eq!(
config.workflow_for(TicketRole::Investigator).as_str(), config.workflow_for(TicketRole::Reviewer).as_str(),
"ticket-orchestrator-routing" "multi-agent-workflow"
); );
} }
@ -759,6 +756,7 @@ workflow = "ticket-orchestrator-routing"
role.default_workflow() role.default_workflow()
))); )));
} }
assert!(!scaffold.contains("[roles.investigator]"));
let config = TicketConfig::from_toml( let config = TicketConfig::from_toml(
temp.path(), temp.path(),
@ -899,7 +897,41 @@ profile = "inherit"
); );
let error = TicketConfig::load_workspace(temp.path()).unwrap_err(); let error = TicketConfig::load_workspace(temp.path()).unwrap_err();
assert!(error.to_string().contains("unknown Ticket role `operator`")); assert!(
error
.to_string()
.contains("unsupported Ticket role `operator`")
);
assert!(
error
.to_string()
.contains("intake, orchestrator, coder, reviewer")
);
}
#[test]
fn stale_investigator_role_config_is_rejected() {
let temp = TempDir::new().unwrap();
write_config(
temp.path(),
r#"
[roles.investigator]
profile = "builtin:default"
workflow = "ticket-orchestrator-routing"
"#,
);
let error = TicketConfig::load_workspace(temp.path()).unwrap_err();
assert!(
error
.to_string()
.contains("unsupported Ticket role `investigator`")
);
assert!(
error
.to_string()
.contains("intake, orchestrator, coder, reviewer")
);
} }
#[test] #[test]

View File

@ -849,6 +849,7 @@ mod tests {
role.default_workflow() role.default_workflow()
))); )));
} }
assert!(!config.contains("[roles.investigator]"));
} }
#[test] #[test]

View File

@ -10,7 +10,7 @@ Do not treat ad-hoc chat summaries, memory records, or Pod notifications as the
- `Ticket`: durable project/orchestration record. It contains requirements, decisions, plans, implementation reports, reviews, artifacts, and resolution history. - `Ticket`: durable project/orchestration record. It contains requirements, decisions, plans, implementation reports, reviews, artifacts, and resolution history.
- `Task`: session-local progress tracking inside a Pod. It is not the project record. - `Task`: session-local progress tracking inside a Pod. It is not the project record.
- `Assignment`: a concrete delegation from an Orchestrator to a coder/reviewer/investigator Pod. - `Assignment`: a concrete delegation from an Orchestrator to a coder/reviewer Pod or task-specific helper Pod.
- `IntentPacket`: the short implementation/review contract derived from a Ticket and handed to an Assignment. - `IntentPacket`: the short implementation/review contract derived from a Ticket and handed to an Assignment.
- `LocalTicketBackend`: the current `.yoi/tickets/` markdown/thread/artifacts storage backend. - `LocalTicketBackend`: the current `.yoi/tickets/` markdown/thread/artifacts storage backend.
@ -80,11 +80,6 @@ workflow = "multi-agent-workflow"
profile = "project:reviewer" profile = "project:reviewer"
launch_prompt = "$workspace/ticket/reviewer/launch" launch_prompt = "$workspace/ticket/reviewer/launch"
workflow = "multi-agent-workflow" workflow = "multi-agent-workflow"
[roles.investigator]
profile = "project:investigator"
launch_prompt = "$workspace/ticket/investigator/launch"
workflow = "ticket-orchestrator-routing"
``` ```
Fixed roles are: Fixed roles are:
@ -93,9 +88,10 @@ Fixed roles are:
- `orchestrator` - `orchestrator`
- `coder` - `coder`
- `reviewer` - `reviewer`
- `investigator`
This is not an arbitrary role registry. The fixed roles are the roles required by Ticket orchestration. This is not an arbitrary role registry. The fixed roles are the roles required by Ticket orchestration.
Stale `[roles.investigator]` config is rejected as an unsupported fixed role; remove it and,
when a spike is useful, let the Orchestrator create an ordinary task-specific read-only helper Pod.
`profile` selects the Pod runtime Profile for that role. The selected Profile owns durable role/system behavior. `ticket.config.toml` does not have a role-level `system_instruction` field. `profile` selects the Pod runtime Profile for that role. The selected Profile owns durable role/system behavior. `ticket.config.toml` does not have a role-level `system_instruction` field.
@ -116,7 +112,6 @@ If `.yoi/ticket.config.toml` is missing, defaults are:
- orchestrator: `ticket-orchestrator-routing` - orchestrator: `ticket-orchestrator-routing`
- coder: `multi-agent-workflow` - coder: `multi-agent-workflow`
- reviewer: `multi-agent-workflow` - reviewer: `multi-agent-workflow`
- investigator: `ticket-orchestrator-routing`
Important: top-level Ticket role launches cannot execute `profile = "inherit"` because top-level launch has no parent Profile to inherit from. Configure concrete role profiles in `.yoi/ticket.config.toml` before using `yoi panel` role-launch actions. Important: top-level Ticket role launches cannot execute `profile = "inherit"` because top-level launch has no parent Profile to inherit from. Configure concrete role profiles in `.yoi/ticket.config.toml` before using `yoi panel` role-launch actions.
@ -227,7 +222,6 @@ Role actions map to the same fixed roles configured in `.yoi/ticket.config.toml`
- intake launches the intake role without an existing Ticket and requires freeform context. - intake launches the intake role without an existing Ticket and requires freeform context.
- route launches the orchestrator role for an existing Ticket. - route launches the orchestrator role for an existing Ticket.
- investigate launches the investigator role for a read-only spike/investigation.
- implement launches the coder role for an implementation assignment. - implement launches the coder role for an implementation assignment.
- review launches the reviewer role for review. - review launches the reviewer role for review.
@ -286,10 +280,6 @@ workflow = "multi-agent-workflow"
[roles.reviewer] [roles.reviewer]
profile = "project:reviewer" profile = "project:reviewer"
workflow = "multi-agent-workflow" workflow = "multi-agent-workflow"
[roles.investigator]
profile = "project:investigator"
workflow = "ticket-orchestrator-routing"
``` ```
If a role still uses `profile = "inherit"`, the panel fails closed with a diagnostic explaining that a concrete profile is required. If a role still uses `profile = "inherit"`, the panel fails closed with a diagnostic explaining that a concrete profile is required.
@ -298,7 +288,7 @@ If a role still uses `profile = "inherit"`, the panel fails closed with a diagno
- `profile = "inherit"`: configure a concrete role Profile in `.yoi/ticket.config.toml`. - `profile = "inherit"`: configure a concrete role Profile in `.yoi/ticket.config.toml`.
- malformed `.yoi/ticket.config.toml`: fix the config and retry. - malformed `.yoi/ticket.config.toml`: fix the config and retry.
- missing Ticket id/slug for route, investigate, implement, or review actions: provide the target Ticket. - missing Ticket id/slug for route, implement, or review actions: provide the target Ticket.
- launch success but no visible completion: attach to or inspect the launched Pod; completion notifications are hints, not authority. - launch success but no visible completion: attach to or inspect the launched Pod; completion notifications are hints, not authority.
## Granularity ## Granularity