Summary
The pg-notify-listener schema-sync path writes generated artifacts into the same git working tree it later needs to fetch/pull/rebase. When those writes leave the tree dirty (or partially written), the next sync aborts on local changes and the listener cannot recover without manual cleanup.
Incident
2026-07-27 ~19:12 UTC — Graybeard host schema-sync aborted after CREATE FUNCTION audit_bootstrap_universal():
- path:
/home/graybeard/.openclaw/workspace/nova-mind
- branch:
main (expected main)
- reason class: diverged / pull blocked by local changes
- dirty files:
agent-install.sh — mode-only change (644→755) from deploy making it executable
database/schema-reference.md — regenerated by the listener itself (same timestamp as the abort)
database/schema.sql — entire file emptied to 0 bytes (likely truncated/failed pg_dump during a prior run)
Ops recovery: stashed dirt, fast-forwarded 210 commits to main@32aaea8. Tree clean afterward.
Positive side-effect of the same event: production validation that nova-mind#508 / PR #535 alert delivery works (sender=graybeard, [schema-sync] prefix, recipients exclude self).
Root Cause
Schema-sync treats the live clone as both:
- the output directory for generated schema artifacts, and
- the git remote tracking tree that must stay clean enough to pull/rebase/push.
A partial dump or in-tree regenerate dirties HEAD'd files, then the pull step fails with "local changes would be overwritten", permanently wedging sync until a human stashes/resets.
Related but distinct from #446 (non-fast-forward when origin advances between sync and push). This issue is specifically self-inflicted dirt in the working tree before pull.
Proposed Fix
Pick one (or combine):
- Generate out-of-tree first — write
schema.sql / schema-reference.md (and any other generated artifacts) to a temp dir; only replace in-tree files after a successful fetch/ff-or-rebase onto origin/main.
- Pre-pull hygiene for generated paths — before pull,
git checkout -- / restore known generated files (or stash only those paths), pull, then regenerate.
- Atomic dump — never rewrite
schema.sql in place; dump to schema.sql.tmp and os.replace only on success (prevents zero-byte truncation wedges).
- Deploy side — avoid leaving mode-only dirty state on tracked scripts if the install step chmods files that git tracks as non-executable (or stop tracking executable bit if install always sets it).
Acceptance Criteria
See also
Summary
The pg-notify-listener schema-sync path writes generated artifacts into the same git working tree it later needs to
fetch/pull/rebase. When those writes leave the tree dirty (or partially written), the next sync aborts on local changes and the listener cannot recover without manual cleanup.Incident
2026-07-27 ~19:12 UTC — Graybeard host schema-sync aborted after
CREATE FUNCTION audit_bootstrap_universal():/home/graybeard/.openclaw/workspace/nova-mindmain(expectedmain)agent-install.sh— mode-only change (644→755) from deploy making it executabledatabase/schema-reference.md— regenerated by the listener itself (same timestamp as the abort)database/schema.sql— entire file emptied to 0 bytes (likely truncated/failedpg_dumpduring a prior run)Ops recovery: stashed dirt, fast-forwarded 210 commits to
main@32aaea8. Tree clean afterward.Positive side-effect of the same event: production validation that nova-mind#508 / PR #535 alert delivery works (
sender=graybeard,[schema-sync]prefix, recipients exclude self).Root Cause
Schema-sync treats the live clone as both:
A partial dump or in-tree regenerate dirties HEAD'd files, then the pull step fails with "local changes would be overwritten", permanently wedging sync until a human stashes/resets.
Related but distinct from #446 (non-fast-forward when origin advances between sync and push). This issue is specifically self-inflicted dirt in the working tree before pull.
Proposed Fix
Pick one (or combine):
schema.sql/schema-reference.md(and any other generated artifacts) to a temp dir; only replace in-tree files after a successful fetch/ff-or-rebase ontoorigin/main.git checkout --/ restore known generated files (or stash only those paths), pull, then regenerate.schema.sqlin place; dump toschema.sql.tmpandos.replaceonly on success (prevents zero-byte truncation wedges).Acceptance Criteria
pg_dumpcannot leave a zero-bytedatabase/schema.sqlas the committed working copy state that blocks the next sync.schema-reference.md(or equivalent) cannot block the subsequent fetch/pull.See also