Merge branch 'feature/issue-ticket-system' into feature/background-tasks-first-class

Intègre le système de tickets/issues V1 (validé QA vert de bout en bout,
mémoire tickets-v1-e2e-validated-qa) dans la branche des tâches de fond.
Conflits résolus : app-tauri/state.rs, domain/events.rs, domain/lib.rs, domain/ports.rs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 15:15:43 +02:00
38 changed files with 6539 additions and 45 deletions

View File

@ -124,8 +124,60 @@ export type DomainEvent =
*/
source?: "mcp" | "file";
}
| { type: "issueCreated"; issueId: string; issueRef: string }
| { type: "issueUpdated"; issueRef: string; version: number }
| {
type: "issueStatusChanged";
issueRef: string;
status: TicketStatus;
version: number;
}
| {
type: "issuePriorityChanged";
issueRef: string;
priority: TicketPriority;
version: number;
}
| { type: "issueCarnetUpdated"; issueRef: string; version: number }
| {
type: "issueLinked";
issueRef: string;
target: string;
kind: TicketLinkKind;
version: number;
}
| {
type: "issueUnlinked";
issueRef: string;
target: string;
kind: TicketLinkKind;
version: number;
}
| {
type: "issueAgentAssigned";
issueRef: string;
agentId: string;
version: number;
}
| {
type: "issueAgentUnassigned";
issueRef: string;
agentId: string;
version: number;
}
| { type: "ptyOutput"; sessionId: string; bytes: number[] };
/**
* Whether a domain event is one of the ticket (`Issue*`) events. A small helper
* so ticket view-models refresh on any ticket mutation without re-enumerating
* every variant at each call site.
*/
export function isTicketEvent(
event: DomainEvent,
): event is Extract<DomainEvent, { type: `issue${string}` }> {
return event.type.startsWith("issue");
}
/** Where a project physically lives (mirror of the backend `RemoteRef`). */
export type RemoteRef =
| { kind: "local" }
@ -757,3 +809,84 @@ export interface GitBranches {
branches: string[];
current: string | null;
}
// ---------------------------------------------------------------------------
// Tickets (public wire name for the backend `Issue` model — "ticket" is the
// user-visible term; all commands/DTOs/events speak `ticket`/`issue`). Mirrors
// the `Ticket*Dto` shapes in `crates/app-tauri/src/tickets.rs` (camelCase).
// ---------------------------------------------------------------------------
/** A ticket reference on the wire, always `#<n>` (e.g. `"#42"`). */
export type TicketRef = string;
/** Lifecycle status of a ticket. */
export type TicketStatus = "open" | "inProgress" | "QA" | "closed";
/** Priority of a ticket. */
export type TicketPriority = "low" | "medium" | "high" | "critical";
/** The kind of relationship between two tickets. */
export type TicketLinkKind =
| "relatesTo"
| "blocks"
| "blockedBy"
| "duplicates"
| "dependsOn";
/** Who performed a ticket mutation (mirror of `TicketActorDto`, tagged `kind`). */
export type TicketActor =
| { kind: "user" }
| { kind: "agent"; agentId: string }
| { kind: "system" };
/** One directed link from a ticket to another (mirror of `TicketLinkDto`). */
export interface TicketLink {
targetRef: TicketRef;
kind: TicketLinkKind;
}
/** The full ticket, as returned by `ticket_read`/`ticket_create`/… . */
export interface Ticket {
id: string;
ref: TicketRef;
number: number;
title: string;
description: string;
status: TicketStatus;
priority: TicketPriority;
/** Present only when the ticket was read with `includeCarnet`. */
carnet?: string;
links: TicketLink[];
assignedAgentIds: string[];
createdBy: TicketActor;
updatedBy: TicketActor;
createdAt: number;
updatedAt: number;
/** Optimistic-concurrency version; echoed back as `expectedVersion` on writes. */
version: number;
}
/** A ticket summary row from `ticket_list` (mirror of `TicketSummaryDto`). */
export interface TicketSummary {
ref: TicketRef;
path: string;
title: string;
status: TicketStatus;
priority: TicketPriority;
assignedAgentIds: string[];
updatedAt: number;
}
/** One page of ticket summaries (mirror of `TicketListDto`). */
export interface TicketList {
items: TicketSummary[];
/** Opaque cursor for the next page, absent when the list is exhausted. */
nextCursor?: string;
}
/** The scoped-to-a-ticket Markdown carnet (mirror of `TicketCarnetDto`). */
export interface TicketCarnet {
ref: TicketRef;
carnet: string;
version: number;
}