import type { GhPR, GhCheck, GhComment } from "./gh.js"; import type { MergePolicy } from "./config.js"; export type CiState = "passing" | "failing" | "pending" | "none"; /** * Every PR state, **in ladder order, highest first** — the same order * `classifyPR` decides in and `loop-mode.md` § "Reconcile playbook" documents. * * A VALUE, not only a type, so a consumer that must handle all of them (the * inbox's icon map) can be pinned to the list instead of quietly rendering a * bare dot for a state someone added in one file. */ export declare const PR_STATES: readonly ["reporter_corrected", "awaiting_reporter", "conflict", "ci_failing", "changes_requested", "review_comments", "ci_pending", "approved_ready", "stale", "awaiting_review"]; export type PRState = (typeof PR_STATES)[number]; /** The `reasons` token for an intent-gated PR (#402) — the same string as the * label the server adds/removes, so the row names the exact thing to look for. */ export declare const REPORTER_REVIEW_REASON: "needs-reporter-review"; /** The `reasons` token that says a fresh reporter CORRECTION is sitting on a * gated PR (#442). It rides beside `REPORTER_REVIEW_REASON`, never instead of * it — the gate is still armed; only the dispatch changed. */ export declare const REPORTER_CORRECTION_REASON = "reporter_correction"; /** The `reasons` token on a gated PR that has a correction the loop may no * longer act on: `max-fix-attempts` reworks have already been spent (#442). * Visible, not silent — a correction that stops being actioned must say so, or * this fix trades park-forever for rework-then-park-forever. */ export declare const REWORK_CEILING_REASON = "rework_ceiling"; /** The `reasons` token on a gated PR carrying human-shaped comments the * detector REFUSED to read, because the PR has no legible loop trail * (`correctionTrailUnreadable`). A visible refusal, never a silent one. */ export declare const CORRECTION_UNREADABLE_REASON = "correction_unreadable"; /** The `reasons` token on a gated PR whose gate has simply STOOD — past * `stale-pr-hours`, with no reporter answer of any kind (#439). * * The ladder ranks `awaiting_reporter` (index 1) far above `stale` (index 8) * and the `intentBlocked` branch short-circuits before the `ageHours >= * staleHours` test ever runs, so a gated PR could never age into `stale`. With * `needsAction` excluding `awaiting_reporter`, that left an intent-gated PR * with **no aging path and no exit** but a human comment the loop cannot * cause — measured live on PRs #401/#418, gated + CONFLICTING and silent for * days. * * The ladder is NOT reordered to fix that, and no `awaiting_reporter_stale` * state is added. Reordering re-admits dispatch on an unconfirmed * interpretation, which is the whole point of #436/#402; a new state * duplicates the parked/WIP/summary plumbing for no new routing. And `stale`'s * own documented handler is "nudge the PR" — a bare `gh pr comment`, i.e. the * unmarked author-side comment that manufactures a phantom correction (#477). * So the gate ages into the mechanism that already exists and is proven: * escalate ONCE, then re-park. */ export declare const REPORTER_GATE_STALE_REASON = "reporter_gate_stale"; /** The three refusals whose documented handler is `issue escalate` ONCE — the * loop may not rework, and it may not silently drop the thread either * (loop-mode.md § "A reporter correction", the `Ceiling`, * `correction_unreadable` and gate-stale rows). * * All land on a PARKED `awaiting_reporter` row, and Phase A iterates on * `needsAttention` — so as shipped the escalation the doc requires could never * be dispatched from a row, and the ceiling case ended as the exact * rework-then-park-forever this issue set out to remove (PR #446 review, * finding 6). The state is right and stays: what changed is that the ROW is * actionable while the escalation is still owed. Once-ness is decided by the * parent issue's `needs-human` label — data the inbox already fetches — so the * row falls back to parked the moment the escalation exists (`inbox.ts`). * * ⚠️ Once-ness is NO LONGER decided by the parent's `needs-human` label (issue * #488): the server removes that label on any human reply, so the key it was * standing in for was erased by design and the row re-escalated every tick. * The key is now a per-(PR, reason) comment marker — which also sets the cap: * a PR can earn AT MOST one escalation per entry in this list, ever. */ export declare const ESCALATE_ONCE_REASONS: readonly string[]; /** Does this row still OWE an escalation? True on a parked gated PR carrying * one of `ESCALATE_ONCE_REASONS`. Says nothing about whether one has already * been filed — that is the caller's half (it needs the parent's comments). */ export declare function owesEscalation(cl: Pick): boolean; /** WHICH escalate-once reasons this row carries — each one the second half of * its own once-key, and the value the orchestrator passes back as * `issue escalate --once-reason`. * * A LIST, not one reason (issue #488): a row can carry more than one refusal, * and each earns exactly one escalation. Returning only the first would park * the row for good the moment that first key was filed, swallowing the second * refusal — so the caller walks this list and takes the first entry with no * marker on file. Always in `ESCALATE_ONCE_REASONS` order so the same tick * cannot file under a different key each time (which would make "once" * unenforceable). Empty when the row owes nothing. */ export declare function escalationReasonsOwed(cl: Pick): string[]; /** Per-check policy. `ciStateOf` rolls this up; do not add a fourth matcher. */ export declare function classifyCheck(c: GhCheck): CiState; /** Collapse the status-check rollup into one CI verdict. */ export declare function ciStateOf(checks: GhCheck[] | undefined): CiState; /** * Why an open PR may need follow-up. Empty = nothing to do. "review_comments" is * advisory (the comment may already be addressed); "changes_requested" and * "ci_failing" are definite. */ export declare function prAttentionReasons(pr: GhPR, me: string): string[]; /** The newest comment on an issue if it's from someone other than `me` (i.e. you * may owe a reply); otherwise null. */ export declare function issueNeedsReply(comments: GhComment[], me: string): GhComment | null; /** Does this PR carry a `shipflow-approved`-style label? The label alone is * not approval (issue #637) — `isApproved` also requires a matching head SHA. */ export declare function hasApprovalLabel(pr: Pick): boolean; /** 40-char lowercase hex, or null. Missing / short / garbage fails closed. */ export declare function commitSha(value: unknown): string | null; /** OPEN token `pr approve` stamps on the attestation comment (issue #637). */ export declare const APPROVED_HEAD_MARKER: "` in the literal) matched with `includes`, like `precedent-applied`. */ export declare const REWORK_FROM_MARKER: "