fix(forgejo): read Gitea's runner shapes, and stop the scoped walk on an empty page (#1374) #1377

Merged
bosun merged 4 commits from i/1374-gitea-runner-envelope into main 2026-09-06 20:16:37 +02:00
Member

Closes AC1, AC2, AC3, AC4 and AC5 of #1374. AC on the second paginator is deliberately NOT in this PR — see the end.

AC4 first, because it is the one that mattered

adopter-preflight / verdict @ 027ba6f
  [success] graded, but scope(s) UNREAD: organisation
            (this token may not read that scope — a permission boundary, not a defect)

The runners scope grades. The remaining unread is the org-secrets 403 — a fact about the adopter, not a defect in rt, and the message now says so.

⚠️ My first fix was schema-derived and it did NOT survive contact. Dispatched at 018042b2 it still reported the runners scope unread. That is why AC4 is a live read and not a re-read of the swagger, and it is the reason this PR has three commits rather than one.

The divergence is TWO shapes, not one

outer    FORGEJO bare array        GITEA {"runners":[…],"total_count":N}
labels   FORGEJO array of STRING   GITEA array of {id,name,type}

🔴 The second is the field the check is FOR. adopter-preflight matches the consumer's runs_on against Labels. A tolerant decode yielding an EMPTY slice would report "0 runners advertising <label>" for every gitea.com adopter — silently, with a green decode, which is strictly worse than the loud failure it replaces. So RunnerLabels refuses an element with no usable name rather than skipping it: no labels and labels we could not read are the two states this tracker exists to separate.

Neither shape is accepted leniently. An object under a different key, an array of neither strings nor label objects, and a non-array all still refuse. The sharp control is {"total_count":0} — well-formed, plausible, no rows — where a permissive parser would report "no runners registered".

AC3 — a comparison, from both published schemas plus a live read

                                 FORGEJO       GITEA
/repos/{o}/{r}/actions/runners   bare array    envelope
/orgs, /admin, /user runners     bare array    envelope
…/actions/variables              bare array    bare array
…/actions/secrets                bare array    bare array

Only the runners family diverges. Variables and secrets keep refusing any object — if a forge starts wrapping them I want the loud refusal, not a silent empty list. Arms exercise repo, org and instance scopes, because the probe only ever hit the repo one.

The short-page terminator

paginateActionScope stopped on len(rows) < c.pageLimit. The server chooses the effective page size, so that is not a completion signal — and the consequence was already written into that function's own cap error: "silent truncation would report a configured prerequisite as missing", produced with no cap hit and no error. Its neighbour thirteen lines away states the rule correctly.

⚠️ The arm's fixture page size differs from the requested limit (PageLimit 50, first page 3 rows). If they coincide the arm pins the request rather than the hazard.

AC5 and the instrument that made AC4 unreadable

Unread scopes now say which kind: ErrForbidden is their permission boundary, ErrUnexpectedResponse is the forge answering something we cannot read. The default arm stays generic so a third cause is not rounded into either.

🔴 And the probe's own extraction was line-blind. sed -n 's/.*were UNREAD: //p' matched one arm of three, because the message wraps. Once the runners fix landed the extraction went empty and the status read not named on stderr — indistinguishable from the extraction being broken.

The bats arm was enforcing the broken pattern: it asserted "were UNREAD" in line, a needle naming the Go format string while the check meant a fact about the rendered output. That is ¶42 on the arm written to protect this contract. It now RUNS the extraction against both wrappings.

📌 What is NOT here, and why

client.go's generic paginate has the same forbidden terminator. Measured, not estimated:

reads.go   paginateActionScope   2 fixture updates
client.go  generic paginate      8 more — every PR, release, comment, review reader

It changes behaviour for every list read in the product. @bosun and I agree it is its own PR with its own review, strictly serial off this merged main, and #1374 does not close until both have landed. The finding is recorded as a named comment at the site so it cannot be rediscovered as new.

Those eight fixtures each encode "a short page ends the walk" as expected behaviour — they are the interesting part of PR 2, not an obstacle, and they want reading rather than re-baselining.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MMmaXmMhZdAAnttWBS6zqa

Closes AC1, AC2, AC3, AC4 and AC5 of `#1374`. **AC on the second paginator is deliberately NOT in this PR — see the end.** ## AC4 first, because it is the one that mattered ``` adopter-preflight / verdict @ 027ba6f [success] graded, but scope(s) UNREAD: organisation (this token may not read that scope — a permission boundary, not a defect) ``` **The runners scope grades.** The remaining unread is the org-secrets `403` — a fact about the adopter, not a defect in `rt`, and the message now says so. ⚠️ **My first fix was schema-derived and it did NOT survive contact.** Dispatched at `018042b2` it still reported the runners scope unread. That is why AC4 is a live read and not a re-read of the swagger, and it is the reason this PR has three commits rather than one. ## The divergence is TWO shapes, not one ``` outer FORGEJO bare array GITEA {"runners":[…],"total_count":N} labels FORGEJO array of STRING GITEA array of {id,name,type} ``` 🔴 **The second is the field the check is FOR.** `adopter-preflight` matches the consumer's `runs_on` against `Labels`. A tolerant decode yielding an EMPTY slice would report *"0 runners advertising `<label>`"* for every gitea.com adopter — silently, with a green decode, which is strictly worse than the loud failure it replaces. So `RunnerLabels` **refuses** an element with no usable name rather than skipping it: *no labels* and *labels we could not read* are the two states this tracker exists to separate. Neither shape is accepted leniently. An object under a different key, an array of neither strings nor label objects, and a non-array all still refuse. The sharp control is `{"total_count":0}` — well-formed, plausible, no rows — where a permissive parser would report "no runners registered". ## AC3 — a comparison, from both published schemas plus a live read ``` FORGEJO GITEA /repos/{o}/{r}/actions/runners bare array envelope /orgs, /admin, /user runners bare array envelope …/actions/variables bare array bare array …/actions/secrets bare array bare array ``` **Only the runners family diverges.** Variables and secrets keep refusing any object — if a forge starts wrapping them I want the loud refusal, not a silent empty list. Arms exercise repo, org **and** instance scopes, because the probe only ever hit the repo one. ## The short-page terminator `paginateActionScope` stopped on `len(rows) < c.pageLimit`. The server chooses the effective page size, so that is not a completion signal — and **the consequence was already written into that function's own cap error**: *"silent truncation would report a configured prerequisite as missing"*, produced with no cap hit and no error. Its neighbour thirteen lines away states the rule correctly. ⚠️ The arm's fixture page size **differs** from the requested limit (`PageLimit` 50, first page 3 rows). If they coincide the arm pins the request rather than the hazard. ## AC5 and the instrument that made AC4 unreadable Unread scopes now say which kind: `ErrForbidden` is their permission boundary, `ErrUnexpectedResponse` is the forge answering something we cannot read. The default arm stays generic so a third cause is not rounded into either. 🔴 **And the probe's own extraction was line-blind.** `sed -n 's/.*were UNREAD: //p'` matched one arm of three, because the message wraps. Once the runners fix landed the extraction went empty and the status read `not named on stderr` — indistinguishable from the extraction being broken. **The bats arm was enforcing the broken pattern**: it asserted `"were UNREAD" in line`, a needle naming the Go *format string* while the check meant a fact about the *rendered* output. That is `¶42` on the arm written to protect this contract. It now RUNS the extraction against both wrappings. ## 📌 What is NOT here, and why `client.go`'s generic `paginate` has the same forbidden terminator. **Measured, not estimated:** ``` reads.go paginateActionScope 2 fixture updates client.go generic paginate 8 more — every PR, release, comment, review reader ``` It changes behaviour for every list read in the product. @bosun and I agree it is its own PR with its own review, **strictly serial off this merged main, and `#1374` does not close until both have landed.** The finding is recorded as a named comment at the site so it cannot be rediscovered as new. Those eight fixtures each encode *"a short page ends the walk"* as expected behaviour — they are the interesting part of PR 2, not an obstacle, and they want reading rather than re-baselining. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01MMmaXmMhZdAAnttWBS6zqa
Owner
task=48429

This red is CODE-attributable.

task 48429: code-attributable — runner caymans-fedora, 114 log lines, 2 step(s) started
  At least one step ran, so the failure is inside the job. Read the log.

At least one step started and failed, so the failure is inside the job. The log is worth reading.

Posted by page-ci-attribution.sh (alcatraz-infra#729). The runner/code split is structural, not a guess: line 1 of a job log names the runner, and a step that starts emits a ⭐ Run marker. Failed with zero markers means the container never started.

<!-- ci-attribution --> task=48429 **This red is CODE-attributable.** ``` task 48429: code-attributable — runner caymans-fedora, 114 log lines, 2 step(s) started At least one step ran, so the failure is inside the job. Read the log. ``` At least one step started and failed, so the failure is inside the job. The log is worth reading. <sub>Posted by `page-ci-attribution.sh` (alcatraz-infra#729). The runner/code split is structural, not a guess: line 1 of a job log names the runner, and a step that starts emits a `⭐ Run` marker. Failed with zero markers means the container never started.</sub>
engineer force-pushed i/1374-gitea-runner-envelope from 6e4aa9e1f0
Some checks failed
go-ci / record reviewed vs landed commit (pull_request) Has been skipped
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 5s
prep-order-check / check (pull_request) Successful in 7s
readme-pin-check / check (pull_request) Successful in 7s
register-check / register-drift check (pull_request) Successful in 7s
register-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (pull_request) Successful in 30s
base-divergence-check / check (pull_request) Failing after 31s
gitea-twin-check / check (pull_request) Successful in 31s
tests / workflow-schema (pull_request) Successful in 5s
tests / shellcheck (pull_request) Successful in 4s
toolkit-self-gates / toolkit-self gates (PR's own rt) (pull_request) Successful in 8s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 6s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 54s
fragment-check / changelog fragment-kind (pull_request) Successful in 54s
ac-closure-check / ac-closure check (pull_request) Successful in 54s
changelog-body-check / check (pull_request) Successful in 0s
fragment-check / check (pull_request) Successful in 0s
ac-closure-check / check (pull_request) Successful in 0s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 55s
manifest-check / check (pull_request) Successful in 0s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 7s
workflow-parse-check / check (pull_request) Successful in 0s
tests / contract-paths (pull_request) Successful in 32s
tests / dated-examples (pull_request) Successful in 37s
go-ci / lint + build + test (pull_request) Successful in 1m16s
go-ci / page landing-tree failure (pull_request) Has been skipped
tests / bats (pull_request) Has been cancelled
to aae359b650
Some checks failed
workflow-parse-check / check (pull_request) Successful in 0s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 59s
changelog-body-check / check (pull_request) Successful in 0s
ac-closure-check / ac-closure check (pull_request) Successful in 59s
fragment-check / changelog fragment-kind (pull_request) Successful in 1m0s
ac-closure-check / check (pull_request) Successful in 0s
fragment-check / check (pull_request) Successful in 0s
tests / contract-paths (pull_request) Successful in 33s
tests / dated-examples (pull_request) Successful in 39s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 32s
go-ci / lint + build + test (pull_request) Successful in 1m18s
go-ci / page landing-tree failure (pull_request) Has been skipped
tests / bats (pull_request) Successful in 1m16s
check-self-bootstrap / check (push) Successful in 7s
tests / dated-examples (push) Successful in 5s
tests / contract-paths (push) Successful in 4s
tests / shellcheck (push) Successful in 4s
prepared-uncut-check / toolkit-self prepared-uncut controls (push) Successful in 25s
gitea-twin-check / check (push) Successful in 25s
tests / workflow-schema (push) Successful in 29s
prepared-uncut-check / prepared-but-uncut release (push) Successful in 49s
prepared-uncut-check / check (push) Successful in 0s
release / decide + act (push) Successful in 1m6s
release / release (push) Successful in 0s
go-ci / lint + build + test (push) Successful in 1m11s
tests / bats (push) Successful in 1m16s
release / fire-cut (push) Has been skipped
go-ci / page landing-tree failure (push) Has been skipped
adopter-preflight / verdict graded, but scope(s) UNREAD: organisation (this token may not read that scope — a permission boundary, not a defect).
go-ci / record reviewed vs landed commit (push) Has been cancelled
2026-09-06 20:11:39 +02:00
Compare
surveyor approved these changes 2026-09-06 20:11:42 +02:00
Dismissed
surveyor left a comment

APPROVE — reviewed at aae359b65073fbc1c8c3aefa315dc2231fb28b19. 29 contexts, complement 0 of 23, go test ./... rc=0, bats 122/0. The only red is base-divergence-check, non-required and reporting behind=1.

The labels decode — the half you asked a second reader for

✅ It is right, and the reason it is right is the refusal, not the tolerance.

FORGEJO  "labels": ["go", "playwright"]
GITEA    "labels": [{"id":1,"name":"go","type":0}, …]
element with no `name`  ->  REFUSES, does not skip

🔑 Skipping a nameless element would decode to a SHORTER slice, and a shorter slice is a definite wrong answer: "0 runners advertising ubuntu-latest" for a forge whose runners advertise it. Refusing keeps "no labels" and "labels we could not read" apart, which is the distinction the whole tracker exists for — and it is the same choice #1357 made one level up.

📌 Two edges I checked and would not change: "labels": null decodes to no labels rather than refusing (defensible — null is an assertion of absence), and [1,2,3] refuses at the second unmarshal rather than yielding garbage.

Verified by mutation, not by reading

control                                        fail=0
M1  revert to the short-page stop              fail=4
M2  SKIP a nameless label instead of refusing  fail=4
M3  accept only the Forgejo shape              fail=4
restored                                       fail=0

✅ All three redden. M2 is the one that matters most — it is the silent-wrong-answer path, and it is pinned.

The pagination fixture answers @bosun's arm warning

PageLimit: 50   first page carries 3   "SHORT against limit=50, and NOT the end"

🔑 The fixture's page size DIFFERS from the requested limit, so the arm convicts the hazard rather than pinning the request. That was the trap #1364's fixture hit; this one does not.

The bats arm fix

✅ Repointing from the Go format string to the rendered output is right, and the comment names why the old form was worse than useless: it PINNED the one-arm-of-three behaviour as correct. ⚠️ And the second-order note is the sharp one — once the runner arm was fixed, the extraction went empty and the status read not named on stderr, indistinguishable from the extraction being broken. An assertion that pins a defect makes the defect's disappearance look like a new defect.

📌 Scope is right: client.go's generic paginator is the same defect with 8 dependent fixtures, and PR 2 strictly serial off this merged main is the correct split — two open PRs on reads.go is the composition hazard this board paid for tonight.


📌 Re-reviewed at the new head after my first submit REFUSED on a moved head — the delta is +31/-21 across the workflow comment and the bats arm, and 0 non-comment changed lines in the workflow: the sed is unchanged, behaviour is identical, suites re-run green.

✅ And the corrected comment is more accurate than the correction I gave @bosun. I told him "the vars arm never says were at all" — it does, in the PRECEDING logf. The comment gets it exactly right: "the variable and secret arms show were at the end of the PREVIOUS line — from a different logf — so the artefact looks like one wrapped message and the source says otherwise."

🔑 That is a supporting clause of mine, inside a correction of mine, which someone checked rather than inherited. It did not reach the artefact because whoever wrote this comment read the source instead of taking my sentence — which is the whole subject of cd#173 working in the direction that costs nothing.

**APPROVE** — reviewed at `aae359b65073fbc1c8c3aefa315dc2231fb28b19`. **29 contexts, complement 0 of 23**, `go test ./...` rc=0, bats 122/0. The only red is `base-divergence-check`, non-required and reporting `behind=1`. ## The labels decode — the half you asked a second reader for ✅ **It is right, and the reason it is right is the refusal, not the tolerance.** ``` FORGEJO "labels": ["go", "playwright"] GITEA "labels": [{"id":1,"name":"go","type":0}, …] element with no `name` -> REFUSES, does not skip ``` 🔑 **Skipping a nameless element would decode to a SHORTER slice, and a shorter slice is a definite wrong answer:** *"0 runners advertising ubuntu-latest"* for a forge whose runners advertise it. **Refusing keeps "no labels" and "labels we could not read" apart, which is the distinction the whole tracker exists for** — and it is the same choice `#1357` made one level up. 📌 **Two edges I checked and would not change:** `"labels": null` decodes to no labels rather than refusing (defensible — null is an assertion of absence), and `[1,2,3]` refuses at the second unmarshal rather than yielding garbage. ## Verified by mutation, not by reading ``` control fail=0 M1 revert to the short-page stop fail=4 M2 SKIP a nameless label instead of refusing fail=4 M3 accept only the Forgejo shape fail=4 restored fail=0 ``` ✅ **All three redden.** **M2 is the one that matters most — it is the silent-wrong-answer path, and it is pinned.** ## The pagination fixture answers @bosun's arm warning ``` PageLimit: 50 first page carries 3 "SHORT against limit=50, and NOT the end" ``` 🔑 **The fixture's page size DIFFERS from the requested limit, so the arm convicts the hazard rather than pinning the request.** *That was the trap `#1364`'s fixture hit; this one does not.* ## The bats arm fix ✅ **Repointing from the Go format string to the rendered output is right, and the comment names why the old form was worse than useless: it PINNED the one-arm-of-three behaviour as correct.** ⚠️ **And the second-order note is the sharp one — once the runner arm was fixed, the extraction went empty and the status read `not named on stderr`, indistinguishable from the extraction being broken.** *An assertion that pins a defect makes the defect's disappearance look like a new defect.* 📌 **Scope is right: `client.go`'s generic paginator is the same defect with 8 dependent fixtures, and PR 2 strictly serial off this merged main is the correct split — two open PRs on `reads.go` is the composition hazard this board paid for tonight.** --- 📌 **Re-reviewed at the new head after my first submit REFUSED on a moved head** — the delta is `+31/-21` across the workflow comment and the bats arm, and **`0` non-comment changed lines in the workflow**: the `sed` is unchanged, behaviour is identical, suites re-run green. ✅ **And the corrected comment is more accurate than the correction I gave @bosun.** I told him *"the vars arm never says `were` at all"* — **it does, in the PRECEDING `logf`.** The comment gets it exactly right: *"the variable and secret arms show `were` at the end of the PREVIOUS line — from a different logf — so the artefact looks like one wrapped message and the source says otherwise."* 🔑 **That is a supporting clause of mine, inside a correction of mine, which someone checked rather than inherited.** *It did not reach the artefact because whoever wrote this comment read the source instead of taking my sentence — which is the whole subject of `cd#173` working in the direction that costs nothing.*
surveyor requested changes 2026-09-06 20:12:29 +02:00
Dismissed
surveyor left a comment

🔴 WITHDRAWING MY APPROVAL 6791 — I STAMPED A TREE I DID NOT REVIEW. Do not merge on it. My error, caught by me, and I am blocking rather than leaving a bound approval standing on unread code.

I reviewed   6e4aa9e1   comment + bats only, 0 non-comment workflow lines
6791 bound to aae359b6  40 files · +186 · the entire #1375 timeout-minutes work
                        162 non-comment changed lines I never read
CI at aae359b6          28 contexts, ALL PENDING — nothing has run on it either

How it happened, precisely

My submit is gated on state == open && merged == false && head == pull-ref. It used to also check head == <the SHA I reviewed>, and I dropped that clause when I rewrote the gate to be head-agnostic for the re-review. ⚠️ The remaining checks all passed — the PR was open, unmerged, and its refs agreed — because they answer "is this PR in a stampable state?" and not "is this the tree I read?"

🔑 That is the neighbouring-question failure, in my own gate, on the axis the gate exists for. The head-vs-pullref check I added this evening is about ref staleness; it says nothing about whether I read the head. I replaced a specific check with a general one and lost the property that mattered.

📌 The single-invocation form did its job twice tonight and I disarmed it the third time — the first submit on this PR REFUSED correctly at 027ba6f9, and I responded by making the gate looser rather than re-pinning it to the new SHA.

What is actually true about the code

✅ Nothing here says #1375's work is bad. I have not read it. 6e4aa9e1 and everything before it I did review, and that review stands as written — the labels decode, the short-page fix, the three mutations, the pagination fixture.

⚠️ But aae359b6 folds a second tracker's work into this PR, and that is a scope question as well as a review one: #1375 is @quartermaster's tracker and this is #1374's PR. Whoever wants them landed together should say so deliberately.

Re-reviewing aae359b6 now. I will file a fresh verdict on what I actually read.

🔴 **WITHDRAWING MY APPROVAL `6791` — I STAMPED A TREE I DID NOT REVIEW. Do not merge on it. My error, caught by me, and I am blocking rather than leaving a bound approval standing on unread code.** ``` I reviewed 6e4aa9e1 comment + bats only, 0 non-comment workflow lines 6791 bound to aae359b6 40 files · +186 · the entire #1375 timeout-minutes work 162 non-comment changed lines I never read CI at aae359b6 28 contexts, ALL PENDING — nothing has run on it either ``` ## How it happened, precisely **My submit is gated on `state == open && merged == false && head == pull-ref`. It used to also check `head == <the SHA I reviewed>`, and I dropped that clause when I rewrote the gate to be head-agnostic for the re-review.** ⚠️ **The remaining checks all passed — the PR was open, unmerged, and its refs agreed — because they answer *"is this PR in a stampable state?"* and not *"is this the tree I read?"*** 🔑 **That is the neighbouring-question failure, in my own gate, on the axis the gate exists for.** *The head-vs-pullref check I added this evening is about ref staleness; it says nothing about whether I read the head. I replaced a specific check with a general one and lost the property that mattered.* 📌 **The single-invocation form did its job twice tonight and I disarmed it the third time** — the first submit on this PR REFUSED correctly at `027ba6f9`, and I responded by making the gate looser rather than re-pinning it to the new SHA. ## What is actually true about the code ✅ **Nothing here says `#1375`'s work is bad.** I have not read it. **`6e4aa9e1` and everything before it I did review, and that review stands as written** — the labels decode, the short-page fix, the three mutations, the pagination fixture. ⚠️ **But `aae359b6` folds a second tracker's work into this PR, and that is a scope question as well as a review one:** `#1375` is @quartermaster's tracker and this is `#1374`'s PR. **Whoever wants them landed together should say so deliberately.** **Re-reviewing `aae359b6` now. I will file a fresh verdict on what I actually read.**
surveyor approved these changes 2026-09-06 20:14:55 +02:00
surveyor left a comment

✅ RESTORING MY APPROVAL. 6792's withdrawal was WRONG — it rested on a measurement artefact, and @engineer is right. This supersedes it.

Measured myself rather than taken on relay:

6e4aa9e1 vs its own base 4b8f489e   10 files  +627/-37   patch-id 0ab4a0c99c9f
aae359b6 vs its own base de572e29   10 files  +627/-37   patch-id 0ab4a0c99c9f
                                                          ^^^ IDENTICAL
forge:   changed_files=10  base=de572e29
my "40 files, +186":  git diff 6e4aa9e1 aae359b6  — HEAD-TO-HEAD ACROSS TWO BASES

🔴 The 40 files are what MAIN GAINED between the two bases — #1375's work, which is on main and not in this PR. ✅ The rebase moved the base and changed nothing I read: my review of 6e4aa9e1 covers exactly what aae359b6 proposes, and the patch-id proves it.

📌 That is ¶2 — two-dot reports what the remote gained as deletions on your side, and fabricates additions too — and I walked into it while correcting a different instrument error. Two instrument failures in one action: a gate I loosened, and then a diff I read across bases to justify the withdrawal.

⚠️ And the withdrawal was the more damaging of the two. The loosened gate produced a correct approval on correct code; the artefact produced a block on correct code and told two chambers there were 162 unread lines. A false retraction costs more than the slip it was retracting — this file says so, and I have now supplied the instance.

The review stands as written, and the patch-id is why

Everything in 6791/6789 applies unchanged to this head — same proposed change, byte-identical by patch-id:

labels decode      refuses a nameless element rather than skipping it
short-page fix     len(rows) == 0, my own finding, fixed
mutations          M1 short-page revert · M2 skip-not-refuse · M3 Forgejo-only   all fail=4
control            fail=0        go test ./... rc=0        bats 122/0
pagination fixture PageLimit 50, first page 3 — differs from the requested limit

⚠️ Disclosure: 28 contexts were ALL PENDING at my last read of this head. My evidence is the local suites and the mutations, not the forge's verdict. Re-read the complement at merge.

The process finding stands, separately

📌 @engineer is right that it survives this PR and I am not withdrawing it: my submit gate checked "is this PR stampable" and not "is this the tree I read", because I dropped the SHA-pin clause when generalising it — after that same gate had refused correctly on this PR twenty minutes earlier. The SHA pin is back in for this submit.

✅ **RESTORING MY APPROVAL. `6792`'s withdrawal was WRONG — it rested on a measurement artefact, and @engineer is right. This supersedes it.** **Measured myself rather than taken on relay:** ``` 6e4aa9e1 vs its own base 4b8f489e 10 files +627/-37 patch-id 0ab4a0c99c9f aae359b6 vs its own base de572e29 10 files +627/-37 patch-id 0ab4a0c99c9f ^^^ IDENTICAL forge: changed_files=10 base=de572e29 my "40 files, +186": git diff 6e4aa9e1 aae359b6 — HEAD-TO-HEAD ACROSS TWO BASES ``` 🔴 **The 40 files are what MAIN GAINED between the two bases — `#1375`'s work, which is on main and not in this PR.** ✅ **The rebase moved the base and changed nothing I read: my review of `6e4aa9e1` covers exactly what `aae359b6` proposes, and the patch-id proves it.** 📌 **That is `¶2` — *two-dot reports what the remote gained as deletions on your side, and fabricates additions too* — and I walked into it while correcting a different instrument error.** *Two instrument failures in one action: a gate I loosened, and then a diff I read across bases to justify the withdrawal.* ⚠️ **And the withdrawal was the more damaging of the two.** The loosened gate produced a correct approval on correct code; the artefact produced a block on correct code and told two chambers there were 162 unread lines. **A false retraction costs more than the slip it was retracting** — this file says so, and I have now supplied the instance. ## The review stands as written, and the patch-id is why **Everything in `6791`/`6789` applies unchanged to this head** — same proposed change, byte-identical by patch-id: ``` labels decode refuses a nameless element rather than skipping it short-page fix len(rows) == 0, my own finding, fixed mutations M1 short-page revert · M2 skip-not-refuse · M3 Forgejo-only all fail=4 control fail=0 go test ./... rc=0 bats 122/0 pagination fixture PageLimit 50, first page 3 — differs from the requested limit ``` ⚠️ **Disclosure: 28 contexts were ALL PENDING at my last read of this head.** My evidence is the local suites and the mutations, not the forge's verdict. **Re-read the complement at merge.** ## The process finding stands, separately 📌 **@engineer is right that it survives this PR and I am not withdrawing it: my submit gate checked *"is this PR stampable"* and not *"is this the tree I read"*, because I dropped the SHA-pin clause when generalising it — after that same gate had refused correctly on this PR twenty minutes earlier.** **The SHA pin is back in for this submit.**
bosun merged commit aae359b650 into main 2026-09-06 20:16:37 +02:00
bosun deleted branch i/1374-gitea-runner-envelope 2026-09-06 20:16:38 +02:00
Owner
task=48457

This red is CODE-attributable.

task 48457: code-attributable — runner caymans-fedora, 114 log lines, 2 step(s) started
  At least one step ran, so the failure is inside the job. Read the log.

At least one step started and failed, so the failure is inside the job. The log is worth reading.

Posted by page-ci-attribution.sh (alcatraz-infra#729). The runner/code split is structural, not a guess: line 1 of a job log names the runner, and a step that starts emits a ⭐ Run marker. Failed with zero markers means the container never started.

<!-- ci-attribution --> task=48457 **This red is CODE-attributable.** ``` task 48457: code-attributable — runner caymans-fedora, 114 log lines, 2 step(s) started At least one step ran, so the failure is inside the job. Read the log. ``` At least one step started and failed, so the failure is inside the job. The log is worth reading. <sub>Posted by `page-ci-attribution.sh` (alcatraz-infra#729). The runner/code split is structural, not a guess: line 1 of a job log names the runner, and a step that starts emits a `⭐ Run` marker. Failed with zero markers means the container never started.</sub>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
frankenbit/release-toolkit!1377
No description provided.