Compare commits

..

No commits in common. "afcc95fbbd2cc864a363b3defdc533f49e63b5b6" and "17a13685a72c60f1dc1dab8b49cf34dfcac6e959" have entirely different histories.

5 changed files with 10 additions and 157 deletions

View file

@ -272,7 +272,7 @@ set_required_bots() { # the PR author is recused by construction
# HEAD_SHA the PR's current head commit
# BASE_SHA the PR's base branch head (the release-shape guard's ref)
# REQUESTED newline-separated logins with a review currently requested
# REVIEWS_JSON JSON array of submitted, gradeable reviews
# REVIEWS_JSON JSON array of submitted (non-PENDING) reviews
# MERGEABLE MERGEABLE | CONFLICTING | UNKNOWN (GitHub's own verdict)
# CHECKS SUCCESS | FAILURE | PENDING | NONE (the check rollup)
# LABELS newline-separated labels currently on the PR
@ -444,25 +444,18 @@ bot_verdict() { # $1 = login → MISSING | BLOCK | APPROVE | STALE | FEEDBACK
if [ -z "$review" ]; then echo MISSING; return; fi
state="$(jq -r '.state' <<<"$review")"
commit="$(jq -r '.commit_id' <<<"$review")"
# This case grades a submitted verdict. The ingestion allow-list answers the
# separate question of whether a row is a submitted review at all (#235).
case "$state" in
CHANGES_REQUESTED | REQUEST_CHANGES)
# blocks at ANY head — both forges' semantic: only a newer review from
# the same reviewer clears it
CHANGES_REQUESTED)
# blocks at ANY head — GitHub's own semantic: only a newer review
# from the same reviewer clears it
echo BLOCK ;;
APPROVED)
if [ "$commit" = "$HEAD_SHA" ]; then echo APPROVE; else echo STALE; fi ;;
COMMENTED | COMMENT)
# A comment is a non-verdict. The machine does not read bodies — if the
# comment is really an agreement, the AUTHOR says so by requesting the
# human's review.
echo FEEDBACK ;;
*)
# An unknown state is not evidence that a reviewer answered. Keep the
# round open and make the next forge vocabulary surprise visible (#235).
log "$1: unrecognised review state $state" >&2
echo MISSING ;;
# COMMENTED and anything else: a non-verdict. The machine does not
# read bodies — if the comment is really an agreement, the AUTHOR
# says so by requesting the human's review.
echo FEEDBACK ;;
esac
}
@ -1061,15 +1054,9 @@ main() {
HEAD_SHA="$(jq -r '.head.sha' <<<"$PR_JSON")"
BASE_SHA="$(jq -r '.base.sha' <<<"$PR_JSON")"
LABELS="$(jq -r '.labels[].name' <<<"$PR_JSON")"
# This allow-list answers whether a row is a submitted, gradeable review;
# bot_verdict separately answers what that submitted verdict says (#235).
# PENDING drafts and Forgejo REQUEST_REVIEW request rows are not reviews.
# PENDING reviews are unsubmitted drafts in someone's browser — not a verdict
REVIEWS_JSON="$(forge_api --paginate "repos/$REPO/pulls/$n/reviews" --jq '.[]' \
| jq -s '[.[] | select(.state == "APPROVED"
or .state == "CHANGES_REQUESTED"
or .state == "REQUEST_CHANGES"
or .state == "COMMENTED"
or .state == "COMMENT")]')"
| jq -s '[.[] | select(.state != "PENDING")]')"
# Read AFTER the reviews, because the raw field is not portable: Forgejo
# never clears it, so it is intersected with who still owes a verdict on
# this head (#188 term 4). A no-op on GitHub, which clears it itself.

View file

@ -1,7 +0,0 @@
### Changed
- Forge release 0.6.2 consolidates upstream 0.6.1 doctrine from upstream #316, #311, #330, and #329, plus upstream 0.6.2's #336 clarification (#231).
- Upstream 0.6.3's #343 membership record and #327 gate fixes land through Forgejo-adapted reconciler changes rather than upstream bytes (#231).
- The upstream drill-record fixes at `86dc2eb`, `a72085b`, and `13ffb0d`, plus upstream 0.7.00.7.4, remain explicitly deferred (#231).

View file

@ -1,3 +0,0 @@
### Fixed
- Forgejo review requests no longer count as verdicts, while its blocking and comment states now grade like their GitHub equivalents (#235).

View file

@ -17,27 +17,6 @@ resolution is standing and does not get re-decided each sync.
Worked example throughout: the `0.6.0` sync (#197, #198), which merged upstream
`8c3a4d1` onto `dad99dd` and took four heads to get green.
## Sync ledger
### 2026-08-24 — upstream 0.6.10.6.3 into forge 0.6.2
- **Consumed through:** upstream tag `upstream-0.6.3`
(`cf2892148305ab18ff234fa0a2ef28ab6fef6c0f`). The `upstream-` prefix is
mandatory in this record because bare `0.6.x` names collide with this
forge's own releases and can identify different trees.
- **Previous baseline:** upstream `0.6.0`, merge-base
`8c3a4d1dee2bdb5ac06a632a285bb65ab2615214`. This adoption ported the
upstream changes in #229 and #230 instead of merging upstream ancestry, so
`.upstream-ref` and the Git merge-base intentionally remain at that commit;
`upstream-0.6.3` is the new content baseline for the next campaign.
- **Adapted here:** the membership and gate changes were applied to the
Forgejo reconciler and its tests, and the upstream CONTRIBUTING changes
were reconciled with this forge's roster and conventions rather than copied
byte-for-byte.
- **Deferred:** upstream's drill-record bookkeeping commits `86dc2eb`,
`a72085b`, and `13ffb0d` correct drill files this forge does not mirror.
Upstream 0.7.00.7.4 is the next sync campaign's standing queue.
## The standing resolutions
These recur every sync. They are decided; re-deciding them is the cost this

View file

@ -172,20 +172,6 @@ $BOT2
$BOT3" REVIEWS_JSON='[]'
expect "requested bots mean bots-reviewing" state:bots-reviewing "$(decide_state)"
# Forgejo materializes each live request as a REQUEST_REVIEW row. Those rows
# are not submitted verdicts (#235): they must leave all three logins
# outstanding, so an opening round stays with the panel rather than falling
# through to the builder as three comment-only answers.
REVIEWS_JSON="$(reviews \
"$(rev "$BOT1" REQUEST_REVIEW "" "" t1)" \
"$(rev "$BOT2" REQUEST_REVIEW "" "" t2)" \
"$(rev "$BOT3" REQUEST_REVIEW "" "" t3)")"
REQUESTED="$(outstanding_requests "$BOT1
$BOT2
$BOT3" 2>"$RTMP/request-round-log")"
expect "three Forgejo request rows keep the opening round with the panel" \
state:bots-reviewing "$(round_state)"
# -- a bot that never reviewed keeps the round open ---------------------------
# With a live request that is the bots' ball; with NO request outstanding it
# is the agent's, because nothing is coming until somebody asks.
@ -229,25 +215,6 @@ REVIEWS_JSON="$(reviews \
"$(rev "$BOT3" APPROVED head1 "" t3)")"
expect "changes-requested blocks even from an old head" state:addressing "$(decide_state)"
# bot_verdict grades submitted states from both forges (#235). Each direct
# assertion names one arm so a later vocabulary regression cannot hide behind
# round_state's shared BLOCK/FEEDBACK handling.
expect "GitHub CHANGES_REQUESTED grades as a block" BLOCK \
"$(bot_verdict "$BOT1")"
REVIEWS_JSON="$(reviews "$(rev "$BOT1" REQUEST_CHANGES old1 "blockers below" t1)")"
expect "Forgejo REQUEST_CHANGES grades as a block" BLOCK \
"$(bot_verdict "$BOT1")"
REVIEWS_JSON="$(reviews "$(rev "$BOT1" COMMENT head1 "non-blocking note" t1)")"
expect "Forgejo COMMENT grades as feedback" FEEDBACK \
"$(bot_verdict "$BOT1")"
REVIEWS_JSON="$(reviews "$(rev "$BOT1" FUTURE_FORGE_STATE head1 "" t1)")"
bot_verdict "$BOT1" >"$RTMP/unknown-verdict" 2>"$RTMP/unknown-verdict-log"
expect "an unrecognised review state is conservatively missing" MISSING \
"$(cat "$RTMP/unknown-verdict")"
expect "an unrecognised review state logs the login and spelling" yes \
"$(grep -qF "$BOT1: unrecognised review state FUTURE_FORGE_STATE" \
"$RTMP/unknown-verdict-log" && echo yes || echo no)"
# -- a stale approval must not promote unreviewed code ------------------------
REVIEWS_JSON="$(reviews \
"$(rev "$BOT1" APPROVED old1 "" t1)" \
@ -286,24 +253,6 @@ REQUESTED="$HUMAN"
expect "re-requested human is needs-human again" state:needs-human "$(decide_state)"
REQUESTED=""
# Forgejo's human-block spelling carries the same meaning (#235). This is
# independently observable because only BLOCK prevents state:needs-human once
# every bot approves.
REVIEWS_JSON="$(reviews \
"$(rev "$BOT1" APPROVED head1 "" t1)" \
"$(rev "$BOT2" APPROVED head1 "" t2)" \
"$(rev "$BOT3" APPROVED head1 "" t3)" \
"$(rev "$HUMAN" REQUEST_CHANGES head1 "not yet" t4)")"
expect "Forgejo human request-changes with bots approving is addressing" \
state:addressing "$(decide_state)"
REVIEWS_JSON="$(reviews \
"$(rev "$BOT1" APPROVED head1 "" t1)" \
"$(rev "$BOT2" APPROVED head1 "" t2)" \
"$(rev "$BOT3" APPROVED head1 "" t3)" \
"$(rev "$HUMAN" APPROVED head1 "" t4)")"
expect "the same Forgejo-shaped fixture with human approval reaches needs-human" \
state:needs-human "$(decide_state)"
# -- an old human comment must not wedge the handoff (codex, #85 round 3) -----
REVIEWS_JSON="$(reviews \
"$(rev "$HUMAN" COMMENTED old1 "early thoughts" t0)" \
@ -1639,49 +1588,6 @@ expect "...with no 'reconciled.' token in the output" \
expect "...naming the attempt that did not happen" \
yes "$(grep -q 'label edit FAILED' <<<"$sf_out" && echo yes || echo no)"
# Drive the ingestion expression through main(), independently of
# bot_verdict (#235). Capturing REVIEWS_JSON at the outstanding_requests
# boundary proves REQUEST_REVIEW never reaches the grader; the COMMENT and
# APPROVED controls prove both gradeable Forgejo states and rows generally
# survive the filter.
review_filter_probe() {
(
REPO=owner/repo
LABELS_CONF="$FIXTURE_CONF"
CEREMONY_FORGE=github
# shellcheck disable=SC2317 # reached through the forge backend, not called directly (#188)
gh() {
if [ "$1" = label ] && [ "$2" = list ]; then core_label_rows | cut -d'|' -f1; return 0; fi
if [ "$1" = pr ] && [ "$2" = list ]; then printf '%s\n' 601; return 0; fi
if [ "$1" = pr ] && [ "$2" = view ]; then
jq -n '{mergeable:"MERGEABLE",statusCheckRollup:[]}'
return 0
fi
if [ "$1" = issue ] && [ "$2" = edit ]; then return 0; fi
case "$(forge_stub_path "$*")" in
*repos/owner/repo/pulls/601/reviews*)
jq -nc \
'{user:{login:"fixture-bot-one"},state:"REQUEST_REVIEW",commit_id:"",submitted_at:"2026-08-22T00:46:05Z"},
{user:{login:"fixture-bot-three"},state:"COMMENT",commit_id:"head1",submitted_at:"2026-08-22T00:46:35Z"},
{user:{login:"fixture-bot-two"},state:"APPROVED",commit_id:"head1",submitted_at:"2026-08-22T00:47:05Z"}' ;;
*/pulls/601)
jq -n '{draft:true,user:{login:"fixture-builder"},head:{sha:"head1"},base:{sha:"base1"},
labels:[{name:"state:building"}],requested_reviewers:[],
created_at:"2026-08-22T00:45:00Z"}' ;;
*) printf '[]\n' ;;
esac
}
# shellcheck disable=SC2317 # main invokes the probe override indirectly
outstanding_requests() {
printf '%s\n' "$REVIEWS_JSON" >"$RTMP/gradeable-reviews.json"
}
main >/dev/null
)
}
review_filter_probe
expect "REQUEST_REVIEW is removed before REVIEWS_JSON reaches the grader" \
COMMENT,APPROVED "$(jq -r 'map(.state) | join(",")' "$RTMP/gradeable-reviews.json")"
# ---------------------------------------------------------------------------
# outstanding_requests — the portable "who still owes a verdict" (#188 term 4)
#
@ -1709,15 +1615,6 @@ expect "a stale approval still owes a verdict" "$BOT3" \
expect "a reviewer who never reviewed still owes one" "nobody" \
"$(outstanding_requests "nobody")"
REVIEWS_JSON="$(reviews \
"$(rev "$BOT1" REQUEST_REVIEW "" "" 2026-08-22T00:46:05Z)")"
expect "a Forgejo request row is not an answer and leaves the login outstanding" \
"$BOT1" "$(outstanding_requests "$BOT1" 2>"$RTMP/request-outstanding-log")"
REVIEWS_JSON="$(reviews \
"$(rev "$BOT1" APPROVED head1 "" 2026-08-01T00:00:00Z)" \
"$(rev "$BOT2" CHANGES_REQUESTED head1 "" 2026-08-01T00:00:00Z)" \
"$(rev "$BOT3" APPROVED head0 "" 2026-07-01T00:00:00Z)")"
# The Forgejo shape, end to end: the field lists all three long after every
# verdict landed. Only the stale one may survive the filter.
expect "the never-cleared forgejo field collapses to who actually owes" \