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_pathsreads onlygit diff --cached --name-onlyand fails open on inspection errors. Unstaged modifications and untracked files are invisible._execute_git_syncinvokes that guard only insidedirty and auto_add.--no-auto-addstill reaches_commit_staged_changes, providing another route for already-staged substantive work._detect_stranded_commit_messageprotects 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_sweepsexplicitly 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.
- 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.
- 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. - Preserve legitimate generated package surfaces.
sync_surfaces.py::sync_pkg_surfacesproduces skills, rules, personas, templates and chores undersrc/gzkit/; blocking every dirtysrc/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. - 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.