Skip to content

Harden hooks/watch against context bloat and blocking scans#31

Merged
JordanCoin merged 4 commits intomainfrom
codex/hooks-context-bloat-hardening
Feb 28, 2026
Merged

Harden hooks/watch against context bloat and blocking scans#31
JordanCoin merged 4 commits intomainfrom
codex/hooks-context-bloat-hardening

Conversation

@JordanCoin
Copy link
Owner

@JordanCoin JordanCoin commented Feb 27, 2026

Summary

This extends the hook coverage work with runtime hardening for the real-world context bloat + hang/crash reports.

What changed

  • Added hard retention limits for watch daemon state/logs:
    • bounded in-memory events
    • events.log rollover/trim
    • tail-only reads for session-start history
  • Made interactive hooks (prompt-submit, pre-edit, post-edit, pre-compact, session-stop fallback path) use cached daemon hub data only (no expensive fallback graph scans).
  • Added hook fail-open timeout (CODEMAP_HOOK_TIMEOUT, default 8s) so hooks cannot block Claude input indefinitely.
  • Added stale daemon restart check at session-start for unhealthy/abandoned daemon state.
  • Made watcher .gitignore-aware for both directory registration and event ingestion, including nested ignored repos.
  • Capped branch diff hook output to reduce context injection from large diffs.
  • Added coverage tooling:
    • make coverage
    • CI coverage floor enforcement (>=30% total on ubuntu/go1.23)

Tests

Validation

  • go test ./...
  • make coverage (currently total coverage: 33.0%)

Related

Copilot AI review requested due to automatic review settings February 27, 2026 23:00
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a6a83256b2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +155 to +156
if state == nil {
return true

Choose a reason for hiding this comment

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

P1 Badge Verify daemon PID before issuing automatic stop

When state.json is missing, shouldRestartDaemon returns true and session-start invokes stopDaemon, but watch.IsRunning only verifies that the PID in .codemap/watch.pid responds to signal 0 (see watch/state.go) and does not confirm it is a codemap watcher. If that PID file is stale and the PID has been reused, this new auto-restart path can send SIGTERM to an unrelated process.

Useful? React with 👍 / 👎.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Addressed in 54f28fc. shouldRestartDaemon now requires daemon ownership verification before auto-stop/restart. I added watch.IsOwnedDaemon (PID cmdline check for watch daemon <absRoot>) and gate restart logic on it, so stale/reused PID files no longer trigger termination of unrelated processes.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the hook + watch daemon pipeline against long-running session bloat and hook blocking by adding retention limits, gitignore-aware watching, hook timeouts, and new/expanded tests + CI coverage enforcement.

Changes:

  • Added watch daemon retention controls (bounded in-memory events, events.log trimming, smaller state snapshots) and gitignore-aware directory watching/event ingestion.
  • Hardened hook execution: added a configurable hook timeout (fail-open) plus “no expensive fallback scans” behavior for interactive hooks; capped diff hook output size.
  • Expanded Go test coverage around the new limits/timeout/gitignore behavior and added CI coverage floor enforcement + a Makefile coverage target.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
watch/events.go Adds gitignore-based event exclusion, bounded event history, state snapshot event cap/copy, and log tail trimming.
watch/daemon.go Skips watching nested gitignored directories during directory registration.
scanner/walker.go Adds GitIgnoreCache.EnsureDir helper for memoized nested .gitignore loading.
watch/watch_test.go Adds an fsnotify-based test asserting gitignored directories don’t produce watcher events.
watch/events_limits_test.go Adds unit tests for bounded event retention and byte-based log trimming.
limits/limits.go Introduces constants for daemon/state/log retention and hook tail-read limits.
limits/budget.go Adds a max diff output budget for hook output truncation.
limits/limits_test.go Adds tests for truncation, adaptive depth, repo budgets, and new retention limits.
cmd/hooks.go Adds hook timeout plumbing, stale-daemon restart logic, tail-only event log reads, no-fallback hub lookups, and diff output truncation.
cmd/hooks_test.go Adds tests for timeout/env parsing, stale-daemon restart decisions, tail-only log reading, and no-fallback hub lookup behavior.
main.go Switches hook invocation to RunHookWithTimeout and fail-open behavior on timeout.
Makefile Adds coverage target to generate and summarize coverage.out.
.github/workflows/ci.yml Enforces a 30% coverage floor (ubuntu/go1.23) and uploads coverage output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 364 to +378
cmd := exec.Command(exe, args...)
cmd.Stdout = os.Stdout
var buf strings.Builder
cmd.Stdout = &buf
cmd.Stderr = os.Stderr
cmd.Run()

output := buf.String()
if len(output) > limits.MaxDiffOutputBytes {
output = limits.TruncateAtLineBoundary(
output,
limits.MaxDiffOutputBytes,
"\n\n... (diff output truncated, run `codemap --diff` for full output)\n",
)
}
fmt.Print(output)
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

showDiffVsMain captures the entire codemap --diff output into a strings.Builder and only truncates afterwards. For very large diffs this can still allocate a lot of memory (even if the final printed output is capped). Consider writing through a size-limiting writer (e.g., stop collecting after MaxDiffOutputBytes + a small slack) so the process never buffers unbounded diff output.

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

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

Addressed in 54f28fc. Same root fix as above: diff capture now uses a bounded writer so we stop retaining arbitrarily large stdout in memory before truncation.

@JordanCoin JordanCoin merged commit f5b8809 into main Feb 28, 2026
12 checks passed
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.

3 participants