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
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.17",
"version": "0.35.18",
"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
6 changes: 6 additions & 0 deletions plugins/session-flow/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog: session-flow plugin

## [0.35.18]

### Fixed

- **`save_point.py new` no longer drops the predecessor's amendment bullets that sit below its `Opening ask:` line.** The carry skipped every line after `Opening ask:` until one starting with `**`, so a bullet (`- **Amended (verbatim, ...)`) below the ask was swallowed and the successor lost it; two hops of one real chain had to re-add those bullets by hand. The skip now ends at any structural marker, a `**` line or a bullet, which keeps the multi-paragraph verbatim ask out of the successor as before. The same boundary is used by both of the validator's reads of the section, including the hop-1 ask-length cap, so all three agree.

## [0.35.17]

### Changed
Expand Down
14 changes: 11 additions & 3 deletions plugins/session-flow/scripts/save_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,14 @@ def _trim_blank(lines: list[str]) -> list[str]:
return lines[start:end]


def _ends_opening_ask(line: str) -> bool:
"""True when `line` resumes Original-goal content below an `Opening ask:`
line. A verbatim ask may run several paragraphs, so prose does not end it,
but the ask carries no bullets: a bullet below the ask is an amendment, and
a `**` line is the next structural marker."""
return line.startswith("**") or bool(BULLET_RE.match(line))
Comment thread
kyle-sexton marked this conversation as resolved.


# --- Cumulative-section entries ---------------------------------------------------


Expand Down Expand Up @@ -626,7 +634,7 @@ def _check_original_goal(doc: Doc, f: Findings, hop: int) -> None:
# line: a verbatim opening ask may run several paragraphs, and every
# one of them belongs to the ask rather than to the goal quote.
if skip:
if line.startswith("**"):
if _ends_opening_ask(line):
skip = False
else:
continue
Expand All @@ -651,7 +659,7 @@ def _check_original_goal(doc: Doc, f: Findings, hop: int) -> None:
# blank-line stop undercounts a multi-paragraph ask against the cap.
block = 0
for line in body[ask_idx + 1 :]:
if line.startswith("**"):
if _ends_opening_ask(line):
break
if line.strip():
block += 1
Expand Down Expand Up @@ -1241,7 +1249,7 @@ def section(title: str, lines: list[str]) -> None:
# and copying it here would smuggle it into the successor's
# immutable goal block instead of leaving the pointer to stand.
if skip:
if line.startswith("**"):
if _ends_opening_ask(line):
skip = False
else:
continue
Expand Down
25 changes: 25 additions & 0 deletions plugins/session-flow/scripts/tests/test_save_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,31 @@ def test_new_hop2_from_shape2_carries_chain_rows_and_tags(tmp_path):
assert "WARN" not in out(validated)


def test_new_hop2_carries_amendments_below_the_opening_ask(tmp_path):
"""An amendment bullet sitting BELOW the predecessor's 'Opening ask:' line
is still part of the goal and must survive the carry: the skip that drops
the ask runs to the next structural marker, and a bullet is one."""
repo = make_repo(tmp_path)
handoffs = repo / ".work" / "handoffs"
run(*new_args(repo, tmp_path, "--no-previous")).check_returncode()
hop1 = handoffs / HOP1
text = fill(hop1.read_text(encoding="utf-8"))
amendment = '- **Amended (verbatim, 2026-09-01):** "Also do the other thing."'
anchor = "Opening ask:\nDo the thing please.\n"
assert anchor in text
hop1.write_text(text.replace(anchor, f"{anchor}\n{amendment}\n"), encoding="utf-8", newline="\n")
result = run(*new_args(repo, tmp_path, "--previous", str(hop1), sid=SID_B, now="2026-09-02T10:00:00Z"))
assert result.returncode == 0, err(result)
hop2 = handoffs / HOP2
carried = hop2.read_text(encoding="utf-8")
assert amendment in carried, carried
# Carried above the pointer, where an amendment belongs.
assert carried.index(amendment) < carried.index(f"Opening ask: see {HOP1} § Original goal")
hop2.write_text(fill(carried), encoding="utf-8", newline="\n")
validated = run("validate", str(hop2), "--strict-transcript")
assert validated.returncode == 0, out(validated) + err(validated)


def test_new_hop2_from_shape1_legacy_tags_and_points(tmp_path):
repo = make_repo(tmp_path)
handoffs = repo / ".work" / "handoffs"
Expand Down
Loading