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
This commit is contained in:
clad2 2026-08-05 21:18:29 +00:00
parent 69d674cb67
commit ceaf66bd13
4 changed files with 27 additions and 23 deletions

View file

@ -37,12 +37,13 @@ on:
bootstrap:
description: >-
Bootstrap the label taxonomy before sweeping. The CALLER passes
this through from its own workflow_dispatch input; a called
workflow cannot read the caller's dispatch inputs on every forge
(Forgejo drops them at the workflow_call boundary — ceremony#215,
probe runs 6/7 vs merged runs 459/523), so the value must arrive
through this declared boundary, never via github.event.inputs.
Absent means "no": an event- or cron-woken sweep must never
this through from its own workflow_dispatch input. The measured
invariant (ceremony#215): the value must be DECLARED here and
EXPLICITLY passed — on this instance the called workflow did not
see the caller's event inputs as an implicit substitute (runs
459/523 bootstrapped on a bootstrap=no dispatch) while the
top-level caller received the value in both contexts (probe runs
6/7). Absent means "no": an event- or cron-woken sweep must never
re-upsert ~20 labels.
type: string
required: false
@ -101,14 +102,14 @@ jobs:
#
# bootstrap: read from the DECLARED workflow_call input and nothing
# else. The old gate read `github.event.inputs.bootstrap` from inside
# this called workflow — which Forgejo leaves empty at the
# workflow_call boundary even though the top-level caller receives the
# value in both contexts (ceremony#215; probe runs 6/7 measured the
# boundary, merged runs 459/523 paid for it: every dispatch-woken
# sweep bootstrapped). The caller passes the value through
# `with.bootstrap`; anything not exactly "yes" is fed through for
# labels-reconcile's own yes|no validation to judge, so a typo refuses
# loudly instead of silently bootstrapping.
# this called workflow, and on this instance that context arrived
# empty (runs 459/523: every dispatch-woken sweep bootstrapped on a
# bootstrap=no body) while the top-level caller received the value in
# both contexts (probe runs 6/7) — ceremony#215. The reliable channel
# is declare-and-pass, so that is the only one used. The caller passes
# the value through `with.bootstrap`; anything not exactly yes|no is
# fed through for labels-reconcile's own validation to judge, so a
# typo refuses loudly instead of silently bootstrapping.
- name: reconcile state + stale
if: github.repository != 'heavy-duty/ceremony'
uses: ./.ceremony-src/actions/labels-reconcile

View file

@ -45,9 +45,10 @@ jobs:
# named `labels` (self-labels.yml).
uses: ./.github/workflows/labels-sweep.yml
with:
# The dispatch input crosses the workflow_call boundary HERE, or not at
# all: the called workflow cannot read this caller's dispatch inputs on
# this forge (ceremony#215). On `schedule` the top-level context is
# The dispatch input crosses the workflow_call boundary HERE, or not
# at all: on this instance the called workflow did not see this
# caller's event inputs implicitly (ceremony#215), so declare-and-pass
# is the only channel used. On `schedule` the top-level context is
# empty, and empty maps to "no" EXPLICITLY — a cron that bootstraps is
# the failure kimi named before it could exist.
bootstrap: ${{ inputs.bootstrap || 'no' }}

View file

@ -1,8 +1,9 @@
### Fixed
- The sweep's `bootstrap` value crosses the `workflow_call` boundary as a
declared input passed by the caller — a called workflow cannot read the
caller's dispatch inputs on this forge (#215).
declared input, explicitly passed by the caller — the one channel measured
to work; the called workflow did not see the caller's event inputs as an
implicit substitute on this instance (#215).
- Before the bridge, `github.event.inputs` was empty inside the called
workflow, so every dispatch-woken sweep bootstrapped: ~20 label upserts on

View file

@ -459,10 +459,11 @@ jobs:
sweep:
uses: heavy-duty/ceremony/.github/workflows/labels-sweep.yml@<pinned-tag>
with:
# Pass the dispatch input through the workflow_call boundary — the
# called workflow cannot read this caller's dispatch inputs on every
# forge (ceremony#215). Empty (schedule) maps to "no" explicitly, so a
# cron-woken sweep never re-upserts the taxonomy.
# Pass the dispatch input through the workflow_call boundary
# explicitly — a called workflow must not rely on reading the caller's
# event inputs (ceremony#215 measured that failing). Empty (schedule)
# maps to "no" explicitly, so a cron-woken sweep never re-upserts the
# taxonomy.
bootstrap: ${{ inputs.bootstrap || 'no' }}
# If this repo's PR-facing labels caller is named anything but `labels`,
# pass that name alongside: `pr_workflow_name: <name>`. The sweep exports