Skip to content

chore(hooks): vendor control-plane hooks from the canonical skills repo#62

Merged
vanducng merged 1 commit into
mainfrom
chore/vendor-hooks-from-skills
Jun 28, 2026
Merged

chore(hooks): vendor control-plane hooks from the canonical skills repo#62
vanducng merged 1 commit into
mainfrom
chore/vendor-hooks-from-skills

Conversation

@vanducng

Copy link
Copy Markdown
Owner

Why

hooks/ here was a hand-maintained copy that had drifted from github.com/vanducng/skills — the repo vd install hooks actually deploys from. The two diverged bidirectionally: the skills copy had LRU caches, isGlobalScratchPath, readOnly feature resolution, and the python notifier/merge-guard hooks; this copy had a separate nearestGitBoundary home-anchor variant. Whenever vd resolved its root to this repo, a stale/divergent hook set could deploy.

Single source of truth

The skills repo is canonical (deployed + tested). This repo now vendors it:

  • hooks/ is vendored verbatim from skills@bdfda7c; the commit is recorded in hooks/.vendored-from.
  • scripts/sync-hooks.sh regenerates the copy — --ref main bumps to the latest skills, bare re-materializes the recorded pin.
  • A hooks-drift CI job re-vendors the pinned commit and fails on any diff, so hooks/ can never silently re-diverge. (Pinned by SHA, so an unrelated skills change doesn't turn this repo's PRs red — bumping is an explicit sync-hooks.sh --ref main + commit.)
  • The three duplicate Go behavior tests (paths_home_anchor, paths_no_git, paths_worktree) are replaced by one that runs the vendored canonical suite hooks/lib/paths.test.cjs — behavior is owned upstream now.

Tradeoff

Drops this repo's nearestGitBoundary home-anchor variant in favor of the deployed, tested skills version (isHomeDir guard). If that variant is genuinely wanted, port it into the skills repo (canonical) and re-vendor — don't reintroduce a fork here.

Verified

  • Full module test green (14 packages); go vet clean.
  • Vendored tree byte-identical to skills/hooks (diff -r).
  • Drift-check simulated both ways: clean vendored copy passes; a committed hand-edit is caught.

Supersedes the no-git fix in #60 (that copy is now replaced by the vendored skills version, which carries the same fix via #261).

Comment thread hooks/dev-rules-reminder.cjs
Comment thread hooks/install.py
Comment thread hooks/install.py
Comment thread hooks/install.py
Comment thread hooks/install.py
Comment thread hooks/install.py
Comment thread hooks/agent-notify.py
Comment thread hooks/agent-notify.py
Comment thread hooks/agent-notify.py Outdated
Comment thread hooks/agent-notify.py Outdated
Comment thread hooks/lib/paths.cjs
Comment thread hooks/lib/paths.cjs
Comment thread hooks/lib/paths.cjs
Comment thread hooks/pr-merge-guard.py
Comment thread hooks/pr-merge-guard.py
Comment thread hooks/pr-merge-guard.py
Comment thread hooks/subagent-init.cjs
Comment thread hooks/pr-merge-guard.py
Comment thread hooks/lib/config.cjs
Comment thread hooks/test_agent_notify.py Outdated
Comment thread scripts/sync-hooks.sh Outdated
Comment thread scripts/sync-hooks.sh Outdated
Comment thread scripts/sync-hooks.sh Outdated
Comment thread hooks/lib/paths.test.cjs
@munmiu

munmiu Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

🔍 OpenCodeReview found 26 issue(s) in this PR.

  • ✅ 24 posted as inline comment(s)
  • 📝 2 posted as summary

📊 Posting Statistics:

  • ✅ Successfully posted: 24 comment(s)
  • ❌ Failed to post: 2 comment(s)

⚠️ Inline comments shown in summary


📄 hooks/lib/config.cjs (L268-L269)

⚠️ GitHub could not post this as an inline comment: Unprocessable Entity: "Line could not be resolved" - https://docs.github.com/rest/pulls/reviews#create-a-review-for-a-pull-request

buildResult carefully computes umbrellaGitRoot and uses it for sanitizeUmbrella, but only exposes _gitRoot: gitRoot (the local root) in the returned object — NOT umbrellaGitRoot. Downstream consumers like resolveUmbrellaRoot (paths.cjs:152) independently call getMainWorktreeRoot(baseDir) and may resolve a different root (e.g., a HOME-rooted main checkout) than what sanitizeUmbrella used. This means sanitizeUmbrella and resolveUmbrellaRoot can disagree on the umbrella root, leading to inconsistent behavior where the umbrella passes sanitization but resolves to a different (potentially unsafe) directory at runtime. Consider exposing umbrellaGitRoot in the result (e.g., as _umbrellaGitRoot) so downstream resolution can reuse it.


📄 hooks/session-init.cjs (L205-L211)

⚠️ GitHub could not post this as an inline comment: Unprocessable Entity: "Line could not be resolved" - https://docs.github.com/rest/pulls/reviews#create-a-review-for-a-pull-request

The old code block that emitted VD_FEATURE_PATH, VD_GLOBAL_PATH, and VD_ARCHIVE_PATH env vars in feature-first mode was deleted entirely. However, the existing test internal/hooks/featurefirst_inject_test.mjs (lines 35-38) still asserts that these vars are emitted:

ok('VD_FEATURE_PATH emitted', has('VD_FEATURE_PATH'));
ok('VD_GLOBAL_PATH emitted', has('VD_GLOBAL_PATH') && val('VD_GLOBAL_PATH').endsWith('_global'));
ok('VD_ARCHIVE_PATH emitted', has('VD_ARCHIVE_PATH') && val('VD_ARCHIVE_PATH').endsWith('_archive'));

These tests will now fail. If the env vars were intentionally removed (e.g., consumers now re-derive paths via resolveFeatureRoot), the corresponding test file must be updated in the same changeset. Otherwise, downstream consumers that rely on VD_FEATURE_PATH / VD_GLOBAL_PATH / VD_ARCHIVE_PATH will silently break.

@vanducng vanducng force-pushed the chore/vendor-hooks-from-skills branch from 9450f80 to f4726b0 Compare June 28, 2026 08:24
The hooks under hooks/ were a hand-maintained copy that had drifted from the
skills repo (github.com/vanducng/skills) — the actual source deployed by
`vd install hooks`. They diverged bidirectionally (skills had LRU caches,
isGlobalScratchPath, readOnly feature resolution + the python notifier/merge-guard;
this copy had a separate nearestGitBoundary home-anchor variant), risking a stale
deploy whenever vd resolved its root here.

Make the skills repo the single source of truth:
- Vendor hooks/ verbatim from skills@<pin>; record the commit in hooks/.vendored-from.
- Add scripts/sync-hooks.sh to regenerate the copy (`--ref main` to bump).
- Add a hooks-drift CI job that re-vendors the pinned commit and fails on any diff,
  so the copy can never silently re-diverge.
- Replace the three duplicate Go behavior tests with one that runs the vendored
  canonical suite (hooks/lib/paths.test.cjs) — behavior is owned upstream now.

Drops this repo's nearestGitBoundary home-anchor variant in favor of the deployed,
tested skills version (isHomeDir guard).
@vanducng vanducng force-pushed the chore/vendor-hooks-from-skills branch from f4726b0 to f37576f Compare June 28, 2026 08:28
@vanducng

Copy link
Copy Markdown
Owner Author

Review triage

scripts/sync-hooks.sh (3 findings) — fixed in the latest commit: EXIT-trap cleanup for both temp dirs, --ref/--src arg-count guards, and a blobless --filter=blob:none --no-checkout clone.

All remaining findings are in hooks/* files, which are vendored verbatim from github.com/vanducng/skills (pin in hooks/.vendored-from, enforced by the hooks-drift CI gate). They can't be edited in this repo — any change here fails that gate. They're also pre-existing upstream (present in skills main), not introduced by this PR, which only relocates the source of truth.

Valid upstream nits (e.g. install.py dynamic-path/with-open, dev-rules-reminder separator normalization) will be triaged in the skills repo and picked up on the next re-vendor. test_agent_notify.py was dropped from the vendored set entirely.

Resolving the vendored-file threads here as not-actionable-in-this-PR.

@vanducng vanducng merged commit 7aa35d1 into main Jun 28, 2026
3 checks passed
@vanducng vanducng deleted the chore/vendor-hooks-from-skills branch June 28, 2026 08:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant