This is the disaster-recovery runbook for ystack. If the live setup is lost — the
/yshifu command gone, the Codex CLI disconnected, labels or CI missing — follow this
top to bottom to turn this repo back into a running coding team.
This repo is the source of truth for how the team works. Everything below is reconstructed from files already in this repo — this runbook only points at them and gives the order. It does not duplicate their contents; open each referenced file and use it as written.
Parameterize, don't hardcode. Wherever you see
<owner>/<repo>(or<owner>), substitute your own target repo(s). Per the reusability rule inAGENTS.md, keep personal usernames and repo names out of the shipped files — supply them here at restore time, not in the templates.
Accounts and access you need before starting:
- A Claude plan that runs Claude Code — the whole team runs in-session: yshifu is an ordinary Claude Code chat, and it spawns the coder (and fix-mode coder) as subagents in that same session (metered ordinary use, no API key).
- Codex (OpenAI) CLI — this is the cross-vendor reviewer, driven by
scripts/codex-review.sh. A ChatGPT plan that includes Codex review is enough for personal repos; the Codex CLI must be installed and signed in. - GitHub access to each target repo, plus the
ghCLI authenticated locally (gh auth statusshould show you logged in) for labels and the loop'sghcalls. jqonPATH— the review/debate gates validate Codex's--jsonevent stream with it, and the merge helper parses GitHub check JSON with it.- The personal config you must supply (keep it parameterized — see the note above):
- the target repo name(s), e.g.
<owner>/<repo>— the repo(s) the team works in. (ystack is its own target repo; add others as you adopt the team elsewhere.)
- the target repo name(s), e.g.
Read README.md once for the mental model (the team, the loop, the
design "why") and AGENTS.md for the conventions and safety rails before
you rebuild.
The resolver is source-only and remains inactive after restore. Confirm the eight
resolver files listed under “Inactive portable profile resolver v1” in
ci/required-files.txt, then run:
bash scripts/test/portable-profile-resolution.test.shThat test obtains the pinned jq 1.6 release with its recorded digest, compiles the
single private no-follow helper and the test-owned direct-execve launcher with the
host compiler, creates hermetic SHA-1/SHA-256 repositories, and validates the output
with the restored scripts/core-contract.sh. No compiled helper is installed or
restored. A future activation must separately qualify and bind a production trusted
parent; restoring these files does not select a live profile.
yshifu is the only human-facing surface — you talk only to yshifu; the workers have no human channel.
- Create a Claude Code project (a chat you keep).
- Paste the full contents of
manager/CLAUDE.mdin as the project's persistent instructions / persona. - Recreate the
/yshifuslash command by runningscripts/install.sh(no arguments). It generates~/.claude/commands/yshifu.mdfromtemplates/yshifu-command.md, substituting this clone's own path for the placeholder — so the command never hardcodes a repo location. Idempotent: re-running is safe, and an existing differingyshifu.mdis backed up toyshifu.md.bakbefore overwriting. A retired legacy command file is not recreated or deleted by the installer. If legacy~/.claude/commands/faber.mdremains, the installer warns and leaves it byte-for-byte untouched. Retire it in this order before running doctor/full smoke:-
Verify the new command exists and names this clone:
test -f ~/.claude/commands/yshifu.md && grep -qF "$(pwd -P)/" ~/.claude/commands/yshifu.md. -
Inspect whether the legacy file is the generated bridge or contains custom work. Never discard custom content.
-
Move it outside the active command-discovery tree into a unique timestamped directory; never overwrite the installer's fixed
.bakor an earlier retirement:set -eu legacy_cmd="$HOME/.claude/commands/faber.md" # legacy operator cleanup if [ ! -e "$legacy_cmd" ] && [ ! -L "$legacy_cmd" ]; then echo "retired command is already absent: $legacy_cmd" >&2 exit 1 fi claude_root="$(cd "$HOME/.claude" && pwd -P)" retired_root="$HOME/.claude/retired-commands" if [ -L "$retired_root" ] || { [ -e "$retired_root" ] && [ ! -d "$retired_root" ]; }; then echo "refusing unsafe retired-command root: $retired_root" >&2 exit 1 fi if [ ! -e "$retired_root" ]; then mkdir -m 700 "$retired_root" fi if [ ! -O "$retired_root" ] || [ -n "$(find "$retired_root" -prune \( -perm -020 -o -perm -002 \) -print -quit)" ]; then echo "retired-command root must be owned by this user and not group/other writable" >&2 exit 1 fi retired_real="$(cd "$retired_root" && pwd -P)" case "$retired_real" in "$claude_root"/*) ;; *) echo "retired-command root escaped $claude_root" >&2; exit 1 ;; esac stamp="$(date -u +%Y%m%dT%H%M%SZ)" retired_dir="$(mktemp -d "$retired_real/legacy-faber-$stamp.XXXXXX")" # legacy backup retired="$retired_dir/command.md" if [ -e "$retired" ] || [ -L "$retired" ]; then echo "refusing to overwrite retirement destination: $retired" >&2 exit 1 fi if ! mv "$legacy_cmd" "$retired"; then echo "failed to move retired command; original path was not intentionally removed" >&2 exit 1 fi printf 'retired=%s\n' "$retired"
-
Run
scripts/doctor.sh, restart Claude Code, and run the full/yshifusmoke below. -
Roll back only if the command path is still absent: Set
retiredto the exact path printed above, then runlegacy_cmd="$HOME/.claude/commands/faber.md"; test ! -e "$legacy_cmd" && test ! -L "$legacy_cmd" && mv "$retired" "$legacy_cmd". If either path conflicts, stop and inspect it; never overwrite. The move preserves the original bytes and mode. # legacy rollback Do not recreate this command by hand.
-
- Give that session GitHub access (
ghCLI or the GitHub connector) so yshifu can read state and open issues.
yshifu never writes code or opens PRs and never approves on your behalf — it opens
issues and orchestrates the loop. yshifu never merges either: when a PR is CI-green and
Codex passed that exact head/base, it labels the PR merge-ready and hands it to you, naming the risk
when there is one (safety-rail changes, north-star / goal drift, high-risk back-look). The
intake gate is your approval of the concrete intake draft for user-directed work;
proactive work uses yshifu⇄Codex consensus under your approved north star. yshifu records
the accepted exact issue title/body digests on the issue. Neither path directly earns ready.
New normal work then needs operator-merged G1 intent and G2 spec-with-risk, and the
applicable plan gate. Only then does yshifu apply ready. Before spawn it records a
unique claim, adds/verifies claimed, consumes the existing ready label, and verifies
the exact state. Under the hard one-manager-session invariant, a crash leaves claimed
visible and blocks duplicate pickup; it is not a cross-manager mutex. Parallel managers
pause with needs-human. This
remains an in-session action, not a separate automated trigger.
Nothing to "wire" here — the coder and brief are not standalone services. They are
instruction files in routines/ that yshifu reads and passes (with the
specific task context) to the subagents it spawns in-session. To restore them, just make
sure the files are present on main:
| File | Role |
|---|---|
routines/coder.md |
Coder baseline instructions yshifu passes to a spawned coder subagent |
routines/coder-revision.md |
Coder fix-mode instructions for a spawned revision subagent |
routines/brief.md |
Brief instructions yshifu can run for resurfacing (read-only; not auto-scheduled) |
Notes:
- The
/yshifucommand (step 1) already points yshifu at these files, so once it is installed yshifu will use them when it spawns a coder; there is no separate trigger, repository, or connector setting to configure. - The coder instructions self-guard: build requires intake
claimedwithready|needs-humanabsent; fix requires PRclaimed, PRneeds-humanabsent, and parent-intakeready|claimed|needs-humanabsent. Both match a unique claim ID and exact tuple before doing anything. For normal work,readymeans the durable intake record, G1/G2, and applicable plan gate all cleared; named bootstraps use their dedicated approved plan. Approval or consensus alone is not enough.
The reviewer runs on Codex (OpenAI), not on Claude — that cross-vendor split is
deliberate (decorrelated blind spots). It uses Codex's built-in review
(codex exec review) driven by the in-session harness — see
reviewer/codex-review.md for the mechanism and the loop.
Make sure the Codex CLI is installed and signed in, then drive review with
scripts/codex-review.sh. The script lives only in this
control-plane repo, so from within the target repo's clone yshifu invokes it by absolute
path — "$HOME/git/ystack/scripts/codex-review.sh" <PR#> (substitute your ystack
clone; or put <ystack>/scripts on PATH and call codex-review.sh <PR#>). Do not
copy the script into each target repo. gh infers <owner>/<repo> from the cwd; the
script runs codex exec review (read-only forced via -c sandbox_mode="read-only") and
posts Codex's verdict to the PR verbatim. No GitHub-side wiring needed; the script
itself only posts a comment.
Comments only / read-only is non-negotiable: Codex (and the script) get no write access beyond posting review comments. It never pushes, never approves-to-merge, never merges, and is never the author of the code it reviews (see Safety rails below).
Future, not wired. Codex also offers a GitHub integration that could review PRs automatically on open/update with no yshifu session — an autonomous upgrade. It is not set up here; the in-session harness above is the only review path today.
Do this once per target repo. The full checklist already exists — reuse it, do
not re-derive it: templates/repo-setup.md.
That checklist covers:
- Labels — the
debating/ready/claimed/round-0..round-3/needs-human/merge-readyset the loop uses as its state (each coder spawn is stateless, so the round lives in the label;debatingmarks a proactive issue under manager-debate, not yet approved). Thegh label createloop is in that file.setup-target-repo.shis the canonical source of truth for these labels: a normal run force-edits each live label to the script's definitions, so re-running reconciles any drift. To verify labels after a restore without mutating anything, run the read-only dry mode —scripts/setup-target-repo.sh --check <owner>/<repo>— which reports per labelmatches/differs/missingand exits non-zero if anything is missing or differs. After activating this policy, audit oldreadyissues too. Remove it from every already-open implementation PR beforelegacy-openfix mode. A PR-absent issue keeps it only with a complete new build tuple (and exact approved plan record for a named bootstrap). The reconciled description does not turn an old label into evidence. - Branch protection on
main— require CI status checks to pass; keep GitHub's native auto-merge button off (merging is the operator's, gated on green CI and amerge-readylabel — never a server-side trigger, and never an agent). Caveat: that section ofrepo-setup.mdis a UI checkbox checklist with no command (unlike the labels loop), and branch protection isn't available on free private repos — it needs a paid plan or a public repo. If you can't enable it, CI is still the hard gate (see Safety rails); you just lose the server-side enforcement. - CI — comes from
.github/workflows/ci.yml(structure check + shellcheck). It is the hard merge gate; restore it by having this repo's.github/workflows/present onmain. Don't copy its steps here — link to it.- The structure check enforces the full backup against
ci/required-files.txt— the source of truth for every restore-critical file. It fails the build if any listed path is missing (and if a listedscripts/*.shisn't executable), so a PR can't silently dropinstall.sh,RESTORE.md, or any other load-bearing file and stay green. When you add a file the team needs to be reconstructable, add it to that manifest. - Out of scope:
claude.yml..github/workflows/also containsclaude.yml— the optional@claude-mention helper (anthropics/claude-code-action, pinned to the full commit SHA for the v1 loop). It is not part of the team loop and is not required to restore the coding team, so it is out of scope for this runbook. If you do want it back, note that it needs aCLAUDE_CODE_OAUTH_TOKENrepo secret, which lives only in GitHub repo settings (not in any file here) and must be re-created by hand.
- The structure check enforces the full backup against
- North star (per target) — the team steers by the target's committed
.ystack/north-star.md(resolved viascripts/lib/north-star.sh; the manager-debate gate reads its committed content). Restore it in the target repo: copytemplates/.ystack/north-star.mdto.ystack/north-star.md, replace the placeholder with your direction, remove the<!-- ystack-shipped-default -->marker, and commit it — a missing / still-marked / no-status: activestar FAILs the proactive gate (manager-review.sh) and WARNs indoctor.sh. Setup does not seed it —setup-target-repo.shonly creates the loop labels. (When restoring ystack itself, its north star is the rootNORTH_STAR.md— ystack is its own target — so there's no separate.ystack/north-star.mdto restore.) - Conventions — drop
templates/target-CLAUDE.mdinto the target repo's root, filled in for that repo. - The in-session setup — install
/yshifu(step 1) and connect the Codex CLI forscripts/codex-review.sh(step 3); there are no per-repo routine triggers to wire.
If you are restoring ystack itself, the labels and CI live in this repo already;
recreate any labels that were lost with the loop in templates/repo-setup.md using
<owner>/<repo> = your clone/copy of this repo (restoring ystack is the same repo, not
a fork).
The stable manual command is scripts/core-contract.sh.
Its selected root, ingress boundary, five private modules, fixtures, ledgers, and tests
are all listed in ci/required-files.txt. Restore those files
from the same commit; do not mix generations or edit a published generation in place.
Install jq 1.6 on PATH, confirm jq --version prints jq-1.6, then run:
bash scripts/test/portable-core-assembly.test.shThe proof checks the fixed package, public error boundary, and the complete 34-row and
279-row migration ledgers. It does not install or activate a profile, contact a target,
or grant authority. The existing /yshifu restore path remains separate.
The v1 generation registry is ordered and append-only. Its last generation contains the inactive accounted-validation interface and remains restorable after the v2 selection. Restore every file listed in the manifest from the same commit, then run its focused proof:
bash scripts/test/portable-core-accounted-validation.test.shThat proof checks caller-owned mode-0700 scratch, exact boundary admission, the fixed descriptor-3 receipt, ordinary-call compatibility, cleanup, and byte-for-byte copies of the unchanged exports. It performs no install, activation, networked validation, or target use.
core/v2/ contains append-only inactive generations, including the unchanged
fake-forge generation. The stable wrapper and inactive profile resolver select the
evidence-identity correction. Restore the registry and both complete generations from
one commit, then run:
bash scripts/test/portable-core-v2-fake-forge.test.shThis proof validates the atomic wrapper selection and deterministic fake candidate materialization in a caller-disposable repository. The package is not qualified for a real forge. It grants no credential, network, publish, push, merge, or remote branch-write capability. The switch does not install the resolver or select a live profile.
Restore the complete selected v2 evidence-identity generation, its dedicated ledger, and its focused test from the manifest, then run:
bash scripts/test/portable-core-v2-evidence-identity.test.shThis proof requires passed evidence to retain the exact selected performer, binding, environment, capability, and metadata projection. It also proves that all-non-passing incident mismatches remain preservable. Its selection is bound to the reviewed generation merge and publisher receipt and grants no authority, qualification, credential, network, activation, or external effect.
Restore every path in the manifest's inactive fake adapter matrix block, then run:
bash scripts/test/portable-adapter-contracts.test.shThe proof resolves four fake-only profiles, validates both core stage records in each cell, and checks the accepted 2×2 matrix plus closed negative protocol cases. It uses no real adapter or credential and makes no network/host isolation, qualification, activation, or external-target-smoke claim.
Restore the three paths in the manifest's inactive control policy-set block, then run:
bash scripts/test/control-policy-set.test.shThe proof validates only the canonical six-section identity bundle. It does not evaluate a policy, grant authority, activate a profile, or enforce sandbox, credential, risk, kill-switch, or evidence behavior.
Restore the two paths in the manifest's inactive Control foundation roll-up block, then run:
bash scripts/test/control-foundation-rollup.test.shThis recomputes the six policy and six decision file identities, their common core generation and package closure, and the inactive fail-closed boundary. It adds no aggregator runtime and makes no enforcement, qualification, authority, activation, or external-effect claim.
Restore the five paths in the manifest's inactive duty-separation block, then run:
bash scripts/test/control-duty-separation.test.shThis checks the exact policy and decision links, mirrored policy-set validator, full selected public-core package closure, evaluator identities, role and permission ceilings, all three identity-separation dimensions, dormant publisher behavior, and canonical observation results. It does not enforce effective sandbox or credential permissions and grants no authority or external write.
Restore the five paths in the manifest's inactive risk-gates block, then run:
bash scripts/test/control-risk-gates.test.shThis checks the exact policy, decision, evaluator, duty-separation, policy-set, and
public-core identity closure; tier downgrade and malformed-input handling; and
canonical violated or inconclusive observations. Decision input is an
unqualified immutable claim, so an accept claim cannot produce satisfied or grant
approval, authority, qualification, activation, permission, or an external effect.
Restore the five paths in the manifest's inactive kill-switch block, then run:
bash scripts/test/control-kill-switch.test.shThis checks exact identity closure, all five stop scopes, cleared and missing state, rollback, replay, ambiguity, duty failure, malformed input, deterministic output, and bounded child-process cleanup. The evaluator is inactive and observation only. It does not send a signal, cancel or run a candidate, grant authority, use a credential, activate a profile, or perform an external write.
Restore the five paths in the manifest's inactive sandbox-policy block, then run:
bash scripts/test/control-sandbox-policy.test.shThis checks exact identity closure, the complete allowlisted declaration, unknown and violated inputs, duty failure, stale links, malformed input, deterministic output, snapshotted jq execution, and postflight mutation detection. The result is inactive and declaration-only. It does not enforce or qualify a real sandbox, run a candidate or adapter, use a credential, activate a profile, or perform a network or external-write action.
Restore the five paths in the manifest's inactive credential-policy block, then run:
bash scripts/test/control-credential-policy.test.shThis checks exact identity closure, the brokered single-stage model-inference ceiling, protected-role and incompatible-permission handling, incomplete and malformed claims, stale links, deterministic output, bounded child cleanup, and postflight mutation detection. The evaluator is inactive and observation only. It does not read credential material or credential-like environment values, qualify a claim, grant authority, activate a profile, run a candidate or adapter, or perform a network or external-write action.
Restore the five paths in the manifest's inactive evidence-integrity block, then run:
bash scripts/test/control-evidence-integrity.test.shThis checks exact policy-set and public-core closure, stage and qualification identity binding, canonical evidence/prior sets, stale or aliased references, and deterministic observation output. It also checks the explicit trusted-launcher boundary, exact marked-payload identity, producer-final scratch identities, accounted core receipt and cleanup, and nested Bash environment isolation. The launcher is not self-attested. It does not read proof bytes, establish proof truth, qualify a workflow, store evidence, grant authority, activate a profile, run a candidate or adapter, or perform a network or external-write action.
Restore the five paths in the manifest's inactive canonical state scanner block from the same commit. With the same pinned, architecture-bound jq 1.6 runtime used by the portable core, run:
bash scripts/test/orchestrator-state-scanner.test.shThis checks bounded canonical snapshots, exact repository and commit binding, deterministic pending and stranded classifications, recovery reasons, private runtime snapshots, and fail-closed input handling. The scanner remains inactive and observation only. It does not deliver or retry events, reconcile or write state, use a credential or network, activate a profile, or touch a target.
Restore the two paths in the manifest's inactive reconciliation planner block, then run:
bash scripts/test/orchestrator-reconciliation-plan.test.shThis checks deterministic at-least-once planning, failed-stage retry, stranded attempt recovery without a new attempt, acknowledged-delivery suppression, operator messages, and stable backpressure. It also rejects malformed, stale, duplicate, unsorted, and oversized inputs. The jq filter remains inactive and planning only. It does not dispatch, schedule, execute recovery, write state, use a credential or network, activate a profile, publish, or touch a target.
Restore the two paths in the manifest's inactive GitHub forge normalizer payload block, then run:
bash scripts/test/default-github-forge-adapter.test.shThis checks exact caller bindings, deterministic state normalization, opaque provider data, and fail-closed malformed or stale input. This stage intentionally has no adapter manifest. A later assembly PR can bind the payload through a durable main commit and add default-set wiring. The pure jq payload is offline and unqualified. It does not call GitHub, use a credential, change a repository or request, grant authority or qualification, or activate a profile.
Restore the two paths in the manifest's inactive Codex native reviewer normalizer payload block, then run:
bash scripts/test/default-codex-native-reviewer-adapter.test.shThis checks exact caller bindings, deterministic clean and finding states, opaque provider severity, explicit hidden-execution unavailability, and fail-closed malformed or stale input. This stage intentionally has no adapter manifest. A later assembly PR can bind the payload through a durable main commit and add default-set wiring. The pure jq payload is offline, read-only, and unqualified. It does not invoke a model or CLI, use a credential or network, post a review, grant authority or qualification, or activate a profile.
Restore the two paths in the manifest's inactive GitHub Actions CI normalizer payload block, then run:
bash scripts/test/default-github-actions-ci-adapter.test.shThis checks exact caller bindings, deterministic workflow and job-state normalization, opaque provider data, and fail-closed malformed, contradictory, incomplete, or stale input. This stage intentionally has no adapter manifest. A later assembly PR can bind the payload through a durable main commit and add default-set wiring. The pure jq payload is offline and unqualified. It does not call GitHub, use a credential, rerun, cancel, or dispatch work, change a repository, grant authority or qualification, or activate a profile.
Restore the four paths in the manifest's inactive local Git materializer block, then run:
bash scripts/test/local-git-materializer-adapter.test.shThis builds disposable SHA-1 and SHA-256 source repositories, validates a complete
portable-core v2 profile and stage request, and proves that a contract-bound patch
becomes a deterministic bare child commit and path-free receipt. The negative
matrix rejects moved identities, unsafe directories and paths, hooks, filters,
remotes, worktrees, alternates, shallow or partial repositories, replace state,
binary patches, symlinks, submodules, host Git templates, and reachable source
history above the fixed 65,536-object or 256 MiB import budget. Copy/rename patch
metadata and source trees containing empty subtrees also fail closed. It proves empty-patch
no-change and rejects tree scans above 65,536 entries, 64 path components, or a
16 MiB encoded listing. It also checks that the source stays unchanged and scratch
is removed. Input and config snapshots are stream-capped before parsing. The
complete source filesystem scan is capped at 65,536 entries and 8 MiB. Tree scans
are capped at 1,024 tree objects, with each object size-checked before non-recursive
expansion. A shared-large-blob
fixture proves the 256 MiB pre-apply candidate
budget blocks path fan-out before Git writes changed blobs.
The test compiles the private object-closure helper with strict warnings. It proves
that an oversized historical tree and an oversized packed-refs file fail before
recursive traversal or parsing. The runtime takes the pinned jq 1.6 executable as
an explicit dependency and ignores the caller's executable search path.
This payload has no adapter manifest. A later assembly PR may bind the directory tree through the payload's durable main commit. It is an inactive, local-only materializer, not a GitHub or GitLab operation. Restoring it does not qualify an adapter, select or activate a profile, read a credential, contact a provider or real target during construction, or permit push, publish, merge, or another external write.
Restore the two paths in the manifest's inactive Claude Code producer normalizer payload block, then run:
bash scripts/test/default-claude-code-producer-adapter.test.shThis checks the caller-supplied core request, resolved profile, manifest, target, package, config, prompt, skills, tools, model, effort, and execution boundary. The test manifest is synthetic fixture data; no adapter manifest ships in this unit. A caller constructs the trust context only after canonical SHA-256 verification of the snapshot pair. The proof rejects moved untrusted content, changed attempt identity, and non-diff output for a changed git-patch request. A later assembly PR can bind the durable main payload into the default profile. The pure jq payload is inactive, offline, and unqualified. It does not call Claude Code, invoke a model, use a credential or network, write a target, publish, or activate a profile.
Restore the three paths in the manifest's inactive local Git materializer protocol block, then run:
bash scripts/test/local-git-materializer-protocol.test.shThis builds only synthetic JSON fixtures. It validates the exact portable-core v2 profile, request, manifest, contract, payload, receipt, and result relations. The negative matrix rejects malformed, stale, duplicate, relabelled, unsafe-path, expanded-mode, and weakened-limit inputs, and repeat checks require canonical output. It also requires caller-verified content-and-digest payload pairs, binds the source repository, commit, and tree to the request, enforces patch and changed-path limits, and revalidates the envelope before every projection. A stage result also requires a caller-verified receipt pair whose request, attempt, source, limits, and changed/no-change outcome all match before the receipt digest can back passing evidence.
There is no materialization executable in this stage. Restoring it cannot read or write a Git repository, create a candidate, run provider tooling, use a credential or network, grant authority or qualification, or perform an external effect. A later runtime PR may consume the protocol and test fixture; only a still-later assembly may add a manifest after the complete package has a durable main commit.
Restore the two paths in the manifest's inactive dormant publisher normalizer payload block, then run:
bash scripts/test/default-dormant-publisher-adapter.test.shThis checks exact attempt, idempotency, repository, change-request, candidate, path, evidence, decision, terminal-time, observation-time, and boundary bindings. The caller supplies a canonical, SHA-256-verified claim pair, and changed content is rejected. Permit, deny, and inconclusive claims all remain inert; malformed input is rejected and moved input becomes stale. This stage has no adapter manifest. A later assembly PR can bind its durable main payload with empty capability, permission, and tool sets. It is separate from the temporary construction publisher gate and performs no credential, network, merge, external write, authority, qualification, or profile activation.
Restore the two paths in the manifest's inactive deterministic verifier normalizer payload block, then run:
bash scripts/test/default-deterministic-verifier-adapter.test.shThis validates exact core-v2 verifier request/profile/result relations, the deterministic role and permission ceiling, candidate and verification-plan binding, exact caller-verified snapshot and result pairs, attempt identity, timestamp and evidence precedence, stale inputs, and the boundary that keeps CI observations out of verifier evidence. This stage intentionally has no adapter manifest. The pure jq payload is offline and unqualified. It does not execute a candidate or tool, read proof bytes, enforce a sandbox, use a credential or network, write evidence, grant authority or qualification, or activate a profile.
Pre-flight first. Before running the full live loop, run the read-only self-check
from this clone to catch the cheap failures fast — a missing credential, an
uninstalled /yshifu, a dropped restore-critical file, or absent loop labels:
scripts/doctor.sh # checks (a) /yshifu points here (b) gh auth (c) claude on PATH (d) codex (e) required files
scripts/doctor.sh <owner>/<repo> # also verifies that target repo's loop labels (delegates to setup-target-repo.sh --check)It prints a pass/fail line per check and exits non-zero if anything fails; it never
mutates anything. Fix any fail: line before the smoke test below — otherwise the
loop will stall at exactly that gap. Then prove the team end to end:
Run one trivial issue through the full loop end to end, all from your yshifu session:
- Ask yshifu for a throwaway change (e.g. a one-line doc tweak). Your ask is the request; yshifu drafts an intake issue.
- You approve that concrete intake. Confirm yshifu records the exact title/body digests,
then coordinate G1 intent and G2 spec-with-risk PRs through independent review and
your merge. Confirm the plan gate passes: high risk uses a reviewed plan-only
PR you merge; routine work uses a plan-first remote head accepted by a different reviewer.
Only then confirm yshifu applies
ready, takes a verifiedclaimedpickup, clearsready, and spawns a coder subagent. - Confirm the implementation PR says
Closes #<n>and carriesround-0; the earlier artifact/plan PRs must have used non-closingTracks #<n>. - Confirm the review path:
- yshifu runs
scripts/codex-review.sh <PR#>and Codex posts review comments to the PR (and nothing else — no approve, no merge). - CI runs on the PR and goes green.
- yshifu runs
- If there's feedback, confirm yshifu spawns a fix-mode coder that pushes follow-up
commits and bumps the
round-Nlabel, then re-runscodex-review.sh. - Confirm the merge path: for a clean PR (CI green + Codex passed at that head/base) yshifu
applies
merge-readyand hands it over — you merge. Nothing else has a merge path:mainrequires a pull request and an approving review, the reviewer is comments-only, and no agent has a bypass.
If every step above fired, the team is back. If one stage is silent: re-check /yshifu is
installed and points at this repo (step 1), the coder instruction files are present (step
2), and the Codex CLI is signed in so codex-review.sh runs (step 3).
Optional: confirm the brief by asking yshifu to run it, and check you get one action-first message back.
These are load-bearing — per the self-modification safety section of
AGENTS.md, never weaken them without explicit human sign-off:
- Reviewer stays read-only / comments-only. Codex never pushes, approves-to-merge, or merges, and is never the author.
- Merging is yours, always. yshifu labels a reviewed-clean head/base
merge-readyand hands the PR over; it never merges, and the label goes void when the reviewed head or base moves. The in-session auto-merge v1 allowed was retired when the branch ruleset landed. Carve-outs survive as handoff duties: safety-rail changes, ambiguous specs, anything escalated (needs-human/round-cap), north-star milestones / goal drift, and high-risk back-look (auth, migrations, shared repos) are named as such when handed to you. Codex never approves or merges either. - Rounds cap (~3) +
needs-humanescalation stay intact. Because each coder spawn is stateless, this state lives in the labels (round-0..3,needs-human), not in agent memory — so the labels (step 4) are part of the safety system, not decoration. - CI is the hard gate. Merges require green CI; restore CI before trusting the loop.
- One of two intake paths starts one shared artifact/plan pipeline. A
user-directed issue starts after you approve the concrete intake draft. A
proactive issue starts after you've approved the active north star and yshifu⇄Codex
manager-debate reaches consensus — no per-issue ask. The exact accepted issue title/body digests
is recorded. Then G1 intent, G2 spec-with-risk, and the applicable plan gate must pass.
readymeans that whole sequence cleared; named bootstraps use only their dedicated approved plan.claimedmeans a pickup is active or unresolved and, under the one-manager invariant, blocks another spawn. It is not a cross-manager mutex. yshifu never infers intake acceptance or self-accepts a plan.
Real lessons from setting this up:
/yshifunot found, or points at the wrong repo? Re-runscripts/install.sh(no arguments) from your ystack clone — it regenerates~/.claude/commands/yshifu.mdwith this clone's path. Do not hand-edit it.- Local
ghauth is what the loop uses. yshifu, the spawned coder, andcodex-review.shall run in your local Claude Code session and hit GitHub through your localghauth. If GitHub calls fail, checkgh auth statusfirst. - Coder won't start on an issue?
readyis only the unclaimed cue. The manager must take a uniqueclaimedpickup and clearready; the coder then requires that claim, noneeds-human, and an exact tuple. Under one manager, an unresolved claim blocks another spawn; parallel managers requireneeds-human. Approval or consensus alone is not runnable; confirm every applicable gate and claim transition. - No Codex review on the PR? The review is not automatic — yshifu must run
scripts/codex-review.sh <PR#>from the target repo's clone. Check the Codex CLI is installed and signed in (codexruns), and that the script is invoked by absolute path. Claude and Codex never talk directly — the PR is the only message bus.