Turn a dirty working copy into one PR standing ready at its gate. Use when the maintainer invokes /tensho <target-branch|main>, or asks to branch this off, commit and PR what I have, or open a PR for these changes. Kurapika moves the work off main if needed, reviews every uncommitted file before staging it, commits, opens the PR with the body the template requires, then checks it against its gate. Never merges, never commits a file it flagged without an answer.
Install
npx skillscat add zheref/hatsu/surfaces-antigravity-skills-tensho Install via the SkillsCat registry.
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.
Tensho — a dirty working copy becomes a PR at your gate
Nature: Manipulator carries every run — branching, staging, committing, opening and requesting
review on a PR is GitHub-side operation by definition, whichever nature authored the diff.
Which authorship nature the diff itself is: Enhancer (product code), Conjurer (governance/
canon — CONSTITUTION.md, handbooks/, nen/ (the taxonomy), agents/), or
Transmuter (machinery — workflows, scripts, hooks, scaffolding, a nen/contract.json project
block). nen gate derive (§ 5) narrows this to G2-or-G4 and names
which path set hit; Conjurer-vs-Transmuter inside a G4 hit is judgment this skill states, never
Nen's to decide. Say the pair when a diff genuinely spans both, and which one leads.
Take what I have here and turn it into one PR, ready for me.
Where `/jujisho` splits a working copy into several efforts, tensho
treats it as one. If the
changes are plainly two unrelated efforts, say so and offer jujisho rather than filing one PR that
carries two concerns.
Composition — the phases this skill already is
Read from the phase lattice rather than from this file's own numbering, tensho is:
`kokusen` (if the tree is dirty) → `aka` →
`mukai` → offers `en` (starts it undermukai.autoEn)
This is a restatement, and it changes no mechanics. Every section below stands exactly as it is
written: § 2's nen wc classify reading, § 3's staging triage, § 4's commit shaping, § 5's body
checks and gate derivation, § 6's handover. What the line adds is where each of them lives once
the lattice is the map — the triage-and-commit half is kokusen's phase, the push half is aka's,
the review-tests-coverage-evidence-and-PR half is mukai's, and the drive to Ready is en's
composing `sharingan`, which is what § 6 already hands to.
Two consequences follow, and neither is new:
- The human calls stay human calls.
akaandmukaiare the maintainer's
(`docs/WORKFLOW.md` § 4), so/tenshois itself a human call
that spends both of them at once — which it always was, since it commits, pushes and opens a PR
in one run. It is not a way to reachakaormukaiwithout asking; it is the maintainer asking
for all of it in one word. - A step that stops ends the run where it stopped, and the stop belongs to the skill that owns
the phase — a flagged file in the triage, a red required test, a semantic conflict, coverage under
the minimum, an unsettled finding. Tensho adds no stop of its own.
1. Invocation
/tensho <target-branch> # default: mainThe argument is the PR's base, not the source. main is the default and the overwhelming
case; an integration/<chore> or integration/epic-<n> base is the other real one, and it means
the work is a leg of a chore or epic rather than a standalone change — say so, because it changes
what "done" looks like: the delivery PR is the integration branch's, not this one's.
The base is also where the branch is cut from. A branch cut from main and opened againstintegration/<chore> carries every commit on main that the chore branch lacks, so the PR's diff
is the whole divergence rather than the change. Cut from the base you are targeting, always, and
re-fetch it first.
A missing argument or configuration item is asked for and set up inline (missing-argument,missing-configuration; `WORKFLOW.md` § 4 Ask, set up, continue); a
product code the registry lacks is set up through nen/repos.json's own key.
nen parsewas considered for this grammar and declined — and nenv0.2.0(#67) made the
refusal explicit rather than silent. At the portnen parse tensho --grammar "[<target-branch>]" --line ""refused with<target-branch> is required, and--grammar "onto [<target-branch>]" --line "onto"misread the literal as the value. Verified live atv0.3.0: the bare-bracket template
is now refused at the template, by design — "template '[]' is refused: its
leading slot is bracketed but nothing introduces it, so an omitted value cannot be
told apart from a mistyped one. Anchor it behind a literal ('word []') or drop the
brackets" (exit2) — and the anchored formonto [<target-branch>]parsesontowith the clause
absent. Tensho's own grammar is one optional word with a fixed default and no introducing
literal, which this engine deliberately will not express — so the default-to-mainhandling below
stays this skill's own rule, in prose, not anen parseinvocation. Not a finding any more; a
documented boundary.
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. Where the work goes
Read the current checkout before moving anything:
nen wc classify --repo <path to the checkout> --base <target-branch>Reports exactly one of three cases — never a guard, never a guess:
nen wc classify case |
What tensho does |
|---|---|
must-move — on the trunk, dirty |
Must move. Cut kurapika/<slug> from the target base, not reflexively from main, and take the changes with it. Kurapika never pushes main |
on-branch-dirty — on a branch, uncommitted work present |
The verb reports the branch's existing commit subjects and the uncommitted paths as evidence, and says outright it is not the verb's to decide whether they are the same effort. Commit on top and reuse the branch when they are; say so and cut a fresh branch from the target base for a genuinely different effort — smuggling a second concern into an open PR is how a reviewer ends up approving something nobody described |
on-branch-clean — nothing uncommitted |
Nothing to commit. Open or report the PR and go to § 6 |
"Same effort" is a judgement, so show the evidence rather than asserting it: the branch's
existing commit subjects and the uncommitted paths the verb prints, plus one line on why they are
or are not one thing. If it is genuinely ambiguous, ask — one round-trip is cheaper than a
mis-scoped PR.
A git command that genuinely fails — a detached HEAD, a --base that does not resolve — is
never folded into one of the three cases as an empty reading; nen wc classify reports it as an
error on stderr and exits non-zero instead (verified live, docs/ab/tensho.md § 2.1).
Branch naming is kurapika/<slug>, <slug> from the change, not from the date — the local plane's
own convention, not a CON-{n} rule.
Resolving the product code — needed for object notation (§ 5) and, where GitHub calls are
involved, for --repo/--gh-repo — is nen repo resolve's job:
nen repo resolve --repo <path> --from <path> # matches the checkout's own 'origin'
nen repo resolve <CODE> --repo <path> # matches an explicit code insteadFinding this port filed against
v0.1.0, closed by nenv0.2.0(#66, closes zheref/nen#27). At
the portnen repo resolve --repo <path> --from <path>(no token), standing inside a<reference-repo>checkout, matched the working copy'soriginonly against the registry'sconsumers[]entries and refused the registry's own repo withthat is not in this registry, even
thoughBCwas a listed code. Sincev0.2.0every token — the origin included — resolves from
everything the registry records (consumers,product_codeskeys and values,maintained_tools,pending_onboarding;nen repo --help, andsrc/repo/resolve.tsrule 5 names this exact case). The
token form is verified live (nen repo resolve BC --repo <path>→<reference-repo> (BC) via code); the
origin form needs a checkout whoseoriginthe registry records and was not re-run here. Either form
works from inside the registry-owning repo now; if the origin form ever refuses a repository its own
code list names, pass the code explicitly and reconcile the finding through DISCOVERY.md;
unavailable prerequisites remain a pending record, not a claimed new issue.RETIRED at nen
0.5: a target with NO registry refuses at exit2, naming the file — where throughv0.4.0it failed at exit1, indistinguishable from an unresolved token. Read1as the registry
opened and the token is not in it and2as there is no registry here; a registry present but
malformed stays1. Either is the trigger to ask for the code and set the entry up (§ 1), not the end.
3. Staging — every file is looked at, and some are asked about
Never git add -A blind.
nen stage triage --repo <path> [--scope <in-scope prefixes>] [--mentions "<commit/PR draft text>"]Detects, never decides, and exits 1 whenever anything is flagged — verified live against a
constructed working copy carrying one of each (docs/ab/tensho.md § 2.2):
Flag nen stage triage reports |
Trigger, verified live |
|---|---|
secret-shape |
.env, *.pem, *.key, credentials*, or a token/key shape in the diff |
ignored |
The path is git-ignored and would need -f to stage |
binary |
The file's content is binary |
out-of-scope |
The path falls outside every --scope prefix given — omitted entirely when --scope is not passed at all (no scope declared, nothing to compare against) |
unmentioned-deletion |
A tracked path was deleted in the working copy and its basename does not appear in --mentions's text |
One path can carry more than one reason at once (verified: a deleted, out-of-scope file reports
both out-of-scope and unmentioned-deletion in the same row).
Present every flagged file at once, with the reason(s) nen printed, and take one answer per
file. A flagged file is never committed without an explicit yes — not "it was probably fine,"
not "it was in the diff already." That yes is never the verb's to give (its own --help says so
verbatim), and it stays this skill's. secret-shape is the one flag category that is never
askable — the per-file ask above applies to the other flag categories only. There is no yes forsecret-shape; the answer is always no, and the fix is to rotate or remove the secret, never to
stage it. § 8's hard limit is authoritative here: a secret is never committed, full stop.
RETIRED at nen
0.7: the two flag categories with no detectorBoth are detectors now — this skill and `/kokusen` § 4 were each
compensating for the same gap by eye, which is what made it a gap rather than a preference:
local-configmatches the.localfilename infix (settings.local.json,.env.local,config.local.yml, a barenotes.local). It is a FILENAME check like the secret shape, not a
directory rule —.claude/and.vscode/hold committed project configuration as often as
personal settings, and flagging every file under them would bury the rows that need a decision.
So.claude/settings.local.jsonis caught by its name, and a.claude/settings.jsonthat
belongs to the project is not.largeflags a file at or over--large-bytes, default 1 MiB. The same 2.6 MB
constructed fixture that reported clean atv0.6.0comes backbig.txt [large]at the
pinned0.7.0(verified live,docs/ab/kokusen.md§ Retired at nen 0.7). A path the verb
could not measure — a deletion, a broken symlink — is never flaggedlarge.Ask about them the same way as every other flag, one answer per path, from the reasons the
verb printed. The by-eye pass is retired; do not run it in parallel and do not report a category
the verb did not name.
Deliberately untracked leftovers stay untracked. If something should be ignored rather than
committed, say so and offer the .gitignore line; do not commit it to be tidy.
4. The commits
nen commit format --repo <path> --type <feat|fix|chore|docs|refactor|test|perf|build|ci> \
--subject "<short imperative subject>" [--scope <scope>] [--breaking] \
[--body "<paragraph>"] [--trailer "Hatsu-Agent=<responsible-persona>"]Conventional Commits, one commit per coherent step where the work has steps. Validates shape
only (a declared type, a non-empty subject under 72 characters, no trailing punctuation) — what
changed and why stays this skill's to write, never nen's. Verified live: a bad type, an empty
subject, a >72-char header and a trailing-punctuation subject all refuse with a named reason at
exit 2 (docs/ab/tensho.md § 2.3); --trailer accepts comma-separated key=value pairs, and
here exactly one truthful canonical pair rides: Hatsu-Agent=<responsible-persona>.
TWO PROVENANCE TRAILERS, ONE PER PLANE (maintainer's ruling, 2026-09-10) — Hatsu-Agent: is
what this plane writes, Akatsuki-Agent: belongs to an Akatsuki roster agent on the autonomous CI
plane, and tensho never writes that one: both are admitted by policy so a commit from either
plane passes one hook, which is not licence to write it. Each names the system's own provenance
rather than a model claiming authorship, which is why no other AI attribution trailer is ever
recorded. No Co-Authored-By:, no Claude-Session:, no Signed-off-by:, no "Generated with …"
line; the allow-list and the forbidden list are data, in nen/workflow.json → commits, and a
harness that would mandate Co-Authored-By: is configured off (includeCoAuthoredBy: false).
Enforcement is three-layered and only the first ships here — the skill refusing to write it, then a
target repository's commit-msg hook and nen commit format --repo, both nen 0.4.0 and both
target-dependent at this pin (`docs/WORKFLOW.md` § commits). Git
author stays the maintainer. Never --no-verify.
Never force-push. Never push main.
5. The PR
Body follows the target repository's own PR template (e.g. schemas/templates/pr.md — a template is
not one of the four taxonomy files nen's schemas/→nen/ migration moved, so it stays wherever the
target keeps it), and two parts are checked, never eyeballed:
nen pr body-check --body-from <path to the drafted body> --requirements-from <path>--requirements-from is a JSON array of {name, pattern} — this repository's own template
convention, never a literal nen ships:
[
{"name": "What this changes for you", "pattern": "^# What this changes for you"},
{"name": "How to verify", "pattern": "^## How to verify"}
]Verified live: every requirement is checked and reported, never stopped at the first miss
(docs/ab/tensho.md § 2.4) — # What this changes for you (effect before mechanism, for a
reviewer who has not followed the work; state what it costs, not only what it gives) and## How to verify (per-scenario steps someone can actually run — with no backing issue, this is
the acceptance criteria).
The changelog.d/ fragment is a separate, diff-shaped check — nen pr body-check never looks
at changed paths:
nen changelog fragment-required --spec-paths "CONSTITUTION.md,handbooks/,nen/,schemas/,agents/,.github/workflows/" \
--fragment-dir changelog.d --files <the changed paths> --head-changelog <path to CHANGELOG.md> \
[--body-from <path to the drafted PR body>](--spec-paths is a literal prefix list, outside nen's taxonomy resolution entirely — it names
both directories so a taxonomy edit owes a fragment whether the target has migrated or not. At
the pinned build the schemas/→nen/ fallback is removed, which changes what nen reads
and changes nothing about a literal prefix: an un-migrated target still edits a real schemas/*.json
and still owes a fragment for it, so both stay listed.)
Verified live (docs/ab/tensho.md § 2.5): reports not-applicable when the diff touches none of--spec-paths; required when it does and no fragment is among the changed files (a fragment
sitting in the directory from an earlier PR does not satisfy it — it must be part of this diff);fragment-present once the fragment path is included in --files; opt-out when --body-from
carries a no CHANGELOG entry: <reason> line. A direct ### Unreleased edit still fails the guard
this verb reports against — it was never a satisfying diff shape.
The target gate is derived, never asserted:
The protocol is `docs/WORKFLOW.md` § Gate derivation, the same
one `sharingan` § 2 reads: the role first, nen gate derive with both path
sets for a canon target and no run for a consumer, --files from gh pr diff <n> --name-only
(no nen verb fetches a remote PR's changed-file set), the derived gate standing over an asserted
one, and the base read off the PR. It lived here as a copy for long enough to drift; one document
owns it now.
This skill's own decision line: decide the repository's ROLE before anything is derived. A
canon repository — one nen repo classify reports as role: canon (nen/repos.json →maintained_tools), whose product is the process — derives with its own path sets.
Everything else is G2 by role and the verb is not run: a consumer's nen/*.json, its CI
workflow and its scripts/ are its own configuration, not anybody's canon. Maintainer's ruling,
2026-09-18 (`docs/ROSTER.md` § Rulings of 2026-09-18). A consumer that
looks like a genuine exception is a G5, never an improvised path set.
Closes #N only if the PR completes an issue; Part of #N otherwise (nen ref format/nen ref parse render and read the <CODE>-<IS|PR>-#<N> notation itself — verified live, docs/ab/tensho.md
§ 2.7). Before the PR opens, prove the work through the verbs the repository declares — nen shu build, nen shu test, nen shu lint (--dry-run once on a repository you have not built; exit 4 is
a seat to quote, exit 5 sends you to nen shu tools, a repository with no declaration runs its own
documented commands, said so — claude/agents/kurapika.md § The shu verbs). Request Copilot on
open:
export GH_TOKEN=$(gh auth token)
nen pr request-reviews --target <owner/name> --pr <n> --add-bots BOT_kgDOCnlnWACopilot is a Bot, so it goes through --add-bots with its node id, never --add-reviewers copilot, which cannot resolve it (zheref/nen#160 is about resolution by login). On the maintainer's
user token it registers, where a bot token silently no-ops — nen's own --help states this and
that it cannot enforce which credential ran it, only warn. This is a
mutating GitHub call; per this port's ground rules it is A/B'd by contract inspection only
(docs/ab/tensho.md § 3), never exercised live against <reference-repo>.
6. Then drive it to its gate
Tensho's drive phase is `/sharingan`'s engine — the whole of it, not a
substitute. Once the PR is open, hand it over as /sharingan <CODE>#<N> to <G2|G4> against the
gate § 5's nen gate derive named, and let that skill do what it owns: the first-blocking-condition
diagnosis, thread stewardship, the wake channel fired alone, the adversarial confirmation pass, and
the stop at the gate. Tensho does not restate or reimplement any of it, and it does not stop at a
bare readiness reading when the PR can actually be driven.
Fallback only, when sharingan cannot run at all (an unresolvable code, no network for the checks it
needs): the readiness check by itself is `/pr-state`'s verb —
export GH_TOKEN=$(gh auth token)
# $hatsu_root is THIS plugin's checkout, ABSOLUTE: the root ten § 0 already resolved, taken
# from $HATSU_PLUGIN_ROOT where the session exports it, else from the single-quoted line § 0 printed,
# pasted verbatim in place of '<…>' (quotes included, nothing else). No manifest is parsed here: with
# neither in hand, re-resolve through ten § 0 rather than reading plugin.json inline.
hatsu_root="${HATSU_PLUGIN_ROOT:-}"; [ -n "$hatsu_root" ] || hatsu_root='<the absolute path § 0 printed>'
[ -f "$hatsu_root/.claude-plugin/plugin.json" ] && [ -d "$hatsu_root/claude/skills" ] \
|| { echo "no Hatsu root in hand: re-run ten § 0, or pass --reviewers and explicit --approvers by hand (sharingan § 4)" >&2; exit 1; }
nen pr ready <CODE>#<N> --repo <path> --gates "$hatsu_root/contracts/reference.gates.json" --explainThe --gates shown is `sharingan` § 4's identity rule, cited rather than
copied, its $hatsu_root anchor included — the Hatsu checkout as
`ten` § 0 resolves it on every surface, taken by the block above
from $HATSU_PLUGIN_ROOT or the line § 0 printed, never re-derived from a manifest here and never$CLAUDE_PLUGIN_ROOT alone, which is Claude Code's: it holds ONLY where the target is frozen<reference-repo> itself (no gates file of its own);
a target that ships its own nen/gates.json needs no identity flag at all; any OTHER target with nonen/gates.json gets --reviewers plus explicit --approvers supplied by hand — reviewer identities
from its CODEOWNERS or the PR's own requested reviewers, approval policy from an authoritative target
declaration or maintainer ruling, never this file. A repository is never judged by another repository's reviewers.
On that hand-supplied path, Nen 0.10.0 defaults omitted --approvers to the reviewer set. Pass
the target's declared approvers explicitly, or --approvers "" only for a declaredreview-round-only policy, and state the policy source on the page — whilecontracts/reference.gates.json carries its own default_approvers (sasuke, tenma), so the--gates form shown above is a real approver check even with no --approvers flag.
Quote the verdict verbatim, render the conjunct table --explain prints, and apply/pr-state's own binding rule unchanged: a readiness claim is that verdict, quoted, or it is
not made — never a paraphrase, never ready for a PR that came back unevaluated. Say plainly
that this was a check and not a drive: it reports where the PR stands and moves nothing.
Render the stop with nen stop --who kurapika --gate <G2|G4> <efforts.md> (verified live,docs/ab/tensho.md § 2.8) and say the handover out loud: "PR open at RR-PR-#N; <verdict>
against G4."
7. Authority
- Permitted: branch, commit, push a non-
mainbranch, open/update a PR, request reviewers,
and hand the PR to `/sharingan` (or, in the § 6 fallback, read its
readiness via/pr-state's own verb). - Not permitted:
bankai:agent/*,bankai:stage/*, any G1 mode label, any merge, any review
vote —request_changesabove all, since Kurapika acts on the maintainer's own credentials and
the vote would be recorded as theirs. tensho creates work and hands it off; it does not route or
release it.
8. Hard limits
- Never commits a flagged file without an explicit yes, and never a secret at all —
secret-shapeis not one of the categories that ask is for; rotate or remove it instead. - Never pushes
main, never force-pushes, never--no-verify. - Never puts two unrelated efforts in one PR — that is jujisho's job, offered not assumed.
- Never opens a PR without
# What this changes for you,## How to verify, and thechangelog.d/fragment where one is owed — all three checked by verb, none by eye. - Never claims readiness by eye — the verdict § 6 reports is
nen pr ready's (whether it
arrives throughsharinganor through the fallback check), quoted, or it is not made. - Never merges — the merge is
en§ 5's, at the PR's terminus (ruling 2026-09-29 (3)); another's PR is the maintainer's.