Repository navigation
hooks: 馃悰 Keep primitive fire limits stable across releases - #315
Merged
Merged
Conversation
Context: The hook-diet lane in the release-v3 drive found a `max_fires=1` nudge firing repeatedly, tracked as captain-hook cc-notes task `8ec2bdf`. One session held 39 keys for the cc-notes "cc-notes is installed" nudge, one per release from 12.73.0 to 12.87.6, and it fired 73 times. The monorepo's `pr_landing_fields` nudge had about 28 keys and appeared 4-5 times to agents. Summary: Attribute primitive hooks to the pack or repo module that registered them so their state keys and fire counters survive capt-hook releases. Motivation: Every primitive registers a framework-defined handler through `on()`, which recorded the handler's file under `~/.daemonkit/tools/capt-hook/<version>/`. `RegisteredHook._state_identity` could not make that file relative to the pack directory and used the absolute path. Each release therefore minted a new key and reset the hook's `max_fires` counter mid-session. Details: - Match `hook()` by attributing framework-defined handlers to the first caller outside the framework. User and builtin-pack handlers keep their own file, preserving each handler's module when a decorator is shared across modules. - Add keyword-only `source_file` to `on()`. The loader's `provision_pack_resources`, `announce_pr_status`, and `announce_faults` hooks pass their source file explicitly; `CliState.discover` stamps their identities as package-relative `loader.py`. - Give review FIX routing the pack or repo file for primitive fires. Store the watched repo root in `PackIndex` and rename `plugin_rel_path` to `root_rel_path`. Relativize repo-local targets so scanning sessions can find them with `git cat-file -e origin/<default>:<path>`, including `hook()` and `@on` hooks. - Include repo directories containing only primitive hooks in `capt-hook list` and the hook-lint pass. - Add a subprocess test that loads `captain_hook` from two symlinked installs, asserting identical keys and identity `ccx\0guard.py` for `nudge`, `gate`, `llm_nudge`, `set_tool_input`, and `rewrite_command`. Cover shared decorators, `loader.py` identities, repo-relative FIX targets, and the caller file in decision records. - Leave old session keys unmigrated. Each primitive's counter restarts once after upgrading. cc-task: 8ec2bdf Claude-Session-Id: 900424b6-7393-480c-a26a-f1bd21da6e57
yasyf
force-pushed
the
hook-diet-nudgekey
branch
from
October 5, 2026 07:21
ac8ede3 to
f533c8b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context: The hook-diet lane in the release-v3 drive found a
max_fires=1nudgefiring repeatedly, tracked as captain-hook cc-notes task
8ec2bdf. One session held39 keys for the cc-notes "cc-notes is installed" nudge, one per release from 12.73.0
to 12.87.6, and it fired 73 times. The monorepo's
pr_landing_fieldsnudge had about28 keys and appeared 4-5 times to agents.
Summary: Attribute primitive hooks to the pack or repo module that registered them
so their state keys and fire counters survive capt-hook releases.
Motivation: Every primitive registers a framework-defined handler through
on(),which recorded the handler's file under
~/.daemonkit/tools/capt-hook/<version>/.RegisteredHook._state_identitycould not make that file relative to the packdirectory and used the absolute path. Each release therefore minted a new key and
reset the hook's
max_firescounter mid-session.Details:
hook()by attributing framework-defined handlers to the first calleroutside the framework. User and builtin-pack handlers keep their own file,
preserving each handler's module when a decorator is shared across modules.
source_filetoon(). The loader'sprovision_pack_resources,announce_pr_status, andannounce_faultshooks pass their source file explicitly;CliState.discoverstamps their identities as package-relativeloader.py.watched repo root in
PackIndexand renameplugin_rel_pathtoroot_rel_path.Relativize repo-local targets so scanning sessions can find them with
git cat-file -e origin/<default>:<path>, includinghook()and@onhooks.capt-hook listand the hook-lint pass.
captain_hookfrom two symlinked installs,asserting identical keys and identity
ccx\0guard.pyfornudge,gate,llm_nudge,set_tool_input, andrewrite_command. Cover shared decorators,loader.pyidentities, repo-relative FIX targets, and the caller file indecision records.
upgrading.