Skip to content

fix!: activate hooks only in initialized repositories - #27

Merged
badrus123 merged 1 commit into
mainfrom
fix/hook-init-only-activation
Aug 9, 2026
Merged

fix!: activate hooks only in initialized repositories#27
badrus123 merged 1 commit into
mainfrom
fix/hook-init-only-activation

Conversation

@badrus123

Copy link
Copy Markdown
Contributor

Hooks fired in any repository with a detected language, so a repository that never ran forgeguard init was gated, blocked, and written to. That contradicted docs/ARCHITECTURE.md, which states global hooks pass silently outside initialized repositories.

Changes

  • Init-only activation. is_hook_project now requires .forgeguard/config.toml; a detected language is no longer treated as consent. evaluate_stop_hook uses the guard too — it previously had none. Nested repositories inside an initialized workspace still inherit gating.
  • Retry budget. Retry and no-progress counters both reset when the worktree or task state advances. A session that progressed on every turn could previously exhaust the budget and receive continue: false.
  • Duplicate hook registrations. A stop decision is replayed for 1.5s when repository and task state are unchanged, so a global and a project hook entry firing the same event consume one turn of budget, not two.
  • Auto-poke recovery. A blocked task releases its auto-poke budget when the session registers a different objective. Reframing the same objective still carries the budget over.
  • Command scope. Gates skip the configured commands when every changed path is documentation or an asset. Removed paths and files under test/, tests/, spec/, fixtures/, testdata/ still run them.
  • Timeouts. forgeguard init writes a stop-hook timeout covering the configured command budget and repairs existing entries whose timeout is too short.

Verification

cargo test --locked --workspace              107 passed, 0 failed
cargo clippy ... -D warnings                 no issues
cargo fmt --all -- --check                   clean
forgeguard gate --output compact             0 errors, 0 warnings, 0 failed checks
forgeguard doctor                            healthy

Manual reproduction in a scratch repository:

scenario result
uninitialized repo, stop and context hooks no output, no .forgeguard/, .gitignore untouched
after forgeguard init, source change decision: block
after forgeguard init, markdown-only change pass
deleted source file plus markdown edit decision: block
two hook registrations, same event identical decision, one attempt consumed
five turns with real progress attempt 1/3 each turn, no stop
four turns without progress escalates to stop on turn three
stop timeout vs command budget 720s >= 600s

Breaking change

Repositories without .forgeguard/config.toml are no longer gated. Run forgeguard init to keep enforcement.

Hooks fired in any repository with a detected language, so a repository
that never ran `forgeguard init` was gated, blocked, and written to. That
turned unrelated work into blocked agent turns and contradicted the
documented contract that global hooks pass silently outside initialized
repositories.

- Activation now requires `.forgeguard/config.toml`. The stop hook checks
  it too; it previously had no project guard at all. Nested repositories
  inside an initialized workspace still inherit gating.
- Retry and no-progress budgets both reset when the worktree or task state
  advances. A session that progressed every turn could previously exhaust
  the retry budget and be stopped.
- A stop decision is replayed for a short window when repository and task
  state are unchanged, so a duplicated hook registration cannot consume the
  retry, no-progress, or auto-poke budget.
- A blocked task releases its auto-poke budget when the session registers a
  different objective, instead of stopping the next objective on its first
  turn.
- Stop-hook gates skip the configured commands when every changed path is
  documentation or an asset. Removed paths and files under test trees still
  run them.
- `forgeguard init` writes a stop-hook timeout that covers the configured
  command budget, and repairs existing entries whose timeout is too short.

BREAKING CHANGE: repositories without `.forgeguard/config.toml` are no
longer gated. Run `forgeguard init` to keep enforcement.
@badrus123
badrus123 merged commit a9a802d into main Aug 9, 2026
3 checks passed
@badrus123
badrus123 deleted the fix/hook-init-only-activation branch August 9, 2026 19:44
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