Skip to content

feat(ci): draft contributor PRs until review-readiness checklist is complete - #969

Merged
lidge-jun merged 9 commits into
devfrom
codex/pr-readiness-gate
Aug 4, 2026
Merged

feat(ci): draft contributor PRs until review-readiness checklist is complete#969
lidge-jun merged 9 commits into
devfrom
codex/pr-readiness-gate

Conversation

@Wibias

@Wibias Wibias commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Contributor PRs (authors without repository push permission) now open in draft and stay there until a four-box review readiness checklist in the PR description is complete: local CI green, branch on the latest dev commit, all correct Codex and CodeRabbit findings fixed, and the ready-for-review confirmation.
  • When all four boxes are ticked, enforce-target marks the PR ready for review and notifies the maintainers listed in MAINTAINERS.md (excluding the author). Wrong-base PRs are drafted as before; retargeting to dev updates the bot message and is remembered, but the draft stays until the checklist is complete.
  • The checklist is injected into the PR body once (idempotent markers), the checklist message is a separate bot comment under the wrong-branch/creation comment, and the maintainer ping happens once (state-tracked). Maintainers keep the previous failure-only contract; the enforce-target check itself stays green while only the checklist is pending.

Test plan

  • bun test tests/ci-workflows.test.ts — 81/81 pass (harness-executed scenarios: draft-on-open, retarget-remember, checklist completion, maintainer ping, wrong-base + complete checklist, permission-recovery restore and failure retention, corrupted-state guard, checkpointed draft ownership, and the existing enforcer audit cases).
  • node --test .github/scripts/enforce-pr-target.test.cjs .github/scripts/pr-quality.test.cjs — 58/58 pass (checklist extraction/injection/stripping, inverted/duplicate marker, per-item state, description-gate isolation).
  • bun run typecheck — pass; bun run lint:gui — pass; bun run privacy:scan — pass; doctor:gui:if-changed — skip (no gui change).
  • node --check on the extracted workflow script — pass; the script contains no ${{ }} interpolation and keeps the exact pull_request_target trigger/permission allowlist.
  • Full suite on this Windows machine: 7928 pass; the 16 failures are pre-existing symlink tests that require Windows Developer Mode/admin (verified: symlink creation refused on this machine; the same 6 tests/config.test.ts failures reproduce on an untouched baseline). One unrelated scan test timed out once under load and passes in isolation.
  • CI (GitHub Actions): Linux shards, gates, validator tests, keyring, and CodeRabbit green on the previous head; the macOS lane hit a Bun 1.3.14 runtime segfault once and passed on rerun (same crash class reproduced locally on Windows; recent dev runs on the identical base tip are green).

Review notes

  • The tickable boxes live in the PR description (only the author can edit it); the bot's checklist comment mirrors per-item state and carries the maintainer ping. The bot-managed section is stripped before description-quality assessment, so it can never satisfy the Summary/Test-plan gate on its own.
  • A failed permission lookup fails closed (PR treated as contributor); once the lookup recovers, the bot restores a maintainer PR's ready state and retires the injected checklist section from its body.
  • Malformed or inverted checklist markers are treated as present-but-incomplete, so an author edit can never make the bot append a second checklist.
  • Automation accounts such as Dependabot are non-collaborators and are drafted like any contributor PR — the checklist is self-attestation, so no bot-specific carve-out was added.
  • pull_request_target runs the workflow from the PR's base branch, so the trusted script checkout now uses github.event.pull_request.base.ref — after this lands on dev, the gate loads the matching dev scripts instead of the pre-promotion main ones.
  • Review rounds addressed: round 1 (Codex + CodeRabbit) in bc66a878/40b99b73; round 2 (Codex) in a62bc19 — description-gate isolation, recovery ownership retention, checkpointed draft ownership, maintainer checklist retirement, base-revision script checkout.

Limitations

  • Checklist confirmation is author self-attestation; CI status is not queried automatically.

Summary by CodeRabbit

  • New Features

    • Added a four-item review-readiness checklist for contributor pull requests.
    • Contributor pull requests remain drafts until all checklist items are complete.
    • Completed checklists automatically mark pull requests ready for review and notify maintainers.
    • Improved handling for retargeted pull requests, malformed checklist content, and workflow update failures.
    • Maintainer pull requests continue through quality checks without the contributor readiness gate.
  • Documentation

    • Documented contributor draft and checklist requirements in project guidance and contribution documentation.

@github-actions github-actions Bot added the enhancement New feature or request label Aug 3, 2026
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds a four-item contributor review-readiness checklist. The workflow manages checklist state, draft status, readiness comments, ready-for-review transitions, and maintainer notifications. Tests and documentation cover the new behavior.

Changes

Contributor review-readiness gate

Layer / File(s) Summary
Checklist contract and helpers
.github/scripts/pr-quality.cjs, .github/scripts/pr-quality.test.cjs
Adds marker constants, checklist generation, parsing, append and strip helpers, public exports, and unit coverage for valid and malformed states.
Workflow readiness state and transitions
.github/workflows/enforce-pr-target.yml
Adds trusted maintainer-file loading, checklist injection, readiness state and comments, contributor draft enforcement, ready-for-review transitions, failure handling, and maintainer notifications.
Contributor policy documentation
AGENTS.md, MAINTAINERS.md, docs-site/src/content/docs/contributing/pr-quality.md
Documents checklist requirements and contributor draft-to-ready behavior.
Workflow integration validation
tests/ci-workflows.test.ts, tests/helpers/enforce-pr-target-harness.ts, .github/scripts/enforce-pr-target.test.cjs
Covers checklist states, permissions, mutation ordering, failures, notifications, malformed state, base checkout, sparse checkout, and restricted filesystem access.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Contributor
  participant enforce_pr_target
  participant GitHub
  participant MAINTAINERS
  Contributor->>GitHub: Update PR checklist
  enforce_pr_target->>GitHub: Read PR body and permissions
  enforce_pr_target->>MAINTAINERS: Read maintainer logins
  enforce_pr_target->>GitHub: Update checklist, comments, and draft state
  GitHub-->>MAINTAINERS: Send completion notification
Loading

Possibly related PRs

Suggested reviewers: ingwannu, lidge-jun

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main CI change: keeping contributor pull requests in draft status until the review-readiness checklist is complete.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/pr-readiness-gate

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 08923a5fba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

if (!storedState?.active) {
if (!storedState?.active && !checklistRequired) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore drafts after a permission lookup recovers

When getCollaboratorPermissionLevel transiently fails for a clean maintainer PR, the fail-closed contributor path injects the checklist and converts the PR to draft; on a later successful run, checklistRequired becomes false and this early return ignores storedReadinessState.autoDraftedByBot, leaving the maintainer PR permanently drafted by the bot. Include the readiness ownership state in the restoration decision before returning so authors with push permission retain the documented failure-only behavior.

AGENTS.md reference: AGENTS.md:L181-L186

Useful? React with 👍 / 👎.

Comment thread .github/workflows/enforce-pr-target.yml Outdated
screenshotFailed: false
};

const titleResult = storedState.titlePrefixedByBot

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle unreadable enforcer state before completing readiness

When a contributor completes the checklist while an existing bot comment has missing or malformed JSON state, parseState returns null but botComment still makes this block run, so dereferencing storedState.titlePrefixedByBot throws and the workflow never marks the PR ready or notifies maintainers. Use optional access or synthesize a cleared state before reading these fields, as the failure path already does.

AGENTS.md reference: AGENTS.md:L181-L185

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/pr-quality.cjs:
- Around line 243-246: Update appendReviewReadinessSection’s marker validation
so any existing but malformed REVIEW_READINESS_START/REVIEW_READINESS_END
markers are reported as present and incomplete, preventing another checklist
from being appended; only bodies with exactly one correctly ordered pair should
be treated as absent or complete. Add regression coverage for inverted and
duplicate markers, asserting appendReviewReadinessSection returns each body
unchanged.

In @.github/workflows/enforce-pr-target.yml:
- Around line 859-897: Resolve the maintainer list with readMaintainerLogins()
before updating readinessState in the checklist-complete flow. Set
maintainersPinged and notified to true only when maintainers.length is greater
than zero, so an empty or failed lookup leaves the state unclaimed and allows a
later run to notify maintainers.
- Around line 806-855: Guard the `storedState` property reads in `titleResult`
and `draftResult` within the `botComment` completion block, matching the
existing optional-chaining usage so missing or unparsable state preserves the
fallback messages without throwing. Add a regression case in
`tests/ci-workflows.test.ts` covering a contributor PR with a complete checklist
and a marker-only or corrupted bot comment, asserting the workflow completes
successfully.
- Around line 183-207: Update extractReviewReadiness to return per-item checked
state, then change readinessChecklistLines to use each item’s corresponding
state rather than comparing index with readiness.checked. Preserve the existing
aggregate checked/total values and the fixed 4/4 completion text in
upsertReadinessComment.
- Around line 161-181: Update readMaintainerLogins to extract only the text
between the “## Current maintainers” heading and the next “##” heading, then
apply matchAll to that section instead of the full file. Preserve the existing
login deduplication and error-handling behavior.

In `@docs-site/src/content/docs/contributing/pr-quality.md`:
- Around line 43-50: Update the maintainer-notification statements in
docs-site/src/content/docs/contributing/pr-quality.md lines 43-50, AGENTS.md
lines 181-185, and MAINTAINERS.md lines 28-33 to specify that notifications go
to maintainers listed in MAINTAINERS.md, excluding the pull request author.

In `@tests/ci-workflows.test.ts`:
- Around line 12-36: Update lastEnforcerCommentBody and lastReadinessCommentBody
to explicitly detect when no matching comment exists before accessing the final
entry. Throw a descriptive error that identifies the missing enforcer or
readiness comment family, while preserving the existing behavior of returning
the latest matching update or creation.

In `@tests/helpers/enforce-pr-target-harness.ts`:
- Line 529: The harness must stop exposing the real filesystem and use a
recorded read-only stub instead. In tests/helpers/enforce-pr-target-harness.ts
at lines 529-529, remove node:fs from ALLOWED_MODULES and update scopedRequire
to allow only MAINTAINERS.md under process.cwd(), honoring
options.maintainersFile; in tests/ci-workflows.test.ts at lines 1196-1199,
provide a fixture roster through that override or derive expected logins from
MAINTAINERS.md, removing the hardcoded roster assertions at both referenced
locations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2cdb4d2f-b53a-4022-b167-7097f1bc2b12

📥 Commits

Reviewing files that changed from the base of the PR and between a088e4b and 08923a5.

📒 Files selected for processing (8)
  • .github/scripts/pr-quality.cjs
  • .github/scripts/pr-quality.test.cjs
  • .github/workflows/enforce-pr-target.yml
  • AGENTS.md
  • MAINTAINERS.md
  • docs-site/src/content/docs/contributing/pr-quality.md
  • tests/ci-workflows.test.ts
  • tests/helpers/enforce-pr-target-harness.ts

Comment thread .github/scripts/pr-quality.cjs Outdated
Comment thread .github/workflows/enforce-pr-target.yml
Comment thread .github/workflows/enforce-pr-target.yml
Comment thread .github/workflows/enforce-pr-target.yml
Comment thread .github/workflows/enforce-pr-target.yml
Comment thread docs-site/src/content/docs/contributing/pr-quality.md Outdated
Comment thread tests/ci-workflows.test.ts
Comment thread tests/helpers/enforce-pr-target-harness.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/enforce-pr-target.test.cjs:
- Around line 79-81: Update the failure-block regex assertion in the test to
anchor on the quality-failure branch, using `if (failures.length > 0) {` or
otherwise validating both `core.setFailed(` call sites, so call reordering
cannot select the checklist conversion path. Rename the `"workflow must have a
draft path"` assertion message to identify the quality-failure path.

In `@tests/ci-workflows.test.ts`:
- Around line 1196-1204: Update the failure test around lastReadinessCommentBody
and result.warnings to filter setFailed: warnings, assert exactly one matching
entry, and verify that entry contains the fail-closed message. Add a
complete-checklist case using a bot comment with LEGACY_COMMENT_MARKER and no
parseable state, then guard the enforce-pr-target workflow path after parseState
returns null so storedState is not dereferenced.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7bec0bae-14b4-422b-941e-7d7ac8f8b583

📥 Commits

Reviewing files that changed from the base of the PR and between 08923a5 and 7006ea5.

📒 Files selected for processing (3)
  • .github/scripts/enforce-pr-target.test.cjs
  • .github/workflows/enforce-pr-target.yml
  • tests/ci-workflows.test.ts

Comment thread .github/scripts/enforce-pr-target.test.cjs
Comment thread tests/ci-workflows.test.ts
@Wibias

Wibias commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator Author

[GD] Addressed feedback

feedbacks:

  • review_comment:3708080650
  • review_comment:3708080653
  • review_comment:3708084654
  • review_comment:3708084657
  • review_comment:3708084661
  • review_comment:3708084665
  • review_comment:3708084698
  • review_comment:3708084714
  • review_comment:3708084718
  • review_comment:3708084723
  • review_comment:3708107004

commit: 40b99b7

@Wibias
Wibias marked this pull request as draft August 3, 2026 22:38
@Wibias

Wibias commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator Author

[GD] Verdict: gated

PR: #969 — feat(ci): draft contributor PRs until review-readiness checklist is complete
Head: 40b99b73 on dev (mergeStateStatus: CLEAN, mergeable)

Semantic propagation

  • Concepts audited: PR draft ownership; the four-box review-readiness checklist contract; the maintainer notification list; the enforcer state machine.
  • Authoritative sources: .github/scripts/pr-quality.cjs (checklist markers + items), MAINTAINERS.md (read at runtime, only the ## Current maintainers section), .github/AGENTS.md (workflow rules), docs.
  • Producers and consumers checked: workflow script (inject/parse/convert/comment), PR authors (tick boxes), maintainers (ping), harness + unit tests that mirror the contract.
  • Public/derived representations checked: PR body checklist == bot comment mirror (per-item state) == docs text == test fixtures; ping list == MAINTAINERS.md current-maintainers table minus author; marker/state names consistent between script and workflow.
  • Material variant partitions checked: maintainer vs contributor; dev vs wrong base; checklist absent/partial/complete/over-complete/inverted; permission-lookup failure vs recovery; draft/ready conversion failure vs success; corrupted state marker.
  • Positive and negative assertions checked: extra/fewer boxes stay incomplete; inverted or duplicate markers are present-but-incomplete and never re-appended; unticked-after-complete re-drafts; no ${{ }} interpolation; no PR-head checkout; fs access restricted to a recorded MAINTAINERS.md read.
  • Unmapped surfaces: none — translated docs checked (pr-quality.md exists only in English).
  • Unproven equivalence assumptions: none.
  • Representation mismatches: none.
  • Variant coverage gaps: none.
  • Axis verdict: pass

Linked: none

Usefulness

Real gap closed: contributor PRs could previously be marked ready for review with unresolved Codex/CodeRabbit findings or no proof of local CI, while the repo's own contract defines ready as a claim that the change is complete, understood, and tested. The gate makes that claim explicit and mechanical for non-maintainer authors.

Bugs / correctness

  • Method: bug-review.md — Bugbot: n/a-unavailable (Codex host); complementary: done (silent_failures / resource_leaks / edge_cases / state_consistency / error_propagation lenses).
  • Findings: 3 confirmed, all fixed this session — checklist-path draft conversion failure left the PR ready with a green check (bc66a878, fail closed + regression); storedState null deref on a corrupted enforcer comment (40b99b73); maintainer PR drafted during a permission-lookup failure never restored (40b99b73).
  • Fixed this session: bc66a878, 40b99b73.

Security

  • Scope reviewed: CI / GitHub Actions trust boundary (pull_request_target, no PR-head checkout or execution), authz (collaborator permission lookup, fail-closed), injection (no ${{ }}; mentions constrained by the maintainer regex; author-controlled body only written back to the author's own PR), supply chain (SHA-pinned actions unchanged; trusted default-branch sparse checkout incl. MAINTAINERS.md), secrets/logging (state markers carry booleans/version only).
  • Findings: none confirmed.
  • Decision: Pass — no Critical/High; required surfaces covered (authz, injection, business_logic, secrets_config, supply_chain, logging_privacy; authn/uploads_files/data_storage/iac_docker n/a with reasons).

Spec / standards

  • Spec source: user request (this conversation). All requirements implemented: auto-draft non-collaborator PRs; wrong-base drafting with the retarget message edited and remembered; the only-mark-ready instruction text; a separate checklist message under the wrong-branch/creation message; four boxes (final box set off by a blank line); requirements statement above the boxes; auto-ready + maintainer ping on completion.
  • Standards: .github/AGENTS.md satisfied (minimal permissions, SHA pins, no PR-head checkout, docs sync); root AGENTS.md + MAINTAINERS.md branch-enforcement text updated in the same PR; tests at both layers (harness + node unit).

Reviews

  • Owners/maintainers: none open.
  • Bots: CodeRabbit round + Codex Review triaged — 11 findings fixed in bc66a878/40b99b73, 1 trivial declined with rationale in-thread (#discussion_r3708147563); aggregated record posted ([GD] Addressed feedback, head marker). 2 Codex threads remain open (both fixes verified; resolution requires explicit instruction per the active review mode).

Base / CI

  • Behind/conflicts: updated from dev in 7006ea5a3 (merge of a088e4b1c) — clean, no conflicts.
  • Required checks: green on 40b99b73 — all 4 Linux shards, macos (passed on rerun after a Bun 1.3.14 runtime segfault; identical segfault class reproduced locally on Windows and the same base tip is green on dev runs), gates, validator tests, enforce-target, keyring, react-doctor, label.
  • Local tip compile/tests: bun test tests/ci-workflows.test.ts 80/80; node --test on .github/scripts 55/55; typecheck, lint:gui, privacy:scan pass; full local suite 7928 pass with 16 pre-existing Windows symlink-environment failures (no Developer Mode — verified on an untouched baseline), one unrelated scan timeout passes in isolation.

Simplification (applied — explicitly requested)

  • acb93454 — flattened the nested checklist-pending condition, extracted clearedEnforcerState() / defaultReadinessState(), re-indented the hoisted failures block. Behavior preserved: the same 80 harness scenarios + 55 unit tests pass unchanged on the post-simplification head.

Gate

Bottom line

The change is complete and reviewed: all axes pass, CI is green on the final head, and every bot finding is fixed or explicitly declined. The verdict is gated only by the PR's draft state and the two addressed-but-unresolved Codex threads. Once the PR is converted to ready for review (and thread resolution is authorized if desired), the merge-ready bar is met.

@Wibias
Wibias marked this pull request as ready for review August 3, 2026 22:53

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40b99b73e7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// cannot churn the body on every run.
let readiness = extractReviewReadiness(pr.body);
if (checklistRequired && !readiness.present) {
const injectedBody = appendReviewReadinessSection(pr.body ?? "");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude the injected checklist from description quality

When a contributor opens a PR with an empty, placeholder, escaped-newline, or thin body, the first run injects this roughly 400-character checklist and triggers an edited run; on that run, assessPrDescription treats the bot-authored heading, instructions, and checklist items as substantive content, so bad_description clears without the author adding a Summary or Test plan. Strip the bounded readiness section before calling collectPrQualityFailures (or classify its lines as boilerplate) so contributor PRs still enforce the documented description gate.

AGENTS.md reference: AGENTS.md:L177-L180

Useful? React with 👍 / 👎.

Comment thread .github/workflows/enforce-pr-target.yml Outdated
Comment on lines +807 to +808
await upsertReadinessComment(
{ ...storedReadinessState, autoDraftedByBot: false },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retain ownership when permission-recovery conversion fails

When a maintainer was auto-drafted during a failed permission lookup and markReadyForReview then fails transiently after permission recovery, this unconditional update clears autoDraftedByBot and claims the PR is ready even though it remains a draft; later runs therefore never retry the conversion. Fresh evidence in this revision is that the new recovery catch continues into this state-clearing update; preserve ownership and report the failure unless the GraphQL mutation succeeds.

AGENTS.md reference: AGENTS.md:L181-L186

Useful? React with 👍 / 👎.

Comment thread .github/workflows/enforce-pr-target.yml Outdated
Comment on lines +771 to +773
await convertToDraft();
draftConverted = true;
readinessState.autoDraftedByBot = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Checkpoint checklist draft ownership before conversion

In the checklist-only path, ownership is recorded only after convertToDraft succeeds. If that mutation succeeds but the following readiness-comment update fails or the job is interrupted, the durable comment still says autoDraftedByBot:false; when a failed permission lookup later recovers—or the author gains push permission—the maintainer recovery path leaves the bot-created draft in place permanently. Persist a pending ownership claim before the mutation, as the quality-failure path already does, then clear it only when conversion fails.

AGENTS.md reference: AGENTS.md:L181-L186

Useful? React with 👍 / 👎.

Comment on lines +234 to +236
readiness.present
? "This PR is kept in **draft** until every requirement below is fulfilled. The tickable checklist has been added to your PR description — tick all four boxes there."
: "This PR is ready for review; the review readiness checklist is not required for this author.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop rendering recovered maintainers as checklist-gated

After a transient permission lookup failure, the bot has already injected the checklist into the maintainer's PR body, so the recovery run still has readiness.present === true; this condition therefore renders “kept in draft until every requirement” and the four unchecked items even after markReadyForReview succeeds and the same comment says the PR is ready. Base applicability on checklistRequired rather than marker presence, and remove or retire the bot-managed body section when the author is confirmed to have push permission.

AGENTS.md reference: AGENTS.md:L181-L186

Useful? React with 👍 / 👎.

Comment on lines +47 to +49
extractReviewReadiness,
appendReviewReadinessSection,
REVIEW_READINESS_ITEMS

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Load helpers from the workflow's trusted base revision

After this change lands on dev, pull_request_target loads this workflow from the PR's dev base, but the checkout still explicitly loads .github/scripts from the repository default branch (main). Until the next maintainer promotion, main's pr-quality.cjs lacks the newly destructured readiness exports, so every subsequent PR run reaches extractReviewReadiness(...) as undefined and aborts before enforcing anything. Load the helpers from the trusted base revision or keep the workflow compatible with the currently promoted default-branch script.

AGENTS.md reference: AGENTS.md:L159-L161

Useful? React with 👍 / 👎.

@Wibias

Wibias commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

[GD] Addressed feedback

feedbacks:

  • review_comment:3708253683
  • review_comment:3708253689
  • review_comment:3708253697
  • review_comment:3708253700
  • review_comment:3708253703

commit: a62bc19

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
tests/ci-workflows.test.ts (1)

22-26: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Reject created comments that lack an enforcer marker.

Line 22 falls back to every issues.createComment call when no enforcer comment exists. A readiness-only comment can then be returned by lastEnforcerCommentBody, so later assertions inspect the wrong comment instead of reporting the missing enforcer comment.

Return only enforcerCreates. Throw "scenario recorded no enforcer comment" when that list is empty.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/ci-workflows.test.ts` around lines 22 - 26, Update
lastEnforcerCommentBody to use only enforcerCreates, removing the fallback to
creates; throw "scenario recorded no enforcer comment" when enforcerCreates is
empty, then return its last comment body.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/pr-quality.cjs:
- Around line 250-275: Update the review-readiness parsing logic around
REVIEW_READINESS_START and REVIEW_READINESS_END to require exactly one
occurrence of each marker before parsing checklist boxes. Return present: true
and complete: false for duplicate, partial, or inverted markers, and ensure
stripReviewReadinessSection leaves malformed duplicate sections unchanged.
Extend the related tests to cover duplicate sections being incomplete and
preserved during stripping.

In @.github/workflows/enforce-pr-target.yml:
- Around line 32-36: Pin the workflow checkout in
.github/workflows/enforce-pr-target.yml to github.event.pull_request.base.sha
instead of base.ref, ensuring trusted policy files match the event base commit.
Update the corresponding assertions in
.github/scripts/enforce-pr-target.test.cjs lines 51-53 and
tests/ci-workflows.test.ts lines 843-846, then run bun run typecheck and bun run
test.

---

Duplicate comments:
In `@tests/ci-workflows.test.ts`:
- Around line 22-26: Update lastEnforcerCommentBody to use only enforcerCreates,
removing the fallback to creates; throw "scenario recorded no enforcer comment"
when enforcerCreates is empty, then return its last comment body.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a5253fa3-a31c-40f5-b86a-4d1241187a2e

📥 Commits

Reviewing files that changed from the base of the PR and between 7006ea5 and a62bc19.

📒 Files selected for processing (9)
  • .github/scripts/enforce-pr-target.test.cjs
  • .github/scripts/pr-quality.cjs
  • .github/scripts/pr-quality.test.cjs
  • .github/workflows/enforce-pr-target.yml
  • AGENTS.md
  • MAINTAINERS.md
  • docs-site/src/content/docs/contributing/pr-quality.md
  • tests/ci-workflows.test.ts
  • tests/helpers/enforce-pr-target-harness.ts

Comment thread .github/scripts/pr-quality.cjs
Comment thread .github/workflows/enforce-pr-target.yml Outdated
@Wibias

Wibias commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

[GD] Addressed feedback

feedbacks:

  • review_comment:3708620976
  • review_comment:3708620979

commit: a924576

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/enforce-pr-target.test.cjs:
- Line 53: Update the workflow test around the base-SHA assertion to target the
“Checkout trusted PR-quality scripts” step specifically, parsing or capturing
that step and validating its with.ref value equals
github.event.pull_request.base.sha. Keep the existing sparse-checkout assertions
bound to that same checkout step, rather than searching the entire workflow
text.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: baf0fcd9-3824-46f4-8816-254f8ec1a34e

📥 Commits

Reviewing files that changed from the base of the PR and between a62bc19 and a924576.

📒 Files selected for processing (5)
  • .github/scripts/enforce-pr-target.test.cjs
  • .github/scripts/pr-quality.cjs
  • .github/scripts/pr-quality.test.cjs
  • .github/workflows/enforce-pr-target.yml
  • tests/ci-workflows.test.ts

Comment thread .github/scripts/enforce-pr-target.test.cjs Outdated
@Wibias

Wibias commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

[GD] Addressed feedback

feedbacks:

  • review_comment:3708640774

commit: 1fc7ab9

…ayer

`assessPrDescription` already refuses the injected section, and the unit tests
cover that directly. What was not pinned is the sequence that exploits it,
which no single-function test can express: the attack needs two runs and a
body the bot itself wrote in between.

Run one opens a contributor PR with an empty description; the gate fails
`bad_description` and injects the checklist. Run two ticks the four boxes the
bot just added. If the injected section ever counts as substance again, the
description gate passes on text the author never wrote and the PR is marked
ready for review — an empty PR, reviewable, with the maintainers pinged.

The test takes the injected body from the recorded `pulls.update` call rather
than a hand-built fixture, so it exercises the text the gate actually writes,
and asserts `graphql` is never reached — that is where
`markPullRequestReadyForReview` goes out.

The screenshot axis gets the same treatment: the injected section adds
renderable structure but no image, so a gui-cued PR with a complete checklist
and no screenshot must still fail and stay drafted.

Driven red to prove it is not vacuous: reverting the `stripReviewReadinessSection`
call in `assessPrDescription` fails the laundering test and nothing else.

Verified: bun test tests/ci-workflows.test.ts 83 pass; node --test on
.github/scripts 58 pass; typecheck and privacy:scan pass.
@lidge-jun
lidge-jun merged commit 91da63f into dev Aug 4, 2026
19 checks passed
@lidge-jun
lidge-jun deleted the codex/pr-readiness-gate branch August 4, 2026 14:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants