Skip to content

gate.sh's shell/python selftests inherit ambient git config and can wedge every gate run #201

Description

@melihucar

Gate: bootstrap
Agent: codex

What I found

Two gate steps run git init / git commit in throwaway temp repos as part of
their --selftest, and both inherit the invoking user's global git config:

Both authors correctly anticipated that user.email/user.name may be unset
and set them per-repo. Neither neutralises global config that actively breaks
committing. Reproduced on the host, both steps:

printf "[commit]\n\tgpgsign = true\n[gpg]\n\tprogram = /usr/bin/false\n" > /tmp/gc
GIT_CONFIG_GLOBAL=/tmp/gc python3 scripts/check_orchestrator.py --selftest
  -> CalledProcessError: ["git","commit","-q","-m","base"] exit 128
GIT_CONFIG_GLOBAL=/tmp/gc bash scripts/check_test_suite.sh --selftest
  -> SELFTEST FAIL (6 of 27 cases)

A global core.hooksPath pointing at a rejecting pre-commit hook (several dev
tooling installers set this) reproduces the same failure in both.

Why it matters

test-suite-intact and orchestrator-runnable run in every profile,
including bootstrap, in every worktree. If the human who owns this laptop
ever enables commit signing globally — a mundane setting — every gate run fails
at step 1, merge_worktree() never reaches git merge --ff-only, and an
unattended overnight run discards 100% of its work for a reason that has nothing
to do with any agent's code. It fails closed and loudly, so nothing wrong reaches
main; the cost is a wasted run, not a correctness hole. Hence p2, not p0.

The host's config is currently clean (commit.gpgsign, core.hooksPath,
init.templateDir all unset), which is why the gate is green today.

Suggested fix

Neutralise ambient config in both selftests. In check_test_suite.sh, in
run_selftest() — not at file scope, so the real run is unaffected:

export GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null

and pass -c commit.gpgsign=false -c core.hooksPath=/dev/null on the git init
in git_fixture() (or set them via git config right after, alongside the
existing user.email/user.name lines).

Do the same in scripts/check_orchestrator.py's repo-scaffolding helper
(:2763), either via the same two git config calls or by passing
env={**os.environ, "GIT_CONFIG_GLOBAL": os.devnull, "GIT_CONFIG_SYSTEM": os.devnull}
to the subprocess.run wrapper it uses.

Acceptance criterion

Both commands below exit 0:

GIT_CONFIG_GLOBAL=/tmp/gc bash scripts/check_test_suite.sh --selftest
GIT_CONFIG_GLOBAL=/tmp/gc python3 scripts/check_orchestrator.py --selftest

with /tmp/gc containing both the commit.gpgsign = true + gpg.program = /usr/bin/false stanza and a core.hooksPath pointing at a rejecting
pre-commit. Ideally add that as a case inside each selftest so it stays pinned.

Out of scope

  • Changing what either check actually verifies. This is purely about the
    selftest scaffolding being hermetic.
  • The real (non-selftest) derive_min() git reads in check_test_suite.sh.
    Those are read-only (merge-base, grep) and unaffected by signing/hooks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    fr:followupFiled by an agent mid-taskfr:p2Normal -- the default when untriagedfr:readyClaimable by an agent

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions