import type { PrerequisiteCheckResult, RequestType } from './artifact-prerequisites.js'; import type { RequestArtifactRole } from './artifact-templates.js'; export type RequestArtifactState = 'draft' | 'confirmed-by-user' | 'direction-locked' | 'spec-locked' | 'implemented' | 'qa-handoff' | 'running' | 'verdict-issued' | 'impact-recorded' | 'boundary-recorded' | 'handed-off' | 'blocked'; export declare const ALLOWED_STATES_PER_ROLE: Record>; export declare function allowedStatesForRole(role: RequestArtifactRole): ReadonlyArray; export declare class PrerequisitesNotSatisfiedError extends Error { readonly code = "PREREQUISITES_MISSING"; readonly role: RequestArtifactRole; readonly newState: RequestArtifactState; readonly sessionId: string; readonly missing: PrerequisiteCheckResult['missing']; /** * entries. Surfaced in the CLI error response under `data.warnings` * so the operator can see which prereqs were soft-blocked under * the 1-minor-release back-compat window (e.g. MUT_REPORT). Always * present (possibly empty array) to keep the response shape stable. */ readonly warnings: PrerequisiteCheckResult['warnings']; constructor(role: RequestArtifactRole, newState: RequestArtifactState, sessionId: string, missing: PrerequisiteCheckResult['missing'], warnings?: PrerequisiteCheckResult['warnings']); } /** The diagnosis a lint refusal carries: the `line`, `text` and `reason` the linter * computed for each finding and this error previously kept only a count of. */ export type LintGateFinding = { line: number; text: string; reason: string; }; export declare class LintGateError extends Error { readonly code = "LINT_GATE_FAILED"; readonly role: RequestArtifactRole; readonly newState: RequestArtifactState; readonly errorCount: number; readonly findings: ReadonlyArray; readonly path: string; constructor(role: RequestArtifactRole, newState: RequestArtifactState, findings: ReadonlyArray, path: string); } export declare class TypeSanityViolationError extends Error { readonly code = "TYPE_SANITY_VIOLATION"; readonly declaredType: RequestType; readonly suggestedTypes: ReadonlyArray; readonly rationale: string; constructor(declaredType: RequestType, suggestedTypes: ReadonlyArray, rationale: string); } export declare class FileSizeViolationError extends Error { readonly code = "FILE_SIZE_VIOLATION"; /** `previousLines` is the file's size at the scan's base ref, or `null` when * the change CREATED it over the cap. Carried so the message can name the * growth — a refusal that does not say how much worse the file got reads as * an absolute cap breach, which is the reading this gate no longer makes. * * The `violations` array also drives the sentence naming the AGGREGATE * ratchet row this gate is easily confused with. This gate is PER FILE; that * row is a sum over the census. In a working tree the two can disagree with * neither being wrong, and the message says so rather than leaving a reader * to conclude that one of them is broken. */ readonly violations: Array<{ file: string; lines: number; cap: number; previousLines: number | null; }>; constructor(violations: Array<{ file: string; lines: number; cap: number; previousLines: number | null; }>); } export interface ArtifactStateLocation { /** Index of the authoritative `- state:` line, or -1 when the document has none. */ stateLineIndex: number; /** The authoritative state, or null when `stateLineIndex` is -1. */ state: string | null; } /** * The ONE rule for "which `state:` line is this artifact's state": the LAST * one. A request artifact is an append-only log — each QA round appends a * section ending in its own `## Status`, and `request transition` rewrites the * newest state line in place — so the last line is the current round and the * earlier ones are history. * * Every reader (`verify-pipeline`, `request show`, the resume detector) and the * writer (`updateStatusBlock`) MUST go through this. The 2026-09-14 defect was * three readers disagreeing about one file: `verify-pipeline` and the resume * detector took the first match, `request show` took the last, so an artifact * appended to more than once read as its first round to the checker and the * resume detector while reading as its last round to the viewer and the writer. * * Scoping the search to the last `## Status` block was evaluated as an * alternative and rejected — as a SECOND locator, not as a broken rule. It is * well defined on the specimen that motivated this slice (four blocks) and * returns `verdict-issued`, the right answer; and a trailing appended * `- state:` line does not defeat it the way it defeats this rule — on that * input the two rules disagree, and the one the block rule then disagrees with * is the writer. `updateStatusBlock` rewrites the last `- state:` line wherever * it sits and never moves it into the newest block, so a block-scoped reader * parts company with the writer as soon as those two positions differ: the * writer writes the appended line while the block reader keeps reporting the * block's own line. That is this slice's reader-vs-writer divergence on a new * axis. Sharing the writer's locator makes the agreement structural, not * accidental. * * Known boundary of this rule, pinned in * `request-artifact-state-authority.test.ts` rather than hidden: a process that * appends a bare `- state:` line takes over the field — consistent with the * writer being its only sanctioned producer. * * Two narrowings keep that boundary to lines the writer could have produced. * A line inside a fenced code region is skipped, and the line must start at * column 0. Neither is defensive decoration: a document that *describes* the * state machine quotes `- state: qa-block` inside a fence, and this job's own * `qa/requests/*.md` artifacts do exactly that — under the unfenced rule the * quoted example was an input to the transition checker, and it survived only * because the quoted copies happened not to be last. Both narrowings were * measured against every `*.md` under `.peaks/` (833 files) and change no * artifact's answer, and the writer already satisfies both by construction * (`updateStatusBlock` writes `- state: ` at column 0), so reader and * writer stay the same locator. * * Fuller record: the slice-3 section of this session's `rd/tech-doc.md` and * the repair-round section of `rd/repair2-meta-integrity-fixes.md`. */ export declare function locateArtifactState(lines: ReadonlyArray): ArtifactStateLocation; /** `locateArtifactState` over a whole document. Null when there is no `- state:` line. */ export declare function readArtifactState(markdown: string): string | null; export declare function updateStatusBlock(markdown: string, newState: RequestArtifactState, timestamp: string, reason?: string): { updated: string; previousState: string; };