zheref

izanagi

Repeat a task that ACTS until a condition holds, under a mandatory iteration cap. Use when the maintainer invokes /izanagi <task> until <condition> up to <N>, or asks to keep doing something until it is done. The cap is required grammar, not a default — an invocation without "up to <N>" is asked for the cap, which is never defaulted or derived. Each iteration runs under its own authority; a human gate ends the loop rather than being retried past.

zheref 0 Updated 3w ago
GitHub

Install

npx skillscat add zheref/hatsu/surfaces-antigravity-skills-izanagi

Install via the SkillsCat registry.

SKILL.md

Izanagi — act until it is true, and never more than N times

Nature: whatever the looped task is — name Kurapika's work-mode for it (Enhancer, Conjurer,
Transmuter, Manipulator, Emitter, Specialist), and name it again if an iteration's task switches
modes.

Keep doing this until that is true — and stop after N tries no matter what.

Izanagi is the mutating half of the loop pair; `/izanami` is the
read-only half. The split is deliberate: a loop that writes needs a bound a loop that reads does
not, and making the bound part of the grammar means it can never be defaulted, inherited, or
forgotten.

The old <reference-repo> skill enforced the cap and the parse by an agent reading its own prose grammar
by eye, every invocation. This port replaces the parse and the per-iteration condition check with
nen: nen parse izanagi splits the line and exits 2 when the cap is missing or malformed —
the trigger to ask for it (§ 1), never to default it — nen watch until — the same read-only observation engine izanami's port uses —
evaluates the condition, run single-shot per iteration, and from nen 0.7 nen loop iterate
holds the count of acting iterations across the whole loop
, refusing the claim at the cap instead
of trusting a caller to stop (§ 3). What nen still does not own, verified live below, is the act
itself
— it stays exactly where the old skill's prose kept it, with the skill, under the looped
task's own authority.


1. Invocation — nen parse izanagi does the split, the cap is required

/izanagi <task description> until <condition> up to <N>

Before iteration 1, echo the parse through the verb itself:

nen parse izanagi "<the invocation line>"

nen parse izanagi (no --grammar — it is one of the three built-in grammars, alongside futon
and izanami; verified live, --grammar/--line are refused for it with "parse izanagi takes the
invocation string") matches until and up to case-insensitively, as the LAST whole-word
occurrence of each
, and echoes task: / until: / cap: back before anything runs.

Verified live, a well-formed invocation:

$ nen parse izanagi "gh pr merge 925 --repo <reference-repo> until it is merged up to 3"
task: gh pr merge 925 --repo <reference-repo>
until: it is merged
cap: 3
$ echo $?
0

Case-insensitive, last-occurrence matching of both until and up to — verified live (an
earlier stray "UNTIL"/"UP TO" inside the task text does not win):

$ nen parse izanagi "gh pr merge 925 UNTIL first mention it is merged UNTIL second mention really merged UP TO 5"
task: gh pr merge 925 UNTIL first mention it is merged
until: second mention really merged
cap: 5
$ echo $?
0

There is only ONE grammar shape — unlike izanami, which publishes a second until <condition> + following-command-lines form, nen parse izanagi accepts no such alternate; the
single-line form above is the whole grammar (verified live: feeding it izanami's two-line shape
does not parse as a task/commands split — it is read as trailing text after up to, and fails the
integer-cap check instead). Never offer a second form to the maintainer; there isn't one.

The missing-cap case — the load-bearing refusal, verified live:

$ nen parse izanagi "gh pr merge 925 --repo <reference-repo> until it is merged"
nen: no 'up to <N>'. Izanagi is the MUTATING half of the loop pair and the cap is required grammar, never defaulted or inferred -- an invocation without it is refused rather than run once 'to see'.
  try: gh pr merge 925 --repo <reference-repo> until it is merged up to <N>
$ echo $?
2

Do not infer a cap from the task, do not star or default one, and do not run "just once" to see.
Exit 2 is the trigger to ask for the cap, the rest of the line intact, and re-run nen parse izanagi on the answer; nothing iterates until it parses. This is the one case that makes the whole
grammar mean something, and it is nen's to enforce now, not the agent's to remember.

The cap is the maintainer's word, typed, never picked (missing-maintainer-choice;
`WORKFLOW.md` § 4 The maintainer's word is never derived): asked for as
free text, never derived, defaulted or offered as a picker option — not nen's placeholder, not a
value "that worked last time". Any other missing argument or configuration item is asked for and set
up inline (missing-argument, missing-configuration; § 4 Ask, set up, continue).

Missing until, and a malformed cap — both exit 2, verified live, and both are asked for the same way:

$ nen parse izanagi "gh pr merge 925 --repo <reference-repo> up to 3"
nen: no 'until <condition>'. Expected '<task> until <condition> up to <N>'.
  try: gh pr merge 925 --repo <reference-repo> until <condition> up to 3
$ echo $?
2

$ nen parse izanagi "gh pr merge 925 --repo <reference-repo> until it is merged up to zero"
nen: 'up to zero' is not a positive integer cap.
  try: gh pr merge 925 --repo <reference-repo> until it is merged up to 3
$ echo $?
2

$ nen parse izanagi "gh pr merge 925 --repo <reference-repo> until it is merged up to 0"
nen: 'up to 0' is not a positive integer cap.
  try: gh pr merge 925 --repo <reference-repo> until it is merged up to 3
$ echo $?
2

Minor observation, not a routing rule: the corrected line on an invalid (non-integer or zero)
cap always suggests up to 3 verbatim, regardless of what was typed — it is a syntactic placeholder,
not a proposed value. Never present it to the maintainer as nen's recommended cap; ask for a real
one instead.

If the task is entirely read-only, say so and point at `/izanami`, which
needs no cap and no confirmation. Running a read-only watch through izanagi is harmless but buys a
ceiling nobody needed.

nen parse izanagi runs no classifier over the task — verified live, unlike nen parse izanami
(which tags every command [read-only]/[mutating]/[unknown] and refuses on a bad tag). Feeding
izanagi a task that looks read-only still parses fine — there is nothing here to gate, because
izanagi's task is expected to mutate:

$ nen parse izanagi "gh pr checks 925 --repo <reference-repo> until all green up to 4"
task: gh pr checks 925 --repo <reference-repo>
until: all green
cap: 4
$ echo $?
0

This is why § 1's single confirmation is what stands guard here instead of a classifier — echo
the full parse (task, condition, cap) and take one confirmation before iteration 1. This is the
only confirmation the run takes on its own account, and it is what makes the rest of it autonomous,
so it must show exactly what will happen up to N times.

2. Authority — per iteration, never accumulated

Izanagi grants nothing of its own. Every iteration runs the looped task under that task's own
authority, resolved fresh
— hatsu carries no ratified run-scoped delegation grammar yet
(docs/delegation-grammar-DRAFT.md is explicitly DRAFT, NOT RATIFIED), so nothing about
izanagi's own up-front confirmation stands in for, or widens, what Kurapika's own per-action rule
already requires (claude/agents/kurapika.md: "Gate labels: per action by default… a 'go ahead' for
one issue is not authority for the next"):

  • A looped `/build` holds build's own delegation for that iteration
    only, lapsing when the iteration ends.
  • A looped `/sharingan` holds no routing or release delegation at all — that
    is sharingan's own scope.
  • A looped `/file` or
    `/backlog-synthesis` still presents its plan and takes
    its own confirmation
    — izanagi's single up-front confirmation does not stand in for it. A
    loop that pre-approves N unseen plans is not a loop with oversight.
  • A looped `/tensho` or `/jujisho` — same
    rule: their own PR-posting and split confirmations are never waived by izanagi's.

The confirmation in § 1 authorizes the repetition, not the contents. Anything the looped task
would have asked for on its own, it still asks for.

Log every mutating action — object, action, iteration number, time — and report the whole log
when the run ends. An unlogged write inside a loop is the one thing nobody can reconstruct
afterwards. No nen verb keeps this ledger for izanagi the way nen label apply's own ledger does
for a single label call; it is the skill's own bookkeeping across the whole run.

3. The loop — what nen owns, and what stays with the skill

Each iteration, in order:

  1. Re-read live state and evaluate the condition FIRST, before acting. Never act on the
    previous iteration's picture; that is how a loop repeats an action that already succeeded. If
    the condition already holds before any act has run, stop and report it as iteration 0 — a
    reporting label for "true before the loop ever acted," never one of the N acting iterations.
    Iteration 0 does not spend the cap: it costs nothing against N, so if the pre-check instead
    finds the condition not yet true, the loop still has all N acting iterations available to it.
  2. Claim the iteration, then run the task under § 2 — in that order.
    nen loop iterate --id <id> --line "<the invocation>" --repo <path> comes FIRST: exit 0 prints
    iteration <n>/<cap> and how many remain, and exit 1 is the cap, which ends the run at
    § 4's cap-out row without the act happening at all. A claim that follows the write it was meant to
    bound is a count, not a cap. Nothing is claimed for step 1's pre-check — that is iteration 0,
    and it spends none of the cap. (Retired residue; the box after this list has the transcripts.)
  3. Re-evaluate, and report one line: iteration number, what was done, what changed, and the
    condition's current value.

Step 1 and step 3's condition check reuse nen watch until, single-shot, per iteration — the
exact same read-only observation engine and truth semantics izanami's port uses, rather than
hand-rolled pattern matching:

nen watch until --command "<the read-only condition check>" [--true-pattern <regex>] \
  --max-iterations 1 [--interval-ms <ms>] [--cwd <path>]

--max-iterations 1 makes this exactly one observation: exit 0 means the condition is already
true, exit 1 means "not yet true" (never treated as an error), and an observation error is
reported and relayed as such, never silently folded into "not yet". When this call is the very
first one of the run — before any act — an exit 0 here is exactly the iteration 0 case (§ 3's
opening step): reported, but not counted against the cap. Every other call to this same command,
before or after an iteration's act, is an ordinary part of that iteration's own step 1 or step 3,
not a fresh "iteration 0". Verified live, the same condition false, then true, then reused
against a real target:

$ nen watch until --command "git diff" --true-pattern "DONE" --max-iterations 1 --cwd . --interval-ms 100
[1] condition is not yet true (exit 0)
nen: stopped at the --max-iterations bound (1) without the condition becoming true
$ echo $?
1

$ echo DONE >> watched.txt
$ nen watch until --command "git diff" --true-pattern "DONE" --max-iterations 1 --cwd . --interval-ms 100
[1] condition is true (exit 0)
condition became true after 1 observation(s)
$ echo $?
0

$ export GH_TOKEN=$(gh auth token)
$ nen watch until --command "gh pr view 925 --repo <reference-repo> --json state -q .state" --true-pattern "OPEN" --max-iterations 1 --interval-ms 1000
[1] condition is true (exit 0)
condition became true after 1 observation(s)
$ echo $?
0

The composition boundary, verified live: nen watch until REFUSES the act itself. Handing it
the task instead of the condition check — even a plain git add, which is not on the classifier's
refused list, only its unrecognized one — is refused outright, redirecting to izanagi's own parse
verb:

$ nen watch until --command "git add watched.txt" --true-pattern "x" --max-iterations 1 --cwd . --interval-ms 100
nen: 'git add watched.txt' classifies as unknown (matches neither izanami's allowlist nor a named refusal -- an unrecognized command is never assumed safe). izanami watches only; a command that writes needs 'nen parse izanagi <task> until <condition> up to <N>' instead.
$ echo $?
2

This proves the act can never be routed through nen watch until — by design, not by omission.
The verb that owns condition-polling is read-only on purpose (izanami's whole safety property); a
loop that acts needs a different mechanism for the act, and nen does not supply one, because the
act is exactly the part that only the looped task's own machinery (a skill invocation, a gh/git
mutation under its own review) can be trusted to run under its own authority (§ 2). The skill runs
the act directly, between condition checks — never through watch until.

RETIRED at nen 0.7: counting the acting iterations by hand

nen loop iterate is the backstop, and the cap is enforced by a claim being REFUSED rather than
by anyone remembering.
Through the pinned v0.6.0 the count of how many times the mutating
task had actually run lived entirely in this skill's own prose — nen parse izanagi extracts and
refuses on N exactly once, before iteration 1, and then never sees iteration 2 (at v0.6.0
nen loop iterate --id … was unknown option '--id', exit 2; the loop family carried
slots alone). Claim each iteration BEFORE performing it:

nen loop iterate --id <this loop's own label> --line "<the whole invocation line>" --repo <path>

Verified live at the pinned 0.7.0 (docs/ab/izanagi.md § Retired at nen 0.7), a cap of 3:

$ nen loop iterate --id demo --line "append a line to watched.txt until watched.txt contains DONE up to 3" --repo <fx>
iteration 1/3 -- append a line to watched.txt until watched.txt contains DONE
  2 remaining after this one; ledger <fx>/.nen/loop/demo.json                            # exit 0
…
$ # the fourth claim
nen: loop 'demo' has claimed all 3 iteration(s) its invocation allowed, so this claim is REFUSED.
nen:   append a line to watched.txt until watched.txt contains DONE up to 3
nen: This is the cap doing its job, not a failure: izanagi's cap is grammar rather than a default
precisely so that reaching it is a decision to bring back to a human, never a bound to raise and
re-run. End the loop with --release <why>, and take what it reached to the gate.                 # exit 1

Exit 1 at the cap is an ANSWER, not a failure — the verb's own words, and § 4's rule stated
by the binary rather than only here. The ledger is .nen/loop/<id>.json under --repo — the
dot-prefixed generated tree nen stop --mark already writes to, never the committed nen/.

Three properties this buys that prose could not. (a) The line is restated on every claim,
and a claim whose line differs from the running one is refused, naming both
— verified live,
re-typing the same task with up to 20 against a loop running up to 3 is exit 2,
"A cap a caller can raise by re-typing the line with a bigger N is not a cap"; the same rule
catches the honest version, a second loop reusing an id that belongs to a different task.
(b) An --id that is not one path segment is REFUSED rather than sanitised (--id a/b →
exit 2), because two ids mangled to one segment would silently share a cap between two loops.
(c) --release <why> ends the loop and is never blocked by the cap — a loop at its cap can
always still be released (exit 0), and a claim after a release is exit 2.

What is NOT delegated, and is not a gap: the act. nen loop iterate owns the COUNT and
nothing else — it runs no loop body, spawns nothing, and knows nothing about what the task does.
The act still runs directly, between condition checks, under the looped task's own authority (§ 2),
for the reason the box below this one states: the verb that polls the condition is read-only by
design, and nen supplies no verb that acts on a caller's behalf.

Where the count starts is unchanged, and it is the skill's to get right when it claims. The
claim belongs to iteration 1's act, not to iteration 0's pre-check (§ 3's opening step):
iteration 0 is a reporting label for a condition already true before anything ran, so it is not
claimed at all
and spends none of the cap. When the condition is not already true, the loop still
has the full N acting iterations ahead of it — and nen loop iterate's remaining says how many,
per claim, rather than this skill's arithmetic. nen watch until --max-iterations remains a
different, unrelated bound: its own --help still says so — "a SAFETY bound, not izanagi's
mandatory cap" — and it bounds one condition check, never the count of acting iterations.

Verified live — the full composed loop, converging before the cap (cap: 5, condition becomes
true on the act itself, at iteration 3):

$ nen parse izanagi "append a line to watched.txt until watched.txt contains DONE up to 5"
task: append a line to watched.txt
until: watched.txt contains DONE
cap: 5

--- iteration 1 ---
$ nen watch until --command "git diff" --true-pattern "DONE" --max-iterations 1 --cwd . --interval-ms 100
[1] condition is not yet true (exit 0)
$ echo "attempt 1" >> watched.txt
$ nen watch until --command "git diff" --true-pattern "DONE" --max-iterations 1 --cwd . --interval-ms 100
[1] condition is not yet true (exit 0)
report: [1] appended 'attempt 1'; condition not yet true

--- iteration 2 ---
$ echo "attempt 2" >> watched.txt
$ nen watch until --command "git diff" --true-pattern "DONE" --max-iterations 1 --cwd . --interval-ms 100
[1] condition is not yet true (exit 0)
report: [2] appended 'attempt 2'; condition not yet true

--- iteration 3 ---
$ echo DONE >> watched.txt
$ nen watch until --command "git diff" --true-pattern "DONE" --max-iterations 1 --cwd . --interval-ms 100
[1] condition is true (exit 0)
report: [3] appended 'DONE'; condition TRUE -- stop, cap (5) not reached

Verified live — the cap-out case (cap: 3, the condition never becomes true; full transcript in
docs/ab/izanagi.md § 2.4):

report: [1] appended 'attempt 1'; condition not yet true
report: [2] appended 'attempt 2'; condition not yet true
report: [3] appended 'attempt 3'; condition not yet true
cap (3) reached without the condition becoming true -- stop.

4. Stopping — five ways, and four of them are not the cap

Stop Rule What's mechanical vs. judgment
Condition true The success case. Report what became true and the evidence The evidence is nen watch until's own matching stdout / exit code (§ 3)
Cap reached Stop at N. Report what is still not true and what the next iteration would have done — a cap-out that only says "gave up" wastes everything the run learned. Then nen loop iterate --release "<why>", so the ledger records that this loop ended and why Mechanical from nen 0.7: the claim is REFUSED at the cap, exit 1, with the invocation named — quote that refusal rather than announcing a number this skill counted (§ 3)
No progress 3 consecutive claimed acting iterations that change nothing end the run, even below the cap. A loop repeating a no-op is not converging; it is burning the cap to reach the same place. Read-only observations that the composed task explicitly excludes from its acting iterations—En's quiet pending reads are the canonical case—do not enter this count Pure judgment — no nen verb detects "no-op"; the skill compares each claimed iteration's before/after state itself
A human gate G1–G5 ends the loop. It is never retried past, never worked around, and never "tried once more in case". A gate is a stop for the maintainer; looping through one would manufacture consent by repetition Pure judgment — kurapika.md's gate discipline, unchanged by looping
Impossible condition Named, not waited on — a closed PR will not go green. Stop and say why Pure judgment, same discipline izanami's port keeps (its own § 4)

The cap is a ceiling, not a target. Reaching it is a failure to converge and is reported as one.

5. Reporting

One line per iteration, no banner — a loop in progress is not a gate event. nen watch until's
own per-check line ([1] condition is/is not yet true (exit N)) is internal to the condition-check
step (§ 3); the skill's own reported line per iteration is the one in the transcripts above —
iteration number, what was done, what changed, the condition's current value.

The banner fires when the run ends and the maintainer is needed: a gate stop, a cap-out, a
no-progress stop, or an impossible condition. Render it with nen stop (--who, --gate, an
efforts table) exactly as every other Kurapika stop does. The end-of-run report carries the outcome,
the iteration count, every mutating action logged (§ 2), and — where it did not converge — what
remains and the recommended next step.

6. Hard limits

  • Never runs without an explicit up to <N> — nen parse izanagi exits 2 on it (§ 1) and the
    cap is asked for, and nen loop iterate --line refuses the same shape again on every claim; never
    route around either.
  • Never raises its own cap mid-run, and never restarts itself to get more iterations. Refused
    mechanically from nen 0.7
    : a claim whose --line differs from the running one is exit 2
    naming both lines, so re-typing the invocation with a bigger N is not a way past this (§ 3).
  • Never claims an iteration it did not then perform, and never performs one it did not claim —
    the claim is the cap, and a write outside it is a write the cap never saw.
  • Never retries past a human gate.
  • Never substitutes its confirmation for a looped skill's own plan confirmation (§ 2).
  • Never accumulates authority across iterations — each resolves fresh and lapses (§ 2).
  • Never routes the act through nen watch until — verified live (§ 3) to refuse it outright;
    the act runs directly, under the looped task's own machinery.
  • Never merges (the merge is en § 5's, never a loop's), never applies a G1 mode label, never casts a review vote — the looped
    task's limits are the loop's limits, and looping cannot widen them (kurapika.md's Manipulator
    rules, unchanged by looping).
  • Never reports convergence it did not observe — the condition's evidence from nen watch until,
    or it did not happen.