Files
msd-core/tests/emitted-drift-acks
Tom Boucher 39673ae9ff fix(#3738): antigravity global skills/agents install to ~/.gemini/config (#3921)
* test(#3738): antigravity global skills/agents must resolve under ~/.gemini/config

Regression tests (RED first): --skills-root and gsd-tools query surfaces,
install-plan dest dirs, and converter skills-path rewrite.

* fix(#3738): antigravity global skills/agents install to ~/.gemini/config

Antigravity's machine-local discovery scans ~/.gemini/config/{skills,agents};
the configHome (~/.gemini/antigravity) is deprecated for artifacts. Declare the
ADR-1239 skills/agents 'home' override on the antigravity global layout — the
same mechanism codex uses (.agents) — and divert ~/.claude/skills/ references
in converted global content to ~/.gemini/config/skills/. configHome, settings,
probe/migration semantics, and the local .agents layout are unchanged.

* fix(#3738): retire deprecated configHome artifacts via installer migration 010

Next install converges an existing antigravity install: manifest-managed
skills/gsd-*/ and agents/gsd-*.md under the configHome (a location AGY does
not scan) are removed — modified files backed up first, unmanifested and
non-gsd entries preserved — and now-empty containers retired. Global scope
only; the local .agents surface is live. Docs + inventory updated.

* fix(#3738): converter sync in bin/install.js, harness emit-root coverage, migration baseline

- bin/install.js converter gains the same ~/.claude/skills → ~/.gemini/config/
  rewrite as src (ADR-1508 dual copy must stay in sync).
- Parity-manifest walk covers home-override emit roots (extraEmitRootsFor) so
  antigravity's emitted skills/agents stay differential-visible at their new
  install root; install-tree fixture regen confirms an unchanged key set.
- skills-from-commands rule declares the antigravity converter as a
  runtime-scoped transform; one ack fragment covers the identity-classed
  workflow whose antigravity copy embeds the old skills path.
- Migration 010 checksum baseline + home-override set doc updated; existing
  tests updated to the #3738 contract (global dest, golden parity via layout
  dest, integration expectations).

* fix(#3738): tolerate an absent extra emit root on baseline-side measurement

The base tree's installer predates the home override, so <HOME>/.gemini/config
does not exist there; walk() threw ENOENT and the in-job baseline build failed.
An absent extra root is the legitimate pre-override shape — skip it.

* fix(#3738): review findings — manifest agents root, bare skills-path rewrite, guard comment

- writeManifest resolves the agents-kind home override (_kindDestDirSafe), so
  the manifest records agents at their actual install root and drift detection
  keeps working (isolated review finding 1, major).
- Converter bare forms ~/.claude/skills and $HOME/.claude/skills (no trailing
  slash) divert to ~/.gemini/config/skills instead of falling through to the
  retired configHome path (finding 2).
- real-home-guard comment updated: antigravity's global agents kind is the
  first agents-kind home override (finding 3, doc-only).
- Regression tests for both behavioral findings.

* chore(#3738): changeset fragment (pr number backfilled after PR creation)

* chore(#3738): backfill changeset PR number (3921)

* fix(#3738): sandbox HOME in tests that install antigravity global artifacts

antigravity is the first home-override runtime in the golden-parity and
skills-wrapper suites (codex is not in their runtime lists), so those tests
never needed HOME sandboxing — the real-home guard now (correctly) refuses
their un-sandboxed global installs on CI, where HOME is the passwd home.

* fix(#3738): stop the K3 sequential-sandbox env leak; sandbox L2's home-override plans

K3's two back-to-back sandboxHome calls leave HOME pointing at the first
sandbox once the after-hooks restore (each call saves the env as it found
it, so the second saves the first's sandbox as 'original'). On the windows
matrix that leaked gsd-k3-qwen-* home into the L2 property, whose
antigravity/global run then (correctly) refused via the #3712 real-home
guard — antigravity is the runtime that made L2's plan escape into
os.homedir(). K3 now manages the env with a single restore; L2 sandboxes
HOME per run, mirroring L1.

* fix(#3738): L2 property's HOME sandbox must exist on disk

The #3712 guard's sandbox exemption fails closed when identify(effectiveHome)
is 'absent' — L2 never created its configDir, so on the windows matrix (tmpdir
under the real home) the antigravity/global run refused even with HOME
sandboxed. Create the per-run sandbox dir and clean it up.

---------

Co-authored-by: sim <sim@local>
2026-08-27 02:24:03 -04:00
..

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

  1. The gate names its own remedy. When an emitted-artifact hash moves, or a gsd-core/workflows/*.md / agents/gsd-*.md file 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 single tests/emitted-drift-ack.json.

  2. 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 under gsd-core/workflows/ or agents/ (explore.md).

  3. 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 next its prose is already at the base — it is spent and can no longer clear anything, while still owning its path keys. The guard-no-ack-on-next job reds next and prints the exact git rm for every fully-spent fragment.

    You no longer have to run that git rm yourself (#3875). The ack-fragment-sweep workflow 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 red next.

    The sweep is automated because the manual remedy could not keep up. The guard evaluates at MERGE time; a hand-authored git rm is 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 left next red 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.