ticket: plan ticket config roles

This commit is contained in:
Keisuke Hirata 2026-06-06 02:35:34 +09:00
parent 4fb6a3a688
commit 956394b4ac
No known key found for this signature in database
4 changed files with 504 additions and 0 deletions

View File

@ -0,0 +1,297 @@
# 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.
- Ticket role config: binds a fixed Ticket role to a Profile selector and optional prompt/workflow refs.
- System instruction: role's durable behavior/boundary.
- 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 `<workspace>/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 / system instruction
- `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.
## 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`.
- `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"
system_instruction = "$workspace/ticket/intake/system"
launch_prompt = "$workspace/ticket/intake/launch"
workflow = "ticket-intake-workflow"
[roles.orchestrator]
profile = "project:orchestrator"
system_instruction = "$workspace/ticket/orchestrator/system"
launch_prompt = "$workspace/ticket/orchestrator/launch"
workflow = "ticket-orchestrator-routing"
[roles.coder]
profile = "inherit"
system_instruction = "$workspace/ticket/coder/system"
launch_prompt = "$workspace/ticket/coder/launch"
workflow = "multi-agent-workflow"
[roles.reviewer]
profile = "project:reviewer"
system_instruction = "$workspace/ticket/reviewer/system"
launch_prompt = "$workspace/ticket/reviewer/launch"
workflow = "multi-agent-workflow"
[roles.investigator]
profile = "inherit"
system_instruction = "$workspace/ticket/investigator/system"
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`
- system instruction / 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 system_instruction: Option<PromptRef>,
pub launch_prompt: Option<PromptRef>,
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 `<workspace>/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 profile selector using existing Profile registry.
- Resolve role system instruction separately from initial launch prompt.
- 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 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`

View File

@ -0,0 +1,161 @@
---
id: 20260605-173322-ticket-config-role-profile-mapping
slug: ticket-config-role-profile-mapping
title: Ticket config role profile mapping
status: open
kind: task
priority: P1
labels: [ticket, config, profile, orchestration]
created_at: 2026-06-05T17:33:22Z
updated_at: 2026-06-05T17:35:08Z
assignee: null
legacy_ticket: null
---
## Background
Ticket orchestration now has typed Ticket backend/tools and workflows for Intake and Orchestrator routing. The next step before TUI role actions is to make the workspace's Ticket orchestration configuration explicit.
The project should not introduce an arbitrary Role registry. The roles needed here are fixed by the Ticket feature/workflows:
- intake
- orchestrator
- coder
- reviewer
- investigator
Each fixed role needs to select a Profile and, later, separate role-level system instruction, first launch prompt, and workflow binding. This is Ticket orchestration configuration, not a generic Profile replacement.
## Goal
Add workspace-local Ticket configuration at `.yoi/ticket.config.toml` and a typed parser/resolver for fixed Ticket role profile mappings.
The MVP should establish the configuration file, fixed role schema, backend root configuration, validation, and role-to-profile selector resolution. It should not yet spawn Pods or add TUI actions.
## Requirements
- Add typed Ticket orchestration config support for `.yoi/ticket.config.toml`.
- Keep roles fixed, not arbitrary:
- `intake`
- `orchestrator`
- `coder`
- `reviewer`
- `investigator`
- Support backend configuration:
- local backend kind;
- root path, defaulting to `work-items` relative to the workspace.
- Support per-role configuration:
- `profile` selector string;
- optional role system instruction prompt reference;
- optional launch/initial prompt reference;
- optional workflow slug/reference.
- Keep `profile` selector syntax aligned with existing SpawnPod/profile selectors where possible:
- `inherit`
- `default`
- `builtin:<name>`
- `user:<name>`
- `project:<name>`
- unqualified registry selector when accepted by existing profile resolution.
- Preserve the conceptual separation:
- Profile = Pod runtime recipe.
- role system instruction = role's durable behavior/boundary.
- launch prompt = first committed task/user message for a specific Ticket/action.
- workflow = procedural flow, later potentially stateful.
- Validate known fields and reject/diagnose unknown roles or malformed fields.
- Resolve relative backend roots against workspace root.
- Do not auto-create backend directories in this ticket.
- Update the existing Ticket built-in feature adapter to use the configured backend root when available, falling back to `work-items`.
- Expose a reusable resolver API for later Pod launch/TUI code:
- role -> profile selector;
- role -> optional system instruction ref;
- role -> optional launch prompt ref;
- role -> optional workflow slug;
- backend root.
## Non-goals
- Arbitrary role registry.
- Pod spawning or role launcher implementation.
- TUI action implementation.
- Stateful workflow engine.
- Per-phase workflow prompt injection.
- Changing Profile authoring/resolution semantics.
- Replacing `profiles.toml`.
- Renaming `work-items/`.
- External tracker integration.
- Scheduler/lease/queue automation.
## Suggested schema
```toml
[backend]
kind = "local"
root = "work-items"
[roles.intake]
profile = "project:intake"
system_instruction = "$workspace/ticket/intake/system"
launch_prompt = "$workspace/ticket/intake/launch"
workflow = "ticket-intake-workflow"
[roles.orchestrator]
profile = "project:orchestrator"
system_instruction = "$workspace/ticket/orchestrator/system"
launch_prompt = "$workspace/ticket/orchestrator/launch"
workflow = "ticket-orchestrator-routing"
[roles.coder]
profile = "inherit"
system_instruction = "$workspace/ticket/coder/system"
launch_prompt = "$workspace/ticket/coder/launch"
workflow = "multi-agent-workflow"
[roles.reviewer]
profile = "project:reviewer"
system_instruction = "$workspace/ticket/reviewer/system"
launch_prompt = "$workspace/ticket/reviewer/launch"
workflow = "multi-agent-workflow"
[roles.investigator]
profile = "inherit"
system_instruction = "$workspace/ticket/investigator/system"
launch_prompt = "$workspace/ticket/investigator/launch"
workflow = "ticket-orchestrator-routing"
```
MVP may make all role fields optional except `profile` when a role section is present. Missing file and missing role sections should fall back to builtin defaults.
## Default behavior
When `.yoi/ticket.config.toml` is absent:
- backend kind: local
- backend root: `<workspace>/work-items`
- all 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`
- system instruction / launch prompt refs: none
## Acceptance criteria
- `.yoi/ticket.config.toml` can be parsed from a workspace root.
- Missing config falls back to documented defaults.
- Fixed role sections parse correctly.
- Unknown roles are rejected or reported as configuration errors.
- Relative backend root resolves against workspace root.
- Backend root from config is used by the Ticket built-in feature adapter.
- Role profile selector strings are retained/parsed in a form later usable by role launching code.
- Optional `system_instruction`, `launch_prompt`, and `workflow` refs are parsed and exposed without trying to run a workflow engine.
- Tests cover missing config, full config, partial role config, unknown role, relative backend root, and adapter backend-root usage.
- `cargo test -p ticket` and focused `cargo test -p pod ticket --lib` pass.
- `cargo check --workspace --all-targets`, `cargo fmt --check`, `git diff --check`, and `./tickets.sh doctor` pass.
## Follow-up tickets
- `ticket-role-pod-launcher`: construct role-specific `SpawnPod` requests from Ticket context, role config, separated system instruction, launch prompt, workflow binding, and scope policy.
- `tui-ticket-role-actions`: expose fixed Ticket role actions in TUI using the launcher.
- Later workflow-state engine: persisted workflow phase/state, phase-specific allowed tools, and phase prompts committed to history before model use.

View File

@ -0,0 +1,46 @@
<!-- event: create author: tickets.sh at: 2026-06-05T17:33:22Z -->
## Created
Created by tickets.sh create.
---
<!-- event: decision author: hare at: 2026-06-05T17:35:08Z -->
## Decision
Decision: implement `.yoi/ticket.config.toml` as Ticket orchestration configuration with fixed Ticket role slots.
Use fixed roles, not an arbitrary Role registry:
- intake
- orchestrator
- coder
- reviewer
- investigator
The config maps these fixed Ticket roles to Profile selector strings and optional role system instruction / launch prompt / workflow refs. This keeps Profile as the Pod runtime recipe while Ticket orchestration owns the role-to-profile binding.
The first implementation should parse/validate config and wire the configured backend root into Ticket tools. It should not spawn Pods, add TUI actions, or implement a stateful workflow engine yet.
Detailed investigation and implementation plan: `artifacts/investigation-plan.md`.
---
<!-- event: plan author: hare at: 2026-06-05T17:35:08Z -->
## Plan
Plan:
1. Add Ticket config model/parser, probably in `crates/ticket/src/config.rs`, using lightweight string wrapper types for Profile/prompt/workflow refs so `ticket` does not depend on `pod`.
2. Parse `.yoi/ticket.config.toml` from a workspace root, with defaults when missing.
3. Support `[backend]` local root and fixed `[roles.*]` sections.
4. Wire the configured backend root into `crates/pod/src/feature/builtin/ticket.rs` so Ticket tools no longer hard-code `<workspace>/work-items`.
5. Add focused tests in `ticket` and `pod`.
6. Defer Pod role launching, TUI actions, and workflow-state/prompt sequencing to follow-up tickets.
---