InstallHooks: reconcile copied hooks against registered hooks after apply#1540
InstallHooks: reconcile copied hooks against registered hooks after apply#1540tzioup wants to merge 1 commit into
Conversation
…pply Copying the hook scripts and merging hooks.json are two independent operations, each verified on its own. A hook can land on disk without any settings entry naming it, and nothing currently compares the two sets. After --apply, shell the shipped reconciler (Doctor.ts --reconcile, already on disk at this point) and include its unwired list in the install report. Degrades loudly if Doctor.ts is absent rather than skipping silently. On a clean v7.1.1 payload this reports LoopDetector, DriftReminder and PostToolObserver. Refs danielmiessler#1539.
|
A generalization this PR naturally extends to — recording it here rather than opening a separate issue, since the machinery is identical and already shipped. Three pieces already exist; this PR connects the first two, for hooks. They generalize:
Two low-cost generalizations, both building only on the above:
No new subsystem is required; all three pieces ship today. Out of scope here, noted for completeness: the broader class of "entries removed from inside a surviving config/doc structure" (e.g. a registration deleted from an array that otherwise stays valid) is wider than the two-inventory |
|
Ported into source, thank you. One note on mechanics: the local InstallHooks had diverged from the shipped copy (it already counts pre-existing files and skips collisions), so your diff was re-applied as intent onto that base rather than verbatim; behavior is exactly your design, reconcile after apply, |
Fixes the detection gap in #1539.
What this changes
InstallHooks.tsdoes two independent things and verifies each on its own: itcpSyncs the payloadhooks/tree onto disk (checked by file count), and it mergeshooks.jsonintosettings.json(checked byadded/skipped/events). Both succeed. Nothing compares the set of files copied against the set of hooks registered, so a hook can land on disk with no settings entry naming it and the install still reports success — accurately.After
--apply, this shellsDoctor.ts --reconcile(already on disk at that point, copied by the same install) withCLAUDE_CONFIG_DIRpointed at the target config root, and includes itsunwiredlist in the existing report JSON.No logic is duplicated — the reconciler you shipped in v7.1.1 already does the transitive closure through dispatcher imports correctly. This just asks it.
Behaviour
On a clean v7.1.1 payload into an empty config root:
{ "ok": true, "added": 45, "events": 11, "hookFilesCopied": 72, "unwired": [ "LoopDetector.hook.ts", "DriftReminder.hook.ts", "PostToolObserver.hook.ts" ] }If
Doctor.tsisn't present at that path, the report carries areconcilestring saying so rather than skipping silently — matching the "LOUD blocker, never a silent no-op" rule inWorkflows/Setup.md.Testing
Ran the patched installer against a scratch config root containing only
settings.json({}) andLIFEOS/TOOLS/Doctor.ts, with--skill-rootpointed at this repo'sLifeOS/. Output above is verbatim from that run. Existing report fields are unchanged; the two new keys are additive.Note
unwiredis reported, not enforced — this doesn't fail the install or change exit codes. Happy to make it a blocker instead, or to drop the subprocess and inline the comparison, whichever you'd prefer.