Lesson: an unattended sync must decode its diff leniently and never stage credentials
Date: 2026-09-20 Lane: hermes Confidence: high
Context
Cron job nightly-github-sync (c737c1447aa8, 02:00 daily AEST,
deliver=telegram:[redacted-id]) runs ~/.hermes/scripts/github_sync_report.py,
which wraps /Users/dwayne/clawd/scripts/github-sync-orchestrator.py. The
2026-09-20 02:00 fire exited 1 with
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 55567476: invalid continuation byte, raised inside the orchestrator's secret leak scan.
The three defects
-
Crash:
text=Trueon a byte-unsafe diff. The leak check ransubprocess.run(["git","diff","--cached","-U0","--","."], capture_output=True, text=True). A staged tree containing non-UTF-8 bytes (a legacy-encoded vendored source file, a binary.db, a log with latin-1 bytes) makes Python raiseUnicodeDecodeErrorduring decoding. The whole nightly sync aborted before any repo was committed or pushed. Fix: capture bytes (text=False) and.decode("utf-8", errors="replace"); treat a non-zero diff returncode as "cannot scan this repo, skip and report" rather than raising. -
Credential exposure risk. A backup tree (
~/Projects/removed-mindroom-backup-20260919, 4.76 GB, 21,541 staged files) carrieddot-mindroom/.envanddot-mindroom/.env.bak-pre-edge-fleet-20260806-203839into the staged set. The leak regex inspectsgit difftext only; it is not a dependable gate for a whole fresh file, and the 50 MB / extension filters let.envthrough.unstage_excluded()now un-stages.envand.env.*outright, with.env.example/.env.template/.env.sampleas explicit exceptions. (Both staged.envfiles were inspected and found to hold no credential-shaped value; the control was added regardless.) -
An unattended multi-GB first commit. The repo had zero commits and a dirty tree, so
git commit -m "Claw Automated Sync"would have created a first commit over 21k files and then attempted a 4.76 GB push. The orchestrator now refuses whenHEADdoes not exist and the tree exceedsFIRST_COMMIT_SIZE_LIMIT(500 MB), printingSKIPPING \<repo>: no commits yet and the tree is N GB... needs a supervised init.
Rule
A backup/sync job that runs unattended must (a) never assume its own inputs decode as UTF-8, (b) treat credential filenames as a hard un-stage rule rather than relying on content regexes, and (c) refuse a first commit above a stated size threshold instead of staging gigabytes. The leak regex is a content backstop; the filename and size rules are the actual guards.
Verification
python3 -m py_compileclean.- Unit-level:
is_sensitive_env_filereturns True fordot-mindroom/.envanddot-mindroom/.env.bak-pre-edge-fleet-20260806-203839, False for.env.example. unstage_excluded()against the real repo:Unstaging 2 excluded file(s)- exactly the two.envfiles; staged.envcount then 0.sync_project()against the real 4.76 GB repo prints the SKIPPING line, return code 0, and leaves the repo with zero commits and zero staged files.sync_project()against a normal project (leader-briefing-builds) still completes the full stage → leak scan → commit → push path:Successfully synced.- The orchestrator was committed to
~/clawdas24c05b3(2026-09-20).
Files
/Users/dwayne/clawd/scripts/github-sync-orchestrator.py(fixed)/Users/dwayne/.hermes/scripts/github_sync_report.py(wrapper; unchanged)/Users/dwayne/Projects/removed-mindroom-backup-20260919(uncommitted, no remote content)
Residual
- Superseded 2026-09-25 (hermes): the repo is no longer "uncommitted". It now
holds exactly one commit,
848349a4"chore: autonomous wip backup by Dodge janitor (2026-09-21T10:15:23.704987)", created by/Users/dwayne/clawd/scripts/ProjectJanitor.py(cronweekly-project-janitor,cb63565fd572, 10:15 Mondays) on itsdodge-wip-20260921branch, later renamed tomain. That initial commit bypassed the orchestrator'sFIRST_COMMIT_SIZE_LIMITguard entirely, so the guard never fired. - Superseded 2026-09-25 (hermes): the push now fails every night with
remote: error: GH001: Large files detected ... pre-receive hook declined, and has done so on each 02:00nightly-github-sync(c737c1447aa8) run since 2026-09-21; the branch rename tomainis the only step that succeeds. Remotedwaynehelena/claw-proj-removed-mindroom-backup-20260919is PRIVATE,isEmpty: true,diskUsage: 0,pushedAt: 2026-09-19T16:01:47Z. - Measured 2026-09-25 (hermes): tracked tree is 21,805 files / 2.43 GB (2,317
MB). Eight tracked files exceed 50 MB:
dot-mindroom/mindroom_data/logs/ mindroom_20260910_042158.log424 MB,.../mindroom_20260811_061349.log191 MB,dot-mindroom/build-tools/zig-x86_64-macos-0.16.0/zig186 MB,dot-mindroom/mindroom_data/teams/team_aidlc_team_7301a920a5c5/sessions/ team_aidlc_team_7301a920a5c5.db120 MB,dot-mindroom/mindroom_data/ edge_fleet.db80 MB,dot-mindroom/telegram-bridge-runtime/mautrix-telegram70 MB,dot-mindroom/mindroom_data/event_cache.db57 MB,dot-mindroom/build-tools/zig.tar.xz55 MB. Directory totals:mindroom_data1,823 MB,build-tools418 MB,telegram-bridge-runtime70 MB. - Root cause of the recurring failure:
unstage_excluded()only un-stages files newly added bygit add .. Files already inside the single existing commit are untouched, so the sync re-pushes the same oversized commit and fails. The 50 MB filter cannot repair an existing history. - Clean subset measured 2026-09-25 (hermes): 626 tracked files / 6.0 MB when
dot-mindroom/mindroom_data/,dot-mindroom/build-tools/,dot-mindroom/telegram-bridge-runtime/and.env*are excluded, with zero files over 50 MB. A repaired backup would carry that subset plus a.gitignore. dot-mindroom/.envanddot-mindroom/.env.bak-pre-edge-fleet-20260806-203839are tracked inside that commit. Re-checked 2026-09-25: no credential-shaped value in either (theunstage_excludedfilename rule covers new adds only).- Repairing this needs a history rebuild plus a force-push, which is a consequential action and requires Dwayne's explicit approval. Do not rewrite or force-push unattended.
~/Projects/gods-eye-viewand~/Projects/archifyhaveoriginpointing at upstreams this account only has READ on (bilawalsidhu/gods-eye-view,tt-a1i/archify, both public).gods-eye-viewholds one unpushedClaw Automated Synccommit. The orchestrator's private-visibility gate reads theclaw-proj-*repo name and so reports PRIVATE while the real push target is a public upstream it cannot write to. Any fleet commit meant to reach a deployed origin belongs in aclaw-proj-*repo or needs a deliberate fork.- A scheduled sync must not be re-fired while its own agent phase holds the
fire_claim;hermes cron run c737c1447aa8returns "already being fired". Exercise the fixed code by importingsync_projectdirectly instead.