merge develop into setup wizard worktree

This commit is contained in:
Keisuke Hirata 2026-06-10 18:12:31 +09:00
commit 6bb023e9fe
No known key found for this signature in database
22 changed files with 483 additions and 61 deletions

View File

@ -0,0 +1 @@
{"id":"orch-plan-20260610-090202-1","ticket_id":"00001KSKBPSJG","kind":"accepted_plan","accepted_plan":{"summary":"Implement the model setup wizard as an explicit one-shot CLI path, not normal Pod startup: add `yoi setup-model` that launches a setup TUI. The wizard should select a bundled model/provider entry, optionally accept an auth hint/reference when the provider requires one, and persist a user default Profile by updating the user Profile registry under the normal config root (`profiles.toml` plus a generated user Profile Lua file such as `profiles/default.lua`). It must not write workspace `.yoi`, session history, Ticket files, runtime/local/secret-like files, or start/attach a Pod during setup. Existing normal launch semantics remain unchanged except that subsequent default startup can use the persisted user default Profile.","branch":"tui-model-setup-wizard","worktree":"/home/hare/Projects/yoi/.worktree/tui-model-setup-wizard","role_plan":"Coder implements in `.worktree/tui-model-setup-wizard` with write scope limited to the child worktree. Orchestrator keeps Ticket/progress records in the main workspace. Reviewer will be delegated after coder report."},"author":"orchestrator","at":"2026-06-10T09:02:02Z"}

View File

@ -2,7 +2,7 @@
title: "TUI: ユーザーマニフェストのモデル設定 wizard" title: "TUI: ユーザーマニフェストのモデル設定 wizard"
state: 'inprogress' state: 'inprogress'
created_at: "2026-05-27T00:00:18Z" created_at: "2026-05-27T00:00:18Z"
updated_at: '2026-06-10T07:59:32Z' updated_at: '2026-06-10T09:08:59Z'
queued_by: 'yoi ticket' queued_by: 'yoi ticket'
queued_at: '2026-06-10T07:59:32Z' queued_at: '2026-06-10T07:59:32Z'
--- ---

View File

@ -104,4 +104,69 @@ State changed to `inprogress`.
Accepting implementation after workspace inspection. Expected overlap with prompt-resource cleanup is low, so route in parallel in a dedicated worktree. Accepting implementation after workspace inspection. Expected overlap with prompt-resource cleanup is low, so route in parallel in a dedicated worktree.
---
<!-- event: plan author: orchestrator at: 2026-06-10T09:02:02Z -->
## Plan
Routing decision: implementation_ready
Reason:
- Ticket is already `inprogress`; relation query and orchestration-plan query show no blockers.
- Existing thread marked this as needing a small preflight. Bounded code/context check resolved the implementation-shaping decisions without needing another planning round.
Evidence checked:
- Ticket body/thread/artifacts for `00001KSKBPSJG`.
- Relation records: none.
- Orchestration plan records: none before this routing pass.
- Current worktree/branch state: `.worktree/tui-model-setup-wizard` exists and has no implementation commit yet.
- Current CLI surface in `crates/yoi/src/main.rs` includes top-level `panel`, `keys`, `pod`, `ticket`, `objective`, `session`, `memory lint`; setup is not yet present.
- Profile/config surface in `crates/manifest/src/paths.rs` and `crates/manifest/src/profile.rs`; active reusable Profiles are Lua-first with user registry/config under the normal config root.
- Provider/model catalog resources under `resources/providers` and `resources/models`.
Binding decisions / invariants:
- Add an explicit one-shot setup entry point `yoi setup-model`; it must not run normal Pod startup, create sessions, attach sockets, or spawn Pods while configuring.
- Persist the result as a user default Profile through normal Profile mechanisms: user `profiles.toml` plus a generated user Profile Lua file (for example `profiles/default.lua`) under the normal config root. Do not write workspace `.yoi`, Ticket records, session history, runtime/local/secret-like files, or project records from the setup wizard.
- Do not introduce legacy manifest authority or new environment-variable configuration surfaces.
- Keep existing startup/profile semantics intact; the setup wizard only creates/updates config that normal startup already knows how to consume.
- If provider credential handling needs more than selecting/recording an existing auth hint/ref, escalate rather than inventing a new secret store flow.
Implementation latitude:
- Exact TUI layout and internal state machine organization are up to coder, bounded by the one-shot setup and persistence invariants.
- Coder may choose the generated profile name/path if it is deterministic, user-config scoped, and unambiguous from the default selector.
- Coder may keep the catalog/model choice set modest and testable for the first implementation.
Validation:
- Focused CLI/parser and setup persistence tests.
- Focused TUI/setup module tests where practical.
- `cargo fmt --check` or `cargo fmt`, `git diff --check`, focused cargo tests, and broader `cargo check`/`nix build .#yoi` before merge because this touches CLI/TUI/config/package source surfaces.
Escalate if:
- The implementation requires changing Profile registry semantics, secret-store behavior, normal Pod launch behavior, or workspace `.yoi` authority.
- Credential entry/storage cannot be limited to an existing explicit auth reference/hint.
---
<!-- event: plan author: orchestrator at: 2026-06-10T09:08:59Z -->
## Plan
Routing update: blocked_by_dependency_or_missing_authority
Reason:
- This Ticket remains `inprogress` and has an accepted implementation plan for `.worktree/tui-model-setup-wizard`, but the current Orchestrator process cannot delegate write scope to the child worktree.
- Attempted `SpawnPod` for `coder-tui-model-setup-wizard` with write scope limited to the child worktree failed: requested scope `/home/hare/Projects/yoi/.worktree/tui-model-setup-wizard` is not within the spawner effective scope.
- Retrying with broader `/home/hare/Projects/yoi` write scope also failed for the same effective-scope reason.
- Direct file write to the child worktree through the available tool scope is read-only, so the Orchestrator cannot safely implement the Ticket in this session either.
Evidence checked:
- Ticket body/thread/artifacts and relation/orchestration-plan records.
- Current CLI/profile/provider code map was boundedly inspected and the accepted plan was recorded.
- Worktree `.worktree/tui-model-setup-wizard` exists, but no coder was spawned and no implementation files were changed.
Next action:
- Continue this Ticket from a Pod/session with delegated write scope for `.worktree/tui-model-setup-wizard`, or explicitly re-route/clean up the existing inprogress worktree.
- Do not treat this as planning uncertainty; the current blocker is write/delegation authority for implementation side effects.
--- ---

View File

@ -0,0 +1 @@
{"id":"orch-plan-20260610-083728-1","ticket_id":"00001KTR6D3C5","kind":"waiting_capacity_note","note":"Panel launch did not specify a target Ticket and instructed Orchestrator to wait for explicit follow-up before spawning role Pods. Queue acceptance was not recorded yet. Bounded check: Ticket body/thread show implementation_ready, relation query has no blockers, orchestration plan has no existing records, main workspace is clean, and active worktrees are prompt-resource-centralization plus tui-model-setup-wizard. Waiting reason: human gate/explicit target authorization.","author":"orchestrator","at":"2026-06-10T08:37:28Z"}

View File

@ -1,9 +1,11 @@
--- ---
title: 'Lua Profileに組み込みyoi APIとimport/extendを追加する' title: 'Lua Profileに組み込みyoi APIとimport/extendを追加する'
state: 'ready' state: 'queued'
created_at: '2026-06-10T07:19:31Z' created_at: '2026-06-10T07:19:31Z'
updated_at: '2026-06-10T07:41:26Z' updated_at: '2026-06-10T08:37:28Z'
assignee: null assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-10T08:04:05Z'
--- ---
## 背景 ## 背景

View File

@ -38,4 +38,13 @@ Orchestrator routing: queued 後は implementation に進めてよい。実装
Intake により implementation_ready と判断したため、state を ready にする。実装開始は panel による ready -> queued と Orchestrator routing に委ねる。 Intake により implementation_ready と判断したため、state を ready にする。実装開始は panel による ready -> queued と Orchestrator routing に委ねる。
---
<!-- event: state_changed author: workspace-panel at: 2026-06-10T08:04:05Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
--- ---

View File

@ -1,8 +1,8 @@
--- ---
title: 'LLM向けプロンプト直書きを廃止してresources/promptsへ集約する' title: 'LLM向けプロンプト直書きを廃止してresources/promptsへ集約する'
state: 'inprogress' state: 'closed'
created_at: '2026-06-10T07:29:13Z' created_at: '2026-06-10T07:29:13Z'
updated_at: '2026-06-10T07:59:33Z' updated_at: '2026-06-10T09:08:26Z'
assignee: null assignee: null
queued_by: 'workspace-panel' queued_by: 'workspace-panel'
queued_at: '2026-06-10T07:49:23Z' queued_at: '2026-06-10T07:49:23Z'

View File

@ -0,0 +1,20 @@
完了しました。
実施内容:
- Branch `prompt-resource-centralization``develop``--no-ff` merge しました。
- Merge commit: `9aaa3232 merge: prompt resource centralization`
- LLM 向け Ticket role launch prompt prose は `crates/client/src/ticket_role.rs` の production hardcoding から `resources/prompts/ticket_role/*.md` へ移動されました。
- Rust 側は runtime dynamic fields の sectioned/bounded composition と workspace/user/builtin prompt resource lookup を担当します。
- Workspace prompt override の regression coverage が追加されています。
確認:
- Branch-local reviewer `reviewer-prompt-resource-centralization` が approve。
- `cargo fmt --check` passed。
- `git diff --check` passed。
- `cargo test -p client ticket_role --lib` passed。
- `target/debug/yoi ticket doctor` passed。
- typed `TicketDoctor` は 0 errors / 3 pre-existing diagnostics。
- `nix build .#yoi` passed。
残作業:
- なし。user-level prompt override 専用 test は将来の追加余地として non-blocking note に留めました。

View File

@ -47,4 +47,171 @@ State changed to `inprogress`.
Accepting implementation after workspace inspection. Expected overlap with TUI model setup wizard is low, so route in parallel in a dedicated worktree. Accepting implementation after workspace inspection. Expected overlap with TUI model setup wizard is low, so route in parallel in a dedicated worktree.
---
<!-- event: implementation_report author: hare at: 2026-06-10T08:11:58Z -->
## Implementation report
Implemented directly in worktree .worktree/prompt-resource-centralization because Pod-management tools are not available in this API session.\n\nBranch/commit:\n- prompt-resource-centralization / 342d5cf refactor: move ticket role prompts to resources\n\nSummary:\n- Moved Ticket role launch guidance strings from crates/client/src/ticket_role.rs into resources/prompts/ticket_role/*.md.\n- Added workspace/user override lookup for ticket_role prompt fragments before falling back to embedded resources.\n- Kept Rust code responsible for runtime composition, bounded dynamic fields, and segment assembly.\n- Added regression coverage for workspace prompt override behavior.\n\nValidation:\n- cargo fmt\n- cargo test -p client ticket_role --lib\n- git diff --check\n- cargo check -p client\n\nPending:\n- External review/merge validation not yet performed.\n- TUI model setup wizard worktree exists but implementation has not started.
---
<!-- event: plan author: orchestrator at: 2026-06-10T09:00:04Z -->
## Plan
Routing update: review_needed
Reason:
- Implementation report exists for branch `prompt-resource-centralization` / commit `342d5cf8`, but independent review and merge validation are not yet recorded.
Evidence checked:
- Ticket body/thread/artifacts and relation/orchestration-plan records.
- Worktree status for `.worktree/prompt-resource-centralization` is clean and branch has commit `342d5cf8` over `develop`.
- Visible Pods showed no existing child reviewer.
Next action:
- Spawned branch-local reviewer Pod `reviewer-prompt-resource-centralization` with read-only scope over the worktree and this Ticket. Reviewer is asked for approve/request_changes, blockers, notes, and focused validation evidence.
Boundary:
- This is branch-local review evidence only; no main-branch Ticket approval, merge, close, or cleanup is recorded by this routing step.
---
<!-- event: review author: reviewer-prompt-resource-centralization at: 2026-06-10T09:06:45Z status: approve -->
## Review: approve
Branch-local review verdict: approve
Evidence:
- Reviewed branch diff from merge-base `76165b47d664cb02a79c16272fbb5f8377a693bb` against `develop`.
- Verified production `crates/client/src/ticket_role.rs` no longer hardcodes the large LLM-facing Ticket role launch / intake handoff / orchestrator / coder / reviewer prompt prose; prompt fragments now live under `resources/prompts/ticket_role/*.md`.
- Verified runtime values remain sectioned/bounded and resource lookup order is workspace `.yoi/prompts/ticket_role/*.md` -> user prompts dir -> builtin embedded resource.
- Verified existing semantics and tests cover representative builtin prompt generation and workspace override behavior.
Validation run by reviewer:
- `cargo test -p client ticket_role --lib` passed.
- `git diff --check $(git merge-base HEAD develop)..HEAD` passed.
- `nix build .#yoi` passed.
Non-blocking notes:
- Dedicated user-level prompt override test could be added later, but workspace override exercises the same loader path sufficiently for this change.
- Prompt text fragments remain in test assertions only; this is not production prompt hardcoding.
This is branch-local review evidence; final main-branch approval/close still belongs to merge-completion.
---
<!-- event: implementation_report author: orchestrator at: 2026-06-10T09:07:14Z -->
## Implementation report
Merge-ready dossier for merge-completion
Ticket id: `00001KTR6YVDB`
Branch/worktree: `prompt-resource-centralization` / `.worktree/prompt-resource-centralization`
Commits: `342d5cf8 refactor: move ticket role prompts to resources`
Intent / invariant check:
- Move LLM-facing Ticket role launch prompt prose out of Rust hardcoding into `resources/prompts`-managed resources.
- Preserve runtime dynamic composition, bounded fields, role launch semantics, and override behavior.
- Keep workflow markdown authority and avoid moving UI/diagnostic/protocol literals unnecessarily.
Implementation summary:
- `crates/client/src/ticket_role.rs` now loads Ticket role prompt fragments from `resources/prompts/ticket_role/*.md` with workspace/user/builtin fallback.
- Runtime values remain assembled by Rust as sectioned/bounded context.
- Regression coverage includes resource-backed launch prompt generation and workspace override behavior.
Coder/reviewer evidence:
- Implementation report recorded branch/commit and validation: `cargo fmt`, `cargo test -p client ticket_role --lib`, `git diff --check`, `cargo check -p client`.
- Branch-local reviewer Pod `reviewer-prompt-resource-centralization` approved.
- Reviewer validation: `cargo test -p client ticket_role --lib`, `git diff --check $(git merge-base HEAD develop)..HEAD`, `nix build .#yoi` all passed.
Blockers fixed or rejected findings:
- No blockers.
- Non-blocking user-level override test suggestion rejected for this change because workspace override covers the same loader path sufficiently.
Residual risks:
- Low; resource packaging and prompt-resource lookup are the main risk, covered by reviewer `nix build .#yoi` and focused tests.
Dirty state:
- Routing/review records were committed separately as `41f74490 ticket: record routing progress`; main workspace is expected clean before merge.
Parent/human decision needs:
- User requested inprogress消化; proceed with authorized merge-completion for this inprogress Ticket.
---
<!-- event: review author: orchestrator at: 2026-06-10T09:08:18Z status: approve -->
## Review: approve
Main-branch review/merge-completion approval.
Verified before merge:
- Merge-ready dossier matched branch `prompt-resource-centralization`, worktree `.worktree/prompt-resource-centralization`, and implementation commit `342d5cf8`.
- Branch-local independent review approved with no blockers.
- Main workspace dirty state was limited to committed Ticket routing/dossier records.
Merged:
- `git merge --no-ff prompt-resource-centralization -m "merge: prompt resource centralization"`
- Merge commit: `9aaa3232 merge: prompt resource centralization`
Post-merge validation:
- `cargo fmt --check` passed.
- `git diff --check` passed.
- `cargo test -p client ticket_role --lib` passed.
- `target/debug/yoi ticket doctor` passed.
- typed `TicketDoctor` reported 0 errors and 3 pre-existing diagnostics.
- `nix build .#yoi` passed.
Result: approve.
---
<!-- event: state_changed author: orchestrator at: 2026-06-10T09:08:18Z from: inprogress to: done reason: merged_and_validated field: state -->
## State changed
Merged branch `prompt-resource-centralization` with merge commit `9aaa3232`; branch-local review approved and post-merge validation passed (`cargo fmt --check`, `git diff --check`, `cargo test -p client ticket_role --lib`, `target/debug/yoi ticket doctor`, `nix build .#yoi`).
---
<!-- event: state_changed author: hare at: 2026-06-10T09:08:26Z from: done to: closed reason: closed field: state -->
## State changed
Ticket を closed にしました。
---
<!-- event: close author: hare at: 2026-06-10T09:08:26Z status: closed -->
## 完了
完了しました。
実施内容:
- Branch `prompt-resource-centralization``develop``--no-ff` merge しました。
- Merge commit: `9aaa3232 merge: prompt resource centralization`
- LLM 向け Ticket role launch prompt prose は `crates/client/src/ticket_role.rs` の production hardcoding から `resources/prompts/ticket_role/*.md` へ移動されました。
- Rust 側は runtime dynamic fields の sectioned/bounded composition と workspace/user/builtin prompt resource lookup を担当します。
- Workspace prompt override の regression coverage が追加されています。
確認:
- Branch-local reviewer `reviewer-prompt-resource-centralization` が approve。
- `cargo fmt --check` passed。
- `git diff --check` passed。
- `cargo test -p client ticket_role --lib` passed。
- `target/debug/yoi ticket doctor` passed。
- typed `TicketDoctor` は 0 errors / 3 pre-existing diagnostics。
- `nix build .#yoi` passed。
残作業:
- なし。user-level prompt override 専用 test は将来の追加余地として non-blocking note に留めました。
--- ---

View File

@ -0,0 +1 @@
{"id":"orch-plan-20260610-083728-1","ticket_id":"00001KTR83D6E","kind":"waiting_capacity_note","note":"Panel launch did not specify a target Ticket and instructed Orchestrator to wait for explicit follow-up before spawning role Pods. Queue acceptance was not recorded yet. Bounded check: Ticket body/thread show implementation_ready, relation query has no blockers, orchestration plan has no existing records, main workspace is clean, and active worktrees are prompt-resource-centralization plus tui-model-setup-wizard. Waiting reason: human gate/explicit target authorization.","author":"orchestrator","at":"2026-06-10T08:37:28Z"}

View File

@ -1,9 +1,11 @@
--- ---
title: 'Edit/Writeの同一ファイル変更をToolExecutionContextで直列化する' title: 'Edit/Writeの同一ファイル変更をToolExecutionContextで直列化する'
state: 'planning' state: 'queued'
created_at: '2026-06-10T07:49:10Z' created_at: '2026-06-10T07:49:10Z'
updated_at: '2026-06-10T07:49:10Z' updated_at: '2026-06-10T08:37:28Z'
assignee: null assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-10T08:10:58Z'
--- ---
## 背景 ## 背景

View File

@ -4,4 +4,29 @@
LocalTicketBackend によって作成されました。 LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: intake at: 2026-06-10T08:09:54Z -->
## Intake summary
既存 Ticket `00001KTR83D6E` の本文・thread・artifacts を確認し、関連する closed Ticket `00001KTNVGT8G`ToolExecutionContext 導入)と `00001KTNS9B50`same-file multiple Edit の analyticsも確認した。同目的の未完了重複は見当たらず、本 Ticket は `ToolExecutionContext` 基盤を使う concrete follow-up として妥当。目的は Worker の approved tool call 並列実行を維持しつつ、`Edit` / `Write` など同一 target file への built-in mutation を tool 側の per-file boundary で直列化し、同一 `batch_id` 内では `call_index` 昇順に実行すること。binding decisions は Worker に resource scheduler を持たせないこと、Hook/Interceptor に lock lifecycle を置かないこと、全 tool/全 response を直列化しないこと、分散 file lock はこの Ticket の非目標であること。実装裁量として、異なる `batch_id` の同一 file mutation に厳密 global ordering が必要か単純な per-file mutex で十分かを判断し、理由を記録する余地がある。Reviewer focus / risk flags は concurrency、path-canonicalization、scope-permission-boundary、diagnostics-privacy、failure/timeout/drop 時の guard 解放。blocking open question はなく、受け入れ条件と validation が明確なため implementation_ready と判断する。
---
<!-- event: state_changed author: intake at: 2026-06-10T08:09:54Z from: planning to: ready reason: intake_ready field: state -->
## State changed
既存 Ticket の本文・thread・artifacts と関連 Ticket を確認した。要件・非目標・受け入れ条件・レビュー焦点が実装 routing 可能な粒度で揃っているため、planning から ready にします。
---
<!-- event: state_changed author: workspace-panel at: 2026-06-10T08:10:58Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
--- ---

View File

@ -4,8 +4,9 @@
//! host-side Pod spawning behind the `client` crate so UI callers do not need to //! host-side Pod spawning behind the `client` crate so UI callers do not need to
//! depend on `pod` internals. //! depend on `pod` internals.
use std::fs;
use std::io; use std::io;
use std::path::PathBuf; use std::path::{Path, PathBuf};
use std::time::Duration; use std::time::Duration;
use manifest::{ProfileDiscovery, ProfileResolveOptions, ProfileResolver, ProfileSelector}; use manifest::{ProfileDiscovery, ProfileResolveOptions, ProfileResolver, ProfileSelector};
@ -38,13 +39,13 @@ impl TicketRef {
non_empty(self.id.as_deref()) non_empty(self.id.as_deref())
} }
fn append_prompt_lines(&self, out: &mut String) { fn append_prompt_lines(&self, out: &mut String, prompts: &TicketRolePromptTemplates) {
match non_empty(self.id.as_deref()) { match non_empty(self.id.as_deref()) {
None => out.push_str("Target Ticket: not specified\n"), None => out.push_str("Target Ticket: not specified\n"),
Some(id) => { Some(id) => {
out.push_str("Target Ticket:\n"); out.push_str("Target Ticket:\n");
push_bounded_bullet(out, "id", id); push_bounded_bullet(out, "id", id);
out.push_str("- Treat the Ticket id as an opaque storage id. Read TicketShow body/thread/artifacts before routing or implementation; do not infer requirements from id or title alone.\n"); push_prompt_fragment(out, &prompts.ticket_id_guidance);
} }
} }
} }
@ -65,13 +66,11 @@ impl TicketIntakeHandoff {
} }
} }
fn append_prompt_lines(&self, out: &mut String) { fn append_prompt_lines(&self, out: &mut String, prompts: &TicketRolePromptTemplates) {
out.push_str("\nPanel handoff:\n"); out.push_str("\nPanel handoff:\n");
push_bounded_bullet(out, "workspace", &self.workspace_label); push_bounded_bullet(out, "workspace", &self.workspace_label);
push_bounded_bullet(out, "workspace_orchestrator_pod", &self.orchestrator_pod); push_bounded_bullet(out, "workspace_orchestrator_pod", &self.orchestrator_pod);
out.push_str("- When Intake has clarified the request and created/updated the Ticket, use the typed Ticket tool surface to append `intake_summary` and set `state = ready` when the Ticket is ready to queue; use planning language for Tickets that still need clarification/preparation.\n"); push_prompt_fragment(out, &prompts.intake_handoff);
out.push_str("- Handoff report fields: created_or_updated_ticket_id, state, open_questions_or_risk_flags, intake_summary.\n");
out.push_str("- Do not start implementation automatically; the user queues a ready Ticket via panel (`ready -> queued`), and Orchestrator treats `queued` as schedulable before moving it to `inprogress` when starting.\n");
} }
} }
@ -450,15 +449,110 @@ async fn wait_for_run_acceptance(
.map_err(|_| TicketRoleLaunchError::RunAcceptanceTimeout)? .map_err(|_| TicketRoleLaunchError::RunAcceptanceTimeout)?
} }
#[derive(Debug, Clone)]
struct TicketRolePromptTemplates {
launch_preamble: String,
ticket_id_guidance: String,
record_language_configured: String,
record_language_unconfigured: String,
intake_handoff: String,
orchestrator_worktree_routing: String,
orchestrator_merge_completion: String,
coder_worktree_routing: String,
reviewer_worktree_routing: String,
}
impl TicketRolePromptTemplates {
fn load(workspace_root: &Path) -> Self {
Self {
launch_preamble: load_ticket_role_prompt(
workspace_root,
"launch_preamble",
include_str!("../../../resources/prompts/ticket_role/launch_preamble.md"),
),
ticket_id_guidance: load_ticket_role_prompt(
workspace_root,
"ticket_id_guidance",
include_str!("../../../resources/prompts/ticket_role/ticket_id_guidance.md"),
),
record_language_configured: load_ticket_role_prompt(
workspace_root,
"record_language_configured",
include_str!(
"../../../resources/prompts/ticket_role/record_language_configured.md"
),
),
record_language_unconfigured: load_ticket_role_prompt(
workspace_root,
"record_language_unconfigured",
include_str!(
"../../../resources/prompts/ticket_role/record_language_unconfigured.md"
),
),
intake_handoff: load_ticket_role_prompt(
workspace_root,
"intake_handoff",
include_str!("../../../resources/prompts/ticket_role/intake_handoff.md"),
),
orchestrator_worktree_routing: load_ticket_role_prompt(
workspace_root,
"orchestrator_worktree_routing",
include_str!(
"../../../resources/prompts/ticket_role/orchestrator_worktree_routing.md"
),
),
orchestrator_merge_completion: load_ticket_role_prompt(
workspace_root,
"orchestrator_merge_completion",
include_str!(
"../../../resources/prompts/ticket_role/orchestrator_merge_completion.md"
),
),
coder_worktree_routing: load_ticket_role_prompt(
workspace_root,
"coder_worktree_routing",
include_str!("../../../resources/prompts/ticket_role/coder_worktree_routing.md"),
),
reviewer_worktree_routing: load_ticket_role_prompt(
workspace_root,
"reviewer_worktree_routing",
include_str!("../../../resources/prompts/ticket_role/reviewer_worktree_routing.md"),
),
}
}
}
fn load_ticket_role_prompt(workspace_root: &Path, name: &str, builtin: &str) -> String {
let relative = Path::new("ticket_role").join(format!("{name}.md"));
for candidate in [
Some(workspace_root.join(".yoi/prompts").join(&relative)),
manifest::paths::user_prompts_dir().map(|dir| dir.join(&relative)),
]
.into_iter()
.flatten()
{
if let Ok(text) = fs::read_to_string(candidate) {
return text;
}
}
builtin.to_string()
}
fn push_prompt_fragment(out: &mut String, fragment: &str) {
out.push_str(fragment.trim_end());
out.push('\n');
}
fn build_launch_prompt( fn build_launch_prompt(
context: &TicketRoleLaunchContext, context: &TicketRoleLaunchContext,
profile: &str, profile: &str,
workflow: &str, workflow: &str,
launch_prompt_ref: Option<&str>, launch_prompt_ref: Option<&str>,
) -> String { ) -> String {
let prompts = TicketRolePromptTemplates::load(&context.workspace_root);
let mut out = String::new(); let mut out = String::new();
out.push_str("# Ticket role launch\n\n"); push_prompt_fragment(&mut out, &prompts.launch_preamble);
out.push_str("Profile supplies durable system/role behavior. The workflow segment supplies the procedural flow. This generated launch prompt supplies only the concrete Ticket/action context for the first committed user task.\n\n"); out.push('\n');
push_bounded_field(&mut out, "Role", context.role.as_str()); push_bounded_field(&mut out, "Role", context.role.as_str());
push_bounded_field(&mut out, "Profile selector", profile); push_bounded_field(&mut out, "Profile selector", profile);
push_bounded_field(&mut out, "Workflow", workflow); push_bounded_field(&mut out, "Workflow", workflow);
@ -474,14 +568,14 @@ fn build_launch_prompt(
match non_empty(context.ticket_record_language.as_deref()) { match non_empty(context.ticket_record_language.as_deref()) {
Some(language) => { Some(language) => {
push_bounded_field(&mut out, "Ticket record language", language); push_bounded_field(&mut out, "Ticket record language", language);
out.push_str("Ticket record language guidance: write durable Ticket item/thread/resolution text and Ticket tool bodies in this language. This does not change normal worker response language or memory/Knowledge generation language. Do not translate protocol literals, file paths, commands, logs, identifiers, or quoted external text solely because this language is configured.\n"); push_prompt_fragment(&mut out, &prompts.record_language_configured);
} }
None => out.push_str("Ticket record language: not configured; preserve existing/default Ticket record language behavior.\n"), None => push_prompt_fragment(&mut out, &prompts.record_language_unconfigured),
} }
out.push('\n'); out.push('\n');
if let Some(ticket) = &context.ticket { if let Some(ticket) = &context.ticket {
ticket.append_prompt_lines(&mut out); ticket.append_prompt_lines(&mut out, &prompts);
} else { } else {
out.push_str("Target Ticket: not specified\n"); out.push_str("Target Ticket: not specified\n");
} }
@ -492,7 +586,7 @@ fn build_launch_prompt(
} }
if let Some(handoff) = &context.intake_handoff { if let Some(handoff) = &context.intake_handoff {
handoff.append_prompt_lines(&mut out); handoff.append_prompt_lines(&mut out, &prompts);
} }
if let Some(intent_packet) = non_empty(context.intent_packet.as_deref()) { if let Some(intent_packet) = non_empty(context.intent_packet.as_deref()) {
@ -520,56 +614,35 @@ fn build_launch_prompt(
); );
} }
append_role_execution_guidance(&mut out, context.role); append_role_execution_guidance(&mut out, context.role, &prompts);
out out
} }
fn append_role_execution_guidance(out: &mut String, role: TicketRole) { fn append_role_execution_guidance(
out: &mut String,
role: TicketRole,
prompts: &TicketRolePromptTemplates,
) {
match role { match role {
TicketRole::Orchestrator => append_orchestrator_agent_routing_guidance(out), TicketRole::Orchestrator => {
TicketRole::Coder => append_coder_agent_routing_guidance(out), out.push('\n');
TicketRole::Reviewer => append_reviewer_agent_routing_guidance(out), push_prompt_fragment(out, &prompts.orchestrator_worktree_routing);
out.push('\n');
push_prompt_fragment(out, &prompts.orchestrator_merge_completion);
}
TicketRole::Coder => {
out.push('\n');
push_prompt_fragment(out, &prompts.coder_worktree_routing);
}
TicketRole::Reviewer => {
out.push('\n');
push_prompt_fragment(out, &prompts.reviewer_worktree_routing);
}
TicketRole::Intake => {} TicketRole::Intake => {}
} }
} }
fn append_orchestrator_agent_routing_guidance(out: &mut String) {
out.push_str("\nOrchestrator worktree + agent routing guidance:\n");
out.push_str("- Treat `ticket-orchestrator-routing` as the routing gate. Read the Ticket and workspace state first; `ready -> queued` authorizes routing, not implementation side effects.\n");
out.push_str("- Create worktrees or spawn coder/reviewer Pods only after `state = inprogress` is already recorded and accepted. If the Ticket is still queued and unblocked, record `queued -> inprogress` before any worktree/SpawnPod side effect.\n");
out.push_str("- Use `worktree-workflow` for the mechanical worktree plan: create `.worktree/<task-name>`, keep tracked `.yoi` project records visible in the child worktree, exclude `.yoi/memory` plus local/runtime/log/lock/secret-like `.yoi` paths, and keep active orchestration progress plus final review/approval/close in the main workspace unless explicitly designed otherwise.\n");
out.push_str("- Use `multi-agent-workflow` for the sibling loop: coder and reviewer are siblings under this Orchestrator; coder gets narrow write scope to the child worktree; reviewer is read-only by default.\n");
out.push_str("- Give the coder an intent packet that distinguishes binding decisions/invariants, implementation latitude, escalation conditions, child worktree/branch, validation commands, and report expectations; set SpawnPod `cwd` to the child worktree while delegating explicit scope separately, prohibit editing main-workspace `.yoi`/Ticket/workflow/docs records, and prohibit creating generated memory/local/runtime/secret-like files in the child worktree.\n");
out.push_str("- Give the reviewer the recorded Ticket intent, binding decisions/invariants, implementation latitude, acceptance criteria, explicit escalation conditions, diff/commits, validation evidence, and blocker/non-blocker criteria; reviewer judgment is against recorded requirements and decisions, not unrecorded preferred tactics. Keep branch-local reviewer verdicts in the review report or merge-ready dossier rather than recording them as final main-branch Ticket approval.\n");
out.push_str("- Ticket thread progress may record worktree plan, coder delegated/completed/blocked, reviewer delegated, blocker/fix-loop summaries, and merge-ready dossier pointer; do not merge, close, or record final main approval in this routing/branch-review phase.\n");
out.push_str("- Stop at a merge-ready dossier for `orchestrator-merge-completion` containing Ticket id, branch/worktree, commits, intent/invariant check, implementation summary, coder/reviewer Pods, blockers fixed or rejected findings with reasons, validation performed, residual risks, dirty state, and parent/human decision needs if any.\n");
out.push_str("\nOrchestrator merge-completion guidance:\n");
out.push_str("- Enter merge-completion only for an `inprogress` Ticket with a merge-ready dossier. Conservative or missing authorization mode stops at the dossier; do not infer merge authority from public/default configuration.\n");
out.push_str("- Required dossier fields before merge: Ticket id; branch/worktree; commits; intent/invariant check; implementation summary; coder/reviewer Pods; blockers fixed or rejected findings with reasons; validation performed; residual risks; dirty state; parent/human decision needs if any.\n");
out.push_str("- Before merging, verify the dossier branch/worktree/commits match the branch to merge, independent reviewer approval exists in the dossier or an explicit human override decision is recorded, the main workspace is safe, and unrelated dirty changes are understood.\n");
out.push_str("- Merge only when dogfooding/workspace policy grants merge authority. If authority is unavailable, record/return the dossier and stop without merge, close, final main Ticket approval, or cleanup.\n");
out.push_str("- Preserve the boundary: branch-local reviewer verdicts are dossier evidence; final main-branch Ticket approval or close happens only during authorized merge-completion after merge and validation evidence.\n");
out.push_str("- Authorized sequence: stop/reclaim coder and reviewer Pods where appropriate; merge with `git merge --no-ff <branch>` or the project-agreed method; run post-merge validation appropriate to the change; record review, merge, and validation outcomes in the Ticket thread during merge-completion; transition `inprogress -> done` or close according to typed Ticket workflow rules; then remove the merged child worktree and delete the merged branch unless explicitly kept.\n");
out.push_str("- Post-merge validation baseline: run focused tests from the Ticket/dossier, `cargo fmt --check`, `git diff --check`, and `target/debug/yoi ticket doctor` where applicable; add broader validation such as `cargo check --workspace --all-targets`, `nix build .#yoi`, or equivalent when risk, API surface, packaging, runtime resources, prompts, or touched files warrant it.\n");
}
fn append_coder_agent_routing_guidance(out: &mut String) {
out.push_str("\nCoder worktree routing guidance:\n");
out.push_str("- Implement only in the provided child worktree/branch. SpawnPod should set `cwd` to that worktree so Bash/tool defaults already start there; do not treat `cwd` as authority, and do not edit main-workspace `.yoi`, Ticket, workflow, docs, or memory records; child-worktree `.yoi` project records may be visible when they are part of the branch.\n");
out.push_str("- Do not create `.yoi/memory`, local/runtime state, logs, locks, caches, sockets, or secret-like files in the child worktree.\n");
out.push_str("- Treat the intent packet, binding decisions/invariants, implementation latitude, validation expectations, and report expectations as the contract. Investigate and choose local tactics only within the recorded implementation latitude; escalate to Orchestrator rather than expanding scope when design, permission, history, prompt-context, dependency, or Ticket-boundary questions appear.\n");
out.push_str("- Report worktree path, branch, commits/status, changed files, implementation summary, validation run, unresolved notes, and whether the branch is ready for external review. Do not merge, push, close Tickets, or delete worktrees.\n");
}
fn append_reviewer_agent_routing_guidance(out: &mut String) {
out.push_str("\nReviewer worktree routing guidance:\n");
out.push_str("- Review as a sibling of the coder under Orchestrator, read-only by default. Read the Ticket/intent packet, branch diff or commits, and validation evidence before judging. Judge implementation against recorded intent, binding decisions/invariants, implementation latitude, acceptance criteria, and explicit escalation conditions, not unrecorded preferred tactics.\n");
out.push_str("- Classify findings as blockers, non-blocking follow-ups, or parent-decision items against the recorded intent, binding decisions/invariants, implementation latitude, acceptance criteria, and explicit escalation conditions; include concrete file/line evidence where useful.\n");
out.push_str("- Keep the branch-local reviewer verdict in the review report for the Orchestrator merge-ready dossier. Do not record final main-branch Ticket approval, merge, close, push, or instruct the coder directly.\n");
}
fn default_pod_name(role: TicketRole, ticket: Option<&TicketRef>) -> String { fn default_pod_name(role: TicketRole, ticket: Option<&TicketRef>) -> String {
let mut name = format!("ticket-{}", role.as_str()); let mut name = format!("ticket-{}", role.as_str());
if let Some(seed) = ticket.and_then(TicketRef::pod_name_seed) { if let Some(seed) = ticket.and_then(TicketRef::pod_name_seed) {
@ -1203,6 +1276,27 @@ workflow = "ticket-review-workflow"
assert!(text.contains("when risk, API surface, packaging, runtime resources, prompts, or touched files warrant it")); assert!(text.contains("when risk, API surface, packaging, runtime resources, prompts, or touched files warrant it"));
} }
#[test]
fn workspace_prompt_override_replaces_ticket_role_fragment() {
let temp = TempDir::new().unwrap();
write_builtin_role_config(temp.path(), &[TicketRole::Orchestrator]);
let override_dir = temp.path().join(".yoi/prompts/ticket_role");
std::fs::create_dir_all(&override_dir).unwrap();
std::fs::write(
override_dir.join("orchestrator_worktree_routing.md"),
"Orchestrator worktree + agent routing guidance:\n- WORKSPACE OVERRIDE MARKER\n",
)
.unwrap();
let mut orchestrator = TicketRoleLaunchContext::new(temp.path(), TicketRole::Orchestrator);
orchestrator.ticket = Some(TicketRef::id("prompt-resource-override"));
let plan = plan_ticket_role_launch(orchestrator).unwrap();
let text = text_segment(&plan);
assert!(text.contains("WORKSPACE OVERRIDE MARKER"));
assert!(!text.contains("Use `multi-agent-workflow`"));
}
#[test] #[test]
fn caller_provided_pod_name_is_used_exactly() { fn caller_provided_pod_name_is_used_exactly() {
let temp = TempDir::new().unwrap(); let temp = TempDir::new().unwrap();

View File

@ -0,0 +1,5 @@
Coder worktree routing guidance:
- Implement only in the provided child worktree/branch. SpawnPod should set `cwd` to that worktree so Bash/tool defaults already start there; do not treat `cwd` as authority, and do not edit main-workspace `.yoi`, Ticket, workflow, docs, or memory records; child-worktree `.yoi` project records may be visible when they are part of the branch.
- Do not create `.yoi/memory`, local/runtime state, logs, locks, caches, sockets, or secret-like files in the child worktree.
- Treat the intent packet, binding decisions/invariants, implementation latitude, validation expectations, and report expectations as the contract. Investigate and choose local tactics only within the recorded implementation latitude; escalate to Orchestrator rather than expanding scope when design, permission, history, prompt-context, dependency, or Ticket-boundary questions appear.
- Report worktree path, branch, commits/status, changed files, implementation summary, validation run, unresolved notes, and whether the branch is ready for external review. Do not merge, push, close Tickets, or delete worktrees.

View File

@ -0,0 +1,3 @@
- When Intake has clarified the request and created/updated the Ticket, use the typed Ticket tool surface to append `intake_summary` and set `state = ready` when the Ticket is ready to queue; use planning language for Tickets that still need clarification/preparation.
- Handoff report fields: created_or_updated_ticket_id, state, open_questions_or_risk_flags, intake_summary.
- Do not start implementation automatically; the user queues a ready Ticket via panel (`ready -> queued`), and Orchestrator treats `queued` as schedulable before moving it to `inprogress` when starting.

View File

@ -0,0 +1,3 @@
# Ticket role launch
Profile supplies durable system/role behavior. The workflow segment supplies the procedural flow. This generated launch prompt supplies only the concrete Ticket/action context for the first committed user task.

View File

@ -0,0 +1,8 @@
Orchestrator merge-completion guidance:
- Enter merge-completion only for an `inprogress` Ticket with a merge-ready dossier. Conservative or missing authorization mode stops at the dossier; do not infer merge authority from public/default configuration.
- Required dossier fields before merge: Ticket id; branch/worktree; commits; intent/invariant check; implementation summary; coder/reviewer Pods; blockers fixed or rejected findings with reasons; validation performed; residual risks; dirty state; parent/human decision needs if any.
- Before merging, verify the dossier branch/worktree/commits match the branch to merge, independent reviewer approval exists in the dossier or an explicit human override decision is recorded, the main workspace is safe, and unrelated dirty changes are understood.
- Merge only when dogfooding/workspace policy grants merge authority. If authority is unavailable, record/return the dossier and stop without merge, close, final main Ticket approval, or cleanup.
- Preserve the boundary: branch-local reviewer verdicts are dossier evidence; final main-branch Ticket approval or close happens only during authorized merge-completion after merge and validation evidence.
- Authorized sequence: stop/reclaim coder and reviewer Pods where appropriate; merge with `git merge --no-ff <branch>` or the project-agreed method; run post-merge validation appropriate to the change; record review, merge, and validation outcomes in the Ticket thread during merge-completion; transition `inprogress -> done` or close according to typed Ticket workflow rules; then remove the merged child worktree and delete the merged branch unless explicitly kept.
- Post-merge validation baseline: run focused tests from the Ticket/dossier, `cargo fmt --check`, `git diff --check`, and `target/debug/yoi ticket doctor` where applicable; add broader validation such as `cargo check --workspace --all-targets`, `nix build .#yoi`, or equivalent when risk, API surface, packaging, runtime resources, prompts, or touched files warrant it.

View File

@ -0,0 +1,9 @@
Orchestrator worktree + agent routing guidance:
- Treat `ticket-orchestrator-routing` as the routing gate. Read the Ticket and workspace state first; `ready -> queued` authorizes routing, not implementation side effects.
- Create worktrees or spawn coder/reviewer Pods only after `state = inprogress` is already recorded and accepted. If the Ticket is still queued and unblocked, record `queued -> inprogress` before any worktree/SpawnPod side effect.
- Use `worktree-workflow` for the mechanical worktree plan: create `.worktree/<task-name>`, keep tracked `.yoi` project records visible in the child worktree, exclude `.yoi/memory` plus local/runtime/log/lock/secret-like `.yoi` paths, and keep active orchestration progress plus final review/approval/close in the main workspace unless explicitly designed otherwise.
- Use `multi-agent-workflow` for the sibling loop: coder and reviewer are siblings under this Orchestrator; coder gets narrow write scope to the child worktree; reviewer is read-only by default.
- Give the coder an intent packet that distinguishes binding decisions/invariants, implementation latitude, escalation conditions, child worktree/branch, validation commands, and report expectations; set SpawnPod `cwd` to the child worktree while delegating explicit scope separately, prohibit editing main-workspace `.yoi`/Ticket/workflow/docs records, and prohibit creating generated memory/local/runtime/secret-like files in the child worktree.
- Give the reviewer the recorded Ticket intent, binding decisions/invariants, implementation latitude, acceptance criteria, explicit escalation conditions, diff/commits, validation evidence, and blocker/non-blocker criteria; reviewer judgment is against recorded requirements and decisions, not unrecorded preferred tactics. Keep branch-local reviewer verdicts in the review report or merge-ready dossier rather than recording them as final main-branch Ticket approval.
- Ticket thread progress may record worktree plan, coder delegated/completed/blocked, reviewer delegated, blocker/fix-loop summaries, and merge-ready dossier pointer; do not merge, close, or record final main approval in this routing/branch-review phase.
- Stop at a merge-ready dossier for `orchestrator-merge-completion` containing Ticket id, branch/worktree, commits, intent/invariant check, implementation summary, coder/reviewer Pods, blockers fixed or rejected findings with reasons, validation performed, residual risks, dirty state, and parent/human decision needs if any.

View File

@ -0,0 +1 @@
Ticket record language guidance: write durable Ticket item/thread/resolution text and Ticket tool bodies in this language. This does not change normal worker response language or memory/Knowledge generation language. Do not translate protocol literals, file paths, commands, logs, identifiers, or quoted external text solely because this language is configured.

View File

@ -0,0 +1 @@
Ticket record language: not configured; preserve existing/default Ticket record language behavior.

View File

@ -0,0 +1,4 @@
Reviewer worktree routing guidance:
- Review as a sibling of the coder under Orchestrator, read-only by default. Read the Ticket/intent packet, branch diff or commits, and validation evidence before judging. Judge implementation against recorded intent, binding decisions/invariants, implementation latitude, acceptance criteria, and explicit escalation conditions, not unrecorded preferred tactics.
- Classify findings as blockers, non-blocking follow-ups, or parent-decision items against the recorded intent, binding decisions/invariants, implementation latitude, acceptance criteria, and explicit escalation conditions; include concrete file/line evidence where useful.
- Keep the branch-local reviewer verdict in the review report for the Orchestrator merge-ready dossier. Do not record final main-branch Ticket approval, merge, close, push, or instruct the coder directly.

View File

@ -0,0 +1 @@
- Treat the Ticket id as an opaque storage id. Read TicketShow body/thread/artifacts before routing or implementation; do not infer requirements from id or title alone.