Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 41 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,24 +77,56 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Check for committed parity receipt
id: check-receipt
# The receipt must be present, not merely checked for.
#
# This step previously set `has_receipt=false` and let the two steps below
# skip, so a branch with no committed receipt produced a **green** job that
# had reproduced nothing. That is survivable while the job is advisory, but
# it becomes a merge-authorizing false green the moment the check is
# required: the cheapest way to satisfy a failing parity gate would be to
# delete the receipt. Absence is now a failure.
- name: Require a committed parity receipt
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
# Explicit, because the script below depends on `set -euo pipefail`.
# Runners default to bash today, but that is an implementation detail,
# and a gate whose failure mode is "silently did not fail" should not
# rest on one.
shell: bash
run: |
if [ -f docs/migration/parity-receipt.json ]; then
echo "has_receipt=true" >> $GITHUB_OUTPUT
else
echo "has_receipt=false" >> $GITHUB_OUTPUT
echo "No committed parity receipt found — skipping reproduction check."
set -euo pipefail
if [ ! -f docs/migration/parity-receipt.json ]; then
echo "::error file=docs/migration/parity-receipt.json::Missing committed parity receipt"
{
echo "## parity-receipt-reproduction failed"
echo
echo "**Invariant.** The committed parity receipt at"
echo "\`docs/migration/parity-receipt.json\` is reproducible from the source"
echo "it names."
echo
echo "**Why it matters.** This job exists to prove the committed receipt is"
echo "not a stale artifact. With no receipt there is nothing to reproduce, and"
echo "a green result would assert a reproduction that never ran. Missing is"
echo "not green."
echo
echo "**Authority.** Repository CI, reported by \`github-actions\` as"
echo "\`parity-receipt-reproduction\`."
echo
echo "**Evidence required.** Either restore the committed receipt, or — if the"
echo "migration it records is genuinely retired — remove this job in the same"
echo "change that removes the receipt, so the deletion is a reviewable"
echo "decision rather than a silent loss of coverage."
echo
echo "**Do not weaken.** Do not restore the previous skip-if-absent behavior"
echo "to recover green."
} >> "$GITHUB_STEP_SUMMARY"
exit 1
fi

- name: Reproduce parity receipt
if: steps.check-receipt.outputs.has_receipt == 'true'
run: |
TARGET_SHA=$(node -p "require('./docs/migration/parity-receipt.json').refs.targetSha")
node scripts/migration/verify-clone-parity.mjs --target-ref "$TARGET_SHA" --out .parity-ci

- name: Compare CI receipt against committed reference
if: steps.check-receipt.outputs.has_receipt == 'true'
run: |
node scripts/migration/verify-receipt.mjs \
docs/migration/parity-receipt.json \
Expand Down
61 changes: 39 additions & 22 deletions docs/review/merge-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Values below were read from the GitHub branch-protection API for `main`.

| Setting | Measured value |
| --- | --- |
| Required status checks | `build-and-smoke (20)`, `build-and-smoke (22)`, **`Greptile Review`** |
| Required status checks | `build-and-smoke (20)`, `build-and-smoke (22)`, **`Greptile Review`**, `standard-candidate-consumption`, `SonarCloud Code Analysis` |
| `strict` (branch must be up to date) | `true` |
| `required_conversation_resolution` | `true` |
| `required_approving_review_count` | `0` |
Expand All @@ -65,9 +65,10 @@ Values below were read from the GitHub branch-protection API for `main`.
| `allow_force_pushes` / `allow_deletions` | `false` / `false` |
| Repository rulesets | none (`[]`) |

So a merge into `main` requires: the two `build-and-smoke` contexts and
`Greptile Review` green on an up-to-date head, and **every conversation resolved**.
No review approval is required.
So a merge into `main` requires: the two `build-and-smoke` contexts,
`Greptile Review`, `standard-candidate-consumption`, and `SonarCloud Code Analysis`
green on an up-to-date head, and **every conversation resolved**. No review approval
is required.

`Greptile Review` was added to the required contexts on 2026-08-12 after the
calibration in [`calibration-2026-08.md`](calibration-2026-08.md), and is pinned to
Expand All @@ -81,26 +82,32 @@ on a head carrying a P1 finding. The semantic half of the gate is
two are only meaningful together, and neither is a substitute for reading the
findings.

This was observed working end to end on PR #12 at head `76d495d`: all three required
contexts `success`, `SonarCloud Code Analysis` **failing but not required and
therefore not blocking**, and `mergeStateStatus=BLOCKED` on a single unresolved
Greptile P1 thread.
This was observed working end to end on PR #12 at head `76d495d`, **under the
three-context protection in force on 2026-08-12**: all three required contexts
`success`, `SonarCloud Code Analysis` failing but not required and therefore not
blocking, and `mergeStateStatus=BLOCKED` on a single unresolved Greptile P1 thread.

### Observed gap — recorded, not acted on here
That observation is kept as the record of the mechanism, not as a description of
current state: `SonarCloud Code Analysis` has since been promoted, and the same
failure on the same PR blocks today. What the observation still shows — that status
and conversation resolution are separate halves of the gate — is unaffected.

CI produces four check runs, but only two are required:
### Observed gap — narrowed since first recorded

CI produces four check runs. Three are required:

| Check run | Required? |
| --- | --- |
| `build-and-smoke (20)` | yes |
| `build-and-smoke (22)` | yes |
| `parity-receipt-reproduction` | **no** |
| `standard-candidate-consumption` | **no** |
| `standard-candidate-consumption` | yes — promoted since first recorded |
| `parity-receipt-reproduction` | **no** — promotion is META-337 |

A failure in either unrequired job does not block a merge today. That is a separate
governance decision from the reviewer question this document's owner issue covers,
and it is recorded here so it is not mistaken for a setting someone already chose
deliberately. Changing it is out of scope for META-322.
`parity-receipt-reproduction` is the one CI job whose failure still does not block a
merge. Promoting it is META-337's subject, and this PR is its prerequisite: the job
could conclude `success` without reproducing anything, so requiring it first would
have made a false green merge-authorizing. Absence is now a failure, which is what
makes the promotion safe to make.

### Refreshing this section

Expand All @@ -118,11 +125,21 @@ is stale — treat that as a documentation defect, and correct it in the PR that
noticed. A merge-eligibility claim that has drifted from the setting it describes
is worse than no claim, because it will be believed.

Measured on 2026-08-12 against `main` at `f61e0cb`, immediately after
`Greptile Review` was added to the required contexts. The earlier reading in
[`calibration-2026-08.md`](calibration-2026-08.md) — `main` at `a31242b`, two
required contexts — is the pre-policy baseline and is kept there as the *before*
half of the record, not as a description of current state.
Measured on 2026-08-13 against `main` at `70cfd57`. The two preceding readings are
kept as the *before* halves of the record, not as descriptions of current state:
`main` at `f61e0cb` on 2026-08-12 (three required contexts, immediately after
`Greptile Review` was promoted), and `main` at `a31242b` in
[`calibration-2026-08.md`](calibration-2026-08.md) (two required contexts,
pre-policy baseline).

The 2026-08-13 reading was taken because this section had already drifted: it
claimed three required contexts while the API returned five —
`standard-candidate-consumption` and `SonarCloud Code Analysis` had been promoted
without the change reaching this file. Corrected here under the rule directly above,
which is the first time that rule has been exercised. The drift is itself the
argument for the rule: the stale table said `SonarCloud Code Analysis` was
non-blocking, and a reader trusting it would have concluded PR #12 was mergeable
while it was in fact blocked on exactly that context.

## 3. Conversation resolution

Expand Down Expand Up @@ -187,7 +204,7 @@ First post-policy proof, PR #12 (META-285) at head `76d495d`, base `f61e0cb`:
| --- | --- |
| `build-and-smoke (20)` / `(22)` | success / success |
| `Greptile Review` | success — `5 files reviewed, 0 comments added` on this head |
| `SonarCloud Code Analysis` | **failure — not a required context, did not block** |
| `SonarCloud Code Analysis` | **failure — not a required context *at that time*, did not block. It is required as of the 2026-08-13 reading in §2, and the same failure blocks today** |
| `Sourcery review` | success (not required) |
| Unresolved threads | 1 — a Greptile P1 on `scripts/migration/verify-receipt.mjs` |
| `mergeStateStatus` | **`BLOCKED`** |
Expand Down
Loading