Commit graph

630 commits

Author SHA1 Message Date
b7a6aedc52 Merge pull request 'changelog.d/220.md — the 0.4.1 → 0.6.1 gap statement (#220)' (#221) from build/220-gap-fragment into main
All checks were successful
CI / test (push) Successful in 3m15s
CI / release-exercise (push) Has been skipped
CI / self-guards (push) Successful in 7s
CI / action-exercise (push) Successful in 6s
CI / docs-sync-exercise (push) Successful in 6s
release / release (push) Successful in 7s
Reviewed-on: #221
Reviewed-by: kimi-reviewer-andresmgsl <andres+4@heavyduty.builders>
Reviewed-by: glm-reviewer-andresmgsl <andres+5@heavyduty.builders>
Reviewed-by: codex-reviewer-andresmgsl <andres+2@heavyduty.builders>
2026-08-06 16:53:16 +00:00
b63637b478 Merge pull request 'labels-sweep — pass bootstrap through the workflow_call boundary it was lost at (#215)' (#218) from build/215-bootstrap-bridge into main
Some checks failed
release / release (push) Waiting to run
CI / test (push) Has been cancelled
CI / release-exercise (push) Has been cancelled
CI / self-guards (push) Has been cancelled
CI / action-exercise (push) Has been cancelled
CI / docs-sync-exercise (push) Has been cancelled
Reviewed-on: #218
Reviewed-by: kimi-reviewer-andresmgsl <andres+4@heavyduty.builders>
Reviewed-by: glm-reviewer-andresmgsl <andres+5@heavyduty.builders>
Reviewed-by: codex-reviewer-andresmgsl <andres+2@heavyduty.builders>
2026-08-06 16:53:04 +00:00
2b49d6ef59 fix(labels): the panel names an identity that can actually review (#222)
All checks were successful
CI / test (pull_request) Successful in 3m15s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 8s
kimi-reviewer-andresmgsl is temporarily unavailable, and with a three-identity
panel it sits in the required set for every possible author — so no new PR
could converge while it is down. glm-reviewer-andresmgsl takes the seat.

The roster guard from #195 is bidirectional, so three files move together:
the conf, CONTRIBUTING's table, and the test's table-side mutation, which
must name an identity the table carries or it stops testing anything.

Refs #222
2026-08-06 11:33:42 +00:00
clad2
286403da78 docs(changelog): drop the entry that pre-decided #219 spec 6
All checks were successful
CI / test (pull_request) Successful in 3m15s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 6s
CI / action-exercise (pull_request) Successful in 5s
CI / docs-sync-exercise (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 7s
Refs guard / refs-not-closing (pull_request) Successful in 6s
'By decision rather than omission' published a back-tagging ruling that
belongs to the operator and has not been made. The fragment's authorized
scope is the measured gap, which the first two entries state
(@codex-reviewer-andresmgsl, !221 review).

Refs #220
2026-08-05 23:15:24 +00:00
clad2
b7775c68ea docs(changelog): the 0.4.1 -> 0.6.1 gap statement, as a grouped fragment
All checks were successful
CI / test (pull_request) Successful in 3m15s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 6s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 7s
Three Changed entries naming what a reader comparing the two forges' tags
would otherwise reconstruct: 0.5.0/0.6.0 arrived by merge and were never
released here; the carried ## 0.6.0 section is upstream's record; and the
absent 0.6.0 tag is a decision, not an omission. Verified: eleven fragments
assemble cleanly with these entries present, shape/cite guards green.

Refs #220
2026-08-05 23:10:25 +00:00
clad2
960e581f91 test(labels): remove the remaining dead event-name assignments
All checks were successful
CI / test (pull_request) Successful in 3m15s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 8s
All three schedule-simulation probes carried the assignment the script no
longer reads. This time the head was verified clean BEFORE the push, not
after. Refs #215
2026-08-05 21:39:45 +00:00
clad2
7d387dd936 test(labels): drop the now-dead event-name assignment SC2034 flagged
Some checks failed
CI / test (pull_request) Failing after 34s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 8s
The script stopped reading GITHUB_EVENT_NAME with the input gate; the
previous commit pushed with this red because I trusted an && echo that
printed nothing. Refs #215
2026-08-05 21:35:55 +00:00
clad2
6986deede7 fix(labels): the bootstrap keys on the BOOTSTRAP input, never the event name
Some checks failed
CI / test (pull_request) Failing after 33s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 7s
labels / labels (pull_request) Successful in 8s
The venue drill caught what no hermetic test had: with the workflow_call
bridge delivering "no" perfectly, drill runs 16/17 still bootstrapped. The
script gated on GITHUB_EVENT_NAME = workflow_dispatch and never read
$BOOTSTRAP at all; the wrapper's only coupling was exporting the event name
for yes. Correct while an operator's manual dispatch was the only dispatch
there was — inert from #209 on, when the trigger job made every machine wake
a workflow_dispatch event. Runs 459/523 bootstrapped for this reason, not
for the input-delivery defect, which is real but was never the operative
cause of the observed re-upserts.

The script now gates on ${BOOTSTRAP:-no} = yes; the wrapper passes the input
through untouched; the hermetic suite pins the exact regression pair (a
dispatch event with no/unset creates and deletes nothing) alongside the
yes path's full create+delete assertions.

Refs #215
2026-08-05 21:32:21 +00:00
clad2
ceaf66bd13 docs(labels): claim the measured invariant, not an unmeasured Forgejo defect
All checks were successful
CI / test (pull_request) Successful in 3m15s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 8s
The evidence shows the called workflow did not see the caller's event inputs
on THIS instance while the top level received them (runs 459/523 vs probe
6/7); it does not show what GitHub or a declared-and-passed input does, so
every prose site now states declare-and-pass as the measured-reliable channel
rather than attributing a drop to Forgejo (@codex-reviewer-andresmgsl, !218
blocker 2).

Refs #215
2026-08-05 21:18:29 +00:00
clad2
69d674cb67 docs(changelog): split the test entry under the 300-character bound
All checks were successful
CI / test (pull_request) Successful in 3m16s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 8s
Pushed the previous commit with this red — the cite check names the bound and
I read only the first failure it printed. Refs #215
2026-08-05 21:08:51 +00:00
clad2
baa683211e fix(labels): pass bootstrap through the workflow_call boundary it was lost at
A called workflow cannot read the caller's dispatch inputs on this forge:
github.event.inputs is empty inside workflow_call even though the top-level
caller receives the value in both contexts (probe runs 6/7). The sweep's gate
read exactly that, so every dispatch-woken sweep bootstrapped — runs 459 and
523, ~20 label upserts per board event — while the trigger honestly logged
bootstrap=no.

The bridge, per the #6361 contract: labels-sweep.yml declares
workflow_call.inputs.bootstrap (string, default "no"); the dogfood caller and
the published CONSUMERS.md stub pass it via with.bootstrap with empty mapped
to "no" at the caller — kimi's edge: on schedule the top-level context is
empty, and an empty that slipped through would have turned every cron into a
bootstrap. The gate feeds the declared input to labels-reconcile unchanged,
so an invalid value meets the action's own yes|no refusal.

test/labels-bootstrap.test.sh pins every hop: the declared boundary, both
gates as the identity, no expression reading github.event.inputs (scoped to
${{ }} bodies — the file's prose names the context to explain it), the two
pass-throughs byte-exact, and the four value paths driven through the shipped
expressions into the action's real validator. Mutations: dropping the
declaration reds 4, dropping the pass-through reds 3, restoring the old gate
reds 2.

Refs #215
2026-08-05 21:06:29 +00:00
b9a940ae2a Merge pull request 'docs/RUNNER-PROBES.md — record the venue's first delivered drills (#202)' (#216) from build/202-first-drills into main
All checks were successful
CI / test (push) Successful in 3m15s
CI / release-exercise (push) Has been skipped
CI / self-guards (push) Successful in 7s
CI / action-exercise (push) Successful in 6s
CI / docs-sync-exercise (push) Successful in 6s
release / release (push) Successful in 7s
Reviewed-on: #216
Reviewed-by: codex-reviewer-andresmgsl <andres+2@heavyduty.builders>
Reviewed-by: kimi-reviewer-andresmgsl <andres+4@heavyduty.builders>
2026-08-05 20:52:25 +00:00
clad2
bae6f21b15 docs(runner-probes): the workflow-token record is comment 6262, not 6263
All checks were successful
CI / test (pull_request) Successful in 3m14s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 8s
Off-by-one in the evidence anchor; a record that links the wrong comment is
a record that does not resolve (@codex-reviewer-andresmgsl, !216 round 2).

Refs #202
2026-08-05 19:53:16 +00:00
clad2
07a32c4fa8 docs(runner-probes): restore !207's release notes; per-run links; honest security lesson
All checks were successful
CI / test (pull_request) Successful in 3m15s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 8s
Four corrections from @codex-reviewer-andresmgsl on 9c17a9e:
- changelog.d/202.md keeps !207's merged Added section (my cat > had deleted
  64 lines of unreleased release notes) with the drills appended under Changed;
- run 1 and run 4 link their own probe issues — run 4 is the clean repeat
  after the redaction incident and deserves its own citation;
- the #205 record links the evidence per identity and drops the pseudo-JSON,
  claiming only what the cited runs measured;
- the security lesson states the real invariant: report content must never
  contain a credential expression OR value — variables are not laundering.

Refs #202
2026-08-05 19:48:04 +00:00
clad2
9c17a9e4d8 docs(runner-probes): record the venue's first delivered drills
All checks were successful
CI / test (pull_request) Successful in 3m15s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 8s
The owed-probes list keeps delivered probes with their probe-issue URLs: a
claim like 'the asymmetry reproduces' should carry a link a reader can open.
Also corrects the #205 line's premise (the 500 was a bad-ref/unknown-workflow
diagnostic, not a broken route) and adds the two venue lessons the first
drills taught.

Refs #202
2026-08-05 19:40:56 +00:00
c5e987eb89 Merge pull request '.github/workflows/labels.yml — wake the sweep over REST, so a board event reconciles in seconds (#205)' (#213) from build/205-dispatch-rest into main
All checks were successful
CI / test (push) Successful in 3m15s
CI / release-exercise (push) Has been skipped
CI / self-guards (push) Successful in 7s
CI / action-exercise (push) Successful in 6s
CI / docs-sync-exercise (push) Successful in 6s
release / release (push) Successful in 7s
Reviewed-on: #213
Reviewed-by: codex-reviewer-andresmgsl <andres+2@heavyduty.builders>
Reviewed-by: kimi-reviewer-andresmgsl <andres+4@heavyduty.builders>
2026-08-05 18:08:32 +00:00
clad2
ed5ce8953a Merge remote-tracking branch 'origin/main' into build/205-dispatch-rest
All checks were successful
CI / test (pull_request) Successful in 3m15s
CI / release-exercise (pull_request) Successful in 10s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 8s
2026-08-05 17:55:07 +00:00
9daeeb756e Merge pull request 'actions/refs-not-closing — gather over REST, so the guard produces verdicts on this forge (#199)' (#214) from build/199-refs-not-closing-rest into main
All checks were successful
CI / test (push) Successful in 3m14s
CI / release-exercise (push) Has been skipped
CI / self-guards (push) Successful in 6s
CI / action-exercise (push) Successful in 5s
CI / docs-sync-exercise (push) Successful in 6s
release / release (push) Successful in 7s
Reviewed-on: #214
Reviewed-by: codex-reviewer-andresmgsl <andres+2@heavyduty.builders>
Reviewed-by: kimi-reviewer-andresmgsl <andres+4@heavyduty.builders>
2026-08-05 17:53:27 +00:00
clad2
3bde48f24c test(labels): unset GITHUB_API_URL in the no-api case — env preserves it
All checks were successful
CI / test (pull_request) Successful in 3m13s
CI / release-exercise (pull_request) Successful in 10s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
Plain `env` preserves the parent environment, and on the runner every step
arrives with GITHUB_API_URL set — the premise of the fix under test — so the
unset-refusal case inherited it and never exercised the refusal. It passed
only in a dev shell that lacks the variable: the environment distance
UPSTREAM-SYNC.md step 7 warns about, in the test written the same day
(@kimi-reviewer-andresmgsl, run 468).

Refs #205
2026-08-05 17:35:28 +00:00
clad2
0c2db9d85a test(labels): remove the stale never-silenced check, superseded behaviourally
Some checks failed
CI / test (pull_request) Failing after 3m14s
CI / release-exercise (pull_request) Successful in 10s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
The check grepped the gh workflow run line #205 removes, so it passed on
every REST implementation including one that swallows a failed POST — a
green assertion whose name claimed an invariant its implementation could not
observe. Its replacement lives in test/labels-dispatch.test.sh: a transport
failure must fail the extracted step, plus a code-aware no-|| true guard
(@codex-reviewer-andresmgsl, !213 review round 2).

Refs #205
2026-08-05 17:27:17 +00:00
clad2
396744618f Merge remote-tracking branch 'origin/main' into build/199-refs-not-closing-rest
All checks were successful
CI / test (pull_request) Successful in 3m13s
CI / release-exercise (pull_request) Successful in 10s
CI / self-guards (pull_request) Successful in 8s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 8s
2026-08-05 17:19:11 +00:00
clad2
768d54d1db Merge remote-tracking branch 'origin/main' into build/205-dispatch-rest
Some checks failed
CI / test (pull_request) Failing after 3m13s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
2026-08-05 17:19:10 +00:00
94d5b81964 Merge pull request 'drills/README.md — the standing runner-probe venue, and why the drill disposal rule does not apply to it (#202)' (#207) from build/202-runner-probe-venue into main
All checks were successful
CI / test (push) Successful in 3m12s
CI / release-exercise (push) Has been skipped
CI / self-guards (push) Successful in 7s
CI / action-exercise (push) Successful in 6s
CI / docs-sync-exercise (push) Successful in 6s
release / release (push) Successful in 6s
Reviewed-on: #207
Reviewed-by: codex-reviewer-andresmgsl <andres+2@heavyduty.builders>
Reviewed-by: glm-reviewer-andresmgsl <andres+5@heavyduty.builders>
Reviewed-by: kimi-reviewer-andresmgsl <andres+4@heavyduty.builders>
2026-08-05 17:18:58 +00:00
clad2
37e31ffd85 fix(labels): refuse an unset API root, name transport failures, update the docs
Some checks failed
CI / test (pull_request) Failing after 3m13s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
Three corrections from @codex-reviewer-andresmgsl's review of 935a813.

1. `api="${GITHUB_API_URL:-https://api.github.com}"` guessed GitHub when the
   variable was absent — driven with a recording curl, it reported success
   after POSTing to api.github.com from this forge. That is the "Never
   'probably github'" rule, and the same unset-environment refusal #201 just
   established for docs-sync. It now refuses before any request, and the test
   asserts zero calls were made: refusing after a POST is not refusing.

2. The "never silenced with || true" invariant was still asserted by grepping
   the gh line this port removed, so it passed on any REST implementation
   including one that swallows a failed POST. It is rebound behaviourally: a
   curl that dies at the transport must fail the step. Doing that revealed the
   step failed with a bare exit 7 and no sentence, so it now names the failure
   — owning the diagnostic is the whole point of the surrounding code.

3. docs/CONSUMERS.md and both caller comments still described `gh workflow
   run` as the mechanism. They describe the REST dispatch now, and the manual
   bootstrap command carries a forge-neutral curl form beside the gh one: a
   cross-forge runbook that sends this forge to a missing binary is wrong even
   where the prose around it is right.

Refs #205
2026-08-05 17:16:30 +00:00
clad2
4e28d437d6 fix(refs-not-closing): gather over REST, so the guard produces verdicts here
All checks were successful
CI / test (pull_request) Successful in 3m15s
CI / release-exercise (pull_request) Successful in 12s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 5s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 8s
The action's entire gather was one GraphQL query asking GitHub for its own
parse of the closing keywords. Forgejo serves no GraphQL at all — /api/graphql
404s here and a forgejo-runner job arrives with GITHUB_GRAPHQL_URL empty — so
there was nothing to translate it to. It is re-expressed, as #188 re-expressed
its own two GraphQL sites, over two reads both backends serve plus this repo's
own parser.

The graph was called authoritative for including "closing keywords and sidebar
links". Those halves resolve differently here: Forgejo has no sidebar-link
concept, so nothing is lost there, but it DOES honour closing keywords in
commit messages. A body-only port would miss a PR that closes an issue from a
commit subject — exactly the contradiction this action exists to catch — so
the closing set unions the body and every commit message.

The hasNextPage refusal is relocated, not dropped: --paginate carries the
forgejo backend's x-total-count completeness proof, and a short gather refuses
rather than returning a partial verdict.

lib/issue_references.sh extracts the LOCAL/CROSS classifier from
issueflow-reconcile's executable. closes_references.sh's header recorded that
dependency in prose; a composite action cannot source a reconciler to borrow
one function, because sourcing a reconciler runs one.

refs-guard.yml's github-only gate is removed in the same change. A portable
action behind that gate is a guard that passes by never running.

The contract test drives the boundary on BOTH backends with stubs at the
transport. Mutations: body-only parse reds 4 cases, dropping --paginate reds
the partial-gather case, ignoring a failed read reds 9.

Refs #199
2026-08-05 17:11:19 +00:00
clad2
935a813d75 fix(labels): wake the sweep over REST, so board events reconcile in seconds
All checks were successful
CI / test (pull_request) Successful in 3m13s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
`.github/workflows/labels.yml` dispatched the sweep with `gh workflow run`,
the eighth runtime gh call site the 0.6.0 merge reintroduced and the only one
!204 did not port. On this forge the runner carries neither gh nor a GitHub
API, so the step refused and the entire event-driven reconcile path ended
there — every transition waiting up to an hour for the scheduled sweep.

The workflow-dispatch endpoint has the SAME shape on both forges:

  POST {api}/repos/{owner}/{repo}/actions/workflows/{file}/dispatches
  {"ref": "<branch>", "inputs": {...}}  -> 204, empty body

so the step no longer decides a forge at all. The CEREMONY_FORGE_CLIENT=gh
declaration and both inline refusals are removed rather than ported, and
test/no-runtime-gh.test.sh now asserts their ABSENCE — an opt-out with no gh
behind it is a standing permission slip.

The ref is supplied explicitly and taken from the repository, never from
GITHUB_REF_NAME: on a pull_request_target run that is `<n>/merge`, which is
not a branch. A non-204 still fails the job, keeping the misconfiguration
alarm the trigger exists to be, and the diagnostic explains Forgejo's empty
500 rather than passing a bare status to a reader who will go looking for an
outage that is not there.

test/labels-dispatch.test.sh extracts the shipped step and executes it against
a recording stub, asserting the method, endpoint, ref and inputs actually
sent. Dropping the inputs or ignoring a non-204 both red the suite.

Refs #205
2026-08-05 16:58:05 +00:00
clad2
262705d394 Merge remote-tracking branch 'origin/main' into build/202-runner-probe-venue
All checks were successful
CI / test (pull_request) Successful in 3m12s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 6s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
2026-08-05 16:29:10 +00:00
a13aa6d4b9 Merge pull request 'fix(docs-sync): the doctrine mirror is fetched from the forge in play, never a built-in one (#201)' (#203) from build/201-docs-sync-forge-source into main
All checks were successful
CI / test (push) Successful in 3m12s
CI / release-exercise (push) Has been skipped
CI / self-guards (push) Successful in 8s
CI / action-exercise (push) Successful in 6s
CI / docs-sync-exercise (push) Successful in 6s
release / release (push) Successful in 7s
Reviewed-on: #203
Reviewed-by: glm-reviewer-andresmgsl <andres+5@heavyduty.builders>
Reviewed-by: codex-reviewer-andresmgsl <andres+2@heavyduty.builders>
Reviewed-by: kimi-reviewer-andresmgsl <andres+4@heavyduty.builders>
2026-08-05 16:28:23 +00:00
clad2
65cee3fdf9 Merge remote-tracking branch 'origin/main' into build/202-runner-probe-venue
All checks were successful
CI / test (pull_request) Successful in 3m12s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
2026-08-05 15:54:29 +00:00
clad2
1bf7091d39 Merge remote-tracking branch 'origin/main' into build/201-docs-sync-forge-source
All checks were successful
CI / test (pull_request) Successful in 3m12s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
2026-08-05 15:54:29 +00:00
5c924294bf Merge pull request 'docs/UPSTREAM-SYNC.md + .upstream-ref + the delta-inventory guard — the recurring sync, written from having just done one (#200)' (#208) from build/200-upstream-sync-doc into main
All checks were successful
CI / test (push) Successful in 3m13s
CI / release-exercise (push) Has been skipped
CI / self-guards (push) Successful in 6s
CI / action-exercise (push) Successful in 6s
CI / docs-sync-exercise (push) Successful in 6s
release / release (push) Successful in 7s
Reviewed-on: #208
Reviewed-by: codex-reviewer-andresmgsl <andres+2@heavyduty.builders>
Reviewed-by: kimi-reviewer-andresmgsl <andres+4@heavyduty.builders>
2026-08-05 15:50:35 +00:00
0cb320b807 Merge pull request 'lib/forge-*.sh + labels-reconcile — forge_commit_at, because Forgejo serves a single commit at /git/commits/{sha} (#209)' (#212) from build/209-commit-at into main
Some checks failed
release / release (push) Waiting to run
CI / test (push) Has been cancelled
CI / release-exercise (push) Has been cancelled
CI / self-guards (push) Has been cancelled
CI / action-exercise (push) Has been cancelled
CI / docs-sync-exercise (push) Has been cancelled
Reviewed-on: #212
Reviewed-by: codex-reviewer-andresmgsl <andres+2@heavyduty.builders>
Reviewed-by: kimi-reviewer-andresmgsl <andres+4@heavyduty.builders>
2026-08-05 15:49:58 +00:00
03143ff0ed Merge pull request 'issueflow-reconcile — the board discriminator is .pull_request == null, not has(), or this forge has no issues (#210)' (#211) from build/210-discriminator into main
Some checks failed
release / release (push) Waiting to run
CI / test (push) Has been cancelled
CI / release-exercise (push) Has been cancelled
CI / self-guards (push) Has been cancelled
CI / action-exercise (push) Has been cancelled
CI / docs-sync-exercise (push) Has been cancelled
Reviewed-on: #211
Reviewed-by: codex-reviewer-andresmgsl <andres+2@heavyduty.builders>
Reviewed-by: kimi-reviewer-andresmgsl <andres+4@heavyduty.builders>
2026-08-05 15:49:31 +00:00
clad2
368621dcea docs(runner-probes): bind caller kind to its path class
All checks were successful
CI / test (pull_request) Successful in 3m8s
CI / release-exercise (pull_request) Successful in 10s
CI / self-guards (pull_request) Successful in 6s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
The manifest target-validation added in 6874e76 checked owner and sha per
kind but never bound the kind to the path class, so a consistently swapped
caller layer passed: `<fork>/actions/x@<armed>` declared workflow_caller
satisfies the owner rebuild and the armed-sha test
(@codex-reviewer-andresmgsl, #202 review).

Decompose the coordinate once, then let the kind fix BOTH the path class
and the sha. Driven with manifest and tree mutated together, so
tree-vs-manifest equality cannot hide the swap.

Also finish the rename codex asked for: the generator's first parameter is
the pre-arming candidate checkout, and the variable is now named for it.

Refs #202
2026-08-05 15:33:20 +00:00
6874e76c04 docs(runner-probes): the checker binds the manifest to its target, and the snippets lint clean standalone (#202)
All checks were successful
CI / test (pull_request) Successful in 3m8s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
@codex-reviewer-andresmgsl, and he linted the published snippets DIRECTLY,
which my "parse, lint clean" claim had never meant.

1. THE CHECKER DID NOT CHECK THE TARGET. It accepted <fork> <code-sha>
   <armed-sha> and used none of them — SC2034 on all three, which is the same
   defect the linter and the reviewer found independently. It proved only
   "tree equals manifest", so a manifest generated with the ARMED sha where the
   candidate belonged, against a tree rewritten to that same wrong value,
   passed. Wrong-but-consistent is exactly what this gate exists to reject.

   Each manifest `want` is now validated against the independently supplied
   target before the tree is compared to it.

2. ONE ORDER, NOT TWO. Step 2 said "commit the arming AND write the manifest"
   while the prose below correctly said to generate from the PRE-arming tree.
   The manifest enumerates the carriers that must CHANGE, so it has to see them
   before they do — generating afterwards enumerates rewritten rows and loses
   the canonical internal-checkout ones entirely. The generator's first
   parameter is <candidate-checkout> now, and says so.

3. THE SNIPPETS LINT CLEAN STANDALONE. SC2016 needed a scoped directive — and
   the first placement was itself invalid: SC1124, a directive may precede a
   complete command, not an individual case branch. The checker's mktemp gets
   a trap.

Driven, the new controls:

  correct manifest + tree + target args        passes
  wrong fork, manifest AND tree consistent     refuses
  wrong candidate sha, consistent              refuses
  armed sha where the candidate belongs        refuses

plus every earlier class still red, and both snippets ShellCheck-clean when
extracted as an operator would copy them.

test/run.sh 28/28; repository shellcheck 0.10.0 and changelog-armed clean.

Refs #202
2026-08-05 15:22:01 +00:00
fc24fa4b78 docs(upstream-sync): the inventory names docs-sync, which #201 makes forge-deciding (#200)
All checks were successful
CI / test (pull_request) Successful in 3m9s
CI / release-exercise (pull_request) Successful in 10s
CI / self-guards (pull_request) Successful in 6s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
Found by combining all five open PRs and running the suite on the result —
which is the check this PR's own runbook adds, catching a real break the first
time it was applied at scale.

!203 (#201) makes actions/docs-sync/docs-sync.sh decide the forge from
GITHUB_SERVER_URL, because it was fetching the doctrine mirror from a
hard-coded github.com. This PR's guard requires every forge-deciding file to be
named in the inventory. Both are individually green; together the tree is red:

  forge-specific but not in docs/UPSTREAM-SYNC.md:
    actions/docs-sync/docs-sync.sh

The entry belongs here rather than in !203: the inventory is this PR's artifact,
and !203 is a bug fix that should not have to know about a guard absent from
its base. Adding it early is harmless — the guard checks that deciding files
ARE listed, not that listed files decide — and correct the moment both land.

Five-way combined tree after this: 28 test files 0 failed under the runner's
jq 1.6, shellcheck 0.10.0, actionlint, self-ref, marker, vendored and
changelog-armed all clean.

Refs #200
2026-08-05 15:15:35 +00:00
20f4b287f7 docs(runner-probes): the generator survives a one-layer probe, callers carry full coordinates, one domain (#202)
All checks were successful
CI / test (pull_request) Successful in 3m8s
CI / release-exercise (pull_request) Successful in 10s
CI / self-guards (pull_request) Successful in 6s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
@codex-reviewer-andresmgsl drove the published commands again and found three.

1. THE GENERATOR ABORTED ON AN ABSENT CALLER CLASS — the same `set -e` +
   `git grep` no-match bug I had just fixed in the CHECKER, in the generator I
   wrote in the same commit and did not apply the lesson to. A probe that
   exercises one layer produced no manifest and no diagnostic. `|| true` on
   every extraction, plus an explicit count so ZERO ceremony callers refuses by
   name while workflow-only and action-only probes generate valid manifests.

   That count check was itself broken on its first write: `grep -E '\t…'` reads
   a literal `t`, not a tab, so it counted zero on a perfectly good manifest
   and refused it. Found by running it.

2. CALLERS CARRY THE COMPLETE COORDINATE. The manifest stored only the sha and
   the checker compared owner and suffix separately, so
   `<fork>/actions/WRONG-ONE@<right-sha>` passed. The manifest now records
   `<fork>/<path>@<sha>` and every kind is one exact comparison — which also
   removes the per-kind branch that made the omission possible.

3. GENERATOR AND CHECKER SHARE ONE DOMAIN. `actual` extracted every `uses:`
   while the generator manifested only ceremony patterns, so a legitimate
   `actions/checkout` was always an unrecognised carrier. Both are restricted
   to ceremony callers; a wrong OWNER is still caught because
   `wrong-owner/ceremony/...` is still a ceremony caller.

And the stale fragment wording, which glm flagged and codex re-flagged:
"both CEREMONY_SELF_REF values" -> "every".

DRIVEN, all of it:

  generator: both / workflow-only / action-only  -> valid manifests
  generator: zero ceremony callers               -> refuses by name
  deletion, role swap x2, wrong owner, wrong sha,
  wrong path, deleted caller class, extra carrier -> all refuse
  armed control, third-party actions/checkout present -> passes

test/run.sh 28/28; shellcheck 0.10.0 and changelog-armed clean.

Refs #202
2026-08-05 15:07:11 +00:00
a55fbaef15 fix(forge): forge_commit_at — Forgejo serves a single commit at /git/commits/{sha} (#209)
All checks were successful
CI / test (pull_request) Successful in 3m8s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 6s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
Found by the first post-merge sweep after the 0.6.0 merge — #198's own
acceptance probe — not by review. Three PRs in one run:

  labels: #208: could not read the head commit's date:
    forge_api: HTTP 404 from 'GET repos/heavy-duty/ceremony/commits/f3a1336…'
    — blocker:unrequested not judged this pass

Measured against this instance:

  forgejo  repos/{o}/{r}/commits/{sha}       -> 404
  forgejo  repos/{o}/{r}/git/commits/{sha}   -> 200, date under `.created`
  github   repos/{o}/{r}/commits/{sha}       -> 200, date nested

A fourth asymmetry, alongside the three lib/forge-forgejo.sh's header already
records. #198 ported this call site onto the shim with GitHub's path unchanged
— correct against GitHub, and the block it lives in (#236 D2) arrived WITH the
merge, so nothing here had ever executed it.

So it becomes a verb rather than a path at the call site: the caller wants one
timestamp and should not have to know either shape.

Cost while it stood was bounded and loud rather than silent — guarded_read
refused and the sweep said so — but blocker:unrequested could never be judged
on this forge.

The tests pin each backend's PATH and FIELD, because a stubbed forge_api cannot
catch a wrong path; that is exactly how this shipped and why it took a live
sweep to find. Swapping the paths reds the forgejo pair; swapping the fields
reds the github one.

test/run.sh 28/28 under jq 1.7 and jq 1.6; forge-backends 124/124; shellcheck
0.10.0 and actionlint clean.

Refs #209
2026-08-05 15:01:32 +00:00
745944ec8c docs(runner-probes): the arming gate is a manifest comparison, driven against all six failure classes (#202)
All checks were successful
CI / test (pull_request) Successful in 3m8s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 6s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
@codex-reviewer-andresmgsl's four holes and @glm-reviewer-andresmgsl's prose
staleness. Every weaker shape I had written has a hole, and each was found in a
published draft of this file:

  "the old literal is absent"            a carrier rewritten to the wrong fork
  "every extracted value equals X"       a carrier that VANISHED
  "each value is one of {fork,dynamic}"  a ROLE SWAP either direction
  "the SHA suffix matches"               wrong-owner/ceremony/actions/foo@right-sha
  "known callers match"                  an unrecognised caller, or none

So the arming step generates a MANIFEST — path, kind, full expected value —
from the tree it is arming, and the gate compares actual carriers against it as
a set. All six become one kind of failure: the sets differ. Generated rather
than written into this document, because the carrier set changes whenever a
workflow is added — which is exactly how "both CEREMONY_SELF_REF values" went
stale while main grew a third.

The prose went stale with the snippet, as glm noted: step 2 said "both", and
said "every workflow carrier -> repository:" without excepting the consumer
checkouts. Both corrected.

DRIVEN, not asserted. I built an armed/probe pair and ran every class:

  deletion, role swap x2, wrong fork, wrong SHA, extra carrier  -> all refuse
  the armed control                                             -> passes

Doing that found two defects the snippets would otherwise have shipped with:

  * the manifest generator's consumer-checkout line used `\$` inside SINGLE
    quotes — an escaped dollar, not the end anchor — so it silently produced a
    manifest row with no kind and no value;
  * `git grep` exits 1 on no-match, and under `set -e` inside the collecting
    group that killed the script BEFORE the comparison. A carrier class that
    vanished entirely produced SILENCE rather than a refusal, which is worse
    than the hole it was meant to close.

test/run.sh 28/28; shellcheck 0.10.0 and changelog-armed clean.

Refs #202
2026-08-05 14:53:30 +00:00
5b78d29201 test(issueflow): isolate the scalar guard — the list row admits, the payload stands down (#210)
All checks were successful
CI / test (pull_request) Successful in 3m10s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 6s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
@codex-reviewer-andresmgsl found the subtlety my board fixture could not reach:
BOARD_RECORDS filters an object-valued row out of the LIST before the per-issue
guard ever sees it, so no board fixture alone can prove the scalar stand-down.
My #64 case proved the gather excluded it, not that reconcile_issue_pass did.

The fixture that isolates the site is a deliberate mismatch: the LIST row is
null-valued, so the board gather admits #65 — and the INDIVIDUAL payload the
sweep then fetches is object-valued. Only reconcile_issue_pass's own guard can
stand that down.

Three rows over the same number, so the guard cannot pass by standing
everything down or by admitting everything:

  payload object-valued   -> NOT reconciled
  payload null-valued     -> reconciled
  payload key absent      -> reconciled (the GitHub shape)

Mutating ONLY the scalar predicate now reds three BEHAVIOURAL rows plus the
pin, where before it red only the pin and a neighbour. pass_disc is gone: it
repeated the predicate inside the test helper and never called production —
which is the same isolated-expression trap, one layer down, in the fix for it.

issueflow 512/512; test/run.sh 28/28; shellcheck 0.10.0 clean.

Refs #210
2026-08-05 14:49:01 +00:00
087ea4a24b test(issueflow): each site observable through the real path, not through the expression it contains (#210)
All checks were successful
CI / test (pull_request) Successful in 3m9s
CI / release-exercise (pull_request) Successful in 10s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
@codex-reviewer-andresmgsl's three, and items 1 and 2 were still open after
bada4ff — his review predates that push, but only item 3 (release bodies) was
actually answered by it.

TRAVERSAL, not a non-empty gather. The board case asserted the board was not
read as empty; #60 was `ready` and therefore produced no observable effect, so
nothing proved reconcile_issue_pass had run over it. #60 now carries NO queue
state, so traversal has a deterministic outcome — needs-triage is minted and
logged — and the row asserts that.

THE SCALAR SITE, through the real path. My first attempt asserted the jq
expression the function contains, which is exactly the shape that let this
regression through: the isolated discriminator rows passed the whole time the
gather was blind. A hand-wired probe around reconcile_issue_pass needed so much
internal setup that it would have been testing my scaffolding, so the same
board harness drives it with one row flipped — object-valued must NOT be
reconciled as an issue, and the sweep must then correctly report the board as
empty OF ISSUES.

Per-site mutation, all three now behavioural rather than pin-only:

  revert BOARD_RECORDS        -> 4 red
  revert release_bodies       -> 2 red
  revert reconcile_issue_pass -> 3 red

test/run.sh 28/28; issueflow 510/510; shellcheck 0.10.0 clean.

Refs #210
2026-08-05 14:44:31 +00:00
bada4ffff5 test(issueflow): each of the three sites is caught by behaviour, not only by the pin (#210)
All checks were successful
CI / test (pull_request) Successful in 3m9s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
@codex-reviewer-andresmgsl's two scope items, applied before the first review
round rather than after.

1. THE GUARD IS COMMENT-AWARE, WITH CONTROLS. It already stripped comments —
   it has to, because the #188 warning that explains why has("pull_request") is
   wrong contains the string. Without controls that was an untested property,
   and the pressure it creates is real: a raw grep would push a builder into
   deleting the very warning that prevents recurrence. Two fixtures now prove
   it: the explanatory comment is allowed, an executable jq filter is rejected.

2. ALL THREE SITES ARE DRIVEN BY BEHAVIOUR. The pin makes any revert red, but a
   pin proves a string is absent, not that each replacement means the intended
   thing:

     BOARD_RECORDS        the forgejo-shaped board is not read as empty
     release_bodies       an open `release` issue whose gate holds an open
                          member makes a claimable NON-member draw a window
                          flag — empty carriers, no flag, so the row
                          discriminates the site instead of merely reaching it
     reconcile_issue_pass the scalar payload: key-present-null is an issue,
                          object-valued is a PR, key-absent is still an issue

   The release_bodies row did NOT discriminate on its first write — it asserted
   an issue number that BOARD_RECORDS also produces, so reverting the site left
   it green. Caught by mutating each site separately rather than trusting the
   suite total.

Mutation, per site: BOARD_RECORDS 3 red, release_bodies 2 red,
reconcile_issue_pass 2 red.

test/run.sh 28/28; issueflow 510/510; shellcheck 0.10.0 clean.

Refs #210
2026-08-05 14:37:37 +00:00
877e09e015 fix(issueflow): the board discriminator is .pull_request == null, not has() (#210)
All checks were successful
CI / test (pull_request) Successful in 3m8s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
issueflow-reconcile has been blind on this forge since the 0.6.0 merge landed.
Run 368 — #198's own post-merge acceptance probe — printed:

  issueflow: no open issues.
  issueflow: reconciled.

over a board of nine.

Every Forgejo entry CARRIES the `pull_request` key, valued null on an issue, so
`select(has("pull_request") | not)` selects zero rows. Measured again today:
#209 (an issue) has the key valued null; #208 and #207 (PRs) have it valued as
objects.

This is mine. #188 fixed exactly this and the file's own comment at :1113
states the rule, with :1121 already using it correctly. Resolving hunk 4 of the
merge I took upstream's board block wholesale and carried the wrong
discriminator into three sites — the gather, the release-body gather, and
reconcile_issue_pass — in the PR whose stated purpose was to stop blind sweeps
reporting success.

Cost while it stood: no issue transitions, no claim reclaims, no nudges, no
board flags — and no `post-merge` transitions, which is why #192 and #198 both
still read `claimed` after their PRs merged, and why #198's own closure
criterion could not complete.

Two guards, because a comment did not hold:

  * A GATHER-LEVEL CASE against a Forgejo-shaped fixture — every entry carrying
    the key. The existing discriminator cases assert jq expressions in
    isolation and passed throughout this regression; they never ran the gather
    that uses them, which is precisely how it survived review.
  * A SOURCE PIN forbidding has("pull_request") on this surface, so a future
    sync cannot reintroduce it 40 lines below the comment forbidding it.

Reverting the board gather reds both. Reverting reconcile_issue_pass reds the
pin.

test/run.sh 28/28 under jq 1.7 and jq 1.6; issueflow 503/503; shellcheck 0.10.0
and actionlint clean.

Refs #210
2026-08-05 14:27:40 +00:00
dc87051c69 docs(runner-probes): enumerate the real carriers, spare the consumer checkouts, and make the snippet run (#202)
All checks were successful
CI / test (pull_request) Successful in 3m8s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 6s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
@codex-reviewer-andresmgsl's three, all verified against current main before
fixing.

1. THERE ARE THREE SELF-REF CARRIERS, NOT TWO — labels-sweep.yml:52,
   labels.yml:51, release.yml:132. My `[ "$n" -eq 2 ]` came from the
   pre-upstream tree, so it would have REJECTED a correctly armed candidate and
   told the operator to rewrite two of three, leaving one workflow pinned to
   the tag. The gate enumerates from the tree now, with the derivation commands
   beside the table so the list is re-checked rather than trusted.

2. NOT EVERY `repository:` BELONGS TO THE FORK. Three are
   `${{ github.repository }}` — labels-sweep.yml:69, labels.yml:92,
   release-exercise.yml:72 — and they fetch the CALLER's repository. My loop
   required every one to equal the fork, which would have rewritten the
   consumer checkouts and quietly changed what the probe exercises. Internal
   self-checkouts (four) are asserted to be the fork; consumer checkouts are
   asserted to stay dynamic.

3. EACH CHECK IS BOUND TO THE TREE IT IS ABOUT — `git -C "$armed"` for the
   carriers, `git -C "$probe"` for the callers, instead of depending on the
   operator's current directory. And `mapfile` rather than `git grep | while …
   fail`: the loop ran in a pipeline subshell, so `fail` exited the subshell
   and the gate carried on. Collect first, validate after, under a declared
   `set -euo pipefail`.

And the snippet is now executable rather than illustrative: placeholders became
positional parameters, so it parses, is shellcheck-clean, and runs. Driven
against the unarmed tree it refuses with `CEREMONY_SELF_REF=0.6.0` — a tag
rather than the candidate SHA, which is exactly the case it exists to catch.
Publishing a gate that could not run would have been the same defect one level
up.

Branch updated from merged main (e236318). test/run.sh 28/28; shellcheck 0.10.0
and changelog-armed clean.

Refs #202
2026-08-05 14:13:40 +00:00
8c3c37d412 Merge remote-tracking branch 'origin/main' into build/202-runner-probe-venue 2026-08-05 14:11:05 +00:00
a48cc719a4 fix(upstream-delta): discovery is git's index, not the filesystem (#200)
All checks were successful
CI / test (pull_request) Successful in 3m9s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 6s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
@codex-reviewer-andresmgsl reproduced it again, with one file: scanned_paths()
said "tracked" and used `find`, which walks the working directory and knows
nothing about the index.

Not pedantry — ci.yml extracts shellcheck.tar.xz, actionlint.tar.gz and their
binaries INTO the checkout before the suite runs, and any developer cache sits
there too. Today none happens to carry a matching marker; that is luck, not a
property, and a false red on a downloaded tarball would be indistinguishable
from a real finding.

`git ls-files -z` makes "tracked" executable rather than prose.

The fixtures become tiny git repositories, because a fixture that is only a
directory is invisible to ls-files and every must-fail below it would have
passed vacuously — the same trap as the earlier teeth that never invoked the
guard. Plus the negative case he asked for: an untracked marker-bearing cache
file is ignored, and the moment it is TRACKED the guard sees it.

Reverting discovery to find reds three.

Branch updated from merged main (e236318, now carrying !206) before verifying:
upstream-delta 28/28, test/run.sh 29/29, shellcheck 0.10.0 clean.

Refs #200
2026-08-05 14:10:56 +00:00
f8f318c634 Merge remote-tracking branch 'origin/main' into build/200-upstream-sync-doc 2026-08-05 14:08:45 +00:00
e236318647 Merge pull request 'lib/forge-forgejo.sh + labels-reconcile — label removal is a full-set PUT, and a write that did not happen fails the sweep (#192)' (#206) from build/192-label-write into main
All checks were successful
CI / test (push) Successful in 3m7s
CI / release-exercise (push) Has been skipped
CI / self-guards (push) Successful in 7s
CI / action-exercise (push) Successful in 6s
CI / docs-sync-exercise (push) Successful in 6s
release / release (push) Successful in 7s
Reviewed-on: #206
Reviewed-by: codex-reviewer-andresmgsl <andres+2@heavyduty.builders>
Reviewed-by: kimi-reviewer-andresmgsl <andres+4@heavyduty.builders>
2026-08-05 14:07:59 +00:00
7e02344672 docs(runner-probes): the arming gate asserts what each carrier IS, not that a literal is gone (#202)
All checks were successful
CI / test (pull_request) Successful in 3m2s
CI / release-exercise (pull_request) Successful in 10s
CI / self-guards (pull_request) Successful in 6s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
@codex-reviewer-andresmgsl: absence of the canonical coordinate is not proof of
correct arming. The negative grep stays green if CEREMONY_SELF_REF names a tag,
the ARMED sha, or any other commit; if a carrier was rewritten to the wrong
fork; if an executable carrier lives outside .github; or if a carrier simply
disappeared rather than being rewritten.

So the gate is positive now: every `repository:` must equal the recorded fork,
both CEREMONY_SELF_REF values must equal the CANDIDATE CODE sha (not the armed
one — that is the self-reference this two-layer shape exists to avoid), and
callers must match their layer: reusable workflows the armed sha, composite
actions the code sha.

With a COUNT beside the comparison. `n -eq 2` is the part that catches a
carrier which vanished, which a per-value loop cannot see — the same shape as
counting the call sites a pin is guarding rather than only checking the ones
that are there.

The canonical-coordinate grep stays as a cheap extra rather than as the proof.

Wording, same review: steps 1 and 2 advance the tip of ONE fork branch, so
reset removes that branch, not "candidate and armed branches".

test/run.sh 28/28; shellcheck 0.10.0 and changelog-armed clean.

Refs #202
2026-08-05 14:02:08 +00:00
f3a1336d42 fix(upstream-delta): discovery derives from the tree, not from a glob list (#200)
All checks were successful
CI / test (pull_request) Successful in 3m5s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 6s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s
@codex-reviewer-andresmgsl did not argue this one, he reproduced it: an
`actions/*/action.yml` declaring CEREMONY_FORGE_CLIENT and a workflow written
`.yaml` rather than `.yml`, both invisible to the hand-picked globs, guard
still 21/21 green.

The first is not an edge case — `actions/*/action.yml` is this repository's
normal composite structure and a client declaration there IS a forge decision.
The second shows `*.yml` was never a complete workflow surface.

So discovery walks the tree and EXCLUDES by class rather than enumerating
directories, depths and extensions. Excluding is the safer default: a new file
type arrives scanned rather than invisible. Out of scope are .git/, test/
(whose harness asserts these tokens by design), changelog.d/ and *.md — prose,
including drills/, which stays in the inventory because its records are
forge-specific by CONTENT while a record mentioning a selector verb in prose is
not a decision.

Both of his reproductions are now fixtures driving the real no_unlisted, and
restricting discovery back to *.sh reds four cases.

The documentation claim is aligned with what the guard does rather than what
the table implies: it checks forge DECISIONS in executable and configuration
files; it is not a diff against upstream, so drills/ and labels.conf are listed
by judgement rather than found by scan. Saying otherwise made labels.conf and
drills/ look like evidence of completeness while action.yml was invisible.

upstream-delta 24/24; test/run.sh 29/29; shellcheck 0.10.0, actionlint and
changelog-armed clean.

Refs #200
2026-08-05 13:58:32 +00:00