ceremony/changelog.d/200.md
cluade-reviewer-andresmgsl f3a1336d42 fix(upstream-delta): discovery derives from the tree, not from a glob list (#200)
@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

2.1 KiB

Added

  • docs/UPSTREAM-SYNC.md — the recurring upstream sync as a runbook: the standing resolutions, which side wins each and the issue that decided it (#200).

  • It names the step the 0.6.0 sync nearly shipped without: auditing what the merge brought in that did not conflict. git merge asks no question about a function upstream added to a file this tree owns (#200).

  • It records that the same mechanic applies to state, not just to call sites: a resolved region can silently remove a producer whose consumers auto-merged, and every one of those consumers degrades to empty rather than erroring (#200).

  • It says to verify with the runner's tooling, because "green locally" was wrong three times in one sync — untracked files, a pinned linter, and a pinned jq whose empty-input exit code differs (#200).

  • It says every branch open across a sync is stale afterwards — Forgejo never re-tests an open PR when main moves, so a prior approval is evidence about a tree that no longer exists (#200).

  • It says to audit post-merge runs by executed steps rather than colour, and to inventory what the sync changed about workflow triggers and jobs first (#200).

  • .upstream-ref records the upstream commit this tree carries, in machine-readable form beside the CHANGELOG's prose (#200).

  • test/upstream-delta.test.sh fails the PR that scatters a forge decision into a file the inventory does not name. Discovery is derived from the tree, so a composite action.yml or a .yaml workflow is seen without anyone remembering to add a glob (#200).

  • It refuses when the recorded commit is missing, absent from the object store, or not an ancestor — three distinct refusals, none of them a skip. ci.yml fetches that exact object so the test reads local evidence without CI omitting it (#200).

  • Its mutation cases drive the real check against a constructed tree, so replacing the guard with return 0 reds five of them (#200).

  • docs/CONSUMERS.md states that two ceremonies answer to the same version number, and how a consumer says which one it pinned (#200).