zheref

jujisho

Split a mixed working copy into up to two stacked branches and PRs, by axis, leaving nothing behind. Use when the maintainer invokes hatsu:jujisho, or asks to split these changes, separate these concerns, or break this into separate PRs. Kurapika names every axis it finds, stacks the second PR on the first, states the merge order in both bodies, and asks which two when there are more. Never merges, never drops a hunk.

zheref 0 Updated 3w ago
GitHub

Install

npx skillscat add zheref/hatsu/jujisho

Install via the SkillsCat registry.

SKILL.md

Shared policy location: docs/DISCOVERY.md, docs/WORKFLOW.md,
docs/LAUNCH-MIGRATION.md, docs/STANDALONE-ENTRY.md, docs/PROCESS.md and docs/SURFACES.md belong to the resolved Hatsu plugin
root
, not the consuming repository. On an installed surface, use the absolute root printed by
ten to read those files (re-resolve through that skill if unavailable). Relative links
below identify source locations; a missing consumer docs/ copy is not a missing policy and must not
trigger a duplicate filing. Never copy or invent a second policy in the target repository.

Jujisho — one working copy, two stacked PRs, nothing left behind

Nature: Enhancer for product code, Conjurer for spec/governance, Transmuter for
machinery — and Manipulator for the GitHub-side mechanics (branch, PR, retarget) once the axes
are named. A mixed working copy routinely needs more than one; say which mode leads when it does.

Separate what I have by what it is actually about, and leave no dot unconnected.

`hatsu:tensho` treats the working copy as one effort. jujisho is for when it
is not.


Composition — the phases this skill already is

Read from the phase lattice rather than from this file's own numbering, jujisho is
`hatsu:tensho`'s composition run once per axis:

per axis: `kokusen` → `aka` →
`mukai` → offers `en` — A first, B stacked on A

This is a restatement, and it changes no mechanics. Every section below stands exactly as it is
written. What the line adds is three things the per-axis reading makes explicit:

  • nen split verify runs before any axis reaches kokusen's phase, not per axis afterwards.
    § 2's proof is over the whole original diff, once — OK means every hunk lands in exactly one
    branch, and nothing is opened until it reads OK. Splitting first and proving per branch would
    prove each branch internally consistent while leaving the union short, which is precisely the
    silent leftover this skill exists to prevent.
  • The second PR is stacked, so B's mukai runs against A's branch as its base, not main (§ 4).
    B's own gate derivation, body and evidence are B's; what it inherits from the stack is the base and
    the merge order, stated in both bodies as an instruction.
  • en runs on A first (§ 7). Driving B to Ready while A is unmerged produces a readiness verdict
    against a base that is about to change; B's watch starts once A's merge retargets it.

The human calls stay human calls. aka and mukai are the maintainer's
(`docs/WORKFLOW.md` § 4), and hatsu:jujisho is one call that spends
both of them per axis — which is why § 3's cap of two, and its ask when there are more, is a
question about how much the one call is being asked to cover.


1. Invocation

hatsu:jujisho

No arguments. The working copy is the input — there is nothing here for nen parse to check
(that verb validates a skill's own flag grammar; jujisho takes none).

The base is main for two independent efforts, and integration/<chore> when the two legs are
one logical effort
— the CON-36 case in § 4. Decide that before cutting branches, because it
changes what A is based on and what "done" means: for a chore, both legs land on the chore branch
and the maintainer's gate is the integration/<chore> → main delivery PR, not either leg.

Run nen wc classify --repo <path> --base <target base> first, exactly as
`hatsu:tensho` does: it reports whether the checkout is must-move (on the
trunk, dirty — cut every axis branch from the target base, never reflexively from main),
on-branch-dirty (uncommitted work sits on an existing branch — read the evidence it hands back
before assuming any of it is one of the axes), or on-branch-clean. It is a report, not a guard;
the judgment of which axis the existing branch's commits belong to stays yours.

The phases this composite calls SKIP their own ## 0. Standalone entry sections. `docs/STANDALONE-ENTRY.md` is the
contract for a phase typed by hand into an arbitrary checkout; a phase reached from here inherits P1–P4
from this run — the warm-up, the orientation, the change set and every derived argument — and
re-deriving them would produce a second answer to a question this composite already settled. Each phase
says which composite is holding it instead.

2. Find the axes — then say what you found before touching anything

An axis is what a change is about: a subsystem, a concern, a layer, a bug versus the
refactor it sits on, spec versus the machinery implementing it. Derive axes from the diff, not
from file paths — two files in one directory are routinely two concerns, and one concern routinely
spans four directories.

Report every axis before splitting: its name, the hunks it owns, and one line on what makes it
one thing. This is the step the maintainer checks; the mechanics after it are just git.

A change that cannot be cleanly separated is not split. If axis B's code does not compile
without axis A's, they are one effort delivered in sequence — which is what stacking is for (§ 4),
not a reason to force a cut through the middle of a function.

Prove it, before opening anything — nen split verify

Nothing is left behind — this is the rule the skill exists for. Every hunk, every new file,
every deletion, every untracked file lands on exactly one axis. A leftover hunk is a silent bug,
because it is invisible in both PRs and will surface later as an unexplained local change. If a
hunk genuinely belongs to both axes, it goes on the lower one in the stack, and both bodies say
so.

This used to be a claim the skill asked the reader to trust by eye. It no longer is:

git diff <base> > original.diff                       # BEFORE cutting any branch
# … cut each axis branch, commit only that axis's hunks …
git diff <base>...<axis-A-branch> > axis-a.diff
git diff <base>...<axis-B-branch> > axis-b.diff
nen split verify --original original.diff --branches axis-a.diff,axis-b.diff

nen split verify proves every hunk in --original lands in exactly one of --branches, same
body, and reports MISSING / DUPLICATED / ALTERED / extra otherwise. Exit 0 means proven;
exit 1 means the split is incomplete and nothing is opened until it reads OK.

RETIRED at nen 0.7: an unreadable diff that read like a failed split

A --original or --branches file this verb cannot read is exit 2, not 1 — and the two
now say different things. Through the pinned v0.6.0 a mistyped path escaped as a raw errno under
the code that means "the thing you asked for did not work", for what is "you typed it wrong":

v0.6.0  $ nen split verify --original /nope/a.diff --branches /nope/b.diff        # exit 1
        nen: could not read --original '/nope/a.diff': Error: ENOENT: no such file or directory, open '/nope/a.diff'

0.7.0   $ nen split verify --original /nope/a.diff --branches /nope/b.diff        # exit 2
        nen split: could not read '/nope/a.diff' (ENOENT). --original names the diff every branch is
        compared against, so an unreadable one is refused rather than compared against nothing.

Verified live at the pinned 0.7.0 (docs/ab/jujisho.md § Retired at nen 0.7). This verb's
whole answer is a comparison between files
, so one of them being absent is a question that was
never asked rather than a verdict that came out negative — which is exactly the reading this skill
needs, because 1 is the code that stops the split from being opened. So: exit 1 is a real
finding about the split and stops everything; exit 2 is a mistyped path — fix the invocation and
re-run, and never report it as an incomplete split.
A branch file that could not be read now
says WHICH one.

The path flags move with it: a relative --original/--branches resolves against --repo's
root
from nen 0.7, not the process's cwd (zheref/nen#100) — so pass --repo, or pass
absolute paths as the block above does.

Defect this port filed against v0.1.0, closed by nen v0.2.0 (#61, closes zheref/nen#21).
At the port a multi-file --original misparsed every file but the last (a false ALTERED reading
line N: original "(absent)" vs branch "" one past the hunk's true end), and the same false
ALTERED fired at a hunk boundary inside one file whenever a hunk was genuinely missing —
reproduced five ways, docs/ab/jujisho.md § 2.1–2.2. The cause was the hunk-body terminator: the
parser stopped a body at the next header rather than at the @@ counts, and v0.2.0 fixed exactly
that. Verified live at v0.3.0 with a hand-written two-file original split across two axis
diffs: files: 2 in original, 2 across branches / OK -- every hunk in the original lands in exactly one branch, unaltered, and nothing extra was found. (exit 0); with the second axis omitted:
MISSING (in original, in no branch): b.txt @@ -1,2 +1,3 @@ and exit 1, with no false ALTERED
beside it. The per-file-slice workaround is retired: run the proof once, over the whole
original
, and read OK as proven and any MISSING/DUPLICATED/ALTERED/extra line as the split
being incomplete. The report also prints the file count on each side (files: N in original, M across branches), which is the file-set check the workaround used to do by hand. If a bare
ALTERED on a hunk you know is byte-identical ever reappears, that is a new finding to file with
the two diffs attached — not a reason to trust the split.

3. Three or more axes — report, then ask

The cap is two. When the working copy genuinely has three or more axes, do not silently merge two
unrelated ones to satisfy the cap
— that is the unconnected-dots failure in a different costume.

Name every axis with what it carries, then ask through the harness's question interface, with a
recommendation: combine two named axes (say which and why they are closest), defer one to a
follow-up run, or lift the cap for this run. The maintainer's answer decides.

4. Stacking — always, and what it costs

PR B targets PR A's branch, never main. So B's diff shows only B's change, and a reviewer sees
one concern at a time — which is the whole point of splitting.

Order the stack by dependency, then by unblocking power: A is the one B needs, or if neither
needs the other, A is the one whose merge frees the most.

State the cost in both bodies, plainly:

  • B cannot merge until A does, and retargeting it is a step you take. GitHub only auto-retargets
    a PR when its base branch is deleted, which depends on the repo's delete-on-merge setting and
    on the maintainer not keeping the branch. Do not rely on it: after A merges, run
    nen pr retarget --target <owner/name> --pr <B> --base <branch.base> (the trunk nen/workflow.json
    names, never a literal main; resolve <owner/name> with
    nen repo resolve --from <checkout> rather than reading git remote -v by hand) and confirm the
    base changed — nen exposes no read-back for a PR's current base, so gh pr view <B> --json baseRefName remains the confirmation step (docs/ab/jujisho.md § 3 residue). A stacked PR
    silently left pointing at a merged branch shows an empty or nonsensical diff, and reads as
    "already done". This paragraph is the one owner of the retarget mechanism: getsuga § 3 cites
    it for a stacked release proposal (zheref/hatsu#98) and adds only that proposal's own rule.
  • If A moves, B's base moves under it. When A takes review commits, or main cascades into A,
    cascade A into B in the same pass, by merge, never by rebase — a rebase of A orphans B's
    history and is the failure CON-21 exists to prevent. With B checked out:
    nen pr cascade-main --repo <path> --trunk <A's-branch-name> — the verb's --trunk override
    makes it merge (never rebase) whatever branch you name into the current one and push on a clean
    merge, reporting a conflict rather than resolving it. It is written for merging main in; naming
    A instead is exactly this skill's use of it.
  • B's checks are only meaningful against A's current head. A red check on B that is really A's
    problem is reported as such, not iterated on.

When the two legs are one logical effort, this is a CON-36 chore — say so. Two PRs
delivering one unit of framework work belong on an integration/<chore> branch, where the
delivery PR is integration/<chore> → main and is the maintainer's gate. Stacking two
main-bound PRs and stacking two legs of a chore look identical in git and are governed
differently. Name which one this is, in both bodies.

5. Staging discipline

Every rule from `hatsu:tensho` § 3 applies unchanged, per branch: run
nen stage triage --repo <path> [--scope <axis's own paths>] [--mentions "<commit/PR draft text>"]
before staging each axis. It detects, never decides — secret shapes, git-ignored files,
binaries, out-of-scope paths, unmentioned deletions — and exits 1 on anything flagged. A
flagged file is never committed without an explicit yes
; that yes is the skill's to give, never
the verb's.

RETIRED at nen 0.7: the local-config category with no detector. nen stage triage carries
local-config (the .local filename infix — settings.local.json, .env.local,
config.local.yml) and large (at or over --large-bytes, default 1 MiB) as detectors of
its own, so the by-eye pass this skill and `hatsu:tensho` § 3 each ran is
gone. Verified live at the pinned 0.7.0 (docs/ab/kokusen.md § Retired at nen 0.7). Read
the flags per axis and take one answer per flagged path
; a path flagged on one axis is flagged
on whichever axis actually carries it, so run the triage per axis rather than once for the whole
working copy.

Format each commit with nen commit format --repo <path> --type <t> --subject "<...>" [--scope <s>] [--body "..."] [--trailer Hatsu-Agent=<responsible-persona>] —
it validates shape (declared type, non-empty subject under 72 characters, no trailing punctuation),
never content; what changed and why stays yours to write.

Branches are kurapika/<slug> per axis (the local plane's own convention — not CON-27, which
governs worktree isolation), slugs from the axes, not part-1/part-2 — the names are what a
reader sees in the PR list.

6. The PRs

Both carry what the target's PR template requires (e.g. schemas/templates/pr.md — a template is not
one of the four files nen's schemas/→nen/ migration moved) — # What this changes for you
(CON-17(a)), ## How to verify (CON-17). Check whether a changelog.d/ fragment is owed with
nen changelog fragment-required --files <this axis's changed paths> --spec-paths "CONSTITUTION.md,handbooks/,nen/,schemas/,agents/,.github/workflows/" --fragment-dir changelog.d --head-changelog CHANGELOG.md (CON-33(a)) rather than eyeballing the path list by hand — it
answers per axis, since the two PRs' changed-path sets differ. --spec-paths is a literal prefix list
outside nen's taxonomy fallback, so it names both nen/ and schemas/ for the whole v0.3 line
(`tensho` § 5 says when to drop the second).

Additionally, each body carries the split itself:

  • The axis this PR is, in one line.
  • The sibling, formatted with nen ref format --code <CODE> --kind PR --number <A|B> [--state <s>] rather than hand-typed object notation, linked, with the relationship: "stacked on
    <CODE>-PR-#<A>; merge that first."
    / "<CODE>-PR-#<B> is stacked on this; retarget it to
    the final base once this merges."
  • What was split off and why, so a reviewer knows what they are not looking at and does not
    file the absence as a gap.
  • The merge order, stated as an instruction, not an implication.

7. After

Report both PRs with the stack drawn, then hand them to
`hatsu:sharingan` — A first. Driving B to readiness while A is unmerged produces a PR that is ready
against a base that will change; take A to its gate, and start B once A's merge retargets it.

8. Authority

  • Permitted: branch, commit, push non-main branches, open PRs, request reviewers (nen pr request-reviews), cascade A into B, retarget B once A merges, and everything sharingan is
    permitted.
  • Not permitted: bankai:agent/*, bankai:stage/*, G1 mode labels, any merge (including
    merging A to unblock B), any review vote (never request_changes — Manipulator's standing rule).

9. Hard limits

  • Never leaves a hunk behind. The union of the splits equals the original diff, proven by
    nen split verify over the whole original — the multi-file defect § 2 records is closed since nen
    v0.2.0, and the proof runs once.
  • Never opens a PR for an axis it has not proved through the repository's declared verbs — nen shu build/test/lint per axis branch, as `tensho` § 5 does for one; a
    repository with no declaration runs its own documented commands, said so.
  • Never merges two unrelated axes to fit the cap — it reports and asks.
  • Never rebases a pushed branch; A reaches B by merge (nen pr cascade-main --trunk <A>,
    CON-21).
  • Never opens B against main while A is unmerged.
  • Never commits a flagged file without an explicit yes.
  • Never merges A to unblock B — A merges at its own terminus through en § 5, never earlier.

Categories