Skip to content

feat(cc027): PreToolUse hook-tool-trace.sh metadata signal layer#54

Merged
screenleon merged 5 commits into
mainfrom
feat/cc027-tool-trace-hook
May 15, 2026
Merged

feat(cc027): PreToolUse hook-tool-trace.sh metadata signal layer#54
screenleon merged 5 commits into
mainfrom
feat/cc027-tool-trace-hook

Conversation

@screenleon
Copy link
Copy Markdown
Owner

Summary

  • Add scripts/hook-tool-trace.sh: PreToolUse hook (matcher "*") that appends a 4-field JSONL record per tool invocation to ~/.claude/projects/<proj>/memory/tool-trace.jsonl. Pure metadata ({ts, session_id, tool, first_arg_or_skill}), non-blocking, ~10ms/call.
  • Wire via scripts/install-hooks.sh with idempotent jq-splice matching the existing PreToolUse guard pattern.
  • Add 27 new test-hooks.sh cases (happy paths, hedge variants, security redaction, rotation, performance budget, install idempotency).
  • Unblocks CC-025 /skill-refine + CC-026 /skill-distill + CC-028 routing-log auto-append by providing the per-tool signal source.

Design

  • Multi-path JSON hedge for tool name (.tool_name / .tool / .tool_use.name) and per-tool first-arg fields, per [[feedback_undocumented_harness_payload]].
  • Security redaction: Bash env-prefix (X=Y ...) → null; Grep/Glob patterns → null; Read/Edit/Write paths → cwd-relative > basename > null; only bare lowercase command names persisted from Bash.
  • Rotation baseline: 4 MiB cap with single archive slot (.jsonl.1); CC-044 designs multi-window upgrade.
  • Non-blocking: all failure paths exit 0 + audit to tool-trace.err sidecar; no stdout writes (PreToolUse stdout is interpreted by harness).
  • Escape hatch: CLAUDE_TOOL_TRACE_DISABLE=1 for immediate disable.

Commit stack

  1. a4abd29 feat: initial spike
  2. fae73c2 fix: address PR-gate security + risk blocks (redaction + rotation)
  3. 2464b33 fix: address PR-gate qa block + critic advise (perf test + dead code)
  4. e282198 fix: drop cwd from schema (4-field contract per BACKLOG.md:202)

PR-gate (full tier, sequential mode)

Final round verdict: GO (advise only). Stack went through 3 gate rounds; all block verdicts resolved via fixup commits, no overrides.

Reviewer R1 R2 R3
critic advise advise advise
qa-tester advise block approve
architecture approve advise advise
security block approve approve
risk block advise approve

Backlog

  • CC-027 → ✅ closed (this PR)
  • CC-035 (install-hooks basename collision edge) → active, surfaced during spike audit
  • CC-027b deferred: tool-trace health signal (bounded error counter)
  • CC-027c deferred: strict JSON validation (jq inline cost prohibitive; async path)
  • CC-044 deferred: multi-window rotation upgrade

Test plan

  • bash scripts/test-hooks.sh → 279 passed, 0 failed
  • Performance budget test enforces <3500ms / 100 calls in CI
  • Leak-grep audit (manual): TOKEN/API_KEY/sk-live//etc/passwd never appear in jsonl across 5 firings
  • Rotation evidence: 4 MiB target → .1 archive, main resets to fresh file
  • Smoke: hook fires from real Claude session in this PR's working tree

🤖 Generated with Claude Code

screenleon and others added 5 commits May 15, 2026 14:58
Add per-tool-call JSONL trace (~/.claude/projects/<proj>/memory/tool-trace.jsonl)
to unblock CC-025 /skill-refine and CC-028 routing-log auto-append.

- scripts/hook-tool-trace.sh (new): pure-bash, no jq, multi-path JSON hedge
  for tool name (.tool_name / .tool / .tool_use.name) and first_arg per
  tool family; fast-path escape skip for safe-char values; non-blocking
  append (chmod 0444 → exit 0); CLAUDE_TOOL_TRACE_DISABLE=1 escape hatch.
  Median per-call overhead 8.2ms (100-iter benchmark).
- scripts/install-hooks.sh: wire as PreToolUse matcher "*" with idempotent
  jq-splice pattern matching existing three guards.
- scripts/test-hooks.sh: 14 cases (happy paths, hedge variants, missing
  fields, malformed JSON, non-blocking proof, payload-leak prevention,
  install idempotency); 267/267 pass.
- BACKLOG.md: CC-027 ✅ closed; CC-035 (install-hooks basename collision
  edge case from spike audit), CC-044 (rotation/retention deferred).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Security (block → cleared):
- Bash first_arg: redact env-prefix (^X=...) → null; only safe bare command
  names (^[a-z][a-z0-9_-]{0,30}$) kept; pathful/quoted commands → null.
- Grep/Glob first_arg: always null (patterns can carry secrets/hostnames).
- Read/Edit/Write first_arg: cwd-relative when inside repo; basename only
  when under $HOME but outside cwd; null for foreign paths.

Risk (block → cleared):
- Rotation: stat-based 4 MiB cap; over-cap → mv to .jsonl.1 (single archive
  slot) then start fresh. Constant-time check, non-blocking on mv failure.

Advise findings handled:
- critic medium / qa medium (malformed key-bearing JSON): strict jq
  validation deferred to CC-027c — `jq -e .` alone is ~25ms/call subprocess
  startup, exceeds entire 12ms budget. Bash brace heuristic stays
  best-effort; garbage-line risk is data quality, not security/risk.
- qa medium (docstrings): all new tool_trace_case invocations carry
  `# Behavior:` + `# Steps:` lines per QA AGENT.md §1 Step 3.
- qa low (no-leakage coverage): added cases for Bash env-prefix, Grep
  pattern, Read foreign path, Read cwd-relative.

BACKLOG:
- CC-027b deferred: tool-trace health signal (bounded error counter).
- CC-027c deferred: async strict JSON validation (inline jq cost).

Self-verify (main shell): 278 passed, 0 failed. Perf 9.45ms/call < 12ms.
leak_grep_audit returned 0 matches. Rotation evidenced.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
QA block (missing perf budget coverage):
- Add tool_trace_performance_budget test: 100 calls < 3500ms wall time
  (≈ 35ms/call cap, generous for slow CI runners). Local observed
  ~10ms/call; CI headroom prevents flakes.

Critic advise (dead helpers + rotation/retention contract):
- Remove unused json_string_after_stdout, first_nonempty, home_abbrev.
  Each had only its own definition with zero call sites.
- Update CC-044 prose to reflect that 4 MiB single-archive rotation is
  now the shipped BASELINE, not deferred. CC-044 scope becomes the
  UPGRADE from "current + one overwritten archive" to multi-window
  retention (gzip windows, time-based eviction, etc.).

Architecture advise (latency as architectural constraint):
- Addressed by the new perf budget test — global PreToolUse matcher "*"
  latency is now structurally guarded in CI rather than design-only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR-gate critic + security both noted `cwd` exceeded the documented
4-field contract at BACKLOG.md:202 (`{ts, session_id, tool,
first_arg_or_skill}`) and could leak path metadata if traces are
shared. The cwd is already encoded in the jsonl's directory location
(`~/.claude/projects/<encoded-cwd>/memory/tool-trace.jsonl`), so the
inline field was redundant. Consumers (CC-025/026, not yet
implemented) derive project anchor from file location.

- hook-tool-trace.sh: emit 4-field JSONL; cwd still read from envelope
  for project resolution + Read/Edit/Write path relativization (used
  internally only).
- test-hooks.sh: update schema_shape assertion (5→4 keys); update
  happy_path_bash to assert `has("cwd") == false`.

279 passed, 0 failed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per critic low advise from PR-gate round 3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@screenleon screenleon merged commit efc3847 into main May 15, 2026
6 checks passed
@screenleon screenleon deleted the feat/cc027-tool-trace-hook branch May 15, 2026 06:48
screenleon added a commit that referenced this pull request May 16, 2026
…/028/034 (#60)

Index row CC-029 was still 🔵 active even though PR #57 landed it
2026-05-15. Promote to ✅ closed and add Outcome + See pointers.

Backfill missing **See**: pr:#NN stubs for previously-closed entries
CC-027/CC-028/CC-034 that already pointed at PR #54/#55/#53 in their
Refs column but lacked the body-side See stub required by
pm/scripts/validate.sh E-CLOSURE-NO-SEE.

Pure data drift fix; no validator or runtime change. Remaining
baseline noise (CC-035 E-INDEX-MISMATCH, CC-038 E-AREA-ENUM +
E-REFS-PREFIX) is tracked separately and intentionally out of
scope here.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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