Skip to content

tsugu: local-first prepare + human-takeover recognition (spec 012)#53

Merged
caasi merged 30 commits into
mainfrom
feat/tsugu-local-first-prepare
Jun 18, 2026
Merged

tsugu: local-first prepare + human-takeover recognition (spec 012)#53
caasi merged 30 commits into
mainfrom
feat/tsugu-local-first-prepare

Conversation

@caasi

@caasi caasi commented Jun 17, 2026

Copy link
Copy Markdown
Owner

Implements docs/superpowers/specs/012-tsugu-local-first-prepare-design.md. Closes #52. Extends 011.

Local-first preparepush-prepare-branches default flips yesno; prepare keeps work on local prepare/*; discovery reads work prefixes local + remote (remote read regardless of push default — for opt-in pushes + leftovers); remote push is a cross-machine agent-collaboration opt-in. Removes the lingering remote prepare/* that confused cold-start sessions.

Human-takeover by containment — a prepare/<slug> whose tip a non-default, non-work branch contains is taken over (generalizing 011's accepted-prefix slug-pairing to human-named branches; slug-name stays the squash catch). Detection is a fresh, branch-scoped, alias/work-ref-excluding, remote-normalized git for-each-ref --contains — git-native, no shipped script.

Disposition = suppress-and-surface, never auto-delete — a taken-over branch is suppressed from auto-work and surfaced at prune/converge; cleanup is local and remote, both human-confirmed (containment can false-positive on a build-on-top branch). Classification is per-ref/per-tip (a stale remote tip never suppresses a newer local in-progress tip).

Auto-push invariantprepare auto-pushes only the work-prefix branch (when opted in), never accepted/human branches; cross-machine agent-to-agent push deferred.

Schema 4 → 5 — fresh init defaults push-prepare-branches: no; the 4→5 migration pins the explicit yes into existing repos (behavior never flips silently).

Built subagent-driven (8 implementation tasks, spec+quality review each + a final whole-branch review); content-regression guard tools/tsugu/test-skill-content.sh at 112 anchors; cross-plugin guards pass.

🤖 Generated with Claude Code

caasi added 12 commits June 17, 2026 14:12
…nup; auto-push invariant (spec 012 Changes C, D)
…eover (review, spec 012)

Task-1-3 review (Minor): the pre-existing 011 note said containment outside accepted
prefixes 'carries no derived meaning' — Change B now makes it the takeover signal.
Reword so pending=slug-pairing and foreign containment=takeover don't contradict.
…ec 012)

The re-run migration example still said a schema-1 repo runs 1→2→3→4 and used a
3→4 commit-message example. Bump to 1→2→3→4→5 / 4→5 to match the schema-5 bump.
…hema-5 straggler, spec 012)

Task-9 catch-all sweep caught a stale current-claim: the 3→4 section said a
schema-1 repo runs 1→2→3→4 'under the N→N+1 contract'. Under schema 5 the full
cumulative run is 1→2→3→4→5 (… then 4→5). (The 3→4 step's own 'Stamp tsugu-schema:
4 — last' at :418 is correct and stays.)
…n-over; README cold-start reads local+remote (spec 012)

Final whole-branch review (2 Minor doc-consistency): git-recipes' prune-sweep
'surface + confirm' list now includes the taken-over (redundant prepare) bucket
next to possibly-landed; README's cold-start paragraph now says the queue is read
from local + remote work refs (was a leftover 'remote-tracking refs' line). The
third Minor (duplicate 0.7.0 jq check) is harmless and left as-is. Refs #52.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the tsugu skill to spec 012 by making prepare local-first (no remote push by default), adding human-takeover recognition by containment, and introducing schema/metadata updates so existing repos don’t silently change behavior.

Changes:

  • Flip default behavior to local-first prepare via schema 5 + 4→5 migration that pins legacy push-prepare-branches: yes when previously implicit.
  • Document takeover-by-containment and the taken-over (redundant prepare) disposition (suppress from auto-work, surface for human-confirmed cleanup).
  • Bump tsugu marketplace/plugin metadata to v0.7.0 and expand content-guard checks accordingly.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tools/tsugu/test-skill-content.sh Extends content-regression checks for spec-012 wording, schema 5, and version 0.7.0.
plugins/tsugu/skills/tsugu/templates/policy.md Updates default policy template to tsugu-schema: 5 and push-prepare-branches: no with revised rationale.
plugins/tsugu/skills/tsugu/SKILL.md Documents local-first discovery (local+remote), takeover-by-containment, taken-over disposition, and schema-aware push defaults.
plugins/tsugu/skills/tsugu/references/policy-and-intake.md Updates schema references to current=5 and migration chain to include 5.
plugins/tsugu/skills/tsugu/references/notes-and-packet.md Documents taken-over (redundant prepare) surfacing and human-confirmed cleanup.
plugins/tsugu/skills/tsugu/references/migrations.md Adds Migration 4→5 definition (pin old push default; then stamp schema 5).
plugins/tsugu/skills/tsugu/references/git-recipes.md Updates cold-start queue reading to local+remote union and adds takeover-by-containment recipe notes.
plugins/tsugu/skills/tsugu/README.md Updates README to describe local-first prepare and takeover surfacing at prune/converge.
plugins/tsugu/commands/prune.md Extends prune command description to include taken-over (redundant prepare) surfacing.
plugins/tsugu/commands/prepare.md Updates prepare command description/invariants to local-first + local+remote discovery framing.
plugins/tsugu/commands/init.md Updates init command description/invariants for schema 5 and 1→…→5 migration chain.
plugins/tsugu/.claude-plugin/plugin.json Updates tsugu plugin description to mention local-first + containment takeover behavior.
.claude-plugin/marketplace.json Bumps tsugu marketplace entry to v0.7.0 and updates description accordingly.
Comments suppressed due to low confidence (1)

plugins/tsugu/skills/tsugu/README.md:136

  • This README section still describes the derived partition in terms of only “settled” and “pending (slug-paired accepted branch)”. With spec 012, there is now a third derived state (“taken over” via non-default, non-work containment). The README should mention that state (and its effect: suppressed from auto-work, surfaced at prune/converge) to avoid contradicting the updated docs elsewhere.
After `git fetch`, the queue is read from **local + remote** work-prefix refs
(local-first by default; remote work refs are read too, for opt-in pushes and
leftovers) — branch names plus each branch's `context.md` must be legible enough
that a cold-start agent can
reconstruct what branches exist, why, and what's next, with **zero conversation
transcript**.

## State is derived

Tsugu writes **no status fields**. Live coordination state is read from git's own
facts — ref names, ancestry, containment, commit recency — never from a tracked
status line. There is **no inbox layer and no recorded landed-SHA**; the partition
is single-layer, classifying each work branch by two ref-level facts:

- **settled** = the work landed, derived from **containment** (the branch's tip is
  contained in the default branch).
- **pending** (decided, awaiting merge) = a **slug-paired accepted branch** exists —
  a branch under a configured accepted prefix sharing the work branch's slug. The
  pairing is by name, so it survives anything the forge does to commits.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +16 to 22
The schema-version stamp (current: `5`). It is the first line of the file, and a
migration **writes it last** — only after every N→N+1 rename and semantic change
has been applied does `init` stamp the new number, so a half-applied migration is
never mistaken for a completed one. Readers use it to decide whether a re-run of
`init` must migrate (older stamp → apply `references/migrations.md` in order,
1→2→3→4 for a schema-1 repo) or is a plain idempotent repair (stamp already
1→2→3→4→5 for a schema-1 repo) or is a plain idempotent repair (stamp already
current).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 041d983: §Push rewritten to schema-5 local-first — default no, with the schema-aware absent read (yes only if the repo is still tsugu-schema: 4, else no) and the 4→5 migration pinning behavior described. Independently flagged by our local Codex gate (F3).

Comment thread plugins/tsugu/skills/tsugu/references/git-recipes.md Outdated
Comment on lines +154 to +162
**4b. Detect a human takeover by containment.** A `prepare/<slug>` is **taken
over** when its tip is **contained** by a **branch** that is neither the default
(nor its aliases) nor a work-prefix ref — a human carried the work onto their own
branch (`isaac/fix-thing`), which 011's slug-name pairing alone could not see (#52).
This **generalizes** slug-pairing (a tsugu accepted branch contains the tip too, so
containment catches it); slug-name pairing **stays** as the complementary catch for
the squashed/rewritten take. The check must be **precise** — a loose filter
false-positives, and the cleanup (Change C) is destructive on a wrong hit, so the
fetch-first + branch-scope + alias/work-ref exclusion below are **load-bearing**:

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 48ab72c (table) + ec16ed1 (code): §6's partition table now carries the taken-over row, and its snippet actually consumes §4b's for-each-ref --contains ($foreign_contains) so a human's own branch is detected, not mis-read as in-progress. Same finding as our local Codex gate (F1).

caasi and others added 11 commits June 17, 2026 18:25
… note (PR review, spec 012)

PR-phase Codex review found two git-recipes consequences of 012:
- takeover recipe normalizes the <remote>/ prefix but excluded '<remote>/HEAD' —
  post-normalization origin/HEAD becomes bare HEAD and slipped the alias filter
  (false-positive). Exclude the NORMALIZED forms: <default> AND HEAD; <work-prefix>/*
  catches both local and normalized-remote work refs.
- the 'each kept queue line is already a full remote-qualified ref' note was false
  after adding local enumeration — a kept line is local (prepare/foo) OR remote
  (origin/prepare/foo); use each verbatim, never re-prefix a remote one. Refs #52.
…he pending carve-out (spec 012; review F1+F2)

Codex local review found the takeover-by-containment model described two
incompatible ways:

- SKILL.md:130 carved "takeover = containment OUTSIDE accepted prefixes;
  pending = slug-pairing" — re-splitting what SKILL.md:121's table already
  merges into one "decided / taken over" row, and contradicting git-recipes
  §4b, whose filter excludes only default + work-prefix refs (so an
  accepted-prefix handoff branch is caught as takeover, by design — :160's
  "generalizes slug-pairing").
- git-recipes §6 "Partition the queue" billed itself as "the condensed
  canonical form of SKILL.md's partition" but dropped the taken-over row
  entirely (3-way settled/pending/in-progress), and its code echoed `pending`.

Resolution (per author): there is no separate *pending* state. Containment by
any non-default, non-work branch — an accepted-prefix handoff OR a human's own
branch — means a human now owns the work and tsugu stops managing it; both land
in the same disposition (skip, surface at prune/converge, never auto-delete).
Slug-pairing stays only as the complementary catch for the squash/rewrite take
where the rewrite severed containment. Nothing is recorded — all derived live
from refs (single-layer).

- SKILL.md:130 rewritten to the merged "handoff is a takeover" framing.
- git-recipes §6 table + code (echo taken-over) + prose aligned; the §4b
  filter is unchanged (it already catches accepted branches correctly).
- Residual partition-"pending"/"decided" wording (git-recipes :127, :278)
  retagged taken-over.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… schema-aware absent read (spec 012; review F3)

The canonical policy-field doc still documented schema-4 push semantics: the
example stamped `push-prepare-branches: yes`, stated "The default is yes," and
"absent → readers default to yes." Schema 5 flipped the new-install default to
`no` (local-first) with a schema-aware absent read (yes only if the repo is
still tsugu-schema: 4, else no). Rewrote §Push to match SKILL.md:146 and the
templates/policy.md ship value (no), and reframed the branch-as-message line
for local-first (the local branch is the queue on one machine; pushing is the
cross-machine opt-in).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tracking only (spec 012; review F4)

Three labels still framed discovery as remote-tracking-only, contradicting
Change A's local-first union (git-recipes 145–152, "same read path as prepare"):

- converge.md:12 invariants — "from remote-tracking refs after fetch"
- SKILL.md:61 mechanics pointer — "read-queue-from-remote-refs"
- SKILL.md:170 converge step 1 — "read everything from remote-tracking refs"

Under local-first the work stays local (push is opt-in), so a converge that
reads only remote-tracking refs would miss unpushed local-first work on the
provisioned machine. Reworded all three to "local + remote work refs
(local-first, the same union as prepare)," and qualified the machine-B
reconstruct-from-fetch line as the cross-machine push opt-in case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…al-first, takeover (spec 012; review F6)

The project CLAUDE.md tsugu entry was stale after the schema bump: it still
read "Schema 4 (lineage … → 008)", described state as "pending = slug-paired
accepted branch," omitted local-first prepare, and the spec list ended at 011.

- Schema 4 → 5; lineage extended → 011 → 012; spec list appends 012.
- prepare noted as local-first (kept local by default; push is the
  cross-machine opt-in).
- "pending = slug-paired accepted branch" → the reconciled takeover model
  (taken-over = a non-default/non-work branch contains the tip; slug-pairing
  the complementary catch).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e them (review F6)

The content-regression test passed green while four stale spots survived,
because need/refute scan only SKILL.md and the per-file checks didn't reach
them. Added anti-regression guards, each verified to fail on the pre-fix PR-tip
content and pass on the fix:

- F1: git-recipes §6 partition must echo taken-over, never `pending`.
- F2: SKILL.md must not carry the old "outside the accepted prefixes is the
  takeover … not from foreign containment" carve-out.
- F3: policy-and-intake §Push must read local-first + schema-aware, not
  "default is yes".
- F4: converge.md must not frame discovery as "remote-tracking refs after
  fetch".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (review round 2; Codex P1)

The reconciled §6 table claimed "taken over" covers a human's own branch
containing the tip, but the snippet only echoed taken-over for a slug-paired
$accepted ref — a human's own branch (isaac/fix-thing) fell through to
in-progress, so tsugu would keep working a branch a human already took over.

- Added the §4b for-each-ref --contains filter ($foreign_contains) and made
  the taken-over arm fire on `accepted OR foreign_contains`, so the canonical
  snippet is now executable and complete (settled still wins by precedence).
- Fixed the remote-only `branch=<remote>/<work-prefix>/<slug>` line — the
  enumerated ref is taken verbatim from step 4 and may be local or remote.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…chema 5 local-first) (review round 2; Codex)

The init bootstrap question still documented "default: yes" for "may agents
push prep branches automatically," contradicting schema 5's local-first
default (templates/policy.md ships push-prepare-branches: no). Reworded to
default no, with yes as the cross-machine opt-in.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cided, awaiting merge)" partition label (review round 2; Codex P3)

SKILL.md:130 and git-recipes §6 reconciled the partition's second state to
"taken over," but four consumers still taught the retired "pending / decided,
awaiting merge" model as a distinct partition state:

- README.md:134 state-model bullet
- policy-and-intake.md §Accepted Prefixes
- templates/policy.md §Accepted Prefixes comment
- advanced.md forced-squash procedure (×2)

Relabeled each to "taken over" (slug-paired handoff OR foreign containment),
keeping "awaiting-merge" only where it names converge's live display, not the
partition state. "awaiting-merge section"/"disposition" display wording is left
intact per the agreed model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lt, retired pending label (review round 2; Codex P4)

Added anti-regression guards (each verified to fail on the pre-fix PR-tip):

- §6 partition must consume §4b containment ($foreign_contains), so a human's
  own branch reads taken-over, not in-progress — the table/code mismatch the
  prior `echo pending` guard couldn't catch.
- init prep-branch push question must default no (schema-5), not yes.
- the retired "decided, awaiting merge" partition label must be gone across the
  whole skill dir; README must not list "pending" as a partition state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…und 3; Codex)

The new $foreign_contains pipeline ends in `grep -vE`, which exits 1 when no
foreign ref contains the tip — the common in-progress case. Under the recipe's
documented `set -euo pipefail`, the command substitution would then abort the
script before the `else echo in-progress` arm. Appended `|| true` (the project's
set-e-safe grep idiom) and added a test guard so the recipe stays runnable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(Copilot review)

Copilot inline nit (r3426232558): both takeover detection and prune cleanup
consume a leftover remote prepare/*, so the verb is plural.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.

Comment thread plugins/tsugu/skills/tsugu/SKILL.md Outdated
Comment on lines +231 to +232
| **settled** | tip contained in `<remote>/<default>` — the human merged with history intact | **delete on confirm** (low risk — provably landed). Local + remote. The main post-handoff target |
| **taken-over (redundant prepare)** | a `prepare/<slug>` (local or remote) whose tip a **non-work, non-default** branch contains — a human carried the work onto their own branch | **surface + confirm each** (like *possibly-landed*) — never auto-delete; the containment signal can false-positive on a build-on-top branch. On confirm, delete local + remote. **Precedence:** if `<remote>/<default>` contains the tip it is **settled** (above); *taken-over* covers only the case where the containing ref is a **non-default** branch |

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in ec58dd2. By design the prune taken-over (redundant prepare) bucket is the git-containment-derivable take only (a non-default, non-work branch contains the prepare tip). The slug-paired squash/rewrite handoff severs containment and is not git-derivable, so it is intentionally the separate possibly-landed (no containment) bucket (human-confirmed) — folding it into taken-over would double-cover that bucket. Added a disambiguating cross-reference here so the two stay distinct.

Comment on lines +525 to +529
- **taken-over (redundant prepare)** — a `prepare/<slug>` whose tip a **non-default,
non-work branch** contains (a human carried the work onto their own branch — see the
containment-takeover read above). Surface + confirm each, like *possibly-landed*; on
confirmation delete the redundant `prepare/<slug>` **local and remote, both
human-confirmed**; never auto-delete. Precedence: classify as *settled* when default

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in ec58dd2. By design the prune taken-over (redundant prepare) bucket is the git-containment-derivable take only (a non-default, non-work branch contains the prepare tip). The slug-paired squash/rewrite handoff severs containment and is not git-derivable, so it is intentionally the separate possibly-landed (no containment) bucket (human-confirmed) — folding it into taken-over would double-cover that bucket. Added a disambiguating cross-reference here so the two stay distinct.

Comment on lines +14 to +17
<!-- prepare is LOCAL-FIRST by default (no): work stays on local prepare/*
branches — prepare commits locally and does not push. Set yes for the
CROSS-MACHINE OPT-IN: pushing makes the branch a message a second machine's
agent can inherit (cross-machine handoff reads remote refs), and restores

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 841914d: added the missing "that" — "a message that a second machine's agent can inherit."

Comment on lines +112 to +115
# work queue — LOCAL + remote, configured work prefixes (default shown); union by slug.
# Discovery reads remote work refs REGARDLESS of the push default — only PUSHING is gated
# (a leftover or opt-in-pushed remote prepare/* must still be seen; it is what takeover/prune target).
git branch --format='%(refname:short)' \

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in 5202552 (pushed after the commit this review saw, 0d217e9): the parenthetical now reads "it is what takeover/prune targets".

Comment on lines 13 to +16
contained in default) and **leftover worktrees** directly on confirm; surfaces
**dropped / possibly-landed (no containment) / orphaned-accepted** for explicit
confirmation; **stale in-progress** is surfaced read-only and pointed at
**dropped / possibly-landed (no containment) / orphaned-accepted / taken-over
(redundant prepare — a non-work, non-default branch contains the tip)** for explicit
confirmation (never auto-delete on a containment guess); **stale in-progress** is surfaced read-only and pointed at

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in ec58dd2. By design the prune taken-over (redundant prepare) bucket is the git-containment-derivable take only (a non-default, non-work branch contains the prepare tip). The slug-paired squash/rewrite handoff severs containment and is not git-derivable, so it is intentionally the separate possibly-landed (no containment) bucket (human-confirmed) — folding it into taken-over would double-cover that bucket. Added a disambiguating cross-reference here so the two stay distinct.

Comment on lines +144 to +148
**Taken-over (redundant prepare) — surfaced, never auto-deleted.** A
`prepare/<slug>` whose tip a **non-work, non-default** branch contains (a human
carried the work onto their own branch — `isaac/fix-thing`) is **taken over**:
suppressed from auto-work and **surfaced** at `prune`/`converge` (the packet's
`## Need human decisions` / `## Public actions requiring approval`) for the human to

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in ec58dd2. By design the prune taken-over (redundant prepare) bucket is the git-containment-derivable take only (a non-default, non-work branch contains the prepare tip). The slug-paired squash/rewrite handoff severs containment and is not git-derivable, so it is intentionally the separate possibly-landed (no containment) bucket (human-confirmed) — folding it into taken-over would double-cover that bucket. Added a disambiguating cross-reference here so the two stay distinct.

caasi and others added 3 commits June 18, 2026 12:09
…achine's agent can inherit" (Copilot review)

Copilot inline nit (policy.md:17): missing "that" made the cross-machine
opt-in rationale harder to parse in the template init writes into repos.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…derivable take; squash/rewrite → possibly-landed (Copilot review; spec 012)

Copilot flagged that the prune "taken-over (redundant prepare)" bucket defines
takeover via foreign containment only, while the reconciled partition state
"taken-over" also covers the slug-pairing (squash/rewrite) catch — same word,
two scopes, reader-confusion risk.

Resolution (per author — the model is git-containment-based; squash/rewrite is
not git-derivable right now): keep the bucket git-containment-only and add a
disambiguating cross-reference. The non-git-derivable squash/rewrite handoff is
the *possibly-landed (no containment)* bucket (human-confirmed), never this one.

- SKILL.md prune table, git-recipes §prune-sweep bullet, notes-and-packet
  taken-over para: scoped to "git-containment-derivable", squash/rewrite →
  possibly-landed.
- prune.md command desc: same one-line disambiguation.
- test guards for all four (phrase absent at PR-tip).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… the paired-prepare cleanup (review round 5; Codex)

Codex round 5, two points on the round-4 disambiguation:

1. The broad partition state `taken-over` (two sources: foreign containment +
   slug-pairing) shares its name with the prune bucket `taken-over (redundant
   prepare)` (foreign-containment only) — routing stayed ambiguous. Made it
   explicit in SKILL.md's prune table and git-recipes §prune-sweep: the bucket
   takes ONLY the foreign-containment source; the slug-pairing squash/rewrite
   source goes to possibly-landed.

2. Since squash/rewrite is routed to possibly-landed (not git-derivable), the
   surviving paired `prepare/<slug>` needs an explicit cleanup home. The
   possibly-landed bucket now states confirmation also deletes the paired stale
   prepare ref (local + remote, human-confirmed) — matching advanced.md.

- Updated the round-4 test guards to the new wording + added paired-stale guards.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comment on lines +120 to +122
| tip contained in `<remote>/<default>` (or its remote aliases) | **settled** — the work landed | skip; `prune` cleanup candidate |
| a slug-paired branch (by name) exists under a configured `## Accepted Prefixes` **OR** any **non-default, non-work** branch contains the tip | **decided / taken over** — a human carried the work onto their own branch | skip as a candidate; surfaced at `prune`/`converge` (see *taken-over*, Change C) |
| none of the above | **in progress** | candidate: read `context.md`, judge from the narrative |

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 36616c7: the partition row now reads taken over (canonical), with a parenthetical noting the two sources (own-branch carry or slug-paired handoff). Added a refute guard for the retired 'decided / taken over' hybrid.

…e hybrid "decided / taken over" label (Copilot review)

Copilot (SKILL.md:122): the partition table still used the hybrid label
"decided / taken over" while the rest of the docs use "taken over" as the
canonical state name ("decided, awaiting merge" was retired). Relabeled the row
to "taken over" and added a refute guard for the hybrid form.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.

Comment thread CLAUDE.md Outdated
**review-loop:** Assisted, not autonomous, multi-reviewer convergence loop. Local reviewers run first — a Claude subagent always, plus Codex via `codex exec review` (headless) when `codex` is on `PATH`; tmux optional for a live-watch pane — then GitHub Copilot for PR targets. Helper scripts in `skills/review-loop/scripts/` (`copilot.sh`, `pr-comments.sh`) are referenced via `${CLAUDE_PLUGIN_ROOT}`. Target scope is any changed artifact: code or design artifacts (specs, plans, docs). One slash command: `/review-loop [PR# | branch | blank]`. Spec: `docs/superpowers/specs/002-review-loop-plugin-design.md` (Codex channel superseded by `docs/superpowers/specs/003-review-loop-headless-codex-design.md`).

**tsugu:** Git-native skill for unattended work preparation and human–agent convergence (継ぐ — "to inherit / continue / carry forward"). Schema 4 (lineage: 004 → 005 → 006 → 007 → 008). Using git's DAG as the coordination substrate, an agent prepares engineering work privately on git branches (often while the human is away), records evidence in per-ref `context.md`, and promotes durable findings into `knowledge/`; the human then converges — reads prepared branches live, decides what becomes public, and hands the work off for landing in one human-present session. Four routines: `init` (set up the repo's committed `.tsugu/` workspace + `policy.md`; idempotent; migrates older schemas), `prepare` (private git work on the configured work-prefix branches — default `prepare/*` — that **gathers understanding** rather than finalizing: investigation, root cause, option space, trade-offs, with reference code optional/partial and a scope-only branch a first-class outcome; + tests + evidence; recurses into `.tsugu/`-bearing submodules; external silence), `converge` (read branches live, present status view, decide with the human, accept/park/drop, promote knowledge; **accept defaults to a handoff-only rename** — `git branch -m prepare/<slug> <accepted-prefix>/<slug>`, agent does not rebase/verify/push/PR — with a **human-marked maintenance exception** that unlocks the complete-to-ready path; the agent never self-classifies maintenance; invokes no skill — the human triggers skills by keyword), `prune` (human-present, read-only-until-approved sweep of unused local + remote branches: deletes settled / leftover-worktree on confirm, surfaces dropped / possibly-landed / orphaned-accepted for explicit per-item confirmation, never touches unfinished work). Committed `.tsugu/` is a **WIP-knowledge layer** — a richer, agent-maintained sibling of `AGENTS.md`/`CLAUDE.md` — holding exactly: `policy.md` + `context.md` + `knowledge/`. Everything about how Tsugu operates for one human lives in a **personal global folder** (`~/.claude/tsugu/<project-key>/`, per machine, never committed): observation sources, opt-in skills, and the converge packet (derived, regenerated live). **State is single-layer** — no status fields, no intake notes, no `runs/`, no recorded landed-SHA; settled = containment in the default branch; pending = slug-paired accepted branch. A forced-squash severs containment derivation, so the work re-surfaces live at each `converge` until the human confirms landing (retain the accepted branch; no SHA recorded). Accepted-prefixes default `feature/* bugfix/* chore/*`. **Merge commits are recommended.** **Never auto-merges** (no public coordination without approval). **Light / script-free** — recipes are documented guidance; no scripts shipped. **Invokes no user-installed skill by default** — native git + its own built-in subagents only; a human's **personal config** (`~/.claude/tsugu/<project-key>/config.md`) may opt in to named skills per machine. Four slash commands: `/tsugu:init`, `/tsugu:prepare`, `/tsugu:converge`, `/tsugu:prune`. Spec: `docs/superpowers/specs/004-tsugu-skill-design.md` + `docs/superpowers/specs/005-tsugu-agent-first-design.md` + `docs/superpowers/specs/006-tsugu-workspace-transfer-design.md` + `docs/superpowers/specs/007-tsugu-thin-core-design.md` + `docs/superpowers/specs/008-tsugu-submodule-recursion-design.md` + `docs/superpowers/specs/011-tsugu-handoff-converge-design.md`.
**tsugu:** Git-native skill for unattended work preparation and human–agent convergence (継ぐ — "to inherit / continue / carry forward"). Schema 5 (lineage: 004 → 005 → 006 → 007 → 008 → 011 → 012). Using git's DAG as the coordination substrate, an agent prepares engineering work privately on git branches (often while the human is away), records evidence in per-ref `context.md`, and promotes durable findings into `knowledge/`; the human then converges — reads prepared branches live, decides what becomes public, and hands the work off for landing in one human-present session. Four routines: `init` (set up the repo's committed `.tsugu/` workspace + `policy.md`; idempotent; migrates older schemas), `prepare` (private **local-first** git work on the configured work-prefix branches — default `prepare/*`, kept local by default and pushed only on the cross-machine `push-prepare-branches: yes` opt-in — that **gathers understanding** rather than finalizing: investigation, root cause, option space, trade-offs, with reference code optional/partial and a scope-only branch a first-class outcome; + tests + evidence; recurses into `.tsugu/`-bearing submodules; external silence), `converge` (read branches live, present status view, decide with the human, accept/park/drop, promote knowledge; **accept defaults to a handoff-only rename** — `git branch -m prepare/<slug> <accepted-prefix>/<slug>`, agent does not rebase/verify/push/PR — with a **human-marked maintenance exception** that unlocks the complete-to-ready path; the agent never self-classifies maintenance; invokes no skill — the human triggers skills by keyword), `prune` (human-present, read-only-until-approved sweep of unused local + remote branches: deletes settled / leftover-worktree on confirm, surfaces dropped / possibly-landed / orphaned-accepted for explicit per-item confirmation, never touches unfinished work). Committed `.tsugu/` is a **WIP-knowledge layer** — a richer, agent-maintained sibling of `AGENTS.md`/`CLAUDE.md` — holding exactly: `policy.md` + `context.md` + `knowledge/`. Everything about how Tsugu operates for one human lives in a **personal global folder** (`~/.claude/tsugu/<project-key>/`, per machine, never committed): observation sources, opt-in skills, and the converge packet (derived, regenerated live). **State is single-layer** — no status fields, no intake notes, no `runs/`, no recorded landed-SHA; settled = containment in the default branch; taken-over = a non-default, non-work branch (an accepted-prefix handoff **or** a human's own branch) contains the tip, so a human now owns it (slug-pairing is the complementary catch for a history-rewriting landing). A forced-squash severs containment derivation, so the work re-surfaces live at each `converge` until the human confirms landing (retain the accepted branch; no SHA recorded). Accepted-prefixes default `feature/* bugfix/* chore/*`. **Merge commits are recommended.** **Never auto-merges** (no public coordination without approval). **Light / script-free** — recipes are documented guidance; no scripts shipped. **Invokes no user-installed skill by default** — native git + its own built-in subagents only; a human's **personal config** (`~/.claude/tsugu/<project-key>/config.md`) may opt in to named skills per machine. Four slash commands: `/tsugu:init`, `/tsugu:prepare`, `/tsugu:converge`, `/tsugu:prune`. Spec: `docs/superpowers/specs/004-tsugu-skill-design.md` + `docs/superpowers/specs/005-tsugu-agent-first-design.md` + `docs/superpowers/specs/006-tsugu-workspace-transfer-design.md` + `docs/superpowers/specs/007-tsugu-thin-core-design.md` + `docs/superpowers/specs/008-tsugu-submodule-recursion-design.md` + `docs/superpowers/specs/011-tsugu-handoff-converge-design.md` + `docs/superpowers/specs/012-tsugu-local-first-prepare-design.md`.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 15231d0: CLAUDE.md's prune list now includes taken-over (redundant prepare).

Comment on lines +23 to +26
<!-- human-workflow branches the handoff RENAMES prepare/<slug> into for PRs
(converge renames, never cuts). A branch here with the same slug as a work
branch = that work is decided, awaiting merge. -->
branch = that work is taken over (a handoff the human owns; converge
surfaces it as awaiting-merge). -->

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 15231d0: the template comment now names the target — "RENAMES prepare/ into — / — for PRs."

Comment thread plugins/tsugu/skills/tsugu/SKILL.md Outdated
@@ -111,19 +111,23 @@ The core routine. No human is present, so Tsugu does its own git work directly a

1. **Fetch first** (`git fetch --prune <remote>`), resolving `<remote>` + `<default>`, so every read below uses fresh remote-tracking refs, not a stale checkout.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 15231d0: step 1 reworded — fetch refreshes the remote half, and the queue is read local + remote (step 3), not remote-tracking only.

Comment thread plugins/tsugu/skills/tsugu/SKILL.md Outdated
- **Pending pairs by slug, not commits.** The accepted branch shares the work branch's slug; ref names are write-once identity, so the pending state survives anything the forge does to commits (PR-branch rebases, squashes, force-pushes). Containment in refs *outside* the configured accepted prefixes carries no derived meaning.
- **Handoff *is* a takeover; slug-pairing is the complementary catch.** Containment by **any** non-default, non-work branch is the **takeover** signal (Change B) — and that branch may equally be an **accepted-prefix** ref (a `converge` handoff) **or** a human's own branch (`isaac/fix-thing`): either way a human now owns the work and tsugu stops managing it. There is no separate *pending* classification competing with takeover. Slug-pairing stays only as the **complementary** catch for the squashed / rewritten take — where the rewrite severed containment but the accepted branch's name still pairs (ref names are write-once identity, so the pairing survives any commit rewrite the forge does). Both paths land in the **same** disposition: skip as a candidate, surface at `prune`/`converge`, never auto-delete.
- **Core assumes merge commits.** Tsugu **recommends merge commits — do not squash-merge tsugu-managed branches**; preserved history is what makes settlement containment-derivable. Accept is **mode-agnostic** (the work branch is renamed to the accepted branch in **both** `include` and `exclude` mode — see converge / Change B), so settlement reads off the **accepted branch's** containment in default in both modes. Because that disposition can only be read *while the slug-paired accepted ref survives*, **recommend disabling the forge's auto-delete-head-branch for tsugu accepted branches**. *A landing that rewrites history (squash, rebase-before-merge, force-push) — or, in `exclude` mode, the human stripping `.tsugu/` into a fresh published branch before merging — breaks containment-derived settlement; the item then settles via `prune`'s possibly-landed (no containment) — confirm bucket; see `references/advanced.md`.*
- **No marker — the partition + conservative judgment guard (see B1a).** Default handoff writes **no** marker (it renames and stops). A scheduled `prepare` is kept off handed-off work by the two-fact partition: **containment** (a merge-commit landing → settled) or **slug-pairing** (the retained accepted branch, **enumerated local + remote** → decided). For a **non-containment landing** (a rewrite, or an `exclude`-strip) the retained accepted branch still pairs by slug; the advanced narrative backstop is only a **legibility** note on the accepted branch, **not** a resume-guard (a note there can't reach a surviving remote `prepare/<slug>`). The one signal-less residual is the accepted ref **deleted** *and* B3 not yet run, so a stale remote `prepare/<slug>` survives: held to the **same 004–008 guarantee level** — `prepare`'s judgment leans conservative (leave the stale branch for `converge`; reversible; never auto-merges), and running **B3 promptly** removes the ref (once no ref survives, the item just leaves the partition — the work is already in `<default>`). *Judgment, not a written status, governs.*

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 15231d0: '→ decided' retired to '→ taken over' (canonical state).

Comment on lines 96 to +100
customized them. **Also enumerate the configured `## Accepted Prefixes`**
(defaults: `feature`, `bugfix`, `chore`, plus legacy `public`) into a
**separate accepted list** — these are not queue items but are needed in step 6 to
pair a work branch's slug against a decided accepted branch. **The accepted list
spans LOCAL *and* remote refs** (unlike the work queue, which is remote-tracking
only): at `converge`, the just-renamed `<accepted-prefix>/<slug>` exists **locally**
spans LOCAL *and* remote refs** — as does the work queue itself (012 unions

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 15231d0: 'a decided accepted branch' → 'its accepted branch (a taken-over handoff)', aligning with the SKILL.md partition terminology.

Comment on lines 55 to 56
@@ -56,8 +56,9 @@ The **human-workflow** namespaces the handoff **renames** `prepare/<slug>` into
the human's PR (converge renames, never cuts).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 15231d0: now reads 'renames prepare/ into — / — for the human's PR', matching the template.

…rename grammar (Copilot review)

Copilot's pass on the reconciled HEAD found six terminology/grammar leftovers
the model retirement missed:

- SKILL.md:112 prepare step 1 — "remote-tracking refs" reframed to local + remote.
- SKILL.md:132 + git-recipes:100 — "→ decided" / "decided accepted branch"
  retired to "taken over".
- SKILL.md:44 + notes-and-packet:8 — partition state-list "in progress /
  decided / settled" → "taken over".
- git-recipes:132 + migrations:89 — legacy public/* "decided/landed" →
  "taken-over/landed".
- policy-and-intake:56 + templates/policy.md:23 — "renames prepare/<slug> into
  for PRs" (missing target) → names <accepted-prefix>/<slug>.
- CLAUDE.md prune list now includes the taken-over (redundant prepare) bucket.

Added dir-wide guards: no residual "decided" partition label, no "into for"
grammar, CLAUDE.md prune list carries taken-over.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Comment on lines +115 to +118
git branch --format='%(refname:short)' \
| grep --extended-regexp "^(prepare)/" # local-first (default)
git branch --remotes --format='%(refname:short)' \
| grep --extended-regexp "^<remote>/(prepare)/" # pushed mode
| grep --extended-regexp "^<remote>/(prepare)/" # cross-machine opt-in mode (pushed)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b7e9339: appended || true to the work-queue enumeration greps (local + remote) so an empty queue doesn't abort the recipe under set -euo pipefail.

Comment on lines 123 to 126
git branch --format='%(refname:short)' \
| grep --extended-regexp "^(feature|bugfix|chore|public)/" # local accepted
git branch --remotes --format='%(refname:short)' \
| grep --extended-regexp "^<remote>/(feature|bugfix|chore|public)/" # remote accepted

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b7e9339: same || true applied to the accepted-branch enumeration greps. Added a test guard that no grep --extended-regexp enumeration line is left unguarded.

… true) (Copilot review)

Copilot (git-recipes:118, :126): the work-queue and accepted-list enumeration
greps (`git branch | grep --extended-regexp ...`) exit 1 on an empty queue —
a valid outcome — which aborts the copy/pasted recipe under the set -euo
pipefail the doc itself calls out in §6. Same class as the round-3
foreign_contains fix. Appended `|| true` to all four enumeration greps and
added a guard that no `grep --extended-regexp` enumeration line is unguarded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

@caasi caasi merged commit 3ff817f into main Jun 18, 2026
1 check passed
@caasi caasi deleted the feat/tsugu-local-first-prepare branch June 18, 2026 06:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tsugu: cold-start discovery must search for human branches, not just prepare/* (avoid re-working / auto-pushing already-converged work)

2 participants