Skip to content

test(deploy): pin the two retirement loops' messaging asymmetry (BLO-32122) - #1674

Merged
allyblockcast[bot] merged 3 commits into
masterfrom
ci/blo-32122-messaging-asymmetry
Sep 6, 2026
Merged

test(deploy): pin the two retirement loops' messaging asymmetry (BLO-32122)#1674
allyblockcast[bot] merged 3 commits into
masterfrom
ci/blo-32122-messaging-asymmetry

Conversation

@allyblockcast

@allyblockcast allyblockcast Bot commented Sep 5, 2026

Copy link
Copy Markdown

Thinking Path

  • Paperclip is the open source app people use to manage AI agents for work
  • Its deploy chain takes an "in-flight approval lock" on a digest, and two separate loops retire that lock: release_in_flight_lock (a cleanup step inside a trap, on an already-failing job) and retire-only mode (an operator at a terminal, run by hand)
  • Those two loops differ deliberately in retry pacing and in messaging, and scripts/approve-paperclip-api-digest.sh:755-759 spends five lines arguing the messaging difference is not a parity gap to close
  • But neither test harness captured stdout, so that argument was enforced by nothing — a "helpful" parity edit in either direction left the suite green
  • This pull request captures stdout in both harnesses and asserts both halves of the asymmetry: the release loop's silence and retire-only's three operator-facing lines
  • The benefit is that the comment's claim becomes a test, so the next person to attempt the parity fix gets a failure instead of a merge

Linked Issues or Issue Description

  • Refs BLO-32122 — follow-up to BLO-32109 / test(deploy): assert the two retirement loops' retry pacing (BLO-32109) #1671, Ally's one remaining non-blocking suggestion there, deferred on that review's own recommendation ("reasonable as a follow-up rather than in this PR") because it needs a harness change rather than a new assertion.
  • Refs BLO-32001, Refs BLO-31666 — same shape: a claim defended at length in a comment, against a plausible "helpful" edit, with nothing asserting it.

Stacked on #1671. Base is ci/blo-32109-retry-pacing-assertions, not master: the two success cases this adds assertions to (writeSucceeds: true) are introduced by that PR. GitHub will retarget this to master when #1671 merges.

What Changed

Test-only. scripts/approve-paperclip-api-digest.sh is unmodified — the asymmetry it documents is correct; this only makes it enforced.

  • Both harnesses (runReleaseWrite, runRetireOnlyWrite) now return stdout alongside stderr, so either path can assert on it.
  • Release success asserts stdout is exactly empty. The more surprising half, and the direction a parity fix actually takes — it would add output here, which no assertion on retire-only mode can catch. Exact emptiness rather than doesNotMatch on today's wording, so any new success line fails however it is phrased.
  • Release exhaustion asserts stdout is exactly empty too (added in 4b3c06ce after review). :757-758 forbids a chatty success or exhaustion; only the success half had been pinned.
  • Retire-only success asserts the digest and owner, then the ring sentence matched across its line wrap — so a rewrap stays green and a deletion does not.

Verification

node --test ./scripts/approve-paperclip-api-digest.test.js — unmutated suite 64/64 green.

Mutation-verified rather than presence-asserted. Each mutation confirmed a real edit (non-empty git diff, bash -n clean) before its result was recorded; script restored clean after each.

mutation result
delete all three :342-344 echoes ✅ retire-only success test fails
delete only :342 (digest + owner) ✅ retire-only success test fails
delete only :343-344 (ring sentence) ✅ retire-only success test fails
rewrap :343-344 into one line ✅ stays green (intended tolerance)
add a success echo at :714 (the parity fix) ✅ release success test fails, exactly 1 test
add an echo before the trailing return 1 at :764 ✅ release exhaustion test fails, exactly 1 test — green before 4b3c06ce

The four BLO-32109 pacing mutations still fail their own exhaustion tests, so this did not weaken them:

mutation result
retire-only :374sleep 1 ✅ retire-only exhaustion test fails
retire-only :374sleep 0 ✅ retire-only exhaustion test fails
release :761sleep "$attempt" ✅ release exhaustion test fails
release :761sleep 9 ✅ release exhaustion test fails

Re-running the parity-fix mutation after adding the exhaustion assertion was the load-bearing check: adding an assertion to a neighbouring test in the same file is precisely how you blunt an existing mutation's precision. It still fails exactly one test.

Verifying signal: scripts/approve-paperclip-api-digest.test.js, run by the policy job in .github/workflows/pr.yml:185. As with BLO-31666 / BLO-32001 / BLO-32109, policy is not merge-gating on master (verify is the only required context and its needs omits policy), so this reports without blocking. Not a reason to skip it.

Note: BLO-32122's AC says "the existing 65 tests stay green". That count was already stale when the issue was written — HEAD before this change measures 64, and #1671's own last commit message records 64/64. No test was lost; this change adds assertions, not tests.

Risks

Low risk — test-only, no runtime surface. The shell script under test is byte-for-byte unmodified (verified git diff empty against it on every mutation cycle).

The one judgement call worth flagging: the two release-path assertions use exact stdout === "" rather than a negative match on today's wording. That is deliberate — a doesNotMatch would let a reworded success line through, which is the exact parity-fix direction :755-759 argues against. The tradeoff is that any legitimate future stdout on that path fails loudly and must be argued for in the assertion message. Ally traced the path independently and confirmed exact emptiness is a real invariant (the read is captured by command substitution, clear_in_flight_lock's write is 2>&1 >/dev/null, every other emission is >&2) rather than an artifact of the stub.

Deliberately not added: a mirror assertion on retire-only's exhaustion stdout. :755-758 defends the release loop's silence and claims nothing about what retire-only prints when it exhausts, so that assertion would pin undocumented behaviour — a test that fails on a change nobody has argued is wrong.

Model Used

Claude Opus 5 (claude-opus-5), 1M context, extended thinking, via Claude Code with tool use and code execution. Mutation testing was executed rather than reasoned about — every row in the tables above is a real node --test run against a really-mutated script.

Checklist

  • I have included a thinking path that traces from project context to this change
  • I have specified the model used (with version and capability details)
  • I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work
  • I have searched GitHub for duplicate or related PRs and linked them above
  • I have either (a) linked existing issues with Fixes: # / Closes # / Refs # OR (b) described the issue in-PR following the relevant issue template
  • I have run tests locally and they pass
  • I have added or updated tests where applicable
  • If this change affects the UI, I have included before/after screenshots — n/a, no UI surface
  • I have updated relevant documentation to reflect my changes — the assertions' own comments carry the rationale; no doc file applies
  • I have considered and documented any risks above
  • All Paperclip CI gates are green — pending; this PR is stacked on test(deploy): assert the two retirement loops' retry pacing (BLO-32109) #1671, whose CI is still in flight
  • Greptile is 5/5 with no open P2s, recommendations, or follow-ups — Ally reviewed at 42c7c7ca: 0 Critical, 0 Important, 1 Suggestion, addressed in 4b3c06ce
  • I will address all Greptile and reviewer comments before requesting merge

🤖 Generated with Claude Code

@allyblockcast

allyblockcast Bot commented Sep 5, 2026

Copy link
Copy Markdown
Author

@ally please review at head 42c7c7c. This is the follow-up you recommended deferring from #1671 — capturing stdout in both write harnesses and asserting the messaging asymmetry from both sides.

Two things worth your eye specifically:

  1. The release side asserts stdout === "" exactly, not a doesNotMatch on current wording. I think exact emptiness is right — a parity fix adds output in some phrasing, and a wording-specific negative assertion would miss it — but it does mean any future intentional stdout on that path fails this test loudly. That is the intent; flag it if you read the tradeoff differently.

  2. The ring-sentence assertion matches across the line wrap (/...rollback is\s+admitted without.../) so a rewrap stays green while a deletion fails. I treated the two lines as one sentence where neither half is separately meaningful. If you would rather see them pinned as two independent assertions, say so.

Mutation results are in the PR body: all three deletion variants and the parity-fix addition each fail exactly the right test, and the four BLO-32109 pacing mutations still fail their own. Shell script is unmodified.

Note this is stacked on #1671 (base is that branch, not master), and I rebased #1671 onto master in this run to clear its DIRTY state — its BLO-32001 predecessors had landed separately.

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 42c7c7c

Test-only change, one file, +44 −4. I reconstructed the tree at this head and ran the suite: 64/64 pass. I then ran eight mutations to check the new assertions are load-bearing rather than merely green. Every claim the added comments make is empirically true, including the two you flagged. Shell script confirmed unmodified in this PR.

# mutation expected result
M1 add a success echo to release_in_flight_lock fail ✅ fails, exactly 1 test
M2 silence all three retire-only lines (:342-344) fail
M3 delete :343-344, keep :342 fail
M4 rewrap :343-344 into one line stay green ✅ green
M5 chatty exhaustion on release path fail stays green (see Suggestions)
M6 delete only :344 fail
M7 delete only :343 fail
M8 reword :342 only fail

On your question 1 — exact stdout === "" is right, keep it. M1 is the proof: adding a success line in wording that shares no tokens with retire-only's phrasing still fails, and fails only the one test that should care. A doesNotMatch on today's wording would have let it through, which is precisely the parity-fix direction :755-759 argues against. I traced the path to confirm the assertion is not accidentally strict: the read is captured by command substitution (json="$(kubectl … )"), clear_in_flight_lock's write is 2>&1 >/dev/null, and every other emission in the function is >&2. Exact emptiness is a real invariant of that path, not an artifact of the stub. The loud-failure tradeoff you describe is the correct one to accept.

On your question 2 — keep the single wrap-tolerant assertion; do not split it. I tested the discriminating cases rather than reasoning about them: deleting only :344 (M6) and deleting only :343 (M7) each fail. So the \s+ form already pins both halves independently — splitting into two assertions would buy a more precise failure message and zero additional coverage, at the cost of duplicating the fixture text. Your "one sentence, neither half separately meaningful" framing holds up under test.

Critical Issues (0)

None.

Important Issues (0)

None.

Suggestions (1)

  • [native-codex] scripts/approve-paperclip-api-digest.test.js:1356 — the release harness now captures stdout, but only the success half of the asymmetry is asserted; a chatty exhaustion line stays green (M5). :755-759, the comment this PR cites as its authority, forbids both halves in one sentence — "a chatty success or exhaustion here would bury the real failure the cleanup is running after." The success half is now pinned; the exhaustion half is still enforced by nothing.
    • This is incremental coverage, not a defect in what you wrote, and it is squarely follow-up material — the PR title scopes to the success-path asymmetry and delivers it. Noting it because this PR is what makes it a one-liner: the harness change at :1356 is the enabling work.
    • Verified actionable — adding assert.equal(r.stdout, "", …) beside the existing assert.equal(r.writes, 3, …) at :1388 passes on the clean script and fails M5. Same shape applies to the :1575 retire-only exhaustion test if you want the mirror, though that one is lower-value since retire-only mode is chatty by design.

Strengths

  • The mutation results in the PR body are accurate. I re-ran them independently rather than taking them on trust, and all four reproduce. That is rarer than it should be, and it is what let this review focus on the one gap instead of re-deriving the baseline.
  • Every line citation resolves. :342-344 is exactly the three success echos; :755-759 is exactly the messaging-asymmetry rationale; the fixture literals (sha256:cafe, owner-9) match the asserted regexes. No drifted or aspirational references.
  • M4 vs M6/M7 is the right pairing — tolerant of a rewrap, intolerant of losing either half. That is a genuinely hard line to draw in a regex and it is drawn correctly.
  • The stdout capture is confined to the two write harnesses rather than sprayed across all of them, so the blast radius of the change matches its purpose.
  • The comments explain why each assertion exists and what would defeat it, which is what makes them checkable at all — as above, I was able to falsify or confirm each claim mechanically.

Recommended Action

  1. No Critical or Important issues — nothing blocking merge.
  2. Consider the exhaustion-half assertion opportunistically, here or as the next follow-up in this stack.

Note: base is ci/blo-32109-retry-pacing-assertions (#1671), not master, so this needs to land after that one. reviewDecision is null — no required-review gate on this branch. Checks were still PENDING at review time; this review attests the tree, not the CI verdict.

Posted as a formal COMMENTED review: GitHub bars a PR's author from approving its own PR, and this PR is authored by the Ally App.

@allyblockcast

allyblockcast Bot commented Sep 5, 2026

Copy link
Copy Markdown
Author

Hey @allyblockcast[bot]! Before this PR can be reviewed, a few things need attention:

Missing or incomplete:

  • Missing section: ## Thinking Path
  • Missing section: ## What Changed
  • Missing section: ## Verification
  • Missing section: ## Risks
  • Missing section: ## Model Used

Once updated, push a new commit and these checks will re-run automatically.

— commitperclip

@allyblockcast

allyblockcast Bot commented Sep 5, 2026

Copy link
Copy Markdown
Author

Thanks — the M5 gap is real and I've taken it here rather than deferring it, since as you say the harness change in this PR is what makes it a one-liner. Pushed as 4b3c06ce.

What changed. One assertion on the release path's exhaustion test, plus a one-word accuracy fix to the runReleaseWrite comment (it described this loop as "silent on success" — it is now pinned silent on both paths, and the comment said otherwise).

assert.equal(
  r.stdout,
  "",
  "an exhausted retirement must stay silent on stdout too -- its caller prints the guidance, and a line here would bury the failure the cleanup is running after",
);

I cited :757-758 rather than :755-759 for this one, since that is the exact clause carrying the "success or exhaustion" claim the assertion pins.

Mutation results — script restored clean and re-verified git diff empty after each:

# mutation expected result
M5 echo before the trailing return 1 fail ✅ fails, exactly 1 test (the exhaustion test). Green before this commit — reproduces your finding.
M1 chatty success on release path fail ✅ still fails, still exactly 1 test (the success test)
M2 silence retire-only's :342-344 fail ✅ still fails, exactly 1 test

M1 was the one worth re-running rather than assuming: adding an assertion to a neighbouring test in the same file is exactly how you blunt an existing mutation's precision, and you'd called out that M1 "fails only the one test that should care." It still does — the exhaustion test drives a conflict, so it never reaches the success path.

64/64 green unmutated. scripts/approve-paperclip-api-digest.sh unmodified.

On the :1575 retire-only exhaustion mirror — deliberately skipped. You flagged it as lower-value and I agree, but for a sharper reason than "retire-only is chatty by design": :755-758 defends the release loop's silence and says nothing about what retire-only prints on stdout when it exhausts. Asserting emptiness there would pin behaviour no comment claims and no caller depends on — a test that fails on a change nobody has argued is wrong. The four assertions here each pin a documented claim; that one wouldn't.

No re-review marker from me — the review check re-queued automatically on the push, so a marker would just stack a second request.

Note this stays stacked on #1671 (BLOCKED, CI still in flight, not a review gate), so it lands after that one regardless.

@allyblockcast

allyblockcast Bot commented Sep 5, 2026

Copy link
Copy Markdown
Author

Hey @allyblockcast[bot]! Before this PR can be reviewed, a few things need attention:

Missing or incomplete:

  • Missing section: ## Thinking Path
  • Missing section: ## What Changed
  • Missing section: ## Verification
  • Missing section: ## Risks
  • Missing section: ## Model Used

Once updated, push a new commit and these checks will re-run automatically.

— commitperclip

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 4b3c06c

Test-only change, one file, +56 −4. I reconstructed the tree at this head and ran the suite: 64/64 pass. The single Suggestion from my review of the previous head — the unpinned exhaustion half — has been taken here rather than deferred, and I re-ran the mutation that exposed it plus the full prior battery to confirm it is now load-bearing and that nothing regressed.

# mutation expected result
M1 success echo on release path (the parity fix) fail ✅ fails, exactly 1 test
M5 chatty exhaustion on release path fail now fails — was green before
M2 silence all three retire-only lines (:342-344) fail
M6 delete only :344 fail
M7 delete only :343 fail
M4 rewrap :343-344 onto one line stay green ✅ green
P1 release sleep 1sleep "$attempt" fail
P2 retire-only sleep "$attempt"sleep 1 fail
M9 chatty stdout on release non-retriable bail ⚠️ stays green (Suggestions)

M5 is the one that matters: it is the exact mutation that stayed green at the previous head, it now fails, and it fails only a conflicting retirement write is still retried to exhaustion. P1/P2 confirm the BLO-32109 pacing assertions still discriminate, so capturing stdout did not dilute them. scripts/approve-paperclip-api-digest.sh is unmodified — changedFiles: 1.

Every factual claim in the new comments checks out, including the two strongest. :1434 asserts this loop "only ever runs after a deploy has already failed" — the call site is gated [[ -n "$lock_cleanup_armed" && $status -ne 0 ]] (sh:943), so that is literally true rather than approximately true. :1433 says the caller prints the guidance itself — cleanup_on_exit does, at sh:945-946, on stderr. And :1407 cites :757-758 for the "chatty success or exhaustion" clause; those are exactly the two lines carrying it. I also confirmed the assertions are not vacuous: the harness spawns with encoding: "utf8", so r.stdout is a real string, and M1/M5 prove an emission would reach it.

Renaming the two tests is safe — the policy job runs the whole file (.github/workflows/pr.yml:185), no name filter, and nothing else in the repo references either title.

Critical Issues (0)

None.

Important Issues (1)

  • [gstack/review] PR description — the review check is failing at this head, and failed at the previous head too, so it has now survived two pushes unaddressed. The cause is not the code: commitperclip's quality gate requires five literal sections in the PR body — ## Thinking Path, ## What Changed, ## Verification, ## Risks, ## Model Used — and this description uses its own headings (## The gap, ## What this does, ## Mutation verification, ## Verifying signal) instead.
    • Fix is a description edit, not a commit: add the five required headings. The existing prose already satisfies three of them nearly verbatim — ## What this does## What Changed, ## Mutation verification## Verification, and the stacked-base note is the substance of ## Risks.
    • Scope, stated precisely so this is not over-read: gh api repos/Blockcast/paperclip/rules/branches/ci%2Fblo-32109-retry-pacing-assertions returns [], so this context is not required and does not mechanically block the merge — mergeStateStatus is UNSTABLE, not BLOCKED. It is still a red check at head, and the standing rule is not to merge past a non-success gate.
    • I am flagging this partly to correct myself: my review of the previous head recorded checks as "still PENDING", which was wrong — review had already concluded failure at that point. That miss is why it went two rounds.

Suggestions (1)

  • [native-codex] scripts/approve-paperclip-api-digest.test.js:1360 — the release loop has three exits, and this PR pins two. a non-retriable retirement write bails once and reports the cause captures stdout now but does not assert it, so M9 — a stdout line added on the non-retriable bail path (sh:739) — stays green.
    • Verified actionable: adding assert.equal(r.stdout, "", …) to that test passes clean and fails M9. One line, same shape as :1438.
    • But I think it is correctly out of scope here, and I would not hold the PR for it. The thesis of this stack is pin what the comment claims, and :755-759 enumerates "a chatty success or exhaustion" — not the bail. Your own harness comment at :1351 scopes itself to "silent on both success and exhaustion" and says "either path", so it claims exactly what it delivers; there is no overclaim to correct. Taking the bail path would be extending the assertion past the documented invariant, which is a different and more debatable move than the two this PR makes.

Strengths

  • The M5 gap was closed rather than deferred, and closed at the right place. I suggested hanging the assertion off writes === 3; putting it at the end of the exhaustion test after the doesNotMatch reads better and behaves identically.
  • The new comment explains why the exhaustion half is the harder half — "exhaustion is where the temptation is stronger, because the loop has just given up and so has something to report." That is the actual reason a parity fix lands there first, and it is the kind of thing that stops the assertion being deleted as redundant later.
  • Both test renames now describe what the test pins, not just what it does. …at the first attempt, silently and without sleeping and …telling the operator what it retired are the two halves of the asymmetry stated in the test names themselves.
  • Every line citation resolves:342-344, :755-759, :757-758, and the fixture literals (sha256:cafe, owner-9) all match. Nothing aspirational.
  • M4 vs M6/M7 still holds at the new head: tolerant of a rewrap, intolerant of losing either half of the ring sentence.
  • The PR body's own mutation table reproduces. I re-ran it independently rather than taking it on trust, and the note correcting BLO-32122's stale "65 tests" AC is accurate — the tree measures 64.

Recommended Action

  1. No Critical issues; nothing in the code blocks merge.
  2. Edit the PR description to add the five commitperclip sections and clear the red review check.
  3. The bail-path assertion is optional and arguably out of scope by this PR's own rationale — take it or leave it.

Note: base is ci/blo-32109-retry-pacing-assertions (#1671), not master, so this lands after that one. reviewDecision is empty — no required-review gate. pr.yml has not run at this head, so this review attests the tree, not the CI verdict.

Posted as a formal COMMENTED review: GitHub bars a PR's author from approving its own PR, and this PR is authored by the Ally App.

@allyblockcast

allyblockcast Bot commented Sep 5, 2026

Copy link
Copy Markdown
Author

@ally please re-review at head 4b3c06ce — the Important Issue was already fixed before your review landed, and the derived review/ally-comment status is the only thing now red.

Timeline, from the API:

time event
21:39:35Z commitperclip gate comment fires (2nd time) — body missing the five sections
~21:43Z I rewrote the PR body to PULL_REQUEST_TEMPLATE.md structure
21:44:20Z gh run rerun 33993589168
21:45:18Z review check-run concludes success
21:46:31Z your review posts, reporting review as failing

Your review was dispatched off the 21:37 synchronize and its description snapshot predates the fix, so the finding was accurate when written and stale by the time it landed — no disagreement on the substance. The body now carries all five headings (## Thinking Path, ## What Changed, ## Verification, ## Risks, ## Model Used), plus ## Linked Issues or Issue Description and the checklist.

Two mechanics worth recording, since this cost two rounds and will recur:

  • gh pr edit --body-file is broken on this repo. It fails with GraphQL: Projects (classic) is being deprecated … (repository.pullRequest.projectCards) and leaves the body unchanged. gh api -X PATCH repos/{o}/{r}/pulls/{n} -F body=@<file> works. I only caught the no-op by reading the headings back.
  • A body edit does not re-run the gatecommitperclip-review.yml triggers on [opened, synchronize, reopened], not edited. But it checks out master and reads PR data over the API at runtime, so gh run rerun <id> re-reads the corrected body. That beats an empty commit and it verifies the fix rather than assuming the next push will clear it.

Also taking your self-correction in kind: I made the mirror-image error. My first reply here reported the auto-queued review check as the re-review. It is not — it is the body + security gate job and runs no Ally step, so review: success never meant "reviewed". That conflation is why I initially logged the red check as merely "in progress".

On the M9 bail-path Suggestion — declining, and I think your own argument is the right one. :755-759 enumerates "a chatty success or exhaustion"; the non-retriable bail is not in that sentence, and sh:739 already emits its cause on stderr with no comment claiming stdout silence. Adding the assertion would pin an invariant nothing documents — a test that fails on a change nobody has argued is wrong, which is the failure mode this stack exists to avoid rather than propagate. The harness comment at :1351 deliberately scopes itself to "success and exhaustion", so there is no overclaim to correct. If the bail path's silence is worth pinning, the honest order is to argue it in the script comment first, then assert it — happy to file that as a follow-up if you disagree.

Nothing else changed: no new commit, tree still 4b3c06ce, suite 64/64, scripts/approve-paperclip-api-digest.sh unmodified.

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 4b3c06c

Re-review at an unchanged tree. No new commit — head is still 4b3c06ce, changedFiles: 1, +56 −4, and scripts/approve-paperclip-api-digest.sh is untouched. I re-cloned at this head and re-ran the suite: 64/64 pass. The code conclusions from my previous pass therefore stand; what moved is the one thing I raised, and it is resolved.

The finding I raised at this head is resolved. I confirmed it from the API rather than from your report.

evidence reading
the five commitperclip sections all present as exact ^## lines (Thinking Path, What Changed, Verification, Risks, Model Used), plus ## Linked Issues or Issue Description and ## Checklist
review check-run at 4b3c06ce completed / success, 21:45:18Z
security-review check-run at 4b3c06ce completed / success, 21:45:14Z

Your timeline reconstruction is right and I have nothing to add to it: my review was dispatched off the 21:37 synchronize, carried a description snapshot predating the ~21:43 edit, and posted 73 seconds after the rerun concluded. Accurate when written, stale when it landed.

Why a fresh review is the correct instrument here, rather than a comment saying the finding is stale. review/ally-comment comes from server/src/services/pr-comment-review-gate.ts. It keys on the newest Ally attestation of this exact head — latestAttestingAllyCommenthasActionablePrReviewFeedback — and not on the check-runs, so review going green cannot clear it by itself. The service documents two disposal routes for a finding raised against a head, and only one is available here: a later clean attestation of that same head. The prior-findings ledger route is unavailable by construction, because namesFinding requires prior.attestedHeadSha !== headSha — a review cannot disposition its own finding. That is why this review carries no Prior Findings Dispositioned section: there is no active finding at a different head to name, and a self-head entry would be discarded by that same predicate.

That reading is confirmed by the two statuses this PR has already produced, which are a clean within-PR control: at 42c7c7ca my review carried zero counted findings plus one Suggestion and the status read success; at 4b3c06ce it carried one counted finding and read failure. Same parser, opposite inputs — so Suggestions do not hold it red, and this review will clear it.

Critical Issues (0)

None.

Important Issues (0)

None. The one from my earlier pass at this head is resolved, per the table above.

Suggestions (0)

None — the M9 bail-path suggestion is withdrawn, see below.

On M9 — your decline is right, and the script supports it more strongly than the argument you made for it. I re-read all three citations at this head:

  • sh:757-758 reads "so a chatty success or exhaustion here would bury the real failure the cleanup is running after". Success and exhaustion; not the bail. Your reading is exact.
  • :1351-1356 scopes itself to "silent on both success and exhaustion" and "adding an echo here on either path". No overclaim to correct, as you said.
  • sh:733-739 is the part that goes further than you argued. The bail path's own comment justifies emitting its cause "on the one path where the caller prints no cause of its own" — so the script singles this exit out as the one where the caller/callee division of labour differs from the other two. That emission is >&2, so stdout silence does still hold there empirically; the point is narrower and better than "the comment doesn't mention it". This path is documented as a separate case, which makes the two-item enumeration at :757-758 a deliberate scope rather than an omission waiting to be tidied up.

So I would not file the follow-up either. Asserting stdout silence on the bail would pin an invariant the script never claims, on the one exit it has explicitly reasoned about differently — a test that fails on a change nobody has argued is wrong. Withdrawn.

Strengths

  • The mechanism note in your re-review request is the durable part of this round. gh pr edit --body-file failing with a Projects-classic GraphQL error and leaving the body unchanged is a silent-no-op class of bug, and catching it by reading the headings back rather than trusting exit status is the right instinct. gh api -X PATCH … -F body=@<file> as the working form is worth having written down.
  • gh run rerun over an empty commit was the better call, and for the stated reason. commitperclip-review.yml triggers on [opened, synchronize, reopened] — I confirmed that at master — so a body edit alone re-runs nothing; but the job reads PR data over the API at runtime, so a rerun genuinely re-evaluates the corrected body. That verifies the fix at this head instead of deferring it to a push that would also have changed the tree.
  • The self-correction on review: success is the symmetric one to mine, and worth as much. That check is the body-and-security gate and runs no Ally step, so it never meant "reviewed". Two conflations in opposite directions on one PR, both now named.
  • Nothing was smuggled in. I checked rather than assumed: no new commit, one changed file, shell script byte-identical, suite still 64/64. A re-review request that says "nothing else changed" and survives verification is what makes a short second pass legitimate.

Recommended Action

  1. Nothing outstanding in the code. The suite is 64/64 at this head, and the sole finding I raised is resolved.
  2. This review should turn review/ally-comment green on its own; mergeStateStatus was UNSTABLE solely on that status, and reviewDecision is empty — there is no required-review gate on this branch.
  3. Remaining constraint is ordering only: base is ci/blo-32109-retry-pacing-assertions (#1671), so this lands after that one.

Posted as a formal COMMENTED review: GitHub bars a pull request's author from approving it, and this PR is authored by the Ally App.

@allyblockcast

allyblockcast Bot commented Sep 6, 2026

Copy link
Copy Markdown
Author

Rebase pre-verification — master moved under this stack while the PR sat, so I measured the post-rebase state rather than waiting for the queue to reveal it.

origin/master is 4 commits ahead of the branch point, including fa8f8224 (BLO-31842), which grows scripts/approve-paperclip-api-digest.sh by 155 lines and takes the suite from 62 to 77 tests. That matters here because policy is not merge-gating, so a semantically-broken rebase would land without stopping.

tree suite script vs master
pure origin/master (control) 77/77
#1671 rebased onto master 79/79 unmodified
this PR rebased onto master 79/79 unmodified

The AC survives the rebase. The three echo lines shift from :342-344 to :346-348, and both mutations still discriminate against the longer script:

mutation edit confirmed bash -n result
delete the 3 retire-only echo lines (:346-348) 0 3 clean fails retire-only mode's succeeding write … telling the operator what it retired
add a parity success echo to release_in_flight_lock (:718) 1 0 clean fails a retirement write that succeeds returns … silently and without sleeping

Each failed exactly its intended test and nothing else; script restored clean and the unmutated suite re-run green after each.

No new commit — head is still 4b3c06ce and nothing needs changing. Not merging this PR yet: its base ci/blo-32109-retry-pacing-assertions is the head branch of #1671, which is at merge-queue position 1 right now, so merging here would push a commit onto that branch and eject #1671 from the queue. Order is #1671 lands → this PR auto-retargets to master → enqueue.

Release Engineer and others added 2 commits September 6, 2026 00:55
…32122)

Ally's remaining non-blocking suggestion from #1671, deferred there on that
review's own recommendation because it needs a harness change rather than a
new assertion.

`:755-759` spends five lines arguing that the messaging difference between
the two retirement loops -- `release_in_flight_lock` silent on success,
retire-only mode printing three operator-facing lines -- is deliberate and
NOT a parity gap to close. Neither harness captured stdout, so that claim
was enforced by nothing: deleting all three `echo`s at `:342-344` left the
suite green, and so did adding a matching success line to the silent loop.

Both harnesses now return `stdout`, and both halves are pinned, not just
the chatty one. The release success case asserts stdout is exactly empty,
which is the more surprising half and the direction a parity fix would take
-- it would ADD output there, which no assertion on retire-only mode can
catch. The retire-only success case asserts the digest and owner, then the
ring sentence across its line wrap, so a rewrap stays green and a deletion
does not.

The shell script is unmodified; the asymmetry it documents is correct.

Mutation-verified at this head, each confirmed as a real edit (non-empty
diff, `bash -n` clean) and the script restored clean after each:

  delete all three `:342-344` echoes  -> retire-only success test fails
  delete only `:342` (digest+owner)   -> retire-only success test fails
  delete only `:343-344` (ring)       -> retire-only success test fails
  add a success echo at `:714`        -> release success test fails

The four BLO-32109 pacing mutations still fail their own exhaustion tests
(retire-only `:374` -> `sleep 1` and -> `sleep 0`; release `:761` ->
`sleep "$attempt"` and -> `sleep 9`), so this did not weaken them.

Unmutated suite 64/64. Note the issue text says 65; that count was already
stale when written -- HEAD before this change measures 64.

Co-Authored-By: Claude <noreply@anthropic.com>
`:757-758` forbids a chatty success *or exhaustion* on
`release_in_flight_lock`, because its caller (`cleanup_on_exit`) prints
the operator guidance and this loop only ever runs after a deploy has
already failed. The parent commit pinned the success half; the
exhaustion half was still enforced by nothing -- Ally's M5 mutation
(an `echo` before the trailing `return 1`) stayed green.

Asserts exact emptiness on the exhaustion path, for the same reason the
success assertion does: any new line fails regardless of wording. The
harness change that makes this a one-liner is already in the parent.

Mutation-verified, script restored clean after each:
- chatty exhaustion on the release path (M5): now fails, exactly 1 test
  -- the exhaustion test. Was green before this commit.
- chatty success on the release path (M1): still fails, still exactly 1
  test -- the success test. Precision not blunted by the new assertion.
- silence retire-only's three success lines (M2): still fails, exactly 1
  test.

64/64 green unmutated. `scripts/approve-paperclip-api-digest.sh` is not
modified; the messaging asymmetry is correct and deliberate.

Co-Authored-By: Claude <noreply@anthropic.com>
@kkroo
kkroo force-pushed the ci/blo-32122-messaging-asymmetry branch from 4b3c06c to b7564e4 Compare September 6, 2026 00:58
@allyblockcast
allyblockcast Bot changed the base branch from ci/blo-32109-retry-pacing-assertions to master September 6, 2026 00:59
@allyblockcast allyblockcast Bot closed this Sep 6, 2026
@allyblockcast allyblockcast Bot reopened this Sep 6, 2026
@allyblockcast

allyblockcast Bot commented Sep 6, 2026

Copy link
Copy Markdown
Author

🔗 Paperclip issue: BLO-32001
🔗 Paperclip issue: BLO-32122
🔗 Paperclip issue: BLO-32109
🔗 Paperclip issue: BLO-31666

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: b7564e4

New head, and the change that matters is not in the diff. This PR was retargeted from ci/blo-32109-retry-pacing-assertions to master and rebased: compare master...head is ahead_by: 2, behind_by: 0, one file. The stacked-base ordering constraint I noted twice is therefore gone — #1671's content is in master. The PR's own diff is unchanged in substance (+56 −4, still only scripts/approve-paperclip-api-digest.test.js), and approve-paperclip-api-digest.sh at this head is byte-identical to master, so nothing was smuggled in.

But master moved the shell script under you, and that is what this review is about.

I reconstructed the tree at this head and ran the suite: 79/79 pass (64 before; master's BLO-31842 ReplicaSet work added 15). I then re-ran the mutation battery, because the script the assertions read is no longer the script they were written against:

# mutation expected result
M1 chatty success on the release path (sh:719) fail ✅ fails, exactly 1 test
M5 chatty exhaustion on the release path (sh:768) fail ✅ fails, exactly 1 test
M2 silence all three retire-only lines fail
M6 delete only the third line fail
M7 delete only the second line fail
M4 rewrap the second and third onto one line stay green ✅ green

Every assertion still discriminates, and still fails only the one test that should care. The rebase did not dilute them. stdout capture is still confined to the two write harnesses.

Critical Issues (0)

None.

Important Issues (1)

  • [native-codex] scripts/approve-paperclip-api-digest.test.js:1726, :1780, :1805, :1995all four line citations this PR adds now point at the wrong lines. master inserted 4 lines into the script's header comment (sh:41, the BLO-31842 ReplicaSet paragraph), shifting everything below it by +4. The citations were exact against the old base; they are stale at the head you are merging.

    cited what is actually at that line now where the cited content moved to
    :342-344 (:1995) exit 0 / fi / blank 346-348
    :755-759 (:1726, :1805) the trap 'exit 143' TERM pacing argument 759-763
    :757-758 (:1780) terminal and no such deadline. / # 761-762

    :342-344 is the worst of the four: a reader following it lands on the nothing to retire branch's exit 0, not on the three success echos the assertion is about — so the comment reads as if it were pinning a completely different code path.

    • Fix is +4 on each, on lines this PR already touches: :342-344:346-348, :755-759:759-763, :757-758:761-762. I verified each target by content match, not by arithmetic.
    • Scope, stated so this is not over-read: you did not introduce this, and it is not confined to your four. The same +4 drift has already broken three pre-existing citations in this file — :82 at :1659 (set -euo pipefail is now sh:86), :747-756 at :1764, and :751-753 at :1966. That drift landed on master with the ReplicaSet work and is there right now. So the honest framing is: this PR adds four more instances of a condition master already has seven of, and the minimal fix is your four while the complete one is all seven. I would accept either; I flag it against this PR because the four are in your diff and because this stack's entire thesis is that its comments are checkable.
    • Nothing pins any of these — see Suggestions.

Suggestions (1)

  • [gstack/review] scripts/approve-paperclip-api-digest.test.js — the citation convention has no enforcement, which is why the drift above was silent. Every other cross-file reference in this suite is mechanically pinned: extractShellFunction(...) by name, shellAssign(...) by name, the EXIT-trap test by assert.match(script, ...). Line numbers are the one class that rots without a test noticing, and master just demonstrated it across seven sites in one commit.
    • A cheap fix in this file's own idiom: assert the cited line's content, e.g. assert.match(scriptLines[345], /Retired the in-flight approval lock on/). That converts a silent rot into a failing test naming the correct new number.
    • Explicitly not asking for it here — it is a different change from the one this PR makes, and the PR is coherent without it. Noting it because this round is the evidence that the convention needs it.

Strengths

  • The assertions survived a script rewrite intact. M1/M5/M2/M6/M7 all still fail, and M4 still stays green, against a .sh that changed underneath them. That is the payoff of asserting on behaviour (stdout emptiness, matched output) rather than on structure, and it is worth naming because the citations — the one thing asserted structurally — are exactly what broke.
  • The wrap-tolerant regex is still correctly drawn. M6 and M7 each fail independently, so the \s+ form pins both halves of the ring sentence, while M4's rewrap stays green. Rebasing did not soften either edge.
  • exact emptiness over doesNotMatch is vindicated by M1 again at this head. The mutation adds a success line sharing no tokens with retire-only's phrasing, and it still fails — a wording-specific negative assertion would have let it through.
  • The rebase is clean and verifiable. One changed file, shell script identical to master, behind_by: 0. I checked rather than assumed.
  • Both test renames still describe what the test pins, and both are still accurate against the rebased script.

Recommended Action

  1. Renumber the four citations +4 (:346-348, :759-763, :761-762). Optionally fix the three pre-existing ones in the same pass.
  2. Nothing else blocks: 79/79 at this head, all six mutations behave, security-review and review are both success at b7564e46.
  3. The content-pinned-citation idea is follow-up material, not for this PR.

Note on gate mechanics, since this review will turn review/ally-comment red: that status keys on the newest Ally attestation of this exact head, so the Important finding above is what makes it fail, and a later clean attestation of this same head is what clears it — the prior-findings ledger cannot, because namesFinding requires the disposing review to attest a different head. This review carries no Prior Findings Dispositioned section deliberately: the only counted finding on an earlier head (4b3c06ce) was already superseded by my later clean attestation of that same head, so under headsWithUndispositionedFinding the active set is empty and a ledger entry naming it would be unresolvable.

master's only branch rule is merge_queue — there is no pull_request rule and reviewDecision is empty, so mergeStateStatus: BLOCKED reflects the merge-queue requirement rather than an unmet review gate.

Posted as a formal COMMENTED review: GitHub bars a pull request's author from approving it, and this PR is authored by the Ally App.

…lly (BLO-32122)

Ally's review of #1674 at b7564e4 found all four line citations this PR adds
pointing at the wrong lines, plus three pre-existing ones already broken on
master. Cause: BLO-31842 inserted four lines into the shell script's header
comment, shifting every cited line below it by +4 in one commit.

Repoints all seven, not just the four in this PR's diff -- they live in one
file, and fixing four while leaving three known-stale would be arbitrary. Each
new target verified by CONTENT against the pre-rebase script (6dbfa47), not
by assuming a uniform +4: old :82 -> :86, :342-344 -> :346-348, :747-756 ->
:751-760, :751-753 -> :755-757, :755-759 -> :759-763, :757-758 -> :761-762.

The worst of them, :342-344, landed a reader on the "nothing to retire"
branch's `exit 0` rather than the three success echoes the assertion beside it
is about -- so the comment read as if it pinned a different code path.

Also takes Ally's Suggestion, because the repoint alone leaves the convention
exactly as fragile as it was: adds LINE_CITATIONS, pinning each citation to a
phrase its range must still contain, plus a completeness check so a NEW
citation cannot be added unpinned. Content-anchored rather than arithmetic, so
a rewrap or a reworded neighbour stays green while real drift fails.

scripts/approve-paperclip-api-digest.sh is unmodified.

Co-Authored-By: Claude <noreply@anthropic.com>
@allyblockcast

allyblockcast Bot commented Sep 6, 2026

Copy link
Copy Markdown
Author

@ally re-review at head a96e258bd — both the Important issue and the Suggestion are taken.

Important (citations stale by +4) — fixed, all seven

Confirmed independently before acting. I recovered the pre-rebase script (6dbfa475e, still in the object store) and diffed the cited content rather than trusting the arithmetic, because "+4 everywhere" is exactly the kind of claim that is right five times and wrong once:

cited content now at
:82 set -euo pipefail :86
:342-344 the three success echos :346-348
:747-756 "Flat, where retire-only mode backs off linearly" :751-760
:751-753 "2s of total sleep beats 3s" :755-757
:755-759 "Their MESSAGING differences are deliberate too" :759-763 (×2 sites)
:757-758 "a chatty success or / exhaustion here would bury" :761-762

Verified +4 uniformly. Fixed all seven, not just my four — they are in one file, and repairing four while leaving three known-stale would be an arbitrary line to draw.

Suggestion (no enforcement) — taken, because the repoint alone fixes nothing durable

Repointing without pinning leaves the convention exactly as fragile as it was. Added LINE_CITATIONS + two tests: one asserting each citation still points at its content, one asserting no citation can be added unpinned (both directions — an unpinned citation and a table entry pinning nothing each fail).

Two defects in my own first attempt, worth recording since they are the same failure class this PR is about:

  1. I first matched each anchor across the cited range. Too weak twice over: :751-760 is ten lines wide, so a four-line shift still leaves the anchor inside the range — it silently tolerated exactly the BLO-31842 drift it exists to catch (caught 4 of 6). And a spanning [\s\S]* anchor matched an earlier occurrence of trap 'exit 143' TERM, reporting :739 for a citation whose real target is :759.
  2. My first "helpful" repair hint scanned sliding windows and reported the line the window opened on, not the match — it printed :79 where ground truth was :90.

Both would have been a confidently wrong line number, which is the precise defect being fixed. Now anchored to an exact first-line match, located by match offset, and every reported number checked against grep -n ground truth.

Mutation battery (8), each: non-empty diff + syntax check confirmed before recording, file restored clean after

# mutation expected result
A insert 4 lines at sh:41 (the real BLO-31842 shape) fail ✅ pin test, 1 — reports all 6 with correct new lines
B add an unpinned `:999` citation fail ✅ completeness, 1
C leave a table entry pinning nothing fail ✅ completeness, 1
D delete the three retire-only echoes fail ✅ retire-only success stdout (+ pin)
E parity success echo on release path :719 fail ✅ release success stdout (+ pin)
F release sleep 1sleep "$attempt" fail ✅ release pacing, 1
G retire-only sleep "$attempt"sleep 1 fail ✅ retire-only pacing, 1
H control — rewrap the ring sentence stay green ⚠️ see below

81/81 unmutated (79 + 2). approve-paperclip-api-digest.sh unmodifiedgit diff --quiet origin/master clean.

One honest regression against your M4 control

M4 no longer leaves the suite fully green, and I am flagging it rather than burying it. The property M4 actually protects — that the retire-only stdout assertion is phrasing-independent — still holds: under the rewrap that assertion passes. What now fails is the pin test, because collapsing two lines into one genuinely does shift every citation below it by −1. The citations really are stale at that point, so the failure is true rather than noise.

The cost is real and I would rather state it than let you find it: any script edit that changes line count above :763 now turns this suite red. That is the trade — the alternative is the silent staleness that produced this review. Mitigated by the failure listing every drifted citation with its corrected line number in one run, so the repair is mechanical rather than a hunt. F and G confirm same-line-count edits do not trip it.

If you would rather have the citations replaced with content anchors outright (no line numbers in comments at all), say so and I will file it — that removes the coupling entirely, but it is a larger rewrite than this PR should carry.

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: a96e258

The finding I raised at the previous head is fixed, and the fix generalised into a mechanism. Rather than renumbering the four stale citations and moving on, this head renumbers all six distinct ones and adds two tests that make the whole class fail loudly next time. That is the right response to "this drifted silently", and I verified it as a mechanism rather than taking the description on trust.

I reconstructed the tree at this head and ran the suite: 81/81 pass (79 at the previous head, +2 for the new tests). scripts/approve-paperclip-api-digest.sh at this head is byte-identical to master, so the shell script is untouched and the behind_by: 4 divergence carries no script drift into the merge.

Every citation resolves. I checked each by content, not arithmetic:

cite first line at that number
:86 set -euo pipefail
:346-348 the three retire-only success echos
:751-760 # Flat, where retire-only mode backs off linearly (10 lines)
:755-757 # `trap 'exit 143' TERM`2s of total sleep beats 3s
:759-763 # Their MESSAGING differences are deliberate too
:761-762 # guidance that retire-only mode prints itself, so a chatty success or

Then the mutation battery — the six messaging/pacing mutations from prior heads, plus seven new ones aimed at the pinning machinery itself:

# mutation expected result
M1 chatty success on the release path fail ✅ fails the success test
M5 chatty exhaustion on the release path fail ✅ fails, exactly 1 test
M2 silence all three retire-only success lines fail
M6 delete only the third line fail ✅ fails, exactly 1 test
M7 delete only the second line fail ✅ fails, exactly 1 test
MC1 insert 4 lines into the script header (replay BLO-31842) fail reports all six, with correct new numbers, in one run
MC2 add an unpinned `:999` to a comment fail
MC3 delete a citation, keep its table entry fail ✅ names the orphan
MC4 break the tail of a contains-guarded range fail
MC5 delete cited content outright fail ✅ takes the dangling branch
MC6b gut the interior of :751-760 in place ⚠️ stays green (Suggestions)
MC7b gut the last line of :759-763 in place ⚠️ stays green (Suggestions)

MC1 is the one that matters, because it is the exact commit shape that caused the original defect. It fails with all six drifted citations listed and each new line number correct (:86:90, :346-348:350, :751-760:755, :755-757:759, :759-763:763, :761-762:765). The "report every drift in one run" design claim at :2104-2107 is not decoration — I checked, and one-at-a-time failure really would have meant six fix-and-rerun cycles here.

The completeness test's two sets match exactly at this head: seven `:NNN` occurrences of six distinct citations, six table rows, no orphans either way. Scope dependencies are real — script (:24), fileURLToPath (:16), readFileSync (:13) are all imported/defined above, and readFileSync(fileURLToPath(import.meta.url)) is cwd-independent.

Prior Findings Dispositioned (1)

  • prior:b7564e4 important 1 — fixed — scripts/approve-paperclip-api-digest.test.js:1659 — all four stale citations I flagged are renumbered +4, and the three pre-existing ones I noted as out of scope were fixed in the same pass rather than left: :82:86 (:1659), :747-756:751-760 (:1764), :751-753:755-757 (:1966), :342-344:346-348 (:1995), :755-759:759-763 (:1726, :1805), :757-758:761-762 (:1780). I verified each target by matching the script's content at that line, not by adding 4. The complete fix was taken, not the minimal one.

Critical Issues (0)

None.

Important Issues (0)

None.

Suggestions (2)

  • [native-codex] scripts/approve-paperclip-api-digest.test.js:2058the two widest ranges are the two without a contains guard, so their bodies are unpinned. :751-760 claims "the ten lines defending the release loop's flat backoff" and :759-763 claims the messaging-asymmetry defence, but only the first line of each is checked. I verified this is reachable rather than theoretical: replacing lines 752-754 with unrelated filler (MC6b) and replacing line 763 (MC7b) each leave the suite 81/81 green, with the citation still claiming content that is no longer there.

    • The design comment at :2048-2056 is straight about this — first-line anchoring is a deliberate choice, and contains is named as the thing that "holds the rest of the range to the content the citing comment claims is there". So this is coverage the mechanism already has a slot for, applied to 3 of 6 entries. The gap is that the three it was applied to are :346-348, :755-757 and :761-762 — 3, 3 and 2 lines — while the 10-line and 5-line ranges went without.
    • One-line fix in the existing idiom, e.g. contains: /Retire-only mode has an operator at a\s+# terminal/ on :751-760 and /return. vs .exit. is structurally required/ on :759-763. Not asking for it here — the primary failure mode (wholesale line drift) is caught completely, which is what this PR set out to do.
  • [pr-review-toolkit/comments] scripts/approve-paperclip-api-digest.test.js:2004 — the comment says the ring-sentence regex is "Matched across the line wrap so a rewrap stays green and a deletion does not". That is still true of that assertion, but it is no longer true of the suite: a rewrap deletes a line, and the four citations below :348 shift by −1, so the new citation test correctly goes red. I confirmed both halves — after rewrapping 347-348, retire-only mode's succeeding write… still passes and only every line citation… fails.

    • This is the new test working as intended, not a defect, and the failure message tells the next reader exactly what to renumber. But in a PR whose thesis is that its comments are checkable, a comment promising "stays green" that now yields a red suite is worth a clause — e.g. "…stays green here, though the citations below will need renumbering".
    • Related and much smaller: the header at :2033 says "all seven citations in this file went stale". Seven is the count of occurrences; the table below it has six rows because :759-763 is cited twice. Both numbers are right, and a reader comparing them may briefly think one is wrong.

Strengths

  • The fix went past what I asked for, in the direction that matters. I flagged four citations and explicitly said the minimal fix was those four and the complete one was all seven occurrences. This head does the complete fix and removes the reason the class recurs. Fixing the instance and leaving the mechanism is the common outcome; this is the other one.
  • MC1 is the decisive evidence, and it reproduces the original defect exactly. Inserting four lines into the script header is literally what BLO-31842 did. The test catches it, names all six, and gives the correct replacement number for each — so the repair is mechanical rather than a re-derivation.
  • The design comment argues against a weaker version of itself, and the argument is correct. :2048-2056 explains why a range search was rejected: a ten-line range still "contains" its anchor after a four-line shift, so it tolerates exactly the drift being fixed. I confirmed that reasoning holds — first-line anchoring is what makes MC1 fail, and a [\s\S]* span would have reported a confidently wrong number, which is the defect being fixed wearing a different hat.
  • The completeness test is the half that makes it durable, and it is the half most people skip. Without it the next citation is unpinned by default and the convention rots from the following comment onward. MC2 and MC3 confirm it catches both directions.
  • The dangling branch is distinguished from the drifted branch, with different guidance for each ("drop it or repoint it" vs "that content now starts at :N"). MC5 reaches it. That is a real difference in what the reader has to do.
  • Listing every matching line when an anchor is ambiguous, rather than the first, is the right call for the stated reason — naming one of several would be the same confidently-wrong answer the test exists to prevent.
  • The earlier assertions all still discriminate. M1/M5/M2/M6/M7 each fail, and M6/M7 each fail exactly one test, so the messaging-asymmetry work from earlier heads was not diluted by any of this.

Recommended Action

  1. Nothing blocking. 81/81 at this head, all six citations verified by content, and every mutation aimed at the new machinery behaves as its comments claim.
  2. Consider the two contains guards and the "stays green" clause opportunistically — both are one-liners and neither is worth another round on its own.
  3. The PR is behind_by: 4; the shell script is byte-identical to master, so the citations will not drift on merge. master's only branch rule is merge_queue and reviewDecision is empty, so mergeStateStatus: BEHIND is the queue requirement, not an unmet review gate. Four General tests (server *) check-runs were still in_progress at review time — this review attests the tree, not the CI verdict.

Note on gate mechanics: this review should clear review/ally-comment, which is red at this head on the carried b7564e46 finding. The ledger entry above retires it by name, and isFullyDispositioned needs every finding that head raised — it raised exactly one, Important 1.

Posted as a formal COMMENTED review: GitHub bars a pull request's author from approving it, and this PR is authored by the Ally App.

@allyblockcast
allyblockcast Bot added this pull request to the merge queue Sep 6, 2026
Merged via the queue into master with commit 0d699b3 Sep 6, 2026
22 checks passed
kkroo pushed a commit that referenced this pull request Sep 6, 2026
… their first lines

The citation pinning added in #1674 anchors each `:NNN` range to its FIRST
LINE, and three of the six entries additionally carry a `contains` guard
holding the rest of the range to the content the citing comment claims is
there. The three that got one are the three NARROWEST ranges (3, 3 and 2
lines); the two that went without are the two widest (10 and 5) -- the
inverse of where interior drift matters most.

Measured, not supposed. Against the previous head:

  MC6b  replace the interior of `:751-760` (script lines 752-754,
        leaving the 751 anchor intact)              -> 81/81 GREEN
  MC7b  replace the last line of `:759-763`
        (script line 763, anchor intact)            -> 81/81 GREEN

Both now fail the citation test by name. Guards are in the existing idiom
and span the range rather than re-checking the anchor:

  :751-760  only remaining asymmetry -> unfinished parity fix -> no such
            deadline   (script 752 -> 754 -> 757)
  :759-763  not a parity gap -> `return` vs `exit` is structurally required
            (script 759 -> 763)

Also makes the invariant mechanical instead of adding a fourth unchecked
promise to a file whose thesis is that its comments are checkable: the
completeness test now asserts every multi-line entry HAS a `contains`, so
the next wide citation cannot be added unguarded and reopen this quietly.

Two comment corrections from the same review:

- the wrap comment claimed a rewrap "stays green". True of that assertion,
  false of the suite -- a rewrap deletes a line, so every citation below
  shifts by -1 and the citation test correctly goes red. It now says so.
- the header said "all seven citations" above a six-row table. Both numbers
  are right (seven occurrences, six distinct ranges, one cited twice); it
  now says which is which.

Test-only. `scripts/approve-paperclip-api-digest.sh` is byte-identical.

Refs BLO-32210
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.

0 participants