Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ Two goals drive the backlog:
from the thread. Proactive work cites a passed manager-review verdict
whose `Intake-title-sha256` and `Intake-body-sha256` markers match those digests. The
reviewer must see both unchanged before and after its run or post no verdict. Authenticate the source like merge
evidence: first select the newest comment by the current `gh` operator with the exact
evidence: first select the newest comment by the current `gh` operator with
exactly one manager-reviewer clean header and one matching anchored 64-hex marker for
each digest, without filtering by
verdict. Then require exactly one anchored `VERDICT:` line and require it to be
Expand Down Expand Up @@ -266,7 +266,7 @@ Two goals drive the backlog:
plans and human merge; they are the only process exceptions to a `work/<slug>/plan.md`.
This gate is manually enforced by the manager, coder, and reviewer today; no hook
or workflow may be claimed until one lands.
- **Plain language, always** (operator rule, 2026-08-26): every artifact
- **Plain language, always**: every artifact
(intent/spec/plan), PR title/description, and review comment is written for a
tired human. Short sentences. Everyday words. No jargon where a plain word
works. If two phrasings say the same thing, use the shorter one.
Expand Down Expand Up @@ -353,9 +353,9 @@ must remain in source or move to an accepted sidecar/metadata mechanism.
PR description when a prompt changes.
- **Never weaken the safety rails without explicit human sign-off:** reviewer stays
read-only / comments-only. Outside the active, identity-matching ystack-self
construction overlay, **merging is the operator's, always** — the in-session
auto-merge v1 was retired when the branch ruleset landed, and no agent has a
merge path. The overlay above is the sole narrow exception and authorizes only
construction overlay, **merging is the operator's, always** — this is policy, and it binds every
agent whatever its harness; `.claude/hooks/no-merge-guard.sh` is only defense-in-depth inside this repo, for
known Claude Bash merge and direct-main-push commands; targets do not get it. The overlay above is the sole narrow exception and authorizes only
the publisher named by the matching mode record after the exact gates above
pass. The rounds cap and `needs-human` escalation stay
intact. yshifu never writes code/opens PRs and **never self-approves acting alone** — a
Expand Down
17 changes: 0 additions & 17 deletions config/models.conf
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
# both are present.) See README.md's "Model policy" section for the full
# spend-by-leverage rationale.
#
# Wiring status: foundation + gates + coder spawn + hands all wired (issues #109-#112).
# This file is read by the review/debate gates (scripts/codex-review.sh /
# scripts/manager-review.sh), by the coder spawn, and by the hands delegation policy —
# see README.md's "Wiring status" note for the full picture. Gate wiring is script-level
Expand Down Expand Up @@ -56,19 +55,3 @@ YSTACK_REVIEW_EFFORT=high

# YSTACK_DEBATE_EFFORT: reasoning effort for the manager-debate gate (manager-review.sh).
YSTACK_DEBATE_EFFORT=high

# --- Legacy bridge — FABRICA_* mirrors of the keys above ---------------------------
# The live manager still reads the legacy FABRICA_* names. These mirrors keep it working
# until the follow-up PR flips its keys; delete them then. Each mirror copies its
# YSTACK_* value above, so the two families can never drift apart. The mirrors are
# consumed by the scripts and persona that source this file, not in this file itself:
# shellcheck disable=SC2034
FABRICA_CODER_MODEL="$YSTACK_CODER_MODEL" # legacy bridge
# shellcheck disable=SC2034
FABRICA_HANDS_MODEL="$YSTACK_HANDS_MODEL" # legacy bridge
# shellcheck disable=SC2034
FABRICA_CODEX_MODEL="$YSTACK_CODEX_MODEL" # legacy bridge
# shellcheck disable=SC2034
FABRICA_REVIEW_EFFORT="$YSTACK_REVIEW_EFFORT" # legacy bridge
# shellcheck disable=SC2034
FABRICA_DEBATE_EFFORT="$YSTACK_DEBATE_EFFORT" # legacy bridge
80 changes: 36 additions & 44 deletions manager/CLAUDE.md

Large diffs are not rendered by default.

11 changes: 5 additions & 6 deletions reviewer/codex-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ every `gh` call, so a `GH_REPO` in the environment can't redirect the comment to
(approval is already `never` for review); the script deliberately does **not** pass
`--dangerously-bypass-approvals-and-sandbox`, and avoids `--ignore-user-config` so the
operator's model/effort defaults still apply. See **model policy** below for how `<effort>`
and the optional `-m <model>` are resolved (#110) — the gate's reasoning effort is **always**
and the optional `-m <model>` are resolved — the gate's reasoning effort is **always**
pinned explicitly, never left to inherit whatever the operator's personal Codex config
happens to default to.
3. Posts Codex's review to the PR **verbatim**: `gh pr comment <PR#> --body-file <tmpfile>`,
Expand Down Expand Up @@ -111,7 +111,7 @@ the repo, and are cleaned up via the `trap ... EXIT` (removed even on failure)
`<tmpfile>` exists only to capture Codex's clean final review off the noisy exec trace.
Neither is **ever** committed; the **PR comment is the durable reviewer output**.

## Model policy (#110)
## Model policy

The review gate is a **max-capability decision point** (spend-by-leverage — see
[`config/models.conf`](../config/models.conf) and README.md's "Model policy" section), so it
Expand All @@ -126,8 +126,7 @@ own** config — operator-owned and doctor-validated — so sourcing it directly
If the **reviewed repo** has committed its own [`.ystack/models.conf`](../templates/.ystack/models.conf)
(same format/keys, an opt-in per-target override), it may override the **producer/model keys
only**. A target that has not renamed yet may still keep it at the legacy `.fabrica/models.conf` path — the harness still reads it there.
Two properties are security-critical here (a P1 finding from an adversarial review of
PR #115 — the original design got both wrong):
Two properties are security-critical here:

- **Trust anchor: the gh-bound DEFAULT branch, fetched fresh — never the PR head.** The PR head
is the untrusted diff *under review*; reading a config override off it would let a malicious
Expand Down Expand Up @@ -164,7 +163,7 @@ Applying the resolved config:
pinned) — so every review documents on the record exactly what gated it, and any drift from
a stray personal config is visible in the PR history, not just in a log nobody reads.

## Degraded-review detection (#117, hardened in #119)
## Degraded-review detection

The script FAILS LOUDLY on a degraded/non-substantive Codex run instead of posting a fake
"clean" verdict. Real incident (2026-07-11): `codex-code-mode-host` failed to spawn (missing
Expand Down Expand Up @@ -208,7 +207,7 @@ NO `Reviewed-head`/`Reviewed-base` markers. That means the marker parser in the
never mistake a degraded run for a completed review — belt-and-suspenders on top of yshifu
reading the comment text before it labels anything `merge-ready`.

**The DEGRADED comment never embeds codex's raw output verbatim (#119 P2 integrity fix).** The
**The DEGRADED comment never embeds codex's raw output verbatim.** The
DEGRADED comment is posted by, and so is authored as, the same gh-authenticated operator the
operator's own `scripts/merge-pr.sh` trusts — so it is exactly the kind of comment that
parser's author+header
Expand Down
31 changes: 14 additions & 17 deletions reviewer/manager-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The north star is **per target**: the debate is judged against the **target repo
committed `.ystack/north-star.md`** — set, committed, and operator-approved in that repo.
`manager-review.sh` resolves it via the shared resolver (`scripts/lib/north-star.sh`) from
the cwd's checkout and reads the **committed** copy pinned to the **gh-bound remote's
default-branch commit, fetched fresh** (#102) — not raw local HEAD. The default branch is
default-branch commit, fetched fresh** — not raw local HEAD. The default branch is
where reviewed changes land via the loop, so its committed star is the *integrated* one (the
best available proxy for operator approval); anchoring there stops a star committed on a
**feature branch** from authorizing proactive work. The gate selects the git remote whose URL
Expand All @@ -34,7 +34,7 @@ does **not** authorize proactive work — nor does one still carrying the shippe
(`ystack-shipped-default`; the legacy `fabrica-shipped-default` marker is treated the same way):
the gate FAILs before invoking Codex, with a pointer back here.

The anchor is **gh-authoritative and fail-closed** (an adversarial sweep, #102 round-2): every
The anchor is **gh-authoritative and fail-closed** (an adversarial sweep): every
input that decides *what* the gate reads is proven against the **same `gh` binding the verdict
posts to**, and any step not provable → **FAIL**. (1) The **default-branch NAME** comes from
gh's `defaultBranchRef` (`gh repo view --json defaultBranchRef`), *not* the stale/locally-
Expand Down Expand Up @@ -167,8 +167,7 @@ the issue is closed.
what consensus filtered out and override it if they want. Default-drop is the floor, not
a silent shredder.

> **Front gate at the north-star altitude (#49).** Per the user-authorized front-gate
> change in **#49**, the manager-debate consensus **is** the intake gate for proactive issues:
> **Front gate at the north-star altitude.** The manager-debate consensus **is** the intake gate for proactive issues:
> on consensus yshifu removes `debating` and records exact-revision intake acceptance —
> **no per-issue user approval.** G1, G2-with-risk, and the plan gate still precede `ready`.
> The user's gate moved up an altitude: the user approves the **north star /
Expand All @@ -191,7 +190,7 @@ explicit `--repo` to every `gh` call, so a `GH_REPO` in the environment can't re
comment to a *different* repo's issue. Then:

1. **Reads the target's committed north star at the gh-bound default-branch commit, fetched
fresh** (#102) — resolved *for the target this run operates on* via the shared resolver
fresh** — resolved *for the target this run operates on* via the shared resolver
(`scripts/lib/north-star.sh`, located from the script's own location by following symlinks
then `dirname/..`, the same derivation `install.sh`/`doctor.sh` use). It selects the git
remote matching the repo `gh` resolves (shared `scripts/lib/gh-remote.sh` — the same gh-bound
Expand Down Expand Up @@ -221,7 +220,7 @@ comment to a *different* repo's issue. Then:
sandbox so the review can't inherit a writable default from the operator's Codex config;
the script deliberately does **not** pass `--dangerously-bypass-approvals-and-sandbox`,
and avoids `--ignore-user-config` so the operator's model/effort defaults still apply. See
**model policy** below for how `<effort>` and the optional `-m <model>` are resolved (#110)
**model policy** below for how `<effort>` and the optional `-m <model>` are resolved —
the gate's reasoning effort is **always** pinned explicitly, never left to inherit whatever
the operator's personal Codex config happens to default to.
Codex grounds its judgment by reading a **clean detached temp worktree at the same anchored
Expand Down Expand Up @@ -258,7 +257,7 @@ comment to a *different* repo's issue. Then:
# manager-review.sh <issue#>
```

## Model policy (#110)
## Model policy

The manager-debate gate is a **max-capability decision point** (spend-by-leverage — see
[`config/models.conf`](../config/models.conf) and README.md's "Model policy" section), so it
Expand All @@ -274,14 +273,12 @@ If the **target repo** has committed its own [`.ystack/models.conf`](../template
(same format/keys, an opt-in per-target override), it may override the **producer/model keys
only**, applied on top of the shipped defaults. A target that has not renamed yet may still
keep the override at the legacy `.fabrica/models.conf` path — the harness still reads it there.
This script's trust anchor was already correct —
the override is read from the **same anchored worktree the debate runs against** (the detached
The override is read from the **same anchored worktree the debate runs against** (the detached
worktree checked out at the fetched default-branch commit, step 2 above), never the operator's
possibly-stale or dirty cwd checkout, so the override always reflects the SAME integrated commit
the north star was read from and Codex is grounding its judgment in — but an adversarial review
of PR #115 found a P1: the override used to be **`source`d directly into this non-sandboxed
harness shell**, so a target-committed file could run as arbitrary shell with the operator's own
`gh`/`codex` credentials. It is now read as **data**, via a strict line-by-line parser
the north star was read from and Codex is grounding its judgment in. A target-committed file
must never run as shell in this non-sandboxed harness (it would execute with the operator's own
`gh`/`codex` credentials), so it is read as **data**, via a strict line-by-line parser
(`mc_parse_target_override` in [`scripts/lib/models-conf.sh`](../scripts/lib/models-conf.sh)) —
never `source`/`.`/`eval`. Only a line matching exactly `YSTACK_<allowedkey>=<value>` is
recognized (value charset-restricted, optionally quoted); every other line — comments, blank
Expand All @@ -308,10 +305,10 @@ Applying the resolved config:
pinned) — so every debate documents on the record exactly what gated it, and any drift from
a stray personal config is visible in the issue history, not just in a log nobody reads.

## Degraded-review detection (#117, hardened in #119)
## Degraded-review detection

The script FAILS LOUDLY on a degraded/non-substantive Codex run instead of posting a fake
`PROCEED`/`REFINE`/`DROP` verdict — the same hardening as `codex-review.sh` (#117), sharing its
`PROCEED`/`REFINE`/`DROP` verdict — the same hardening as `codex-review.sh`, sharing its
detector so the two gates can't diverge on what counts as degraded (real incident and rationale:
see `codex-review.sh`'s **Degraded-review detection** section and
[`scripts/lib/codex-degraded.sh`](../scripts/lib/codex-degraded.sh)).
Expand All @@ -336,8 +333,8 @@ On detection: the script exits non-zero and posts `VERDICT: DEGRADED` (never
manager-reviewer (cross-vendor, read-only)` one, so yshifu's "proceed only on consensus" rule can
never read this as a `PROCEED`.

**The DEGRADED comment never embeds codex's raw output verbatim (#119 P2 integrity fix, same
as `codex-review.sh`).** It never embeds the `-o` verdict answer (untrustworthy on a degraded
**The DEGRADED comment never embeds codex's raw output verbatim (same as
`codex-review.sh`).** It never embeds the `-o` verdict answer (untrustworthy on a degraded
run), **never embeds JSONL** (it contains private agent/command/repository payloads), and embeds
only a bounded, sanitized raw-stderr tail via `cd_sanitize_snippet` — every line prefixed `> `,
which breaks the line anchors a marker parser like the one in the operator's
Expand Down
20 changes: 10 additions & 10 deletions routines/coder-revision.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ You are the Coder, spawned under one exact fix claim to handle review feedback o
follow-up issue yshifu opens, not more rounds). This scoped-down change is TERMINAL and is
allowed only when the brief names newly re-accepted current intent/spec-with-risk/plan
blobs whose scope is exactly that core plus the recorded deferred issue. Missing or
stale artifacts stop before edits.
still subject to the **step-3 command discovery, the step-3.5 PR-CI-presence gate, and
step-5 verify-locally-before-push**: run the step-3 discovery and the step-3.5 gate first
stale artifacts stop before edits. The scoped-down change remains subject to the step-3
command discovery, the step-3.5 PR-CI-presence gate, and step-5 verify-locally-before-push:
run the step-3 discovery and the step-3.5 gate first
(escalate with the SHORT reason `ambiguous-spec` / `failure`, verify `ready` absent,
then add and verify `needs-human`; stop only if no source yields runnable
commands, or if no PR-triggered CI is detectable), make exactly that change, verify
Expand Down Expand Up @@ -135,8 +135,7 @@ You are the Coder, spawned under one exact fix claim to handle review feedback o
`tox.ini`, etc.).
(d) **Only if none** of (a)–(c) yield runnable commands → do NOT guess: comment with
the SHORT reason `ambiguous-spec`, add and verify `needs-human`, then verify
`ready` absent; stop before editing or pushing — the #54 guard, now the last resort,
not a prerequisite (`CLAUDE.md` is an
`ready` absent; stop before editing or pushing (`CLAUDE.md` is an
optional supplement). A docs/trivial repo with no toolchain has nothing to discover and
proceeds normally. **EXCEPTION — a designated greenfield-bootstrap PR** (mirrors
`coder.md`): when yshifu has briefed this fix-mode spawn as the greenfield-bootstrap PR — the
Expand Down Expand Up @@ -187,7 +186,7 @@ You are the Coder, spawned under one exact fix claim to handle review feedback o
`<target>/.ystack/north-star.md`** carries the **yshifu-provided** north star (an active
`status: active` heading, the operator's goal + a done-signal, **NO `ystack-shipped-default`
marker**, **no invented approval token**) — **commit the text yshifu's brief provides; do not
invent the goal** (the post-98a gate reads the committed file and FAILs on missing /
invent the goal** (the gate reads the committed file and FAILs on missing /
marker-carrying / no-active-entry). Also **narrow + sole-purpose** —
only the greenfield-bootstrap PR; any other PR on a command-less / CI-less repo still
escalates and stops per this gate. (This bootstrap PR is operator-approved + human-merged —
Expand Down Expand Up @@ -242,10 +241,11 @@ You are the Coder, spawned under one exact fix claim to handle review feedback o
same branch. **Match CI's pinned tool versions:** when CI pins a linter/formatter/toolchain
to a specific version, lint with **that exact version** locally — a different local
version reports different findings/codes for the same code (e.g. shellcheck SC2317 vs
SC2329) and can be "clean locally" yet land CI-red. In **this repo (ystack itself)**
shellcheck is pinned to **`0.11.0`** (`SHELLCHECK_VERSION` in `.github/workflows/ci.yml`);
lint with `shellcheck -x -S style` over `find . -name '*.sh' -not -path './.git/*'` using
0.11.0, grabbing that static release if your local version differs.
SC2329) and can be "clean locally" yet land CI-red. Read the pinned version from the CI
config and install it the same way CI does (npm, pip, a setup action, or whatever the
workflow uses). In ystack itself the pin is `SHELLCHECK_VERSION` in
`.github/workflows/ci.yml`, and the matching static binary comes from the shellcheck
GitHub releases.
Local green is necessary but not sufficient — the PR's own CI is the ultimate gate,
but you don't wait on it: **yshifu checks PR CI before it hands the PR to the operator**
(no `merge-ready` label until CI is green). Your job is the local green, then the push —
Expand Down
Loading