Root filesystem disk audit: point-in-time attribution and recommendations (v2 supersedes v1)
Report overview
Root filesystem disk audit: point-in-time attribution and recommendations (v2 supersedes v1)
Point-in-time, approximate, read-only analysis. Nothing was deleted, restarted, rotated or executed. Recommendations only. No prices or cost estimates. Generic labels only.
Figures in GiB come from the inventory-v2 scan. Byte fields suffixed _b_v1 come from an earlier v1 pass and are not mixed into GiB figures.
Headline numbers
Byte-derived df used figure.
inventory-v2 total. Hardlink duplicates are excluded.
Separate series. The allocated-minus-apparent gap is inferred.
Not counted in allocated dedup.
df used minus allocated dedup. Not yet attributed to any group.
Counts only. 1921024 bytes. Audit snapshot values above are unchanged. Later probe (16:00Z, not a replacement for the snapshot): 124 rows holding 74.8 MiB (lsof +L1).
Swap files, counted within the inventory parts.
df size 160921083904 bytes, df available 0 bytes. At the audit snapshot (taken mid-cleanup, ~14:27-14:40Z) the root filesystem was full, with 0 bytes available. A later read-only probe at 16:00Z (different time and metric) showed 16.11 GiB available, 89% used, and falling: down 245 MiB since ~15:19Z.
The v1 byte figures below come from a different pass (14:27-14:30). They are not directly comparable to the v2 GiB figures above.
| Measure (v1 pass, bytes) | Bytes |
|---|---|
| Allocated (v1) | 161921875968 |
| Apparent (v1) | 156491051863 |
| Hardlink duplicate (v1) | 2556145664 |
| Residual (v1) | 1315233792 |
Allocation inventory (GiB, v2)
- P01 lane-worktree-group
- P02 os-usr
- P03 session-checkout-group
- P04 swap-group
- P05 scratch-group
- P06 primary-checkout-1
- P07 home-config-group
- P08 agent-home-group
- P09 cache-group
- P10 venv-group
- P11 other-worktree-group
- P12 audio-group
- P13 other-root-group
- P14 os-var
- P15 os-boot
- P16 other-top-level-group
- R rounding remainder (unverified)
Ranked by size
Additivity (centi-GiB integers): parts 148.42 + remainder 0.01 = 148.43 GiB; total_gib 148.43 GiB. Result: additive.
| ID | Group | GiB | Files | Share | Confidence | Note |
|---|---|---|---|---|---|---|
| P01 | lane-worktree-group | 46.92 | 922503 | 31.6% | executor-reported | 233 lane categories merged. Largest reclaimable-candidate finding. Candidate only: ownership of each lane is unverified; do not delete without owner review. v1 listed only 26 lane categories (5.63 GiB). |
| P02 | os-usr | 13.89 | 232359 | 9.4% | executor-reported | OS and toolchain. Not a candidate. |
| P03 | session-checkout-group | 12.63 | 378764 | 8.5% | executor-reported | 2 categories merged. Agent session checkouts. |
| P04 | swap-group | 12.00 | 3 | 8.1% | verified | Two swap files, byte-verified (12884901888 B in v1 pass). Swap in use is 4.26 GiB (research note). Not a delete candidate. |
| P05 | scratch-group | 10.59 | 70208 | 7.1% | executor-reported | 8 categories merged. Scratch: bounded quota and TTL are a recommendation (R4). |
| P06 | primary-checkout-1 | 9.83 | 90763 | 6.6% | executor-reported | Live checkout. Includes user pack data (webapp packs/uploads). Never auto-deleted. |
| P07 | home-config-group | 9.82 | 177473 | 6.6% | executor-reported | 4 categories merged. Home and configuration. |
| P08 | agent-home-group | 9.55 | 118827 | 6.4% | executor-reported | 10 categories merged. Agent and tool homes. |
| P09 | cache-group | 7.26 | 78785 | 4.9% | executor-reported | Caches. |
| P10 | venv-group | 6.90 | 53345 | 4.7% | executor-reported | 4 categories merged. Virtual environments. |
| P11 | other-worktree-group | 2.89 | 99534 | 1.9% | executor-reported | 57 categories merged. Non-lane worktrees. |
| P12 | audio-group | 2.75 | 30150 | 1.9% | executor-reported | Audio assets. Storage routing is a recommendation (FLAC to StorageBox, OGG to object storage). |
| P13 | other-root-group | 2.63 | 21447 | 1.8% | executor-reported | 53 categories merged. Includes two largest-file archives. |
| P14 | os-var | 0.69 | 7188 | 0.5% | executor-reported | Variable OS data including logs. |
| P15 | os-boot | 0.06 | 577 | 0.0% | executor-reported | Boot. |
| P16 | other-top-level-group | 0.01 | 1963 | 0.0% | executor-reported | 6 categories merged. |
| R | rounding remainder (unverified) | 0.01 | — | — | unverified | Explicit 0.01 GiB rounding row so parts sum to total. Not the 67.25 GiB v1 remainder, which was a truncation artefact (v2 v1_truncation block). |
Allocated (dedup) versus apparent size
| Series | GiB | Confidence | Note |
|---|---|---|---|
| Allocated, dedup | 148.43 | measured | Additive total. Hardlink duplicates excluded. |
| Apparent | 145.75 | inferred | Separate series. Not merged with allocated. |
| Hardlink duplicates excluded | 2.38 | measured | Removed from the allocated dedup series. |
| Allocated minus apparent (difference) | 2.68 | inferred | Attributed to block rounding and sparse files. Inferred, not measured. |
Two separate series, never merged. Allocated minus apparent (2.68 GiB) is attributed to ext4 block rounding and sparse-file effects; this is INFERRED, not measured. Hardlink duplicates (2.38 GiB) are excluded from allocated_dedup. v1 pass (different snapshot): allocated 161921875968 B, apparent 156491051863 B.
*_b_v1 byte figures come from a different (v1) pass than v2 GiB. v1 bytes: allocated 161921875968 B, apparent 156491051863 B, hardlink 2556145664 B. Not mixed into the GiB figures above.
Section confidence: inferred
df reconciliation (waterfall, GiB)
Waterfall: df used 149.65 GiB − allocated dedup 148.43 GiB = 1.22 GiB residual, unattributed (stated residual 1.22 GiB).
| Entry | GiB | Confidence |
|---|---|---|
| df size | 149.87 | measured |
| df used | 149.65 | measured |
| df available | 0.00 | measured |
| Allocated dedup (v2) | 148.43 | measured |
| Hardlink duplicate (excluded) | 2.38 | measured |
| Residual (1.22 GiB), unattributed | 1.22 | unattributed |
| Unexplained gap (0.07 GiB) | 0.07 | unverified |
| Deleted-open files | 0.00 | measured |
| Swap files (in P04) | 12.00 | verified |
Overlapping rollups
Each row is a separate view of the same bytes. Do not add rows together.
| Label | GiB | Files | Additive |
|---|---|---|---|
| lane-worktree-group | 46.92 | 922503 | false |
| tooling-group | 35.41 | 7692 | false |
| venv-group | 22.52 | 234289 | false |
| session-checkout-group | 12.63 | 378764 | false |
| swap-group | 12.00 | 2 | false |
| cache-group | 7.55 | 84213 | false |
| audio-group | 7.44 | 3385 | false |
| node_modules-group | 7.29 | 256941 | false |
| git-group | 1.81 | 40654 | false |
| pycache-group | 1.48 | 69279 | false |
| logs-group | 0.45 | 7364 | false |
Extensions (ranked by GiB)
Ranked by allocated GiB. Extension is taken from the filename, not from file content.
Modified-time histogram (PROXY ONLY)
PROXY ONLY, not growth, no time series. Last-write, create, copy or touch time; not a growth measure.
Months sum to 148.36 GiB against 148.43 GiB allocated: a 0.07 GiB gap. Cause not measured (unverified).
Largest entries (redacted)
Redacted: leaf names are withheld. Columns are rank, GiB, type and directory group only.
| Rank | GiB | Type | Dir group |
|---|---|---|---|
| 1 | 8.00 | (none) | swap-group |
| 2 | 4.00 | (none) | swap-group |
| 3 | 1.34 | zip | other-root-group |
| 4 | 0.84 | so | cache-group |
| 5 | 0.84 | so | scratch-group |
| 6 | 0.84 | so | venv-group |
| 7 | 0.53 | 9 | cache-group |
| 8 | 0.53 | 9 | scratch-group |
| 9 | 0.53 | 9 | venv-group |
| 10 | 0.50 | 13 | os-usr |
| 11 | 0.50 | zip | scratch-group |
| 12 | 0.50 | so | cache-group |
| 13 | 0.50 | so | scratch-group |
| 14 | 0.50 | so | venv-group |
| 15 | 0.42 | so | os-usr |
| 16 | 0.42 | so | os-usr |
| 17 | 0.41 | 12 | cache-group |
| 18 | 0.41 | 12 | scratch-group |
| 19 | 0.41 | 12 | venv-group |
| 20 | 0.41 | so | cache-group |
| 21 | 0.41 | so | scratch-group |
| 22 | 0.41 | so | venv-group |
| 23 | 0.41 | so | audio-group |
| 24 | 0.39 | so | os-usr |
| 25 | 0.34 | (none) | home-config-group |
| 26 | 0.34 | (none) | home-config-group |
| 27 | 0.33 | zip | scratch-group |
| 28 | 0.28 | (none) | home-config-group |
| 29 | 0.28 | (none) | os-usr |
| 30 | 0.27 | (none) | agent-home-group |
Worktrees
| Metric | Value |
|---|---|
| Lane groups (v2) | 233.00 |
| Lane GiB | 46.92 |
| Git linked worktree dirs | 587.00 |
| Git main repo dirs | 120.00 |
| Listed by git worktree list | 301.00 |
| Status | unreconciled |
| Classification | candidate lane groups; ownership unverified; no deletion without owner review (derived from p2 note) |
Dependencies
Edges are the recorded dependency intent (configured, policy or observed). Node state is what was last recorded for each node. Nothing here was probed live.
Edge confidence
- verified (solid line)
- inferred (dashed line)
- unverified (dashed line)
Node state
Failed
- Gateway app (API origin service) — FAILED, disabled; cause unestablished (observed, runtime notes §8)
- Host resource guard — service FAILED; 2-minute timer active (observed)
Stopped or disabled
- Scaler service — inactive, disabled (observed, runtime notes §8)
- Local worker service (stale unit) — inactive, disabled (observed, runtime notes §8); absence consistent with enqueue-only policy
Not inspected (policy only)
- Postgres job queue — not inspected (policy-described only)
- S3-compatible object storage — not inspected (policy-described only)
- Archival storage box (FLAC) — not inspected (policy-described only)
- Cloudflare Pages frontend — not inspected; reported unaffected by gateway loss (policy)
Live or observed active
- Primary live checkout — live; installed units target it (observed); parity with repo unverified
- Reapers (request-path, startup, timer-driven) — request-path and startup triggers configured; tmp sweep timer active (observed); no local packs/projects reaper
Configured (static read)
- Scratch writers (TEMP, export cache, jobs) — writers configured (static read); no TTL or quota bound today
Shutdown impact
Derived only from recorded edges. A node with no recorded dependents is not proof that nothing depends on it.
| Node | State | Depends on | If it stops, loses function for |
|---|---|---|---|
| Gateway app (API origin service) | FAILED, disabled; cause unestablished (observed, runtime notes §8) | Postgres job queue (enqueues jobs to job_queue); S3-compatible object storage (OGG routed to S3-compatible storage); Archival storage box (FLAC) (FLAC routed to archival storage box); Scratch writers (TEMP, export cache, jobs) (TEMP, export cache and jobs writers); Primary live checkout (installed unit WorkingDirectory targets live checkout (parity unverified)) | Cloudflare Pages frontend: /api/* proxied to API-origin gateway [inferred]; Scaler service: depends on gateway [inferred]; Reapers (request-path, startup, timer-driven): reapers run only from gateway startup and request paths [inferred] |
| Scaler service | inactive, disabled (observed, runtime notes §8) | Gateway app (API origin service) (depends on gateway); Postgres job queue (depends on job queue) | No recorded dependents in evidence. |
| Local worker service (stale unit) | inactive, disabled (observed, runtime notes §8); absence consistent with enqueue-only policy | Postgres job queue (claims jobs in Modal-side workers; no local worker by enqueue-only policy) | No recorded dependents in evidence. |
| Postgres job queue | not inspected (policy-described only) | None recorded. | Gateway app (API origin service): enqueues jobs to job_queue [inferred]; Scaler service: depends on job queue [inferred]; Local worker service (stale unit): claims jobs in Modal-side workers; no local worker by enqueue-only policy [inferred] |
| S3-compatible object storage | not inspected (policy-described only) | None recorded. | Gateway app (API origin service): OGG routed to S3-compatible storage [inferred] |
| Archival storage box (FLAC) | not inspected (policy-described only) | None recorded. | Gateway app (API origin service): FLAC routed to archival storage box [inferred] |
| Cloudflare Pages frontend | not inspected; reported unaffected by gateway loss (policy) | Gateway app (API origin service) (/api/* proxied to API-origin gateway) | No recorded dependents in evidence. |
| Primary live checkout | live; installed units target it (observed); parity with repo unverified | None recorded. | Gateway app (API origin service): installed unit WorkingDirectory targets live checkout (parity unverified) [verified]; Host resource guard: reclaim touches build outputs and pycache only (configured list) [inferred] |
| Reapers (request-path, startup, timer-driven) | request-path and startup triggers configured; tmp sweep timer active (observed); no local packs/projects reaper | Gateway app (API origin service) (reapers run only from gateway startup and request paths) | No recorded dependents in evidence. |
| Host resource guard | service FAILED; 2-minute timer active (observed) | Primary live checkout (reclaim touches build outputs and pycache only (configured list)) | No recorded dependents in evidence. |
| Scratch writers (TEMP, export cache, jobs) | writers configured (static read); no TTL or quota bound today | None recorded. | Gateway app (API origin service): TEMP, export cache and jobs writers [inferred] |
Installed services: wiring and shutdown impact
Services on this host and how they connect. Confidence: M = measured, C = config-evidenced, I = inferred.
| Unit | State on this host | ExecStart or role | Port / route | Local data deps | Remote deps (names only) | Shutdown impact | Confidence |
|---|---|---|---|---|---|---|---|
| producehits-app | disabled; nothing listens on port 7860 | role: FastAPI gateway on the API origin | port 7860 (no listener) | none recorded | PostgreSQL job queue; Hetzner Cloud API via scaler | none on this host (already not serving) | M |
| producehits-scaler | disabled | role: provisions auto-scaled workers | none | none recorded | PostgreSQL job queue; Hetzner Cloud API | none on this host while disabled | M |
| producehits-worker | disabled | role: job worker daemon; hostname guard refuses to start on this host | none | none recorded | PostgreSQL job queue; Modal GPU | none on this host (disabled by policy) | M |
| nginx | songchop.com and api-origin.songchop.com forward to 127.0.0.1:7860, which refuses connections (curl exit 7) | role: reverse proxy and static file server (config) | songchop.com and api-origin.songchop.com to 7860; other product domains serve static files from the coming-soon web root | coming-soon web root (static files) | none | stopping would not change the 7860 refusal; static product domains go offline | C (routes); public 502 is I (inferred, not curl-verified) |
| producehits-boot-selfheal.service | enabled; unit body not read | role: boot-time self-heal unit (unit body not read, so role is unverified) | none recorded | unverified | unverified | Unverified | M (enabled, unit list); body unverified |
| producehits-host-guard | timer-driven guard; earlier audit recorded the service as failed with timer active; current state not re-verified | role: timer-driven host guard | none | unverified | none recorded | unverified; prune call sites appear commented out around lines 293-294 (unverified) | M (earlier audit); current state unverified |
| cx-console-tunnel | unit disabled; a cloudflared process started 2026-10-17 is still running from a deleted binary named cloudflared.old | role: tunnel to console hosts | ingress covers history and console hosts on 8898, /mission on 8899 (not listening), files on 8081 | none recorded | Cloudflare tunnel edge | restart would swap in the new binary; owner decision, not taken here | M (process); C (ingress) |
| cx-sync | timer every 10 minutes; last run 15:04 UTC succeeded | role: pushes an encrypted credential blob via the cx sync script | none | encrypted credential blob (contents not read) | remote sync target (name only, not recorded here) | pauses sync pushes; no local service impact | M |
| producehits-backup | daily timer at 03:30 UTC; deploy/backup-to-storagebox.sh; systemd reports exit 0 at 03:34:54 UTC | deploy/backup-to-storagebox.sh: writes bundle, tarball and encrypted env to a remote storage box; also uploads a credentials archive (operator credentials and keys) and a checksum list to the dated code-backups folder; keeps 14 dated dirs | none | bundle, tarball, encrypted env, credentials archive, checksum list | remote storage box (name only) | I: a failed run would leave no new off-host copy until the next run (upload unverified) | M (exit 0); upload unverified: journal has no entries for the day |
| temp Postgres lane | running on port 54329 from /tmp/lane-pg; not a system unit; 101 MB | role: temporary Postgres instance | port 54329 | /tmp/lane-pg data directory (101 MB) | none | stopping drops only this temporary lane data | M |
| producehits-sync | not a unit, timer or cron; name is only a cache directory | used by deploy/dev-sync.sh:8 and deploy/go-live.sh:9 in manual operator flows | none | cache directory only | none | none (no running process) | I |
Documentation conflicts
- API origin is named as 138.201.247.255 in AGENTS.md and CLAUDE.md, but as 49.12.12.91 in docs/ops/2026-10-15-old-server-deletion-audit.md:164-171, where that box is recorded as deleted.
- DB host is 46.224.109.170 (public) and 10.88.0.20 (private); docs agree with shared/scaler.py:71-74.
- 188.245.228.253 appears only as a fallback default in shared/scaler.py:74, deploy/worker_start.sh:31, deploy/scaler_start.sh:21 and deploy/provision_dedicated_worker.py:42. No doc names it.
- This host is named nowhere in the documented topology.
Installed vs source
- Installed tree is on main with 8 uncommitted entries; the session branch is clean.
- Four shared files differ between installed tree and source: audio_source.py, routes.py, worker_daemon.py, and one migration.
- The venv python reports 3.14.4 while its config records 3.12.3 (M).
Unverified
- Public 502 on songchop.com and api-origin.songchop.com (inferred from the refused 7860 connection, not curl-verified).
- Whether the daily backup upload to the remote storage box succeeded (journal has no entries for the day).
- Current state of producehits-host-guard (earlier audit recorded failed with timer active).
- Working directories for each unit, except the sampled ones.
- Owner of the true gateway behind the documented API origin addresses.
Writers, triggers, retention and measured growth
| Category | Writer | Trigger | Retention enforced | Current activity (mtime proxy) | Growth evidence | Confidence |
|---|---|---|---|---|---|---|
| Worktrees (lane and session) | Lane and session tooling; creator not traced | Autosave timer (producehits-session-autosave); creation trigger unverified | prune.sh:33: git worktree remove only when the worktree is under the session folder, clean, landed, idle 24 h or more (default), and has no process inside. This is the only worktree removal path found. | Not measured (sizes only) | Lanes 47.37 GiB over 233 roots (prior 46.92; +0.45, cause not established). Sessions 10.13 GiB over 54 roots. Other roots 12.21 GiB (derived). | measured |
| Session and artifact dirs | Pi and CX session logs (~/.pi/agent/sessions, ~/cx-sessions) | Each session run (inferred) | None found. | ~/.pi/agent/sessions: 786 files changed in 24 h. ~/cx-sessions: 63,695 files changed in 24 h (766.93 MB). | ~/.pi/agent/sessions 279.7 MB; ~/cx-sessions 2.9 GB (2,945.3 MB). No prior baseline, so growth is not measured. | inferred |
| Harness homes | ~/.config/cx/harness-homes, written by harness runs | Harness launch and session activity (inferred) | host-guard prune_stale_harness_dirs (claude-*, opencode; over 120 min) at host-guard:242. Its call sites at :293-294 are commented out, so pruning is probably inactive (unverified). | 16,642 files changed in 24 h (288.57 MB) | ~/.config/cx/harness-homes 997.8 MB now. No prior baseline. | inferred |
| Caches (~/.cache, uv) | Package and tool caches. Largest entry is puccinialin (747 MB). uv lives in ~/.local/share/uv. | Tool invocation (inferred) | host-guard reclaim_disk:254-260 removes ~/.cache/uv on the disk-pressure branch. Same branch also removes /var/crash/*.crash. The threshold and whether the call runs are unverified. No age rule. | ~/.cache: 89 files (8.42 MB) changed in 24 h | ~/.cache 7,378,226,887 B to 944,160,768 B (-6.0 GiB, measured). ~/.local/share/uv 1,504,415,086 B to 1,653,059,584 B (+141.8 MiB, measured). The ~/.cache drop has no per-subdirectory baseline. | measured |
| Journal and /var/log | systemd-journald; services through logrotate (cx-proxy, nginx, rsyslog, ufw, others) | Continuous logging; logrotate.timer last ran at 00:23 today | afcut-host-cleanup.sh:11 runs journalctl --vacuum-size=200M daily (last run 00:23:50 freed 50M from the current dir). logrotate: weekly, rotate 4; cx-proxy size 25M, rotate 3. Logrotate caps by file count, not total size. No rule removes the old machine-id dir. | /var/log: 22 files (29.80 MB) changed in 24 h | journalctl --disk-usage reports 24M (current dir only); du reports 145M for the whole tree. Old dir is 113M, dated 2026-10-13, and not counted. /var/log 206.7 MB. Caps: SystemMaxUse 200M (20-producehits-cap.conf:3) and 500M (size-cap.conf:2). Effective 500M is inferred from file order. | measured |
| /tmp and /var/tmp (/tmp is tmpfs) | Session and tool scratch (lane-pg, packmobile, dbprobe, multiselect-chrome, lane-node, jiti) | Tool runs (inferred) | afcut-host-cleanup.sh:14-17 deletes top-level /tmp entries older than 2 days, excluding claude-*, tmux-*, systemd-private-*, jiti and node-compile-cache. host-guard prune_tmp_caches and scratch pruning (:195-205) are inactive at :293-294 (unverified). tmpfiles tmp.conf:9-14 is exclude-only; there is no age rule for /tmp or /var/tmp. producehits-tmp-sweep thresholds are unread. | /tmp: 6,969 files changed in 24 h (241.39 MB). /var/tmp: 3 files (0.13 MB). | /tmp 332.0 MB. /var/tmp 216.4 MB with only 3 recent files and no age rule. Largest /tmp entries: lane-pg 101M, packmobile 65M (two dirs), dbprobe 45M. No prior baseline. | measured |
| 1G tmpfs under home (~/fill.UDRv/tmp) | Unknown | Unknown | None found. Separate filesystem; not in the / used count. | Not measured | 1G tmpfs size (per brief); seen in findmnt; contents not measured. | unverified |
| Temp Postgres lane (/tmp/lane-pg) | Unknown; likely the temp Postgres lane (inferred) | Lane start (unverified) | None found. The 2-day /tmp rule applies only if the entry is older than 2 days (not checked). | Not measured | 101 MB (du). Liveness on port 54329 not checked. | unverified |
| Backups and build outputs | producehits-backup (remote dated dirs; also uploads a credentials archive and a checksum list to the dated code-backups folder; upload success unverified); build output $REPO/dist and __pycache__ | producehits-backup timer; build runs | backup-to-storagebox.sh: KEEP=14 newest dated dirs (:34); remote rm at :173; local stage and credential cleanup by trap (:55, :101). reclaim_disk removes $REPO/dist and __pycache__ under disk pressure (:254-260). | Not measured | Not measured. Remote backup state not checked. | config-evidenced |
| Large tool installs (sizes only) | Editor server, sound bench, hermes, codex, grok sessions | Tool use (inferred) | None found. | ~/.hermes: 151 files (4.69 MB) in 24 h. ~/.codex: 1 file. ~/.vscode-server and ~/sound-bench: none in 24 h. ~/.grok/sessions: none. | ~/.vscode-server 4,213.4 MB; ~/sound-bench 2,975.9 MB; ~/.hermes 3,506.4 MB; ~/.codex 2,443.0 MB; ~/.grok/sessions 1,225.2 MB. No growth baseline. | measured |
Facts
- Free space: 4.1 GiB available at 14:56Z; 16.4 GiB now (+12.3 GiB available). About 12.6 GiB of used space was reclaimed. Attribution is unexplained.
- Audit-time used figure: 149.65 GiB (earlier report, at audit time). df used was 127.4 GiB at ~15:19Z and 127.6 GiB at 16:00Z. These are different times; do not equate them.
- Used-space gap: df used 127.4 GiB against home plus /var at 101.3 GiB leaves 26.1 GiB unattributed by that sum. Reserved blocks (6.1 GiB) affect Avail only and are not part of this gap.
- Deleted-but-open files: 36.7 MiB across 4 processes (fail2ban-server, chrome, bash, filebrowser). Target paths not verified.
- Disk-free log (/tmp/disk-free-log.txt, mtime 14:56): its counters record 0 bytes freed for every itemized step. Manual agent deletions on the audit day are the candidate writer of the observed free-space change (INFERRED, partly unverified; no per-path bytes re-measured; the audit allocated-size metric and df-used are different metrics, so delta attribution is not established).
Gaps (unexplained)
- Writer of /tmp/disk-free-log.txt is not found in the searched trees. Its 100M vacuum counter matches no configured vacuum (scripts use 200M).
- About 6.6 GiB of the 12.6 GiB reclaimed has no candidate after the ~/.cache drop (6.0 GiB, measured, unattributed); candidate is manual agent deletions on the audit day (inferred).
- The ~/.cache shrink has no per-subdirectory baseline; the prior snapshot itemizes about 29 MB of 7.38 GB.
- About 9.7 GiB of the 26.1 GiB used gap remains after a 2.4 GiB ext4 metadata estimate (inode size assumed, not measured). Data hidden under mountpoints and stale superblock counters are not checked.
- Journal: journalctl reports 24M, du reports 145M. The old machine-id dir (113M) is not counted, and no rule removes it.
- Effective journald cap (500M rather than 200M) is inferred from file order and has not been checked with systemd-analyze.
- Lane worktree +0.45 GiB has no established cause. The prior 587 dirs and 120 repos are not reconciled; re-measured as 300 linked plus 1 main checkout over 301 roots.
- host-guard prune functions and reclaim_disk: call sites at :293-294 are commented out. Whether any of them run, and the disk threshold, are unverified.
- producehits-tmp-sweep thresholds are unread, so what it deletes is unknown.
- /var/tmp holds 216.4 MB with 3 recent files and no age rule. Its contents and writer are not checked.
- /tmp/lane-pg liveness is not checked. The writer of the 1G tmpfs under home (~/fill.UDRv/tmp) is unknown.
- node_modules and venv share is unverified (find at depth 3 returned 0).
- Root crontab is empty. The contents of /etc/cron.d (certbot, e2scrub, producehits-*) were not read.
- Merged and pushed status of the largest worktrees is not checked.
Sizes are apparent bytes. The mtime proxy shows recent writes only, not growth.
Migration
RECOMMENDATION ONLY: each item is a proposed change, not an instruction.
| ID | Title | Kind | Status | Blockers | Tag |
|---|---|---|---|---|---|
| M-1 | Clock-gate the three existing reapers on one gateway timer | code | recommended | R4 bounded scratch quota and TTL should land first (sequencing) | RECOMMENDATION ONLY |
| M-2 | Opt-in local pack/project reclaim after remote manifest and object checks | code | blocked | Founder retention policy for pack and upload directories (retention.md §6); research-first (F4) | RECOMMENDATION ONLY |
| M-3 | Parameterize the host resource guard (statvfs, env thresholds, CRIT names pack and upload dirs) | code | recommended | F3: diff installed host guard against repo copy first | RECOMMENDATION ONLY |
| R1 | Neutralize the stale worker unit after owner confirms its state | ops | recommended | Owner confirmation of stale worker unit state | RECOMMENDATION ONLY |
| R2 | Ensure gateway and scaler units are enabled and revivable | ops | blocked | F1: gateway FAILED cause not established; research before any restart | RECOMMENDATION ONLY |
| R3 | Clock-gate existing reapers (alias of M-1) | code | alias | See M-1 | RECOMMENDATION ONLY |
| R4 | Bounded scratch quota and TTL over TEMP, export cache, jobs and projects roots | code | recommended | F2: checkout parity and stray tmpfs origin (BLOCKS R4) | RECOMMENDATION ONLY |
| R5 | Opt-in local pack/project reclaim (alias of M-2) | code | blocked | Founder retention policy (see M-2) | RECOMMENDATION ONLY |
| R6 | Parameterize host resource guard (alias of M-3) | code | alias | See M-3 | RECOMMENDATION ONLY |
| LW-1 | Lane-worktree reclaim candidate (~46.9 GiB executor-reported, 233 categories) | ops | candidate, ownership unverified, owner review required | Per-lane ownership not verified; Owner review required before any action | RECOMMENDATION ONLY |
Principles
- Cutover: stage each change beside the running system, verify it, then switch over in one documented step.
- Rollback: keep the previous artifact and its config; restore it, re-run the verifier, and record the outcome.
- Bounded scratch quotas with TTL on TEMP, export cache, jobs and projects roots.
- FLAC goes to the archival StorageBox.
- OGG goes to S3-compatible object storage.
- WAV is produced on demand, not stored by default.
- SANCT stays intact: no change to its components or its data paths.
- No processing worker on the gateway; processing runs on workers only.
- No prices appear in this report.
- Nothing executed by this report: every item below needs its own owner decision and execution.
Coding-only relocation checklist and bounded lifecycle plan
Labels: measured = command output in the topology notes; config-evidenced = unit, script or file text; inferred = reasoned from those, not checked.
Part A. Coding-only host: relocation checklist
| Item | Current state | Decision | Label | Notes |
|---|---|---|---|---|
| producehits-app (uvicorn gateway, loopback 7860) | Disabled; nothing listens on 7860 | MOVE (already absent here; live copy is on the gateway) | measured | Nothing to move. Remove the dependent nginx routes here. |
| producehits-scaler | Disabled | MOVE-CHECK: confirm exactly one active scaler remotely before any change | measured | Docs place the only scaler on the gateway. Do not start a second one here. |
| producehits-worker | Disabled | STAYS DISABLED (repo law: no worker on the API origin) | measured | Workers are auto-scaled Hetzner instances. None run here. |
| nginx routes for songchop.com and www | Proxy to 127.0.0.1:7860 (dead upstream) | REMOVE after gate 1 (DNS confirmed to the gateway) | config-evidenced | Other product domains serve static coming-soon roots. Leave them unless the owner decides otherwise. |
| nginx route for api-origin | Proxy to 127.0.0.1:7860 (dead upstream) | REMOVE after gate 2 (gateway route verified from outside) | config-evidenced | Save the site file first (cutover C1). |
| nginx /screenfeed/ and /beat-bench/ routes | /screenfeed/ targets 127.0.0.1:8899, which is not listening | OWNER DECISION: remove or keep | measured | Port 8899 not listening at sample time. |
| producehits-backup (timer, nightly 03:30 UTC) | Exit 0 at 03:34:54 UTC today; journal has no entries for the unit today | MOVE to the remote gateway, or STAY with a recorded decision. Do not disable without a replacement | config-evidenced | The only off-GitHub backup in the config read. Run output unverified. |
| Local database dependencies | Production DB is remote (46.224.109.170, per docs). A temporary Postgres lane runs on loopback 54329 | Production path: none local. Temporary lane: DISCARD after owner confirms | measured | The temporary lane is not production. Its data directory sits in a temp folder. |
| cx-proxy and cx-claude-failover | Enabled and running (loopback 8327 and 8317) | STAY | measured | Breaking them breaks local CX model routing. |
| cx-console tunnel (cloudflared) | Unit disabled; process running from a deleted binary | STAY | measured | Four public hostnames route through it. The /mission route is already dead. |
| cx-sync timer (every 10 min) | Active | STAY | config-evidenced | Cross-machine credential sync. Not a product dependency. |
| Git worktrees | 301 entries in the worktree list | STAY | measured | Lifecycle is in Part B. Nothing is removed by this plan. |
| pi sessions and graphify | Tooling state and knowledge graph | STAY | config-evidenced | Project graph is rebuilt by the owning tooling. |
| producehits-sync, go-live.sh, dev-sync.sh | No unit or timer; manual scripts. Headers read, bodies not read | STAY (not moved). Do not run | measured | go-live.sh deploys the studio. Read its body before any use. |
Dependency order (sequence, not executed)
- Gate 0: close topology questions T1 to T4 (table below) by written receipts.
- Gate 1: DNS for api-origin and songchop.com resolves to the one confirmed gateway address (check R1, run from outside this host).
- Gate 2: the gateway serves the API route to a check from outside (check R2).
- Gate 3: exactly one scaler process and one autopull timer on the gateway (checks R3, owner-run).
- Gate 4: a fresh remote backup exists and its run is logged (check R4, owner-run, not from this seat).
- Gate 5: rollback copies exist: nginx site files and the backup timer unit saved to a dated folder (cutover C1).
- Cutover C2 to C6 (below). Only after gates 0 to 5 pass in writing.
- Part B configuration (journald, logrotate, tmpfiles, observability) is independent of the cutover. Apply after gate 0 with owner approval.
| Check | Command (NOT RUN) | Pass criterion | Run by |
|---|---|---|---|
| R1 | dig +short api-origin.songchop.com, then dig +short songchop.com (run from outside this host) | Both return the single confirmed gateway address | Owner or delivery worker |
| R2 | curl -s --proto-default https -o /dev/null -w '%{response_code}' api-origin.songchop.com/api/product-config (from outside) | Returns 200 | Owner or delivery worker |
| R3 | On the gateway: systemctl is-active producehits-scaler; pgrep -fc 'shared/scaler.py'; systemctl list-timers --all | Scaler active; pgrep count 1; exactly one autopull timer listed | Owner (gateway access) |
| R4 | On the backup target: list the newest dated folder. On the backup host: journalctl -u producehits-backup since today | Dated folder from the last run; a journal entry for the run | Owner (not from this seat) |
| R5 | On the gateway: grep -c '^DB_PRIVATE_HOST=' <gateway checkout>/.env (counts lines only; never print values) | Count 1. Confirms the private DB host is set, not defaulted | Owner (gateway access) |
| R6 | On this host: ss -ltnp | grep -c ':7860 ' ; nginx -t (after any edit) | Count 0 before and after cutover; nginx -t passes | Worker at cutover |
| R7 | On this host: systemctl is-enabled producehits-app producehits-scaler producehits-worker | All three report disabled | Worker at cutover |
Cutover steps (later)
- C1: copy the nginx site files and the backup timer unit to a dated folder (to be created). Gate 5.
- C2: remove the songchop.com, www and api-origin proxy blocks from the nginx site files. Keep the static roots. Run nginx -t.
- C3: reload nginx only after C2 passes nginx -t.
- C4: disable producehits-backup.timer only if the backup has moved, and only after the new host has completed one successful run.
- C5: run R6 and the public check from outside. Record the result in the cutover receipt.
- C6: write the disposition receipts for every item touched.
Rollback steps (prepared before cutover)
- RB1: restore the saved nginx site files, run nginx -t, then reload nginx.
- RB2: if C4 was applied, re-enable and start producehits-backup.timer.
- RB3: if DNS was changed by the owner, revert the records to the values recorded at gate 1. This plan takes no DNS action itself.
- RB4: remove the Part B drop-in files and restore the prior journald settings. The effect follows the next journald configuration load.
| Service | Impact if stopped or removed | Label | Source note |
|---|---|---|---|
| producehits-app, producehits-scaler, producehits-worker | None here (no listener, disabled) | measured | Units disabled; port 7860 closed. |
| nginx (stop or reload) | Static coming-soon sites and /screenfeed/ on this host stop answering | config-evidenced | Whether those domains still resolve here was not checked. |
| nginx routes for songchop.com and api-origin | Responses change from the dead upstream to whatever the removal yields | inferred | Not verified with a public request. |
| cx-console tunnel | Four public hostnames go dark | config-evidenced | The /mission route already fails because 8899 is not listening (measured). |
| cx-proxy and cx-claude-failover | Local CX model routing breaks | inferred | Not tested. |
| cx-sync timer | Cross-machine credential sync stops | config-evidenced | Timer every 10 minutes. |
| producehits-backup timer | Nightly off-GitHub backup stops | config-evidenced | Today run exit 0, journal empty (measured). Run contents unverified. |
| Temporary Postgres lane (loopback 54329) | Temporary test data stops | measured | Not production. Discard only after owner confirmation. |
| certbot (failing since 2026-10-14) | Not changed by this plan | config-evidenced | Separate task. Listed so it is not mixed into the cutover. |
Unresolved topology questions blocking the plan
| # | Question | What the docs say | What was measured | Blocks |
|---|---|---|---|---|
| T1 | Which gateway host is current? | Repo law and CLAUDE.md name 138.201.247.255 as the API origin. The infrastructure reference names 49.12.12.91 as the live gateway and scaler. The 2026-10-15 deletion audit says the older box is deleted. | Nothing listens on 7860 here. The gateway address was not checked from this host. | Gate 1, every nginx route change |
| T2 | What are the DB host defaults? | Production DB 46.224.109.170 is consistent across docs. Private 10.88.0.20 is the default when a network id is set. 188.245.228.253 is the fallback default in scaler code and three deploy scripts. No doc names it as a current host. | No environment dump was taken. Whether a running environment uses 188.245.228.253 is unknown. | Any move of the scaler or workers; the local-DB answer |
| T3 | Is this host in the documented topology? | The migration audit names this coding host as the migration target (cutover 2026-10-13). The topology docs do not list it as a role. | Hostname measured. Role inferred as coding and CX host. | Host-role decisions in Part A; which backup stays |
| T4 | Did the backup run and what did it upload? | Timer config keeps 14 dated folders. Unit exit 0 at 03:34:54 UTC today. | Journal has no entries for the unit today (journal not short of space). Upload unverified. | Gate 4 and the backup move |
Part B. Bounded lifecycle: coding host
| Category | Executable owner (unit or script) | Limit / quota | TTL | Success cleanup | Failure cleanup | Startup cleanup |
|---|---|---|---|---|---|---|
| Scratch | afcut-host-cleanup.sh (existing; top level of /tmp only, older than 2 days). producehits-tmp-sweep (existing; thresholds not read, unverified). producehits-host-guard prune_tmp_caches (existing; its call sites are commented out, so activity unverified). New dedicated scratch folder: to be created | 2 GiB cap on the dedicated scratch folder (proposed, to be created) | 2 days (proposed; matches the existing /tmp rule) | Each job deletes its own scratch subfolder on exit (to be created, same trap pattern as the backup script) | Failed-job folders kept for the TTL for diagnosis, then removed by the tmpfiles age rule (to be created) | systemd-tmpfiles-clean.timer (existing; daily at 13:51 UTC, measured). No /tmp age rule exists |
| Logs | systemd-journald with a single drop-in (to be created; two conflicting caps exist today, 200M and 500M, effective 500M inferred). logrotate.timer for file logs (existing). afcut-host-cleanup vacuum (existing; 200M) | SystemMaxUse 200M; SystemMaxFileSize 50M; SystemKeepFree 2G (proposed) | MaxRetentionSec: 2week (existing in the 200M cap drop-in) | Not applicable | Journal vacuum under pressure. Align the afcut-host-cleanup vacuum target to 200M so the two agree | journald applies its caps on start. Writer of the disk-free log file is unknown: Parked |
| Caches | afcut-host-cleanup.sh (existing; apt-get clean). producehits-host-guard reclaim_disk (existing; conditional; disk threshold unverified; covers uv, copilot, npm and node-gyp caches). cx-sync (temporary files only). Per-cache cap: to be created | 2 GiB per cache (proposed). Current sizes of the caches not measured | 14 days idle (proposed; no existing rule) | None | reclaim_disk on disk pressure (existing; threshold unverified) | None |
| Workspaces and worktrees | producehits-session-autosave prune.sh (existing; the only automatic remover; manual agent deletions also occurred on the audit day, see Cleanup forensic verdict). It removes a worktree only when it sits under the session folder, is clean, has no unlanded commits against the land or live branch, has been idle at least SESSION_PRUNE_IDLE_HOURS (default 24), and has no process whose working directory is inside it | No count-based deletion. 301 entries measured; report-only candidate list (below) | 24 hours idle (existing guard default; configured value unverified) | prune.sh only, after the guard passes. Its owner must dispose each family first | No automatic cleanup. Dirty or unlanded worktrees go to dispositions (Parked by default) | None |
Part B. Bounded lifecycle: future service VPS
| Category | Executable owner (unit or script) | Limit / quota | TTL | Success cleanup | Failure cleanup | Startup cleanup |
|---|---|---|---|---|---|---|
| Scratch | To be created. The app has a temp-folder setting (name only); no unit or script cleanup was measured for it on the service host | 2 GiB cap (proposed) | 2 days (proposed) | Each job deletes its own subfolder on exit (to be created) | Failed folders kept for the TTL, then removed by the tmpfiles age rule (to be created) | systemd-tmpfiles-clean.timer, if present on the service image (unverified) |
| Logs | systemd-journald drop-in (to be created; no cap found in the config read). producehits-app journal output (existing unit; memory-guard drop-in in config). Scaler and autopull logs in journald | SystemMaxUse 1G; SystemMaxFileSize 100M; SystemKeepFree 2G (proposed; the disk size of the service host is not measured) | MaxRetentionSec: 2week (proposed) | Not applicable | Journal vacuum under pressure (to be created) | journald applies its caps on start |
| Caches | To be created. No cache owner was measured on the service host | 2 GiB per cache (proposed) | 14 days idle (proposed) | None | Pressure-driven clean-up (to be created) | None |
| Workspaces and working files | To be created. The service host should hold no git worktrees (proposal). Any working audio files follow the storage routing law and are owned by the app | To be created (no measured size) | To be created | Owned by the app; cleanup after the job reaches its final state (to be created) | Failed jobs kept for the TTL, then removed (to be created) | None |
| Backup staging | producehits-backup (existing; local staging removed by its exit trap). Remote dated folders kept at 14 by the script (existing) | Local staging: one run only | Remote: 14 dated folders (existing) | Local staging removed by the trap | Local staging removed by the trap | None. Remote storage is not counted as local retention |
Remote storage (backup box, object storage) is not retention without ownership, limits, TTL and cleanup. It is not counted as capacity for any category above.
Settings for journald, logrotate, tmpfiles and caches
| Setting | Coding host | Service VPS | Basis |
|---|---|---|---|
| journald cap (SystemMaxUse) | Single drop-in at 200M. Remove the 500M size-cap drop-in so one value wins | 1G (proposed) | Two conflicting drop-ins measured on the coding host; service host cap not found |
| journald file size (SystemMaxFileSize) | 50M (existing value) | 100M (proposed) | Existing coding-host drop-in |
| journald free space (SystemKeepFree) | 2G (existing in size-cap drop-in) | 2G (proposed) | Existing coding-host drop-in |
| journald retention (MaxRetentionSec: 2week) | 2week (existing) | 2week (proposed) | Existing coding-host drop-in |
| logrotate, file logs | weekly, rotate 4 (default config, measured); cx-proxy size 25M, rotate 3 (existing) | weekly, maxsize 25M, rotate 4, compress (proposed) | logrotate caps by count only, so pair the count with maxsize |
| tmpfiles scratch rule | Dedicated folder with age rule: e /var/tmp/producehits-scratch 0750 root root 2d (to be created) | Same line, owned by the service user (to be created) | No age-delete rule exists for /tmp or /var/tmp today; do not add one for all of /tmp |
| Cache TTL rule | Age rule for each cache folder at 14 days (to be created) | Same (to be created) | No cache age rule exists today |
Observability
| Metric | Threshold and cadence | Alert level | Owner |
|---|---|---|---|
| Filesystem free space (df) | Check every 15 minutes. Alert on used percentage: warn 80%, alert 90%, critical 95% | warn, alert, critical as stated | Owner to be created |
| Inode use (df -i) | Check every 15 minutes. Warn 80%, alert 90%, critical 95%. Current inode use not measured | warn, alert, critical as stated | Owner to be created |
| du snapshot | Daily at 04:45 UTC on both hosts, and before and after any cleanup | Report only | Owner to be created |
| Per-category byte metrics | Daily, from the du snapshot: scratch, journal, file logs, caches, worktrees, pi sessions, backup staging | Report only; alert if a category grows 50% in a day (proposed) | Owner to be created |
| Report-only candidate list | Weekly. Lists worktrees and folders that meet the existing guard criteria or exceed 1 GiB and are older than 7 days. Deletes nothing | Report only | Owner to be created |
Worktree rule
Dispositions for dirty or unfinished work
Dispositions: Ready, Continue, Salvage, Parked, Superseded. Receipts go under docs/work-dispositions/. This checkout has a top-level work-dispositions/ folder and no docs/ folder, so the receipt location must be reconciled. Nothing below is Ready.
| Item | Evidence | Disposition | Receipt | Next outcome and owner |
|---|---|---|---|---|
| Installed checkout: 8 uncommitted entries; HEAD differs from the session HEAD (measured) | Not inspected | Parked | docs/work-dispositions/<item-id>.md (to be created) | No owner assigned. The next outcome is a classification pass by an assigned owner |
| Four shared/ files differ between session and installed copies (measured) | Audio source, a user-images migration, routes, worker daemon | Parked | To be created | Do not adopt. Routes and the worker daemon are high-risk files |
| Topology docs disagree on gateway and deleted host (T1, T3) | Docs stale per the 2026-10-15 audit | Continue | To be created | One owner confirms the gateway address through R1 and R2, then edits the docs. Owner not yet assigned |
| Backup run evidence (T4) | Empty journal; no upload check | Continue | To be created | Read-only check by the owner (R4). Blocks the backup move |
| Lane crew-frontier family worktree: 28 uncommitted entries (measured, worktree-sizing.md) | lane/crew-frontier-ca5dc309 holds uncommitted work; no owner classification yet | Parked, preserved | To be created | Never prune or relocate until an owner classifies. No owner assigned |
| Session 20261017-071650-cx worktree: 130 uncommitted entries (measured, worktree-sizing.md) | session/20261017-071650-cx holds a large uncommitted set; no owner classification yet | Parked, preserved | To be created | Never prune or relocate until an owner classifies. No owner assigned |
| Disk-free log file writer | No writer found in the searched paths | Parked | To be created | Preserve as is. Do not delete the file |
| Temporary Postgres lane (loopback 54329) | Temporary data; not production | Parked | To be created | Discard only after owner confirmation |
| Session worktrees (301 entries) | Each entry needs its own disposition | Parked by default | One receipt per candidate | Superseded only with a replacement recorded. Nothing is deleted by this plan |
| producehits-sync, go-live.sh and dev-sync.sh | Headers read; bodies not read | Parked | To be created | Read the bodies before any use. go-live.sh deploys the studio |
| Presumed-lost derived datasets from the key-corpus scratch directory (derived-round8 and wav; no copy found; owner copy unknown) | Manual agent deletions on the audit day (Claude session ~14:17–14:56Z); no pre-deletion backup for non-cache items | Parked | Preserved evidence; no receipt created | Preserved evidence. No recovery attempted. Owner copy unknown; no owner assigned |
| The scratch-service workload on port 18765 stopped during cleanup (no auto-restart; owner of that app unchecked) | Stopped during the audit-day cleanup session (~14:17–14:56Z) | Parked | To be created | Owner of the app unchecked. No restart attempted; no owner assigned |
Not verified in this plan
- The gateway address was not checked from this host (T1).
- Whether any running environment uses the 188.245.228.253 default (T2).
- Whether the backup uploaded (T4). Journal empty today.
- Disk thresholds in producehits-tmp-sweep and producehits-host-guard reclaim_disk; whether the host-guard prune functions run.
- Journald effective cap (inferred from file order, not from systemd output).
- Old journal directory (about 113M) and whether any rule removes it.
- Current inode use and cache sizes. Service host disk size.
- The full bodies of go-live.sh and dev-sync.sh.
- Public requests to songchop.com and api-origin.
- The cloudflared binary path used by the unit.
Cleanup forensic verdict: what was deleted on the audit day
What was deleted, and the evidence for each removal
| Deletion | Status | Evidence tier | Observed impact (vs causal inference) | Recovery options (none executed) |
|---|---|---|---|---|
| Lane virtual environment (6.2 GB) | Run | confirmed regenerable | Absent at 16:00Z. No unit, env file or script references it. No requirements file was captured before deletion, so the exact package set is unknown. | Rebuild from the lane requirements if they exist; otherwise re-resolve. Pins unverified. |
| Key-corpus scratch: genre, full-audio and raw sub-directories (3.4 GB) | Run | preserved-recovery-path (unexercised) | Absent at 16:00Z. A committed-manifest restore script can rebuild these from recorded sources and hashes, and fails closed if anything is missing. Not run by this report. | Run the restore in a scratch location after confirming the manifests and fixtures are present (presence unverified). |
| Key-corpus scratch: derived round-8 outputs and wav set (about 1.7 GB) | Run | not verifiable | No directory with that name under the searched roots at 16:00Z. No copy found. No recovery script covers derived outputs. Presumed lost. | Ask the data owner whether a copy exists. If none, regeneration from the source pipeline is needed (pipeline not checked). |
| Scratch service virtual environment (6.2 GB) and the local service it served | Run; service process stopped | concrete breakage | No listener on the scratch port at 16:00Z; the scratch directory is empty. No unit restarts it. The app owner was not checked. The venv itself is regenerable. | Identify the owner first. Rebuild the venv and restart only with owner confirmation. |
| Sync package virtual environment (6.2 GB) | Run | confirmed regenerable | Absent at 16:00Z. Cache-class path with no references found. | Recreated on the next install. |
| Pip and uv temporary directories and caches (about 6 GB) | Run | confirmed regenerable | Absent at 16:00Z. Re-downloaded on the next install. Home cache directory now 0.94 GB. | Automatic on the next install. |
| Five test-output cache directories (about 370 MB) | Run | confirmed regenerable | All absent at 16:00Z. Re-emitted by the next test run. | Re-run the tests. |
| npm, pip and model caches; old editor server installs | Run | confirmed regenerable | Cache class. Not re-probed at 16:00Z; the record of the removal is the only evidence. | Re-downloaded or reinstalled on demand. |
| One removal call | Denied by the built-in safety check | not applicable | The target is not named in the record. The call removed nothing. | None needed. Record the refused target if it is identified. |
| Systemd journal vacuumed to 100 MB | Run | not verifiable | Journal history before the vacuum is gone and cannot be checked. Journal size caps were added separately. | None for the removed history. Keep the caps. |
| Proxy log and system log truncated | Run | not verifiable | Prior contents cannot be recovered or checked. Logrotate and journald caps were added. | None for the prior content. |
| Consequence: the key-corpus restore script default input roots | Not a removal; the defaults now dangle | concrete breakage | The default roots point at the removed directories. Without environment overrides the script exits non-zero. Confirmed by grep of the script; the script was not run. | Restore the corpus, or set the input-root overrides to its new location. Editing the script is an owner change. |
Did we mess up?
Partly, yes. Most of what was removed was regenerable cache and virtual-environment material, and that class can be restored by reinstalling. The real losses are different. The derived round-8 outputs and wav set (about 1.7 GB) appear gone with no copy found. The scratch service was stopped without checking its owner and is down. The key-corpus restore script now points at directories that no longer exist. Journal history and log content before the trims cannot be recovered. The removals were agent-chosen. For non-cache items there was no pre-deletion backup or bundle, and no per-path byte counts were taken first. The "about 24 GB free" figure in the cleanup record matches no measurement. Measured available space was 4.1 GiB at 14:56Z and 16.11 GiB at 16:00Z, and it is falling again. The freed-space gain cannot be attributed to named deletions. The producehits-app failure at 13:19Z predates the cleanup and is not attributed to it.
Snapshot against the later probes
Figures from different metrics and times are shown side by side for context. They are not a before-and-after comparison of any single deletion.
| Measure | Audit snapshot (14:27-14:40Z, mid-cleanup) | Later probes (16:00Z unless stated) | Comparable? |
|---|---|---|---|
| Available space (df) | 0 bytes | 4.1 GiB at 14:56Z; 16.11 GiB at 16:00Z; down 245.2 MiB since about 15:19Z | Same metric; different times. Not attributed to any deletion. |
| Used space (df) | 149.65 GiB (byte-derived) | 127.6 GiB | Same metric; different times. |
| Allocated, dedup | 148.43 GiB (inventory v2) | Not re-measured | Different metric from df used. Do not subtract one from the other. |
| Deleted-open | 7 inodes, 1.9 MB | 124 lsof rows, 74.8 MiB | Different counting method. Shown as a later probe only. |
| Git worktrees | 301 (14:39Z) | 302 | Not a deletion effect; no worktrees were removed. |
| Sessions directory | Not recorded | 55 entries | No baseline. |
| Listed deleted paths | Not checked | 11 of 11 absent | Presence check only; no byte counts. |
| Failed systemd units | producehits-app failed at 13:19Z (before the cleanup window) | 3 failed; producehits-app named | The failure predates the cleanup and is not attributed to it. |
Limits
- No per-path bytes were measured before deletion, so no deletion can be credited with a specific byte count.
- The later probes are one batch at 16:00Z. Corpus recovery was not exercised and no fixture manifests were opened.
- The "about 24 GB free" figure in the cleanup record was not reproduced by any measurement.
Not done by this report
No cleanup, restore or migration performed by this report. No deletion, service start or restart, credential change or file restore was made. The later-probe figures are read-only observations.
Update: causes, cleanup accounting and preventive changes (evening 2026-10-19)
Current correction: 2026-10-20 recovery and enforcement
The historical audit below is preserved, not a live dashboard. At 13:30 UTC the development disk was again 96% used. About 240 crew checkout directories occupied 52,466 MiB. The replacement services host was only 4% used: moving scheduled services did not move or bound agent checkouts.
Recovery removed 206 clean inactive checkout copies with remotely preserved commits and no ignored files, retaining their branches. Dirty, active, held, parked and unarchived work was preserved. Disk use fell to 64%, with about 53 GB available. Package cache reclaim was separate. This is a current measured recovery, not validation of the older inferred reclaim totals.
Permanent enforcement is now active on the development host: new lane creation invokes retention and admission under the creation lock; default maximum 120 claimed checkout directories and minimum 20 GiB free. A separate installed timer runs safe retention every 15 minutes with a 6-hour age. No session or pane is killed. Ignored files block removal and all safety gates are checked again immediately before removal. Source commit 069ed9d6 is backed up; actual local dispatch uses this source. Fleet publication is pending unrelated intentional edits in the authoritative checkout, not silently claimed complete.
The services-host scheduled backup did fire at 03:53:18 UTC with exit status 0. Both daily analytics jobs have successful target-side output. Weekly search jobs are not yet due. The development backup timer remains disabled. Pack migration completed: 476 directories checked, 409 remotely verified and evicted, zero errors or missing uploads. The 67 differing-manifest directories remain preserved, with no overwrite. Local packs fell to 561 MiB and root ended at 62% used, about 56 GB available.
Causes of continued growth, from paired measurements
Used space rose about 1.25 GB in the 7 minutes between samples A and B and about 0.36 GB in the 6 minutes between B and C. Disk was 87% used with about 19.9 GB free at the last sample. Ranked growers:
| Grower | What was seen | Confidence | Owner or note |
|---|---|---|---|
| Lane and crew git worktrees | Each new checkout is about 223 MB (docs about 91 MB, models about 34 MB, annotations about 33 MB, plus static assets). 4 were created between samples A and B and 1 between B and C. 191 directories now total about 42.5 GB. | measured | Created by the agent lane spawner in the CX toolkit, not by the web application. |
| One-shot archive and rebuild bundles | About 395 MB appeared in an archive directory (two bundles of 195 MB and 209 MB) and a matching 395 MB rebuild scratch directory, written once at about 22:59Z and static afterwards. | measured | One-off by a current session; not a recurring grower. |
| Primary repository pack rewrite | A 108 MB pack file was rewritten between 22:50Z and 22:59Z (garbage collection or repack). | measured | Rewrite, not net growth. |
| Mission database | About 160 MB, growing about 1 MB per 5 minutes with an active write-ahead log. | measured | Slow and steady. |
| Small new directories in the window | Three tool-cache directories of 84-90 MB each. | measured | Not tracked further. |
Not growing in the window
| Item | Result | Confidence |
|---|---|---|
| AI agent session logs | Not the hot grower in this window. | measured |
| Generated pack directories (476) | 4.96 GB, no change across the samples. | measured |
| System journal | 40 MB. | measured |
| User uploads | 4 KB. | measured |
| Backups directory | 4 MB. | measured |
| Deleted-but-open files over 50 MB | None found. | measured |
Reading: the audit-day pressure came from many large worktrees. In this window the dominant growth is still new worktrees, not session logs, packs, uploads or the journal. Three samples over about 13 minutes are a short window, not a trend.
Cleanup accounting since the audit
| Item | Amount | Confidence |
|---|---|---|
| Cleanup already reported on the audit day | 12.3 GiB | executor-reported |
| Merged worktrees removed after the audit | 284 | executor-reported |
| Net space reclaimed since the audit by those removals | about 36 GB (range 36-45 GB) | inferred |
| Available space | 16.11 GiB at 16:00Z; about 19.9 GiB at 23:05Z | measured |
The 36-45 GB reclaim figure is an inference from free space moving from 16 GiB at 16:00Z while new worktrees kept arriving. There is no byte receipt for it and it is not added to the 12.3 GiB figure as a measurement. The two amounts are separate and not summed here.
Permanent preventive changes: honest status
| Change | Status | Evidence and limit |
|---|---|---|
| Worktree count cap and age-based cleanup in the CX toolkit | pending | No landed commit found. The only retention work located is an incomplete checkpoint branch for cold session homes, marked not approved to land. A cap and TTL for lane worktrees was not found in the toolkit history. |
| Cold-session archiver to the archival storage box | in progress | Incomplete checkpoint on a non-deployment branch (classifier, verified archive and eviction tool, offline tests). Its own receipt says not approved to land; known conflicts are open. Nothing is installed or running from it. |
| Bounded scratch with a time-based sweeper (temporary files) | landed | A 5-minute temporary-file sweeper timer is on the main branch and was observed active on this host. This covers the host temporary directory only. |
| Upload sweeper: signed-in reclaim, bounded cost | landed | On the main branch (commit present in the remote main history). Whether it is deployed on the gateway was not checked. |
| Pack cold-mirror fix: non-destructive publish, exact-size reclaim gate, bounded retry, packs to archival storage | landed | Source and tests are on the main branch; the disposition receipt reads Ready with a 29 of 29 gate. Gateway deployment and a production run were not verified here. |
| Webapp scratch directories bounded with a TTL sweeper (all of them) | pending | No dedicated source-fix branch was found. Generated pack directories still have no local reaper per the dependency section. |
| Services host owns scheduled jobs and backup | in progress | Documentation on the main branch records the services-host backup timer enabled and active, with its first run due 2026-10-20 03:53Z; the service had never run when documented. The move of some duties is documented as partial. |
| Development host backup timer disabled | observed only | The unit file state was read as disabled on this host at 23:08Z. The timer was documented as live earlier the same day, and no landed commit recording the disable was found. Who disabled it, and why, is unverified. |
Status words follow the work-disposition receipts and the main branch history. Landed means present in the remote main history, not deployed or proven in production.
Verified
- Paired disk-usage samples were taken three times and compared directory by directory (measured).
- Worktree directory count, total size and per-checkout size were measured on the host.
- Packs, uploads, journal, backups and deleted-open files were measured flat or small in the window.
- Landed status of the named source commits was checked against the remote main history.
Not verified
- The 284 removed worktrees: count, sizes and that every one was fully merged have no receipt in this report.
- The 36-45 GB reclaimed figure (inferred only).
- Whether the worktree cap and the cold-session archiver exist anywhere beyond the incomplete checkpoint.
- Gateway deployment of the pack and upload fixes, and any production run of the cold mirror.
- The first services-host backup run, due after this update; backup restore parity.
- Who disabled the development host backup timer, and why.
- Growth beyond the 13-minute window; no daily or weekly trend exists.
- Ownership of each remaining worktree directory.
- Independent review of this update.
Duplication and overlap
Hardlink duplicates are excluded from the allocated total. The rollups below overlap each other and the inventory parts. Do not add them up or to the allocated total.
| Hardlink measure | Value | Confidence |
|---|---|---|
| Hardlink duplicate excluded (measurements, v2) | 2.38 GiB | measured |
| Hardlink duplicate (allocated_vs_apparent) | 2.38 GiB | inferred |
| Rollup | GiB | Files | Additive |
|---|---|---|---|
| lane-worktree-group | 46.92 GiB | 922503 | overlapping, not additive |
| tooling-group | 35.41 GiB | 7692 | overlapping, not additive |
| venv-group | 22.52 GiB | 234289 | overlapping, not additive |
| session-checkout-group | 12.63 GiB | 378764 | overlapping, not additive |
| swap-group | 12.00 GiB | 2 | overlapping, not additive |
| cache-group | 7.55 GiB | 84213 | overlapping, not additive |
| audio-group | 7.44 GiB | 3385 | overlapping, not additive |
| node_modules-group | 7.29 GiB | 256941 | overlapping, not additive |
| git-group | 1.81 GiB | 40654 | overlapping, not additive |
| pycache-group | 1.48 GiB | 69279 | overlapping, not additive |
| logs-group | 0.45 GiB | 7364 | overlapping, not additive |
Limits of this evidence
These limits apply to every figure in this report. Read them before acting on any number.
- residual 1.22 GiB (df used minus allocated dedup) is unattributed
- worktree counts unreconciled; lane groups are candidates only
- executor group labels not verified against workload
- unit states observed in executor logs only
- root-checkout parity with installed units unverified
- fill tmpfs origin not inspected
- DB worker rows and billing not checked
- gateway-down cause not established
- top-30 leaf names intentionally withheld
- point-in-time snapshot with concurrent writers
- v1 figures superseded by v2: v1 listed 26 lane categories and left 45.87 GiB uncategorised due to truncation; df used is byte-derived 149.65 GiB, not 149.63
- allocated vs apparent difference attributed to block rounding and sparse files is inferred, not measured
- extension is a filename property, not a content type
- independent review pending
- models claimed cxproxy/claude-haiku-5-5 unverified
- mtime month map sums to 148.36 GiB vs 148.43 GiB dedup allocated: 0.07 GiB difference unexplained, likely two-decimal rounding of ~300 near-zero buckets (unverified)
- durable copy limitation: at the audit snapshot (~14:27-14:40Z) the root filesystem was full with 0 bytes available and the Pages deployment plus /tmp artifacts are the only copies; /tmp is volatile. The 16:00Z probe showed 16.11 GiB available, so this is time-scoped, not current
- update section: 284 merged-worktree removals and the 36-45 GB reclaim are executor-reported or inferred; no byte receipt
- update section: preventive-change status is from commit and receipt inspection only; deployment and production behaviour not verified
- update section: three paired samples over about 13 minutes are not a growth trend
- Exposure (measured, session grep output): a session-level grep printed literal database credentials from a team-access runbook. Values are not repeated in this report. Owner decision required; no credential changes were made by this report.
- Deletions occurred during and after the snapshot window (Claude session approx. 14:17-14:56Z), so the audit snapshot (14:27-14:40Z) does not describe the post-cleanup disk state.
- No per-path pre-delete byte counts were captured, so the amount freed by each deletion is not measured.
- The audit allocated-size figure and the later df-used figure are different metrics at different times; delta attribution of df-used change to the deletions is not established.
- One generated dataset (derived round data and wav files) is unrecovered and presumed lost.
- The cleanup record claim of about 24 GB free matches no measurement. The audit snapshot shows df avail 0 B; the post-cleanup probe shows df avail 17,303,072,768 B (about 17.3 GB). Free-space figures from the df free column and the avail column are not interchangeable.
Independent review
Review status: independent review pending. This report has not been independently reviewed. Treat every figure as provisional until that review is recorded.
Claims flagged unverified or inferred
- root-checkout parity with installed units unverified
- allocated vs apparent difference attributed to block rounding and sparse files is inferred, not measured
- models claimed cxproxy/claude-haiku-5-5 unverified
- mtime month map sums to 148.36 GiB vs 148.43 GiB dedup allocated: 0.07 GiB difference unexplained, likely two-decimal rounding of ~300 near-zero buckets (unverified)
- update section: 284 merged-worktree removals and the 36-45 GB reclaim are executor-reported or inferred; no byte receipt
Sources
- inventory-v2 redacted summary (publishable; scan 14:39-14:40; authoritative for all GiB figures)
- v1 redacted summary and v1 reconciliation (14:30; superseded where they disagree with v2)
- research handoff (v1 figures; superseded where they disagree with v2)
- research runtime and retention notes (observed unit states from executor logs; recommendations)
- deleted-open summary (7 inodes, counts only)
- growth measurement summary (three paired samples 22:52Z-23:05Z, read-only)