/** * Declared-intent receipt + reconciliation for the auto-close enforcement seam * (mmnto-ai/totem#1762). * * D1 (PR-time required check) scans the PR corpus — title, description, and ALL * branch commit messages (config-verified: the governed repos compose squash * bodies from `COMMIT_MESSAGES`, so every branch commit is a squash-seed input) * — for close-keyword-adjacent refs via {@link findAutoCloseRefs}, and fails on * any ref that is not AUTHORIZED. * * AUTHORIZATION IS PROVENANCE-DISTINCT (codex #3 — the circularity fix). GitHub * DERIVES `closingIssuesReferences` FROM the PR body's own close keywords, so a * body keyword would self-whitelist against it. The ONLY authorizing channel is * therefore the provenance-distinct `totem-close` marker (an HTML comment or a * `Totem-Close:` trailer the author writes — see {@link parseDeclaredCloseIntent}). * `closingIssuesReferences` is recorded on the receipt as OBSERVED GitHub state * (informational), never as an authorization. Author workflow: declare every * intended close with the marker. * * D2 (post-merge reconciliation, OBSERVATION MODE) compares the merged HEAD * commit message against the receipt via {@link reconcile}. It alerts loud — * never auto-reopens — until positive+negative controls arm enforcement (the * Tenet 9 sense→enforce gate). * * Never scan issue/PR COMMENT bodies anywhere — comments never auto-close. */ export declare const AUTO_CLOSE_RECEIPT_SCHEMA_VERSION = 2; /** * A GitHub `closingIssuesReferences` node (from the PR GraphQL/REST API): the * issue GitHub itself recognizes as a linked closing reference for the PR. * OBSERVED state — informational only, NEVER authorizing (GitHub derives it from * body keywords). */ export interface ClosingIssueRef { number: number; /** `owner/repo` the issue lives in, when cross-repo; omitted for same-repo. */ repoWithOwner?: string; } /** * The durable D1→D2 receipt. `declaredByMarker` is the AUTHORIZING set (marker * provenance only); `closingIssuesReferences` is OBSERVED GitHub state recorded * for the audit trail but not used to authorize. Persisted as a GitHub Actions * artifact keyed to the PR (see the D1 workflow script). */ export interface AutoCloseReceipt { schemaVersion: number; /** `owner/repo` the PR targets. */ repo: string; prNumber: number; /** PR head SHA at D1 time (lets D2 correlate the artifact to the merge). */ headSha: string; /** * AUTHORIZING set: normalized keys declared via the provenance-distinct * `totem-close` marker/trailer ONLY. reconcile authorizes against THIS set. */ declaredByMarker: string[]; /** * INFORMATIONAL: GitHub's derived `closingIssuesReferences` (normalized keys). * Recorded for the audit trail; NOT authorizing (breaks the self-whitelist * circularity — codex #3). */ closingIssuesReferences: string[]; /** Normalized keys the D1 corpus scan found (audit only). */ corpusFindings: string[]; generatedAt: string; note: string; } /** A structured-intent reference the author explicitly declared. */ export interface DeclaredIntentRef { qualifier?: string; issue: number; } /** * Parse the structured-intent declarations out of `text`. Returns the refs the * author explicitly whitelisted for closure. Does NOT interpret close keywords — * a marker carries bare/qualified refs only. */ export declare function parseDeclaredCloseIntent(text: string): DeclaredIntentRef[]; /** The D1 corpus surfaces (never includes comment bodies). */ export interface PrCorpus { title: string; body: string; commitMessages: string[]; closingIssuesReferences: ClosingIssueRef[]; repo: string; } /** Result of the D1 corpus scan. `ok` iff nothing unauthorized was found. */ export interface PrScanResult { ok: boolean; /** Normalized keys found across the corpus. */ findings: string[]; /** AUTHORIZING set (marker-declared only) — the receipt payload. */ declaredByMarker: string[]; /** INFORMATIONAL: GitHub's derived closingIssuesReferences (normalized keys). */ closingIssuesReferences: string[]; /** Findings not authorized by the marker set — these fail the check. */ undeclared: string[]; } /** * D1: scan the PR corpus (title + body + every branch commit message) for * close-keyword-adjacent refs and split them into authorized vs undeclared. A * finding is authorized ONLY by the provenance-distinct `totem-close` marker — * NOT by GitHub's `closingIssuesReferences` (which GitHub derives from the same * body keywords, so it would self-whitelist; codex #3). Comment bodies are NEVER * part of the corpus. */ export declare function scanPrCorpus(corpus: PrCorpus): PrScanResult; /** Assemble the durable D1 receipt from a corpus scan. */ export declare function buildReceipt(corpus: Pick, prNumber: number, headSha: string, scan: PrScanResult, now?: Date): AutoCloseReceipt; export type ReconcileStatus = 'clean' | 'anomaly' | 'missing-receipt' | 'ambiguous-receipt' | 'unexpected-body'; export interface ReconcileResult { status: ReconcileStatus; /** Normalized keys found in the merged commit message. */ findings: string[]; /** Findings not covered by the receipt's authorizing set (the anomaly set). */ undeclared: string[]; /** * OBSERVATION MODE: the issues an armed enforcer WOULD reopen. Reported for the * audit trail; D2 never acts on it (no auto-reopen until controls pass). */ reopenCandidates: string[]; /** * Whether the merged commit carried a non-empty BODY after RFC-822 trailer * lines are stripped (mmnto-ai/totem#1762 addendum + the 0330Z trailer-strip * fold-in). Under the E lever (squash message = BLANK) the body should be * empty; a non-empty non-trailer body is the posture signal (`unexpected-body`). */ bodyPresent: boolean; /** Precise operator-facing message for the job log. */ message: string; } /** Options for {@link reconcile}. */ export interface ReconcileOptions { /** `owner/repo` the merge landed on (enables same-repo key equivalence). */ repo?: string; /** Expected PR number — a receipt for a different PR is ambiguous. */ expectedPrNumber?: number; } /** * D2: reconcile the merged HEAD commit message against the D1 receipt. * OBSERVATION MODE — every non-clean outcome ALERTS (the caller decides exit * code); NONE reopens. * * - `clean` — no close-keyword-adjacent ref, or every ref is * authorized. Quiet path (empty / trailer-only body). * - `anomaly` — a closure-capable message with ≥1 UNAUTHORIZED ref. * The zero-allowed-set (`declaredByMarker: []`) + a * closure-capable message is the #2471 specimen. * - `missing-receipt` — a closure-capable message but NO receipt (PR merged * before D1 existed, or the artifact expired / * could not be downloaded). Alert, never guess. * - `ambiguous-receipt`— a closure-capable message but the receipt is malformed * or is for a different PR. Alert, never guess. * - `unexpected-body` — a non-empty non-trailer body under BLANK with NO * unauthorized ref: posture-drift / `--body`-override * evidence (no closure harm). The caller surfaces it as * a non-failing signal (interpretation call). */ export declare function reconcile(receipt: AutoCloseReceipt | null, mergedBody: string, opts?: ReconcileOptions): ReconcileResult; //# sourceMappingURL=receipt.d.ts.map