# How the Auto-Cleanup Hook Detects Stale Files in claude-code-harness

> Learn how the auto-cleanup hook in claude-code-harness detects stale files by comparing line counts and emitting JSON warnings. Understand its role alongside session-end hooks for artifact removal.

- Repository: [Chachamaru/claude-code-harness](https://github.com/Chachamaru127/claude-code-harness)
- Tags: how-to-guide
- Published: 2026-05-28

---

**The auto-cleanup hook handles stale file detection by comparing line counts against configurable thresholds after write operations, emitting JSON warnings for oversized Plans.md, session-log.md, or CLAUDE.md files, but it never performs removal—actual deletion of temporary artifacts is delegated to separate session-end hooks.**

The auto-cleanup hook in **claude-code-harness** provides proactive file size management without destructive actions. Integrated via the `PostToolUse` event, this hook analyzes files immediately after they are written or edited, comparing line counts against environment-defined limits. Unlike traditional cleanup mechanisms, it detects "stale" or oversized files through threshold comparisons and archive-section analysis while delegating actual removal to separate session-end hooks.

## How the Hook Triggers on PostToolUse Events

The auto-cleanup mechanism activates through the `PostToolUse` event registration in [`hooks/hooks.json`](https://github.com/Chachamaru127/claude-code-harness/blob/main/hooks/hooks.json). According to the claude-code-harness source code, the hook executes after the `Write`, `Edit`, or `Task` matchers complete their operations. Immediately following a file modification, the harness binary at `bin/harness` routes execution to the auto-cleanup implementation, passing a JSON payload via standard input.

## Parsing JSON Input and Normalizing Paths

When invoked, the hook reads the JSON payload from **STDIN** to extract critical fields: `tool_input.file_path` (or `tool_response.filePath`) and the current working directory (`cwd`). In [`scripts/auto-cleanup-hook.sh`](https://github.com/Chachamaru127/claude-code-harness/blob/main/scripts/auto-cleanup-hook.sh), the script first attempts to parse this data using `jq`, but falls back to a Python one-liner if `jq` is unavailable (lines 47-50).

Path normalization occurs next. If the file path begins with the `$cwd/` prefix, the hook strips this segment to create a deterministic, project-relative path. This normalization ensures consistent threshold checking regardless of absolute path variations across different execution environments.

## Threshold-Based Stale File Detection

The auto-cleanup hook implements **size-based staleness detection** using configurable line-count thresholds. Default limits are defined at lines 52-56 of [`scripts/auto-cleanup-hook.sh`](https://github.com/Chachamaru127/claude-code-harness/blob/main/scripts/auto-cleanup-hook.sh) and mirrored in the Go implementation:

- `PLANS_MAX_LINES`: 200 lines
- `SESSION_LOG_MAX_LINES`: 500 lines
- `CLAUDE_MD_MAX_LINES`: 100 lines

The hook uses `wc -l` to count lines in the target file, then dispatches to file-specific check functions. In [`go/internal/hookhandler/auto_cleanup_hook.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/go/internal/hookhandler/auto_cleanup_hook.go), the `checkFile` function (lines 12-25) routes to specialized validators: `checkPlans` for Plans.md, `checkSessionLog` for session-log.md, and `checkClaudeMd` for CLAUDE.md.

### Individual File Validation Logic

**Plans.md** checks occur in the `checkPlans` function (lines 29-65). If the file exceeds 200 lines, the hook assembles a warning recommending archival via `/maintenance`.

**session-log.md** validation happens in `checkSessionLog` (lines 68-79), triggering when the file surpasses 500 lines with suggestions to split content by month.

**CLAUDE.md** monitoring in `checkClaudeMd` (lines 82-93) warns at 100 lines, suggesting migration to `.claude/rules/` or a dedicated docs directory.

## Archive Section Detection and SSOT Synchronization

For Plans.md specifically, the hook performs **deep content analysis** beyond simple line counting. The implementation scans for archive markers including `📦 アーカイブ`, `## アーカイブ`, or the string "Archive" (lines 68-88 in the shell script).

When the hook detects an archive section, it locates the repository root using `git rev-parse` and checks for the existence of `.claude/state/.ssot-synced-this-session`. If this flag file is missing, the hook appends an urgent secondary warning recommending **`/memory sync`** execution before any cleanup occurs. This prevents data loss by ensuring significant decisions and learning patterns are persisted to the SSOT (decisions.md/patterns.md) before users archive active content.

## Go vs Shell Implementation

The claude-code-harness provides dual implementations of identical logic. The compiled Go binary contains the `AutoCleanupHandler` function in [`go/internal/hookhandler/auto_cleanup_hook.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/go/internal/hookhandler/auto_cleanup_hook.go), while the shell script at [`scripts/auto-cleanup-hook.sh`](https://github.com/Chachamaru127/claude-code-harness/blob/main/scripts/auto-cleanup-hook.sh) serves as a fallback.

Both versions parse JSON input identically (Go lines 49-78), resolve thresholds from environment variables with hard-coded defaults, execute the same file-type dispatch logic, and output identical JSON structures via `writeCleanupOutput` (Go lines 45-66). Users can invoke the hook directly via the binary:

```bash
echo '{"tool_name":"Write","tool_input":{"file_path":"CLAUDE.md"},"cwd":"/repo"}' \
  | ./bin/harness PostToolUse

```

## JSON Output and SystemMessage Integration

Rather than deleting files, the auto-cleanup hook generates JSON output containing warning messages. When thresholds are exceeded, the hook constructs a response where `hookSpecificOutput.additionalContext` contains the concatenated warning strings (shell script lines 112-115).

For example, an oversized CLAUDE.md produces:

```json
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "⚠️ CLAUDE.md が 150 行です。.claude/rules/ への分割、または docs/ に移動して @docs/filename.md で参照することを検討してください。"
  }
}

```

The harness injects this text directly into the systemMessage displayed to the user. If no warnings trigger, the hook exits silently with code 0, producing no output.

## Summary

- The auto-cleanup hook triggers on `PostToolUse` events for `Write`, `Edit`, and `Task` operations via configuration in [`hooks/hooks.json`](https://github.com/Chachamaru127/claude-code-harness/blob/main/hooks/hooks.json).
- It detects "stale" (oversized) files by comparing line counts against thresholds: **200 lines** for Plans.md, **500 lines** for session-log.md, and **100 lines** for CLAUDE.md.
- For Plans.md, the hook analyzes content for archive markers and verifies SSOT synchronization via `.claude/state/.ssot-synced-this-session` before permitting cleanup recommendations.
- The hook **never deletes files**; it only emits JSON warnings through `additionalContext` that the harness displays as system messages.
- Dual implementations exist in [`go/internal/hookhandler/auto_cleanup_hook.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/go/internal/hookhandler/auto_cleanup_hook.go) (Go) and [`scripts/auto-cleanup-hook.sh`](https://github.com/Chachamaru127/claude-code-harness/blob/main/scripts/auto-cleanup-hook.sh) (shell), with identical logic but different execution contexts.

## Frequently Asked Questions

### Does the auto-cleanup hook delete files automatically?

No. The auto-cleanup hook in claude-code-harness **does not delete or remove any files**. According to the source code in both [`scripts/auto-cleanup-hook.sh`](https://github.com/Chachamaru127/claude-code-harness/blob/main/scripts/auto-cleanup-hook.sh) and [`go/internal/hookhandler/auto_cleanup_hook.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/go/internal/hookhandler/auto_cleanup_hook.go), the hook only detects oversized files and emits warning messages suggesting manual actions like `/maintenance` or `/memory sync`. Actual removal of temporary artifacts is handled by separate hooks such as `WorktreeRemove` or `SessionEnd`.

### How does the hook determine if Plans.md needs archiving?

The hook checks if Plans.md exceeds **200 lines** (configurable via `PLANS_MAX_LINES`). Additionally, it scans for archive section markers including `📦 アーカイブ`, `## アーカイブ`, or "Archive". When these markers exist, the hook verifies the SSOT sync flag at `.claude/state/.ssot-synced-this-session`. If the flag is missing, it warns the user to run `/memory sync` before archiving to prevent losing important decisions.

### What are the default file size thresholds?

Default thresholds are hard-coded in [`scripts/auto-cleanup-hook.sh`](https://github.com/Chachamaru127/claude-code-harness/blob/main/scripts/auto-cleanup-hook.sh) lines 52-56 and mirrored in the Go implementation:

- **Plans.md**: 200 lines (`PLANS_MAX_LINES`)
- **session-log.md**: 500 lines (`SESSION_LOG_MAX_LINES`)
- **CLAUDE.md**: 100 lines (`CLAUDE_MD_MAX_LINES`)

These values can be overridden by setting the corresponding environment variables before invoking the harness.

### Can I use the auto-cleanup hook without the Go binary?

Yes. The repository includes a shell script implementation at [`scripts/auto-cleanup-hook.sh`](https://github.com/Chachamaru127/claude-code-harness/blob/main/scripts/auto-cleanup-hook.sh) that executes when the compiled Go binary is unavailable. The script provides identical functionality, parsing JSON from STDIN (with a Python fallback if `jq` is missing), normalizing paths, checking thresholds, and outputting warnings in the same JSON format as the Go version's `writeCleanupOutput` function.