Development record
check-issue-resolution-cases.sh runs its scratch-repo git commands under the inherited environment, so an ambient GIT_DIR or global core.hooksPath corrupts the caller's repo or breaks the gate. The harness builds ~60 'git -C $d' fixtures but omits the hermetic-env scrub its sibling check-attribution-cases.sh carries (iss-28, iss-313, resolved four days before this script was authored): an inherited absolute GIT_DIR overrides -C and redirects the fixture commits and config onto the ambient repository while every case still prints ok (green-while-corrupting, reachable on a direct make preflight under a git alias / rebase -x / bisect run); and an inherited GIT_CONFIG_GLOBAL / core.hooksPath — which this repo's .githooks/pre-push documents the maintainer uses — fires the developer's global pre-commit hook inside the scratch repos and turns make lint-issues red with no gate diagnostic. Fix: hoist the same unset GIT_DIR family + export GIT_CONFIG_GLOBAL=/dev/null scrub before the first git call.
check-issue-resolution-cases.sh runs its scratch-repo git commands under the inherited environment, so an ambient GIT_DIR or global core.hooksPath corrupts the caller's repo or breaks the gate. The harness builds ~60 'git -C $d' fixtures but omits the hermetic-env scrub its sibling check-attribution-cases.sh carries (iss-28, iss-313, resolved four days before this script was authored): an inherited absolute GIT_DIR overrides -C and redirects the fixture commits and config onto the ambient repository while every case still prints ok (green-while-corrupting, reachable on a direct make preflight under a git alias / rebase -x / bisect run); and an inherited GIT_CONFIG_GLOBAL / core.hooksPath — which this repo's .githooks/pre-push documents the maintainer uses — fires the developer's global pre-commit hook inside the scratch repos and turns make lint-issues red with no gate diagnostic. Fix: hoist the same unset GIT_DIR family + export GIT_CONFIG_GLOBAL=/dev/null scrub before the first git call.
Frontmatter
| id | iss-2608261041020040 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | minor |
.abcd/work/issues/resolved/iss-2608261041020040-issue-resolution-cases-not-hermetic-git.md
open on GitHub ↗ · commit history ↗
mentions4
- dcn-26082600000000012026-08-26 — bughunt round 7 (state #368): hardened the new issue-resolution
- iss-28scaffold a hermetic git environment for tests that shell out to git: any test invoking git as a subprocess can read the developer's real ~/.gitconfig (identity, aliases, includeIf, hooks path) — non-determinism and identity leakage into fixtures — or, via one un-scoped call (missing -C/cwd) or corrupted repo state, mutate the ambient repo's config, refs, or history. Per-call temp-repo scoping is defence-in-breadth only; the robust fix also isolates the git environment. Ship a shared hermetic-git helper in the generated test scaffolding, per target language (Go gitIsolatedEnv(), pytest fixture, shell shim), that pins HOME and XDG_CONFIG_HOME to a per-test temp dir, sets GIT_CONFIG_GLOBAL/GIT_CONFIG_SYSTEM to /dev/null (or GIT_CONFIG_NOSYSTEM=1), supplies identity via GIT_AUTHOR_*/GIT_COMMITTER_* env rather than git config, sets GIT_TERMINAL_PROMPT=0 and core.hooksPath=/dev/null, and is always combined with scoping to the temp repo — documented in the scaffolded repo's testing conventions/AGENTS.md so it is the path of least resistance. Optional enforcement: a lint/pre-commit check flagging git subprocesses in test code that bypass the helper, turning convention into guarantee. Exactly the cross-repo hygiene the scaffolder exists to standardise; removes a whole class of flaky-test and repo-pollution bugs.
- iss-313check-attribution-cases.sh scratch repos are not hermetic so an inherited GIT_DIR makes the corpus report green while rewriting the ambient repository history
- iss-2608261312157721main's Unreleased section carries a hand-written entry the derived cut refuses. Commit 3dc7aba3 (round-7 resolutions, PR #511) added a '### Fixed' entry under '## [Unreleased]' on a branch parallel to 5897a85d, the commit that adopted the derived-changelog rule (Unreleased must be EMPTY or the launch ship ingest refuses); neither author could see the other, and the collision only exists on merged main. As written, the next 'launch ship' refuses the cut. The entry's content is already carried by the two resolved records it cites (iss-2608261040378346, iss-2608261041020040), so the remedy is removing the hand-written entry, not relocating it. Structural half: nothing gates Unreleased emptiness at merge time, so any two parallel branches can recreate this class silently until release; a compose-time or CI check on '## [Unreleased]' being empty is the durable fix (same family as iss-220's compose-time-prevention finding). Rounds 8a/8b (#512/#513) were checked and touch no CHANGELOG.