Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "throughline",
"description": "Continuous, state-aware session memory for Claude Code. Captures what you did and what is (commands, file changes, decisions, and live git/PR state), then hands it off with judgment at session wrap-up. Readable, committable artifacts; binds to Claude's native memory.",
"version": "0.6.0",
"version": "0.6.1",
"author": {
"name": "Dynamic Agency",
"email": "support@dynamicagency.com"
},
"homepage": "https://github.com/dynamic/throughline",
"repository": "https://github.com/dynamic/throughline",
"license": "MIT",
"keywords": ["memory", "handoff", "session", "context", "continuity", "compaction"]
"keywords": ["memory", "handoff", "session", "context", "continuity", "compaction", "git-state", "cross-harness", "resume", "onboarding"]
}
51 changes: 51 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,57 @@
All notable changes to throughline are documented here. Format loosely follows
[Keep a Changelog](https://keepachangelog.com/); this project uses semantic versioning.

## [0.6.1]

Docs/skill polish batch from the v0.4.0 audit (docs/AUDIT-v0.4.0.md, P2, items
#8/#10/#11). No hook behavior change - verified by the unchanged
154-assertion suite plus shellcheck under both Homebrew and an
`apt-get install shellcheck` Ubuntu container.

### Added
- **Issue #8** - the session-log template (`throughline-handoff` Phase 3) gains
a "What we tried (including what failed)" section, with guidance to record
real evidence (the actual command/error/number), not a summary - a failed
approach is the most expensive thing for a future session to rediscover from
scratch. A **Follows** line lets a log name its predecessor in the same work
stream, so a reader can walk the decision history log-to-log without
HANDOFF.md having to carry it.
- **Issue #10** - a new README "Housekeeping" section documents what's safe to
delete (old archived buffers, a resolved `.capture-errors`) versus what
isn't (`logs/`, `HANDOFF.md`, unarchived buffers) - deliberately a
documented convention, not automated tooling, matching the plugin's
zero-infrastructure identity. The handoff skill (Phase 4) now also clears
`.capture-errors` once its contents have been surfaced in a session log,
instead of leaving it to nag on every future onboard indefinitely.

### Changed
- **Issue #11 polish batch**: the handoff skill's Phase 4.4 no longer offers
"(or delete)" for consumed buffers - archive-only removes an unguarded
data-loss affordance. Phase 4.3's memory-binding step now cites the actual
native auto-memory layout (`MEMORY.md` as a 200-line-truncated index, one
topic file per entry under `~/.claude/projects/<slug>/memory/`) and inlines
a one-line frontmatter example, so an executing model can't produce a
malformed entry. `plugin.json` keywords gained `git-state`,
`cross-harness`, `resume`, `onboarding` - the prior list under-sold the
differentiators. `tests/run.sh`'s header now states it is the single
source of truth for the current assertion count (it prints its own total),
after prose counts drifted across releases before (71/83/88/95 at
different points).

### Fixed (found during review, before merge)
- **The memory-binding frontmatter example didn't match reality**: every
actual memory topic file (`~/.claude/projects/<slug>/memory/*.md`) carries
`node_type: memory` and `originSessionId` under `metadata`, both omitted
from the template this release added - so an agent following it verbatim
would have written a schema-inconsistent entry, the exact failure the
change claimed to prevent. Verified against every real memory file in the
harness (100% carried both fields); the template now matches.
- **Em-dashes in new content**: the new Housekeeping section (README) and
session-log/Phase-4 additions (`throughline-handoff` SKILL.md) used
em-dashes, violating this user's global no-em-dash writing-style rule -
in `handoffs`, a category the rule names explicitly. Replaced with commas,
colons, periods, or spaced hyphens per the rule.

## [0.6.0]

Issue #9 from the v0.4.0 audit (docs/AUDIT-v0.4.0.md, P2): inline post-compaction
Expand Down
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,36 @@ Gitignore both for whichever data dir you use:
> `!/.claude/throughline/logs/`) or set `THROUGHLINE_DATA_DIR=.agent/handoff` so the
> committed artifacts sit outside the ignored tree.

## Housekeeping

Everything throughline writes grows without automatic bound: there is no
background cleanup process, deliberately, to keep the plugin's footprint at
"pure POSIX sh + jq, zero infrastructure." What's safe to clean up by hand,
and what isn't:

**Safe to delete:**
- `buffer/archive/*.md` older than your last `throughline-consolidate` pass -
once a consolidation has mined a log for recurring lessons, an archived raw
buffer behind it has nothing left to give. As a simple rule of thumb, an
archived buffer older than ~90 days with no open question against it is safe
to remove.
- `.capture-errors`, once its contents have been surfaced in a session log and
cleared by the handoff skill (Phase 4): it's a breadcrumb meant to be read
once, not a running log.

**Not safe to delete:**
- `logs/`: these are the evidence trail. `throughline-consolidate` explicitly
never prunes them, and HANDOFF.md's own "Recent Session Logs" list only ever
points at the last 5, so older logs are already off the beaten path without
needing to be deleted.
- `HANDOFF.md` itself, obviously - it's the durable record.
- Any buffer still in `buffer/` (not yet archived) - it may be an in-progress or
unconsumed session; run a handoff first, which moves it to `archive/` once
distilled.

There's no automated retention policy beyond this: clean up by hand on the
cadence above, or leave it, a growing `archive/` costs disk, not correctness.

## Auto-handoff at wrap-up (optional reinforcement)

The handoff skill is written to run proactively when the agent detects the session
Expand Down
46 changes: 41 additions & 5 deletions skills/throughline-handoff/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Create `DATA/logs/handoff-YYYY-MM-DD-HHMM.md` (local time; create `logs/` if nee
```markdown
# Handoff: <Brief Title>
**Date:** <YYYY-MM-DD HH:MM TZ> **Session:** <session-id if known>
**Follows:** [<predecessor title>](handoff-YYYY-MM-DD-HHMM.md) <!-- same work stream only; omit if none -->

## Objective
The big-picture goal of the session.
Expand All @@ -79,6 +80,14 @@ The big-picture goal of the session.
Distilled from the capture buffer + conversation: commands run, files changed,
decisions made — with the *why*.

## What we tried (including what failed)
Approaches attempted and abandoned, with the REAL evidence: the actual command,
error text, or number that killed the approach, not a summary of it. A failed
approach is the most expensive thing for a future session to rediscover from
scratch; a summary like "tried caching, didn't work" gives a future session
nothing to avoid re-trying. Omit this section only if nothing was actually tried
and abandoned this session.

## Progress
### Completed ### In progress ### Not started

Expand All @@ -96,6 +105,11 @@ Ordered, specific — exact commands, paths, expected outcomes.
Key paths, URLs, credential locations (names only).
```

**Chain link:** if this session continues work a prior log already started (same
work stream, not just the same project), name that predecessor in the **Follows**
line above so a reader can walk the decision history log-to-log without HANDOFF.md
having to carry it. Omit the line entirely for a session that starts something new.

---

## Phase 4: Update durable HANDOFF.md + memory binding
Expand All @@ -107,12 +121,34 @@ Key paths, URLs, credential locations (names only).
3. **Memory binding (native system):** ask "did this session surface a durable fact
worth pinning?" Types: a confirmed preference (`feedback`), a fact about the
user/context (`user`), a project constraint/decision (`project`), a resource
pointer (`reference`). If yes, write it to the native memory dir
(`~/.claude/projects/<slug>/memory/`) using the established frontmatter and
update `MEMORY.md`. This promotion is **curated** — never auto-dump the buffer
into memory. One entry per genuine insight; skip if nothing new.
pointer (`reference`). Native memory is two layers: `MEMORY.md` is an
always-loaded index (truncated past 200 lines, so every entry there must stay a
single short line), and each entry's full content lives in its own topic file
under `~/.claude/projects/<slug>/memory/`, read on demand. If yes, write the
topic file with frontmatter shaped like this, then add its one-line pointer to
`MEMORY.md`:
```markdown
---
name: short-kebab-case-slug
description: one-line summary used to judge relevance in a future session
metadata:
node_type: memory
type: feedback # or: user, project, reference
originSessionId: <current session id>
---
```
This promotion is **curated**: never auto-dump the buffer into memory. One entry
per genuine insight; skip if nothing new.
4. **Consume the buffers:** move distilled `DATA/buffer/session-*.md` into
`DATA/buffer/archive/` (or delete) so they aren't re-processed next session.
`DATA/buffer/archive/` so they aren't re-processed next session. Archive only,
never delete: an archived buffer is the recovery path if a distillation later
turns out to have missed something. See "Housekeeping" in the README for when an
archived buffer is old enough to actually delete.
5. **Clear resolved breadcrumbs:** if `DATA/.capture-errors` exists and its contents
were surfaced above (as a Phase 2 "Resolved Issues" entry or in the session log),
clear the file now that it has been distilled - it exists to make a swallowed
capture failure visible exactly once, not to keep nagging on every future onboard
after it's already been read and acted on.

---

Expand Down
7 changes: 7 additions & 0 deletions tests/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
# and asserts on the resulting buffer / stdout. No network, no model, no deps
# beyond jq + coreutils + git. Run: sh tests/run.sh (exit 0 = all passed).
#
# This file is the single source of truth for the current assertion count -
# it prints its own total ("passed: N") on every run. Historical assertion
# counts have drifted in prose before (CHANGELOG/HANDOFF entries citing 71,
# 83, 88, 95... at different points, issue #11): if you cite a count in
# prose, anchor it to a specific version ("143 assertions as of v0.5.2") or
# just point here instead of hardcoding a number that will go stale.
#
# Test fixtures legitimately contain literal markdown backticks and $ inside
# single quotes, so SC2016 (no-expansion-in-single-quotes) is expected here.
# shellcheck disable=SC2016
Expand Down
Loading