The Generated maps fresh check in .github/workflows/ci.yml runs only on pull_request. Nothing runs it on push to main.
Consequence, observed rather than hypothesised
On 2026-08-10, PR #1202 added specs/149-canonical-dependency-map/ without regenerating the maps. Main then carried a stale docs/reports/maps-v1-findings.md with no failing run anywhere to report it, because no workflow ever evaluated main.
The cost transferred to the next PRs opened: three unrelated PRs (#1243, #1247, #1248) went red on a check none of them had broken. CI evaluates the PR merged into main, so a branch whose own tree is fine still fails, and -- check passes locally on the exact commit CI fails on. That discrepancy is the only available clue, and it points away from the real cause.
Why this is worth fixing rather than absorbing
It is structurally the same finding as R0 (#1246): a correct mechanism wired to one path and not the one that needed it. The freshness gate exists precisely because "the committed maps went three projects stale and carried 25 corrupted rows with nothing objecting" (its own XML doc). Running it only on pull_request recreates a narrower version of that gap: main can be stale and silent, and the check reports it to the wrong person.
Do
Add the freshness check to a push trigger on main, or a scheduled run, so main going stale is reported against main rather than against whoever opens the next PR.
Consider whether it should block or merely notify. Blocking on push is not possible after the fact, so the useful outcomes are a notification and a fast follow-up, or an auto-regenerating job that opens a PR.
Related trap worth documenting alongside
docs/maps/manifest.json is regenerated by -- all but deliberately excluded from the comparison by MapFreshness (it carries input_fingerprint and git_head, which move with every commit; its own comment calls it "bookkeeping, not a description of the tree").
Committing a regenerated manifest therefore achieves nothing the gate reads, and actively manufactures conflicts: two concurrent PRs that both regenerate maps will conflict on it, because each carries a different git_head. That is exactly what made #1247 go CONFLICTING the moment #1248 squash-merged, while #1243, where the manifest was left untouched, stayed mergeable through both merges.
Worth a line in the contribution guidance: regenerate with -- all, then stage only the changed real map files.
Start at
.github/workflows/ci.yml (the Generated maps fresh job)
tools/maps/Elsa.Maps.Generator/MapFreshness.cs (the exclusion and its rationale)
Done when
Main going stale is reported against main, and the manifest guidance is written down where a contributor will see it.
The
Generated maps freshcheck in.github/workflows/ci.ymlruns only onpull_request. Nothing runs it on push tomain.Consequence, observed rather than hypothesised
On 2026-08-10, PR #1202 added
specs/149-canonical-dependency-map/without regenerating the maps. Main then carried a staledocs/reports/maps-v1-findings.mdwith no failing run anywhere to report it, because no workflow ever evaluated main.The cost transferred to the next PRs opened: three unrelated PRs (#1243, #1247, #1248) went red on a check none of them had broken. CI evaluates the PR merged into main, so a branch whose own tree is fine still fails, and
-- checkpasses locally on the exact commit CI fails on. That discrepancy is the only available clue, and it points away from the real cause.Why this is worth fixing rather than absorbing
It is structurally the same finding as R0 (#1246): a correct mechanism wired to one path and not the one that needed it. The freshness gate exists precisely because "the committed maps went three projects stale and carried 25 corrupted rows with nothing objecting" (its own XML doc). Running it only on
pull_requestrecreates a narrower version of that gap: main can be stale and silent, and the check reports it to the wrong person.Do
Add the freshness check to a
pushtrigger onmain, or a scheduled run, so main going stale is reported against main rather than against whoever opens the next PR.Consider whether it should block or merely notify. Blocking on push is not possible after the fact, so the useful outcomes are a notification and a fast follow-up, or an auto-regenerating job that opens a PR.
Related trap worth documenting alongside
docs/maps/manifest.jsonis regenerated by-- allbut deliberately excluded from the comparison byMapFreshness(it carriesinput_fingerprintandgit_head, which move with every commit; its own comment calls it "bookkeeping, not a description of the tree").Committing a regenerated manifest therefore achieves nothing the gate reads, and actively manufactures conflicts: two concurrent PRs that both regenerate maps will conflict on it, because each carries a different
git_head. That is exactly what made #1247 goCONFLICTINGthe moment #1248 squash-merged, while #1243, where the manifest was left untouched, stayed mergeable through both merges.Worth a line in the contribution guidance: regenerate with
-- all, then stage only the changed real map files.Start at
.github/workflows/ci.yml(theGenerated maps freshjob)tools/maps/Elsa.Maps.Generator/MapFreshness.cs(the exclusion and its rationale)Done when
Main going stale is reported against main, and the manifest guidance is written down where a contributor will see it.