/** * Component PRs landed by an integration PR (T12671, T12672). * * A task's own PR (the COMPONENT) often merges into an integration branch, * whose PR (the INTEGRATION PR) then lands on the default branch. The * integration PR carries the merge commit on the default branch and the CI * that ran; the component PR carries the task linkage and the task's change. * `pr:@` and `ci:@` record * both, and this module is the one place that verifies the relationship and * derives the component's files: * * - the component PR merged into the integration PR's head branch; * - its merge commit is an ancestor of the integration PR's final head, so * the integration PR carried it; * - its files are the ones its merge commit changed that still exist in the * integration PR's merge commit — never the whole integration diff. * * A component that did not merge into the integration branch as a PR — closed * by hand after a `git merge --no-ff`, marked merged by GitHub, stacked on * another component, main-merged afterwards, or rebased into the batch — is * verified commit by commit instead (T12710): every commit of its own is in * the integration merge by ancestry, or by `git patch-id` when rebased, and * {@link findComponentLanding} follows it to that integration PR. * * @task T12671 * @task T12672 * @task T12710 */ /** One component PR as `gh pr view` reports it. */ export interface ComponentPrView { /** PR number. */ number: number; /** PR title. */ title: string; /** PR body. */ body: string; /** Head branch. */ headRefName: string; /** Branch it merged into. */ baseRefName: string; /** `MERGED`, `OPEN` or `CLOSED`. */ state: string; /** Merge commit, or null when unmerged. */ mergeCommitSha: string | null; /** Head branch tip the PR carried, when known (T12710). */ headRefOid?: string | null; /** The PR's commits, oldest first, when known (T12710). */ commits?: string[]; } /** Reads one PR; null when `gh` cannot. */ export type ViewComponentPr = (prNumber: number, root: string) => Promise; /** The integration PR that landed the component on the default branch. */ export interface LandingPr { /** Integration PR number. */ prNumber: number; /** Its head (integration) branch. */ headRefName: string; /** Its final head commit, when known. */ headRefOid: string | null | undefined; /** Its merge commit on the default branch. */ mergeCommitSha: string; } /** A verified component PR, or why it is not one. */ export type ComponentPrResolution = { ok: true; /** Component PR number. */ prNumber: number; /** Title, body and head branch: the task-linkage assertions. */ title: string; body: string; headRefName: string; /** Files the component changed that survive in the integration merge. */ files: string[]; /** Files the component deleted. */ deleted: string[]; } | { ok: false; reason: string; codeName: 'E_EVIDENCE_INSUFFICIENT' | 'E_EVIDENCE_CONTENT_MISMATCH' | 'E_EVIDENCE_TOOL_FAILED'; }; /** * Deadline for one git subprocess in component verification * (`CLEO_GIT_TIMEOUT_MS`, default 30 s). A read that outlives it is killed * and the verification refuses with `E_EVIDENCE_TOOL_FAILED` (T12710 review). * * @returns Milliseconds. * @task T12710 */ export declare function gitTimeoutMs(): number; /** * A git subprocess outlived {@link gitTimeoutMs} and was killed. * * @task T12710 */ export declare class GitTimeoutError extends Error { /** @param args - The git arguments that timed out. */ constructor(args: readonly string[]); } /** Default {@link ViewComponentPr}: `gh pr view `. */ export declare const defaultViewComponentPr: ViewComponentPr; /** * The component's change as it landed: first-parent name-status of its merge * (or squash) commit, keeping only the changes the landing merge commit still * carries (T12671 review HIGH) — a changed file whose blob in the landing * commit equals the component's resulting blob, and a deletion whose path is * absent from the landing commit. A path that merely exists is not enough: a * component reverted on the integration branch lands its paths unchanged. * * Limitation: a REBASE-merged component's `mergeCommit` is only the last * rebased commit, so earlier commits' files are not seen. That under-reports * the change and fails closed (fewer surviving files, possibly a refusal); * GitHub does not report the merge method needed to diff the whole range. * * @param root - Git checkout holding both commits. * @param componentMerge - The component PR's merge commit. * @param landingMerge - The integration PR's merge commit. * @returns Surviving changed files and the component's deletions. * @task T12672 */ export declare function componentLandedChanges(root: string, componentMerge: string, landingMerge: string): { files: string[]; deleted: string[]; }; /** * The deletions of a PR merged directly into its base (T14024): each changed * path the merge commit's tree lacks, split by whether HEAD lacks it too. * Reading the merged tree rather than one commit's diff works for merge, * squash and rebase merges alike, since the PR's changed paths come from its * file inventory. * * @param root - Git checkout holding the merge commit. * @param mergeCommitSha - The PR's merge commit. * @param changedPaths - The PR's complete changed-file inventory. * @returns The PR's deletions and those of them that exist again at HEAD; * both empty when the merge commit is not available locally or git times * out (fails closed). * @task T14024 */ export declare function directMergeDeletions(root: string, mergeCommitSha: string, changedPaths: readonly string[]): { deleted: string[]; restored: string[]; }; /** * The given paths whose change by `merge` (its first-parent diff) a later * commit still carries (T14025): the same rule as {@link componentLandedChanges} * — identical bytes, or own hunks that reverse-apply there and do not * re-apply — for an earlier `pr:` atom whose listed files are read at a later * PR's merge. Like that function it only sees the merge's first-parent diff, so * a rebase-merged PR under-reports and fails closed. * * @param root - Git checkout holding both commits. * @param merge - The earlier PR's merge (or squash) commit. * @param landing - The commit whose bytes are read (a later PR's merge). * @param paths - Repo-relative paths the earlier PR changed. * @returns The subset of `paths` still carried by `landing`; none on a git timeout. * @task T14025 */ export declare function changesCarriedBy(root: string, merge: string, landing: string, paths: readonly string[]): string[]; /** A refusal in the shape every component check returns. */ type Refusal = Extract; /** The integration PR's side of a commit-level check. */ export type LandingMerge = Pick; /** A component's change verified commit by commit against one landing merge. */ export type ComponentCommitsLanded = { ok: true; /** Newest component commit the landing carries (the original tip when rebased). */ tip: string; /** Files the component changed that survive in the landing merge. */ files: string[]; /** Files the component deleted that the landing merge lacks. */ deleted: string[]; } | Refusal; /** * Verify, commit by commit, that a landing merge carries a component PR's * change, whatever happened to the PR itself (T12710): closed by hand, marked * merged by GitHub, stacked on another component, main merged into its head * afterwards, or rebased into the batch. * * - Every non-merge commit of the PR is an ancestor of the landing merge, or * — when the batch was rebased — has the same `git patch-id` as a commit * the landing merge introduced (`^1..`). Git is the proof; * no PR body is consulted. * - At least one of them is new in the landing merge (not already on its * first parent), so the landing PR is the one that landed it. * - The credited files are only the paths the PR's own commits changed, and * only where the landing merge still carries the change (blob identical, * or the component's hunks reverse-apply and do not re-apply — T12689). * * @param root - Git checkout holding every commit. * @param c - The component PR, with its commit list. * @param landing - The integration PR number and its merge commit. * @returns The component's tip and surviving changes, or the refusal. * @task T12710 */ export declare function componentCommitsLanded(root: string, c: ComponentPrView, landing: LandingMerge): ComponentCommitsLanded; /** * The first commit on `ref`'s first-parent chain that contains `sha`: the * commit that landed it on the default branch. `sha` itself when it is on * that chain; null when `ref` does not contain it. * * @param root - Git checkout. * @param sha - A commit. * @param ref - The default branch ref, e.g. `origin/main`. * @returns The landing commit, or null. * @task T12710 */ export declare function landingCommitOn(root: string, sha: string, ref: string): string | null; /** A merged PR that may have landed a component (a candidate integration PR). */ export interface LandingCandidate { /** PR number. */ number: number; /** Head (integration) branch. */ headRefName: string; /** Branch it merged into. */ baseRefName: string; /** `MERGED`, `OPEN` or `CLOSED`. */ state: string; /** Final head commit, when known. */ headRefOid: string | null; /** Merge commit on the base branch, or null. */ mergeCommitSha: string | null; /** PR body (a hint only, never proof). */ body: string; } /** A bounded `gh` PR search: the PRs, or why it failed (offline, timeout). */ export type LandingSearch = { ok: true; prs: LandingCandidate[]; } | { ok: false; reason: string; }; /** `gh` lookups {@link findComponentLanding} needs (tests inject). */ export interface ComponentLandingDeps { /** Merged PRs whose merge commit may be `sha`. */ prsByMergeCommit?: (sha: string, root: string) => Promise; /** Merged PRs whose body may list `#` — a hint for rebased batches. */ prsListingComponent?: (componentPrNumber: number, root: string) => Promise; } /** Default {@link ComponentLandingDeps}: bounded `gh pr list` searches. */ export declare const defaultComponentLandingDeps: Required; /** The integration PR that landed a component, with the component's surviving changes. */ export type ComponentLanding = { ok: true; /** The integration PR (merged into the default branch). */ landing: LandingCandidate & { mergeCommitSha: string; }; /** Files the component changed that survive in the landing merge. */ files: string[]; /** Files the component deleted that the landing merge lacks. */ deleted: string[]; } | Refusal; /** * Follow a component PR to the PR that landed it on the default branch * (T12671, T12710). Resolution, git first: * * 1. Ancestry: the newest PR commit the default branch contains is landed by * the first commit on the default branch's first-parent chain containing * it; the merged PR whose merge commit that is, is the landing PR. When * that commit is the component's own merge, it landed directly: `null`. * 2. Rebased batch (no PR commit is on the default branch): merged PRs into * the default branch whose body lists `#` are candidates — a * hint only — and each is accepted only through patch-id proof. * * Either way {@link componentCommitsLanded} is the proof: every commit of the * component is in the landing merge, and only its surviving files are * credited. A `gh` failure (offline, timeout) is a refusal, never a hang. * * @param root - Git checkout with the default branch fetched. * @param c - The component PR (with its commits). * @param defaultRef - The default branch ref, e.g. `origin/main`. * @param deps - `gh` lookups (tests inject). * @returns The landing PR and surviving changes, a refusal, or `null` when the * PR landed directly (its own merge is on the default branch). * @task T12710 */ export declare function findComponentLanding(root: string, c: ComponentPrView, defaultRef: string, deps?: ComponentLandingDeps): Promise; /** * Verify that `componentPrNumber` is a component the integration PR landed, * and derive its landed files. Two proofs, first match wins: * * - the component PR merged into the integration PR's head branch, whose * head contains its merge commit (T12671); * - otherwise every commit of its own is in the integration merge, by * ancestry or patch-id ({@link componentCommitsLanded}, T12710) — a * component closed by hand, marked merged by GitHub, stacked, main-merged * afterwards, or rebased into the batch. * * @param componentPrNumber - The task's own (component) PR. * @param landing - The integration PR that reached the default branch. * @param root - Git checkout (the execution root). * @param view - PR reader (tests inject). * @returns The component's linkage fields and files, or the refusal. * @task T12671 * @task T12710 */ export declare function resolveComponentPr(componentPrNumber: number, landing: LandingPr, root: string, view?: ViewComponentPr): Promise; export {}; //# sourceMappingURL=component-pr.d.ts.map