Skip to content

pre_build stages untracked files, publishing uncommitted human work #232

Description

@Jammy2211

pre_build.sh's run_workspace runs, for each of the 13 workspace repos:

for d in scripts slam_pipeline; do black "$d/"; done
...
for d in notebooks scripts; do git add "$d/"; done

Both operations reach untracked files. git add <dir>/ stages them, so any uncommitted work under a workspace's scripts/ or notebooks/ is reformatted by black and pushed inside the "pre build" commit — to a public repo, with no prompt and exit status 0.

This is the same leak class as #126, which was fixed for dataset/ and config/ by deleting their staging lines (#156). The scripts/ path kept the hole. The comment above the staging block asserts "Releases require clean mains (Heart gates on it)", but nothing in the script enforces that for the 13 repos it actually commits to — the clean-main gate at the top covers PyAutoHands alone.

How it surfaced

A near-miss during the 2026-08-07 release drive: an uncommitted WIP script in autolens_assistant/scripts/ would have been reformatted and published. It was caught only because the operator noticed and moved the file out of the repo by hand, restoring it afterwards and verifying it byte-identical by md5.

Reproduced against the unmodified script on throwaway fixture repos with real bare remotes: the private file was committed as "pre build" and pushed to the remote, exit 0, silently.

Fix

  1. Fail-fast preflight over every repo, before the first is touched. run_workspace commits and pushes each repo before moving to the next, so a per-repo check aborting midway would leave earlier repos already published. Uses git ls-files --others --exclude-standard, which honours .gitignore and tolerates pathspecs matching nothing. Also aborts on a missing checkout, which previously surfaced as a bare cd error partway through a run.

    This answers the open atomicity question in docs/pre_build_failure_audit.md §6 — "worth a fail-fast pre-pass (all repos validated before any push)?", costed there as a follow-up.

  2. Narrow the staging to git add -u (tracked edits and deletions) plus files created by the run, added by explicit path — so the directory-wide form cannot return. New notebooks from generate.py must still be staged, which is why git add -u alone is insufficient.

  3. The repo list moves from run_workspace "..." call lines into a WORKSPACE_SPECS array, because two passes now read it and a second hand-maintained list would drift.

No --allow-dirty override: an override is precisely the operator vigilance this replaces.

Verification

Text assertions cannot prove a gate fires, so tests/test_pre_build_staging.py runs the real script against a throwaway PYAUTOBASE of fixture git repos with black/python/gh stubbed and real bare remotes — covering the WIP abort, multi-repo reporting, gitignored files not blocking a release, new generated notebooks still being staged, tracked deletions, and the missing-checkout abort.

Related

The filed prompt draft/bug/pyautobuild/root_level_git_add_stages_nothing_on_unmatched_glob.md in PyAutoMind is obsolete — the root-level glob git add line it describes no longer exists, having been deleted outright by #156 as a measured no-op in all 13 repos. Retired to complete/archive/shelved/ rather than left to be worked.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions