Skip to content

stack 5/5: define review lifecycle and governance rollout - #905

Closed
Wibias wants to merge 3 commits into
agent/pr-hygiene-gatefrom
agent/pr-governance-rollout
Closed

stack 5/5: define review lifecycle and governance rollout#905
Wibias wants to merge 3 commits into
agent/pr-hygiene-gatefrom
agent/pr-governance-rollout

Conversation

@Wibias

@Wibias Wibias commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Stack

5/5 — review lifecycle and governance rollout

Base: agent/pr-hygiene-gate (#903)
Final stack layer

Summary

  • count substantial maintainer review rounds once per distinct head SHA
  • flag review: limit-reached after two unsuccessful rounds without auto-closing
  • create and document standardized closure-reason labels
  • state clearly that authors own branch repair, CI failures, tests, and review fixes
  • expand CODEOWNERS across adapters, providers, Codex integration, and server behavior while preserving stricter security ownership
  • wire all stack validators into the repository policy-test workflow
  • add public contributor documentation
  • provide an exact owner/admin checklist for required checks, rulesets, synthetic fork testing, and merge queue rollout

Verification

  • node --test .github/scripts/pr-review-lifecycle.test.cjs — 7 passed, 0 failed
  • lifecycle and policy-test workflow YAML parsed successfully
  • placeholder scan passed
  • GitHub-side lifecycle workflow was fetched and verified after replacing a corrupted transfer before opening this PR
  • branch remains one incremental commit over stack 4/5: add deterministic anti-slop CI #903

Important limitation

The connected Wibias account has write access but not repository admin access. This PR documents the exact ruleset and merge-queue settings, but the project owner or another administrator must activate them after the synthetic fork test succeeds.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • ^dev$
  • ^preview$

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e5ac194b-fc84-45ec-b11b-3763392a2086

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

Wibias commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator Author

Stack navigation

  1. stack 1/5: add contribution intake firewall #900 — contribution intake firewall
  2. stack 2/5: gate review readiness and CodeRabbit #901 — readiness and CodeRabbit gate
  3. stack 3/5: add first-contributor trust lane #902 — first-contributor trust lane
  4. stack 4/5: add deterministic anti-slop CI #903 — deterministic anti-slop CI
  5. stack 5/5: define review lifecycle and governance rollout #905 — review lifecycle and governance rollout

Review and merge bottom-up. Each PR targets the preceding stack branch, so its Files changed view contains only that layer.

@Wibias Wibias added the chore Maintenance, CI, tests, refactors, or build changes (not a user-facing bug or feature). label Aug 2, 2026
@lidge-jun

Copy link
Copy Markdown
Owner

Two things here, one mechanical and one about content.

Mechanical: this PR's head is not a descendant of #903's current head — their merge base is 0cbaf4ba, so the stack is broken at the last link. It needs a rebase regardless of what we decide about the rest.

Content: CODEOWNERS and the contributor/governance docs I want. The review-lifecycle tracker I am less sure of: pr-review-lifecycle.cjs:3-54 calls a review "substantial" at 40 characters, which is short enough that "looks good to me, ship it" clears the bar while a precise one-line objection to a specific hunk might not. A heuristic that can be satisfied by padding is not measuring what its name claims.

Where this leaves the stack, having reviewed all five together and measured the CI lane first:

PR Disposition
#899 (mine) Landing first — the measured bottleneck fix
#903 Next, extracted to stand alone; deterministic, lowest friction
#900 Held on a policy decision about pre-approved issues
#902 Sponsorship rule wanted; size/concurrency caps held
#905 CODEOWNERS + docs wanted; lifecycle tracker needs a better signal than length

If you want to split this into a CODEOWNERS-plus-docs PR against dev, I will take that one straight away. The lifecycle tracker can follow once we agree on what counts as a review.

Thanks for the volume of work in this stack — the specs in docs/superpowers/ made it genuinely reviewable, which is not the norm for governance changes.

@Wibias
Wibias marked this pull request as ready for review August 3, 2026 02:50

Wibias commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator Author

[shipping-github] Verdict: gated

PR: #905 — stack 5/5: define review lifecycle and governance rollout
Head: d3f2e3b0 on agent/pr-hygiene-gate (mergeable, updated from the current stack-4/5 head 7a6982d0)

Semantic propagation

  • Concepts audited: review-round accounting, closure taxonomy, exception-label ownership, CODEOWNERS security boundary, validator CI wiring, contributor-facing contract.
  • Authoritative sources: pr-review-lifecycle.cjs, docs/superpowers/specs/2026-08-02-pr-review-lifecycle-design.md, MAINTAINERS.md, CONTRIBUTING.md, .github/CODEOWNERS, .coderabbit.yaml.
  • Producers and consumers checked: lifecycle workflow; stack 2/5: gate review readiness and CodeRabbit #901 readiness (awaiting-* owner); stack 1/5: add contribution intake firewall #900 admission/stale; stack 3/5: add first-contributor trust lane #902/stack 4/5: add deterministic anti-slop CI #903 exception and blocked labels; stack 5/5: define review lifecycle and governance rollout #905's own policy-test wiring; docs-site page; rollout checklist.
  • Public/derived representations checked: closure labels vs design doc vs CONTRIBUTING; review: round-*/limit-reached vs readiness interplay; CODEOWNERS last-match-wins (stricter security ownership preserved for src/server/management-api.ts etc.); policy-test paths/run list vs every validator.
  • Material variant partitions checked: maintainer vs bot reviews; distinct vs same head SHA; round 1/2/limit; approval clears; labels map vs ensured labels; draft vs ready.
  • Positive and negative assertions checked: 7 lifecycle tests, full script suite green, end-to-end harness across 5 scenarios, and a live policy-test run (run 170, success) exercising every stack validator.
  • Unmapped surfaces: none.
  • Unproven equivalence assumptions: none.
  • Representation mismatches: none.
  • Variant coverage gaps: none blocking.
  • Axis verdict: pass.

Usefulness

High: closes the stack with review-round accounting, standardized closure dispositions, the exact owner/admin ruleset checklist, public contributor docs, and the CI wiring that makes every validator testable. The live policy-test run proves the wiring end to end.

Bugs / correctness

  • Method: bug-review.md — Bugbot: n/a (Codex host, complementary only); complementary: done (silent_failures / resource_leaks / edge_cases).
  • O1 (owner, fixed in f17438d5 merge): branch was based on the old 0cbaf4ba stack 4/5: add deterministic anti-slop CI #903 head; merged the current 7a6982d0. The owner's mechanical point is resolved; mergeable: true, 0 behind.
  • F1 (P1, fixed in d3f2e3b0): pr-review-lifecycle.yml granted pull-requests: read, but PR label writes require pull-requests: write (documented in pr-labeler.yml); the workflow would have failed on its first review event. Now job-scoped with permissions: {} at workflow level.
  • F2 (P2, fixed in d3f2e3b0): lifecycle and readiness fought over awaiting-author/awaiting-maintainer - readiness re-marked review-feedback PRs as maintainer-ready within one reconcile, so the author-action label (and its stale-closure protection) never stuck. Lifecycle no longer manages those labels; they are readiness-owned. Design doc synced.
  • F3 (P2, fixed in d3f2e3b0): approvals never cleared review: round-*/limit-reached; they now do, so an approved PR is not labeled as churn.
  • S1 (fixed in d3f2e3b0): reuses authorHasPushPermission from pr-quality.cjs.
  • Residual: none new beyond the O2 decision below.

Security

  • Scope reviewed: pull_request_review trusted default-branch execution, token permissions, label/comment writes, CODEOWNERS boundary.
  • Findings: none confirmed after fixes. No PR-head checkout or execution; action pins verified; CODEOWNERS keeps the stricter two-owner security paths via last-match-wins.
  • The workflow's writes are limited to labels and one bot comment on the reviewed PR.

Spec / standards

  • Spec source: PR body + lifecycle design doc + owner comment; all eight summary claims verified, plus the owner's mechanical point (now fixed).
  • Gaps: O2 remains open by decision - the owner's substantive point about the 40-character "substantial review" heuristic (padding passes, precise one-liners fail; reproduced in a probe). Per the author's direction, this review did not change the tracker definition; the round-counting signal is the pending owner/author agreement.

Reviews

  • Owners/maintainers: @lidge-jun commented (02:38) with the two points above; no reviewers requested yet. This is a .github/** + CODEOWNERS + governance security-boundary change; explicit maintainer/owner review is required before merge.
  • Bots: none - CodeRabbit skips branch bases.

Base / CI

  • Base: updated to 7a6982d0; mergeable: true, 0 behind.
  • Required checks: the renamed "Issue and PR policy tests" workflow ran live on this head (run 170, success), exercising every stack validator; React Doctor green (2 runs); CodeRabbit success.
  • Local tip compile/tests: full script suite green, both workflow YAMLs parse, lifecycle script syntax-checked, 5-scenario harness passed (round-1/round-2/limit, same-head dedupe, approval clears, non-maintainer ignored, no awaiting-* ops).

Gate

Draft gate: the PR remains a GitHub draft, so this verdict is gated (not merge-ready). ship-gate.mjs could not run on this machine (gh token invalid); connector evidence used instead.

Bottom line

Mechanically clean and approve-worthy after O1 + F1-F3 + S1: the stack link is fixed, the label fights and permission bug are gone, approvals clear rounds, and the CI wiring is proven live. Two things remain before this layer can move: the draft status, and the owner's O2 decision on what counts as a review round - which the author has deferred for a follow-up look. A reply to @lidge-jun acknowledging the two points and stating the plan (base fixed; tracker signal to be redefined and agreed) is drafted for author confirmation.

@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: d3f2e3b043

ℹ️ 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".

Comment on lines +3 to +5
on:
pull_request_review:
types: [submitted]

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 Route fork review mutations through a write-capable trusted event

For reviews on external fork PRs—the primary scenario documented by this rollout—GitHub downgrades GITHUB_TOKEN to read-only, regardless of the requested issues: write and pull-requests: write permissions. Consequently, the label and comment mutations below fail with Resource not accessible by integration, so review rounds are never recorded for external contributors. Keep the untrusted head unchecked-out, but perform mutations from a trusted write-capable follow-up such as a carefully validated workflow_run.

Useful? React with 👍 / 👎.

});
// An approval supersedes prior rounds: clear the round and limit
// labels so an approved PR is not still labeled as review churn.
if (String(review.state || "").toLowerCase() === "approved") {

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 Restrict approval cleanup to maintainer reviews

When any user with public read access submits an APPROVED review, this branch clears all review-round and limit labels without checking reviewerHasPushPermission. Non-maintainer approvals do not satisfy GitHub's required-review policy, but here they can erase the workflow's maintainer disposition signal; require the already-computed push-permission check before performing this cleanup.

Useful? React with 👍 / 👎.

Comment on lines +104 to +105
core.info("Approval clears review-round labels.");
return;

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 Reset persisted rounds when approval supersedes them

If a maintainer approves after one or more rounds and the author subsequently pushes another revision, this branch removes only the visible labels while leaving the hidden comment state unchanged. The next change request resumes the old count and can immediately restore review: limit-reached, contradicting the stated superseding behavior; reset or remove the persisted lifecycle state before returning.

Useful? React with 👍 / 👎.


if (String(reviewState || "").toLowerCase() !== "changes_requested") return result;
if (!reviewerHasPushPermission || !isSubstantialReview(reviewBody)) return result;
if (!headSha || headSha === current.lastCountedHeadSha) return result;

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 Track every counted SHA rather than only the latest one

When an author force-pushes a previously reviewed head again—for example, the sequence A → B → A—the final A differs from lastCountedHeadSha and is counted a second time. This violates the documented once-per-distinct-SHA rule and can reach the limit with only one unique revision being counted twice; persist the set of counted head SHAs instead of only the last value.

Useful? React with 👍 / 👎.

Comment on lines +108 to +111
if (!result.counted) {
core.info("Review did not start a new substantial maintainer review round.");
return;
}

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 Initialize closure labels before skipping uncounted reviews

On a fresh rollout, closure reasons such as close: no-approved-issue and close: abandoned do not exist until a substantial maintainer change request is counted, because all other review events return before ensureLabel runs. Those labels are specifically needed for PRs that may be closed before human review, so initialize the taxonomy during rollout or before this early return.

Useful? React with 👍 / 👎.

Comment on lines +52 to +54
## 4. Enable merge queue

Enable the merge queue for `dev` after required checks are stable. Require queued commits to rerun the same checks against the current integration state. Do not enable auto-merge as a substitute for approvals or unresolved-thread checks.

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 Add merge-group support before enabling the queue

The rollout instructs the administrator to enable a merge queue and rerun the same required checks, but none of the listed required workflows (enforce-pr-target, admission, readiness, trust lane, hygiene, or cross-platform CI) declares a merge_group trigger. Once the queue is enabled, those checks therefore do not report for the synthetic merge-group commit and queued PRs cannot merge; add merge-group-capable checks and adapt PR-specific jobs before performing this step.

Useful? React with 👍 / 👎.

- `PR readiness / reconcile`
- `PR trust lane / trust-lane`
- `PR hygiene / hygiene`
- the cross-platform CI jobs required by current release policy

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 Make required CI report for path-filtered pull requests

The proposed ruleset globally requires the cross-platform CI jobs, while .github/workflows/ci.yml runs on pull requests only when one of its paths entries changes. A documentation-only or governance-only PR therefore never creates those required checks and remains blocked indefinitely; remove the workflow-level path filter or add an always-running required aggregate check that succeeds when the expensive matrix is intentionally skipped.

Useful? React with 👍 / 👎.

Comment thread MAINTAINERS.md
Comment on lines +48 to +50
## Pull request disposition

Use one standardized closure label and a concise comment naming the reopening condition:

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 Apply the abandoned disposition during stale closure

The new policy requires every closure to carry one standardized reason, but the existing .github/workflows/stale-author-prs.yml closes inactive awaiting-author PRs without applying close: abandoned. Automated abandonment closures consequently violate the policy and cannot be included reliably in the rollout's closure-by-reason measurements; have the stale workflow apply that label before closing.

Useful? React with 👍 / 👎.

Comment on lines +82 to +83
const result = applyReviewEvent({
state: storedState,

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 Count rounds only while the PR awaits maintainer review

The workflow invokes round accounting for every maintainer CHANGES_REQUESTED review without checking whether the PR carries awaiting-maintainer. A maintainer comment on a draft, intake-blocked, or already awaiting-author PR therefore consumes one of the two allowed rounds even though the new policy explicitly says human review starts only after readiness gates pass; gate accounting on the live readiness label.

Useful? React with 👍 / 👎.

Comment on lines +84 to +86
reviewState: review.state,
reviewBody: review.body,
reviewerHasPushPermission,

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 Include actionable inline review comments in the threshold

For a maintainer review that requests changes through detailed inline comments but leaves the optional review summary blank or shorter than 40 characters, only review.body is passed to isSubstantialReview, so the round is silently ignored. Fetch the comments belonging to the submitted review and include their actionable text when deciding whether the review is substantial.

Useful? React with 👍 / 👎.

@lidge-jun

Copy link
Copy Markdown
Owner

CODEOWNERS and the contributor docs are in #920 with your co-authorship. Closing this rather than leaving it open, since its head was never a descendant of #903 anyway (merge base 0cbaf4ba) and the stack it sat on is now resolved.

Kept: the CODEOWNERS entries for src/adapters/, src/providers/, src/codex/, src/server/, and the documentation skeleton.

Rewritten: the docs described an approved-for-work admission gate, a 500-line cap, and automatic closure timers. None of those exist here — I dropped them in #900 and #902 for reasons recorded on each. Publishing rules the repository does not enforce is worse than publishing none, so the page now describes what actually runs: the deterministic hygiene gate, the CI shape after #899, and the sponsored surfaces.

Dropped: the review-lifecycle tracker. "Substantial review" at 40 characters is satisfiable by padding — "looks good to me, ship it" clears the bar while a precise one-line objection to a specific hunk might not. A heuristic that can be gamed by typing more is not measuring review quality. If you want to bring this back with a signal that resists that, I will read it properly.

The specs under docs/superpowers/ made this whole stack reviewable, which is unusual for governance changes. Thank you for that.

@lidge-jun lidge-jun closed this Aug 3, 2026
Wibias added a commit to n3wr1ch/opencodex that referenced this pull request Aug 3, 2026
…n the docs

Consolidates the parts of @Wibias's five-PR governance stack (lidge-jun#900, lidge-jun#902, lidge-jun#905)
that reduce risk without adding friction, and drops the parts that do the
opposite. The measurement behind the ordering: Windows was the last job to
finish in 23 of 23 recent CI runs at a 17m41s median, so lidge-jun#899 was the actual
bottleneck and everything here is judged by whether it makes the lane worse.

Kept, from lidge-jun#902's trust lane: authentication, credential handling, GitHub
Actions workflows, release automation, and dependency installation need a
maintainer to sponsor the change before it merges. MAINTAINERS.md already
requires security review for exactly these; this makes the requirement visible
on the pull request instead of relying on a reviewer noticing. It runs inside
the existing hygiene job rather than adding a workflow, and it applies to every
contributor — blast radius does not depend on how many PRs someone has merged,
which is why the upstream first-timer exemption is gone.

Dropped, from the same PR: the 500-line cap and the one-open-PR limit. A
provider preset with its registry rows, adapter wiring, tests, and five locales
clears 500 lines by itself, and several good first contributions here have.
Telling a newcomer their fix is too big is a worse failure than reviewing a
large diff.

Dropped, from lidge-jun#900: the admission gate requiring a pre-approved issue, and the
five-day auto-close. The DeepSeek reasoning replay, the Cursor Grok parameters,
the AgentRouter EOF tolerance, the tool-result image forwarding — every one
arrived as an unplanned PR from someone who hit the bug. A gate that required a
planning discussion first would have lost all of them.

Kept, from lidge-jun#905: CODEOWNERS entries for the high-impact runtime directories, and
the contributor documentation — rewritten to describe what is actually enforced.
The submitted version documented the approved-for-work gate, the size caps, and
the automatic closure timers, none of which exist here, and publishing rules the
repository does not enforce is worse than publishing none.

Not included: lidge-jun#901's readiness gate. It makes CodeRabbit's judgment blocking and
triggers per check_run, which scales with the job count lidge-jun#899 just raised.

Co-authored-by: Wibias <37517432+Wibias@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore Maintenance, CI, tests, refactors, or build changes (not a user-facing bug or feature).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants