import { type ResolvedScope } from '../roster.js'; import { type StoreStatus } from './shared/storeRead.js'; export interface ProjectResolutionError { error: 'PROJECT_NOT_FOUND'; message: string; suggestion: string; } export declare function isProjectResolutionError(result: unknown): result is ProjectResolutionError; export type Severity = 'low' | 'med' | 'high' | 'critical'; export type Priority = 'low' | 'medium' | 'high' | 'critical'; export type EpicStatus = 'planned' | 'in_progress' | 'shipped' | 'on_hold' | 'cancelled'; export type IssueStatus = 'open' | 'in_progress' | 'blocked' | 'closed' | 'wontfix'; export interface CreateIssueInput { projectId?: string; severity: Severity; title: string; details: string; epic_id?: string; /** Both of these are in the canonical Issue model and in CreateIssueSpec. * They were absent here, so create_issue accepted them (the MCP schema is * additionalProperties: true) and silently dropped them. */ priority?: Priority; tags?: string[]; /** * Who is working on it — EPIC-0040. A prefixed identity: user:, * agent: or human:. Rejected if it names a team. */ assignee?: string; /** Which team owns it — EPIC-0040. `team:`, minted by HQ. */ team?: string; } export interface CreateIssueOutput { id: string; timestamp: string; path: string; status: string; epic_id?: string; location: 'project' | 'global'; } export interface CloseIssueInput { projectId?: string; issue_id: string; resolution?: string; status?: 'closed' | 'wontfix'; } export interface CloseIssueOutput { id: string; path: string; status: IssueStatus; closed_at: string; /** The resolution the record carries after the call, not the one passed in. */ resolution?: string; /** * Present and true only when the record was already closed with a resolution, * so this call preserved it instead of overwriting it (ISS-0168). Callers that * report "closed X" should report this case differently — that they cannot is * what made the overwrite silent. */ already_closed?: true; } export interface CloseIssueError { error: 'ISSUE_NOT_FOUND'; projectId: string; issue_id: string; message: string; available_issues: Array<{ id: string; title: string; }>; } /** * More than one record answers to the same id, so the write target is * undefined. Refusing is the only safe answer: picking readdir-first silently * edited one issue and left its twin untouched (ISS-0131). */ export interface AmbiguousIssueIdError { error: 'AMBIGUOUS_ISSUE_ID'; projectId: string; issue_id: string; message: string; /** Filenames that all matched, each usable verbatim as an unambiguous id. */ candidates: string[]; } export interface ListRepoIssuesInput { projectId?: string; status?: IssueStatus; /** EPIC-0040 scoped read: a person or agent id, expanded through HQ's roster. */ assignee?: string; /** EPIC-0040 scoped read: team:. */ team?: string; } export interface ListRepoIssuesOutput { issues: IssueSummary[]; /** Present on a scoped read: what was matched, and whether aliases applied. */ scope?: ResolvedScope; /** Files in the issues dir that could not be parsed — surfaced so silent * skips can't masquerade as a shorter, clean list. */ malformed?: number; malformed_files?: string[]; /** Records that only parsed after salvaging corrupted YAML — readable, but * the underlying files need repair. */ degraded?: number; degraded_files?: string[]; /** Ids claimed by more than one record. Writers refuse to target these * (AMBIGUOUS_ISSUE_ID), so they are a data-repair queue, not a cosmetic * nit — surfaced here rather than discovered mid-incident (ISS-0131). */ duplicate_ids?: number; duplicate_id_files?: Record; } export interface LogEpicInput { projectId?: string; title: string; summary: string; motivation?: string[]; outcomes?: string[]; acceptance_criteria?: string[]; priority?: Priority; tags?: string[]; owner?: string; squad?: string; /** * Who leads it — EPIC-0040. A prefixed identity: user:, agent: * or human:. The id-bearing successor to free-text `owner`, which is * kept and read as an alias rather than renamed (ADR-0010). */ assignee?: string; /** Which team owns it — EPIC-0040. `team:`. Successor to `squad`. */ team?: string; } export interface LogEpicOutput { epic_id: string; timestamp: string; path: string; location: 'project' | 'global'; } export interface ListEpicsInput { projectId?: string; status?: EpicStatus; priority?: Priority; tags?: string[]; /** EPIC-0040 scoped read. Epics fall back to `owner` when unset. */ assignee?: string; /** EPIC-0040 scoped read. Epics fall back to `squad` when unset. */ team?: string; } export interface EpicSummary { id: string; title: string; status: EpicStatus; priority: Priority; /** Only when set (EPIC-0040). */ assignee?: string; team?: string; } export interface ListEpicsOutput { epics: EpicSummary[]; /** Present on a scoped read. */ scope?: ResolvedScope; /** Which of empty / unreadable / partial this listing actually is (ISS-0153). */ store_status?: StoreStatus; /** Records present but skipped. Non-zero means the list is short. */ unreadable_count?: number; store_reason?: string; } export interface GetEpicInput { projectId?: string; epic_id: string; } export interface EpicNote { /** ISO timestamp from the `## Note (…)` heading update_epic writes. */ at: string; text: string; } export interface Epic { id: string; title: string; summary: string; status: EpicStatus; priority: Priority; motivation: string[]; outcomes: string[]; acceptance_criteria: string[]; tags: string[]; owner: string; squad: string; /** Prefixed identity. Falls back to `owner` when unset. */ assignee: string; /** `team:`. Falls back to `squad` when unset. */ team: string; created_at: string; /** * Timestamped notes appended by update_epic, oldest first. * * These were written to the body and then unreadable: the model had no field * for them, so read_epic returned an epic whose entire revision history was * missing. EPIC-0038 alone carried five, including the note recording why * Phase 4's target was withdrawn and why the filename renames were dropped — * the two decisions most likely to be re-litigated by someone reading the * epic through the tool. */ notes: EpicNote[]; } export interface GetEpicOutput { epic: Epic | null; error?: string; } export interface UpdateEpicInput { projectId?: string; epic_id: string; status?: EpicStatus; priority?: Priority; summary?: string; title?: string; owner?: string; squad?: string; /** Reassign. Empty string clears it. */ assignee?: string; /** Move team. Empty string clears it. */ team?: string; tags?: string[]; /** Appended to the body as a timestamped entry, preserving prior notes. */ note?: string; } export interface UpdateEpicOutput { id: string; path: string; status: EpicStatus; priority: Priority; updated_at: string; /** Human-readable field transitions; empty when the patch was a no-op. */ changes: string[]; } /** More than one epic record answers to the id, so the write target is undefined. */ export interface AmbiguousEpicIdError { error: 'AMBIGUOUS_EPIC_ID'; epic_id: string; message: string; candidates: string[]; } export interface GetEpicIssuesInput { projectId?: string; epic_id: string; } export interface IssueSummary { id: string; title: string; severity: Severity; status: string; /** * Linked epic id (e.g., "EPIC-0001") if the issue's frontmatter declares one. * Surfaced via list_issues / list_epic_issues so consumers (HQ, MCP clients) * can build the epic→issue relationship without a per-issue read_issue call. */ epic_id?: string; /** Priority level (low / medium / high / critical) when set. */ priority?: string; /** Optional tags array. Parsed from inline-flow YAML (`tags: [a, b]`). */ tags?: string[]; /** ISO timestamp of issue creation. */ created_at?: string; /** ISO timestamp of last modification. */ updated_at?: string; /** Only when set (EPIC-0040). */ assignee?: string; team?: string; /** * True when the record only parsed after salvaging — its YAML was corrupted * by a markdown section appended to a bare-YAML file (see `close_issue` * writing `## Resolution` into `.yml` records). The issue is readable, but * the file needs repair. */ degraded?: boolean; } export interface GetEpicIssuesOutput { issues: IssueSummary[]; } export interface ResolveEpicInput { projectId?: string; query: string; limit?: number; } export interface EpicMatch { id: string; title: string; status: EpicStatus; priority: Priority; score: number; } export interface ResolveEpicOutput { matches: EpicMatch[]; } export interface EpicNotFoundError { error: 'EPIC_NOT_FOUND'; epic_id: string; message: string; suggested_epics: Array<{ id: string; title: string; }>; } export declare function createIssue(input: CreateIssueInput): Promise; export declare function closeIssue(input: CloseIssueInput): Promise; /** * Delegates to the Phase 3 repository. The output contract is unchanged — the * point of the seam is that callers cannot tell which layer answered. * * `degraded` changes meaning slightly and for the better: it used to mean "only * parsed after salvaging corrupted YAML", and now means "readable, but the * codec had to interpret something" — a superset that also catches a legacy * status value or a title that fell back to the filename. Both are the same * repair queue. */ export declare function listRepoIssues(input: ListRepoIssuesInput): Promise; export declare function logEpic(input: LogEpicInput): Promise; export declare function listEpics(input: ListEpicsInput): Promise; export declare function getEpic(input: GetEpicInput): Promise; /** * Update an epic in place. * * Epics were write-once: log_epic created them and nothing could change status, * priority, summary, or ownership afterwards (ISS-0140). The practical effects * were that `status` stayed `planned` forever — making list_epics(status:...) * useless and feeding wrong epic state into oracle/roadmap reporting * (ISS-0110) — and that correcting an epic meant hand-editing the file, which * is the markdown-into-YAML write hazard that corrupted issue records in the * first place. * * Frontmatter is mutated as a parsed object and re-serialized, never patched by * regex, and the file is replaced atomically. The body's `## Summary` section * mirrors the frontmatter `summary` field, so both are updated together — * leaving them to drift is what makes hand-editing an epic error-prone. */ export declare function updateEpic(input: UpdateEpicInput): Promise; export declare function getEpicIssues(input: GetEpicIssuesInput): Promise; export declare function resolveEpic(input: ResolveEpicInput): Promise; //# sourceMappingURL=sentinel.d.ts.map