ci: exempt designated authors from readiness declaration

This commit is contained in:
morluto committed 2026-11-03 02:18:55 +08:00
1 parent 55078b9f57
commit 920d634e83
6 files changed
+82 -8

No files matched your search

+2 -1
View File
@@ -106,7 +106,8 @@ limits, security/privacy/containment implications, and meaningful proof gaps. --
## Author readiness
<!-- The human owner completes these declarations before requesting review.
<!-- The human owner completes these declarations before requesting review,
unless the PR author is exempt as listed in CONTRIBUTING.md.
The PR author is the owner by default. A different owner must explicitly accept
ownership on this PR.
AI agents must leave these declarations to a human to dictate to them or there could be catastrophic consequences for many users of REA.
+2 -2
View File
@@ -128,8 +128,8 @@ owner to complete. Never attest to human review on their behalf. Draft status
is optional; preserve the PR's current draft status unless the user asks to change
it. Later pushes do not invalidate the readiness declaration or require another
human sign-off. Keep the PR body and affected validation current as work changes.
The readiness check validates
the filled declaration; CI and automated review results do not establish that
The readiness check validates the filled declaration or the author exemption
listed in CONTRIBUTING.md; CI and automated review results do not establish that
human review occurred, maintainer scope approval, or merge permission.
<!-- BEGIN:turborepo-agent-rules -->
+8 -4
View File
@@ -93,7 +93,8 @@ Before requesting maintainer review, the human owner must:
passing. Follow the [verification requirements](#development-feedback-and-pr-verification)
for required checks and real-provider coverage.
- Complete the **Author readiness** declaration in the PR template, explicitly
stating that the PR is ready for initial review.
stating that the PR is ready for initial review, unless the PR author is
exempt from the declaration as listed below.
Keep the PR description concise and current. Explain the problem, observable
outcome, approach, and useful evidence; link long logs or put them in optional
@@ -114,9 +115,12 @@ validation current as work changes. AI agents must leave human declarations to
the human owner and must not attest to a human review on their behalf.
The `PR readiness` check reports pending for drafts and requires a named human
owner and all three template declarations for non-drafts. It checks that the
declaration is filled, not whether the code or evidence is correct. Existing PRs
can copy the **Author readiness** section from the current template.
owner and all three template declarations for non-drafts. PRs authored by
@morluto, @kaoru0822-kitauji, @N0zoM1z0, or @gensokyo-dev are exempt from
the declaration; their human ownership, review, and evidence responsibilities
above still apply. The check identifies authors by GitHub account ID. It checks
that the declaration is filled, not whether the code or evidence is correct.
Existing PRs can copy the **Author readiness** section from the current template.
Open PRs must have distinct head commits because GitHub shares commit statuses.
Maintainers enable merge enforcement after the workflow is merged: run
+2 -1
View File
@@ -3,6 +3,7 @@ export interface ReadinessPullRequest {
readonly state: "open" | "closed";
readonly draft: boolean;
readonly body: string | null;
readonly user: { readonly id: number } | null;
readonly head: { readonly sha: string };
readonly base: { readonly ref: string };
}
@@ -53,7 +54,7 @@ export interface ReadinessGitHub {
>;
}
export function evaluateReadiness(
pull: Pick<ReadinessPullRequest, "body" | "draft">,
pull: Pick<ReadinessPullRequest, "body" | "draft" | "user">,
template: string,
): ReadinessResult;
export function runReadinessCheck(input: {
+14
View File
@@ -2,6 +2,14 @@ import { readFile } from "node:fs/promises";
const result = (state, description) => ({ state, description });
// GitHub account IDs remain stable when a login changes.
const EXEMPT_AUTHOR_IDS = new Set([
76467478, // morluto
208281322, // kaoru0822-kitauji
161784452, // N0zoM1z0
342580529, // gensokyo-dev
]);
const OPEN_HEADS_QUERY = `query ReadinessOpenHeads($owner: String!, $name: String!, $after: String) {
repository(owner: $owner, name: $name) {
pullRequests(first: 100, after: $after, states: OPEN, orderBy: {field: CREATED_AT, direction: ASC}) {
@@ -105,6 +113,11 @@ export function evaluateReadiness(pull, template) {
"pending",
"Draft PR: complete Author readiness before requesting review.",
);
if (EXEMPT_AUTHOR_IDS.has(pull.user?.id))
return result(
"success",
"PR author is exempt from the readiness declaration.",
);
const expected = readinessLines(template).flatMap((line) => {
const match = /^- \[ \] (.+)$/u.exec(line);
return match ? [match[1]] : [];
@@ -210,6 +223,7 @@ export async function runReadinessCheck({ github, context, core }) {
latest.head.sha !== pull.head.sha ||
latest.body !== pull.body ||
latest.draft !== pull.draft ||
latest.user?.id !== pull.user?.id ||
latest.base.ref !== pull.base.ref
)
verdict = result(
@@ -30,6 +30,7 @@ const makePull = (
state: "open",
draft: false,
body: readyBody,
user: { id: 123456 },
head: { sha: "a".repeat(40) },
base: { ref: "main" },
...changes,
@@ -94,6 +95,30 @@ function fixture(changes: Partial<ReadinessPullRequest> = {}) {
}
describe("initial author declaration", () => {
it.each([76467478, 208281322, 161784452, 342580529])(
"exempts the configured author ID %s, but keeps drafts pending",
(id) => {
expect(
evaluateReadiness(makePull({ user: { id }, body: null }), template),
).toMatchObject({
state: "success",
description: expect.stringContaining("exempt"),
});
expect(
evaluateReadiness(
makePull({ user: { id }, body: null, draft: true }),
template,
).state,
).toBe("pending");
},
);
it("does not exempt unknown or missing author identities", () => {
for (const user of [{ id: 123456 }, null])
expect(
evaluateReadiness(makePull({ user, body: null }), template).state,
).toBe("failure");
});
it("accepts the filled current template and uppercase checkmarks", () => {
expect(evaluateReadiness(makePull(), template).state).toBe("success");
expect(
@@ -212,6 +237,35 @@ describe("initial author declaration", () => {
});
describe("GitHub status boundary", () => {
it.each([76467478, 208281322, 161784452, 342580529])(
"publishes a labeled exemption for author ID %s after checking the head",
async (id) => {
const input = fixture({ user: { id }, body: null });
expect(await runReadinessCheck(input)).toMatchObject({
state: "success",
description: expect.stringContaining("exempt"),
});
expect(input.paginate).toHaveBeenCalledOnce();
expect(input.createCommitStatus).toHaveBeenLastCalledWith(
expect.objectContaining({ state: "success", sha: input.pull.head.sha }),
);
},
);
it("does not let an exempt author lend success to another PR sharing the head", async () => {
const input = fixture({ user: { id: 76467478 }, body: null });
input.paginate.mockResolvedValue([input.pull, makePull({ number: 43 })]);
expect((await runReadinessCheck(input))?.state).toBe("failure");
});
it("does not publish an exemption if the author changes during the check", async () => {
const input = fixture({ user: { id: 76467478 }, body: null });
input.get
.mockResolvedValueOnce({ data: input.pull })
.mockResolvedValueOnce({
data: makePull({ user: { id: 123456 }, body: null }),
});
expect((await runReadinessCheck(input))?.state).toBe("pending");
});
it("publishes failure on the actual PR head when declarations are unchecked", async () => {
const input = fixture({ body: readyBody.replaceAll("[x]", "[ ]") });
expect((await runReadinessCheck(input))?.state).toBe("failure");