Implement repository hygiene

Add-git seeds a minimal .gitignore (.DS_Store) before the initial
stage — the seed rides "Initial board state" and .DS_Store never
enters history; an existing .gitignore (or a directory wearing the
name) is left alone forever, adoption and repo-nested seed nothing.

GitHousekeeping is the periodic loose-object repack: filesystem
enumeration of objects/<2hex>/<38hex> (never git_odb_foreach, which
would rewrite the whole database into a fresh pack each pass),
git_packbuilder_insert one oid at a time, additive pack write — and
deletion only after each oid is re-verified against the written
pack opened as a standalone one-pack odb with no loose backend. Any
failure returns before deleting; the worst case is a stray pack.
Nothing prunes, expires, or consolidates — existing packs
accumulate, recorded as the accepted cost of never rewriting
storage the app didn't write. GitHousekeeper schedules it: git's
own 6700 threshold, 8s after session activation (outlasting the
launch catch-up), background priority, skipped under pause states,
held locks, or an in-flight commit, never retried — the next open
tries again. Free tier composes none of it.

20 new tests: full-odb equality, per-oid survival, identical walks,
byte+mtime-identical refs/HEAD/working tree, whole-.git identity on
every declined pass, and deleting-never-forgets. 2394 tests / 412
suites green; InertGitTests untouched. Closes pro-m1-git-undo.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-31 17:06:57 -04:00
parent 1f7d84bf64
commit a7f35a0e5a
7 changed files with 1180 additions and 12 deletions
+8 -4
View File
@@ -187,16 +187,20 @@ struct HistoryStoreAddGitTests {
#expect(tracked.contains("\(Ident.lane1)/\(Ident.card1)/index.md"))
}
@Test("Add-git seeds no `.gitignore` — repository hygiene is a later card")
func addGitSeedsNoGitignore() async throws {
@Test("Add-git seeds a `.gitignore` into the initial commit")
func addGitSeedsTheIgnoreFile() async throws {
let fixture = try makeBoard()
defer { fixture.tearDown() }
let git = try #require(HistoryStore.compose(boardRoot: fixture.root, tier: .pro))
#expect(await git.addGit())
#expect(!fixture.exists(".gitignore"))
#expect(!GitRepository.trackedPaths(at: fixture.root).contains(".gitignore"))
// The one file the app ever writes into a board because of git, and the one moment it writes
// it (06 Repository hygiene). `RepositoryHygieneTests` carries the rest of the rule the
// untouched existing file, the `.DS_Store` that never enters history, adoption seeding
// nothing; here it is only the fact that add-git's tree includes it.
#expect(fixture.exists(".gitignore"))
#expect(GitRepository.trackedPaths(at: fixture.root).contains(".gitignore"))
}
@Test("The root commit is authored by the derived default when the repo names nobody")