ticket: use base32 project record ids

This commit is contained in:
2026-06-09 22:10:47 +09:00
parent 0803bc3725
commit 4203988d74
798 changed files with 477 additions and 105 deletions
@@ -0,0 +1,185 @@
# Ticket definition and API shape
## Definition
In yoi, a Ticket is the durable orchestration record shared by humans, Intake Pods, Orchestrator Pods, coder Pods, and reviewer Pods.
Short definition:
> A Ticket is a durable coordination contract that preserves agreed intent, requirements, readiness, decisions, evidence, review results, and resolution history so Pod groups can route, delegate, implement, verify, and complete work without losing the original intent.
A local markdown ticket, GitHub Issue, Linear issue, Jira issue, or another tracker record can be a backend representation of a Ticket, but none of those storage representations define the concept.
The current local backend is the repository's `work-items/` directory managed by `tickets.sh`. That path remains unchanged for now.
## Roles served by a Ticket
A Ticket is simultaneously:
- Intent anchor: preserves what the user and Intake agreed to.
- Requirements contract: states observable requirements, acceptance criteria, non-goals, and escalation conditions.
- Routing unit: lets Orchestrator decide between requirements sync, preflight, spike, implementation, review, blocked/action-required, pending, or close.
- Delegation source: lets Orchestrator derive an `IntentPacket` for a concrete Pod `Assignment`.
- Review contract: gives reviewer Pods an explicit basis for approval or request-changes.
- Evidence ledger: records plans, decisions, implementation reports, validation, review, artifacts, commits, branches, and resolution.
- Backend-neutral record: local files are the first backend, not the product concept.
## Relationship to other terms
- `Task`: session-local progress tracking inside a Pod/session. Smaller and more ephemeral than a Ticket.
- `Ticket`: durable project/orchestration record.
- `Assignment`: concrete delegation from Orchestrator to a coder/reviewer/investigator Pod.
- `IntentPacket`: concise implementation/review contract extracted from a Ticket and passed to an Assignment.
- `TicketBackend`: backend abstraction for Ticket storage/mutation.
- `LocalTicketBackend`: current local file backend over `work-items/`.
## Conceptual shape
The Rust model should use a thin typed envelope around Markdown/freeform body content.
Typed enough for machines:
- id / slug / title;
- kind / priority / labels;
- status;
- readiness;
- needs-preflight;
- risk flags;
- action-required state;
- event kind;
- review result;
- artifact references;
- branch/commit/Pod references where useful.
Flexible enough for humans/LLMs:
- background;
- requirements;
- acceptance criteria;
- rationale;
- design notes;
- plan;
- implementation report;
- review body;
- resolution.
## Candidate types
```rust
struct Ticket {
id: TicketId,
slug: TicketSlug,
title: String,
meta: TicketMeta,
body: TicketDocument,
events: Vec<TicketEvent>,
artifacts: Vec<TicketArtifactRef>,
}
struct TicketSummary {
id: TicketId,
slug: TicketSlug,
title: String,
status: TicketStatus,
kind: TicketKind,
priority: TicketPriority,
readiness: TicketReadiness,
needs_preflight: bool,
action_required: Option<ActionRequired>,
}
enum TicketStatus {
Open,
Pending,
Closed,
Other(String),
}
enum TicketReadiness {
Unspecified,
RequirementsSyncNeeded,
SpikeNeeded,
ImplementationReady,
Blocked,
Other(String),
}
enum TicketEventKind {
Comment,
Plan,
Decision,
ImplementationReport,
Review,
StatusChanged,
Closed,
Other(String),
}
struct TicketEvent {
kind: TicketEventKind,
body: MarkdownText,
refs: Vec<TicketRef>,
}
```
## Candidate backend trait
```rust
trait TicketBackend {
fn list(&self, filter: TicketFilter) -> Result<Vec<TicketSummary>>;
fn show(&self, id: TicketIdOrSlug) -> Result<Ticket>;
fn create(&self, input: NewTicket) -> Result<TicketRef>;
fn add_event(&self, id: TicketIdOrSlug, event: NewTicketEvent) -> Result<()>;
fn review(&self, id: TicketIdOrSlug, review: TicketReview) -> Result<()>;
fn set_status(&self, id: TicketIdOrSlug, status: TicketStatus) -> Result<()>;
fn close(&self, id: TicketIdOrSlug, resolution: MarkdownText) -> Result<()>;
fn doctor(&self) -> Result<TicketDoctorReport>;
}
```
The trait should remain backend-shaped and should not bake local file paths into the concept. Local paths belong in `LocalTicketBackend` configuration and implementation.
## Authority boundary
Ticket authority is separate from ordinary source/worktree filesystem scope.
Filesystem scope protects implementation areas and source edits. Ticket management is shared project coordination state and should be mediated through typed Ticket operations, backend locks/conflict handling, and audit/history records rather than delegated arbitrary write access.
This does not imply unlimited Ticket access. Ticket tools should be granted explicitly and limited to configured Ticket backends and operations.
## Backend layering
```text
Ticket API / TicketBackend
├─ LocalTicketBackend -> current work-items/ directory
├─ GitHubTicketBackend -> possible future backend
├─ LinearTicketBackend -> possible future backend
└─ McpTicketBackend -> possible future bridge
```
Local files are the first backend because they are already the authoritative project record in this repository. External tracker support is not part of the MVP.
## Validation/linting direction
Prefer a Ticket linter/policy layer over strict structural enforcement for all body content.
The code should enforce mechanical consistency and safe mutation:
- required frontmatter fields;
- status directory consistency;
- id/slug uniqueness;
- thread event parseability;
- review/result shape;
- resolution existence for closed tickets;
- artifact path containment;
- lock/conflict behavior;
- bounded diagnostics.
The code should not require every Ticket body to fit a rigid universal schema. Ticket bodies remain Markdown/freeform because design, cleanup, bugs, investigations, and orchestration epics need different prose.
## Current implementation sequence
1. `ticket-local-files-backend`
2. `ticket-built-in-feature-tools`
3. `ticket-intake-workflow`
4. `ticket-orchestrator-routing`
@@ -0,0 +1,220 @@
# WorkItem definition and API shape
> Superseded on 2026-06-05: the durable orchestration record concept is now named `Ticket`, not `WorkItem`. See `ticket-definition-and-api-shape-20260605.md` and the ticket thread decision for current terminology. This file is retained as historical design context.
## Definition
In Insomnia, a WorkItem is not primarily an issue tracker item. A WorkItem is an agreed execution contract that fixes user intent into a bounded unit that Pods can schedule, delegate, implement, review, and close safely.
Short definition:
> A WorkItem is a durable work contract that preserves user/system-agreed intent and gives Pod groups enough boundary, readiness, and evidence to execute, verify, and complete the work without degrading the original intent.
A ticket file, GitHub Issue, Linear issue, Jira issue, or another tracker record can be a persistence representation of a WorkItem, but none of those representations define the concept itself.
## Roles served by a WorkItem
A WorkItem is simultaneously:
- Intent anchor: preserves what the user and Intake agreed to.
- Delegation packet: lets Orchestrator/Coder understand what should be done and where to stop.
- Review contract: gives Reviewer an explicit basis for approval or request-changes.
- Scheduling unit: lets Orchestrator prioritize, interrupt, parallelize, and assign work.
- Boundary: records scope, non-goals, authority limits, privacy constraints, and escalation conditions.
- Lifecycle record: records plans, decisions, implementation reports, reviews, commits, validation, and resolution.
## Relationship to tickets and issue trackers
- WorkItem is the Insomnia orchestration concept.
- Local markdown ticket is one backend representation.
- GitHub/Linear/Jira issue is another possible backend representation.
- The backend should be abstracted below the WorkItem API.
The WorkItem abstraction should not try to become a complete issue tracker abstraction. It should cover only the subset needed for Insomnia orchestration: create/list/show, structured lifecycle events, status/readiness, review, close, consistency checks, and references/evidence.
## Typing strategy
Use a thin typed envelope with rich Markdown/freeform bodies and events.
The API should type fields that the system reads mechanically, while leaving the content that carries human intent as Markdown/freeform text. This keeps compatibility with external issue trackers and avoids turning Intake into a rigid form-filling flow.
### Typed fields
These should be typed because Orchestrator, TUI, policy, or tools branch on them:
- Identity:
- backend id
- external/work item id
- Classification:
- kind
- priority
- labels as free strings
- State:
- canonical status
- readiness
- action-required state
- needs-preflight flag
- risk flags
- Lifecycle operation/event kind:
- comment
- plan
- decision
- implementation report
- review approved
- review request-changes
- status changed
- closed
- References:
- related WorkItems
- files
- branches
- commits
- Pods
- URLs/artifacts
- Backend capability metadata:
- create/comment/review/close support
- artifact support
- offline support
- transactional/local-git support
Enums should not be closed too tightly. Use escape hatches such as `Other(String)` or canonical+raw mappings for tracker compatibility.
Examples:
```rust
enum WorkItemKind {
Bug,
Task,
Feature,
Design,
Chore,
Other(String),
}
struct MappedStatus {
canonical: WorkItemStatus,
raw: Option<String>,
}
```
### Freeform / Markdown fields
These should remain Markdown/freeform because AI agents and humans can read them well and external trackers can round-trip them:
- issue / problem statement
- user intent
- background / rationale
- requirements
- acceptance criteria
- non-goals
- invariants
- escalation conditions
- investigation notes
- implementation reports
- review comments
- resolution details
The event kind should be typed, but event body should remain Markdown.
```rust
struct WorkItemEvent {
kind: WorkItemEventKind,
body: MarkdownText,
refs: Vec<WorkItemRef>,
}
```
## Suggested initial model
```rust
struct WorkItem {
id: WorkItemId,
title: String,
classification: WorkItemClassification,
state: WorkItemState,
body: WorkItemDocument,
events: Vec<WorkItemEvent>,
refs: Vec<WorkItemRef>,
backend: BackendMetadata,
extensions: serde_json::Map<String, serde_json::Value>,
}
```
```rust
struct WorkItemState {
status: MappedStatus,
readiness: Readiness,
action_required: Option<ActionRequired>,
needs_preflight: bool,
risk_flags: Vec<RiskFlag>,
}
```
```rust
struct WorkItemDocument {
issue: MarkdownText,
requirements: MarkdownText,
acceptance_criteria: MarkdownText,
non_goals: Option<MarkdownText>,
notes: Option<MarkdownText>,
}
```
## API principle
Operations should be typed; payload bodies should be flexible.
Examples:
- `CreateWorkItem(NewWorkItem)` is typed.
- `AddWorkItemEvent { kind, body }` is typed by operation/event kind, freeform in body.
- `UpdateWorkItemState { readiness, action_required, risk_flags }` is typed.
- `CloseWorkItem { resolution: MarkdownText }` has typed operation semantics and freeform resolution text.
This gives enough structure for audit, scheduling, TUI display, and automation, without forcing issue-tracker-specific fields into the core model.
## Lint over hard schema
Prefer a WorkItem linter/policy layer over strict structural enforcement for body content.
Examples:
- title must be present.
- issue/requirements should not be empty.
- P0/P1 items without acceptance criteria should warn.
- risk flags with `needs_preflight = false` should warn.
- secret-like literals should warn or fail depending on policy.
- closing without review/resolution can warn or fail depending on project policy.
This makes the system agent-friendly and backend-compatible while still catching low-quality WorkItems.
## Backend abstraction
Backends should implement WorkItem storage/sync, not redefine WorkItem semantics.
```text
Intake / Orchestrator / TUI / Workflows
WorkItem API
WorkItemBackend
├─ LocalFilesBackend
├─ GitHubIssuesBackend
├─ LinearBackend
├─ JiraBackend
└─ MCPWorkItemBackend
```
Each backend should expose capabilities. The tool/TUI surface should show only operations supported by the configured backend.
## Scope and authority
WorkItem tool authority is separate from normal code/worktree filesystem scope.
Filesystem scope protects exclusive editing of implementation areas. WorkItem management is shared project coordination state and should be mediated by typed WorkItem operations, backend locks/conflict handling, and audit records rather than ordinary delegated write scope.
This does not imply unlimited file access. The WorkItem tool should be limited to configured WorkItem backends and operations.
## Design consequence
The first implementation can be LocalFilesBackend over current `work-items/`, preserving markdown/frontmatter/thread/artifacts and `tickets.sh` compatibility. The API should already be backend-shaped so GitHub Issues or other trackers can be added later without changing Intake/Orchestrator semantics.