zheref

senkei

Inventory a consuming product repo's own backlog — open epics, live integration branches, and open PRs — classify each effort, and drive every open PR to a CON-32/G2-readiness determination. Use when the maintainer asks for the status of a product repo (`<product-repo-A>`, `<product-repo-B>`, or any other Bankai consumer), wants its existing efforts driven forward, or invokes `hatsu:senkei`. Kurapika (Manipulator) enumerates the target repo's backlog, classifies each effort (delivering/building/stalled/queued/idle/undecidable), states a Ready/not-Ready call with the reason for every open PR, and reports one status table per pass. Carries no CON-25 delegation and never merges — G2 stays the maintainer's.

zheref 0 Updated 3w ago
GitHub

Install

npx skillscat add zheref/hatsu/senkei

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.

Senkei — inventory and drive a product repo's own backlog to G2-readiness

Nature: Manipulator. GitHub-side ops/reporting over a product repo's own backlog. Kurapika
says so when he runs it.

This is the product-repo counterpart of
`backlog-loop`. backlog-loop drives <reference-repo>'s own backlog to
zero as G4-ready PRs; senkei
inventories one consuming product repo's backlog — its open epics, live integration/*
branches, and open PRs — and drives those PRs to CON-32/G2-readiness. It never touches
<reference-repo>'s own backlog, and it never mutates the target repo's product code.

The old (<reference-repo>) version of this skill computed every one of §§2–4 below by improvised prose
and raw gh: hand-listing epics and their children over gh issue list, hand-computing
ahead/behind for each integration/* branch, hand-reasoning the five-class taxonomy from labels,
and re-running a dead reviewer job with a bare gh run rerun <id> --failed. This port replaces
those steps with nen repo inventory, nen effort classify and nen run rerun-failed — the three
verbs zheref/hatsu#2 names for this port — plus nen pr ready for readiness (unchanged pointer,
shared with every other ported skill via `pr-state`) and
nen parse for the invocation grammar. What remains genuinely un-mechanizable (the five-class
label→input reshape, the Ready/not-Ready narrative, the escalation channel, the closing table)
stays this skill's judgment. See docs/ab/senkei.md for the full mapping, the live evidence
against two real consumer repos, and every finding filed against the binary along the way.

Read the target repo's own backlog only. Never <reference-repo>'s — that is
`backlog-loop`'s job, and this skill carries no delegation to touch
it, read or write.

In phase-lattice terms, and changing no mechanics below: senkei is an inventory phase, not a
delivery one.
It enumerates (§ 2), classifies (§ 3), and drives each open PR to a CON-32
determination through `sharingan`'s own engine — invoked once per open
PR
, its first-blocking-condition ordering, its unblock channel and its escalation ladder, exactly
as § 4 already describes them rather than a second implementation of them. It calls none of the
five human-called phases
— aka, mukai, the merge, kagutsuchi and mugetsu are the
maintainer's (`docs/WORKFLOW.md` § 4) — and it never prompts for one:
it opens no PR, pushes nothing, merges nothing, uploads nothing and publishes nothing. What it
produces is a table and a Ready/not-Ready call per PR; every gate in that table stays exactly where
it was.


1. Invocation

hatsu:senkei <repo>

<repo> is a product code, a short name, or a full owner/repo — take it from the invocation
(hatsu:senkei <product-repo-A>, or hatsu:senkei <product-repo-A>); never guess it. Mechanized:

nen parse senkei --grammar "<repo>" --line "<what the maintainer typed>"

Verified live (docs/ab/senkei.md § 2.1): a single mandatory slot with no bracketed optional
clause parses correctly both ways (<product-repo-A>, <product-repo-A>) and refuses cleanly with a
corrected line when the slot is empty. This is the same generic --grammar path
`backlog-state` found broken for a bracketed clause — the defect is
specific to [ ... ], not to nen parse as a whole, and senkei's own grammar has no bracket to
trip it.

When no repo is given, resolve the registry and ask:

nen repo resolve all --repo <path to a checkout carrying nen/repos.json — or schemas/repos.json under the fallback nen keeps until v0.4.0>

An empty slot (nen parse exit 2) or an unknown repo is the trigger to ask, not the end: the
picker names the token and offers the codes nen repo resolve's own refusal prints — never a guess,
never a prefix match. A missing argument or configuration item is asked for and set up inline
(missing-argument, missing-configuration; `WORKFLOW.md` § 4 Ask, set
up, continue
). An unregistered product repo is reported, never written: the registry that would record it is a
foreign one — zheref/bankai-handbooks' (a canon repository at G4, whose public registry names no
private repository) or, at the port, the frozen <reference-repo>'s — so senkei names the missing
consumers entry and the repository that owns the registry, and stops that repo's run (WORKFLOW § 4
What is not a gap).

Product repos do not reliably ship their own registry. Verified live against the real
<product-repo-A> at the port: it carried no schemas/ directory at all — no repos.json, no
gates.json, no colors.yml, no labels.json (docs/ab/senkei.md § 4.1, three 404s) — and the
check at the current pin is "no nen/ directory" — nothing else. RETIRED at nen 0.5: there is
no schemas/ fallback.
It was removed in the release this repository pins, so a target carrying the
four files only under schemas/ is refused exactly like one carrying them nowhere, with the refusal
naming the migration; nen schema check --repo <checkout> answers it in one call, per file, and its
--json row now carries legacy — a boolean saying a schemas/<file> copy is on disk, detected,
never read. At the port, the registry that named its product code (RA) and its Nen scenario
(swiftui-tca-uzf-v2) was the frozen <reference-repo>'s own, under consumers, alongside
<product-repo-B> (RB). Since 2026-09-28 the canon home is the public zheref/bankai-handbooks, whose
registry records no consumers (private products are not named there), so --repo <path> for
resolution and object notation names whichever registry records the target —
`bankai-handbooks` § 1's order: the canon registry first, then the
product repo's own nen/repos.json when it records itself with a scenario — never a guess, and never
the product repo's checkout as a stand-in for a registry it does not carry. Resolve once, up front, and
carry the resolved owner/repo slug forward:

nen repo resolve RA --repo <registry checkout>          # <product-repo-A>  (RA)  via code
nen repo scenario --repo <registry checkout> --target <product-repo-A>   # swiftui-tca-uzf-v2

A product repo's own build declaration is a different file, and senkei reads it rather than writes
it.
A consumer product repo is, unlike <reference-repo>, one of nen's seven stacks (a
swiftui-tca-uzf-v2 scenario is an xcode-ios tree; compose-uzf-v2 a gradle-android one), so with
a local checkout of it in hand:

nen shu detect --repo <product repo checkout>        # exit 0: a project block proposed, withheld rows and their reasons; exit 1: no lane detected
nen schema check --repo <product repo checkout>      # which taxonomy files it carries, and where nen read each from

Report what detect proposes and withholds (an xcode-ios lane proposes no command row on any
tree
— every row names a simulator only the machine knows — and says what it did answer) as part of
the status pass; a product repo with no nen/contract.json is a finding for that repo's maintainer,
and writing the declaration is a PR into that repo at its own gate, never a --write this skill runs.
Driving that repo's PRs to readiness (§ 4) does not depend on it.

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. Enumerate the target repo's backlog — nen repo inventory

export GH_TOKEN=$(gh auth token)
nen repo inventory --target <owner/name> --epic-label <label> --integration-prefix <prefix> [--trunk main]

This is senkei's own live enumeration, named as such in the verb's own --help: every open issue
carrying --epic-label with its children, every branch under --integration-prefix with its
ahead/behind vs --trunk, and every open PR. Always fetched live — never work from a cached
list; labels, branches and PRs change under you.

  • --epic-label is the target repo's own epic marker (bankai:epic on <product-repo-A>/<product-repo-B> —
    confirm against the repo's label set rather than assuming the <reference-repo> name matches).
  • --integration-prefix has no default. The naming convention for a live integration branch
    (integration/ on <product-repo-A>) is the target repository's own; never hard-code a literal here.
  • --trunk defaults to main.

Verified live against the real <product-repo-A> (docs/ab/senkei.md § 3.1):

nen repo inventory --target <product-repo-A> --epic-label "bankai:epic" \
                   --integration-prefix "integration/" --trunk main

returned 7 open epics (42 children total — 6+6+6+7+6+5+6 across the seven epics, a mix of
open/closed — verified live, re-run 2026-09-01, docs/ab/senkei.md § 3.1), 4 live integration
branches
, 1 open PR. Two of the four integration branches — integration/epic-193 and
integration/epic-213 — belong to epics already closed (confirmed independently against the
live issues, docs/ab/senkei.md § 3.2): exactly the idle class in § 3 below, found on real
data, not constructed. This is the same defect shape <reference-repo>#929
(docs/ab/backlog-state.md § 5) names for a different skill: state that outlives the object it was
opened for, left alive because nothing swept it.

3. Classify each effort — nen effort classify

Class Meaning
delivering A delivery PR is open at G2 — the final integration/<epic> → trunk PR, or a shikai-mode child PR.
building A child issue is released (bankai:stage/building) to a builder; no PR yet, or a PR is in review.
stalled Released with no branch/PR ever opened, or a reviewer job that never posted a Verdict: line.
queued G1-approved (a mode label picked) but not yet released with bankai:stage/building.
idle An integration/* branch is still alive but its epic is closed — flag for cleanup.

The taxonomy is those five. The verb returns SEVEN values, and the other two are answers about
the taxonomy rather than members of it
— nen 0.7 settles this in --help itself, where through
v0.6.0 the text called itself "senkei §3's five-class taxonomy", listed six names and returned
seven, so undecidable appeared in neither the count nor the list (zheref/nen#53; verified live,
docs/ab/senkei.md § Retired at nen 0.7):

Not a class Meaning
state-machine-violation Two stage labels on one object contradict each other. Flagged, never resolved by guessing which is authoritative.
undecidable No stage label, no mode label, no PR and no live branch: nothing here places the object anywhere in the taxonomy. A genuinely untriaged issue, reported rather than silently folded into queued.

A caller switching on the class must handle both, which is --help's own instruction now — and
this skill's report says undecidable in those words rather than translating it into a class the
issue does not have.

nen effort classify --input <path.json>

The input is a JSON array of {kind:"epic"|"child", issueState, stageLabels[], modeLabelPresent, hasPr, prOpen, prIsDelivery, integrationBranchAlive, reviewerVerdictMissing} per effort — reshape
nen repo inventory's rows into this shape before calling it (the verb classifies; it does not
fetch). state-machine-violation (two stage labels on one object) is flagged, never resolved by
guessing which is authoritative.

RETIRED at nen 0.7: --input's second base

A relative --input resolves against --repo's root, never the process's own directory
(zheref/nen#100). Nothing was inconsistent within a single invocation, which is exactly why it
survived — run from the repository root the two bases are the same path — and it appeared the
moment a caller ran from somewhere else: a worktree, a wrapper script, a step with its own working
directory. Verified live with a decoy of the same relative name in the calling directory
(docs/ab/senkei.md § Retired at nen 0.7): v0.6.0 read the decoy and classified it, 0.7.0
reads --repo's copy. An absolute path is still used as-is, and --repo still defaults to the
process's own directory, so a caller standing in the repository sees no change at all.

Pass --repo on this call, therefore, and pass it deliberately: it is what decides which
efforts file the verdict is about.

The stageLabels / modeLabelPresent split is load-bearing, and getting it wrong
misclassifies real data.
modeLabelPresent is whether a G1 mode label
(bankai:stage/ready-for-bankai / bankai:stage/ready-for-shikai) was picked; stageLabels is
every other bankai:stage/* label the object carries. Verified live, three ways
(docs/ab/senkei.md § 3.3–3.5):

  • Feeding the mode label itself into stageLabels (as it literally appears in GitHub's own
    label list — both live under the same bankai:stage/* prefix) makes a G1-approved-but-not-yet-
    released epic misclassify as building, because the verb reads any non-empty stageLabels
    as "released" — regardless of which label it is. Reproduced against the real
    RA-IS-#17 / RA-IS-#178 epics: feeding their real bankai:stage/ready-for-bankai label into
    stageLabels reports building; excluding it (mode label → modeLabelPresent: true,
    stageLabels: []) reports the correct queued — "G1-approved (a mode label was picked) but
    not yet released with a stage label"
    — matching the old skill's own taxonomy exactly.
  • A pre-G1 label (bankai:stage/researched, bankai:stage/idea) fed into stageLabels
    reports stalled — "released, but no branch or PR was ever opened" — even though the old
    skill's own taxonomy defines queued/stalled as states an effort reaches only after G1
    approval. An issue still at researched/idea/triage has not yet entered senkei's
    classification universe at all: do not feed it to nen effort classify — report it
    separately as "not yet G1-approved," never as stalled.
  • queued is reachable ONLY when stageLabels is empty and modeLabelPresent is true —
    confirmed true even with an alive integration branch (RA-IS-#17/#178 again): an alive
    branch does not upgrade a queued row to building on its own; only a genuine (non-mode)
    stage label does.

Never pass the raw GitHub label list straight through as stageLabels. Strip the G1 mode
labels into modeLabelPresent first, and exclude pre-G1 labels from the classification pass
entirely — feeding either wrong is a silent misclassification, not a refusal, so there is no
error to catch it for you.

stalled's live-signal half is caller-supplied, not fetched here. A reviewer job that died
mid-run or a builder that burned its turn cap is read from reviewerVerdictMissing — the verb's
own --help says so plainly. The mechanical rule (released, no branch, no PR) still reaches
stalled without it; set reviewerVerdictMissing: true only when you have independently confirmed
the dead-reviewer signal (§ 4).

A child carrying two stage labels at once is state-machine-violation — flagged, never
resolved by guessing which one is authoritative (agents/_conventions.md § State machine).

4. Drive every open PR to CON-32, not to "checks green"

For each open PR, state a Ready / not-Ready determination with the reason — never
"required checks are green" alone.

The determination comes from nen pr ready — the same pointer every other ported skill uses
(hatsu:pr-state, docs/ab/pr-state.md):

export GH_TOKEN=$(gh auth token)
nen pr ready <ref> --gh-repo <owner/name> [--gates <path> | --reviewers a,b,c --approvers a,b] --explain

Most product repos ship no gates file of their own — verified against <product-repo-A>, not
assumed.
Without --gates or --reviewers, nen pr ready refuses outright: "no reviewer
identities. This gate never falls back to a built-in reviewer set..."
(exit 2; at v0.3.0 the
refusal names nen/gates.json, the one file it looks for) — reproduced
live against the real <product-repo-A>#509 (docs/ab/senkei.md § 4.1). This is the same refusal
`pr-state` documents for <reference-repo>, and the fix is the same shape but a
different file: hatsu's contracts/reference.gates.json is <reference-repo>'s own reviewer
identities and must never be reused for a different repo's PR — that would judge one repo
against another's vocabulary. For a product repo with no --gates file of its own:

  • If the repo ships nen/gates.json, no flag is needed at
    all.
  • Otherwise, pass --reviewers naming that repo's own configured reviewer bots and explicit
    --approvers from its authoritative approval policy (--approvers "" only for a declared
    review-round-only policy) — derived from its own configuration, read directly, not assumed and not
    copied from <reference-repo>'s registry. Verified live, read-only, against <product-repo-A>'s own
    workflow files
    : bankai-review-gates.yml calls sasuke-review.yml, tenma-review.yml and
    bisky-review.yml as reusable workflows — a genuine review trio, not a pair, and <reference-repo>'s
    own registry's consumes list for this repo corroborates the same three (plus copilot
    nowhere in it as a review identity — copilot-sweeper.yml is a different, non-review workflow).
    Never guess a reviewer set and never substitute <reference-repo>'s own contracts/reference.gates.json.
  • A disclosed limitation, not routed around: the reduced --reviewers a,b,c --approvers a,b
    CLI path is a flat, static list — it cannot model bisky's conditional approver behaviour,
    which only joins the approval set for a PR it has actually posted a review on at the current
    head
    (approves_when_posted_at_head). This is exactly the dynamic-enrolment shape
    `pr-state`'s own gates-contract precedent names for <reference-repo> itself
    (docs/ab/pr-state.md § 1 row 8: "+bisky/+bugbot only when their check is present at head",
    driven by enrolment_check_pattern in a real gates.json, never by a flat CLI list) — the same
    structural gap, reproduced against a different repo. Verified live (docs/ab/senkei.md
    § 4.2): bisky-bankai had already posted an APPROVED review on <product-repo-A>#509 at the
    head evaluated, yet the --reviewers sasuke,tenma,copilot --approvers sasuke,tenma call this
    port actually runs neither names bisky nor sees that approval — it is invisible to the verdict
    computed this way, not merely omitted from the writeup.
  • Why this set, stated plainly: sasuke,tenma,copilot (reviewers) / sasuke,tenma (approvers)
    is <reference-repo>'s own convention (contracts/reference.gates.json's base_reviewers/
    default_approvers), reused here as the pragmatic fallback — not because <product-repo-A>'s own
    workflows call for copilot as a review-pair identity (they don't; copilot-pull-request-reviewer
    shows up only as a PR commenter, per gh pr view --json reviews, never as a reusable-workflow
    review-pair job) but because the CLI's flat --reviewers/--approvers shape cannot express
    bisky's conditional round anyway, so naming it as a plain reviewer would misrepresent it as
    unconditional. The gap this leaves: a full nen/gates.json authored for
    <product-repo-A> itself, modeling bisky the way <reference-repo>'s own contract does, is what
    would close this fully — until one exists, say so every time this fallback is used, rather than
    presenting sasuke,tenma,copilot as <product-repo-A>'s own considered choice.
  • If neither is available, ask for the identities (the maintainer's word: never derived) and offer
    a nen/gates.json as that repo's own declaration PR at its gate; with no answer the refusal is
    the report — surface it verbatim (nen pr never falls back to a guess), never route around it with
    an assumed identity set.

Real verdict, verified live against <product-repo-A>#509 with its actual reviewer identities
supplied (docs/ab/senkei.md § 4.2): not-ready: required checks reported but are not all green (CON-32a) — the conjunct table (--explain) shows exactly which check rollup entries are not yet
green, at the current head, short-circuited before any reviewer-round conjunct is evaluated.

Two rules unchanged from `pr-state` and
`backlog-state`: never re-derive readiness from the check rollup by
eye
(the current-head rule catches what a glance at the checks page misses), and the
adversarial confirmation pass may only veto a ready verdict, never promote a not-ready one
.

Product repos hit failure modes <reference-repo>'s own backlog does not surface as often — check for
these explicitly, by name, in the determination:

  • A reviewer job that died mid-run, leaving a stale CHANGES_REQUESTED on a required check.
    Re-run the failed job — mechanized:

    nen run rerun-failed --target <owner/name> --run-id <n>

    This is 'gh run rerun <n> --failed', exactly — the verb's own --help names it "senkei's
    dead-reviewer recovery."
    Never re-label to force a re-vote. Verified live and safely: run
    against a real zheref/hatsu workflow run whose jobs had all already succeeded — nothing to
    rerun, so the call refuses cleanly with no mutation (docs/ab/senkei.md § 5.1): "run … cannot
    be rerun; This workflow run cannot be retried"
    (exit 1). This is the refusal shape a caller
    sees when there is nothing dead to recover — the affirmative "actually reran a failed job" path
    was not exercised (this verb mutates GitHub with no dry-run mode at all, unlike nen label apply/nen wake fire/nen labels sync), and this port never exercises it against a real
    consumer repo's PR — only the refusal shape is confirmed, per the shared brief's mutation
    boundary.

  • A builder that burned its turn cap and pushed nothing — the PR looks "in progress" but is
    actually stalled; call it stalled in the classify pass (§ 3), not building.

  • A child carrying two bankai:stage/* labels at once — state-machine-violation (§ 3);
    flag it, don't guess which one is authoritative.

Where a PR is stuck for a reason nen run rerun-failed doesn't cover (a swallowed wake, a
request_changes vote substituted for one), use the same unblock channel as
`backlog-loop`/`sharingan`: bankai:wake/iterate,
fired ALONE, never in the same breath as a comment. That mechanization (nen wake fire) is outside this port's own
Scope (zheref/hatsu#2 names only nen repo inventory, nen effort classify, nen run rerun-failed for senkei) and is left as prose here, exactly as the old skill carried it — see
docs/ab/senkei.md § 6 residue.

Verify the wake reached the builder — <agent> / build having run. A probe that is
cancelled with no build job is a failed wake, not an attempt, and must be re-fired rather
than counted.

5. Report

One status table per pass, <CODE>-<IS|PR>-#<N> notation (nen ref format), gate named per row:

nen ref format --repo <reference-repo checkout> --code RA --kind IS --number 178 --state open \
              --url https://github.com/<product-repo-A>/issues/178
# 📄 RA-IS-#178
Issue Class PR Checks Reviews Ready? Awaiting
RA-IS-#178 queued — — — — Release to a builder — G1-M
RA-PR-#509 building RA-PR-#509 not all green — ❌ not-Ready CON-32(a) — checks

Draw the gate-stop banner (nen stop) only when the maintainer must actually act on something in
the table — a status-only pass reports plainly, no banner.

Explicitly out of scope

  • No new authority. CON-25's third carve-out (run-scoped label delegation) belongs to
    `backlog-loop` by name; senkei applies no
    bankai:agent/* or bankai:stage/* label without the maintainer's explicit per-action
    confirmation, same as Kurapika outside a named run.
  • No merging, anywhere. G2 is the target repo's maintainer's, same as it is on <reference-repo>.
  • No writes to product code by this skill. It inventories and drives — it never edits the
    target repo's product source itself.

Hard limits

  • Never merge, on any repo, for any reason.
  • Never apply a label without the maintainer's per-action confirmation — this skill carries no
    run-scoped delegation.
  • Never cast a request_changes review to move a stuck PR — use bankai:wake/iterate (prose
    channel, § 4).
  • Never guess the target repo when none is given (§ 1).
  • Never feed a pre-G1 or mode-carrying label straight into nen effort classify's stageLabels
    without the reshape § 3 requires — verified live to misclassify.
  • Never reuse contracts/reference.gates.json (or any other repo's reviewer identities) for a
    different repo's nen pr ready call (§ 4).
  • Never exercise nen run rerun-failed's affirmative rerun path against a repo whose PRs you do
    not control the consequences of — this port only confirms the refusal shape (§ 4).
  • The reviewer-round policy is `hatsu:sharingan` § 6's, stated there once and
    not restated here
    : one round after hatsu:hanten settles; a request only through nen pr request-reviews, only while requested < round_policy.maxRounds and resolved < round_policy.minRounds,
    never once minRounds resolved rounds exist, never after a push that changed nothing reviewable;
    either bound reached ends the pass not-ready with the board (zheref/hatsu#102). nen/workflow.json →
    monitor.maxCycles is en's acting-cycle cap, a different number.