R06

The cause is an attribution guard based on prior staging, not on the content the ceremony will commit. No failed commit is needed.

Impact account: ordinary edits enter the working tree/index; commands/sync.py::_execute_git_sync stages them with git add -A; _commit_staged_changes writes a generated chore message containing Task: TASK-gz-git-sync. That durable Git record attributes substantive work to the ceremony. _build_sync_commit_message also lists governance anchors and recent ledger events, but those best-effort summaries do not restore the work’s own attribution.

The downstream checks explain the apparent success. validate_commit_trailers.py::_validate_commit_trailers accepts any valid Task trailer for src/ and tests/; tasks.py::has_task_trailer accepts this ceremony slug. The configured prepare-commit-msg hook explicitly preserves an existing trailer, so it cannot repair the attribution. Formal TASK discovery through validate_task_envelope.py::_commit_trailer_channel_for_obpi uses parse_task_trailers, which extracts formal identifiers, not the ceremony slug. Thus syntactic compliance can coexist with lost TASK-to-work linkage.

The governing boundary is .gzkit/rules/tests.md’s distinction between direct-fix/formal TASK attribution and ceremony attribution for bundled surface synchronization. Trust doctrine requires provenance behind labels; state doctrine makes ledger events authoritative for completion, so a successful commit must not be interpreted as completion evidence. Tool/skill/runbook alignment requires the documented preview and execution to agree.

Evidence of the hole:

  • _staged_governed_paths reads only git diff --cached --name-only and fails open on inspection errors. Unstaged modifications and untracked files are invisible.
  • _execute_git_sync invokes that guard only inside dirty and auto_add. --no-auto-add still reaches _commit_staged_changes, providing another route for already-staged substantive work.
  • _detect_stranded_commit_message protects a different condition: a previous conventional commit message left in COMMIT_EDITMSG. With no attempted commit, it supplies no protection.
  • test_sync_sweep_guard.py::test_empty_index_still_sweeps explicitly accepts an empty index, but models no working-tree content. It therefore does not distinguish harmless pending synchronization from ordinary unstaged source work.

Smallest complete repair: strengthen the existing ceremony admission boundary, retaining its documented src/ and tests/ attribution scope.

  1. Inspect the entire candidate set before staging: staged changes, unstaged tracked changes, and nonignored untracked files. Use NUL-delimited Git output; include deletions and both rename endpoints. Fail closed with the inspection error when the candidate set cannot be established.
  2. Apply the same predicate in preview and execution, with a final staged-content check before generating the commit, regardless of --auto-add. Refuse substantive candidates, naming paths, the attribution obligation, and recovery: commit the work under its own authored message/trailers, then sync. Do not diagnose a failed hook merely from pending work.
  3. Preserve legitimate generated package surfaces. sync_surfaces.py::sync_pkg_surfaces produces skills, rules, personas, templates and chores under src/gzkit/; blocking every dirty src/ path would regress normal synchronization. Admit only outputs verified against the existing producer’s classification/rendering rules, including the filtered chores registry. A directory-name exemption would permit hand-edited implementation files through. Reuse that derivation rather than inventing another independent allowlist.
  4. Update the canonical git-sync skill, manpage and affected tests together; deliver skill mirrors through the established synchronization mechanism. Keep valid ceremony trailers and existing stranded-message protection.

Proposed verification: add failing cases for ordinary unstaged source edits, new tests, staged/unstaged mixtures, deletions/renames, inspection errors and --no-auto-add. Assert no add/commit/pull/push occurs on rejection and preview agrees. Positive controls should actually reach generated commit construction for ledger/vendor-only changes and verified package outputs; a modified package output must fail. Preserve authored-message recovery tests and test_sync_pull_ordering.py’s dirty-behind rebase, clean-behind fast-forward and level-tree no-pull controls. Test the emitted ceremony message against the existing trailer consumer. These are proposed checks, not executed results.

Uncertainty: the snapshot has no Git history; I cannot recover the incident’s exact paths or original intended message. I have not established current live-brief ownership or all possible generated-output deletion cases. This recommendation does not silently extend mandatory TASK scope to arbitrary docs/config changes. No workflow or tests were executed and no snapshot files were changed.

Read account: 10 shell/read calls, including task read and answer write. Content reads, some bounded excerpts: R06.md; under snapshot S, src/gzkit/{git_sync.py,commands/sync.py,commands/validate_commit_trailers.py,commands/validate_task_envelope.py,tasks.py,sync_surfaces.py,cli/parser_maintenance.py}; tests/commands/{test_sync_sweep_guard.py,test_sync_pull_ordering.py,test_sync_cmds.py}; .gzkit/{skills/git-sync/SKILL.md,personas/main-session.md,rules/tests.md,rules/tool-skill-runbook-alignment.md,hooks/prepare-commit-msg-task-trailers}; .pre-commit-config.yaml; docs/user/{manpages/git-sync.md,skills/git-sync.md}; docs/governance/{trust-doctrine.md,state-doctrine.md,advisory-rules-audit.md}; ADR-0.22.0’s OBPI-0.22.0-03-git-commit-linkage.md. Additional filename/search-only discovery covered sync surfaces, hooks, CLI, task-parser callers and OBPI references; those searches were not full-file reads.