Skip to content

Turn off git auto-maintenance in the archive - #1

Merged
PEEKPerformer merged 1 commit into
mainfrom
fix/disable-auto-maintenance
Sep 17, 2026
Merged

PEEKPerformer merged 1 commit into
mainfrom
fix/disable-auto-maintenance

Conversation

@PEEKPerformer

@PEEKPerformer PEEKPerformer commented Sep 17, 2026 •

Copy link
Copy Markdown
Owner

What this PR does

Sets maintenance.auto=false and gc.auto=0 in the archive's local git config, so a holotype commit never starts a background repack. init.py sets them for new archives. ingest.py sets them for existing archives on their next tick.

Why

git runs git maintenance run --auto --detach after every commit. When the loose-object count passes gc.auto, that starts a detached full repack (git repack --cruft and git pack-objects --all). A second commit starts a second repack even while the first is still running. Ingest makes one commit per changed session, so a busy tick starts several at once.

An archive's pack is mostly zstd or git-crypt blobs, which do not compress or delta. Each repack reads the whole pack and holds gigabytes. On a real archive (7.3 GiB pack, 16 GB Mac):

  • One ingest tick left 63 git processes holding about 44 GB, alive for more than 12 hours.
  • macOS logged repeated low-memory kill events, and the machine had to be force-restarted.
  • The repacks never finished. The pack file was last written two weeks earlier, so the loose-object count stayed over the threshold and every new commit started another repack.

Reproduced on git 2.54 with a scratch repository: four commits in a row left five concurrent pack-objects --all processes. With the two settings in place, the same commits started none.

Turning auto-maintenance off is safer than tuning it. Tuning would still let repacks overlap, and repacking incompressible blobs gains nothing. Loose objects now wait for a manual git repack -d, which packs only the loose objects and leaves the existing large pack alone.

Type of change

  • Bug fix (non-breaking change that fixes a documented or observed defect)
  • New feature (non-breaking change that adds capability)
  • Breaking change (alters existing behavior; requires major-version bump and CHANGELOG entry)
  • Documentation only
  • Performance improvement (no functional change)
  • Internal refactor (no user-visible change)
  • CI / tooling

Non-negotiables check

  • Forensic completeness (no filtering of transcript content)
  • Provenance via hash chain (SHA-256 per session, verifiable with stock Unix tools)
  • Git is the archive substrate
  • Environment capture (host CLI version, OS, holotype version, project-repo git state)
  • Append-only by design
  • No silent network behavior

The change touches only .git/config in the archive. No tracked content changes.

Selftest

  • python scripts/selftest.py passes locally
  • If this PR adds a feature or fixes a bug, a new selftest assertion exercises the change
  • CI is green (GitHub Actions workflow on this PR)

Two new steps: init.py sets both keys, and ingest.py restores them on an archive where they were unset.

Manifest / archive-format impact

  • No archive-format change
  • manifest_version bumped from N to N+1
  • index_schema_version bumped from N to N+1
  • On-disk archive layout changed (describe migration path)

Documentation

  • CHANGELOG.md updated (under the next-release section, 2.4.1)
  • README.md updated if user-facing behavior changed
  • SKILL.md updated if wizard flow changed
  • docs/ updated as appropriate

Cross-platform impact

  • Verified on macOS
  • Verified on Linux (CI, ubuntu-latest)
  • Verified on Windows (not applicable: no Windows runner configured)
  • CI passes on all configured runners

AI assistance

Diagnosis, fix, and draft written by Claude Code from the macOS memory-kill reports and a scratch-repository reproduction. Selftest run locally.

Anything else reviewers should know

Out of scope: run_git in ingest.py still has no timeout, so a hung git push can block a tick. That did not cause this incident.

git runs `git maintenance run --auto --detach` after every commit.
Once the loose-object count passes gc.auto, each commit starts its own
detached full repack, and nothing stops a second one from starting
while the first is still running. An archive's pack is mostly zstd or
git-crypt blobs, so each repack holds gigabytes. One ingest tick makes
one commit per changed session, which stacked repacks until a 16 GB
Mac with a 7.3 GiB archive ran out of memory and had to be
force-restarted.

Set maintenance.auto=false and gc.auto=0 in the archive's local git
config. init.py sets them after `git init`. ingest.py sets them under
the ingest lock before the first commit, which migrates existing
archives on their next tick. Loose objects now wait for a manual
`git repack -d`.

The selftest checks both the init path and the migration path.
@PEEKPerformer
PEEKPerformer merged commit eac5055 into main Sep 17, 2026
4 checks passed
@PEEKPerformer
PEEKPerformer deleted the fix/disable-auto-maintenance branch September 17, 2026 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant