diff --git a/docs/lost-substrate/artifacts/2026-04-29-corruption/fsck-extracts.txt b/docs/lost-substrate/artifacts/2026-04-29-corruption/fsck-extracts.txt new file mode 100644 index 000000000..11e92d360 --- /dev/null +++ b/docs/lost-substrate/artifacts/2026-04-29-corruption/fsck-extracts.txt @@ -0,0 +1,89 @@ +# fsck extracts — load-bearing matches with context +# Source: `git fsck --full --no-progress 2>&1 | grep -C 5` (reflog-inclusive) +# Captured 2026-04-29 (Day 3 corruption triage) + +## Reflog-inclusive fsck — context for 8d5e67fd +error: inflate: data stream error (incorrect data check) +error: corrupt loose object '8d5e67fd313573855848705e4af114f3ff0eecbc' +error: unable to unpack contents of .git/objects/8d/5e67fd313573855848705e4af114f3ff0eecbc +error: 8d5e67fd313573855848705e4af114f3ff0eecbc: object corrupt or missing: .git/objects/8d/5e67fd313573855848705e4af114f3ff0eecbc +error: inflate: data stream error (incorrect data check) +error: cannot unpack 9bf2daee3ce53c88633824f9532a0158aaa92ed9 from .git/objects/pack/pack-16732bccb3ace9ec45c913c57a1fd050fd730c3f.pack at offset 4973478 +dangling blob 1f003ef9077ba9779af779c18efa3ff5a6f39470 +dangling commit 2a00a9f5168ddfd359f62f2e274cb768159f9014 +dangling blob e20172cea3dd96f93dc3004d5ebc7eb137693701 +-- +dangling commit 795b1d3a5b7c5379cd6a1ad8419f0a5e9535c476 +dangling commit b55b5628445f31bb2f1df636859915daef5be582 +dangling tree f65bb21a7409dcdadb9ee6e8f02f1c0d07c93a96 +dangling commit 6e5c40364b078f5a947bf79d9cc9540c3ae940e7 +dangling tree ba5cc0356d2b571ba19477f5be8c16e15993faf6 +missing blob 8d5e67fd313573855848705e4af114f3ff0eecbc +dangling commit 385f12ddc72e05da9be752c5a5acc53ba472bb90 +dangling commit c95f5c65cbae62fd249fedb38c9e45d59260d061 +dangling tree 78606cccb9ba0693d61a3cad81d71b05727ddf2f +dangling commit a1605b88ddb8dd3543610235c585b1372f1eaba4 +dangling commit f5609f368a7e17ba22224eec4bbc715ba3c6f4dc + +## Reflog-inclusive fsck — context for 9bf2daee +error: inflate: data stream error (incorrect data check) +error: corrupt loose object '8d5e67fd313573855848705e4af114f3ff0eecbc' +error: unable to unpack contents of .git/objects/8d/5e67fd313573855848705e4af114f3ff0eecbc +error: 8d5e67fd313573855848705e4af114f3ff0eecbc: object corrupt or missing: .git/objects/8d/5e67fd313573855848705e4af114f3ff0eecbc +error: inflate: data stream error (incorrect data check) +error: cannot unpack 9bf2daee3ce53c88633824f9532a0158aaa92ed9 from .git/objects/pack/pack-16732bccb3ace9ec45c913c57a1fd050fd730c3f.pack at offset 4973478 +dangling blob 1f003ef9077ba9779af779c18efa3ff5a6f39470 +dangling commit 2a00a9f5168ddfd359f62f2e274cb768159f9014 +dangling blob e20172cea3dd96f93dc3004d5ebc7eb137693701 +dangling tree fa019e9576ad659d4a75caf4599d831ee4df415b +dangling tree 490275af8f57e4fac5e77d026c4b47f97e81bf27 + +## --no-reflogs fsck — context for 8d5e67fd +error: inflate: data stream error (incorrect data check) +error: corrupt loose object '8d5e67fd313573855848705e4af114f3ff0eecbc' +error: unable to unpack contents of .git/objects/8d/5e67fd313573855848705e4af114f3ff0eecbc +error: 8d5e67fd313573855848705e4af114f3ff0eecbc: object corrupt or missing: .git/objects/8d/5e67fd313573855848705e4af114f3ff0eecbc +error: inflate: data stream error (incorrect data check) +error: cannot unpack 9bf2daee3ce53c88633824f9532a0158aaa92ed9 from .git/objects/pack/pack-16732bccb3ace9ec45c913c57a1fd050fd730c3f.pack at offset 4973478 +dangling blob 1f003ef9077ba9779af779c18efa3ff5a6f39470 +dangling commit 2a00a9f5168ddfd359f62f2e274cb768159f9014 +dangling commit 9f01f442e7362a9efcd6fc7b1c87fed41eb4146f +-- +dangling tree f65bb21a7409dcdadb9ee6e8f02f1c0d07c93a96 +dangling commit 6e5c40364b078f5a947bf79d9cc9540c3ae940e7 +dangling commit 8d5ca82de7eaa851a12c952ef9ae162ee675581a +dangling tree ba5cc0356d2b571ba19477f5be8c16e15993faf6 +dangling commit 9d5db210aee2b017e99c8d2c78f77242fb24ec0b +missing blob 8d5e67fd313573855848705e4af114f3ff0eecbc +dangling commit be5ed39f8f223aac1149a287fa8f24e3a94c52ae +dangling commit e15e99645306000c5f9d6ee549b8bdff88069a71 +dangling commit fc5ec3b3552af63e82f8f9992a3d1fdcd7931c6e +dangling commit 145ff57c8a073d0fc8ff11dece9ac7e492c85adc +dangling commit 255ff59fc67565e9be345b810aa2b784360c7887 + +## --no-reflogs fsck — context for 9bf2daee +error: inflate: data stream error (incorrect data check) +error: corrupt loose object '8d5e67fd313573855848705e4af114f3ff0eecbc' +error: unable to unpack contents of .git/objects/8d/5e67fd313573855848705e4af114f3ff0eecbc +error: 8d5e67fd313573855848705e4af114f3ff0eecbc: object corrupt or missing: .git/objects/8d/5e67fd313573855848705e4af114f3ff0eecbc +error: inflate: data stream error (incorrect data check) +error: cannot unpack 9bf2daee3ce53c88633824f9532a0158aaa92ed9 from .git/objects/pack/pack-16732bccb3ace9ec45c913c57a1fd050fd730c3f.pack at offset 4973478 +dangling blob 1f003ef9077ba9779af779c18efa3ff5a6f39470 +dangling commit 2a00a9f5168ddfd359f62f2e274cb768159f9014 +dangling commit 9f01f442e7362a9efcd6fc7b1c87fed41eb4146f +dangling blob e20172cea3dd96f93dc3004d5ebc7eb137693701 +dangling commit e5019d81cf9d549bfeee9c6751f7c8d5911af94f + +## Connectivity-only fsck — context for 8d5e67fd +(no match) + +## Connectivity-only fsck — context for 9bf2daee +(no match) + +## Mode-comparison summary +fsck-full (reflog-inclusive) line count: 627 +fsck-full-no-reflogs line count: 1116 +fsck-connectivity line count: 620 + +(Mode-dependent reachability: --no-reflogs reports MORE objects as + dangling because reflog-tip commits no longer count as reachable.) diff --git a/docs/lost-substrate/artifacts/2026-04-29-corruption/git-gc-config.txt b/docs/lost-substrate/artifacts/2026-04-29-corruption/git-gc-config.txt new file mode 100644 index 000000000..d91f1ec06 --- /dev/null +++ b/docs/lost-substrate/artifacts/2026-04-29-corruption/git-gc-config.txt @@ -0,0 +1,13 @@ +# git gc + rerere config snapshot — 2026-04-29 corruption triage +# (read-only inspection; no mutation) + +git version = git version 2.54.0 + +gc.auto = (unset; default ~6700) +gc.autoPackLimit = (unset; default 50) +gc.pruneExpire = (unset; default 2.weeks.ago) +gc.reflogExpire = (unset; default 90.days.ago) +gc.reflogExpireUnreachable = (unset; default 30.days.ago) +gc.worktreePruneExpire = (unset; default 3.months.ago) +gc.rerereResolved = (unset; default 60.days) +gc.rerereUnresolved = (unset; default 15.days) diff --git a/docs/lost-substrate/artifacts/2026-04-29-corruption/hour-05Z-ls-remote.txt b/docs/lost-substrate/artifacts/2026-04-29-corruption/hour-05Z-ls-remote.txt new file mode 100644 index 000000000..d30cb91b6 --- /dev/null +++ b/docs/lost-substrate/artifacts/2026-04-29-corruption/hour-05Z-ls-remote.txt @@ -0,0 +1,10 @@ +# git ls-remote --heads origin chore/heartbeat-batch-2026-04-26-hour-05Z +# Captured 2026-04-29 (Day 3 corruption triage) +# +# Result: empty (no output below this header). +# +# Empty output proves origin no longer has the branch +# `refs/heads/chore/heartbeat-batch-2026-04-26-hour-05Z`. +# The same-named local remote-tracking ref under +# `refs/remotes/origin/chore/heartbeat-batch-2026-04-26-hour-05Z` +# is therefore stale (preserved evidence, not origin recovery). diff --git a/docs/lost-substrate/artifacts/2026-04-29-corruption/hour-05Z-show-ref.txt b/docs/lost-substrate/artifacts/2026-04-29-corruption/hour-05Z-show-ref.txt new file mode 100644 index 000000000..f6f27ebb7 --- /dev/null +++ b/docs/lost-substrate/artifacts/2026-04-29-corruption/hour-05Z-show-ref.txt @@ -0,0 +1,2 @@ +d9feb3f08603e37118491e26527c03e6e5760d5b refs/heads/chore/heartbeat-batch-2026-04-26-hour-05Z +d9feb3f08603e37118491e26527c03e6e5760d5b refs/remotes/origin/chore/heartbeat-batch-2026-04-26-hour-05Z diff --git a/docs/lost-substrate/artifacts/2026-04-29-corruption/rev-list-extracts.txt b/docs/lost-substrate/artifacts/2026-04-29-corruption/rev-list-extracts.txt new file mode 100644 index 000000000..b525a2a26 --- /dev/null +++ b/docs/lost-substrate/artifacts/2026-04-29-corruption/rev-list-extracts.txt @@ -0,0 +1,9 @@ +# rev-list extracts — load-bearing matches only +# Source: `git rev-list --objects --all | grep ` +# Captured 2026-04-29 (Day 3 corruption triage) + +## Matches for 8d5e67fd313573855848705e4af114f3ff0eecbc +8d5e67fd313573855848705e4af114f3ff0eecbc docs/hygiene-history/loop-tick-history.md + +## Matches for 9bf2daee3ce53c88633824f9532a0158aaa92ed9 +9bf2daee3ce53c88633824f9532a0158aaa92ed9 docs/category-theory/ctfp-milewski.pdf diff --git a/docs/lost-substrate/artifacts/2026-04-29-corruption/triage-report.md b/docs/lost-substrate/artifacts/2026-04-29-corruption/triage-report.md new file mode 100644 index 000000000..b916bd891 --- /dev/null +++ b/docs/lost-substrate/artifacts/2026-04-29-corruption/triage-report.md @@ -0,0 +1,220 @@ +# Corruption triage — 2026-04-29 — empirical artifact + +This file captures the exact commands run, the raw outputs (in +sibling files), and the conclusion drawn. It exists so a future +reader can verify the prose conclusions in the ledger against the +artifacts, rather than trusting the summary. + +## Sibling artifact files (load-bearing extracts only) + +Per Aaron 2026-04-29 (the repo is the soulfile — keep it +clean): only the **load-bearing extracts** are committed, +not the raw multi-MB diagnostic dumps. The grep recipes that +produce these extracts are below; future readers can re-run +locally to regenerate the raw outputs if needed. + +| File | Source command (extract-recipe in re-run section) | +|---|---| +| `fsck-extracts.txt` | grep -C 5 of corrupt SHAs against `git fsck` outputs in three modes (reflog-inclusive / `--no-reflogs` / `--connectivity-only`) plus per-mode line-counts to evidence mode-dependence | +| `rev-list-extracts.txt` | grep of corrupt SHAs against `git rev-list --objects --all` | +| `git-gc-config.txt` | git version + per-key `git config --get gc.*` + `gc.rerereResolved` / `gc.rerereUnresolved` snapshot | +| `hour-05Z-show-ref.txt` | `git show-ref \| grep hour-05Z` | +| `hour-05Z-ls-remote.txt` | `git ls-remote --heads origin hour-05Z` (empty = origin no longer has the branch) | + +All files are point-in-time evidence; do not edit. + +### Re-run recipe (regenerates raw outputs locally) + +If a future reader wants the raw multi-MB dumps (this file +intentionally does not commit them — see Aaron's +soulfile-cleanliness rule), run from repo root: + +```bash +mkdir -p /tmp/corruption-triage-raw +git fsck --full --no-progress > /tmp/corruption-triage-raw/fsck-full.txt 2>&1 +git fsck --full --no-reflogs --no-progress > /tmp/corruption-triage-raw/fsck-full-no-reflogs.txt 2>&1 +git fsck --connectivity-only --no-progress > /tmp/corruption-triage-raw/fsck-connectivity.txt 2>&1 +git rev-list --objects --all > /tmp/corruption-triage-raw/rev-list-all-objects.txt 2>&1 +ls -la /tmp/corruption-triage-raw/ +``` + +The raw outputs are reproducible from any clone with the same +local refs at the same point in time. Committing the extracts +preserves the load-bearing evidence; committing the raw dumps +would dirty the soulfile. + +## Two corrupt objects under triage + +| SHA | Type | Size | Source path | +|---|---|---|---| +| `9bf2daee3ce53c88633824f9532a0158aaa92ed9` | blob (recovered from origin) | 16,455,417 bytes | unknown — large blob | +| `8d5e67fd313573855848705e4af114f3ff0eecbc` | blob (corrupt loose, locally) | unrecoverable from origin | `docs/hygiene-history/loop-tick-history.md` (intermediate version) | + +## Three-bucket reachability — `8d5e67fd` + +Per Amara 2026-04-29: `git fsck` reachability is mode-dependent +(reflog-inclusive vs `--no-reflogs`). A correct classification +distinguishes three buckets: + +| Bucket | Definition | Verdict for `8d5e67fd` | +|---|---|---| +| A | Live branch / tag / ref reachable | **YES** — `refs/heads/chore/heartbeat-batch-2026-04-26-hour-05Z` reaches it (local-only; the same-named remote-tracking ref is stale — origin no longer has the branch) | +| B | Reflog / stash / local-recovery reachable | Indeterminate from this scan; reflog scan was reflog-inclusive fsck (showed dangling adjacency); `refs/stash` is empty | +| C | Dangling / unreachable only | **NO** — bucket A applies, so C does not | + +### Verifying bucket A + +```text +$ git rev-list --objects --all | grep 8d5e67fd +8d5e67fd313573855848705e4af114f3ff0eecbc docs/hygiene-history/loop-tick-history.md + +$ git for-each-ref ... | per-ref rev-list grep +ref=refs/heads/chore/heartbeat-batch-2026-04-26-hour-05Z → reaches it +ref=refs/remotes/origin/chore/heartbeat-batch-2026-04-26-hour-05Z → reaches it (stale) +``` + +### Verifying the remote-tracking ref is stale + +```text +$ git ls-remote origin refs/heads/chore/heartbeat-batch-2026-04-26-hour-05Z +(empty — origin no longer has this branch) + +$ git clone --no-checkout --quiet \ + --branch chore/heartbeat-batch-2026-04-26-hour-05Z \ + https://github.com/Lucent-Financial-Group/Zeta.git \ + /tmp/repo-fresh-corruption-recheck-2026-04-29 +fatal: Remote branch chore/heartbeat-batch-2026-04-26-hour-05Z not found in upstream origin +``` + +### Content-equivalence verification — `8d5e67fd` + +Per Amara's correction: verify squash-preservation by **content**, +not by ancestry vibe. + +```text +$ BR=refs/heads/chore/heartbeat-batch-2026-04-26-hour-05Z +$ OBJ=8d5e67fd313573855848705e4af114f3ff0eecbc + +$ git ls-tree -r "$BR" | grep "$OBJ" +(no path-match — corrupt blob is NOT at the branch tip) + +$ git show "$BR":docs/hygiene-history/loop-tick-history.md | head -1 +# Loop-tick history ← branch tip's tick-history.md is readable + +$ git diff --name-status origin/main..."$BR" \ + -- docs/hygiene-history/loop-tick-history.md +M docs/hygiene-history/loop-tick-history.md ← diff succeeds + +$ git log --oneline -1 "$BR" +d9feb3f chore(loop-tick-history): hour-05Z bundle close 2026-04-26T05:59:36Z ... +``` + +**Branch tip is clean.** The corrupt blob `8d5e67fd` is from +the branch's **intermediate history** (an earlier commit on the +same branch path), NOT the current tip. The branch is also NOT +an ancestor of `main` (its tip is post-squash-merge and +abandoned), and `main` carries the equivalent content under a +different blob (`de670f72d6fd34208d04863818288d764150a151`). + +```text +main's tick-history blob: de670f72d6fd34208d04863818288d764150a151 +branch-tip's blob: (different, clean — readable via git show) +corrupt blob: 8d5e67fd313573855848705e4af114f3ff0eecbc + ← appears only in intermediate history +``` + +## Final classification — `8d5e67fd` + +```text +CORRUPT_BLOB_REFERENCED_BY_LIVE_LOCAL_BRANCH_AND_STALE_REMOTE_TRACKING_REF +``` + +(sub-bucket of bucket A under the three-bucket schema; refined +sub-condition: branch TIP is clean, corrupt blob is in +intermediate history of the branch only.) + +Substrate-loss assessment: + +- **Current-state use** (checkout / hard-reset-to-tip / diff vs + main): zero impact. Branch tip and main are both readable. +- **Bisect-through-pre-merge-history**: impacted. Bisecting + `loop-tick-history.md` through this branch's intermediate + revisions would surface the corrupt blob; that pre-merge + bisect-step granularity is the only thing actually lost, + and it's a niche use case for a stale post-merge local + branch. + +## Hard-reset readiness — qualified statement + +```text +This corruption does not appear to affect current main, but it +DOES affect at least one live local branch (specifically, the +intermediate history of refs/heads/chore/heartbeat-batch-2026- +04-26-hour-05Z). Hard reset / branch cleanup remains blocked +until the branch's content is classified as preserved, +obsolete, or explicitly abandoned. +``` + +Branch-tip checkout / hard-reset-to-tip is fine. Reading +intermediate-history blobs would surface the corruption. + +## Stale remote-tracking ref — evidence, not origin recovery + +```text +$ git show-ref | grep 'chore/heartbeat-batch-2026-04-26-hour-05Z' +d9feb3f08603e37118491e26527c03e6e5760d5b refs/heads/chore/heartbeat-batch-2026-04-26-hour-05Z +d9feb3f08603e37118491e26527c03e6e5760d5b refs/remotes/origin/chore/heartbeat-batch-2026-04-26-hour-05Z + ↑ same SHA → ref is stale, not authoritative + +$ git ls-remote --heads origin chore/heartbeat-batch-2026-04-26-hour-05Z +(empty — origin no longer has this branch) +``` + +The remote-tracking ref is **stale** — origin deleted the +branch (presumably after squash-merge), but the local repo +still carries the same-named ref under +`refs/remotes/origin/...`. This stale ref is evidence about +the prior state of origin; it is NOT proof origin currently +has the object. Do not prune it during triage — `git fetch +--prune origin` and `git remote prune` would remove it, and +that's a destructive cleanup decision under the same rule as +GC. + +```text +Stale remote-tracking ref is evidence, not origin recovery. +Do not prune it while investigating lost substrate. +``` + +## Cleanup posture + +```text +Cleanup is a destructive decision, not a repair step. +``` + +`git gc` / `git prune` / `git repack -Ad` / `git fsck --lost-found` +remain forbidden during triage. Auto-gc thresholds are at default +(per `gc-config-snapshot.txt`); below-threshold loose-object count +means auto-gc is not imminent, but the discipline is "no implicit +cleanup decision" not "we have time." + +A future cleanup may remove this unreachable/corrupt evidence, so +cleanup must wait until related dangling/reflog/stash surfaces +are classified and either preserved or explicitly abandoned. + +## Composes with + +- `docs/lost-substrate/inventory-ledger-2026-04-29.md` — the + inventory ledger that surfaced these findings. +- `memory/feedback_corruption_triage_discipline_object_health_incident_aaron_amara_2026_04_29.md` + — the durable discipline rule. + +## Distilled keepers (Amara 2026-04-29) + +```text +A corrupt object is not a backlog item — it is a substrate health incident. +Do not prune the evidence while investigating lost evidence. +Recoverable-from-origin requires fresh-clone cat-file type + size verification. +Cleanup is a destructive decision, not a repair step. +Corruption lane means exclusive lane. +If corruption is the incident, everything else is noise. +``` diff --git a/docs/lost-substrate/inventory-ledger-2026-04-29.md b/docs/lost-substrate/inventory-ledger-2026-04-29.md index 86ff41f2e..f27f38ca7 100644 --- a/docs/lost-substrate/inventory-ledger-2026-04-29.md +++ b/docs/lost-substrate/inventory-ledger-2026-04-29.md @@ -1,6 +1,6 @@ --- Scope: Read-only inventory ledger for lost-substrate audit. Generated as the prerequisite step before any destructive action (branch deletion, worktree pruning, hard reset). -Attribution: Aaron 2026-04-29 status-check question via Amara-translated multi-AI converged stance — *"When the factory has too many unknowns, do not choose a fix. Build the inventory ledger."* +Attribution: per Aaron's 2026-04-29 input + Amara synthesis — *"When the factory has too many unknowns, do not choose a fix. Build the inventory ledger."* Peer-verified by Codex + Gemini (independent harnesses); their findings appended below as the "Peer verification gaps" section. Operational status: research-grade Lifecycle status: active Non-fusion disclaimer: This is a snapshot in time, not a continuous metric. Counts will drift as PRs land and worktrees are pruned. Re-run on cadence per B-0090. @@ -16,10 +16,9 @@ Aaron's 2026-04-29 status-check surfaced three real gaps: 2. Trajectory rules filed but mostly not automated 3. 284 gone-upstream branches + 57 locked worktrees not drained -Multi-AI converged stance (Aaron forwarding Amara): *"Do not -forward-sync 562 commits first. Do not delete branches/worktrees -first. Do not create new doctrine. First build the inventory -ledger."* +Per Aaron's input + Amara synthesis: *"Do not forward-sync 562 +commits first. Do not delete branches/worktrees first. Do not +create new doctrine. First build the inventory ledger."* This ledger is that prerequisite step. Read-only audit. No destructive action authorized by this file. @@ -93,7 +92,7 @@ by default. | Bucket | Definition | This snapshot | |---|---|---| -| BRANCH_MERGED_TO_MAIN | Branch tip is reachable from main; safe to delete | TBD via `git branch --merged main` | +| BRANCH_MERGED_TO_MAIN_CANDIDATE | Branch tip mechanically reachable from main per `git branch --merged origin/main`; deletion **candidate**, NOT automatic clearance — still needs open-PR mapping + worktree mapping + peer verification | 121 (sampled 2026-04-29; see Day-2 follow-up below) | | BRANCH_HAS_OPEN_PR | Branch has an open PR; preserve | 27 | | BRANCH_CONTENT_ON_MAIN | Branch tip ≠ main HEAD but content-equivalent (per content-classification) | TBD per-branch audit | | BRANCH_CONTENT_ON_OTHER_BRANCH | Branch content lives on another branch (e.g., a successor branch) | TBD per-branch audit | @@ -169,8 +168,11 @@ In order of safety + leverage: reference each branch tip against main's tree using content-equivalence (per the metric ladder in `memory/feedback_reset_readiness_metric_ladder_content_loss_surface_amara_2026_04_28.md`). -2. **Run `git branch --merged main`** to identify - BRANCH_MERGED_TO_MAIN bucket; these are deletion-safe. +2. **Run `git branch --merged origin/main`** to identify + `BRANCH_MERGED_TO_MAIN_CANDIDATE` bucket; these are deletion + **candidates** (mechanical reachability), NOT automatic + clearance — still need open-PR mapping + worktree mapping + + peer verification per the bucket schema above. 3. **For BRANCH_GONE_UPSTREAM with substrate-y names** (Amara conversation absorbs, Aurora ferry absorbs, alignment edits): manual content-equivalence classification. @@ -188,9 +190,10 @@ Picked by leverage (most uncertainty per branch removed): 1. **Locked worktree audit** — sample 5 worktrees per tick; classify each into the bucket schema; build out the ledger over multiple ticks. -2. **`git branch --merged main`** — identifies safe-to-delete - branches in one command; produces an immediate - BRANCH_MERGED_TO_MAIN bucket. +2. **`git branch --merged origin/main`** — produces an + immediate `BRANCH_MERGED_TO_MAIN_CANDIDATE` bucket + (deletion candidates, NOT auto-cleared; need open-PR + + worktree + peer verification before any delete). 3. **Cross-reference branches against open PRs** — already done in this snapshot (27 BRANCH_HAS_OPEN_PR). 4. **Content-equivalence audit on substrate-y branch names** @@ -239,7 +242,375 @@ Each cycle: (`docs/lost-substrate/inventory-ledger-YYYY-MM-DD.md`) rather than mutating this one. +## Peer verification gaps (Codex + Gemini, 2026-04-29) + +Aaron requested adversarial peer-verification: *"You've thought +you were safe before and every time the others CLIs found +something you missed."* Two independent harnesses found +substantial surfaces this initial inventory MISSED. Counts in +the top-level table are **not yet a substrate-loss proof** — +they are a branch/worktree snapshot only. + +### Surfaces Gemini caught (independent reviewer #1) + +**Ephemeral Git state** (the "almost commits"): + +- **Index / staged-but-uncommitted across all 58 worktrees** — + explicit unfinalized intent that won't sync via push. +- **Git notes** (`git notes`) — invisible in standard logs; + metadata attached to commits. +- **Orphaned objects** (`git fsck --lost-found`) — blobs/trees + dropped from reflog but not yet GC'd. Hard-reset substrate + may be floating here. + +**Untracked local substrate**: + +- **`.gitignore`'d files** — `.env`, local SQLite DBs, + uncommitted `memory/` shards, `.mise` tweaks. Structurally + load-bearing locally. +- **`.git/config` + `.git/hooks/`** — custom workflows, + aliases, pre-commit hooks. Encode intent but unversioned. + +**Networked / outer substrate**: + +- **Submodule state** — local detached HEADs / unpushed + commits in submodules. +- **Git LFS objects** — commit pointer ≠ asset; LFS sync is + separate from commit sync. +- **GitHub-native state** — draft PRs, Wiki pages, Actions + variables/secrets, branch protection rules, Project Boards. + +### Surfaces Codex caught (independent reviewer #2) + +**Repository-internal state** the ledger missed: + +- **Rerere cache** (`.git/rr-cache`) — 293 conflict-resolution + records. Real merge/rebase substrate. +- **Per-worktree transaction state** — `REBASE_HEAD`, + `AUTO_MERGE`, `ORIG_HEAD`, `FETCH_HEAD`, `COMMIT_EDITMSG` + in `.git/worktrees/*/`. Locked-worktree count alone is + insufficient; some are mid-operation. +- **History-rewriting ref namespaces** — `refs/replace`, + `refs/original`, `refs/bisect`, `refs/pull`, `refs/changes`, + plus `.git/info/grafts`. Should prove these are empty/audit. +- **Index flags via `git ls-files -v`** — + `assume-unchanged` / `skip-worktree` flags can hide tracked- + file drift from ordinary `git status`. Plus + `.git/info/exclude` and global excludes (separate from repo + `.gitignore`). +- **Patch/mailbox/bundle artifacts** — search `*.patch`, + `*.diff`, `*.mbox`, `*.eml`, `*.bundle`. Local evidence: + `references/upstreams/voltdb/voltcore.eml` + others. + +**Outer-boundary substrate**: + +- **Nested upstream mirrors** (`references/upstreams/*`) — + embedded `.gitmodules`, lockfiles, upstream state. Each may + need its own inventory; not Zeta branch math. +- **Sibling/factory repos** — `../claude-code`, `../SQLSharp`, + `../runtime`. If "AceHack=LFG" means factory substrate, + repository-local Git is too narrow a boundary. + +### What this means for the ledger + +The branch/worktree snapshot at the top of this ledger is +**not yet a substrate-loss proof**. It is a useful first pass. +Substrate-loss proof for 0/0/0 requires three layers: + +```text +1. Commit graph: ahead/behind counts (already snapshotted) +2. Tree state: git diff --stat --name-status both directions +3. Content-loss surface: classified files/branches/worktrees + + peer-verified missed surfaces above + per-row evidence +``` + +Per Amara's framing: *"A count is not a clearance. A bucket is +not proof. A ledger row needs evidence."* + +### Day-2 inventory results (executed 2026-04-29 post-peer-verification) + +The Day-2 commands ran. **Real findings** — not just empty checks: + +| Surface | Finding | +|---|---| +| **Stashes** | **8 entries** with substantive WIP. stash@{3} alone is 668 files / 208,844 deletions. Each stash is unfinalized intent on a different branch. | +| **Reflog** | 13,822 entries (rich machine-local history; ~30-90 day TTL) | +| **Git notes** | 0 (clean) | +| **fsck dangling objects** | **1,109** (substantial; 1,000+ dangling commits/trees/blobs not on any branch) | +| **Pack corruption** | **`.git/objects/pack/pack-16732bccb3ace9ec45c913c57a1fd050fd730c3f.pack` has data-stream errors**; 2 specific corrupt objects identified — see "Corruption triage results" section below. | +| **History-rewriting refs** | All 5 namespaces (`replace` / `original` / `bisect` / `pull` / `changes`) empty + no `.git/info/grafts`. Clean. | +| **Index flags** | 0 `assume-unchanged` / `skip-worktree` (clean) | +| **Per-worktree mid-operation** | 0 worktrees in REBASE/MERGE/AUTO_MERGE state (clean) | +| **Rerere cache** | **293 conflict-resolution records** (real merge-resolution substrate; codified knowledge) | +| **Patch / bundle artifacts** | 0 outside `references/upstreams/` (clean) | +| **Submodules / LFS** | None / `git lfs` not installed (clean) | +| **Tags** | 0 (none in use) | + +### Corruption triage results (executed post-Amara-correction) + +Two corrupt objects identified + classified per Amara's bucket schema: + +| Object | Type | Size | Bucket | Notes | +|---|---|---|---|---| +| `9bf2daee3ce53c88633824f9532a0158aaa92ed9` | blob | 16,455,417 bytes | RECOVERABLE_FROM_ORIGIN | Fresh clone (`/tmp/zeta-fresh-corruption-check`) returns type+size cleanly. Recovery via `git fetch --refetch origin`. | +| `8d5e67fd313573855848705e4af114f3ff0eecbc` | blob | 439,327 bytes (early `docs/hygiene-history/loop-tick-history.md`) | Round 1: `MISSING_UNRECOVERED` (superseded). **Final (Round 3): `CORRUPT_BLOB_REFERENCED_BY_LIVE_LOCAL_BRANCH_AND_STALE_REMOTE_TRACKING_REF`** | Fresh clone returns "could not get object info" (origin no longer has the branch). Round-3 reachability scan placed it in bucket A: live-local branch reachable; same-named remote-tracking ref is stale. Branch tip is clean — corrupt blob is from intermediate history of the branch only. See "Day-2+: corrected reachability via three-bucket scan" section below for details. | + +**Critical finding (Round 3, final)**: `8d5e67fd` is referenced by a live local branch (`refs/heads/chore/heartbeat-batch-2026-04-26-hour-05Z`) plus a stale same-named remote-tracking ref. Origin no longer has the branch (verified via `git ls-remote`). The branch TIP is clean — the corrupt blob is from the branch's intermediate history only. Substrate-loss for current-state use is zero; only bisect-through-pre-merge-history would surface the corruption. See the corrected reachability + content-equivalence section below. + +### Day-2+: corrected reachability via three-bucket scan (Amara 2026-04-29) + +The first follow-up under-scanned reachability and reached the +wrong conclusion. Amara's correction asked for a three-bucket +schema instead of the implicit two-bucket dangling-vs-live split. + +**Three reachability buckets (Amara 2026-04-29)**: + +```text +A. live branch / tag / ref reachable +B. reflog / stash / local-recovery reachable +C. dangling / unreachable only +``` + +`git fsck` reachability is **mode-dependent** — by default fsck +includes reflogs as heads, but `--no-reflogs` excludes them, so +"dangling under no-reflogs" is not the same as "irrelevant to +all local recovery surfaces." The first follow-up only sampled +10 of 888 commits touching `loop-tick-history.md` and missed +the live-ref reach. + +**Re-verification commands** (artifacts persisted under +`docs/lost-substrate/artifacts/2026-04-29-corruption/`): + +```bash +git rev-list --objects --all | grep 8d5e67fd +git fsck --full --no-progress | grep -C 5 8d5e67fd +git fsck --full --no-reflogs --no-progress | grep -C 5 8d5e67fd +git rev-list --objects refs/stash | grep 8d5e67fd +git for-each-ref --format='%(refname)' | while read r; do + git rev-list --objects "$r" 2>/dev/null | grep 8d5e67fd && echo "ref=$r" +done +``` + +**Result — `8d5e67fd` is bucket A, not bucket C**: + +```text +ref=refs/heads/chore/heartbeat-batch-2026-04-26-hour-05Z → reaches the corrupt blob +ref=refs/remotes/origin/chore/heartbeat-batch-2026-04-26-hour-05Z → reaches the corrupt blob (stale) +``` + +The local branch `chore/heartbeat-batch-2026-04-26-hour-05Z` +is a **live local ref** that reaches the corrupt blob. The +same-named remote-tracking ref is **stale**: a fresh clone of +origin shows the branch was deleted upstream +(`git ls-remote origin refs/heads/chore/heartbeat-batch-2026-04-26-hour-05Z` +returns empty). So the live-ref reach is local-only. + +**Squash-merge equivalence**: the local branch tip `d9feb3f` is +NOT an ancestor of `main`, but the equivalent **content** of +`docs/hygiene-history/loop-tick-history.md` is preserved on +main under a different blob SHA +(`de670f72d6fd34208d04863818288d764150a151`) via the squash-merge +that absorbed the original branch. The intermediate-revision +provenance (per-commit bisect granularity from the pre-squash +branch) exists only locally and cannot be recovered from origin. + +**Corrected classification (Amara 2026-04-29)**: + +```text +CORRUPT_BLOB_REFERENCED_BY_LIVE_LOCAL_BRANCH_AND_STALE_REMOTE_TRACKING_REF +``` + +(sub-bucket of bucket A — live-local-ref reachable; origin no +longer has the branch; the same-named remote-tracking ref +preserves evidence but does not provide origin recovery.) + +**Content-equivalence verification (Amara 2026-04-29)**: +Squash-merge preservation must be verified by **content**, not +by ancestry vibe. Empirical findings: + +```text +$ git ls-tree -r refs/heads/.../hour-05Z | grep 8d5e67fd +(no path-match — corrupt blob is NOT at the branch tip) + +$ git show refs/heads/.../hour-05Z:docs/hygiene-history/loop-tick-history.md +# Loop-tick history +... (succeeds — branch tip's tick-history.md is readable) + +$ git diff --name-status origin/main...refs/heads/.../hour-05Z \ + -- docs/hygiene-history/loop-tick-history.md +M docs/hygiene-history/loop-tick-history.md +(diff succeeds — corrupt blob is not needed for the diff) +``` + +The branch TIP is **clean**. The corrupt blob `8d5e67fd` is +from the branch's **intermediate history** (an earlier commit +on the same branch path), not the current tip. Substrate-loss +for any current-state use case (checkout, hard-reset to tip, +diff vs main) is zero. Substrate-loss for bisect-through- +pre-merge-history is the only real impact, since bisect would +need to read each historical revision of `loop-tick-history.md`. + +**Recovery urgency**: LOW–MEDIUM. Current-state use of the +branch is unaffected. Bisect-through-history of this branch's +intermediate revisions IS affected, but that's a niche use +case for a post-merge stale local branch. + +**Hard-reset readiness — qualified statement (Amara correction)**: + +```text +This corruption does not appear to affect current main, but it +DOES affect at least one live local branch (specifically, the +intermediate history of refs/heads/chore/heartbeat-batch-2026- +04-26-hour-05Z). Hard reset / branch cleanup remains blocked +until the branch's content is classified as preserved, +obsolete, or explicitly abandoned. +``` + +Branch-tip checkout / hard-reset-to-tip is fine; reading +intermediate-history blobs would surface the corruption. + +**Cleanup posture (Amara correction)**: + +```text +Cleanup is a destructive decision, not a repair step. +``` + +A future cleanup may remove this unreachable/corrupt evidence, +so cleanup must wait until related dangling/reflog/stash +surfaces are classified and either preserved or explicitly +abandoned. `git gc` / `git prune` / `git repack -Ad` / +`git fsck --lost-found` remain forbidden during triage. +Auto-gc thresholds are at default per +`docs/lost-substrate/artifacts/2026-04-29-corruption/git-gc-config.txt`; +below-threshold loose-object count +means auto-gc is not imminent — the discipline is "no implicit +cleanup decision" not "we have time." + +**Single-lane exclusivity (Amara correction)**: + +```text +Corruption lane means exclusive lane. +If corruption is the incident, everything else is noise. +``` + +While corruption triage is active, all unrelated automation / +backfill / scanner / queue-drain work pauses. The triage lane +is exclusive until object classification is stable. + +**Initial fsck adjacency observation (preserved)**: + +```text +dangling tree ba5cc0356d2b571ba19477f5be8c16e15993faf6 +dangling commit 9d5db210aee2b017e99c8d2c78f77242fb24ec0b +missing blob 8d5e67fd313573855848705e4af114f3ff0eecbc +``` + +This adjacency is real but does not by itself prove "ONLY +dangling" — the same blob is also reached by the live-local +ref above. Both observations are consistent: the corrupt blob +is referenced from a live local branch tip AND from dangling +substrate, and was not preserved on origin. + +**Per Amara's correction**: do NOT declare "origin has it" without fresh-clone verification. Wording corrected throughout this ledger. + +**Highest-risk findings**: + +1. **1,109 dangling objects** — substrate not reachable from any branch. Hard-reset would destroy these. fsck-recovery is read-only and possible IF the dangling commits encode something we care about. Each requires `git show ` per-object inspection to know what it carries. + +2. **Pack corruption** — one pack file has a data-stream error. Object `9bf2daee3ce53c88633824f9532a0158aaa92ed9` cannot be unpacked. This is a real durability-of-history concern. May need `git fsck --full` + possibly `git repack` + re-fetch from origin if the missing object is also on origin. + +3. **8 stashes** — explicit unfinalized intent. stash@{3} is enormous (668 files; could be a bulk-revert or a test run). Each stash needs per-stash content classification before any stash-prune operation. + +4. **293 rerere records** — codified conflict-resolution knowledge from past merges. NOT auto-disposable; this is real factory-discipline substrate that speeds up future merges. + +**Updated count of substrate surfaces this ledger needs to track**: not just 869 branches + 58 worktrees (top of ledger) — also 8 stashes + 1,109 dangling objects + 293 rerere records + pack-corruption to repair + the 15+ surfaces from peer verification. + +**Honest-statement update**: this ledger v2 (after peer verification + Day-2 execution) is closer to a substrate-loss audit but still NOT complete — the 1,109 dangling objects need per-object classification before any reset/forward-sync is safe. + +### Day-2 follow-up: next safe inventory passes + +Each pass is read-only: + +```bash +# Ephemeral Git state +git stash list +git reflog --all +git notes list +# fsck WITHOUT --lost-found here: --lost-found writes dangling +# objects into .git/lost-found/, which is NOT read-only. Use +# --no-reflogs alone for the read-only mode-comparison check. +git fsck --no-reflogs --no-progress + +# History-rewriting ref namespaces +git for-each-ref refs/replace refs/original refs/bisect refs/pull refs/changes +ls -la .git/info/grafts 2>&1 || echo "no grafts file" + +# Index flags +git ls-files -v | grep -v '^[H ]' | head -30 + +# Per-worktree transaction state +ls .git/worktrees/*/REBASE_HEAD .git/worktrees/*/AUTO_MERGE .git/worktrees/*/ORIG_HEAD 2>&1 | head -20 + +# Rerere cache +ls .git/rr-cache 2>&1 | head -10 +ls .git/rr-cache | wc -l + +# Patch/mailbox/bundle artifacts (excluding upstream mirrors) +find . -path ./references -prune -o \( -name '*.patch' -o -name '*.diff' -o -name '*.mbox' -o -name '*.eml' -o -name '*.bundle' \) -print | head -20 + +# .gitignore'd substrate +git status --ignored --porcelain | head -30 + +# Submodule + LFS +git submodule status 2>&1 || echo "no submodules" +git lfs ls-files 2>&1 | head -10 || echo "no LFS or git lfs not installed" +``` + +These commands run in subsequent ticks. Each finding extends +this ledger; deletion / pruning / forward-sync / hard-reset +remain blocked until the full surface is mapped. + +### Evidence-column schema (per Amara's refinement) + +A useful per-row format for future cycles: + +```text +branch | head_sha | upstream | upstream_status | open_pr | worktree_path | worktree_locked | merged_to_origin_main | content_equivalence_status | classification | recommended_action | evidence_command +``` + +Without evidence columns, future cycles must re-run the audit +to know why each bucket assignment was made. Per-row evidence +makes the classification self-justifying. + +### Three-layer 0/0/0 verification (binding) + +AceHack = LFG at 0/0/0 with NO content loss requires ALL +THREE layers clean: + +```text +Layer 1 — commit graph: + ahead/behind both 0 + status not "diverged" + +Layer 2 — tree state: + git diff origin/main..AceHack/main --stat = empty + git diff AceHack/main..origin/main --stat = empty + +Layer 3 — content-loss surface: + classified files/branches/worktrees show no NEEDS-RECOVERY + NEEDS-FORWARD-SYNC = 0 + CONFLICTS-WITH-CURRENT-MAIN = 0 + NEEDS-HUMAN-REVIEW = 0 + Peer-verified surfaces (above) all classified +``` + +Compare API counts alone are NOT proof. They are diagnostic. + ## The keeper rule > *When the factory has too many unknowns, do not choose a > fix. Build the inventory ledger.* (Amara via Aaron, 2026-04-29) + +> *A count is not a clearance. A bucket is not proof. A ledger +> row needs evidence.* (Amara, 2026-04-29 peer-verification +> follow-up) diff --git a/memory/MEMORY.md b/memory/MEMORY.md index 24c6816db..906a5bbfc 100644 --- a/memory/MEMORY.md +++ b/memory/MEMORY.md @@ -2,6 +2,8 @@ **📌 Fast path: read `CURRENT-aaron.md` and `CURRENT-amara.md` first.** +- [**The git repo is the soulfile — don't commit raw diagnostic dumps (Aaron + Amara, 2026-04-29)**](feedback_repo_is_soulfile_dont_commit_raw_diagnostic_dumps_aaron_amara_2026_04_29.md) — Every committed byte lives forever. Default: extract + recipe. Don't pollute repo with multi-MB raw fsck/rev-list/profile dumps. Caught when corruption-triage commit added 20,978 lines of mostly-irrelevant raw output to PR #757; reduced to ~28 KB of grep-extracts + re-run recipe. Non-soul repos and git-lfs are escape hatches when raw is genuinely load-bearing AND large. Aaron: *"don't let your soul get dirty you control what belongs in there."* +- [**Corruption triage is a substrate health incident, not a backlog item (Aaron + Amara, 2026-04-29)**](feedback_corruption_triage_discipline_object_health_incident_aaron_amara_2026_04_29.md) — When `git fsck` reports corrupt objects, lane narrows hard: stop all background work, do read-only diagnosis first (no `git fsck --lost-found` — it writes), three-bucket reachability scan (live-ref / reflog-stash / dangling-only) — reachability is mode-dependent on fsck flags, fresh-clone verification BEFORE declaring "origin has it," verify squash-preservation by content not ancestry, stale remote-tracking refs are evidence not origin recovery. Worked example: 2026-04-29 audit found 2 corrupt objects — 9bf2daee (RECOVERABLE_FROM_ORIGIN) + 8d5e67fd (CORRUPT_BLOB_REFERENCED_BY_LIVE_LOCAL_BRANCH_AND_STALE_REMOTE_TRACKING_REF after three rounds of triage; branch tip clean, intermediate-history corrupt). Aaron emphasized: *"future self remembers this, this is very important."* - [**PR-boundary restraint validation — bead promoted (Aaron + Aurora + Amara, 2026-04-29)**](feedback_pr_boundary_restraint_validation_bead_promoted_aaron_amara_2026_04_29.md) — Falsifier-not-fired bead-promotion on PR #699; canonical rule *"once a PR enters validation, only validation defects enter that PR; new ideas go to the next PR."* Validation-condition refinement (*"validated when original PR lands clean, not when follow-up opens"*) was Aurora's catch; Amara reactive-elaborator. Allowed/disallowed-changes lists in body. - [**Beacon-promotion pattern — load-bearing rules earn external anchors when they're correct (Aaron + Amara + Claude.ai, 2026-04-28)**](feedback_beacon_promotion_load_bearing_rules_earn_external_anchors_aaron_amara_2026_04_28.md) — Round-level observation: 5 Mirror→Beacon graduations in one round (input-is-not-directive → SDT + RFC 2119; public-company compliance → SEC / Reg FD / SOX; metric corrections → Goodhart / Campbell; evidence lattice → lattice theory; commit-vs-tree → Git internals). Pattern: when an internal factory coinage becomes load-bearing, look for external lineage. Found = graduate Mirror → Beacon. Absent = drift signal worth investigating. Connects to alignment-experiment surface: the rate of load-bearing rules earning external lineage is itself a measurable signal. diff --git a/memory/feedback_corruption_triage_discipline_object_health_incident_aaron_amara_2026_04_29.md b/memory/feedback_corruption_triage_discipline_object_health_incident_aaron_amara_2026_04_29.md new file mode 100644 index 000000000..5e676c22d --- /dev/null +++ b/memory/feedback_corruption_triage_discipline_object_health_incident_aaron_amara_2026_04_29.md @@ -0,0 +1,326 @@ +--- +name: Corruption triage is a substrate health incident, not a backlog item — Aaron + Amara 2026-04-29 +description: When `git fsck` reports corrupt objects, the lane narrows hard — corruption-first triage outranks all background work (PR-queue drain / tick-history shards / scanner builds / branch deletion / worktree pruning). Treat as substrate health incident; corruption lane is exclusive, no side quests. Read-only diagnosis + fresh-clone verification before any repair. Per-object reachability scan uses Amara's three-bucket schema (live-ref / reflog-stash / dangling-only); reachability is mode-dependent on `git fsck` flags. Stale remote-tracking refs are evidence, not origin recovery — do not prune during triage. Verify squash-preservation by content, not ancestry. Cleanup is a destructive decision, not a repair step — never run `git gc` / `git prune` / `git repack -Ad` / `git fsck --lost-found` as a "fix" while investigating lost evidence. +type: feedback +--- + +# Corruption triage discipline — substrate health incident + +## Source + +Aaron 2026-04-29: *"you for sure need to make sure your future +self remembers this, this is very important"* — after a Day-2 +inventory pass surfaced two corrupt git objects on this repo. + +Amara synthesis 2026-04-29: *"A corrupt object is not a backlog +item. It is a substrate health incident. Do not prune the +evidence while investigating lost evidence."* + +## The rule (load-bearing) + +When `git fsck` reports corrupt objects, the lane narrows +**hard**. All background work stops: + +```text +No PR-queue drain. +No tick-history shard generation. +No scanner builds. +No branch deletion. +No worktree pruning. +No new substrate unless it directly records corruption triage. +No git gc / git prune / git repack / git fsck --lost-found as a "fix" yet. +``` + +Corruption-first triage is the lane until object health is +understood. This is not "interesting extra signal"; it's +substrate floorboards creaking. + +## Triage procedure (read-only first) + +### Step 1: Diagnose without writing + +```bash +git status --short +git fsck --full --no-reflogs --no-progress > /tmp/fsck-full.txt 2>&1 +git fsck --connectivity-only --no-progress > /tmp/fsck-connectivity.txt 2>&1 +git verify-pack -v .git/objects/pack/*.idx > /tmp/verify-pack.txt 2>&1 +``` + +**Do NOT use `git fsck --lost-found` as a first move** — that +flag WRITES dangling objects into `.git/lost-found/`. It's not +read-only. + +### Step 2: Per-corrupt-object inspection + +For each `error: corrupt loose object ` or +`error: cannot unpack `: + +```bash +OBJ= +git cat-file -t "$OBJ" # type (may succeed even when content corrupt) +git cat-file -s "$OBJ" # size (may succeed even when content corrupt) +git cat-file -p "$OBJ" # content (this is where corruption surfaces) +``` + +The header may be readable even when the content is corrupt — +type and size will print, but `-p` errors with `inflate: data +stream error`. + +### Step 3: Fresh-clone verification (NOT just current repo) + +```bash +git clone --no-checkout --quiet /tmp/repo-fresh-corruption-check +cd /tmp/repo-fresh-corruption-check +git cat-file -t +git cat-file -s +``` + +**Do NOT say "origin has it" without fresh-clone verification.** +The current repo may be the source of corruption; reading the +object via the same potentially-corrupt repo proves nothing. + +### Step 4: Classify each corrupt object + +**First — three-bucket reachability scan (Amara 2026-04-29)**. +`git fsck` reachability is mode-dependent: by default it +includes reflogs as heads; `--no-reflogs` excludes them. So the +correct frame distinguishes three reachability buckets: + +| Bucket | Definition | +|---|---| +| A | Live branch / tag / ref reachable (`git rev-list --objects --all`; per-ref scan) | +| B | Reflog / stash / local-recovery reachable (default-fsck reach minus bucket A; `refs/stash` rev-list) | +| C | Dangling / unreachable only (no live ref, no reflog, no stash reaches it) | + +**Reachability scan commands** (all read-only; per the soulfile- +cleanliness rule below, run the raw dumps to a `/tmp` working +directory and commit only the load-bearing extracts plus a +re-run recipe — NOT the multi-MB raw outputs themselves): + +```bash +OBJ= +RAW=/tmp/corruption-triage-raw-$(date -u +%Y%m%d) +mkdir -p "$RAW" + +# Raw dumps go to /tmp (NOT committed to soul repo): +git rev-list --objects --all > "$RAW/rev-list-all-objects.txt" 2> "$RAW/rev-list-all-objects.err" +git fsck --full --no-progress > "$RAW/fsck-full.txt" 2>&1 +git fsck --full --no-reflogs --no-progress > "$RAW/fsck-full-no-reflogs.txt" 2>&1 +git rev-list --objects refs/stash > "$RAW/rev-list-stash.txt" 2>&1 || true +git for-each-ref --format='%(refname)' | while read r; do + git rev-list --objects "$r" 2>/dev/null | grep "$OBJ" && echo "ref=$r" +done > "$RAW/per-ref-grep.txt" + +# Grep load-bearing lines: +grep "$OBJ" "$RAW/rev-list-all-objects.txt" +grep -C 5 "$OBJ" "$RAW/fsck-full.txt" +grep -C 5 "$OBJ" "$RAW/fsck-full-no-reflogs.txt" +grep "$OBJ" "$RAW/per-ref-grep.txt" + +# Then commit ONLY the small extracts + a re-run recipe to: +# docs/lost-substrate/artifacts/-corruption/ +# Per +# memory/feedback_repo_is_soulfile_dont_commit_raw_diagnostic_dumps_aaron_amara_2026_04_29.md +``` + +Save extracts (not raw dumps) to the artifacts directory BEFORE +writing prose +conclusions. Future-Claude verifies prose against artifacts, +not the other way around. + +**Then — Amara's recovery-source bucket schema** (orthogonal to +the reachability schema; once reachability is classified, this +table picks the recovery path): + +| Bucket | Definition | +|---|---| +| RECOVERABLE_FROM_ORIGIN | Object exists in fresh clone with same SHA + type + size; recovery via re-clone or `git fetch --refetch` | +| RECOVERABLE_FROM_OTHER_LOCAL_CLONE | Object exists in another local clone of the same repo | +| DANGLING_CANDIDATE_NEEDS_CLASSIFICATION | Object is dangling/unreachable; may be lost substrate; per-object `git show` inspection needed | +| CORRUPT_LOOSE_OBJECT | Loose object in `.git/objects/XX/YY*`; can't be unpacked locally | +| CORRUPT_PACK_OBJECT | Object in pack file with data-stream error | +| MISSING_UNRECOVERED | Not in current repo, not in origin, not in other local clones — lost substrate | + +### Step 5: Repair plan (only after classification) + +```text +RECOVERABLE_FROM_ORIGIN → git fetch --refetch origin (or re-clone) +RECOVERABLE_FROM_OTHER_LOCAL → cherry-pick or fetch from local sibling +DANGLING_CANDIDATE → git show ; preserve before any GC +CORRUPT_LOOSE/PACK + recoverable → re-fetch; verify with git fsck +MISSING_UNRECOVERED → record in lost-substrate ledger; substrate-loss event +``` + +## What this discipline forbids during triage + +- **No `git gc` / `git prune` / `git repack -Ad`.** These can + destroy unreachable evidence. Note: `gc.auto` may run + housekeeping implicitly when porcelain commands grow the + repo; record `git config --get gc.auto` / + `gc.autoPackLimit` / `gc.pruneExpire` as artifact + evidence at the start of triage. Disabling auto-gc + (`git config gc.auto 0`) is itself a deliberate triage + action, not casual. +- **No `git fsck --lost-found` as a first move.** It writes + to `.git/lost-found/`; not read-only. +- **No `stash pop` / `stash apply`.** Stash entries reference + objects that may be the only path to lost work. +- **No "origin has it" without fresh-clone verification.** + Use `git cat-file -t` and `-s` against the fresh clone to + confirm same SHA + type + size. The same-named + remote-tracking ref in your local repo can be **stale** + (origin may have deleted the branch); the stale ref does + not prove origin has the object. +- **No background tasks — single-lane until corruption + health understood.** While corruption triage is active, + all unrelated automation, backfill, scanner, queue-drain, + and substrate work pauses. Corruption lane means + exclusive lane. + +## Specific worked example: 2026-04-29 incident + +Two corrupt objects identified during Day-2 inventory pass: + +| Object | Type | Size | Reachability bucket | Recovery-source bucket | Final classification | +|---|---|---|---|---|---| +| `9bf2daee3ce53c88633824f9532a0158aaa92ed9` | blob | 16,455,417 bytes | A (live) | RECOVERABLE_FROM_ORIGIN | Fresh-clone cat-file confirmed; recoverable via `git fetch --refetch` | +| `8d5e67fd313573855848705e4af114f3ff0eecbc` | blob | 439,327 bytes (intermediate `docs/hygiene-history/loop-tick-history.md`) | A (live local only) | LOCAL_ONLY (origin deleted the branch) | `CORRUPT_BLOB_REFERENCED_BY_LIVE_LOCAL_BRANCH_AND_STALE_REMOTE_TRACKING_REF` | + +**The `8d5e67fd` finding required three rounds of triage** +to classify correctly — each round overturned the prior +conclusion. Lineage: + +- **Round 1**: classified as `MISSING_UNRECOVERED` (fresh + clone "could not get object info"). Wrong scope — only + checked origin recovery; did not check local reachability. +- **Round 2**: claimed `REFERENCED_BY_DANGLING_ONLY` based + on fsck adjacency to a dangling tree. Wrong — sampled + 10 of 888 commits; missed the live-ref reach. +- **Round 3** (correct): three-bucket reachability scan + + per-ref `rev-list` showed live-local-branch reach + (`refs/heads/chore/heartbeat-batch-2026-04-26-hour-05Z`). + Fresh `git ls-remote` confirmed origin no longer has the + branch (the same-named remote-tracking ref is **stale**). + Content-equivalence check (`git ls-tree` + `git show`) + showed the branch TIP is clean — the corrupt blob is from + the branch's intermediate history, not the tip. + +**Final substrate-loss assessment**: zero impact on +current-state use (checkout / hard-reset-to-tip / diff vs +main all succeed). Impact only on bisect-through-pre-merge- +history of this stale post-merge local branch — niche. + +This worked example is the canonical failure-mode template: +**reachability is mode-dependent; classification requires +three-bucket scan + content-equivalence verification, not +ancestry vibes; stale remote-tracking refs preserve evidence +and must not be pruned during triage.** + +## Related discipline + +- **Dangling objects are CANDIDATES, not lost-substrate-confirmed.** + 1,109 dangling objects on this repo include real factory + substrate (Aaron's merge commits, round-44 tick-history). + Apply Candidate-count Goodhart: classify before acting. +- **Rerere cache is merge-immune-memory, NOT a cleanup target.** + 293 rerere records on this repo encode codified conflict- + resolution knowledge. Do NOT prune. +- **Stashes need ranked triage, not bulk.** Inspect each stash + via `git stash show --stat stash@{N}`; no `pop` or `apply` + until a recovery branch is created. + +## Composes with + +- `docs/lost-substrate/inventory-ledger-2026-04-29.md` — the + inventory ledger that surfaced these findings. +- `docs/research/escrowed/aurora-autonomous-flywheel-thesis-2026-04-28.md` + — the escrow primitive; corruption-triage uses similar + bounded-preservation pattern. +- `memory/feedback_candidate_count_goodhart_raw_hits_are_not_violations_aaron_amara_2026_04_28.md` + — Candidate-count Goodhart applies to dangling objects. + +## Distilled rules (keepers) + +```text +A corrupt object is not a backlog item. +It is a substrate health incident. +``` + +```text +Do not prune the evidence while investigating lost evidence. +``` + +```text +Recoverable-from-origin requires fresh-clone cat-file +type + size verification. +``` + +```text +A count is not a clearance. A bucket is not proof. +``` + +```text +Cleanup is a destructive decision, not a repair step. +``` + +```text +Stale remote-tracking ref is evidence, not origin recovery. +Do not prune it while investigating lost substrate. +``` + +```text +Live local branch reference means the incident is not closed. +``` + +```text +Corruption lane means exclusive lane. +Exclusive lane means no side quests. +If corruption is the incident, everything else is noise. +``` + +```text +Reachability is mode-dependent. +Three-bucket scan (live ref / reflog-stash / dangling) +beats two-bucket vibes. +``` + +```text +Verify squash-preservation by content, not by ancestry vibe. +Tip-clean does not prove history-clean; history-corrupt +does not prove tip-corrupt. +``` + +```text +rev-list can enumerate object references even when later +object reads fail. +Enumeration success is not content recovery. +``` + +```text +Reachability claims must record the fsck mode used. +"default fsck" and "fsck --no-reflogs" answer different +questions. +``` + +```text +Reachability is mode-dependent. +Enumeration is not recovery. +Cleanup is not triage. +``` + +```text +Don't dirty the soulfile (the git repo and all its history). +Triage artifacts go in via load-bearing extracts + a re-run +recipe, not multi-MB raw dumps. See +`memory/feedback_repo_is_soulfile_dont_commit_raw_diagnostic_dumps_aaron_amara_2026_04_29.md`. +``` + +## Why this is in memory/, not just a doc + +Aaron emphasized: *"you for sure need to make sure your +future self remembers this, this is very important."* Per the +auto-memory protocol + Aaron's natural-home-of-memories-is-in- +repo rule (2026-04-24), this is durable substrate that future- +Claude must consult on cold-start when corruption is observed. +The ledger itself is point-in-time; this memory is the +discipline. diff --git a/memory/feedback_repo_is_soulfile_dont_commit_raw_diagnostic_dumps_aaron_amara_2026_04_29.md b/memory/feedback_repo_is_soulfile_dont_commit_raw_diagnostic_dumps_aaron_amara_2026_04_29.md new file mode 100644 index 000000000..b0e9fdd2b --- /dev/null +++ b/memory/feedback_repo_is_soulfile_dont_commit_raw_diagnostic_dumps_aaron_amara_2026_04_29.md @@ -0,0 +1,175 @@ +--- +name: The git repo is the soulfile — don't commit raw diagnostic dumps; use extracts + re-run recipes (Aaron + Amara 2026-04-29) +description: The git repo (and all its history) is the project's soulfile — every committed byte lives forever. Don't pollute it with multi-MB raw diagnostic outputs (fsck dumps, rev-list dumps, profile traces, log archives). Commit load-bearing extracts + a re-run recipe instead. Caught when corruption-triage commit added 20,978 lines of mostly-irrelevant raw fsck/rev-list output to PR #757; reduced to ~28 KB of extracts. Non-soul repos and git-lfs are escape hatches when raw artifacts are genuinely load-bearing and large; default is extract+recipe. +type: feedback +--- + +# The git repo is the soulfile — don't dirty it + +## Source + +Aaron 2026-04-29: *"what amara says here about repo size, it +critical load-bearing, its' your soul/soulfile the git repo +and all history, don't let your soul get dirty you control +what belongs in there, and we can have non soul repos too or, +git lfs if needed."* + +Amara 2026-04-29: *"This commit added 20,978 lines of +artifacts. That may be acceptable once, but it's a smell. +GitHub warns at 50 MiB and blocks files over 100 MiB; even +below those limits, dumping large diagnostic logs into normal +repo history can bloat the repo and make future reviews +awful."* + +## The rule (load-bearing) + +```text +The git repo and all its history is the soulfile. +Every committed byte lives forever. +Default discipline: + Commit load-bearing extracts + a re-run recipe. + Do not commit multi-MB raw diagnostic dumps. + +Escape hatches (when raw is genuinely load-bearing AND large): + - Non-soul repo (separate repo for diagnostic archives) + - Git LFS (large files outside main object DB) +``` + +Future-Claude check: before adding a `*.txt` / +`*.log` / `*.jsonl` / `*.csv` artifact to the repo, +ask: +- Is the file > ~100 KB? → smell +- Is the file > ~1 MB? → almost certainly soul-pollution +- Are 99% of the lines non-load-bearing noise? → extract + + re-run recipe instead +- Is the file the **conclusion** or the **evidence-dump + behind a conclusion**? Conclusion → durable. Raw evidence + dump → re-runnable on demand, not durable. + +## Worked example: 2026-04-29 corruption-triage commit + +The first version of PR #757 (corruption-triage corrections) +added 20,978 lines across 9 artifact files, including: + +```text +1,112,132 bytes rev-list-all-objects.txt (1.1 MB; 18,150 objects; + only 1 line load-bearing) + 62,860 bytes fsck-full-no-reflogs.txt (1,116 lines; ~10 lines load-bearing) + 34,987 bytes fsck-full.txt (627 lines; ~10 lines load-bearing) + 34,372 bytes fsck-connectivity.txt (620 lines; 0 lines load-bearing for the SHAs of interest) +``` + +Aaron caught the soulfile pollution before merge. Amara +provided the size-check command: + +```bash +# BSD/macOS-portable (default macOS find lacks -printf): +find docs/lost-substrate/artifacts/2026-04-29-corruption \ + -type f -maxdepth 1 -exec ls -la {} \; | awk '{print $5, $NF}' | sort -nr +# GNU equivalent (Ubuntu/CI runners): +# find docs/lost-substrate/artifacts/2026-04-29-corruption \ +# -type f -maxdepth 1 -printf '%s %p\n' | sort -nr +``` + +Corrective action: replaced the raw dumps with grep extracts +that captured only the load-bearing lines (the SHAs of +interest + per-mode line-count summary that evidences the +mode-dependence finding). Total artifact-directory size +dropped from 1.2 MB → 28 KB. + +The triage-report.md added a **re-run recipe** so a future +reader who needs the raw outputs can regenerate them locally: + +```bash +mkdir -p /tmp/corruption-triage-raw +git fsck --full --no-progress > /tmp/.../fsck-full.txt 2>&1 +git fsck --full --no-reflogs --no-progress > /tmp/.../fsck-full-no-reflogs.txt 2>&1 +git fsck --connectivity-only --no-progress > /tmp/.../fsck-connectivity.txt 2>&1 +git rev-list --objects --all > /tmp/.../rev-list-all-objects.txt 2>&1 +``` + +The raw is reproducible from any clone with the same local +refs at the same point in time. Committing the extracts +preserves the load-bearing evidence; committing the raw dumps +would dirty the soulfile. + +## Composition pattern (extract + recipe + escape hatch) + +For any "I want to capture diagnostic evidence" use case: + +| Component | What it captures | Where it lives | +|---|---|---| +| Extract | Only the load-bearing lines (grep-matched) | Committed to repo | +| Re-run recipe | The exact commands that produce the raw | Committed alongside extract | +| Conclusion / report | What the evidence proves | Committed alongside extract | +| Raw dump | Full multi-MB diagnostic output | NOT committed to soul repo | +| Raw dump (if needed) | Full multi-MB diagnostic output | Non-soul repo, git-lfs, or `/tmp` | + +The default is "extract + recipe + report"; the escape hatch +is "non-soul repo or git-lfs" — used only when the raw is +genuinely load-bearing AND too large to fit cleanly. + +## What this rule forbids + +- **No raw `git fsck` output dumps** in the soul repo. Extract + the SHA-context lines + per-mode line-count, drop the rest. +- **No raw `git rev-list --objects --all`** in the soul repo. + Extract the matching lines for the SHAs of interest. +- **No raw profile traces / flame graphs / dump files** in + the soul repo. Extract summary metrics; raw goes to a + separate location. +- **No raw build logs / CI logs** in the soul repo. Extract + the failing-step / load-bearing lines. +- **No raw network captures / strace / dtrace dumps** in the + soul repo. Extract the relevant transactions. +- **No raw conversation transcripts** in the soul repo — + unless the transcript IS the substrate (e.g., the + Amara-conversation absorb under `docs/amara-full-conversation/` + is the substrate-of-substrate per + `project_aaron_amara_conversation_is_bootstrap_attempt_1...`). + +## What this rule does NOT forbid + +- **Committed test data, fixtures, example inputs** — these + are load-bearing for the code that uses them. Live in + `tests/fixtures/`, `examples/`, etc. +- **Substrate documents** — research notes, ferries, + ledgers, ADRs, triage reports — these ARE the conclusion, + not the raw evidence behind it. +- **Conversational substrate** that is the source-of-truth + for the factory (Amara conversations, ferry texts) — + these are first-class substrate, not diagnostic noise. + +## Composes with + +- `memory/feedback_corruption_triage_discipline_object_health_incident_aaron_amara_2026_04_29.md` + — corruption-triage discipline; this rule was caught + while applying that one. +- `memory/feedback_signal_in_signal_out_clean_or_better_dsp_discipline.md` + — clean-signal discipline at the DSP layer; this rule + is the substrate-cleanliness analogue at the repo layer. + +## Distilled rules (keepers) + +```text +The git repo and all its history is the soulfile. +Every committed byte lives forever. +``` + +```text +Default: extract + recipe. Don't commit raw multi-MB dumps. +``` + +```text +Conclusion is durable. Raw evidence is re-runnable. +``` + +```text +Non-soul repo or git-lfs are escape hatches when raw is +genuinely load-bearing AND too large for the soul repo. +``` + +```text +Don't let your soul get dirty. +You control what belongs in there. +```