Skip to content
Open
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
2 changes: 1 addition & 1 deletion plugins/session-flow/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "session-flow",
"version": "0.35.12",
"version": "0.36.0",
"description": "Session-lifecycle toolkit of fourteen skills: workflow (navigate a staged dev workflow and suggest the next stage), handoff (write a save-point and resume prompt for /clear-and-resume), continue-in-background (delegate the task to a fresh background agent that continues it now, using the same save-point engine as handoff, delivered by launching a detached claude --bg session seeded with the resume prompt; launches only on explicit user request), keep-going (recover and continue after any interruption OR when live off-thread work looks stalled: inventory off-thread work, inspect its real output, act only on evidence, then continue; after a usage limit lifts it continues rather than summarizing-and-stalling), find-handoff (recover a lost handoff after /clear, when the resume prompt was written but never copied, via a read-only detection ladder: known-location glob of the handoffs dir, then a bounded, recency-ranked transcript scan for the handoff directive and dashed-rail markers, then a confirm-before-resume gate; surfaces only the resume prompt + metadata, never raw transcript content), clean-stop (get to a durable, linked stopping point before the machine may go away: sweep every repo/worktree for uncommitted, unpushed, or PR-less work, push it durable, put breadcrumbs in PR/issue bodies, then give a free-and-clear verdict), retro (structured end-of-session retrospective with transcript metrics and learning codification), running-retro (in-flight retrospective checkpoints that spawn a subagent to analyze the transcript so far and append classified findings to a cumulative running ledger, which captures and routes only, the live counterpart to retro; also owns a detached-observer substrate that can watch a session out-of-band and run the checkpoint autonomously after the session ends), orient (read-only session orientation: synthesize where the session stands, what it is doing, and why, from durable + off-thread state the built-in /recap never sees: ledgers, handoffs, workflow checklists, running-retro ledgers, open PRs and work-items, and git), orchestrate (arm a session or worker with proactive-orchestration imperatives), reanchor (verify a session's working assumptions are still true against live reality, checking referenced PRs/issues/branches, base-branch drift, renamed/version-drifted surfaces, stale memory-tier files, and the goal a handoff records, compared across the chain so a re-derived goal reports as drift, before building on them), reconcile (retire finished off-thread work and reconcile this session's task ledger with reality, the prune-and-reconcile counterpart to keep-going's resume: inventory the work this session spawned, inspect its real state, retire the finished and close proven-done tasks, auto-settling the finished and gating any kill of still-running work; sibling sessions in the project are reported read-only), setup (check-centric verification of the observer's runtime prerequisites and configuration), and show-options (lay out which skills fit this moment as a ranked, nothing-hidden menu: a shortlist per bucket plus the complete remainder by name, resolved from the full installed catalog rather than the truncated in-context listing, so the human decides and no option is withheld for looking already-done).",
"author": {
"name": "Melodic Software",
Expand Down
25 changes: 25 additions & 0 deletions plugins/session-flow/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# Changelog: session-flow plugin

## [0.36.0]

### Added

- **`save_point.py fill <file> --slots <json>`: one call replaces every reasoning slot.** The
handoff write procedure was one Edit per `<!-- FILL: ... -->` slot, up to twenty-two of them on
a first hop, so an interrupt mid-batch left a partly filled skeleton. `fill` reads one JSON
object keyed by slot name and applies every value in a single write, after every check has
passed. An inline prefix on a slot's line is preserved, a multi-line value lands as those lines
in place, and an optional slot left out of the object has its line deleted. A closing handoff is
written by giving the `next` slot the value `Next: none (closed)` exactly, which `fill` moves
onto the `Next:` line above before deleting the slot line, since that is the only shape the
validator accepts as closed. A required slot absent, a key naming no slot in the file, a slot
name occurring twice, a value that itself carries a `FILL` slot marker, and a target with no
slot left are each refused by name with the file left byte-identical. A missing or unreadable
target, and a slots file that is missing, unreadable, not a JSON object, or holds a non-string
or non-UTF-8-encodable value, exit 2, as does a target that is not a handoff file, the same
`type: handoff` guard `validate` and `emit` apply, and a target whose shape is not 2, since
substitutions belong to the shape this engine writes. A shape newer than 2 exits 3 with
`validate`'s wording, read it and do not rewrite it, so version skew cannot corrupt a
future-format handoff. The write goes to a temporary file in the target's own directory and is
replaced into place, so an interrupted write cannot truncate the handoff. The target's own line
endings survive, so a CRLF handoff stays CRLF. `new`, `validate`, and `emit` keep their behavior
and exit codes, and the Edit tool is now only the repair path after a failed `validate`.

## [0.35.12]

### Fixed
Expand Down
9 changes: 5 additions & 4 deletions plugins/session-flow/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,11 @@ the whole chain (`chain:` frontmatter plus a `## Prior sessions` table), the ses
path, the user's verbatim goal and opening ask, the cumulative sections (constraints, side effects,
decisions, abandoned approaches, findings) copied forward with `[hN]` provenance tags, a one-line
`## This session` record, and, as its final section, the resume prompt itself. A stdlib-only
Python script, `scripts/save_point.py`, writes every deterministic field (`new`), validates the
finished file before the rails are shown (`validate`, exit 0 gates the prompt), and prints the
stored prompt (`emit`) so the on-screen rails and the file are the same bytes. The model fills
only the reasoning slots. The resume prompt tells the next session to invoke this skill for its
Python script, `scripts/save_point.py`, writes every deterministic field (`new`), replaces the
reasoning slots from one JSON object in a single write (`fill`), validates the finished file
before the rails are shown (`validate`, exit 0 gates the prompt), and prints the stored prompt
(`emit`) so the on-screen rails and the file are the same bytes. The model supplies only the
reasoning slot values. The resume prompt tells the next session to invoke this skill for its
own save-point rather than writing a handoff file free-hand. Older shape-1 files are read as
before and never rewritten.

Expand Down
34 changes: 27 additions & 7 deletions plugins/session-flow/reference/save-point.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ The body sections, the TaskList reconstitute format, and the frontmatter shape (
Walk it while writing the file; never write the section list from memory.

**The file is shape 2, and a script owns its deterministic tier.**
`${CLAUDE_PLUGIN_ROOT}/scripts/save_point.py` has three subcommands, run through the interpreter
`${CLAUDE_PLUGIN_ROOT}/scripts/save_point.py` has four subcommands, run through the interpreter
ladder the structure doc's write procedure shows (`"$PY" -X utf8 …`, Python 3.10+, stdlib only):

- `save_point.py new --topic <slug> --memory-dir <root> (--previous <file> | --no-previous)`
Expand All @@ -208,9 +208,28 @@ ladder the structure doc's write procedure shows (`"$PY" -X utf8 …`, Python 3.
17 headings in order, the goal and amendments and the five cumulative sections copied off the
predecessor with their `[hN]` tags, the `## Prior sessions` table, and the whole
`## Resume prompt` block except its `Next:` headlines) and prints the file's absolute
forward-slash path. Only the `<!-- FILL: <name> — <instruction> -->` slots are the model's;
every optional slot (`goal-rearm`, `below-rail`, `<section>-new`) is deleted when it does not
apply. It never overwrites an existing file.
forward-slash path. Only the `<!-- FILL: <name> — <instruction> -->` slots are the model's, and
`fill` is what applies them. It never overwrites an existing file.
- `save_point.py fill <file> --slots <json>` replaces every slot in the file from one JSON object
keyed by slot name, in a single write, and prints nothing on success. An inline prefix on a
slot's line (`**Amended:**`, `**Next action serves it by:**`, `did:`, the `left:` separator) is
preserved; a multi-line value is one JSON string with escaped newlines and lands as those lines
in place, each taking the file's own line terminator; an optional slot (`goal-rearm`,
`below-rail`, `<section>-new`) left out of the object has its line deleted. It exits 1 on a
required slot absent from the object, a key naming no slot in this file, a slot name occurring
twice in the file, a value that itself carries a `FILL` slot marker, a file with no slot left to
fill, and a closing `next` value whose line above is not exactly `Next:`; 2 on a target that is
missing, unreadable, not a handoff file (no `type: handoff` frontmatter, the same guard
`validate` and `emit` apply), or not shape 2 (shape 1, no `handoff_shape` key, a shape below 1,
or a `handoff_shape` that is not an integer), and on a slots file that is missing, unreadable,
not valid JSON, not a JSON object, or holding a value that is not a string or not encodable as
UTF-8; 3 on a `handoff_shape` newer than the engine knows, `validate`'s own verdict, read it and
do not rewrite it. Every refusal
names the slot or key and leaves the file byte-identical, and the write itself goes to a
temporary file in the target's own directory replaced into place, so nothing is ever
half-applied. The slots JSON lives beside the handoff as
`<same stem>.slots.json` and is left in place. `fill` never judges a value against its slot's
instruction; `validate` is the gate.
- `save_point.py validate <file>` prints PASS/WARN/FAIL lines and exits 0 on pass (shape 1: one
WARN, exit 0), 1 on a validation failure, 2 on usage, 3 on a `handoff_shape` newer than it
knows ("read it, do not rewrite it"). A leftover `FILL` slot, a prefixed `previous_handoff`, a
Expand Down Expand Up @@ -509,9 +528,10 @@ are untouched: prompt-only writes no file, so nothing here has a file to validat
`Remaining actions, in order`. Headlines yes, detail no: the file `@`-referenced on line 1 holds
the sequence, and the between-rails text is what a resuming session or a background agent sees
first. The last headline may be `Then: /<one skill>`, the fully-qualified skill the next stage
starts with, at a stage boundary only, never mid-stage. A closing handoff writes
`Next: none (closed)` and no headlines. The validator refuses a sixth line, a bullet, and a
`Then:` that is not last.
starts with, at a stage boundary only, never mid-stage. A closing handoff is written by giving
`fill` the `next` value `Next: none (closed)` exactly: `fill` puts that text on the `Next:` line
and deletes the slot line, so the closed form carries no headlines. The validator refuses a
sixth line, a bullet, and a `Then:` that is not last.
- **Below the bottom rail, first line:** the sentence `Or reopen the producing session in place:`
followed by `claude --resume <UUID>` in a code span and a period, the alternative to
`/clear`-and-paste when the producing session is still worth reopening. The `/goal` and `/loop` re-arm notes the rules above prescribe follow
Expand Down
51 changes: 39 additions & 12 deletions plugins/session-flow/reference/structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ costs the next session a re-investigation, which is the cost this document exist

**Shape 2.** A handoff file written by this procedure carries `handoff_shape: 2` in its
frontmatter. Every deterministic field of a shape-2 file is written by the engine script
`${CLAUDE_PLUGIN_ROOT}/scripts/save_point.py` (`new` writes the skeleton, `validate` gates it,
`emit` prints its resume prompt); the model fills only the reasoning slots the skeleton leaves as
`<!-- FILL: <name> — <instruction> -->`. The write procedure below is the one path that produces a
shape-2 file. Files written before shape 2 (no `handoff_shape` key) are shape 1: read normally,
tolerated by the validator with one WARN, and never rewritten.
`${CLAUDE_PLUGIN_ROOT}/scripts/save_point.py` (`new` writes the skeleton, `fill` replaces its
slots from one JSON object, `validate` gates it, `emit` prints its resume prompt); the model
supplies only the reasoning slots the skeleton leaves as
`<!-- FILL: <name> — <instruction> -->`, as the values in that object. The write procedure below
is the one path that produces a shape-2 file. Files written before shape 2 (no `handoff_shape`
key) are shape 1: read normally, tolerated by the validator with one WARN, and never rewritten.

## Contents

Expand Down Expand Up @@ -465,9 +466,11 @@ drifts silently, and it has before.
## Full-path write procedure

Write the file into the handoff location (`save-point.md` "Where save-points live"). The
procedure is: resolve the memory root, run the existing guards, run `new`, fill the slots, run
`validate`, then `emit`. Every step that needs no judgment is the script's; the model touches only
the `<!-- FILL: … -->` slots.
procedure is: resolve the memory root, run the existing guards, run `new`, write the slots JSON,
run `fill`, run `validate`, then `emit`. Every step that needs no judgment is the script's; the
model supplies only the values for the `<!-- FILL: … -->` slots, and `fill` applies all of them in
one write, so an interrupt mid-batch cannot leave a partly filled skeleton. The Edit tool is the
repair path after a failed `validate`, not a step of the procedure.

```bash
TOPIC=<short-kebab-topic> # e.g. plan-rev2, retry-loop, post-merge
Expand Down Expand Up @@ -529,16 +532,27 @@ done

# 5. Skeleton. Exactly one of --previous / --no-previous (see "Chain continuity").
# `new` reads CLAUDE_CODE_SESSION_ID itself and prints the file's absolute
# forward-slash path: reuse THAT string for every later step (Edit, validate,
# forward-slash path: reuse THAT string for every later step (fill, validate,
# emit, the directive); never recompute the path in bash.
SAVE_POINT="${CLAUDE_PLUGIN_ROOT}/scripts/save_point.py"
FILE=$("$PY" -X utf8 "$SAVE_POINT" new --topic "$TOPIC" --memory-dir "$MEMORY_ROOT" --no-previous)
# or, continuing a prior handoff's task:
# FILE=$("$PY" -X utf8 "$SAVE_POINT" new --topic "$TOPIC" --memory-dir "$MEMORY_ROOT" \
# --previous "$DIR/<prior>-handoff-<topic>.md")

# 6. Fill every `<!-- FILL: … -->` slot in $FILE with the Edit tool (delete the
# optional ones: goal-rearm, below-rail, <section>-new). Touch nothing else.
# 6. Slot values, then one fill. Read the slot names out of $FILE itself: the set
# is branch-dependent (goal, amended, opening-ask and the bare cumulative
# slots only on a first hop; the <section>-new slots only on a continuation),
# so a remembered template hits fill's unknown-key refusal. Write ONE JSON
# object keyed by those names, every value a string; a multi-line value is one
# string with escaped newlines ("First headline\nSecond headline"), which the
# next slot and the cumulative slots need. Leave an optional slot out
# (goal-rearm, below-rail, <section>-new) and fill deletes its line. For a
# closing handoff the next value is exactly "Next: none (closed)", which fill
# puts on the line above before deleting the slot line.
SLOTS="${FILE%.md}.slots.json" # beside the handoff, same stem
# Write $SLOTS with the Write tool, then apply every slot in a single write:
"$PY" -X utf8 "$SAVE_POINT" fill "$FILE" --slots "$SLOTS"
# 7. Validate; exit 0 gates the rails (save-point.md "Emit the copy/paste resume prompt").
"$PY" -X utf8 "$SAVE_POINT" validate "$FILE"
# 8. Print the stored resume prompt; paste its output on screen verbatim.
Expand All @@ -552,12 +566,25 @@ predecessor flags). It never overwrites. A refusal for a missing or non-UUID ses
save-point to the prompt-only path with that reason stated (`save-point.md` "Choosing the path");
every other refusal names its fix.

`fill` prints nothing and exits 0 once every required slot is keyed and no key names a slot the
file does not carry. It exits 1 when it refuses: a required slot absent from the JSON, a key
naming no slot in the file, a slot name occurring twice in the file, a value that itself carries a
`FILL` slot marker, no slot in the file at all, or a closing `next` value whose line above is not
exactly `Next:`. It exits 2 on usage, on a target that is missing, unreadable, or not a handoff
file (no `type: handoff` frontmatter, the same guard `validate` and `emit` apply), and on a slots
file that is missing, unreadable, not valid JSON, not a JSON object, or holds a value that is not a
string or not encodable as UTF-8. Every refusal names the offending slot or key and leaves the
target byte-identical, so a corrected JSON re-runs cleanly; nothing is half-applied. The write
itself goes to a temporary file in the target's own directory and is then replaced into place, so
an interrupted write cannot leave a truncated handoff.

**Python-absent fallback.** When the ladder finds no Python 3.10+, say so in one line
(`validator unavailable: no python3/python on PATH`), write the shape-2 file by hand from this
document (frontmatter below, the 17 headings in order, the `## Resume prompt` section in the
engine doc's full-path form), mark the checklist box `validate: SKIPPED (no interpreter)`, and
still emit the rails from the file's `## Resume prompt` section. Never a shape-1 file, never a
silent skip.
silent skip. This path is unchanged by `fill`: with no interpreter there is no `fill` either, so
the hand-written file needs no slots JSON.

### Frontmatter shape 2

Expand Down
Loading
Loading