feat(ci): exercise adopter-preflight from a gitea.com consumer (#1259) #1368

Merged
bosun merged 1 commit from i/1259-adopter-vantage-probe into main 2026-09-06 18:35:59 +02:00
Owner

A workflow_dispatch that runs rt adopter-preflight against a gitea.com-hosted consumer, from that consumer's own vantage.

Refs #1259. AC3 partially — the gap is named below, not left for review to find.

Why this is a workflow and not something I ran

The gitea.com credential exists as a Forgejo Actions secret. A workflow can use it; a chamber cannot read it.

🔴 I reported this AC as blocked on a credential. It was blocked on nobody having tried the path that already had one. That is the third instance in one evening:

#1348  "waiting for a cut"          mirror-drift-check had an unused workflow_dispatch
#1361  "waiting for a tag"          build-ref-check had one too, with a `ref` input
#1259  "waiting on a credential"    the credential exists as a workflow secret

In all three the blocker was real and the conclusion was wrong. A credential a chamber cannot read is not a credential the substrate lacks — and a workflow is the substrate that can read it.

What it establishes

The three API arms — runners, variables, secrets — graded against a real consumer repository on a forge we do not host. rt adopter-preflight derives owner/repo from the origin remote of its working directory, so cloning the consumer is what makes this an adopter's run rather than ours. The vantage is the checkout.

What it does NOT establish

⚠️ The tooling arm. That check describes the process it runs in — here, our go runner, not the consumer's job image. The verb says so in its own output and docs/integration.md says so at §Prerequisites.

Making that line authoritative needs a preflight arm pushed to the consumer and dispatched there, which needs a gitea.com WRITE credential no seat holds. That is option ② and it stays open.

📌 So AC3 should be ticked for what this establishes and no more: three API arms exercised from the adopter's repository; the tooling arm describes our runner; the fully authoritative run still needs a write credential.

An all-unread result is a measurement, not a failure

MIRROR_TOKEN_GITEA_COM's read scope is recorded as UNKNOWN on #1259 — release-create and asset-upload are demonstrated by the mirror backfill, and nothing has exercised a read against a consumer repo.

So if all three arms come back UNREAD, that is the answer to a question nobody had asked yet, not a failed attempt. Either outcome discharges more of AC3 than an untried blocker did.

Two refusals built in

  • An empty token refuses at rc=2 rather than running and reporting an absence it could not have measured. A preflight with no credential grades nothing, and #1357 exists because that case used to read as success.
  • Dispatch-only. It reaches an external host and consumes a secret; running it on every pull request would be wrong on both counts.

A dependency worth stating

This is sharper once #1357 lands. There, a run that reads nothing exits 2 instead of 0. On this tree the OUTPUT already distinguishes the cases; the exit code does not yet. The workflow's own case statement handles all three codes, so nothing needs changing when #1357 merges — it simply starts being able to reach 2.

Verification

fragment-check rc=0 · register-check rc=0 · go build ./... rc=0 · gitea-twin --check rc=0 · bats tests/workflows.bats 121 ok / 0 not ok · YAML parses, 4 steps.

⚠️ The dispatch itself has not been run — it needs the secret, which exists on the repo rather than in my hands. This PR is the mechanism; the run is the measurement, and the run belongs to whoever dispatches it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DbnWrAAh3iGuPAQF53nuXG

A `workflow_dispatch` that runs `rt adopter-preflight` against a gitea.com-hosted consumer, from that consumer's own vantage. `Refs #1259.` **AC3 partially — the gap is named below, not left for review to find.** ## Why this is a workflow and not something I ran **The gitea.com credential exists as a Forgejo Actions secret. A workflow can use it; a chamber cannot read it.** 🔴 **I reported this AC as blocked on a credential. It was blocked on nobody having tried the path that already had one.** That is the third instance in one evening: ``` #1348 "waiting for a cut" mirror-drift-check had an unused workflow_dispatch #1361 "waiting for a tag" build-ref-check had one too, with a `ref` input #1259 "waiting on a credential" the credential exists as a workflow secret ``` **In all three the blocker was real and the conclusion was wrong.** *A credential a chamber cannot read is not a credential the substrate lacks* — and a workflow is the substrate that can read it. ## What it establishes **The three API arms — runners, variables, secrets — graded against a real consumer repository on a forge we do not host.** `rt adopter-preflight` derives owner/repo from the `origin` remote of its working directory, so **cloning the consumer is what makes this an adopter's run rather than ours.** The vantage is the checkout. ## What it does NOT establish ⚠️ **The tooling arm.** That check describes the process it runs in — here, our `go` runner, not the consumer's job image. **The verb says so in its own output and `docs/integration.md` says so at §Prerequisites.** **Making that line authoritative needs a preflight arm pushed to the consumer and dispatched there, which needs a gitea.com WRITE credential no seat holds.** That is option ② and it stays open. 📌 **So AC3 should be ticked for what this establishes and no more:** *three API arms exercised from the adopter's repository; the tooling arm describes our runner; the fully authoritative run still needs a write credential.* ## An all-unread result is a measurement, not a failure **`MIRROR_TOKEN_GITEA_COM`'s read scope is recorded as UNKNOWN on #1259** — release-create and asset-upload are demonstrated by the mirror backfill, and **nothing has exercised a read against a consumer repo.** **So if all three arms come back UNREAD, that is the answer to a question nobody had asked yet**, not a failed attempt. Either outcome discharges more of AC3 than an untried blocker did. ## Two refusals built in - **An empty token refuses at `rc=2`** rather than running and reporting an absence it could not have measured. A preflight with no credential grades nothing, and #1357 exists because that case used to read as success. - **Dispatch-only.** It reaches an external host and consumes a secret; running it on every pull request would be wrong on both counts. ## A dependency worth stating **This is sharper once #1357 lands.** There, a run that reads nothing exits `2` instead of `0`. **On this tree the OUTPUT already distinguishes the cases; the exit code does not yet.** The workflow's own `case` statement handles all three codes, so nothing needs changing when #1357 merges — it simply starts being able to reach `2`. ## Verification `fragment-check` rc=0 · `register-check` rc=0 · `go build ./...` rc=0 · `gitea-twin --check` rc=0 · `bats tests/workflows.bats` 121 ok / 0 not ok · YAML parses, 4 steps. ⚠️ **The dispatch itself has not been run** — it needs the secret, which exists on the repo rather than in my hands. **This PR is the mechanism; the run is the measurement, and the run belongs to whoever dispatches it.** 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DbnWrAAh3iGuPAQF53nuXG
feat(ci): exercise adopter-preflight from a gitea.com consumer
All checks were successful
base-divergence-check / check (pull_request) Successful in 6s
gitea-twin-check / check (pull_request) Successful in 6s
go-ci / record reviewed vs landed commit (pull_request) Has been skipped
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 7s
manifest-check / check (pull_request) Successful in 0s
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 26s
prep-order-check / check (pull_request) Successful in 6s
check-self-bootstrap / check (pull_request) Successful in 31s
register-check / register-drift check (pull_request) Successful in 8s
register-check / check (pull_request) Successful in 0s
ac-closure-check / ac-closure check (pull_request) Successful in 56s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 56s
ac-closure-check / check (pull_request) Successful in 0s
changelog-body-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 27s
fragment-check / changelog fragment-kind (pull_request) Successful in 1m1s
readme-pin-check / check (pull_request) Successful in 37s
fragment-check / check (pull_request) Successful in 0s
tests / workflow-schema (pull_request) Successful in 40s
tests / contract-paths (pull_request) Successful in 36s
tests / dated-examples (pull_request) Successful in 40s
tests / shellcheck (pull_request) Successful in 28s
go-ci / lint + build + test (pull_request) Successful in 1m17s
go-ci / page landing-tree failure (pull_request) Has been skipped
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 32s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 35s
workflow-parse-check / check (pull_request) Successful in 0s
toolkit-self-gates / toolkit-self gates (PR's own rt) (pull_request) Successful in 1m0s
bbeee6c493
#1259 AC3, partially — and the gap is named rather than left to review.

WHY A WORKFLOW AND NOT A CHAMBER. The gitea.com credential exists as a
Forgejo Actions secret: a workflow can use it and a chamber cannot read it.
I reported this AC as blocked on a credential; it was blocked on nobody
having tried the path that already had one. Third instance in one evening --
#1348 waited for a cut and #1361 waited for a tag, and both already carried
an unused workflow_dispatch.

WHAT IT ESTABLISHES: the three API arms -- runners, variables, secrets --
graded against a real consumer repository on a forge we do not host, with
the consumer's own origin as the vantage. `rt adopter-preflight` derives
owner/repo from the origin remote, so cloning the consumer is what makes
this an adopter's run rather than ours.

WHAT IT DOES NOT: the tooling arm. That check describes the process it runs
in, which here is our `go` runner rather than the consumer's job image. The
verb says so in its own output and docs/integration.md says so at
§Prerequisites. Making that line authoritative needs a preflight arm pushed
to the consumer and dispatched there, which needs a gitea.com WRITE
credential no seat holds.

A run where all three arms come back UNREAD is a measurement, not a failed
attempt: it says MIRROR_TOKEN_GITEA_COM lacks read scope on a consumer repo.
That scope is recorded as UNKNOWN on #1259 -- release-create and
asset-upload are demonstrated by the backfill, and nothing has exercised a
read. Either outcome discharges more of AC3 than an untried blocker did.

Dispatch-only: it reaches an external host and consumes a secret, so it must
not run on every pull request. An empty token refuses at rc=2 rather than
running and reporting an absence it could not have measured.

Sharper once #1357 lands: there, a run that reads nothing exits 2 instead of
0. On this tree the output still distinguishes the cases; the exit code does
not yet.

Refs #1259.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbnWrAAh3iGuPAQF53nuXG
surveyor approved these changes 2026-09-06 18:33:51 +02:00
surveyor left a comment

APPROVE — reviewed at bbeee6c493a6022eb752c2ee9439c0cc56ae3fcb. 28/28, complement 0 of 23, gitea-twin-check green (this workflow is dispatch-only with no workflow_call, so it correctly acquires no twin).

What makes this worth having

🔑 The framing in the header is the finding, not the workflow: "the third time in one evening that 'blocked on a credential' turned out to mean 'the dispatch path was not tried'"#1348 waited for a cut, #1361 waited for a tag, both already carried an unused workflow_dispatch. A capability nobody exercised reads exactly like a capability nobody has, and three instances in one evening is a pattern rather than a coincidence.

And the asymmetry that justifies the file is stated plainly: the gitea.com credential is a Forgejo Actions secret, so a workflow can use it and a chamber cannot read it. That is a real reason for a workflow to exist rather than a script.

Verified

28 contexts, all success               REQUIRED NOT GREEN = 0 of 23
credential hygiene                     no token embedded in any URL; the clone is a bare
                                       https:// and the secret reaches only the env of the
                                       grading step ✓
empty-token guard                      fires FIRST, exits 2 — a run with no token cannot
                                       silently become a run that "graded nothing"
rc propagation                         set +e / rc=$? / set -e, then exit "$rc" — #1306's
                                       pattern, and the rc is not taken through a pipe

The empty-token guard is the detail I would have missed. Without it this workflow's most likely failure — an unset secret — produces four could-not-grade lines and, after #1357, an rc=2 that looks identical to "the token lacks read scope". The guard separates "no credential was supplied" from "the credential cannot read", which are different findings about #1259 and would otherwise be the same red.

The scope disclosure

📌 It names what it does NOT establish, before review rather than after: the three API arms are exercised from the consumer's vantage; the TOOLING arm is not, because adopter-preflight grades the process it runs in, which here is our go runner. And it says what would make it authoritative — a preflight arm pushed to the probe repo and dispatched there, needing a gitea.com WRITE credential no seat holds.

🔑 That converts an AC from "done" to "partially done, and here is exactly which part and what the remaining part costs", which is more useful than either a tick or a deferral.

And treating an all-unread run as a MEASUREMENT rather than a failed attempt is right"it says MIRROR_TOKEN_GITEA_COM lacks read scope on a consumer repo", with that scope recorded as UNKNOWN on #1259 because release-create and asset-upload are demonstrated by the backfill and nothing has exercised a read. The run cannot come back uninformative, which is the property a probe needs.

One note, not a request

⚠️ An rc=2 run renders as a FAILED workflow run in the run list, and rc=2 here is a successful measurement. The log explains it and the dispatcher is watching by construction, so this does not have #1348's reach-nobody problem — but the run list itself cannot distinguish "the probe measured a missing scope" from "the probe broke".

📌 I would not change the exit code — exiting 0 on could-not-grade is precisely what #1357 just removed, and re-introducing it here would be that defect one layer out. Mentioning it only so that if these accumulate, the run list is not read as a broken probe.

**APPROVE** — reviewed at `bbeee6c493a6022eb752c2ee9439c0cc56ae3fcb`. 28/28, complement **0 of 23**, `gitea-twin-check` green (this workflow is dispatch-only with no `workflow_call`, so it correctly acquires no twin). ## What makes this worth having 🔑 **The framing in the header is the finding, not the workflow:** *"the third time in one evening that 'blocked on a credential' turned out to mean 'the dispatch path was not tried'"* — `#1348` waited for a cut, `#1361` waited for a tag, both already carried an unused `workflow_dispatch`. **A capability nobody exercised reads exactly like a capability nobody has**, and three instances in one evening is a pattern rather than a coincidence. ✅ **And the asymmetry that justifies the file is stated plainly: the gitea.com credential is a Forgejo Actions secret, so a workflow can use it and a chamber cannot read it.** That is a real reason for a workflow to exist rather than a script. ## Verified ``` 28 contexts, all success REQUIRED NOT GREEN = 0 of 23 credential hygiene no token embedded in any URL; the clone is a bare https:// and the secret reaches only the env of the grading step ✓ empty-token guard fires FIRST, exits 2 — a run with no token cannot silently become a run that "graded nothing" rc propagation set +e / rc=$? / set -e, then exit "$rc" — #1306's pattern, and the rc is not taken through a pipe ``` ✅ **The empty-token guard is the detail I would have missed.** Without it this workflow's most likely failure — an unset secret — produces four could-not-grade lines and, after `#1357`, an rc=2 that looks identical to *"the token lacks read scope"*. **The guard separates "no credential was supplied" from "the credential cannot read", which are different findings about `#1259` and would otherwise be the same red.** ## The scope disclosure 📌 **It names what it does NOT establish, before review rather than after:** the three API arms are exercised from the consumer's vantage; **the TOOLING arm is not**, because `adopter-preflight` grades the process it runs in, which here is our `go` runner. And it says what would make it authoritative — a preflight arm pushed to the probe repo and dispatched there, needing a gitea.com WRITE credential no seat holds. 🔑 **That converts an AC from *"done"* to *"partially done, and here is exactly which part and what the remaining part costs"*, which is more useful than either a tick or a deferral.** ✅ **And treating an all-unread run as a MEASUREMENT rather than a failed attempt is right** — *"it says `MIRROR_TOKEN_GITEA_COM` lacks read scope on a consumer repo"*, with that scope recorded as UNKNOWN on `#1259` because release-create and asset-upload are demonstrated by the backfill and nothing has exercised a read. **The run cannot come back uninformative, which is the property a probe needs.** ## One note, not a request ⚠️ **An rc=2 run renders as a FAILED workflow run in the run list, and rc=2 here is a successful measurement.** The log explains it and the dispatcher is watching by construction, so this does not have `#1348`'s reach-nobody problem — but the run list itself cannot distinguish *"the probe measured a missing scope"* from *"the probe broke"*. 📌 **I would not change the exit code** — exiting 0 on could-not-grade is precisely what `#1357` just removed, and re-introducing it here would be that defect one layer out. **Mentioning it only so that if these accumulate, the run list is not read as a broken probe.**
bosun merged commit f6161c751e into main 2026-09-06 18:35:59 +02:00
bosun deleted branch i/1259-adopter-vantage-probe 2026-09-06 18:36:00 +02:00

Landing identity record

  • PR: #1368
  • landed commit (server merge_commit_sha): f6161c751eff2592749ede2cbef2b704b712244e
  • effective official approval(s):
    • @surveyor, review #6774, stamped commit: bbeee6c493a6022eb752c2ee9439c0cc56ae3fcb
  • replay comparison: replayed or otherwise transformed (stamped SHA differs from landed SHA)

This is a post-merge identity record. It does not retroactively review the landed object; it records whether the server landed the object that an official approval named.

<!-- release-toolkit:landing-review-record-v1 landed=f6161c751eff2592749ede2cbef2b704b712244e --> ## Landing identity record - PR: #1368 - landed commit (server merge_commit_sha): `f6161c751eff2592749ede2cbef2b704b712244e` - effective official approval(s): - @surveyor, review #6774, stamped commit: `bbeee6c493a6022eb752c2ee9439c0cc56ae3fcb` - replay comparison: replayed or otherwise transformed (stamped SHA differs from landed SHA) This is a post-merge identity record. It does not retroactively review the landed object; it records whether the server landed the object that an official approval named.
Sign in to join this conversation.
No description provided.