Problem
main requires one approving review, but nothing constrains whose approval counts. There is no .github/CODEOWNERS, and scripts/protect-main.sh sets "require_code_owner_reviews": false.
Today that is safe by accident rather than by configuration: vivek7405 is the only account with push access, and GitHub only counts approvals from users with write access, so the maintainer's approval is currently the only one that can satisfy the gate. The moment a second collaborator or bot account is granted write, any of them could approve a PR and merge it without the maintainer ever seeing it.
The repo has 70 forks and 109 stars, so outside PRs are a live possibility, not a hypothetical.
A second, smaller problem surfaced while checking this: the live protection has drifted from the script. scripts/protect-main.sh lists six required contexts; main currently enforces five. In-repo app tests (website + blog) is in the script and is NOT required on the branch, which means the script has not been re-run since that context was added, and a PR can merge today without that job passing.
Design / approach
Two changes, both small.
1. Add .github/CODEOWNERS with * @vivek7405. This makes the maintainer the owner of every path. It auto-requests their review on every PR, and it is the precondition for the second change.
2. Flip require_code_owner_reviews to true in scripts/protect-main.sh and re-run it. With CODEOWNERS in place, only the maintainer's approval satisfies the one-review requirement, regardless of who else holds write access later.
Re-running the script also repairs the context drift, since it PUTs the full contexts array.
What this deliberately does not change. enforce_admins stays false. The script's own header explains why, and the reasoning still holds: GitHub does not let a PR author approve their own PR, so on an effectively solo org the maintainer already merges their own work through the admin bypass. Turning on code-owner reviews does not make that worse, and it does not fix it either. Flip enforce_admins only once a second reviewer account exists, exactly as the header says.
Alternative considered and rejected: restrictions (a push allowlist). It would name the merge-eligible accounts directly, and it is available here because this is an org repo. Rejected because it is redundant with write-access control and adds a second place to keep a list of people in sync.
Implementation notes (for the implementing agent)
Where to edit:
- New file
.github/CODEOWNERS. The .github/ directory currently holds only workflows/.
scripts/protect-main.sh:41 — "require_code_owner_reviews": false becomes true.
- The same file's header comment (
:9-14) documents the review-requirement reasoning. Extend it to say what CODEOWNERS adds, so the next reader does not have to reconstruct it.
Landmines:
- Running the script is part of the change, not a follow-up. Editing the JSON alone changes nothing on GitHub; the file is a script, not declarative state. It needs repo admin.
- The contexts array must match
ci.yml job name: values exactly — GitHub keys required checks on the display name. The header already warns about this. Note ci.yml defines more jobs than the script requires (Bun runtime smoke…, Postgres prod-engine round-trip, E2E (blog served on Bun) are not in the required list); that is a separate decision and should not be quietly changed while fixing the drift. Only restore what the script already lists.
- CODEOWNERS syntax is not gitignore syntax.
* matches every path; a pattern with no owner clears ownership rather than setting it. Keep it to one line unless there is a reason.
- A CODEOWNERS file with an unresolvable owner silently matches nothing. Verify GitHub parses it after merge: the repo Settings → Branches page flags an invalid file, and
gh api repos/webjsdev/webjs/codeowners/errors reports parse errors.
Invariants to respect:
- AGENTS.md: never push to
main; feature branch and PR always. This change is itself governance, so it must land through the normal PR flow rather than a direct push.
- Do not set
enforce_admins: true as part of this issue. It would lock the solo maintainer out of merging their own PRs entirely, which is a different decision with a different trigger (see Design above).
Tests and docs surfaces:
- No unit-test layer applies; this is repo configuration, not framework code. Say so explicitly in the PR body rather than leaving the test checklist blank.
scripts/protect-main.sh is self-documenting via its header; update it as noted above.
- If AGENTS.md describes the merge/review rules anywhere, keep it consistent with the new behaviour.
Acceptance criteria
Problem
mainrequires one approving review, but nothing constrains whose approval counts. There is no.github/CODEOWNERS, andscripts/protect-main.shsets"require_code_owner_reviews": false.Today that is safe by accident rather than by configuration:
vivek7405is the only account with push access, and GitHub only counts approvals from users with write access, so the maintainer's approval is currently the only one that can satisfy the gate. The moment a second collaborator or bot account is granted write, any of them could approve a PR and merge it without the maintainer ever seeing it.The repo has 70 forks and 109 stars, so outside PRs are a live possibility, not a hypothetical.
A second, smaller problem surfaced while checking this: the live protection has drifted from the script.
scripts/protect-main.shlists six required contexts;maincurrently enforces five.In-repo app tests (website + blog)is in the script and is NOT required on the branch, which means the script has not been re-run since that context was added, and a PR can merge today without that job passing.Design / approach
Two changes, both small.
1. Add
.github/CODEOWNERSwith* @vivek7405. This makes the maintainer the owner of every path. It auto-requests their review on every PR, and it is the precondition for the second change.2. Flip
require_code_owner_reviewstotrueinscripts/protect-main.shand re-run it. With CODEOWNERS in place, only the maintainer's approval satisfies the one-review requirement, regardless of who else holds write access later.Re-running the script also repairs the context drift, since it PUTs the full contexts array.
What this deliberately does not change.
enforce_adminsstaysfalse. The script's own header explains why, and the reasoning still holds: GitHub does not let a PR author approve their own PR, so on an effectively solo org the maintainer already merges their own work through the admin bypass. Turning on code-owner reviews does not make that worse, and it does not fix it either. Flipenforce_adminsonly once a second reviewer account exists, exactly as the header says.Alternative considered and rejected:
restrictions(a push allowlist). It would name the merge-eligible accounts directly, and it is available here because this is an org repo. Rejected because it is redundant with write-access control and adds a second place to keep a list of people in sync.Implementation notes (for the implementing agent)
Where to edit:
.github/CODEOWNERS. The.github/directory currently holds onlyworkflows/.scripts/protect-main.sh:41—"require_code_owner_reviews": falsebecomestrue.:9-14) documents the review-requirement reasoning. Extend it to say what CODEOWNERS adds, so the next reader does not have to reconstruct it.Landmines:
ci.ymljobname:values exactly — GitHub keys required checks on the display name. The header already warns about this. Noteci.ymldefines more jobs than the script requires (Bun runtime smoke…,Postgres prod-engine round-trip,E2E (blog served on Bun)are not in the required list); that is a separate decision and should not be quietly changed while fixing the drift. Only restore what the script already lists.*matches every path; a pattern with no owner clears ownership rather than setting it. Keep it to one line unless there is a reason.gh api repos/webjsdev/webjs/codeowners/errorsreports parse errors.Invariants to respect:
main; feature branch and PR always. This change is itself governance, so it must land through the normal PR flow rather than a direct push.enforce_admins: trueas part of this issue. It would lock the solo maintainer out of merging their own PRs entirely, which is a different decision with a different trigger (see Design above).Tests and docs surfaces:
scripts/protect-main.shis self-documenting via its header; update it as noted above.Acceptance criteria
.github/CODEOWNERSexists and contains* @vivek7405gh api repos/webjsdev/webjs/codeowners/errorsreports no parse errorsscripts/protect-main.shsetsrequire_code_owner_reviews: truegh api repos/webjsdev/webjs/branches/main/protectionshowsrequire_code_owner_reviews: trueIn-repo app tests (website + blog)enforce_adminsis stillfalse