tests/emitted-drift-acks/
Per-PR acknowledgment fragments for the differential attribution check
(tests/emitted-attribution.test.cjs, ADR-2719 / #2789 / #2914).
This directory being empty is the healthy steady state. A fragment appearing
in a diff is the alarm; a fragment sitting here on next is spent cruft.
README.md is not a fragment — every reader filters on .json — and exists so
the directory (which CONTEXT.md and CONTRIBUTING.md both reference by path)
survives the sweep that empties it.
The lifecycle, in three steps
-
The gate names its own remedy. When an emitted-artifact hash moves, or a
gsd-core/workflows/*.md/agents/gsd-*.mdfile grows, and your diff cannot explain it, the failure output tells you which key to use and prints a minimal valid document to paste. Create a NEW fragment named for your issue or PR (<NNNN>-<slug>.json) — never reuse someone else's, and never revive the legacy singletests/emitted-drift-ack.json. -
Note the two key spaces. The message says which one applies. An unattributable hash ripple is keyed on the emitted path (
skills/gsd-add-tests/SKILL.md); growth is keyed on the bare filename as it appears undergsd-core/workflows/oragents/(explore.md). -
The fragment is deleted once it has merged (#3078). Every entry is scoped to the diff that introduced it, so the moment it lands on
nextits prose is already at the base — it is spent and can no longer clear anything, while still owning its path keys. Theguard-no-ack-on-nextjob redsnextand prints the exactgit rmfor every fully-spent fragment.You no longer have to run that
git rmyourself (#3875). Theack-fragment-sweepworkflow runs every six hours, asks the guard for its own sweep list (--sweep-plan), and opens a PR deleting exactly what the guard named. Deleting the fragment in a follow-up PR by hand still works and is still welcome — it is simply no longer the only thing standing between a merged fragment and a rednext.The sweep is automated because the manual remedy could not keep up. The guard evaluates at MERGE time; a hand-authored
git rmis fixed at BRANCH time, so any ack-carrying PR that merges in between invalidates it. #3823 lost exactly that race to #3809 on its own merge commit and leftnextred for 24 consecutive pushes.
Why the sweep exists
Fragments end the file conflict the single shared document caused. They do not end the key conflict: two ack sources may never name the same path, and that is a hard, loudly-reported error. So a fully-spent fragment left here walls off every path it owns — the next PR to grow one of them can declare it neither in the owning fragment (spent, gates nothing) nor in its own (duplicate). #2914 assumed a persisting fragment was harmless; #3078 measured the cost at 45 fragments owning 403 paths and made the guard sweep them.
A partially spent fragment is deliberately left alone. That asymmetry is what keeps the re-arm route working: appending prose to a live entry re-arms it, and re-arming deliberately costs an actual new sentence — the comparison strips zero-width characters and collapses whitespace precisely so a zero-information edit cannot fake one.
Never pin a fragment in a test
A fragment is deleted the moment it has merged (see step 3 above), so any test
that asserts one exists, or asserts its contents, will fail the instant
guard-no-ack-on-next sweeps it — and that failure has nothing to do with the
behavior the fragment once explained. This has already cost two suites:
tests/emitted-attribution.test.cjs's three #2914 migration pins, and
tests/agent-tracked-source-rule.test.cjs's #3645 growth-ack pin. What a test
may legitimately assert is the BEHAVIOR the ack explains, or the guard's own
verdict (assertNoAllSpentFragments, assertAbsentOnNext) — never the
paperwork.
Do not regenerate anything
There is no baseline file to re-run a generator over; #2724 deleted it. If you find yourself hunting for one, that is the predictable wrong guess.
See CONTRIBUTING.md → "Editing shipped content", docs/TESTING-SUITES.md, and
CONTEXT.md's RULESET.EMITTED_ATTRIBUTION for the full model.