Skip to content

[finding] 25 more throwaway-git-repo builders spawn git with no environment of their own — the #16624 class, enumerated, with three competing spellings of the fix in tree #16644

Description

@claude

Filed unassigned while implementing #16624 (branch claude/issue-16624-git-env-isolation-for-gate-self-tests). Observation-class: no failure has been measured in these 25 files. Triage's ruling on #16624 asked for the population to be enumerated as part of remedy 2 — "Several gates build throwaway repos; this is the one that was caught" — and it turned out to be too large for one PR, so the enumeration lands here with a proposed split. Duplicate-searched: #9068 and #9109 cover an earlier, smaller slice of this class (two packages/spec test harnesses) and are both closed; no open card carries this signature.

The rule, restated

Git exports GIT_DIR / GIT_WORK_TREE / GIT_INDEX_FILE into every child it runs, and those outrank cwd. A script that builds a throwaway repository and spawns git with no env of its own therefore operates on the real repository whenever it runs under a hook or a merge driver. #16624 measured both halves of that on this box: 8,190 paths staged as deleted in the real index, and core.bare = true written into the SHARED .git/config, breaking the primary checkout for every agent — from a self-test that printed ok throughout.

The population, measured

Measured on objectstack-ai/objectstack at 8ae3b8d2cc (the head of the #16624 branch), over every tracked .mjs / .mts / .js / .ts / .sh file that both creates a temp directory and spawns git init against one. 32 files, in three groups:

Blanket strip — 5 (all landed by #16624): scripts/git-env.mjs · scripts/symbol-anchors.mjs · scripts/check-system-context-census.mjs · scripts/check-adr-symbol-anchors.mjs · scripts/check-scripts-symbol-anchors.mjs

Named-key list — 2 (landed by #9068 / #9109, a DIFFERENT spelling): packages/spec/scripts/build-schemas-check-mode.test.ts · packages/spec/scripts/sharded-artifacts.test.ts. Both carry a hand-maintained LEAKED_GIT_ENV array of 7 keys.

No environment at all — 25. Grouped by who invokes them, because that is what decides the risk:

Tier A — invoked BY git, or from a git hook (do these first)

file init sites why it is Tier A
scripts/git-merge-regen.mjs 3 it IS the merge=os-regen driver — git runs it, so GIT_DIR is exported by construction
scripts/check-regen-pending.mjs 1 run by .githooks/pre-commit and .githooks/pre-push — the exact path #16624 travelled

Tier B — repo gates under scripts/** (CI and hand runs)

scripts/ablation-dist-preflight.mjs (1) · scripts/check-adr-0087-registration.mjs (6) · scripts/check-bash32-floor.mjs (1) · scripts/check-changeset-no-major.mjs (1) · scripts/check-empty-changeset.mjs (4) · scripts/check-engine-split-ratio.mjs (1) · scripts/check-nul-bytes.mjs (1) · scripts/check-skill-frame-freshness.mjs (1) · scripts/check-type-check-coverage.mjs (1) · scripts/docs-audit/check-drift-comment.mjs (1) · scripts/objectui-changeset-digest.mjs (9) · scripts/objectui-range.mjs (1) · scripts/collect-release-notes.sh (1)

Tier C — PM tooling, package tests, agent-hook self-tests

scripts/pm/dispatch-gates.mjs (8) · scripts/pm/check-governed-merges.mjs (1) · scripts/pm/git-history.mjs (1) · scripts/pm/release-rehearsal-clone.mjs (1) · scripts/pm/os-regen-merge.sh (2) · scripts/bump-objectui.selftest.sh (2) · .claude/hooks/guard-main-checkout.selftest.sh (1) · .claude/hooks/guard-main-checkout-bash.selftest.sh (2) · packages/spec/scripts/check-skill-examples.ts (1) · packages/create-objectstack/src/scaffold-skills-single-copy.test.ts (1)

The second finding: three spellings

The repo now states one rule three ways — a 7-key LEAKED_GIT_ENV allowlist (#9068), a local blanket strip (the #16624 incident's own fix), and gitFreeEnv() in scripts/git-env.mjs (#16624). The allowlist shape is the one worth retiring: it has to be maintained against git's own list of location variables, and the key it misses is the key that bites. Converging the two packages/spec files onto gitFreeEnv() is mechanical.

One boundary any migration must respect (measured, and it bites)

⛔ A blanket GIT_* strip is correct only for a child that operates on the repository its cwd and arguments name. It is WRONG for a child that talks to a remote: the agent containers this repo runs in carry GIT_CONFIG_COUNT with GIT_CONFIG_KEY_* / GIT_CONFIG_VALUE_* pairs that rewrite GitHub remotes and disable interactive credentials, plus GIT_SSL_CAINFO naming the proxy CA bundle. Strip those and fetch / clone / push in the child loses its transport configuration. That matters for at least scripts/pm/release-rehearsal-clone.mjs and scripts/objectui-*, which do both kinds of work in one file. scripts/git-env.mjs's header carries the rule and the boundary together.

Suggested split

Three cards, in this order: (A) the two hook/driver-invoked files, (B) the 13 scripts/** gates plus the packages/spec convergence, (C) PM tooling, package tests and the shell self-tests. A repo-wide gate that refuses a new inheriting spawn is worth considering once the population is at zero, but ⛔ not before — it would land red and get weakened.

Reproducing the enumeration

The classifier is 20 lines: every tracked source file that matches mkdtempSync|mktemp|tmpdir() and has a non-comment line spawning git init, then split on whether the file mentions a blanket strip (startsWith('GIT_') / gitFreeEnv / env -u GIT_ / unset GIT_) or the LEAKED_GIT_ENV allowlist. The ObjectStack CLI's own init command is excluded by name so packages/cli/test/** does not inflate the count.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions