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
1.2 KiB
Fixed
-
The sweep's
bootstrapvalue crosses theworkflow_callboundary as a declared input passed by the caller — a called workflow cannot read the caller's dispatch inputs on this forge (#215). -
Before the bridge,
github.event.inputswas empty inside the called workflow, so every dispatch-woken sweep bootstrapped: ~20 label upserts on each board event (#215). -
The caller maps an empty top-level value to
noexplicitly, so a cron-woken sweep can never bootstrap; the declared input also defaults tono, so a consumer that passes nothing gets the safe path (#215). -
The gate feeds the declared input to
labels-reconcileunchanged, so an invalid value meets the action's ownyes|norefusal instead of being silently coerced (#215). -
docs/CONSUMERS.md's published sweep stub carries the same pass-through — without it every consumer inherits the defect ceremony fixed for itself (#215).
Added
test/labels-bootstrap.test.shpins the bridge at every hop: the declared boundary, both gate sites as the identity, no expression readinggithub.event.inputs, the caller and stub pass-throughs byte-exact, and the four value paths driven through the shipped expressions into the action's real validator (#215).