R03

The cause is a guard against an interrupted commit, rather than against misattributing pending work. No failed hook or prior commit attempt is needed to trigger the reported loss.

In src/gzkit/commands/sync.py, _execute_git_sync calls _staged_governed_paths only inside if dirty and auto_add. That helper reads only git diff --cached --name-only. With ordinary unstaged edits or untracked source/tests, it returns nothing; the immediately following git add -A stages them, and _commit_staged_changes commits them under _build_sync_commit_message’s generated chore: subject and Task: TASK-gz-git-sync. _detect_stranded_commit_message cannot protect work for which nobody attempted a commit. --no-auto-add also bypasses the sweep guard while still allowing the later staged-content auto-commit.

Acceptance by commit checks is explained by the actual consumer: _validate_commit_trailers in commands/validate_commit_trailers.py checks has_task_trailer, whose slug regex in tasks.py accepts TASK-gz-git-sync. This establishes trailer presence, not that the ceremony owns the change. Governance anchors extracted into the message body cannot restore the work’s authored subject or ownership.

The tests encode the blind spot: TestSweepGuard.test_empty_index_still_sweeps treats an empty index as safe without modeling unstaged content. test_git_failure_does_not_block_the_sweep explicitly accepts an unreadable index. Message-builder tests even use ordinary source/test paths as examples of acceptable ceremony messages.

I recommend a producer-side repair, keeping the existing trailer syntax:

  1. Replace the staged-only predicate with one pending-change inspection covering staged, unstaged and untracked paths before automatic staging. Use NUL-delimited Git output, including both rename endpoints, so filenames and rename direction cannot evade classification. Inspection failure must block auto-commit rather than imply safety.
  2. Apply the same eligibility decision in the dry-run planner and recheck immediately before automatic commit, including --no-auto-add. Refuse the entire mixed batch before staging when substantive governed work is present; preserve the index and working files. Report the paths and recovery: commit the work with its own message and genuine Task attribution, then rerun sync. Remove the unsupported claim that staged work proves an aborted commit.
  3. Preserve ordinary ceremony work. The established guard boundary is src/** and tests/**; do not broaden it into a blanket refusal of every dirty tree. However, a blanket source-path refusal also catches legitimate package mirrors: sync_surfaces.sync_pkg_surfaces copies skills, rules, personas, templates and filtered chores into src/gzkit/. Permit only demonstrably generated outputs using their existing source/classifier rules and expected content, not whole directory exemptions that could admit Python implementation files. Keep unknown or manually altered source paths blocked.
  4. Update the canonical git-sync skill and manpage with the ownership precondition and recovery; regenerate their delivered mirrors through the existing delivery process during implementation. Adjust parser help, which currently describes git add -A as adding only tracked files.

Verification should first reproduce ordinary unstaged source edits and untracked tests with no COMMIT_EDITMSG, then assert refusal before add/commit/pull/push and unchanged index/content. Cover staged and partially staged work, --no-auto-add, rename/deletion, mixed ceremony/source batches, inspection errors, and agreement between dry-run and apply. Add positive controls for ledger/docs/vendor mirrors, verified package mirrors, clean no-op, clean ahead push and clean behind fast-forward. Retain the existing test_sync_pull_ordering.py behavior: allowed ceremony changes in a behind clone must auto-commit, then rebase using refreshed state and push. Use temporary local repositories/remotes during implementation to verify resulting commit content and attribution, not merely mocked command names.

I performed static reads only: no tests, sync workflow, commits or implementation. Existing positive-control tests support the intended preserved workflow but are not fresh passing evidence. The precise generated-output eligibility implementation needs further inspection of the referenced classifiers; arbitrary substantive work outside the existing src/tests ownership boundary is also not resolved by that boundary alone. There is no Git history here to reconstruct the specific lost commit.

Files read (whole or excerpts; all relative to /tmp/gzkit-three-pillars-discovery/S/):

  • .gzkit/skills/git-sync/SKILL.md; .gzkit/personas/main-session.md; .gzkit/rules/tests.md.
  • src/gzkit/commands/sync.py; src/gzkit/git_sync.py; src/gzkit/commands/validate_commit_trailers.py; src/gzkit/tasks.py; src/gzkit/cli/parser_maintenance.py; src/gzkit/sync_surfaces.py.
  • tests/commands/test_sync_sweep_guard.py; tests/commands/test_sync_pull_ordering.py; tests/commands/test_sync_cmds.py.
  • docs/user/manpages/git-sync.md; docs/user/skills/git-sync.md; docs/design/adr/pool/ADR-pool.atomic-obpi-commits.md (matching excerpts).
  • src/gzkit/utils.py (definition search). Also searched data/vendor-manifest.json, .pre-commit-config.yaml and CLI paths; no broader absence claims follow.

Also read /tmp/gzkit-three-pillars-inputs/R03.md. Shell/read tool-call count: 8, including task read and this answer write.