Render the current backlog as one gate-oriented table — every open issue with its associated PRs, for one repo or all of them, showing the human gate each item sits at, what it needs next, and which session is driving it. Use when the maintainer asks what the state is, what needs them, what is at a gate, or invokes hatsu:backlog-state <repo|all>@<G1|G1-M|G2|G3|G4|G5|all>. Strictly read-only — it never labels, merges, pushes, comments or opens anything.
Install
npx skillscat add zheref/hatsu/backlog-state Install via the SkillsCat registry.
Backlog state — the whole board, by gate, on demand
Nature: Manipulator. GitHub-side ops/reporting over the governance plane. Kurapika says so when
he runs it.
This skill answers exactly one question, and answers it the same way every time:
What is at my gate right now, and what does each thing need from me?
It exists because nothing else answered it. GitHub's issue list reports what is open; nothing
says which gate an item sits at, which PR carries it, who is driving it, or what it needs — across
repos, on demand, in one table.
Read-only, without exception. This skill renders state. It never applies a label, merges,
pushes, opens, closes or comments. If reading the board makes the next action obvious, say what
the action is — do not take it.
The old (<reference-repo>) version of this skill computed every one of §§2–7, 11 below by improvised
prose: hand-grepping schemas/repos.json, eyeballing check pages, reconstructing a colour
precedence table from memory, hand-sorting rows by severity. This port replaces every one of those
steps with a nen verb — nen backlog fetch|order, nen gate derive, nen color status,nen board build|render, nen ref format — per zheref/hatsu#2. What remains genuinely
un-mechanizable (gate interpretation, synthesized titles, the expected-action line, session/lane
attribution, the closing shape) stays this skill's judgment, same as before. Seedocs/ab/backlog-state.md for the full mapping, the live evidence, and every finding filed against
the binary along the way.
1. Invocation
hatsu:backlog-state <repo name | all>@<G1 | G1-M | G2 | G3 | G4 | G5 | all>| Part | Accepts | Default |
|---|---|---|
| repo | a product code, a short name, owner/repo, or all |
the repo you are standing in — see below |
| gate | G1, G1-M, G2, G3, G4, G5, or all |
all when the @<gate> part is omitted entirely |
Parsing rules:
- Case-insensitive on both sides.
bc@g4isBC@G4. @is optional.hatsu:backlog-state allmeansall@all.G1-Mis accepted even though the grammar table lists onlyG1…G5— it is a real gate
(CON-25) and someone will type it. When the filter isG1, includeG1-Mrows and name
themG1-Min the state cell; they are the same human, the same moment in the lifecycle.
Mechanized by nen parse since nen v0.2.0 — this port filed a finding against v0.1.0
(docs/ab/backlog-state.md § 4: a lone leading slot swallowed the whole bracketed [@<gate>] clause,
so BC@G9 parsed as repo: BC@G9 at "ok": true), and nen v0.2.0 closed it (#67, closes
zheref/nen#30). Split the invocation through the verb:
nen parse backlog-state --grammar "<repo>[@<gate:G1|G1-M|G2|G3|G4|G5|all>]" --line "<the raw invocation>"Verified live at v0.3.0: BC@G4 → repo: BC, gate: G4; BC → repo: BC with the clause absent
(exit 0); all → repo: all, no gate; and BC@G9 is refused at exit 2 — "<gate> is one of
G1 | G1-M | G2 | G3 | G4 | G5 | all (case-insensitively), and 'G9' is none of them" — with the corrected
line on stderr. Two of § 1's rules stay prose on top of the parse, because the grammar cannot say them:
an absent @<gate> clause means all, and G1 includes G1-M rows. (Do not write the gate slot
without the brackets: the unbracketed <repo>@<gate> form refuses a bare all as "gate is required".)
With no repo token, the subject is the repo you are standing in. Resolve the working
directory's origin remote against the target registry:
nen repo resolve --repo <path> --from <cwd>A gap this port filed against
v0.1.0, closed by nenv0.2.0(#66, closes zheref/nen#27). At the
port the no-token form matched the resolved origin againstconsumers∪maintained_toolsonly —
never theproduct_codesmap — so, standing in<reference-repo>itself,nen repo resolve --repo <path> --from <same path>refused with "'<reference-repo>' … is not in this registry" while the
same refusal's code list showedBC (<reference-repo>)(docs/ab/backlog-state.md§ 4). Sincev0.2.0a token — the origin included — resolves from everything the registry records:
consumers,product_codeskeys and values,maintained_tools,pending_onboarding(nen repo --helpsays so;src/repo/resolve.tsrule 5 names this port's exact case, "refused the
registry's own origin FROM ITS OWN CHECKOUT — five independent skill ports tripped on exactly that").
The origin form is not re-run live here (it needs a checkout with anoriginremote in the registry);
the token form is:nen repo resolve BC --repo <path>→bankai-core (BC) via code. If the origin form
ever refuses a repository the code list names, that is a new finding — reuse the code its refusal
printed and file it, never hand-read the registry.An
originthat resolves to nothing in the registry at all is never a fallback toall: say which
remote failed, and ask for the repo withproduct_codesas the options (below).
An unknown repo is asked for, never guessed. Say which token failed and offer the codes nen repo resolve's own refusal names in the picker; the answer is re-parsed and the sweep continues. Resolving
a bare <prefix> to the one registry entry whose name happens to begin with it is exactly the kind of
helpfulness that reports the wrong repo's backlog. 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 repository the registry
lacks is a nen/repos.json entry set up in the registry's own checkout, on its own declaration PR
at that repository's gate — the sweep itself stays read-only; a gate token nen parse refuses at exit 2 is the same trigger, its corrected line the
starred option.
2. Resolving the repo set
nen repo resolve all --repo <path>all enumerates the registry's consumers ∪ maintained_tools entries. Whether the standing
repo also appears in that set is registry-content-dependent, not a fixed rule — verified live
against <reference-repo>'s own registry (docs/ab/backlog-state.md § 2.2): <reference-repo> (BC) does
show up in all's own output here, because <reference-repo>'s registry happens to list itself among the
enumerated entries even though it is the source, not a consumer, of that registry. A different
registry that omits the source repo from both arrays would produce the gap the old skill's prose
named and worked around by hand. Check the returned rows by code before doing anything else:
- If the standing repo's code is already among the rows
allreturned, use the set as-is —
adding it again would duplicate the row. - If it is missing, add it explicitly — resolve it via its own product code (
nen repo resolve BC --repo <path>) and fold it into the swept set alongside whateverallreturned.
Either way, state the resolved set before the table, so a reader can see what was actually
swept:
6 repos: <reference-repo>, <scaffold-repo>, <product-repo-A>, <product-repo-B>, <product-repo-C>, kro-pwa.A reader who cannot see what was swept cannot tell an empty band from an unswept one.
A second finding from the same output at the port, closed by nen
v0.2.0(#81, closes
zheref/nen#17).nen repo resolve all's row set used to include the registry's own$comment
documentation key insideproduct_codes, printed as if it were a resolvable repository
(Object-reference notation (...) ($comment) via all), and the same key appeared in the unknown-code
refusal's list (docs/ab/pr-state.md§ 4 finding 3). Sincev0.2.0a$-prefixed key inproduct_codesloads silently as metadata — verified live atv0.3.0against a registry carrying
exactly that key:nen repo resolve alllists six repositories and no$commentrow, andnen parse futon's refusal lists codes only. Still never hard-code N — count whatever the live call returned;
it varies with the registry's own content — and if a non-repository row ever appears again, name it in
the resolved-set line rather than filtering it silently.
3. Fetching
nen backlog fetch --repo-slug <owner/name> [--limit <n>]Fetches open issues and open PRs fresh over the GitHub API — never cached, and paginated:
it follows ?page=N until a short page comes back rather than stopping at GitHub's 100-row clamp.
Returns one row per issue, carrying its prNumbers[] — the assembly of "an issue plus the PRs
that reference it" that used to be hand-reasoned is now the verb's own output shape.
- Omit
--limitto fetch every open row, uncapped.truncated: truein the JSON (or the
printedTRUNCATED at --limit Nline) means exactly that — never render a capped fetch as
complete. - The row schema is thin by design:
{issueNumber, title, labels, prNumbers[], createdAt}—
no severity field, no PR-level detail (no head SHA, no check rollup, no review state, no base
ref). Severity is read offlabels(thebankai:severity/*entry) before feeding rows tonen backlog order(§ 11); PR-level detail isnen pr ready's job (§ 5), not this verb's. - A lone PR that references no open issue is not visible in this fetch's shape (it enumerates
by issue, with each issue'sprNumbers[]attached) — cross-checkgh pr list --repo <owner/name> --state open --json number,titlefor PRs whose issue link, if any, is already
closed, and give that PR its own row. This is the one shapenen backlog fetchdoes not carry;
nonenverb replaces this specific cross-check today (residue,docs/ab/backlog-state.md§ 3). - An issue and the PRs that serve it are ONE row. The unit is the effort, not the object. An
umbrella issue whose children each have PRs is one row that names them.
4. Assigning the gate
Work per row. The PR governs when one exists:
Has an open PR?
├─ YES → does the PR's base branch start "integration/"?
│ ├─ YES → NOT a maintainer gate at all — Roy's cascade/epic-merge lane (CON-5), UNLESS the
│ │ PR carries `bankai:observation-fix` (CON-34), which IS a maintainer integration
│ │ merge and must be worded as one, never as "G2"
│ └─ NO → is it CON-32-Ready? (nen pr ready — § 5)
│ ├─ YES → is the TARGET a canon repo?
│ │ (zheref/hatsu, zheref/nen, zheref/bankai-core, zheref/akatsuki-ai, zheref/bankai-scaffold — NOT zheref/Bankai,
│ │ which is a Swift product repo, and NOT any consumer)
│ │ ├─ NO → G2 (CON-5). A consumer's own nen/*.json or CI workflow is
│ │ │ configuration, not policy; nen gate derive is NOT run
│ │ └─ YES → does the diff touch CONSTITUTION.md, handbooks/, agents/,
│ │ nen/ (or the legacy schemas/), or the process surface
│ │ (.github/workflows/, claude/, scripts/, tests/, docs/)?
│ │ → nen gate derive (below)
│ │ ├─ YES → G4 (CON-7 — policy/spec, or process-as-product)
│ │ └─ NO → G2 (CON-5 — product code)
│ └─ NO → NO GATE. In progress, owned by its author. See § 6.
└─ NO → is a release proposed / is the tag precondition met? → G3 (CON-6)
is the issue at bankai:stage/researched? → G1 (CON-4)
is it routed (bankai:agent/*) without bankai:stage/building? → G1-M (CON-25)
can it move at all without a human decision or action?
├─ NO → G5
└─ YES → NO GATE. It is unstarted or in progress.The base-ref branch is new in this port and is load-bearing — carried in from a live, open
finding against the source this skill ports (RR-IS-#929, discovered while running this port's own
A/B pass): the old tree derived a gate from the diff alone, so a product-code sub-PR based onintegration/epic-* fell through to G2 and issued a MERGE ask the maintainer does not own — that
merge is Roy's (CON-5), and reporting it as the maintainer's hands them a merge that skips
de-dup/base-sync/sibling-propagation. nen gate derive does not read base ref at all — it
computes only from the changed-file set against two path sets, by design (nen gate --help: "This
derives the DIFF's half only"). Base-ref awareness stays squarely this skill's own judgment, and
there is currently no working nen path to a PR's baseRefName either — see the nen pr fetch
finding under § 5. Until one exists, resolve the base branch by reading it directly off the PR
(e.g. gh pr view <n> --json baseRefName -q .baseRefName) rather than skipping the check; a row
whose base could not be determined is reported unresolved, never defaulted to main.
Deriving the diff half, mechanically:
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 stays here, because it is what a sweep does before it derives
anything: decide the repository's ROLE first. A canon repository — zheref/hatsu,zheref/nen, zheref/bankai-core, zheref/akatsuki-ai, zheref/bankai-scaffold, whose product
is the process — derives with its own path sets. Everything else is G2 by role, andnen gate derive is not run at all, because the role already settled it: a consumer's ownnen/*.json, its CI workflow and its scripts/ are its configuration and govern nothing
elsewhere. The one question is whether merging this would change what a DIFFERENT repository
does. A consumer declaring a policy surface of its own is not ruled on — until it is, the gate
is G2 and a genuine-looking exception is a G5 (nen/decisions.json rowunruled-policy-surface). Maintainer's ruling, 2026-09-18
(`docs/ROSTER.md` § Rulings of 2026-09-18 — G4 is the repository's role,
not the file's kind).
In <reference-repo> almost every PR is G4 — it is a governance and machinery repo, so its process
surface is its product. In a consumer repo (<product-repo-A>, <product-repo-B>) it is not a ratio,
it is a rule: the gate is G2 by the repository's role, and no path set is run against it at all.
What is decided per diff is which canon repository's surface a diff hit, never whether a consumer's
configuration counts as policy; it does not.
G5 is not the default bucket — this is the rule this skill exists to enforce
"G5 is any human decision gate not covered by the other gates, yet that shouldn't mean PRs are
not meant to be driven by authors or local monitors until reaching either G2/G4 readiness."
| Situation | Gate | Why |
|---|---|---|
| Checks queued or running | none — in progress | Nobody is asking the human for anything |
| Review rounds outstanding | none — in progress | The author drives them (CON-32) |
| Author iterating, threads open | none — in progress | Owned by its author until Ready |
| Ready, mergeable, zero threads | G2 / G4 | Now it is the human's |
Needs a secret, a setting, a ruleset (CON-2) |
G5 | Only the human can |
| An option to pick, a policy shape to rule on | G5 | Only the human can |
| A conflict to escalate | G5 | Only the human can |
Sub-PR on an integration/* base |
NOT a gate row here | Roy's lane, unless bankai:observation-fix (CON-34) |
G5 means the human is the only actor who can move it. When unsure, the answer is not G5: an
item wrongly left in-progress gets picked up by its author next cycle, while one wrongly marked G5
sits in the human's queue until they read it and discover it was never theirs.
5. Readiness is decided by the verb, never by eye
A row is G2/G4-ready iff nen pr ready says the PR is CON-32-Ready — and the way to ask it,
with the discipline of quoting rather than paraphrasing, is
`hatsu:pr-state`:
export GH_TOKEN=$(gh auth token)
nen pr ready <CODE>#<N> --repo <path> --explain # a repository ships its own nen/gates.json(or <N> --gh-repo <owner/repo> for a bare number.) Which identity flag actually goes on that call
is `sharingan` § 4's identity rule, cited here rather than copied, resolved
BEFORE the call — never defaulted to --gates. A row for a repository that ships its ownnen/gates.json needs no identity flag, as shown above. <reference-repo> (frozen, no gates file of
its own) is the one repository this table can report on where --gates "$hatsu_root/contracts/reference.gates.json" applies — with $hatsu_root SET IN THAT SAME SHELL, by the
explicit-input line hatsu_root='<the absolute path § 0 printed>' (the value
`ten` § 0 prints, already single-quoted with any ' escaped, pasted
quotes included) or by pr-state § 2's resolver. It is never a variable carried from another shell — that
variable is not exported — never a raw path embedded in the command text, and never$CLAUDE_PLUGIN_ROOT alone, which is Claude Code's and is unset or wrong on the two mirrored surfaces. Since nen v0.2.0 a relative --gates resolves against --repo's root, never the cwd (verified
live at v0.3.0, `pr-state` § 2), and the file lives in this plugin's
checkout, not the target's. For any OTHER repository that this table reports on and that ships nonen/gates.json, pass --reviewers plus explicit --approvers by hand instead — reviewer identities
from its CODEOWNERS or the PR's own requested reviewers, approval policy from an authoritative target
declaration or maintainer ruling, never the reference 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 name the policy source on the rendered board — whilecontracts/reference.gates.json carries its own default_approvers (sasuke, tenma), so the--gates row above is a real approver check even with no --approvers flag.
Two things follow, unchanged from the old skill:
- Do not re-derive readiness from the check rollup yourself. The current-head rule (
CON-16) —
an approval that predates the latest push does not count — is exactly what a glance at the
checks page misses. - Honour what the gate does NOT decide (
nen pr ready --explain's own printed caveats:CON-32
(c) and (e) are approximated, not asserted — seepr-state/SKILL.md§ 3). If you believe a row
is ready and the verb disagrees, the row is not green — say what it objected to.
nen pr fetchwas broken against every real PR tested atv0.1.0, and this port never routes
around it.nen pr fetch --target <owner/name> --pr <n>is documented to return "one typed
snapshot: head SHA, mergeability, the check rollup, reviews PER COMMIT, review threads ..." —
verified live at the port, it crashed on every PR tried, in two different repositories
(<reference-repo>#925,#927,#916,#932;zheref/hatsu#5), plain and--jsonoutput printing
the identical error:nen pr: could not fetch <owner/repo>#<n> reviews: gh: Unprocessable Entity (HTTP 422). Provenance at the current pin: nenv0.2.0(#59) made everygh apiargv in the
PR fetch name its HTTP method explicitly; nov0.2.0/v0.3.0changelog entry says the 422 is fixed,
and the crash is not re-verified atv0.3.0(a live GitHub read this reconciliation did not run).
This skill still does not usenen pr fetchfor anything, on the recorded evidence — readiness
comes fromnen pr ready(a different code path, confirmed working), and nothing else in this skill
needspr fetch's remaining fields (base ref excepted — § 4's own caveat). Filed as a finding,docs/ab/backlog-state.md§ 4; not a gap this skill improvises around with a rawgh api
equivalent presented as if a verb produced it. An A/B pass against the pinned binary is what lifts
this, not a changelog inference.
6. Status colour — mechanized, never reconstructed from memory
nen color status --repo <path> --present <a,b,c>Resolves the target repository's own nen/colors.yml
precedence for whatever category values are true of one row, and prints the first match plus what it
outranked. Verified live against <reference-repo>'s own file at the port, and the exit codes re-verified
at v0.3.0 against nen's bundled fixture: a resolved row exits 0; unresolved — nothing present, or a
name that is not a value of the category — exits 1 (a finding, per below); a --category the file does
not declare is a usage error, exit 2, naming the categories it does (nen v0.2.0, #83):
| Test | Result |
|---|---|
--present ready_g2_g4 |
🟢 ready_g2_g4 — G2/G4-ready |
--present ready_g2_g4,blocked |
🔴 blocked — outranked: ready_g2_g4 |
--present blocked,on_hold |
🔵 on_hold — outranked: blocked |
--present in_progress |
🟠 in_progress |
--present "" (nothing true) |
unresolved: nothing was reported present |
Precedence printed alongside every call: on_hold > blocked > ready_g2_g4 > ready_g1 > in_progress
— byte-identical to the old skill's hand-maintained table, now read from the file rather than
carried in the skill's own memory. Never hard-code a glyph or a tie-break — call the verb per
row and relay what it resolved (or unresolved, which is itself a finding to report, never a
default).
| Colour | Means |
|---|---|
| 🔵 on_hold | Deliberately parked — name what it waits on |
| 🔴 blocked | A G5 decision or action is required |
| 🟢 ready_g2_g4 | nen pr ready says Ready and mergeable |
| 🟡 ready_g1 | Ready to be started, or to have its spec iterated — G1 or G1-M |
| 🟠 in_progress | Work is moving; nobody is waiting on the human |
The same circles are also severity colours (a separate
--category severity) — that reuse is
canon, disambiguated only by the column header. Always head the columnStatus, neverStatus / Severitycombined.
7. The table
nen board build --repo-slug <owner/name> --rows-from <path> # rows: {id,title,refs,gate,status,needs}
nen board render --board-from <path>board build assembles a Board from rows this skill has already computed (gate from § 4, colour
from § 6, refs formatted with nen ref format); board render prints it as the padded-markdown
table this repository's own ichigo_board.sh established. Verified live end to end: feeding two
rows (one G4/in-progress, one G1-M/ready-to-start) through build then render produces
| Effort | Refs | Status (gate) | Needs |with each row's status cell holding whatever string was passed — pass the resolved glyph from
§ 6 (🟠, not the bare word in_progress), verified live: the render layer does not itself look upcolors.yml, it prints the cell verbatim. Two facts about that cell since nen v0.2.0/v0.3.0: arefs value that is not an array of ref strings is refused at exit 2 naming the file, row and
field by build, render and diff alike (verified live at v0.3.0; at the port build crashed
with row.refs.join is not a function), and render — like nen stop — escapes a | in a cell as\| and flattens an embedded newline to one space (#83), so a title carrying either no longer shifts
the columns. This skill relays the rendered table and never re-splits it; a consumer that does must
split on (?<!\\)\|.
Two columns the old skill's table carried have no slot in nen's BoardRow schema —{id, title, refs, gate, status, needs} has no field for the conditional Repo column (only
present when sweeping all) or Session · Lane (§ 10). This is a genuine schema gap, not an
oversight to route around silently:
- Repo, when the argument is
all: prefix thetitlewith the resolved product code in
brackets —"[BC] Cancelled build leaves bankai:stage/building set"— rather than adding a columnnencannot render. - Session · Lane (§ 10): fold it into
needs, clearly delimited —"<action> — session: <name>, lane: <lane>"— never silently dropped, and never smuggled intorefsortitlewhere
it would be misread as part of the effort itself.
Lead with a one-line summary before the table: the repo set swept, the gate filter, the row count,
the count needing the human. Never render an empty table silently — "@G3: no rows. No
release is proposed."
8. Synthesized titles
Unchanged — this stays judgment, nen has no verb for it and none is wanted. Write 4–10 words
saying what the item is about, in plain terms:
| Raw | Synthesized |
|---|---|
[Machinery] probe_hosted_health.sh cannot detect a billing block — it checks run… |
Billing blocks go undetected |
CON-22 fan-out: five reusable workflows changed by RR-PR-#603 |
Fan-out owed for five reusables |
Synthesize; never truncate. Never editorialise a title into a claim the issue does not make.
9. Expected action or decision
One line, naming the action and its actor — judgment, unchanged:
| Bad | Good |
|---|---|
| "Needs attention" | "Merge — RR-PR-#588, ready since 14:02" |
| "Blocked" | "Create the integration/* ruleset — only you can (CON-2)" |
| "In review" | "Kisuke addresses 4 Copilot threads" |
If the actor is the maintainer, say what the decision is, not that a decision exists.
10. Session · Lane — derived from evidence, never assumed
Unchanged from the old skill; folded into the needs cell per § 7's schema-gap note.
Lane — the authority driving the work:
| Evidence | Lane |
|---|---|
A machine stamp: <!-- bankai agent={name} run={run_id} ... --> |
CI agent — name it |
| Local commit attribution / a Kurapika mode header | Kurapika authority — name the mode (Enhancer, Conjurer, Transmuter, Manipulator, Emitter, Specialist) |
A bankai:agent/<name> label but no stamp and no PR |
Routed, not started |
| None of the above | No set profile |
Session — where it runs: the workflow run for CI (link the run), the named local session for
local work, or unresolved — never a guessed session name.
11. Ordering
Rows the human must act on first, then the rest:
🟢 G2/G4-ready → 🔴 blocked (G5) → 🟡 G1-ready → 🔵 on hold → 🟠 in progressnen backlog order does not know about status bands — it implements backlog-loop's own §2
priority order (severity → blocks-another → affects-consumers → age → issue number) over a
pre-fetched row set, nothing about gate colour. So the composition is: bucket rows into the five
bands above first (§ 6's own output), then run nen backlog order inside each band:
nen backlog order --rows-from <path> --severity-order critical,high,medium,low \
[--blocks <n,n>] [--affects-consumers <n,n>]Verified live: severity is the primary key (high rows sort before medium, both before an
unrecognised severity string, which ranks last rather than erroring); --blocks/--affects- consumers only break ties within one severity, exactly as documented. Feed nen backlog fetch --json's rows reshaped to {id, severity, createdAt, number} (severity read off thebankai:severity/* label) — the reshape itself is expected caller work, per the verb's own--help example, and since nen v0.3.0 (#107) it is enforced at the read seam: backlog fetch --json's document handed straight through is refused at exit 2 ("must be a JSON ARRAY of rows {
id, severity, createdAt, number }, got an object"), and so is a row missing id ("row at index 0
needs a string 'id'") — both verified live — instead of the {} is not iterable crash of before.id/number come from issueNumber (or the lone prNumbers entry for a PR-only effort),createdAt must be an ISO-8601 instant (ordering compares it as text). --blocks/--affects- consumers take a row's id or its bare issue number, and a token naming no row is refused at2 (nen v0.2.0, #64) — never silently ignored.
12. Close with the shape, not just the rows
Unchanged — judgment. End with two or three lines of shape: what collapses, what shares one root
cause, what is one merge away. Derive it from the rows just rendered; never assert a collapse not
computed from them.
13. What this skill must never do
- Apply, remove or suggest applying any label as a side effect.
- Merge, push, open, close, comment, or re-request review.
- Report a gate it inferred from a title rather than from labels, diff (
nen gate derive) and base
ref (§ 4). - Report readiness it did not get from
nen pr ready. - Fabricate a session name, a run link, or a colour whose precedence it did not get from
nen color status. - Render a truncated fetch (
nen backlog fetch's owntruncatedfield) as if it were complete. - Silently add the source repo to a sweep without saying so, or silently filter a non-repository
row out ofnen repo resolve all's output should one ever reappear (§ 2). - Route around
nen pr fetch's recorded crash with a hand-rolled equivalent presented as if a verb
produced it — a filed finding (docs/ab/backlog-state.md§ 4), not re-verified at the pinned ref,
not a gap this skill papers over. (nen parse's bracket gap andrepo resolve's origin gap are
closed — § 1 — and the verbs are used.)