.forgejo/workflows/release.yml — state RELEASE_TOKEN's real scope set (read:user, write:package, write:repository) #60

Closed
opened 2026-09-02 22:50:35 +00:00 by claude-bot-andresmgsl · 6 comments

Context

.forgejo/workflows/release.yml L8-10 states the credential contract for secrets.RELEASE_TOKEN:

A repository/org secret RELEASE_TOKEN: a token with package:write and repository:write scopes for an account allowed to publish packages under the heavy-duty org.

Superseded on main at 2026-09-03T07:30:22Z — the quote above is the pre-fix comment, kept verbatim (triage). !61 merged as 2230ca25, and L8-12 now read read:user, write:package and write:repository with the rationale attached. The framing verb in the sentence above ("states") was true at the mint and is kept, not rewritten: it is the defect this issue reports, and rewriting it would erase what the issue is about. Read it as "stated, until 2230ca25".

That line was written when the Create release and attach .deb step was three hand-rolled curl calls, which never logged in. #54 merged as !55 (d6a21c9d, 2026-09-02T11:22:58Z) and replaced them with scripts/publish-release.sh, whose first action is run_stoke auth login --url "$FORGE_URL" --token-file "$TOKEN_FILE" (publish-release.sh L41). The conversion added a login — and therefore a third scope — without updating the line that states the contract.

The reproduction is a live run, not a hypothetical. v1.5.0 was tagged at 088e7e2d on 2026-09-02T22:29:15Z and run 990 executed the door in order:

Check out tag               ✅
Run tests                   ✅   141/141
Build .deb                  ✅   dist/stoke_1.5.0_all.deb
Publish to Debian registry  ✅   201 -> "Published."
Create release and attach   ❌   exit 1

The failure, verbatim from the run log:

Authentication failed: token does not have at least one of required scope(s): [read:user]
HTTP status: 403
URL: https://forgejo.heavyduty.builders/api/swagger

The token @andres provisioned matches the documented requirement exactly — it authenticated against the package registry and published stoke 1.5.0. The documentation is what is wrong, and the next person to provision this secret from that comment would create the same insufficient token.

Spec

Decision: correct the stated contract to the three scopes the door actually requires. Do not change auth login's behaviour.

The required set is read:user, write:package, write:repository.

Where each is spent, measured at 088e7e2d:

Scope Spent by Evidence
write:package Publish to Debian registrypublish-deb.sh PUTs to /api/packages/heavy-duty/debian/.../upload run 990 got 201, Published.
read:user Create release and attachauth login calls tokenClient.get('/user') at src/cli.js L225 to resolve login/username/email for the stored config run 990 got 403 … required scope(s): [read:user]
write:repository release view / release create / release upload not yet reached on any run — auth login aborts first

Rejected alternative, and why. The read:user call buys the release flow nothing operationally: publish-release.sh passes --owner and --repo explicitly to all three release commands, and none of them reads config.login (the config.login fallbacks in src/cli.js are auth logout L284/L290, repo list L401, repo create L570 and migrate L657 — no release command among them). So the door could be made to need only two scopes — either by having publish-release.sh write its own $TMP/config.json instead of calling auth login, or by having auth login tolerate a 403 on /user. Both are rejected here. auth login's /user call is a deliberate token validation: it is what turns a typo'd or revoked token into a clear message at the top of the step instead of a confusing one further down. Skipping it is worse than it looks, because publish-release.sh's get-or-create swallows release view's output (>/dev/null 2>&1), so an auth failure there would silently fall through to release create and surface as a create error. Narrowing token validation is also adjacent to #9 (security: narrow default scopes created by stoke auth login), closed, and is not something to reverse as a side effect of a documentation fix. The contract comment is the thing that drifted; fix the comment.

Scope of the diff: the comment block, and nothing else. No workflow step changes, no script changes, no CLI changes. If the two-scope door is later judged worth building, that is a separate issue with its own spec.

Tasks

  • Rewrite .forgejo/workflows/release.yml L8-10 so it names all three scopes — read:user, write:package, write:repository — in the Forgejo scope spelling the token creation screen uses, not the package:write/repository:write order-reversed form currently there. (Ticked by triage 2026-09-03 at 2230ca25. The trailing clause "currently there" inverted the instant this merged — the reversed spellings are gone from main — and is kept verbatim because it is the instruction the builder was actually given.)
  • Say why read:user is needed in the same comment (one clause: publish-release.sh runs stoke auth login, which validates the token against /user), so the next implementation change that removes the login also removes the scope.
  • Add a changelog.d/ fragment.
  • Open the PR with Refs #60, not Closes — the post-merge criterion below outlives the merge.

Acceptance criteria

  • .forgejo/workflows/release.yml's header comment names exactly read:user, write:package, write:repository and no longer says "package:write and repository:write".

  • The comment states which step spends read:user and why.

  • git diff on the PR touches only .forgejo/workflows/release.yml and one changelog.d/*.md fragment. No step, script or CLI change.

  • npm test passes and ci / test is green on the PR head.

  • Post-merge, triage owns the close. The PR references this issue with Refs #60, never Closes #60; the merge moves it to post-merge and releases the claim. Wake condition: the first v* tag run in which the Create release and attach .deb step is reached — that run proves the corrected scope set is sufficient and not merely longer. If that run still fails on a scope, the comment is still wrong and this issue re-opens its spec rather than closing. CORRECTED — the wake is the re-run of run 990's job, not another v* tag (triage, 2026-09-03). This criterion was written at 2026-09-02T22:50:35Z, ~2 minutes before the same diagnosis was written onto #54's AC6 (22:52:29Z), #56 (22:51:54Z) and #27 (22:53:03Z, again 23:49:04Z). This is the one body of the four that never received it — and this issue's own Dependencies section below already carries the right framing ("a re-run of run 990's job"), so the body has contradicted itself since the mint. Read "the first v* tag run" as run 990's job, re-run under a token that also carries read:user. v1.5.0 already exists at 088e7e2d, and publish-deb.sh L44 answers the repeat 409 with Already published (409): this exact version already exists in the registry. and does not exit 1 (that is the *) arm at L45-49), so the re-run walks past the registry step to the step that has never completed. Do not push a new tag to prove this fix, and do not delete v1.5.0 — the registry half of that tag already shipped (debian stoke 1.5.0, created 2026-09-02T22:29:42Z), and hand-recovering v1.4.0 is what cost #54 its proof the first time. Re-measured 2026-09-03T00:0xZ: run 990 is still the only run on v1.5.0 and still failure; GET /repos/heavy-duty/stoke/releases/tags/v1.5.0404; the repo has 3 tags. The old sentence is kept because it is what the board and the builder were told — !61's acceptance criterion 5 repeats it back as "the post-merge tagged-run verification", which is how a stale clause becomes a second reader's belief.

    MET — and it took all four runs to prove, not one (triage, 2026-09-03T21:20Z).

    Read literally, this criterion's own escape clause fires — and a later reader must not act
    on it.
    "the first v* tag run in which the Create release and attach .deb step is
    reached"
    is run 1104 (2026-09-03T19:46:22Z), and that run did
    still fail on a scope
    : Release creation failed: token does not have at least one of required scope(s): [write:repository] / HTTP status: 403. The sentence above says that
    re-opens this issue's spec. It does not. The clause was written about a scope the comment
    omits — run 990's read:user, the omission this issue exists to fix. 1104 failed on
    write:repository, a scope this comment names. The comment was not wrong; the token was
    short. Run 1106 (20:52:30Z) then completed the step with the full
    set: Authenticated as andres using provided token.Release created: v1.5.0 v1.5.0 /
    Release id: 66Asset uploaded: stoke_1.5.0_all.deb🏁 Job succeeded.

    Sufficient, and not merely longer — each of the three has its own failing run:

    scope named at release.yml L8-12 the run that proves it necessary
    write:package 735 — and every registry PUT since; the step that spends it
    read:user 990… required scope(s): [read:user] / 403, spent by stoke auth loginGET /user, which is exactly what acceptance criterion 2 asks this comment to state
    write:repository 1104… required scope(s): [write:repository] / 403
    all three together 1106 — green end to end, release 66 published with its .deb and its CHANGELOG.md notes

    Honest limit, stated rather than papered over: GET /actions/secrets403
    (re-measured this tick), so triage cannot read RELEASE_TOKEN and cannot prove the PAT
    carries only these three. "Not merely longer" is discharged on necessity — each named
    scope has its own 403 — not on inspection of the secret. If the PAT happens to carry a fourth
    scope, this comment is still the minimal correct provisioning instruction, which is all it
    claims to be.

    And the correction this criterion received was itself wrong. The amendment above replaced
    "the first v* tag run" with "the re-run of run 990's job". That act does not exist on
    this instance
    : POST …/actions/runs/990/rerun, …/actions/tasks/990/rerun,
    …/actions/runs/990/rerun-failed-jobs and …/actions/runs/990/jobs/0/rerun all 404
    (probed 2026-09-03T21:1xZ). The recovery was the act this criterion, #54
    and #56 all forbade — delete the tag and re-push it, twice — and it was
    harmless: refs/tags/v1.5.0 still resolves to the same annotated tag object
    6ba87b48acff896d47065a24b0cde7fe3c0d3373 at 088e7e2d, and publish-deb.sh's 409 arm
    (cited right here to argue a re-run was safe) protects a re-push identically. Ironically it is
    this criterion's original wording — "the first v* tag run" — that described what
    actually happened. The correction was applied to four bodies on reasoning nobody tested
    against the API.
    Full account on #54's AC6.

Test plan

The proof is documentary and then behavioural, in that order:

  1. Read the corrected comment and create a token from it. The scope set it names must be selectable as written on Forgejo's token screen — write:package and write:repository are the instance's spellings; package:write is not.
  2. The next tagged run reaches Create release and attach .deb and gets past auth login. Read as the re-run of run 990's job — see the correction on the post-merge criterion above (triage, 2026-09-03). No new tag is required, and none is wanted.

Cases that must fail:

  • A comment that adds read:user but keeps the package:write spelling is not a pass — the misspelling is half of why the original was unusable as a provisioning instruction.
  • A diff that also "fixes" publish-release.sh to skip auth login is not a pass. It is the rejected alternative, it changes behaviour under cover of a doc fix, and it makes the door's failure mode worse.
  • A green ci / test proves nothing here on its own: no test in this repository reads that comment. The criterion is the text and the next tagged run — the re-run of run 990's job; see above (triage, 2026-09-03).

Dependencies

No blockers, and this issue blocks nothing.

  • Not a blocker on #56 (release 1.5.0), and #56 must not wait for it. #56 is unblocked by an operator act — a new PAT carrying read:user pasted into the org secret, then a re-run of run 990's job. This issue only stops the next provisioner from repeating the mistake; it cannot fix a token that already exists.
  • Not a blocker on #54, whose surviving criterion is the same re-run.
  • No collision edge with #57 (claimed, open PR !59). Same family — a token contract stated in a comment that nobody re-derived when the implementation changed underneath it — but a different deliverable and a disjoint file set: #57 touches scripts/publish-deb.sh, test/publish-deb.test.js and changelog.d/57.md; this touches .forgejo/workflows/release.yml and its own fragment. (Stale 2026-09-03T07:30:12Z, kept verbatim: #57 is now closed and !59 merged as bbde478. The collision ruling itself held all the way through — the two file sets stayed disjoint across both merges, and neither PR touched the other's files — but the parenthetical's state adjectives (claimed, open PR) no longer describe the board. Triage, 2026-09-03.)
  • Not Part of #27. That epic's scope is the five CLI gaps, not the release door's documentation.

Notes

  • Do not direct-assign this issue — the builder queue picks ready + unassigned only.
  • The URL: https://forgejo.heavyduty.builders/api/swagger line in the failure above is Forgejo's generic error-document pointer, not the endpoint that was called. The endpoint was /api/v1/user. Do not chase /api/swagger; it is a red herring and it is the second diagnostic in this door to mislead a CI reader, after the one #57 is fixing. (#57 landed as !59 at 2026-09-03T07:30:12Z — read "is fixing" as "fixed"; the /api/swagger red herring below is unchanged and still unowned.) That is an observation, not scope here — folding it in would put a behaviour change in a comment-only diff.
## Context `.forgejo/workflows/release.yml` L8-10 states the credential contract for `secrets.RELEASE_TOKEN`: > A repository/org secret RELEASE_TOKEN: a token with package:write and repository:write scopes for an account allowed to publish packages under the heavy-duty org. **Superseded on `main` at 2026-09-03T07:30:22Z — the quote above is the *pre-fix* comment, kept verbatim (triage).** [!61](https://forgejo.heavyduty.builders/heavy-duty/stoke/pulls/61) merged as [`2230ca25`](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/2230ca250157d2980113827fba480623ea2824ed/.forgejo/workflows/release.yml), and L8-12 now read `read:user, write:package and write:repository` with the rationale attached. The framing verb in the sentence above (*"states"*) was true at the mint and is **kept**, not rewritten: it is the defect this issue reports, and rewriting it would erase what the issue is about. Read it as *"stated, until `2230ca25`"*. That line was written when the *Create release and attach .deb* step was three hand-rolled `curl` calls, which never logged in. [#54](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/54) merged as [!55](https://forgejo.heavyduty.builders/heavy-duty/stoke/pulls/55) (`d6a21c9d`, 2026-09-02T11:22:58Z) and replaced them with `scripts/publish-release.sh`, whose **first** action is `run_stoke auth login --url "$FORGE_URL" --token-file "$TOKEN_FILE"` ([`publish-release.sh` L41](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/088e7e2d66089007a93a00213f2173d0d9142580/scripts/publish-release.sh#L41)). The conversion added a login — and therefore a **third scope** — without updating the line that states the contract. **The reproduction is a live run, not a hypothetical.** `v1.5.0` was tagged at `088e7e2d` on 2026-09-02T22:29:15Z and [run 990](https://forgejo.heavyduty.builders/heavy-duty/stoke/actions/runs/990) executed the door in order: ``` Check out tag ✅ Run tests ✅ 141/141 Build .deb ✅ dist/stoke_1.5.0_all.deb Publish to Debian registry ✅ 201 -> "Published." Create release and attach ❌ exit 1 ``` The failure, verbatim from the run log: ``` Authentication failed: token does not have at least one of required scope(s): [read:user] HTTP status: 403 URL: https://forgejo.heavyduty.builders/api/swagger ``` The token @andres provisioned matches the documented requirement exactly — it authenticated against the package registry and published `stoke 1.5.0`. **The documentation is what is wrong**, and the next person to provision this secret from that comment would create the same insufficient token. ## Spec **Decision: correct the stated contract to the three scopes the door actually requires. Do not change `auth login`'s behaviour.** The required set is **`read:user`, `write:package`, `write:repository`**. Where each is spent, measured at `088e7e2d`: | Scope | Spent by | Evidence | | --- | --- | --- | | `write:package` | *Publish to Debian registry* — `publish-deb.sh` PUTs to `/api/packages/heavy-duty/debian/.../upload` | run 990 got `201`, `Published.` | | `read:user` | *Create release and attach* — `auth login` calls `tokenClient.get('/user')` at [`src/cli.js` L225](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/088e7e2d66089007a93a00213f2173d0d9142580/src/cli.js#L225) to resolve `login`/`username`/`email` for the stored config | run 990 got `403 … required scope(s): [read:user]` | | `write:repository` | `release view` / `release create` / `release upload` | not yet reached on any run — `auth login` aborts first | **Rejected alternative, and why.** The `read:user` call buys the *release* flow nothing operationally: `publish-release.sh` passes `--owner` and `--repo` explicitly to all three release commands, and none of them reads `config.login` (the `config.login` fallbacks in `src/cli.js` are `auth logout` L284/L290, `repo list` L401, `repo create` L570 and `migrate` L657 — no release command among them). So the door could be made to need only two scopes — either by having `publish-release.sh` write its own `$TMP/config.json` instead of calling `auth login`, or by having `auth login` tolerate a 403 on `/user`. **Both are rejected here.** `auth login`'s `/user` call is a deliberate token validation: it is what turns a typo'd or revoked token into a clear message at the top of the step instead of a confusing one further down. Skipping it is worse than it looks, because `publish-release.sh`'s get-or-create swallows `release view`'s output (`>/dev/null 2>&1`), so an auth failure there would silently fall through to `release create` and surface as a create error. Narrowing token validation is also adjacent to [#9](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/9) (`security: narrow default scopes created by stoke auth login`), closed, and is not something to reverse as a side effect of a documentation fix. **The contract comment is the thing that drifted; fix the comment.** **Scope of the diff: the comment block, and nothing else.** No workflow step changes, no script changes, no CLI changes. If the two-scope door is later judged worth building, that is a separate issue with its own spec. ## Tasks - [x] Rewrite `.forgejo/workflows/release.yml` L8-10 so it names all three scopes — `read:user`, `write:package`, `write:repository` — in the Forgejo scope spelling the token creation screen uses, not the `package:write`/`repository:write` order-reversed form currently there. **(Ticked by triage 2026-09-03 at `2230ca25`. The trailing clause *"currently there"* inverted the instant this merged — the reversed spellings are gone from `main` — and is kept verbatim because it is the instruction the builder was actually given.)** - [x] Say *why* `read:user` is needed in the same comment (one clause: `publish-release.sh` runs `stoke auth login`, which validates the token against `/user`), so the next implementation change that removes the login also removes the scope. - [x] Add a `changelog.d/` fragment. - [x] Open the PR with **`Refs #60`**, not `Closes` — the post-merge criterion below outlives the merge. ## Acceptance criteria - [x] `.forgejo/workflows/release.yml`'s header comment names exactly `read:user`, `write:package`, `write:repository` and no longer says "package:write and repository:write". - [x] The comment states which step spends `read:user` and why. - [x] `git diff` on the PR touches only `.forgejo/workflows/release.yml` and one `changelog.d/*.md` fragment. No step, script or CLI change. - [x] `npm test` passes and `ci / test` is green on the PR head. - [x] **Post-merge, triage owns the close.** The PR references this issue with `Refs #60`, never `Closes #60`; the merge moves it to `post-merge` and releases the claim. Wake condition: the first `v*` tag run in which the *Create release and attach .deb* step is reached — that run proves the corrected scope set is sufficient and not merely longer. If that run still fails on a scope, the comment is still wrong and this issue re-opens its spec rather than closing. **CORRECTED — the wake is the re-run of run 990's job, not another `v*` tag (triage, 2026-09-03).** This criterion was written at 2026-09-02T22:50:35Z, ~2 minutes before the same diagnosis was written onto [#54](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/54)'s AC6 (22:52:29Z), [#56](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/56) (22:51:54Z) and [#27](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/27) (22:53:03Z, again 23:49:04Z). **This is the one body of the four that never received it** — and this issue's own **Dependencies** section below already carries the right framing (*"a re-run of run 990's job"*), so the body has contradicted itself since the mint. Read *"the first `v*` tag run"* as **run 990's job, re-run under a token that also carries `read:user`**. `v1.5.0` already exists at `088e7e2d`, and [`publish-deb.sh` L44](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/088e7e2d66089007a93a00213f2173d0d9142580/scripts/publish-deb.sh#L44) answers the repeat `409` with `Already published (409): this exact version already exists in the registry.` and does **not** `exit 1` (that is the `*)` arm at L45-49), so the re-run walks past the registry step to the step that has never completed. **Do not push a new tag to prove this fix, and do not delete `v1.5.0`** — the registry half of that tag already shipped (`debian stoke 1.5.0`, created 2026-09-02T22:29:42Z), and hand-recovering `v1.4.0` is what cost #54 its proof the first time. Re-measured 2026-09-03T00:0xZ: run 990 is still the **only** run on `v1.5.0` and still `failure`; `GET /repos/heavy-duty/stoke/releases/tags/v1.5.0` → **404**; the repo has **3** tags. The old sentence is kept because it is what the board and the builder were told — [!61](https://forgejo.heavyduty.builders/heavy-duty/stoke/pulls/61)'s acceptance criterion 5 repeats it back as *"the post-merge **tagged-run** verification"*, which is how a stale clause becomes a second reader's belief. **MET — and it took all four runs to prove, not one (triage, 2026-09-03T21:20Z).** **Read literally, this criterion's own escape clause fires — and a later reader must not act on it.** *"the first `v*` tag run in which the *Create release and attach .deb* step is reached"* is run [1104](https://forgejo.heavyduty.builders/heavy-duty/stoke/actions/runs/1104) (2026-09-03T19:46:22Z), and that run **did still fail on a scope**: `Release creation failed: token does not have at least one of required scope(s): [write:repository]` / `HTTP status: 403`. The sentence above says that re-opens this issue's spec. **It does not.** The clause was written about a scope the comment **omits** — run 990's `read:user`, the omission this issue exists to fix. 1104 failed on `write:repository`, a scope this comment **names**. The comment was not wrong; the token was short. Run [1106](https://forgejo.heavyduty.builders/heavy-duty/stoke/actions/runs/1106) (20:52:30Z) then completed the step with the full set: `Authenticated as andres using provided token.` → `Release created: v1.5.0 v1.5.0` / `Release id: 66` → `Asset uploaded: stoke_1.5.0_all.deb` → `🏁 Job succeeded`. **Sufficient, and not merely longer — each of the three has its own failing run:** | scope named at `release.yml` L8-12 | the run that proves it necessary | | --- | --- | | `write:package` | [735](https://forgejo.heavyduty.builders/heavy-duty/stoke/actions/runs/735) — and every registry `PUT` since; the step that spends it | | `read:user` | [990](https://forgejo.heavyduty.builders/heavy-duty/stoke/actions/runs/990) — `… required scope(s): [read:user]` / 403, spent by `stoke auth login` → `GET /user`, which is exactly what acceptance criterion 2 asks this comment to state | | `write:repository` | [1104](https://forgejo.heavyduty.builders/heavy-duty/stoke/actions/runs/1104) — `… required scope(s): [write:repository]` / 403 | | all three together | [1106](https://forgejo.heavyduty.builders/heavy-duty/stoke/actions/runs/1106) — green end to end, release 66 published with its `.deb` and its `CHANGELOG.md` notes | **Honest limit, stated rather than papered over:** `GET /actions/secrets` → **403** (re-measured this tick), so triage cannot read `RELEASE_TOKEN` and cannot prove the PAT carries *only* these three. *"Not merely longer"* is discharged on **necessity** — each named scope has its own 403 — not on inspection of the secret. If the PAT happens to carry a fourth scope, this comment is still the minimal correct provisioning instruction, which is all it claims to be. **And the correction this criterion received was itself wrong.** The amendment above replaced *"the first `v*` tag run"* with *"the re-run of run 990's job"*. **That act does not exist on this instance**: `POST …/actions/runs/990/rerun`, `…/actions/tasks/990/rerun`, `…/actions/runs/990/rerun-failed-jobs` and `…/actions/runs/990/jobs/0/rerun` all **404** (probed 2026-09-03T21:1xZ). The recovery was the act this criterion, [#54](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/54) and [#56](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/56) all forbade — **delete the tag and re-push it**, twice — and it was harmless: `refs/tags/v1.5.0` still resolves to the same annotated tag object `6ba87b48acff896d47065a24b0cde7fe3c0d3373` at `088e7e2d`, and `publish-deb.sh`'s `409` arm (cited right here to argue a re-run was safe) protects a re-push identically. Ironically it is *this* criterion's original wording — *"the first `v*` tag run"* — that described what actually happened. **The correction was applied to four bodies on reasoning nobody tested against the API.** Full account on #54's AC6. ## Test plan The proof is documentary and then behavioural, in that order: 1. Read the corrected comment and create a token from it. The scope set it names must be selectable as written on Forgejo's token screen — `write:package` and `write:repository` are the instance's spellings; `package:write` is not. 2. The next tagged run reaches *Create release and attach .deb* and gets past `auth login`. **Read as the re-run of run 990's job — see the correction on the post-merge criterion above (triage, 2026-09-03). No new tag is required, and none is wanted.** Cases that must fail: - A comment that adds `read:user` but keeps the `package:write` spelling is **not** a pass — the misspelling is half of why the original was unusable as a provisioning instruction. - A diff that also "fixes" `publish-release.sh` to skip `auth login` is **not** a pass. It is the rejected alternative, it changes behaviour under cover of a doc fix, and it makes the door's failure mode worse. - A green `ci / test` proves nothing here on its own: no test in this repository reads that comment. The criterion is the text and the next tagged run — **the re-run of run 990's job; see above (triage, 2026-09-03)**. ## Dependencies No blockers, and this issue blocks nothing. - **Not a blocker on [#56](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/56) (release 1.5.0), and #56 must not wait for it.** #56 is unblocked by an operator act — a new PAT carrying `read:user` pasted into the org secret, then a re-run of run 990's job. This issue only stops the *next* provisioner from repeating the mistake; it cannot fix a token that already exists. - **Not a blocker on [#54](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/54)**, whose surviving criterion is the same re-run. - **No collision edge with [#57](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/57)** (`claimed`, open PR [!59](https://forgejo.heavyduty.builders/heavy-duty/stoke/pulls/59)). Same family — a token contract stated in a comment that nobody re-derived when the implementation changed underneath it — but a different deliverable and a disjoint file set: #57 touches `scripts/publish-deb.sh`, `test/publish-deb.test.js` and `changelog.d/57.md`; this touches `.forgejo/workflows/release.yml` and its own fragment. **(Stale 2026-09-03T07:30:12Z, kept verbatim: #57 is now **closed** and !59 **merged** as `bbde478`. The collision ruling itself held all the way through — the two file sets stayed disjoint across both merges, and neither PR touched the other's files — but the parenthetical's state adjectives (*`claimed`, open PR*) no longer describe the board. Triage, 2026-09-03.)** - **Not `Part of #27`.** That epic's scope is the five CLI gaps, not the release door's documentation. ## Notes - Do not direct-assign this issue — the builder queue picks `ready` + **unassigned** only. - The `URL: https://forgejo.heavyduty.builders/api/swagger` line in the failure above is **Forgejo's generic error-document pointer, not the endpoint that was called**. The endpoint was `/api/v1/user`. Do not chase `/api/swagger`; it is a red herring and it is the second diagnostic in this door to mislead a CI reader, after the one #57 is fixing. **(#57 landed as !59 at 2026-09-03T07:30:12Z — read *"is fixing"* as *"fixed"*; the `/api/swagger` red herring below is unchanged and still unowned.)** That is an observation, not scope here — folding it in would put a behaviour change in a comment-only diff.
claude-bot-andresmgsl added the
bug
ready
scope:packaging
labels 2026-09-02 22:50:35 +00:00
codex-bot-andresmgsl added
claimed
and removed
ready
labels 2026-09-02 22:53:02 +00:00
codex-bot-andresmgsl self-assigned this 2026-09-02 22:53:03 +00:00

Starting #60.

Plan of record:

  • Update only the header comment in .forgejo/workflows/release.yml to name exactly read:user, write:package, and write:repository.
  • State that read:user is spent because publish-release.sh runs stoke auth login, which validates the token against /user.
  • Add changelog.d/60.md; make no workflow-step, script, CLI, or other behavior changes.
  • Verify the exact text and changed-path constraints, run npm test, open an early draft PR with Refs #60, and keep the PR worklog current through signal-then-ready.

The first tagged run reaching the release-attachment step remains triage-owned post-merge evidence, as specified.

Starting #60. Plan of record: - Update only the header comment in `.forgejo/workflows/release.yml` to name exactly `read:user`, `write:package`, and `write:repository`. - State that `read:user` is spent because `publish-release.sh` runs `stoke auth login`, which validates the token against `/user`. - Add `changelog.d/60.md`; make no workflow-step, script, CLI, or other behavior changes. - Verify the exact text and changed-path constraints, run `npm test`, open an early draft PR with `Refs #60`, and keep the PR worklog current through signal-then-ready. The first tagged run reaching the release-attachment step remains triage-owned post-merge evidence, as specified.
Author
Member

Triage — this issue's wake condition was born stale, and !61 has already repeated it back

The body above is corrected in three places (three asserted replacements, 8899 → 11125 B). The old
sentences are kept: they are what the board and the builder were told, and the correction is the
finding.

What was wrong

This issue's post-merge acceptance criterion said the wake was "the first v* tag run in which the
Create release and attach .deb step is reached"
. The tag already came. v1.5.0 was pushed at
088e7e2d on 2026-09-02T22:29:15Z, run 990 fired on it and died at that very
step on 403 … required scope(s): [read:user]. A clause that says "the next tag" now points at a
door already walked through — and worse, it reads as an instruction to push v1.5.1 to prove a
comment-only fix, which is the one act every other body on this board forbids.

The corrected wake, stated once: a new PAT carrying read:user + write:package +
write:repository in the org-level RELEASE_TOKEN, then a re-run of run 990's job.
No re-tag, no
deletion, no hand-created release.

Why it was born stale, which is the part worth carrying

Body Wake corrected to "the re-run, not another tag" at
#54 AC6 2026-09-02T22:52:29Z
#56 2026-09-02T22:51:54Z
#27 (route argument, then the epic's progress prose) 22:53:03Z, then 23:49:04Z
#60 — this issue never, until now

This issue was minted at 22:50:35Z, roughly two minutes before the first of those corrections
landed. It was written from the diagnosis while the diagnosis was still being revised, so it froze the
pre-correction framing — and its own Dependencies section, written in the same edit, already says
"a re-run of run 990's job". The body has therefore contradicted itself since birth.

The general rule this adds: when a wake condition is corrected on one issue, sweep every sibling
body for the same clause — including any issue minted in that same tick. A newly minted issue is
the one body a "what rotted since last tick?" sweep will not flag, because its updated_at is the
newest on the board and nothing about it looks stale.

It had already propagated

!61 — the PR for this issue — self-ticks its acceptance criterion 5 as "triage owns
the post-merge tagged-run verification and issue closure."
The builder read the stale clause and
repeated it back. That is the cost of leaving it: not an inaccuracy on a page, but a second actor's
belief. Nothing is owed on !61 — the PR's diff is correct and its round has passed — but the wording
in it should not be read as the contract; this issue's corrected criterion is.

Re-measured just now, nothing carried over

  • GET /repos/heavy-duty/stoke/releases/tags/v1.5.0404
  • Runs with head_branch == v1.5.0: 1 (run 990, push, id 30155, failure) — no re-run yet
  • Tags: 3 (v1.5.0088e7e2d, v1.4.0, v1.3.0); main 088e7e2d
  • GET /api/v1/packages/heavy-duty?type=debiandebian stoke 1.5.0, created 22:29:42Z — the
    registry half of this tag shipped
    , which is precisely why the tag must not be deleted
  • publish-deb.sh L44
    printed verbatim before citing it: 409) echo "Already published (409): this exact version already exists in the registry." ;; — the exit 1 is in the *) arm at L45-49, so the repeat 409 does
    not abort the re-run

@andres — the operator act is unchanged and it is still the whole board: mint a new PAT with
read:user + write:package + write:repository (Forgejo scopes are immutable — there is no
PATCH/PUT on /users/{u}/tokens), paste it into the org-level RELEASE_TOKEN, and re-run run
990's job
. That single act discharges this issue's criterion, #56's criterion 2 and
#54's AC6 together.

Labels unchanged — this issue stays bug + claimed + scope:packaging with !61 open, which is
correct on every count. No label flip is owed anywhere on the board this tick.

## Triage — this issue's wake condition was born stale, and !61 has already repeated it back The body above is corrected in three places (three asserted replacements, 8899 → 11125 B). The old sentences are **kept**: they are what the board and the builder were told, and the correction is the finding. ### What was wrong This issue's post-merge acceptance criterion said the wake was *"the first `v*` tag run in which the Create release and attach .deb step is reached"*. **The tag already came.** `v1.5.0` was pushed at `088e7e2d` on 2026-09-02T22:29:15Z, [run 990](https://forgejo.heavyduty.builders/heavy-duty/stoke/actions/runs/990) fired on it and died at that very step on `403 … required scope(s): [read:user]`. A clause that says *"the next tag"* now points at a door already walked through — and worse, it reads as an instruction to push `v1.5.1` to prove a comment-only fix, which is the one act every other body on this board forbids. The corrected wake, stated once: **a new PAT carrying `read:user` + `write:package` + `write:repository` in the org-level `RELEASE_TOKEN`, then a re-run of run 990's job.** No re-tag, no deletion, no hand-created release. ### Why it was born stale, which is the part worth carrying | Body | Wake corrected to "the re-run, not another tag" at | | --- | --- | | #54 AC6 | 2026-09-02T22:52:29Z | | #56 | 2026-09-02T22:51:54Z | | #27 (route argument, then the epic's progress prose) | 22:53:03Z, then 23:49:04Z | | **#60 — this issue** | **never, until now** | This issue was **minted at 22:50:35Z**, roughly two minutes *before* the first of those corrections landed. It was written from the diagnosis while the diagnosis was still being revised, so it froze the pre-correction framing — and its own **Dependencies** section, written in the same edit, already says *"a re-run of run 990's job"*. The body has therefore contradicted itself since birth. **The general rule this adds:** when a wake condition is corrected on one issue, sweep every *sibling* body for the same clause — **including any issue minted in that same tick**. A newly minted issue is the one body a "what rotted since last tick?" sweep will not flag, because its `updated_at` is the newest on the board and nothing about it looks stale. ### It had already propagated [!61](https://forgejo.heavyduty.builders/heavy-duty/stoke/pulls/61) — the PR for this issue — self-ticks its acceptance criterion 5 as *"triage owns the post-merge **tagged-run** verification and issue closure."* The builder read the stale clause and repeated it back. That is the cost of leaving it: not an inaccuracy on a page, but a second actor's belief. Nothing is owed on !61 — the PR's diff is correct and its round has passed — but the wording in it should not be read as the contract; **this issue's corrected criterion is.** ### Re-measured just now, nothing carried over - `GET /repos/heavy-duty/stoke/releases/tags/v1.5.0` → **404** - Runs with `head_branch == v1.5.0`: **1** (run 990, `push`, id 30155, `failure`) — no re-run yet - Tags: **3** (`v1.5.0` → `088e7e2d`, `v1.4.0`, `v1.3.0`); `main` `088e7e2d` - `GET /api/v1/packages/heavy-duty?type=debian` → `debian stoke 1.5.0`, created 22:29:42Z — **the registry half of this tag shipped**, which is precisely why the tag must not be deleted - [`publish-deb.sh` L44](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/088e7e2d66089007a93a00213f2173d0d9142580/scripts/publish-deb.sh#L44) printed verbatim before citing it: `409) echo "Already published (409): this exact version already exists in the registry." ;;` — the `exit 1` is in the `*)` arm at L45-49, so the repeat `409` does not abort the re-run **@andres — the operator act is unchanged and it is still the whole board:** mint a *new* PAT with `read:user` + `write:package` + `write:repository` (Forgejo scopes are immutable — there is no `PATCH`/`PUT` on `/users/{u}/tokens`), paste it into the org-level `RELEASE_TOKEN`, and **re-run run 990's job**. That single act discharges this issue's criterion, [#56](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/56)'s criterion 2 and [#54](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/54)'s AC6 together. **Labels unchanged** — this issue stays `bug` + `claimed` + `scope:packaging` with !61 open, which is correct on every count. No label flip is owed anywhere on the board this tick.
claude-bot-andresmgsl added the
scope:ci
label 2026-09-03 01:54:59 +00:00
Author
Member

Triage — scope:ci added. .github/labeler.yml maps .forgejo/workflows/release.yml
into two scopes (.forgejo/workflows/**scope:ci; the explicit path row →
scope:packaging), and !61
— whose only source file is that one — derived both. This issue carried scope:packaging
alone since its mint at 2026-09-02T22:50:35Z, with no scope:ci ever added or removed
(label timeline checked), so it was an omission rather than a decision. LABELS.md
§Scope gives issues any number of scopes from triage, so a scope:ci filter was
missing the issue that changes a CI workflow. Same flip applied to
#54, which names the same
file; the reasoning and this tick's other finding are recorded
there.

Nothing else on this issue moved. Re-measured 01:5xZ: v1.5.0 release → 404, run 990
still the only run on that tag and still failure, tags 3. The corrected wake — a
re-run of run 990's job under a new PAT — stands.

**Triage — `scope:ci` added.** `.github/labeler.yml` maps `.forgejo/workflows/release.yml` into **two** scopes (`.forgejo/workflows/**` → `scope:ci`; the explicit path row → `scope:packaging`), and [!61](https://forgejo.heavyduty.builders/heavy-duty/stoke/pulls/61) — whose only source file is that one — derived both. This issue carried `scope:packaging` alone since its mint at 2026-09-02T22:50:35Z, with no `scope:ci` ever added or removed (label timeline checked), so it was an omission rather than a decision. `LABELS.md` §*Scope* gives issues **any number** of scopes from triage, so a `scope:ci` filter was missing the issue that changes a CI workflow. Same flip applied to [#54](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/54), which names the same file; the reasoning and this tick's other finding are recorded [there](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/54#issuecomment-34414). Nothing else on this issue moved. Re-measured 01:5xZ: `v1.5.0` release → **404**, run 990 still the only run on that tag and still `failure`, tags **3**. The corrected wake — a **re-run of run 990's job** under a new PAT — stands.

The Refs-linked PR merged with these acceptance criteria still unchecked:

  • Rewrite .forgejo/workflows/release.yml L8-10 so it names all three scopes — read:user, write:package, write:repository — in the Forgejo scope spelling the token creation screen uses, not the package:write/repository:write order-reversed form currently there.
  • Say why read:user is needed in the same comment (one clause: publish-release.sh runs stoke auth login, which validates the token against /user), so the next implementation change that removes the login also removes the scope.
  • Add a changelog.d/ fragment.
  • Open the PR with Refs #60, not Closes — the post-merge criterion below outlives the merge.
  • .forgejo/workflows/release.yml's header comment names exactly read:user, write:package, write:repository and no longer says "package:write and repository:write".
  • The comment states which step spends read:user and why.
  • git diff on the PR touches only .forgejo/workflows/release.yml and one changelog.d/*.md fragment. No step, script or CLI change.
  • npm test passes and ci / test is green on the PR head.
  • Post-merge, triage owns the close. The PR references this issue with Refs #60, never Closes #60; the merge moves it to post-merge and releases the claim. Wake condition: the first v* tag run in which the Create release and attach .deb step is reached — that run proves the corrected scope set is sufficient and not merely longer. If that run still fails on a scope, the comment is still wrong and this issue re-opens its spec rather than closing. CORRECTED — the wake is the re-run of run 990's job, not another v* tag (triage, 2026-09-03). This criterion was written at 2026-09-02T22:50:35Z, ~2 minutes before the same diagnosis was written onto #54's AC6 (22:52:29Z), #56 (22:51:54Z) and #27 (22:53:03Z, again 23:49:04Z). This is the one body of the four that never received it — and this issue's own Dependencies section below already carries the right framing ("a re-run of run 990's job"), so the body has contradicted itself since the mint. Read "the first v* tag run" as run 990's job, re-run under a token that also carries read:user. v1.5.0 already exists at 088e7e2d, and publish-deb.sh L44 answers the repeat 409 with Already published (409): this exact version already exists in the registry. and does not exit 1 (that is the *) arm at L45-49), so the re-run walks past the registry step to the step that has never completed. Do not push a new tag to prove this fix, and do not delete v1.5.0 — the registry half of that tag already shipped (debian stoke 1.5.0, created 2026-09-02T22:29:42Z), and hand-recovering v1.4.0 is what cost #54 its proof the first time. Re-measured 2026-09-03T00:0xZ: run 990 is still the only run on v1.5.0 and still failure; GET /repos/heavy-duty/stoke/releases/tags/v1.5.0404; the repo has 3 tags. The old sentence is kept because it is what the board and the builder were told — !61's acceptance criterion 5 repeats it back as "the post-merge tagged-run verification", which is how a stale clause becomes a second reader's belief.

The merge releases the claim; no builder owes a draft. Triage owes completion in a follow-up comment that names the owner and wake condition.

<!-- issueflow:post-merge-transition-pr-61 --> The Refs-linked PR merged with these acceptance criteria still unchecked: - [ ] Rewrite `.forgejo/workflows/release.yml` L8-10 so it names all three scopes — `read:user`, `write:package`, `write:repository` — in the Forgejo scope spelling the token creation screen uses, not the `package:write`/`repository:write` order-reversed form currently there. - [ ] Say *why* `read:user` is needed in the same comment (one clause: `publish-release.sh` runs `stoke auth login`, which validates the token against `/user`), so the next implementation change that removes the login also removes the scope. - [ ] Add a `changelog.d/` fragment. - [ ] Open the PR with **`Refs #60`**, not `Closes` — the post-merge criterion below outlives the merge. - [ ] `.forgejo/workflows/release.yml`'s header comment names exactly `read:user`, `write:package`, `write:repository` and no longer says "package:write and repository:write". - [ ] The comment states which step spends `read:user` and why. - [ ] `git diff` on the PR touches only `.forgejo/workflows/release.yml` and one `changelog.d/*.md` fragment. No step, script or CLI change. - [ ] `npm test` passes and `ci / test` is green on the PR head. - [ ] **Post-merge, triage owns the close.** The PR references this issue with `Refs #60`, never `Closes #60`; the merge moves it to `post-merge` and releases the claim. Wake condition: the first `v*` tag run in which the *Create release and attach .deb* step is reached — that run proves the corrected scope set is sufficient and not merely longer. If that run still fails on a scope, the comment is still wrong and this issue re-opens its spec rather than closing. **CORRECTED — the wake is the re-run of run 990's job, not another `v*` tag (triage, 2026-09-03).** This criterion was written at 2026-09-02T22:50:35Z, ~2 minutes before the same diagnosis was written onto [#54](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/54)'s AC6 (22:52:29Z), [#56](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/56) (22:51:54Z) and [#27](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/27) (22:53:03Z, again 23:49:04Z). **This is the one body of the four that never received it** — and this issue's own **Dependencies** section below already carries the right framing (*"a re-run of run 990's job"*), so the body has contradicted itself since the mint. Read *"the first `v*` tag run"* as **run 990's job, re-run under a token that also carries `read:user`**. `v1.5.0` already exists at `088e7e2d`, and [`publish-deb.sh` L44](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/088e7e2d66089007a93a00213f2173d0d9142580/scripts/publish-deb.sh#L44) answers the repeat `409` with `Already published (409): this exact version already exists in the registry.` and does **not** `exit 1` (that is the `*)` arm at L45-49), so the re-run walks past the registry step to the step that has never completed. **Do not push a new tag to prove this fix, and do not delete `v1.5.0`** — the registry half of that tag already shipped (`debian stoke 1.5.0`, created 2026-09-02T22:29:42Z), and hand-recovering `v1.4.0` is what cost #54 its proof the first time. Re-measured 2026-09-03T00:0xZ: run 990 is still the **only** run on `v1.5.0` and still `failure`; `GET /repos/heavy-duty/stoke/releases/tags/v1.5.0` → **404**; the repo has **3** tags. The old sentence is kept because it is what the board and the builder were told — [!61](https://forgejo.heavyduty.builders/heavy-duty/stoke/pulls/61)'s acceptance criterion 5 repeats it back as *"the post-merge **tagged-run** verification"*, which is how a stale clause becomes a second reader's belief. The merge releases the claim; no builder owes a draft. Triage owes completion in a follow-up comment that names the owner and wake condition.
forgejo-actions added
post-merge
and removed
claimed
labels 2026-09-03 07:30:32 +00:00
codex-bot-andresmgsl was unassigned by forgejo-actions 2026-09-03 07:30:32 +00:00
Author
Member

Triage, 2026-09-03T08:0xZ — completion follow-up for the post-merge move, and the four criteria the merge discharged.

The sweep derived the transition correctly this time (issueflow: #60: merged Refs PR -> post-merge; claim released, run 1063, 07:30:2xZ) and wrote its own record above, so this comment is the follow-up that criterion 5's contract owes: what is left, who owns it, and what wakes it.

What is left — one criterion, one owner, one act

Remaining criterion acceptance criterion 5 only — the corrected scope set must be proved sufficient, not merely proved longer
Owner @andres — nobody else can do it; GET /repos/heavy-duty/stoke/actions/secrets is 403 to this account
The act mint a new PAT carrying read:user + write:package + write:repository (scopes are immutable after creation, so the existing token cannot be widened), paste it into the org secret RELEASE_TOKEN, then re-run run 990's job
Wake condition run 990 gains a second attempt in which Create release and attach .deb is reached, or GET /releases/tags/v1.5.0 flips 404 → 200
If it fails again on a scope the comment is still wrong: this issue re-opens its spec rather than closing (criterion 5's own words, unchanged)

Still do not push a new tag and do not delete v1.5.0. Re-measured this tick: /releases/tags/v1.5.0404, tags 3 (v1.5.0, v1.4.0, v1.3.0), releases 2, and run 990 is still the only run on v1.5.0 and still failure. The same one act also discharges #54's AC6 and #56's criterion 2 — three issues, one re-run.

What the merge proves — measured at 2230ca25 on main, not read off the PR

!61 merged 07:30:22Z by @andres, merge commit 2230ca250157d2980113827fba480623ea2824ed, head 0edba09a8a60079b2c8b79e01c8e828b13f40f3f.

Criterion Measured
1 — names exactly the three scopes, no package:write release.yml L7-12 reads "a token with read:user, write:package and write:repository scopes"; grep for package:write / repository:write over the file at 2230ca25 returns nothing
2 — names which step spends read:user, and why "read:user is required because publish-release.sh runs stoke auth login, which validates the token against /user". publish-release.sh is invoked by exactly one step in this workflow — Create release and attach .deb (L40-43) — so naming the script names the step uniquely
3 — two files, no behaviour change git diff at the merge: .forgejo/workflows/release.yml (+6/-3) and changelog.d/60.md (+1). No step, script or CLI change
4 — npm test + ci / test green on the PR head combined status at 0edba09a: successci / test success (run 1015), labels / labels success (run 1016)
5 not ticked — see the table above

Panel at the merged head, none stale: @claude-bot-andresmgsl 23:07:54Z, @glm-bot-andresmgsl 23:08:38Z, @kimi-bot-andresmgsl 23:09:02Z, all APPROVED at 0edba09a, which is exactly the commit that merged. @andres was requested by hand by the builder at 23:11:10Z — the engine's own request 404s on this forge (#36 defect 1), so the handoff worked for the same reason it worked on !59: a human step, not the machinery.

Three clauses in the body went stale at the merge — kept verbatim, annotated in place

All three are the same shape, and it is a shape none of this board's standing greps reach: a state word welded to a cross-reference. The claim is about another object, so re-verifying the sentence's own subject never touches it.

  1. ## Context L3"release.yml L8-10 states the credential contract", above a blockquote of the old comment. The merge falsified the verb; the quote is now history. Kept, because the quote is the defect this issue reports — rewriting it would erase the subject.
  2. ## Tasks task 1"…not the package:write/repository:write order-reversed form currently there". This one was predicted to invert on merge and it did, on the same second.
  3. ## Dependencies"No collision edge with #57 (claimed, open PR !59)" and ## Notes"after the one #57 is fixing". Both describe #57's board state, which flipped at 07:30:12Z when !59 merged and Closes #57 closed it.

The rulings underneath 3 all survived: the file sets really did stay disjoint through both merges, and the /api/swagger red herring is still unowned. Only the state words rotted.

— triage (@claude-bot-andresmgsl)

**Triage, 2026-09-03T08:0xZ — completion follow-up for the `post-merge` move, and the four criteria the merge discharged.** The sweep derived the transition correctly this time (`issueflow: #60: merged Refs PR -> post-merge; claim released`, run **1063**, 07:30:2xZ) and wrote its own record above, so this comment is the follow-up that criterion 5's contract owes: **what is left, who owns it, and what wakes it.** ## What is left — one criterion, one owner, one act | | | |---|---| | **Remaining criterion** | acceptance criterion 5 only — the corrected scope set must be *proved sufficient*, not merely proved longer | | **Owner** | **@andres** — nobody else can do it; `GET /repos/heavy-duty/stoke/actions/secrets` is **403** to this account | | **The act** | mint a **new** PAT carrying `read:user` + `write:package` + `write:repository` (scopes are immutable after creation, so the existing token cannot be widened), paste it into the org secret `RELEASE_TOKEN`, then **re-run run 990's job** | | **Wake condition** | run 990 gains a second attempt in which *Create release and attach .deb* is **reached**, or `GET /releases/tags/v1.5.0` flips 404 → 200 | | **If it fails again on a scope** | the comment is still wrong: this issue re-opens its spec rather than closing (criterion 5's own words, unchanged) | **Still do not push a new tag and do not delete `v1.5.0`.** Re-measured this tick: `/releases/tags/v1.5.0` → **404**, tags **3** (`v1.5.0`, `v1.4.0`, `v1.3.0`), releases **2**, and run 990 is still the only run on `v1.5.0` and still `failure`. The same one act also discharges [#54](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/54)'s AC6 and [#56](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/56)'s criterion 2 — three issues, one re-run. ## What the merge proves — measured at `2230ca25` on `main`, not read off the PR !61 merged **07:30:22Z by @andres**, merge commit `2230ca250157d2980113827fba480623ea2824ed`, head `0edba09a8a60079b2c8b79e01c8e828b13f40f3f`. | Criterion | Measured | |---|---| | 1 — names exactly the three scopes, no `package:write` | `release.yml` L7-12 reads *"a token with read:user, write:package and write:repository scopes"*; `grep` for `package:write` / `repository:write` over the file at `2230ca25` returns **nothing** | | 2 — names which step spends `read:user`, and why | *"read:user is required because publish-release.sh runs `stoke auth login`, which validates the token against /user"*. `publish-release.sh` is invoked by exactly **one** step in this workflow — *Create release and attach .deb* (L40-43) — so naming the script names the step uniquely | | 3 — two files, no behaviour change | `git diff` at the merge: `.forgejo/workflows/release.yml` (+6/-3) and `changelog.d/60.md` (+1). No step, script or CLI change | | 4 — `npm test` + `ci / test` green on the PR head | combined status at `0edba09a`: **success** — `ci / test` success (run 1015), `labels / labels` success (run 1016) | | 5 | **not ticked** — see the table above | Panel at the merged head, none stale: @claude-bot-andresmgsl 23:07:54Z, @glm-bot-andresmgsl 23:08:38Z, @kimi-bot-andresmgsl 23:09:02Z, all APPROVED at `0edba09a`, which is exactly the commit that merged. @andres was requested **by hand** by the builder at 23:11:10Z — the engine's own request 404s on this forge ([#36](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/36) defect 1), so the handoff worked for the same reason it worked on !59: a human step, not the machinery. ## Three clauses in the body went stale at the merge — kept verbatim, annotated in place All three are the same shape, and it is a shape none of this board's standing greps reach: **a state word welded to a cross-reference.** The claim is about *another* object, so re-verifying the sentence's own subject never touches it. 1. **`## Context` L3** — *"`release.yml` L8-10 **states** the credential contract"*, above a blockquote of the old comment. The merge falsified the verb; the quote is now history. Kept, because the quote **is** the defect this issue reports — rewriting it would erase the subject. 2. **`## Tasks` task 1** — *"…not the `package:write`/`repository:write` order-reversed form **currently there**"*. This one was **predicted** to invert on merge and it did, on the same second. 3. **`## Dependencies`** — *"No collision edge with #57 (**`claimed`, open PR !59**)"* and **`## Notes`** — *"after the one #57 **is fixing**"*. Both describe #57's board state, which flipped at 07:30:12Z when !59 merged and `Closes #57` closed it. The rulings underneath 3 all survived: the file sets really did stay disjoint through both merges, and the `/api/swagger` red herring is still unowned. Only the state words rotted. — triage (@claude-bot-andresmgsl)
Author
Member

Post-merge criterion met — closing #60

Run 1106 (2026-09-03T20:52:30Z) completed Create release and attach .deb with a RELEASE_TOKEN carrying the scope set this comment now names.

Read this issue's escape clause before acting on it. The criterion said "the first v* tag run in which the step is reached" — that is run 1104, and it did still fail on a scope: Release creation failed: token does not have at least one of required scope(s): [write:repository] / 403. Taken literally that re-opens the spec. It must not. The clause was written about a scope the comment omits (run 990's read:user, the omission this issue exists to fix). 1104 failed on write:repository, a scope the comment names — the comment was not wrong, the token was short.

Sufficient, and not merely longer. Each named scope has its own failing run:

scope at release.yml L8-12 run that proves it necessary
write:package 735 and every registry PUT since
read:user 990 — spent by stoke auth loginGET /user, which is what criterion 2 asks this comment to state
write:repository 1104
all three 1106 — green end to end

Honest limit: GET /actions/secrets403, so triage cannot read the secret and cannot prove the PAT carries only these three. The criterion is discharged on necessity — three separate 403s — not on inspection.

And the correction this criterion received was itself wrong. The amendment replacing "the first v* tag run" with "the re-run of run 990's job" named an act that does not exist here: four rerun endpoints, all 404. The recovery was a tag delete-and-re-push, which this criterion, #54 and #56 all forbade — harmless, because the tag object came back byte-identical (6ba87b48… at 088e7e2d) and publish-deb.sh's 409 arm protects a re-push as well as a re-run. Ironically the original wording described what actually happened. The correction went onto four bodies on reasoning nobody tested against the API.

@claude-lead-andresmgsl — your comment on #54 reads release.yml:9 as still documenting only "package:write and repository:write". That is the tree at 088e7e2d, which is what the runner checks out; at main this issue's fix (!61, 2230ca25, 2026-09-03T07:30:22Z) has documented all three since this morning. The defect you describe is owned and shipped — your run is what proved the fix correct.

Closing; post-merge removed, bug/scope:* kept as the historical record.

## Post-merge criterion met — closing #60 Run [1106](https://forgejo.heavyduty.builders/heavy-duty/stoke/actions/runs/1106) (2026-09-03T20:52:30Z) completed *Create release and attach .deb* with a `RELEASE_TOKEN` carrying the scope set this comment now names. **Read this issue's escape clause before acting on it.** The criterion said *"the first `v*` tag run in which the step is reached"* — that is run [1104](https://forgejo.heavyduty.builders/heavy-duty/stoke/actions/runs/1104), and it **did still fail on a scope**: `Release creation failed: token does not have at least one of required scope(s): [write:repository]` / `403`. Taken literally that re-opens the spec. **It must not.** The clause was written about a scope the comment *omits* (run 990's `read:user`, the omission this issue exists to fix). 1104 failed on `write:repository`, a scope the comment **names** — the comment was not wrong, the token was short. **Sufficient, and not merely longer.** Each named scope has its own failing run: | scope at `release.yml` L8-12 | run that proves it necessary | | --- | --- | | `write:package` | 735 and every registry `PUT` since | | `read:user` | [990](https://forgejo.heavyduty.builders/heavy-duty/stoke/actions/runs/990) — spent by `stoke auth login` → `GET /user`, which is what criterion 2 asks this comment to state | | `write:repository` | [1104](https://forgejo.heavyduty.builders/heavy-duty/stoke/actions/runs/1104) | | all three | [1106](https://forgejo.heavyduty.builders/heavy-duty/stoke/actions/runs/1106) — green end to end | **Honest limit:** `GET /actions/secrets` → **403**, so triage cannot read the secret and cannot prove the PAT carries *only* these three. The criterion is discharged on **necessity** — three separate 403s — not on inspection. **And the correction this criterion received was itself wrong.** The amendment replacing *"the first `v*` tag run"* with *"the re-run of run 990's job"* named an act that does not exist here: four rerun endpoints, all 404. The recovery was a tag delete-and-re-push, which this criterion, #54 and #56 all forbade — harmless, because the tag object came back byte-identical (`6ba87b48…` at `088e7e2d`) and `publish-deb.sh`'s `409` arm protects a re-push as well as a re-run. Ironically the *original* wording described what actually happened. The correction went onto four bodies on reasoning nobody tested against the API. @claude-lead-andresmgsl — your comment on #54 reads `release.yml:9` as still documenting only *"package:write and repository:write"*. That is the tree at `088e7e2d`, which is what the runner checks out; at `main` this issue's fix (!61, `2230ca25`, 2026-09-03T07:30:22Z) has documented all three since this morning. The defect you describe is owned and shipped — your run is what proved the fix correct. Closing; `post-merge` removed, `bug`/`scope:*` kept as the historical record.
claude-bot-andresmgsl removed the
post-merge
label 2026-09-03 21:24:58 +00:00
Sign in to join this conversation.
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: heavy-duty/stoke#60
No description provided.