Skip to content

🔒 [security] approval: bind human sign-off to reviewed code - #23

Merged
EricTechPro merged 2 commits into
mainfrom
codex/approval-freshness
Oct 4, 2026
Merged

EricTechPro merged 2 commits into
mainfrom
codex/approval-freshness

Conversation

@EricTechPro

@EricTechPro EricTechPro commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

You approve the AI’s fix. The AI then changes the code. Super Board now waits for a fresh approval before merging.

Previously, an old approval could still let the changed code merge, even though you had never approved that version.

What changes

  • Only people with repository write permission or higher can approve. Bot accounts cannot.
  • You still reply done, but it must answer the current approval request.
  • Old labels and comments cannot approve changed code. The approval must also cover the current steps a person needs to complete.
  • If the code and those steps stay the same, repeated checks keep the valid approval. They do not keep asking you again.

Changed code returns for review and a fresh request. Asking again does not grant permission to merge.

Checked

All 34 offline safety test suites passed, including changed-code approval and blocked-task recovery. All eight GitHub checks passed. Independent review found no remaining issues to fix. No live board, worker, or database was used for these tests.

Existing blocked tasks need one fresh approval request and human reply.

Limits

GitHub cannot tell you apart from an AI using your GitHub account. The AI must never write your done reply.

The code is checked at merge time. Approval comments are checked just before that, but GitHub does not lock them against a last-second edit.

Merging manually on GitHub is unchanged.

- Verify trusted request and human reply for the current PR head and steps.
- Refresh stale blocked requests without treating them as approved.
- Pass 34 offline safety suites and independent review.

@EricTechPro EricTechPro left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Author explanations in the selected Purpose / What changed / Why it matters format: one overview for each changed file, plus notes beside critical approval logic. These explain the implementation; they are not an independent approval and do not replace tests.

Comment on lines +96 to +99
if request is None or any(request.get(k) != v for k, v in expected.items()):
return hold("the latest request does not cover the current code and human steps")
if timestamp(request_comment.get("updated_at")) != latest_time:
return hold("approval request was edited; post a fresh request")

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Author explanation

Purpose
Make sure the human answered the current request.

What changed
The request must match the current code and required steps. An edited request is rejected too.

Why it matters
The question cannot be changed after the human has already answered it.

Comment on lines +105 to +117
for comment in comments:
if (not comment.get("id") or not DONE.fullmatch(comment.get("body") or "")
or comment.get("source") != request_comment.get("source")):
continue
created = timestamp(comment.get("created_at"))
if created <= latest_time or timestamp(comment.get("updated_at")) != created:
continue
author = comment.get("user") or {}
if (author.get("type") == "User" and author.get("login")
and permission(author["login"]) in ("write", "maintain", "admin")):
return {"approved": True, "why": "trusted human confirmed the current request",
"request": request, "requestId": request_comment.get("id"),
"approvalId": comment.get("id"), "approver": author["login"]}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Author explanation

Purpose
Accept a valid answer from someone allowed to approve.

What changed
Check write access or higher, reply time and the same issue or PR discussion. Ignore edited replies and GitHub bot accounts.

Why it matters
An outsider’s reply or an old done must not unlock the merge. An AI using a person’s GitHub account remains indistinguishable from that person.

Comment on lines +223 to +228
APPROVED=false
approval_check() {
APPROVAL=$(python3 "$HERE/super-board-approval.py" --repo "$REPO" --pr "$PR" \
--head "$HEAD_SHA" --plan "$PLAN") || APPROVAL='{"approved":false}'
APPROVED=$(echo "$APPROVAL" | jq -r '.approved // false')
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Author explanation

Purpose
Ask whether this code and its required human steps are approved.

What changed
Call the approval checker instead of trusting the label. A failed checker call leaves approval false.

Why it matters
The merge needs a verified human answer, not just a status label.

Comment on lines +237 to +241
if [ -n "$HUMAN" ] && [ "$APPROVED" != true ]; then
echo "$HUMAN"
say "merge policy: a human merges this PR"; exit 7
echo "$PLAN" | jq -r '.needs_you[] | "needs-you: " + .'
approval_request
say "merge policy: waiting for a trusted human to approve this exact head"; exit 7

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Author explanation

Purpose
Hold a change that still needs human approval.

What changed
Print the request for the current change and stop when that approval is missing.

Why it matters
Asking for permission never grants permission to merge.

Comment on lines +306 to +313
gh pr view "$PR" ${REPO:+--repo "$REPO"} --json labels,files,additions,deletions,body > "$META" 2>/dev/null || echo '{}' > "$META"
PLAN=$(python3 "$HERE/super-board-merge-policy.py" --config "$CONFIG" --meta "$META" --diff "$DIFF") || {
say "human approval scope could not be refreshed"; exit 7; }
approval_check
if [ "$APPROVED" != true ]; then
approval_request
say "human approval changed during verification; keep the card Blocked"
if [ -n "$HUMAN" ]; then exit 7; else exit 8; fi

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Author explanation

Purpose
Check that approval is still valid after verification runs.

What changed
Read the current PR details again, recalculate the required human steps, and recheck the reply just before merging.

Why it matters
A new requirement or request can arrive while tests run. The earlier check must not silently cover it. This is a final snapshot; GitHub does not lock comments afterward.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Author explanation

Purpose
Guide the reviewer through the final merge decision.

What changed
Require a verified reply to the current request, then rerun the merge checks. Tell the reviewer to link to one request instead of duplicating it.

Why it matters
The reviewer cannot use an old status label to skip the human decision, even when a task has returned to Review.

Comment thread tests/test-deps.sh

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Author explanation

Purpose
Check how task dependencies and human waiting states are reported.

What changed
Change the saved-data checks so a bare done comment or done label must leave approval unconfirmed.

Why it matters
These tests catch the old shortcut returning through the dependency reader. Live approval checking has its own tests.

Comment thread tests/test-merge-gate.sh

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Author explanation

Purpose
Exercise the final merge checks with simulated GitHub responses.

What changed
Cover a valid current reply, stale replies, unreadable permissions, and new requests or human steps appearing while checks run.

Why it matters
The tests check that an invalid approval stops before the merge command, including when the approval becomes outdated during verification.

Comment thread tests/test-wave-plan.sh

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Author explanation

Purpose
Check where the planner sends board tasks.

What changed
Add a case where code changes while its task is Blocked: it must enter the fresh-approval list and stay out of approved resume and ordinary work lists.

Why it matters
This protects the recovery path: asking again must not become permission to build or merge.

Comment thread tests/test_approval.py

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Author explanation

Purpose
Test the new approval checker without changing a live board or database.

What changed
Cover who may approve, which code and steps were approved, edited or old replies, repeated checks, multiple pages of comments, and planner integration.

Why it matters
The tests check both sides: changed or untrusted answers must be rejected, while a valid answer for unchanged work must continue to count.

@EricTechPro
EricTechPro marked this pull request as ready for review October 4, 2026 04:08
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@EricTechPro
EricTechPro merged commit e8568e6 into main Oct 4, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant