Skip to content

fix: hooks/agents managed-block uninstall, real git hooks dir resolution, install backups (closes #415, closes #438)#449

Open
0xLeif wants to merge 2 commits into
mainfrom
fix/415-438-hooks-managed-block
Open

fix: hooks/agents managed-block uninstall, real git hooks dir resolution, install backups (closes #415, closes #438)#449
0xLeif wants to merge 2 commits into
mainfrom
fix/415-438-hooks-managed-block

Conversation

@0xLeif

@0xLeif 0xLeif commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Problem

#415 (data loss): specsync hooks uninstall --pre-commit deleted the user's ENTIRE pre-existing .git/hooks/pre-commit (a 3-line user hook + appended specsync block = <35 lines starting with #!/bin/sh and containing "specsync check" → fs::remove_file). Also, the specsync block was appended AFTER an existing hook's exit 0 — dead code that never ran.

#438 (lies): hooks install in a nested project root (repo/packages/app) created a fake .git/hooks/pre-commit (green ✓ but git never runs it); install in a non-git directory fabricated .git/hooks and reported success; core.hooksPath was ignored; agents install silently overwrote user-customized skill files with no warning or backup; the installed SKILL.md had an empty ## Companion files section.

Repro (v5.2.0)

$ printf '#!/bin/sh\necho "user hook content"\nexit 0\n' > .git/hooks/pre-commit
$ specsync hooks install --precommit   # ✓ block appended AFTER exit 0 (never runs)
$ specsync hooks uninstall --precommit # ✓ Removed
$ cat .git/hooks/pre-commit            # No such file — USER HOOK DELETED

After:

$ specsync hooks install --precommit   # sentinel block inserted BEFORE exit 0
$ specsync hooks uninstall --precommit # removes only the specsync block
$ cat .git/hooks/pre-commit            # user hook intact (shebang/echo/exit 0)

$ cd packages/app && specsync hooks install --precommit
# → installs into repo/.git/hooks (resolved via `git rev-parse --git-path hooks`)
$ mkdir /tmp/nogit && cd /tmp/nogit && specsync hooks install --precommit
# ✗ /tmp/nogit is not inside a git repository … Run `git init` first … (exit 1, nothing created)

Root cause / fix

  • Pre-commit installs are now wrapped in # >>> specsync:pre-commit … >>> / # <<< specsync:pre-commit <<< sentinels (same managed-block strategy as the markdown companions). Uninstall removes exactly the block — legacy ≤5.2.0 marker blocks and whole-file stock installs are also recognized and removed precisely — and the file is deleted only when nothing but our content remains. A hook with any user content is never deleted; a partial/corrupted marker set errors instead of guessing.
  • Install inserts the block BEFORE a trailing exit 0 instead of appending dead code after it.
  • The hooks directory is resolved via git rev-parse --git-path hooks, which handles nested project roots, linked worktrees/submodules (.git as file), and core.hooksPath. Non-git directories now fail loudly with remediation instead of fabricating a .git/hooks tree git would never read.
  • agents install backs up any differing pre-existing file to <name>.specsync-backup and prints a warning before overwriting; SKILL.md's ## Companion files section now actually contains the companion-files guidance (it was an empty heading).

Scope notes

  • The issue's "also" item about --format json being ignored on agents install/status is a cross-command formatting concern, explicitly out of scope here.
  • agents uninstall intentionally does NOT delete .specsync-backup files (user data).

Tests

New regression tests — src/hooks.rs: install_precommit_errors_in_non_git_dir, install_precommit_wraps_block_in_sentinels, install_precommit_inserts_before_trailing_exit_0, install_precommit_resolves_nested_project_root_via_git, install_precommit_honors_core_hooks_path, uninstall_precommit_removes_only_managed_block, uninstall_precommit_deletes_file_that_is_purely_ours, uninstall_precommit_leaves_user_hook_untouched, uninstall_precommit_removes_legacy_appended_block, uninstall_precommit_removes_legacy_whole_file_hook. src/agents.rs: install_backs_up_user_customized_skill, install_backs_up_user_customized_command, skill_md_companion_files_section_is_not_empty.

Full suite green (plus the one known pre-existing root-only failure in change::tests); cargo clippy -- -D warnings clean; all repros re-verified end-to-end with the patched binary.

Closes #415
Closes #438

@0xLeif
0xLeif requested a review from a team as a code owner July 24, 2026 17:12
@0xLeif
0xLeif requested review from 0xGaspar, Kyntrin and tofu-ux July 24, 2026 17:12
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ Corvin says...

      _
    <(;\  .oO(oh no...)
     |/(\
      \(\\
      " "\\

"Even the dumpster of code seems empty today."

CI Summary

Check Status
Validate action.yml ✅ Passed
Packaged Action Consumer ✅ Passed
Dependency Audit ✅ Passed
Code Coverage ✅ Passed
Format Check ❌ failure
Docs Site ✅ Passed
Spec Validation ❌ failure
Tests (build, test, clippy) ✅ Passed
VS Code Extension ✅ Passed
📋 Spec Validation Details

❌ SpecSync: Failed

Metric Value
Specs checked 62
Passed 62
Errors 1
Warnings 0
File coverage 100% (105/105)
LOC coverage 100% (87261/87261)

Errors

.specsync/sdd.json

  • meaningful changed paths are not covered by an active change: src/agents.rs, src/hooks.rs

Action Items

  • Review and fix -- meaningful changed paths are not covered by an active change: src/agents.rs, src/hooks.rs

Generated by specsync · Run specsync check --format github to reproduce


Powered by corvid-pet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant