zheref

ao

Bring the branch up to date with its base — rebase when nothing has been published, merge when it has, resolve the mechanical conflicts, and stop at G5 with both sides shown for a semantic one. Use when the maintainer invokes $ao [from <base>], asks to pull main in, catch the branch up, or rebase onto main, or whenever $aka, $murasaki or $en needs the branch current. Kurapika classifies every conflicted path before touching one and never picks a side on a conflict of meaning. Never pushes, never force-pushes, never rewrites a commit that is already on the remote.

zheref 0 Updated 3w ago
GitHub

Install

npx skillscat add zheref/hatsu/surfaces-codex-ao

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.

Ao — the branch, caught up

Nature: Manipulator. Reconciling a branch against its base is git-side operation on a body of
work that is not only yours, whichever nature authored the diff.

Put the base underneath me, resolve what is only text, and hand me anything that is meaning.

Ao is `$murasaki`'s first step, `$aka`'s third,
and the step `$en` runs when a PR falls behind. It is also invocable alone.
The whole of its discipline is one distinction — text versus meaning — and one refusal: it never
pushes what it produced.


0. Standalone entry — already total

Ao already reads the checkout rather than a caller. § 2 reads the state before anything, § 3 decides
rebase-vs-merge from whether anything has been published, and from <base> carries the base with
nen/workflow.json → branch.base underneath it. `docs/STANDALONE-ENTRY.md` § 4 lists it as already total; the clauses that
apply are P1 — `$ten ao` when no composite ran it, catching up this phase's prerequisites (ten § 6) — and the
base resolving against the fetched origin/ ref, which § 3 already requires.

Nothing about a standalone call relaxes § 6: a semantic conflict is a G5 stop with both sides
shown, and it is never resolved by picking the side that makes the run continue. Ao still never pushes,
never force-pushes, and never rewrites a commit already on the remote.

Hand-back. Next in the wired run: the caller that needed the branch current —
$murasaki, $aka or $en. Nothing was pushed, and evidence taken before this
catch-up is stale if any tracked path moved.


1. Invocation

$ao [from <base>]
nen parse ao --grammar "from [<base>]" --line "<the invocation, minus the $ao prefix>"

Verified live at v0.3.0 (docs/ab/ao.md § 2.1): from release/1.4 → base: release/1.4; a bare
from parses with the clause absent, exit 0. The clause is anchored behind a literal for the
reason `$spiritual-message` § 1 records.

With no clause the base is nen/workflow.json → branch.base, default main when the key or
the file is absent. Name the base out loud before fetching it — a branch quietly caught up against
the wrong base is a diff nobody can read afterwards.

A missing argument or configuration item is asked for and set up inline (missing-argument,
missing-configuration; `WORKFLOW.md` § 4 Ask, set up, continue).

2. Read the checkout first

nen wc classify --repo <path> --base <base> --json

Verified live at v0.3.0 (docs/ab/ao.md § 2.2), --json carries exactly what this skill needs
before it touches anything: state.branch, state.isTrunk, state.dirty, state.aheadOfBase,
state.existingCommitSubjects[], state.uncommittedPaths[].

nen wc classify case What ao does
must-move — on the trunk Refuse. There is nothing to catch up: the trunk is the base. Say so and hand the caller to `$breath` or `$tensho`, whichever the situation wants
on-branch-dirty Refuse, naming the uncommitted paths the verb printed. A merge or a rebase over uncommitted work either refuses at git's own hands or buries the work in a conflict nobody can untangle. Commit it (`$kokusen`) or stash it deliberately, then re-run
on-branch-clean Proceed to § 3

nen wc classify is not a conflict detector, and a half-finished merge reads as ordinary
dirt — verified live (docs/ab/ao.md § 2.2).
Run inside an unresolved merge it reports
on-branch-dirty with the conflicted paths counted among "uncommitted path(s)", saying nothing
about the merge in progress. So check for one before classifying: a .git/MERGE_HEAD or
.git/rebase-merge present means a previous run stopped here, and this run reports that state
rather than starting a second operation on top of it.

3. Which operation — rebase or merge, decided by whether anything was published

Rebase when nothing on this branch has been published. Merge when something has. Rewriting a
commit somebody else may already have fetched is the one git operation that costs other people
work, and the local plane never does it.

nen wc catch-up --repo <path> --base <base> [--strategy rebase|merge|auto] [--dry-run] [--json]

The verb fetches first, then decides, and the decision is made against what origin says now,
never against what this checkout last heard. auto (what ao passes) rebases when nothing of the
branch is on @{upstream} and merges otherwise; --strategy rebase|merge names one only where the
caller has a reason, said out loud. A dirty tree refuses at exit 2 before any mutation. Exit 0
reports {base, strategy, before, after, behindBefore, aheadBefore, noOp}, and a noOp: true is
still reported with both SHAs
, never as "nothing to do". A conflict exits 1 with
conflicted: [{path, ours, theirs}] and the abort line printed, and § 4 takes over.

  • strategy: merge → the branch exists on the remote → the base merged in.
  • strategy: rebase → nothing published → the branch replayed onto origin/<base>.

A rebase with nothing to rebase onto still rewrites the SHA, and that is expected. Verified
live: a branch already sitting directly on origin/<base>'s tip answered "Successfully rebased
and updated"
and moved HEAD from c8826c7 to 84b0444 — same tree, same message, new commit
object, because the rebase replays the commit rather than noticing it need not. It is harmless
here and only here: § 3 rebases only an unpublished branch, so the rewritten commit is one
nobody has fetched. It is not harmless to report as "nothing to do" — § 7 asks for the base
and its resolved SHA, and a run that says no change while HEAD moved leaves every later
reference to the old SHA silently wrong.

Say which one the verb chose and why, in one line, from its report, and name the refresh:
"origin fetched; the branch is on @{upstream}, so strategy: merge, not a rebase."

RETIRED at nen 0.5, superseded at 0.13. The merge half ran through nen pr cascade-main --repo <path> --trunk <base> --no-push (verified live at v0.5.0, docs/ab/ao.md § Retired at
nen 0.5
) and the rebase half had no verb at any flag. Both halves are now nen wc catch-up's,
which never pushes. cascade-main keeps its mutating row in izanami's automation table,
--no-push included, and is no longer what ao calls.

4. Classify every conflicted path before resolving one

git -C <path> status --porcelain                    # the conflict rows
git -C <path> diff --name-only --diff-filter=U      # the conflicted paths, alone
git -C <path> ls-files -u                           # which stages each path has

Verified live against a constructed fixture carrying one of each (docs/ab/ao.md § 2.4):

Porcelain Kind Stages present
UU both-modified — both sides changed the same file 1 base, 2 ours, 3 theirs
AA add-add — both sides created the same path 2 ours, 3 theirs; no base
UD / DU delete-modify — one side deleted what the other edited the surviving side only
DD, AU, UA the rarer shapes; classify by the same table and name them as git reports

nen wc catch-up --json carries conflicted[] at exit 1, one entry per unmerged path, with
ours and theirs verbatim.
What it does not carry is the kind, so the porcelain table above
is still read for that, and § 6 shows ours/theirs exactly as the verb printed them. The verb's
own rule is the one worth quoting where it lands: it never picks a side.

Classify every path before resolving any of them, and print the table. A run that resolves the
first three and discovers the fourth is semantic has already spent the maintainer's trust on the
first three.

5. Mechanical, or semantic

Mechanical — the resolution is determined, by the repository, not chosen by anyone:

  • an append-only region both sides appended to (an import block, a .gitignore, a changelog list) —
    keep both, in the order the file's own convention gives;
  • a generated or locked file the repository regenerates (a lockfile, a snapshot, a mirror) — take
    the base's side and regenerate, then prove it with the declared build (nen shu build; the
    exit table is `$rasengan` § 6's);
  • a pure formatting or whitespace divergence with no behavioural difference;
  • an identical change made on both sides.

Semantic — anything where a person has to prefer one intent to another. UD/DU is always
semantic
: "deleted" and "still being edited" are two beliefs about whether a thing exists, and no
merge algorithm holds the belief. So is a UU where both sides changed the same behaviour, and an
AA whose two files are not the same file.

The textbook case, verified live (docs/ab/ao.md § 2.4). Base has retries = 3; the branch
raised it to 5; the trunk raised it to 9 and also moved timeout. Textually this is one small
hunk. Semantically it is two people deciding a policy number, and taking either side silently
ships somebody's decision that nobody made. git show :1: / :2: / :3: on the path prints the
three versions exactly — base, ours, theirs — and that is what the stop shows.

Under `ten` § 6's unasked catch-up, two classes are always semantic whatever
their shape (ruling 2026-09-28, ROSTER ruling 9): a conflicted path matched by nen/workflow.json →
review.scopes.security.paths, and any lockfile — the base's side is not taken and nothing is
regenerated without the maintainer seeing both sides, because a silent resolution there is a
supply-chain or credential decision nobody made.

Resolve the mechanical ones, one at a time, saying what each was and why the resolution was
determined.
Then re-run the declared build before the merge is committed — a green merge that was
never built is a claim, not a result.

Then ao commits it. This is the one commit ao makes and it has always been implied — § 5 says
"before the merge is committed" and the hard limits say "never commits a resolved merge without
re-proving the declared build"
— so it is written out here rather than left to be inferred:

nen commit format --repo <path> --type chore --scope merge \
  --subject "bring origin/<base> into <descriptor>" \
  --body "<one line per conflicted path: kind, and why the resolution was determined>" \
  --trailer "Hatsu-Agent=<responsible-persona>" > <msg-file> # exit 0 REQUIRED before the next line
nen commit write --repo <path> --message-file <msg-file> [--dry-run] [--json]   # -> {sha, subject, trailers}

Gate the second line on the first's exit code, and keep the two streams apart. The refusal goes
to stderr with nothing on stdout — verified live at the pin: a 87-character header exits 2
with 0 bytes on stdout and the sentence on stderr, while the accepted message exits 0 with 0
bytes on stderr. So 2>&1 into the message file commits the refusal as the message, and a plain
redirect commits an empty one. Both happened; the second was repaired by an amend that was only safe
because origin had not seen the commit (docs/ab/mukai.md). The reading is
`kokusen` § 6's and holds identically here:

Exit What it means What ao does
0 the message is on stdout use it — nen commit write --message-file, which validates it again and refuses an empty index
2 refused. A shape violation (undeclared type, empty subject, header over 72, trailing punctuation) or — with --repo — an attribution trailer nen/workflow.json does not admit stop. Quote the sentence from stderr, fix the input, re-run. Never commit the file
1 the trailer policy could not be read — nen/workflow.json present and malformed (with --repo) stop. The message is unshaped because the policy is unreadable; that is a repository defect to report, not to commit past

RETIRED at nen 0.5: pass --repo <path> and the binary enforces the house rule. Verified live
at the pin against this repository: --trailer "Co-Authored-By=someone" is refused at exit 2 naming
the file and the keys it admits, while a truthful --trailer "Hatsu-Agent=<responsible-persona>" renders at exit 0. The
policy is opened only when the invocation carries a --trailer, so --repo is not optional here:
without it there is no policy to open and nothing is refused. The caller's own restraint is still the
layer that survives a forgotten flag.

Why ao shapes the message at all: git's default is "Merge remote-tracking branch 'origin/main'
into "
, which is not Conventional Commits, and a repository whose own history uses
chore(merge): … for a hand-made merge (nen's does) gets a stranger's commit in the middle of it
otherwise. The trailer rule is nen/workflow.json → commits, exactly as anywhere else: one
Hatsu-Agent, no AI attribution trailer. Akatsuki-Agent is the CI plane's key and ao never
writes it
— policy admits both so one hook passes a commit from either plane, which is not
permission (`kokusen` § 5).

A rebase has no merge commit, and ao makes none: once the mechanical resolutions are staged,
nen wc catch-up is re-run on the same tree with the same --strategy to finish the operation in
progress, each replayed commit keeping its own message. The build is re-proved the same way before
the rebase is allowed to finish.

A mechanical conflict, and a base that merely moved, are resolved and caught up without a word
(nen/decisions.json row stale-base).

6. A semantic conflict is a stop, at G5

Never pick a side. Not "ours is newer", not "theirs is bigger", not -X ours, not -X theirs,
and never a blend that is neither side's intent.

The stop is `$jutaisho`'s shape, in full — the nen stop banner and
efforts table (nen stop --who Kurapika --gate G5 <efforts.md>, verified live, docs/ab/ao.md
§ 2.5), the `$spiritual-message` report's link, lettered options with a ⭐ on the
report, and the question through the surface's own option picker. What this skill adds to it:

  • Both sides shown, verbatim — conflicted[]'s ours and theirs as the verb printed them,
    with git show :1:<path> where a base stage exists — never summarised, never paraphrased into
    "they disagree about retries".
  • The options are the actual resolutions — four of them, by the maintainer's ruling of 2026-09-19
    (nen/decisions.json row semantic-conflict)
    : "A — keep retries = 5 (ours)", "B — take
    retries = 9 (theirs)", "C — 9 with the branch's timeout"
    (a genuine third the run proposes), and
    "D — resolve it and show me the diff before anything is committed" — the run types, the
    maintainer decides. This is always Crazy Slots; the run never picks a side.
  • The tree is left conflicted, and that is stated, with the one-line undo printed: the abort
    line wc catch-up printed
    , quoted as it came. Leaving it is deliberate — the conflict
    markers and the three stages are what the maintainer needs in front of them — but a checkout left
    mid-merge without anyone saying so is a trap.

The run ends there. A G5 stop is not a thing to retry past; the answer resumes it.

7. Then hand it back — never pushed

Ao finishes with a branch that is current and a working copy that is clean, and nothing on the
remote has moved.
Push is `$aka`'s, on the maintainer's call, and
murasaki's third step for a branch that is already published. Ao invoked from inside either of
them still does not push
: the caller pushes, after ao returns, having seen what ao did.

Report, in one line: the base and its resolved SHA, rebase-or-merge and why, the conflicts by kind,
which were mechanical and how each was resolved, that the build was re-proved, and — where the
operation was a merge — the merge commit ao made: its SHA and its two parents
(§ 5). "…build
re-proved bun run typecheck exit 0; merged as 2d4d5ed, parents 84b0444 cda6b03. Nothing
pushed by ao."
The commit is the whole of what ao left behind that the caller has to publish, so a
report that omits it leaves the caller guessing what its own push is about to move.

Where the operation was a rebase, report HEAD before and after — even when nothing was replayed
onto anything.
§ 3's note is why: the SHA moves whether or not the content did, so the line reads
"rebased onto origin/main at 675cbda; no commits to replay and no conflicts; HEAD c8826c7 →
84b0444"
rather than "already up to date". Anything holding the old SHA — a report already
rendered, a message the maintainer is reading, a range somebody typed — is stale from that moment,
and the pair of SHAs is the cheapest possible way to say so.

Residue

  1. RETIRED at nen 0.5: the merge half (§ 3) is `nen pr cascade-main --repo --trunk --no-push` — verified live, exit `0`, `not pushed (--no-push)` and `--json`'s `noPush: true`.
  2. RETIRED at nen 0.13: the rebase half (§ 3) is nen wc catch-up under auto or
    --strategy rebase; nothing in ao types a rebase.
  3. RETIRED at nen 0.13: the published/unpublished test (§ 3) is wc catch-up's own, fetched
    first and reported back as strategy; no tracking ref is read by hand for the decision.
  4. RETIRED at nen 0.5: the conflict enumeration (§ 4), conflicts[] on cascade-main; at
    0.13 wc catch-up's conflicted[] carries path, ours and theirs
    , the kind still read
    off the porcelain table.
  5. RETIRED at nen 0.13: showing both sides (§ 6) is conflicted[]'s ours/theirs; only a
    :1: base stage is still git show.
  6. The in-progress-merge check (§ 2) — .git/MERGE_HEAD / .git/rebase-merge on disk;
    nen wc classify folds an unresolved merge into on-branch-dirty (verified live).
  7. RETIRED at nen 0.13: the merge commit itself (§ 5) is nen commit write --message-file,
    gated on commit format's exit code with the two streams kept apart; a rebase finishes by
    re-running wc catch-up.

Every one of these is run in the open and reported as by-hand, per the Nen-first rule's second half
(claude/agents/kurapika.md): a missing verb is a finding, not a gap to route around silently.

Authority

  • Permitted: fetch; rebase an unpublished branch; merge the base into a published one;
    resolve a mechanical conflict; re-run the declared build; commit the resolved merge (§ 5,
    one commit, chore(merge), after the build is green); write the working copy and the local
    branch ref.
  • Not permitted: push of any kind, force-push above all; rewriting any commit that exists on the
    remote; resolving a semantic conflict; --no-verify; touching main.
  • Carries no delegation, and being invoked inside `$aka`,
    `$murasaki` or `$en` does not lend it one.

Hard limits

  • Never pushes. Not on a clean merge, not "to save a step", not because the caller was going to
    push anyway. This is why nen wc catch-up, which never pushes, is the verb here and
    nen pr cascade-main is not.
  • Never rewrites a published commit — the published/unpublished test (§ 3) runs before either
    operation, every time, and its answer is stated.
  • Never answers that test from a stale tracking ref. wc catch-up fetches before it decides,
    and ao reads the strategy it reported, never a ref this checkout last heard about: letting one
    select the rebase path is how a published branch gets rewritten.
  • Never picks a side on a semantic conflict, and never -X ours / -X theirs on any merge.
  • Never resolves anything before every conflicted path is classified and the table printed (§ 4).
  • Never starts a second operation on top of a merge or rebase somebody already left in progress.
  • Never commits a resolved merge without re-proving the declared build.
  • Never leaves a conflicted checkout without saying so and printing the abort line.
  • Never presents by-hand git as a verb's output — every step in § Residue is named where it runs.