From harness-claude
Manages persistent state across agent sessions: loads context/progress/decisions/learnings from .harness files at start, tracks throughout, saves at end/milestones. For multi-session work.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Manage persistent state across agent sessions so that context, decisions, progress, and learnings survive context resets. Load state at session start, track position and decisions throughout, and save state for the next session.
Persists architectural decisions, conventions, and progress across sessions by auto-saving to .rune/ files. Loads state at session start for continuity when skills establish patterns.
Manages development session context via tiered summarization (quick updates, full checkpoints, archives) for preservation and resumability at breakpoints or after breaks.
Manages Maestro orchestration session state: creation, updates, tracking, resumption, and archiving via MCP tools, file edits, or scripts.
Share bugs, ideas, or general feedback.
Manage persistent state across agent sessions so that context, decisions, progress, and learnings survive context resets. Load state at session start, track position and decisions throughout, and save state for the next session.
docs/, state tracks progress through plansResolve the stream. State is organized into streams — isolated directories under .harness/streams/<name>/. Before loading any state files:
--stream <name> or use manage_state with stream: "<name>".feature/auth-rework → auth-rework stream) or falls back to the active stream.harness state streams list or the list_streams MCP tool.harness state streams create <name> --branch <branch>.Read .harness/state.json. This is the primary state file. It contains:
Run harness state show to get a formatted view of current state. This is equivalent to reading the JSON but formatted for readability.
Read .harness/learnings.md. This is the append-only knowledge base. Scan for:
Read .harness/failures.md if exists. Scan for active anti-patterns and dead ends.
Read .harness/handoff.json if exists. Structured context from last skill.
Check .harness/archive/ for historical failure logs.
If no state exists, this is a fresh start. Create .harness/state.json with initial structure:
{
"schemaVersion": 1,
"position": { "phase": "start", "task": null },
"progress": {},
"decisions": [],
"blockers": [],
"lastSession": { "date": null, "summary": null }
}
Announce the loaded context. Briefly summarize: "Resuming from [position]. [N] tasks complete. [N] blockers. Key learnings: [summary]." This confirms the state was loaded and gives the human visibility.
Update position when moving between phases or tasks. Every time work shifts to a new task or phase, update position in state:
"position": { "phase": "execute", "task": "Task 3", "step": "writing tests" }
Record decisions when they are made. Decisions are choices that affect future work. Record them immediately — do not wait until the end of the session:
"decisions": [
{
"date": "2026-03-14",
"what": "Use WebSocket instead of SSE for real-time notifications",
"why": "SSE does not support bidirectional communication, which Task 5 requires"
}
]
Log blockers when encountered. A blocker is anything that prevents the current task from completing:
"blockers": [
{
"date": "2026-03-14",
"task": "Task 4",
"description": "Payment gateway API returns 403 — API key may be expired",
"status": "open"
}
]
Update progress after each completed task:
"progress": {
"Task 1": "complete",
"Task 2": "complete",
"Task 3": "in_progress",
"Task 4": "blocked"
}
Keep state concise. State is not a log. Each field should contain the current status, not a history of all changes. History belongs in .harness/learnings.md and git commits.
Identify learnings as they happen. A learning is anything that:
Capture learnings with harness state learn:
harness state learn "Date comparison needs UTC normalization — use Date.now() not new Date()"
This appends to .harness/learnings.md with a timestamp.
Or append directly to .harness/learnings.md with structured format:
## 2026-03-14 — Task 3: Notification Expiry
- [learning]: PostgreSQL's `now()` returns timestamp with timezone, but our
application uses UTC epoch milliseconds. Always convert before comparing.
- [gotcha]: The notifications table has a unique constraint on (userId, type).
Use upsert (ON CONFLICT DO UPDATE) instead of plain INSERT.
- [decision]: Chose to store expiry as epoch milliseconds rather than
ISO timestamp for consistency with the rest of the codebase.
Learnings are append-only. Never edit or delete previous learnings. They are a chronological record. Even if a learning turns out to be wrong, append a correction rather than modifying the original.
What belongs in learnings vs. git commits:
Update .harness/state.json with final position, progress, and session summary:
{
"schemaVersion": 1,
"position": { "phase": "execute", "task": "Task 4" },
"progress": {
"Task 1": "complete",
"Task 2": "complete",
"Task 3": "complete"
},
"decisions": [ ... ],
"blockers": [ ... ],
"lastSession": {
"date": "2026-03-14",
"summary": "Completed Tasks 2-3. Task 3 required UTC date normalization (see learnings). Starting Task 4 next session."
}
}
Verify learnings were captured. Review .harness/learnings.md — were all non-obvious discoveries recorded? If something was tricky during the session, it should be in learnings.
State is saved to the active stream. All writes (state, learnings, handoff, failures) go to the resolved stream's directory (e.g., .harness/streams/auth-rework/state.json). Switching to a different stream in the next session does not affect the current stream's files.
Decide whether to commit state files. State files (.harness/streams/*/state.json, .harness/streams/*/learnings.md) should be committed to git so other team members and agents can access them. Commit state updates separately from code changes so they do not clutter code diffs.
The .harness/learnings.md file grows over the lifetime of the project. It becomes a valuable resource:
Treat learnings as a first-class project artifact. They are as valuable as tests and documentation.
failures.md to .harness/archive/ at milestone boundaries.state.json is always the source of truth.harness state show [--stream <name>] — Display current state in a formatted, readable view. Use at session start to quickly orient.harness state reset [--stream <name>] — Reset state to initial values. Use when starting a completely new effort and old state is no longer relevant. Use with caution — this discards progress tracking.harness state learn "<message>" [--stream <name>] — Append a learning with automatic timestamp formatting.harness state streams list — List all known streams with branch associations and active status.harness state streams create <name> [--branch <branch>] — Create a new stream, optionally associated with a git branch.harness state streams archive <name> — Archive a completed stream.harness state streams activate <name> — Set the active stream for the project..harness/streams/<name>/state.json — Primary state file per stream. Read at session start, updated throughout, saved at session end..harness/streams/<name>/learnings.md — Append-only knowledge base per stream..harness/streams/<name>/failures.md — Active anti-patterns per stream..harness/streams/<name>/handoff.json — Structured context from last skill per stream..harness/streams/index.json — Stream index tracking known streams, branch associations, and active stream..harness/trace.md — Optional reasoning trace. Useful for debugging agent behavior across sessions..harness/archive/ — Archived failure logs. Check for historical context when encountering recurring issues..harness/learnings.md entries follow the structured format (date, task, tagged items)| Rationalization | Reality |
|---|---|
| "The session is short — I'll update state at the end rather than after each task." | Context resets happen without warning. A session that ends mid-task with no state update forces the next session to reconstruct position by reading git history and code, which takes longer and produces an inaccurate picture. State is updated after each task, not at the end of the session. |
| "This decision is obvious from the code — I don't need to record the rationale in state." | What is obvious to the agent that made the decision is opaque to the agent that resumes three weeks later with no memory of the session. Decisions are recorded because the context that made them obvious does not survive a context reset. The rationale is exactly what needs to be saved. |
| "The learnings file is getting long — I'll trim old entries that are no longer relevant." | Learnings are append-only by design. An entry that seems irrelevant may become relevant when a related pattern resurfaces. Trimming destroys the chronological record and the ability to understand why earlier decisions were made. Entries are never deleted, only supplemented with corrections. |
| "I can re-read the plan to figure out where I am — I don't need to update the position in state." | The plan describes what to do; state records what has been done. Re-reading the plan without state requires the next session to infer progress from code, which produces uncertain position. Uncertain position leads to re-executing completed tasks or skipping tasks that appear complete but are not. |
| "The stream auto-resolves from the branch — I don't need to explicitly verify which stream is active before writing." | Auto-resolution works when branch names match stream names and the index is current. When branches are renamed, stale, or when multiple streams exist for the same feature, auto-resolution can write to the wrong stream silently. Always announce the resolved stream before writing state. |
LOAD:
Run: harness state show
Output:
Position: execute / Task 3 (writing tests)
Progress: Task 1 complete, Task 2 complete
Blockers: none
Last session: 2026-03-13 — "Completed Tasks 1-2. Task 2 required
adding a new index on notifications.userId for query performance."
Read: .harness/learnings.md
Most recent:
- [gotcha]: notifications table needs index on userId — queries
were timing out without it
- [decision]: used partial index (WHERE deleted_at IS NULL) to
avoid indexing soft-deleted rows
Summary: "Resuming from Task 3 (writing tests). Tasks 1-2 complete.
Note: notifications table has a partial index on userId — see learnings."
Context: Implementing Task 4, need to choose between polling and WebSocket.
Record decision:
date: "2026-03-14"
what: "Use WebSocket for real-time notification delivery"
why: "Polling would require 1-second intervals for acceptable latency,
which creates too much load. WebSocket gives instant delivery with
one persistent connection per client."
Capture learning:
harness state learn "WebSocket chosen over polling for notifications.
Polling at 1s intervals = ~86k requests/day per client. WebSocket =
1 persistent connection. See Task 4 decision in state."
SAVE:
Update .harness/state.json:
{
"schemaVersion": 1,
"position": { "phase": "execute", "task": "Task 5" },
"progress": {
"Task 1": "complete",
"Task 2": "complete",
"Task 3": "complete",
"Task 4": "complete"
},
"decisions": [
{
"date": "2026-03-14",
"what": "Use WebSocket for real-time notification delivery",
"why": "Polling creates too much load at acceptable latency intervals"
}
],
"blockers": [],
"lastSession": {
"date": "2026-03-14",
"summary": "Completed Tasks 3-4. Task 3 added expiry logic with UTC normalization. Task 4 implemented WebSocket delivery (chose over polling — see decisions). Starting Task 5 (UI integration) next session."
}
}
Verify: .harness/learnings.md has entries for UTC normalization and WebSocket decision.
Commit: git add .harness/ && git commit -m "chore: update harness state after Tasks 3-4"
| Information | Where It Goes | Why |
|---|---|---|
| "Added WebSocket handler in src/ws/" | Git commit message | Describes what changed in code |
| "Chose WebSocket over polling because..." | .harness/state.json decisions | Records the choice and rationale for future sessions |
| "WebSocket requires sticky sessions in load balancer" | .harness/learnings.md | Non-obvious operational concern future sessions need |
| "Task 4 complete" | .harness/state.json progress | Tracks execution position |
| "The WebSocket library auto-reconnects by default" | .harness/learnings.md | Gotcha that saves future debugging time |
| "Tried approach X, failed because Y" | .harness/failures.md | Active anti-pattern to avoid repeating |
| "Completed Tasks 1-3, Task 4 pending" | .harness/handoff.json | Structured context for next skill |
| "[PREPARE 10:30] Loaded 3 failures" | .harness/trace.md | Reasoning trace for debugging agent behavior |