# Investigation and plan: Ticket config role profile mapping ## Conclusion Implement `.yoi/ticket.config.toml` as Ticket orchestration configuration with fixed Ticket role slots. Do not build a generic Role registry. The initial implementation should parse/validate the config, provide defaults, expose role-to-profile and prompt/workflow references, and wire the configured backend root into the existing Ticket built-in feature adapter. Pod spawning, TUI actions, and workflow state should remain follow-up work. ## Design position Use fixed Ticket roles: - `intake` - `orchestrator` - `coder` - `reviewer` - `investigator` These are not arbitrary user-defined roles. They are the roles required by the Ticket feature/workflows. Keep the boundary: - Profile: Pod runtime recipe, including durable role behavior/system instruction when using role-specific profiles. - Ticket role config: binds a fixed Ticket role to a Profile selector and optional launch prompt/workflow refs. - Launch prompt: first committed task/user message for a concrete Ticket/action. - Workflow: procedural flow, later possibly stateful. ## Current code map ### Ticket backend/tools - `crates/ticket/src/lib.rs` - Owns Ticket domain/backend and `LocalTicketBackend`. - Current backend root is supplied by callers. - `crates/ticket/src/tool.rs` - Owns Ticket tool input/output and `llm_worker::Tool` implementations. - `crates/pod/src/feature/builtin/ticket.rs` - Thin built-in feature adapter. - Currently resolves `/work-items` directly. - This is the first integration point for `.yoi/ticket.config.toml` backend root. ### Feature/host authority - `crates/pod/src/feature.rs` - Defines `HostAuthority::TicketBackend { root }`. - Feature descriptor/install path already supports requested host authority and tool contribution wiring. ### Profile selection - `crates/manifest/src/profile.rs` - Owns Profile registry/selector resolution. - `SpawnPod.profile` already accepts selectors such as `inherit`, default/source-qualified/unambiguous registry names. - `crates/pod/src/spawn/tool.rs` - Implements `SpawnPod` tool input with optional profile selector and `inherit` semantics. - This should remain the actual spawning boundary; config should not duplicate full profile resolution behavior. ### Workflow resources - `.yoi/workflow/*.md` - Current workflow files are project-authored resources. - `ticket-intake-workflow.md`, `ticket-orchestrator-routing.md`, `ticket-preflight-workflow.md`, and `multi-agent-workflow.md` are the relevant workflow refs. - `crates/workflow/src/workflow.rs` - Parses workflow frontmatter/body records. - No stateful workflow runner exists yet. ### Prompt resources / launch prompts - `crates/pod/src/prompt/loader.rs` - Resolves instruction-file references like `$yoi/...` and `$user/...` for current startup/instruction use. - Prompt catalog/resources are currently separate from workflow state. - There is no implemented role-specific launch prompt engine yet. - Role-specific durable system behavior should remain in the selected Profile for the MVP; this config should not override Profile system instruction. ## Important constraint Do not make `ticket` depend on `pod`. Possible dependency choices: 1. Put config parsing in `ticket` crate with raw profile/prompt/workflow string refs. - Pros: Ticket config is close to Ticket backend concept. - Cons: `ticket` learns about profile/prompt/workflow reference strings, but not their runtime resolution. 2. Put config parsing in `pod`. - Pros: avoids exposing prompt/profile concepts from `ticket`. - Cons: Ticket config becomes less reusable by future CLI/TUI code unless those crates also depend on `pod`. Recommended MVP: - Add config domain/parser to `crates/ticket`, using lightweight string wrapper types such as `ProfileSelectorRef`, `PromptRef`, and `WorkflowRef` without depending on `manifest` or `pod`. - In the MVP, `PromptRef` is for launch prompts only. Do not add role-level `system_instruction` here; the selected Profile owns durable role system behavior. - `pod` consumes this config and performs runtime interpretation where needed. This preserves: ```text ticket -> llm-worker / serde / toml only pod -> ticket ``` and avoids: ```text ticket -> pod ``` ## Proposed schema ```toml [backend] kind = "local" root = "work-items" [roles.intake] profile = "project:intake" launch_prompt = "$workspace/ticket/intake/launch" workflow = "ticket-intake-workflow" [roles.orchestrator] profile = "project:orchestrator" launch_prompt = "$workspace/ticket/orchestrator/launch" workflow = "ticket-orchestrator-routing" [roles.coder] profile = "inherit" launch_prompt = "$workspace/ticket/coder/launch" workflow = "multi-agent-workflow" [roles.reviewer] profile = "project:reviewer" launch_prompt = "$workspace/ticket/reviewer/launch" workflow = "multi-agent-workflow" [roles.investigator] profile = "inherit" launch_prompt = "$workspace/ticket/investigator/launch" workflow = "ticket-orchestrator-routing" ``` The specific prompt ref syntax should be accepted as opaque strings in this ticket. Runtime prompt resolution belongs to the later role launcher. ## Defaults When `.yoi/ticket.config.toml` is missing: - backend kind: `local` - backend root: `work-items` - role profiles: `inherit` - workflow defaults: - intake: `ticket-intake-workflow` - orchestrator: `ticket-orchestrator-routing` - coder: `multi-agent-workflow` - reviewer: `multi-agent-workflow` - investigator: `ticket-orchestrator-routing` - launch prompt: none When a role section exists but omits optional prompt/workflow refs: - keep configured profile; - fill workflow default for the fixed role; - leave prompt refs as none. ## Implementation plan ### Phase 1: Config model/parser in `ticket` Add a module such as `crates/ticket/src/config.rs`. Types: ```rust pub struct TicketConfig { pub backend: TicketBackendConfig, pub roles: TicketRoleProfiles, } pub struct TicketBackendConfig { pub kind: TicketBackendKind, pub root: PathBuf, } pub enum TicketBackendKind { Local, } pub enum TicketRole { Intake, Orchestrator, Coder, Reviewer, Investigator, } pub struct TicketRoleProfile { pub profile: ProfileSelectorRef, pub launch_prompt: Option, pub workflow: WorkflowRef, } ``` Use string wrapper types for selectors/refs to avoid depending on `manifest`/`pod`. Parsing behavior: - `TicketConfig::load_workspace(workspace_root: &Path)` reads `.yoi/ticket.config.toml` if present. - Missing file returns defaults. - Relative backend root resolves against workspace root. - Unknown roles are errors. - Unknown top-level fields should be diagnostics/errors rather than silently ignored. - Backend kind supports only `local` for now. ### Phase 2: Wire backend root into Pod Ticket feature adapter Update `crates/pod/src/feature/builtin/ticket.rs`: - Load `TicketConfig` from workspace root. - Use `config.backend.root` instead of hard-coded `workspace/work-items`. - Preserve current fail-closed behavior if root is missing/unusable. - Keep `HostAuthority::TicketBackend { root }` consistent with the validated/canonical root where practical. This directly improves existing Ticket tools without introducing role spawning yet. ### Phase 3: Tests Ticket crate tests: - missing config -> defaults; - full config parses; - partial role config uses role workflow defaults; - unknown role rejects; - unsupported backend kind rejects; - relative backend root resolves against workspace; - malformed profile/ref diagnostics are bounded. Pod tests: - Ticket built-in feature uses configured backend root; - missing/unusable configured backend root does not register tools; - default missing config still uses `/work-items`. ### Phase 4: Documentation/example Add one of: - a short `.yoi/ticket.config.example.toml`, or - a documented snippet under the ticket implementation report / docs if adding tracked config now is too early. For this repository, adding actual `.yoi/ticket.config.toml` should be considered carefully. If added, defaults should likely use `inherit` profiles until dedicated profiles exist. ## Deferred follow-ups ### `ticket-role-pod-launcher` - Take TicketRole + Ticket context + role config. - Build `SpawnPod` requests. - Resolve selected Profile using existing Profile registry; role-specific system behavior comes from that Profile. - Resolve launch prompt separately from the selected Profile's system instruction. - Commit launch prompt as the first user/task message, not hidden context. - Include workflow ref in launch/task context. ### `tui-ticket-role-actions` - Add TUI actions for fixed Ticket roles: - Intake/refine Ticket; - Route Ticket; - Investigate; - Implement; - Review. - Use the launcher rather than building SpawnPod requests inside UI code. ### Stateful workflow engine - Persist workflow phase/state. - Gate allowed tools by phase. - Inject phase prompts only by committing them to history first. - Keep Profile/SystemInstruction role-stable and task/phase prompts dynamic. ## Validation for implementation Required: - `cargo test -p ticket` - `cargo test -p pod ticket --lib` - `cargo test -p pod feature --lib` - `cargo check --workspace --all-targets` - `cargo fmt --check` - `git diff --check` - `./tickets.sh doctor` Optional if feasible: - `nix build .#yoi --no-link`