# How the Compaction Hook Detects WIP Tasks Before Context Compaction in Claude-Code-Harness

> Learn how the compaction hook in Claude-Code-Harness detects WIP tasks before context compaction. Discover the pre compact save phase scan for specific markers and system message re injection.

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

---

**The compaction hook detects WIP tasks by scanning [`Plans.md`](https://github.com/Chachamaru127/claude-code-harness/blob/main/Plans.md) for `cc:WIP` or `[in_progress]` markers during the `pre-compact-save` phase, serializing the findings to a JSON snapshot, and re-injecting them into the system message after compaction completes.**

The claude-code-harness implements a safeguard mechanism to prevent accidental loss of in-progress work during context window compaction. When a user triggers the compact command, the harness executes the `pre-compact-save` hook—implemented in [`go/internal/hookhandler/pre_compact_save.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/go/internal/hookhandler/pre_compact_save.go)—to analyze the current project's task state before discarding any session context.

## Pre-Compact Detection Flow

The detection process follows a strict pipeline that transforms raw markdown table data into a structured artifact. This ensures the system captures a complete snapshot of active work before the compaction phase begins.

### Locating and Reading Plans.md

The hook first resolves the project root (or a custom `plansDirectory` if configured) to locate the [`Plans.md`](https://github.com/Chachamaru127/claude-code-harness/blob/main/Plans.md) file. It reads the entire file content and splits each line into discrete rows representing individual tasks. This raw tabular data serves as the input for the WIP detection logic.

### Parsing WIP Status Markers

The core detection uses a case-insensitive regular expression to identify work-in-progress status. According to the source code in [`pre_compact_save.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/pre_compact_save.go) (lines 434–440), the harness compiles the following pattern:

```go
reWip := regexp.MustCompile("(?i)`?cc:WIP`?|\\[in_progress\\]")
wipRows := filterRows(planRows, reWip)

```

This regex matches both the inline code format `` `cc:WIP` `` (with optional backticks) and the bracketed tag `[in_progress]`, ensuring flexibility in how users format task statuses.

### Counting and Extracting Task Details

Once filtered, the hook extracts both quantitative and qualitative data through dedicated functions:

- **`countWIP(planRows)`** (line 353): Returns the total number of rows matching the WIP pattern
- **`getWIPTasks(planRows)`** (line 341): Extracts human-readable task titles for the snapshot

These functions isolate the active tasks while preserving their descriptive context for later reporting.

### Building the Pre-Compact Snapshot

If WIP tasks exist, the hook constructs a summary string and serializes the data to [`precompact-snapshot.json`](https://github.com/Chachamaru127/claude-code-harness/blob/main/precompact-snapshot.json). As implemented in [`pre_compact_save.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/pre_compact_save.go):

```go
summaryParts := []string{}
if wipCount > 0 {
    summaryParts = append(summaryParts, fmt.Sprintf("%d WIP", wipCount))
}
prevSummary := "Before compaction: " + strings.Join(summaryParts, ", ")

artifact := struct {
    Summary  string   `json:"summary"`
    WIPTasks []string `json:"wipTasks"`
}{
    Summary:  prevSummary,
    WIPTasks: wipTasks,
}

```

This artifact persists to disk in the state directory, creating a durable record of active tasks that survives the context compaction process.

## Post-Compact Re-injection Mechanism

After compaction completes, the corresponding `post-compact` hook—implemented in both [`scripts/hook-handlers/post-compact.sh`](https://github.com/Chachamaru127/claude-code-harness/blob/main/scripts/hook-handlers/post-compact.sh) (lines 246–300) and [`go/internal/event/post_compact.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/go/internal/event/post_compact.go) (lines 337–358)—retrieves the snapshot and reconstructs the warning context.

The shell handler reads the JSON artifact:

```sh
PRECOMPACT_SNAPSHOT="${STATE_DIR}/precompact-snapshot.json"

get_precompact_context() {
  if [[ -f "$PRECOMPACT_SNAPSHOT" ]]; then
    wip_tasks=$(jq -r '.wipTasks[]' "$PRECOMPACT_SNAPSHOT" | paste -sd ', ' -)
    echo "Pre-compaction WIP tasks: ${wip_tasks}"
  fi
}

PRECOMPACT_CONTEXT="$(get_precompact_context)"
SYSTEM_MESSAGE="[PostCompact Re-injection] Context was just compacted. ${PRECOMPACT_CONTEXT}"

```

This re-injected message surfaces the previously identified WIP tasks immediately after compaction, alerting the user to active work that may require attention in the newly compressed context.

## Key Implementation Files

The WIP detection logic spans multiple files across the repository:

- **[`go/internal/hookhandler/pre_compact_save.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/go/internal/hookhandler/pre_compact_save.go)** — Contains the primary detection logic, regex compilation, and snapshot generation (lines 341, 353, 434–440)
- **[`scripts/hook-handlers/post-compact.sh`](https://github.com/Chachamaru127/claude-code-harness/blob/main/scripts/hook-handlers/post-compact.sh)** — Shell implementation that reads the snapshot and formats the re-injection message (lines 246–300)
- **[`go/internal/event/post_compact.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/go/internal/event/post_compact.go)** — Go implementation of post-compact handling with structured logging (lines 337–358)
- **[`hooks/hooks.json`](https://github.com/Chachamaru127/claude-code-harness/blob/main/hooks/hooks.json)** — Declares the `pre-compact-save` and `post-compact` hook bindings and argument passing configuration
- **[`go/internal/hookhandler/tdd_order_check.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/go/internal/hookhandler/tdd_order_check.go)** — Provides `hasActiveWIPTask` for rapid presence detection used by other evaluators
- **[`go/internal/hookhandler/stop_session_evaluator.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/go/internal/hookhandler/stop_session_evaluator.go)** — Demonstrates `countWIPTasks` usage for session-stop warnings

## Summary

- The **pre-compact-save** hook intercepts the compact command to analyze [`Plans.md`](https://github.com/Chachamaru127/claude-code-harness/blob/main/Plans.md) before context deletion
- **Regex pattern** `(?i)`?cc:WIP`?|\[in_progress\]` identifies WIP tasks regardless of case or formatting variations
- **`getWIPTasks`** and **`countWIP`** functions extract task titles and totals for the snapshot
- The **[`precompact-snapshot.json`](https://github.com/Chachamaru127/claude-code-harness/blob/main/precompact-snapshot.json)** artifact preserves WIP state through the compaction process
- The **post-compact** hook re-injects WIP warnings into the system message after context compression completes

## Frequently Asked Questions

### What happens if no WIP tasks are detected during pre-compact?

If `countWIP` returns zero, the hook still generates a snapshot but omits the WIP component from the summary string. The post-compact phase will skip the WIP warning and surface only other relevant metadata, such as recent edit counts.

### How does the post-compact hook access the WIP task data?

The post-compact hook reads the [`precompact-snapshot.json`](https://github.com/Chachamaru127/claude-code-harness/blob/main/precompact-snapshot.json) file located in the state directory (`${STATE_DIR}`). It uses `jq` to extract the `wipTasks` array and formats the entries into a comma-separated list for the re-injection message.

### Can the WIP detection regex match custom status markers?

The current implementation in [`pre_compact_save.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/pre_compact_save.go) uses a hardcoded regex targeting specifically `cc:WIP` and `[in_progress]`. Users must conform to these markers for detection; customizing the pattern requires modifying the source regex in [`pre_compact_save.go`](https://github.com/Chachamaru127/claude-code-harness/blob/main/pre_compact_save.go) lines 434–440.

### Where is the pre-compact snapshot stored?

The snapshot persists as [`precompact-snapshot.json`](https://github.com/Chachamaru127/claude-code-harness/blob/main/precompact-snapshot.json) in the harness state directory, typically referenced via the `${STATE_DIR}` environment variable. Both the pre-compact Go code and post-compact shell script rely on this path to communicate WIP state across the compaction boundary.