Skip to content

v1.8.1 — Test-Suite Sandbox Containment

Choose a tag to compare

@denniyahh denniyahh released this 25 Jul 00:08
· 14 commits to main since this release
v1.8.1
229ece2

Test-suite containment and quality cleanup.

No behavior change for devflow users. Every production git invocation was already correctly pinned, so anyone installing from crates.io was unaffected by the defect this fixes. The blast radius was developers and contributors running the test suite from a checkout — for whom it was severe.

Fixed — the test suite could corrupt the developer's own repository

Running cargo test from a git hook — which scripts/hooks/pre-push does on every push — let test fixtures operate on the real checkout instead of their tempdirs. Observed damage in this repository: core.bare=true on the main repo, the committer identity rewritten to a fixture's, and ten fixture commits stacked onto local main, the first of which deleted all 511 tracked files.

Root cause: git exports GIT_DIR into hook environments when the gitdir is non-default — precisely the case when pushing from a linked worktree. GIT_DIR outranks a process's working directory when git decides which repository to act on, and Rust runs a test binary's tests as threads in one process, so the whole suite inherited it and every fixture retargeted the real repo despite correctly pinning .current_dir().

Neither git -C nor GIT_CEILING_DIRECTORIES overrides GIT_DIR (only --git-dir does), so pinning the working directory can never be the containment mechanism — clearing the variables is.

Fixed in three layers:

  1. scripts/hooks/pre-push clears $(git rev-parse --local-env-vars) — git's own authoritative list, so it self-maintains across git upgrades
  2. devflow_core::test_support::git_command applies the same scrub per command, across 50 migrated call sites — containing fixtures on every other launch path (git rebase --exec, git bisect run, CI)
  3. A new guard test fails fast and names the cause when the environment is dirty

Validated by controlled A/B in an isolated clone — same commit, same worktree, only the hook differing: scrubbed, the push is green with 156/156 passing; unscrubbed, the push is blocked with 42 failures, core.bare=true and HEAD hijacked onto a fixture branch, reproducing the original incident down to the branch name.

Also fixed

  • gate show and gate respond no longer duplicate the omitted---stage resolution logic, so their behavior cannot drift apart
  • gate show reads the open-gate list once instead of twice, closing a narrow time-of-check/time-of-use window
  • doctor's planning-doc reconciliation uses config::MAIN instead of a second hardcoded "main"

Added

  • scripts/hooks/pre-commit, a chaining shim. core.hooksPath replaces the hooks directory wholesale, so the documented install step would otherwise silently disable a global pre-commit secret scanner. It delegates to whatever hook you already had, and is a no-op if you have none
  • devflow-core gains an off-by-default test-support feature exposing test_support::git_command

Changed

  • DevFlow is now described as opinionated rather than agent-agnostic across the README, ARCHITECTURE, guides, both crate descriptions and --help. It bakes in one developer's specific take on branching, gating and verification rather than aiming to be a universal platform
  • status no longer re-scans events.jsonl per phase for the stage-entry timestamp

Full changelog: https://github.com/denniyahh/devflow/blob/v1.8.1/CHANGELOG.md