Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c341141
plan: record the probe verdict and the rules it cost us
Aug 27, 2026
84d4045
AGENTS.md: the repo's rules, in the file other agents read
Aug 27, 2026
23941e6
AGENTS.md: the repo's rules, in the file other agents read
Aug 27, 2026
f177948
One rules file: AGENTS.md canonical, CLAUDE.md imports it
Aug 27, 2026
2be937d
AGENTS.md: drop the framing the merge duplicated
Aug 27, 2026
8fd3fe6
plan: fold six review findings — including three of my own rules
Aug 27, 2026
d36c1dc
Fold five more findings: guard the rules file, retire a stale permission
Aug 27, 2026
3b4ada0
Retire the agent merge path everywhere it was still promised
Aug 27, 2026
5bc74b3
Fix two regressions the post-cap review found in live files
Aug 27, 2026
bba368d
plan: the lane ships three workflows; fix-on-review is deferred
Aug 27, 2026
c22703a
Amend the spec to match the ruling, and fix the label it still promised
Aug 27, 2026
8d24a3b
Close the merge-helper bypass; point the plan at the amended spec
Aug 27, 2026
7e13b3e
plan: rebaseline onto the folded spec (#142)
Aug 27, 2026
8c6576f
plan: rebaseline onto the spec's R6 fix (#142)
Aug 27, 2026
2a03442
plan: rebaseline onto the spec's round-3 fold (#142)
Aug 27, 2026
678eab8
plan: rebaseline (#142 deadlock fix)
Aug 27, 2026
c6d5afe
plan: rebaseline onto the spec's stale-PR and edge-count fixes (#142)
Aug 27, 2026
54ec34d
plan PR: drop the stale spec copy it was still carrying
Aug 27, 2026
4211cf3
plan: rebaseline onto the spec's dispatch fix (#142)
Aug 27, 2026
74276d3
plan: rebaseline onto the current spec (#142)
Aug 27, 2026
99fdbd1
plan: rebaseline onto the settled spec (#142)
Aug 27, 2026
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
17 changes: 11 additions & 6 deletions .claude/hooks/no-merge-guard.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
# Deterministic layer under the ystack-main-gate ruleset — defense in depth,
# and the only enforcement in clones where no ruleset exists.
#
# Deliberately NOT blocked: scripts/merge-pr.sh — the v1 in-session merge
# harness keeps its own safety checks (SHA pinning, required-checks gate,
# REVIEW_REQUIRED refusal). Raw merge commands are blocked; the harness or a
# human is the only merge path.
# The merge helper is blocked too. It was exempt while the manager could
# merge in-session; that permission is retired, so the exemption became a
# bypass — in a clone with no ruleset, or under an account that can merge,
# an agent could have merged through it. scripts/merge-pr.sh is now the
# operator's own tool, run by a human, never by an agent.

input=$(cat)
cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // empty')
Expand All @@ -22,9 +23,13 @@ if printf '%s' "$cmd" | grep -Eq 'git[[:space:]]+push[^|;&]*[[:space:]:+](refs/h
exit 2
fi

# Raw merge commands — merging is the human gate (or scripts/merge-pr.sh).
# Every merge path — merging is the operator's, always.
if printf '%s' "$cmd" | grep -Eq 'gh[[:space:]]+pr[[:space:]]+merge'; then
echo "Blocked by ystack guard: agents never merge PRs. A human merges at the gate (or use scripts/merge-pr.sh in-session)." >&2
echo "Blocked by ystack guard: agents never merge PRs. The operator merges at the gate." >&2
exit 2
fi
if printf '%s' "$cmd" | grep -Eq 'merge-pr\.sh'; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match only merge-helper execution

This pattern blocks every Bash command that merely mentions the file, not just commands that run it. For example, cat scripts/merge-pr.sh, git diff -- scripts/merge-pr.sh, and shellcheck scripts/merge-pr.sh all exit 2, so an agent cannot inspect or directly verify the helper during legitimate work. Narrow the match to command-position execution while still enforcing the rule that agents never merge.

AGENTS.md reference: AGENTS.md:L100-L101

Useful? React with 👍 / 👎.

echo "Blocked by ystack guard: merge-pr.sh is the operator's own tool. Apply merge-ready and hand the PR over." >&2
exit 2
Comment on lines +31 to 33

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Install the merge guard in target sessions

In the normal installed path, /yshifu runs from the target clone, so Claude loads that target's .claude/settings.json, not the control-plane repo's settings; scripts/install.sh installs only the command file and never installs this hook. As a result, this new check is active only when Claude is launched inside ystack itself, and an agent in a target without branch protection can still invoke the absolute scripts/merge-pr.sh path with its session credentials. Fresh evidence after the earlier hook comment is that the helper block was added only to this repo-local hook; install a global/target hook or enforce the agent ban inside the helper.

AGENTS.md reference: AGENTS.md:L65-L68

Useful? React with 👍 / 👎.

fi
if printf '%s' "$cmd" | grep -Eq 'gh[[:space:]]+api[[:space:]][^|;&]*pulls/[0-9]+/merge'; then
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/intent-draft/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ Template — keep all six sections and headings exact:
<what the operator or the design stage must still answer>

**Write in plain language.** Short sentences, everyday words — the reader is a
tired human, not another agent (see CLAUDE.md > PR rules).
tired human, not another agent (see AGENTS.md > PR rules).
2 changes: 1 addition & 1 deletion .claude/skills/plan-draft/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ Slug: `$0`
later departs from the plan, update `plan.md` in the same commit that departs.

**Write in plain language.** Short sentences, everyday words — the reader is a
tired human, not another agent (see CLAUDE.md > PR rules).
tired human, not another agent (see AGENTS.md > PR rules).
2 changes: 1 addition & 1 deletion .claude/skills/spec-draft/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ Slug: `$0`
**G2**: approval to build.

**Write in plain language.** Short sentences, everyday words — the reader is a
tired human, not another agent (see CLAUDE.md > PR rules).
tired human, not another agent (see AGENTS.md > PR rules).
107 changes: 107 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Working in this repo — for every agent

ystack is a control plane for an autonomous coding team, and it is its own
target repo: agents here are improving the team itself.

**This file is the single source of working rules.** Every agent reads it,
whatever vendor — Codex and most tools look for `AGENTS.md`, Claude Code
reads `CLAUDE.md`, which imports this file. One file, no drift.

Companions: **`REVIEW.md`** is how work is reviewed here (passes, Important
vs nit, how disagreements end). **`work/<slug>/`** holds the artifact chain —
`intent.md` → `spec.md` → `plan.md`. If you are implementing, your brief is
that slug's plan; it is written so someone who never saw the conversation can
build from it.

Two goals drive the backlog:
1. **Reusable by anyone** — a clean, parameterized, well-documented product others can adopt.
2. **Full backup** — everything needed to reconstruct the team if the live setup is lost.

## What lives here
- `manager/CLAUDE.md` — yshifu's role (the manager persona).
- `routines/*.md` — the coder's baseline instructions yshifu reads to brief a spawned
coder subagent (`coder.md` / `coder-revision.md`) plus the per-task `brief.md`.
- `reviewer/codex-review.md` — the doc for the Codex reviewer harness (`scripts/codex-review.sh`).
- `templates/*` — drop-in files for target repos.
- `scripts/*.sh` — the shipped tooling: `install.sh` (generates the `/yshifu` command),
`setup-target-repo.sh` (bootstraps a target repo's loop labels), and `codex-review.sh`
(runs the Codex reviewer against a PR).

## Stack & commands
- Markdown + shell. The setup/reviewer tooling lives in `scripts/*.sh`; validators are
still to come.
- CI: `.github/workflows/ci.yml` (structure check + shellcheck). **CI must stay green —
it is the hard merge gate.** Add real tests as code lands.
- **Shellcheck is pinned to `0.11.0`** (the `SHELLCHECK_VERSION` constant in
`ci.yml` is the single source of truth). CI downloads that exact static release and
verifies its release-asset SHA-256 and version before linting, so a runner-image bump
or changed download can't silently drift it. **Lint locally against 0.11.0** — not
whatever your local install happens to be — with `shellcheck -x -S style` over
`find . -name '*.sh' -not -path './.git/*'`; another shellcheck version can report
different findings/codes (e.g. SC2317 vs SC2329) and disagree with CI. Grab the pinned
binary from the shellcheck GitHub releases if your local version differs.
- The **structure check** reads `ci/required-files.txt` — the manifest of every
restore-critical file — and fails if any listed path is missing (and if a listed
`scripts/*.sh` isn't executable). This is what makes the full-backup goal enforceable:
delete a load-bearing file and CI goes red. **Add new restore-critical files to the
manifest** so the guarantee keeps holding.

## PR rules (enforced by coder + reviewer)
- **One concern per PR.** Soft size budget ~300–400 net lines; split if bigger.
- Every PR links its issue (`Closes #<n>`) and keeps README/docs in sync with any change.
- **Plain language, always** (operator rule, 2026-08-26): 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.

## CRITICAL — self-modification safety
- The live setup runs from **generated/synced artifacts, not from these files directly.**
Editing a prompt or doc here is a *proposal*; it only takes effect once synced: the live
`/yshifu` command is regenerated by re-running **`scripts/install.sh`**, and the coder
instructions in `routines/coder.md` / `routines/coder-revision.md` take effect when
**yshifu reads them to brief a spawned coder subagent** (there are no UI-pasted routines).
Merging a prompt change does NOT change live behavior until synced — call this out in the
PR description when a prompt changes.
- **Never weaken the safety rails without explicit human sign-off:** reviewer stays
read-only / comments-only; **merging is the operator's, always** — the in-session
auto-merge v1 allowed was retired when the branch ruleset landed, and no agent has a
merge path any more; the rounds cap and `needs-human` escalation stay
Comment on lines +66 to +68

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove merge authority from the live manager prompts

[Compliance] Fresh evidence in this revision is that the new canonical rule forbids every agent merge, but the live sources were not synced: manager/CLAUDE.md:547-559 and templates/yshifu-command.md:25-28 still tell yshifu to run merge-pr.sh without confirmation, and scripts/install.sh:41-58 installs that template. A restored or regenerated /yshifu therefore still has a merge path despite this rule; update the manager, command template, and related restore guidance so only the operator merges.

AGENTS.md reference: AGENTS.md:L58-L64

Useful? React with 👍 / 👎.

Comment on lines +65 to +68

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the installed merge-ready label description

[Compliance] This new never-merge rule leaves scripts/setup-target-repo.sh:108 unchanged, so every setup or reconciliation still installs a merge-ready description saying low-risk PRs are “auto-merged in-session.” Because that script force-updates the canonical labels in target repos, restored and newly configured teams continue to advertise the retired agent authority; update the shipped label definition to say the operator merges.

AGENTS.md reference: AGENTS.md:L65-L68

Useful? React with 👍 / 👎.

Comment on lines +65 to +68

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Block the merge helper in the agent hook

[Security] In clones without branch protection, or when the session account can merge, the new claim that agents have no merge path is false: .claude/hooks/no-merge-guard.sh:8-11 explicitly permits scripts/merge-pr.sh, and lines 25-27 direct the agent to that bypass. The helper then performs the merge with the same session credentials. Block the helper from agent tool calls while leaving it available to an operator outside Claude.

AGENTS.md reference: AGENTS.md:L65-L68

Useful? React with 👍 / 👎.

Comment on lines +65 to +68

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retire the remaining agent-merge instructions

[Compliance] In the final tree, shipped guidance still contradicts this new rule: scripts/merge-pr.sh:10-13 says yshifu invokes the helper, while website/index.html:314-316 tells adopters that yshifu merges clean PRs. Leaving those entry points unchanged means operators or future agents can restore the retired authority even though the canonical file now forbids it; update them as part of the same documentation sync.

AGENTS.md reference: AGENTS.md:L49-L55

Useful? React with 👍 / 👎.

intact. yshifu never writes code/opens PRs and **never self-approves acting alone** — a
user-directed issue is gated by the user's approval of the drafted spec (the one-liner is the
request, not the go), a proactive issue by the passed yshifu⇄Codex manager-debate consensus
(for *proactive* work the user's gate is at the north-star altitude; user-directed issues
still need the user's spec approval).

## v2 artifact chain (work/)
- One initiative = one dir: `work/<slug>/` holding `intent.md` → `spec.md` → `plan.md`.
Each artifact lands via its own PR and the operator's merge IS the gate: G1 accepts
the intent, G2 approves the spec, G3 approves the implementation PR (which carries
`plan.md` + code + tests). Details: `work/README.md`; review policy: `REVIEW.md`.
- Skills: `/intent-draft`, `/spec-draft`, `/plan-draft` hold the templates and stage rules.
- **Hash discipline:** `spec.md` frontmatter records `intent-blob` (`git hash-object` of
the intent it was drafted from); `plan.md` records `spec-blob`. On mismatch with main's
current upstream file, label the PR `stale` and stop — never build on a moved artifact.
- **Stage rules (autonomous lane):** the spec stage writes only `work/<slug>/spec.md`;
the implement stage never touches `intent.md`/`spec.md`; unattended agents never write
the **constitution paths** — `.github/**`, `.claude/**`, `AGENTS.md` (this file),
`CLAUDE.md`, `REVIEW.md` — such changes land as patches under `proposals/` that the
operator applies. That is the same list `REVIEW.md` uses; the two must always match, so
a change to one is a change to both. Operator-driven sessions are exempt; Phase 3 hooks
enforce this mechanically via `YSTACK_STAGE`.
- Deterministic branches: `ystack/intent/<slug>`, `ystack/spec/<slug>`,
`ystack/impl/<slug>` — re-runs update the existing PR, never open a second.

## Reusability goal
- No hardcoded personal values (usernames, repo names) in shipped templates — keep the
reusable path parameterized. Personal config stays out of it.

## The rules that bite

- **Never merge.** Opening a PR ends an agent's authority; the operator
merges. Pushing to `main` is refused server-side anyway.
- **Prove it.** Run the checks the plan names, paste the output, and say
which commit you ran them on. Old proof on a new commit is stale.
- **Old names are gone.** The project and its manager were renamed;
`scripts/check-rename.sh` fails CI if either old name survives in a tracked
file. A line documenting real back-compat must carry the word "legacy" —
that is how the gate tells intent from leftovers.
84 changes: 5 additions & 79 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,83 +1,9 @@
# ystack — conventions for the coding agents

This is the **control-plane repo for an autonomous coding team, and it is its own
target repo** — agents working here are improving the team itself. Two goals drive
the backlog:
1. **Reusable by anyone** — a clean, parameterized, well-documented product others can adopt.
2. **Full backup** — everything needed to reconstruct the team if the live setup is lost.
The working rules live in **[`AGENTS.md`](AGENTS.md)** so every agent reads the
same thing, whatever vendor. Open that file — this one only imports it so
Claude Code loads the same text:

## What lives here
- `manager/CLAUDE.md` — yshifu's role (the manager persona).
- `routines/*.md` — the coder's baseline instructions yshifu reads to brief a spawned
coder subagent (`coder.md` / `coder-revision.md`) plus the per-task `brief.md`.
- `reviewer/codex-review.md` — the doc for the Codex reviewer harness (`scripts/codex-review.sh`).
- `templates/*` — drop-in files for target repos.
- `scripts/*.sh` — the shipped tooling: `install.sh` (generates the `/yshifu` command),
`setup-target-repo.sh` (bootstraps a target repo's loop labels), and `codex-review.sh`
(runs the Codex reviewer against a PR).
@AGENTS.md

## Stack & commands
- Markdown + shell. The setup/reviewer tooling lives in `scripts/*.sh`; validators are
still to come.
- CI: `.github/workflows/ci.yml` (structure check + shellcheck). **CI must stay green —
it is the hard merge gate.** Add real tests as code lands.
- **Shellcheck is pinned to `0.11.0`** (the `SHELLCHECK_VERSION` constant in
`ci.yml` is the single source of truth). CI downloads that exact static release and
verifies its release-asset SHA-256 and version before linting, so a runner-image bump
or changed download can't silently drift it. **Lint locally against 0.11.0** — not
whatever your local install happens to be — with `shellcheck -x -S style` over
`find . -name '*.sh' -not -path './.git/*'`; another shellcheck version can report
different findings/codes (e.g. SC2317 vs SC2329) and disagree with CI. Grab the pinned
binary from the shellcheck GitHub releases if your local version differs.
- The **structure check** reads `ci/required-files.txt` — the manifest of every
restore-critical file — and fails if any listed path is missing (and if a listed
`scripts/*.sh` isn't executable). This is what makes the full-backup goal enforceable:
delete a load-bearing file and CI goes red. **Add new restore-critical files to the
manifest** so the guarantee keeps holding.

## PR rules (enforced by coder + reviewer)
- **One concern per PR.** Soft size budget ~300–400 net lines; split if bigger.
- Every PR links its issue (`Closes #<n>`) and keeps README/docs in sync with any change.
- **Plain language, always** (operator rule, 2026-08-26): 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.

## CRITICAL — self-modification safety
- The live setup runs from **generated/synced artifacts, not from these files directly.**
Editing a prompt or doc here is a *proposal*; it only takes effect once synced: the live
`/yshifu` command is regenerated by re-running **`scripts/install.sh`**, and the coder
instructions in `routines/coder.md` / `routines/coder-revision.md` take effect when
**yshifu reads them to brief a spawned coder subagent** (there are no UI-pasted routines).
Merging a prompt change does NOT change live behavior until synced — call this out in the
PR description when a prompt changes.
- **Never weaken the safety rails without explicit human sign-off:** reviewer stays
read-only / comments-only; merge stays gated (yshifu may merge only CI-green + Codex-clean
+ low-risk PRs under standing authorization, and must bring safety-rail / north-star /
ambiguous / high-risk PRs to the human); the rounds cap and `needs-human` escalation stay
intact. yshifu never writes code/opens PRs and **never self-approves acting alone** — a
user-directed issue is gated by the user's approval of the drafted spec (the one-liner is the
request, not the go), a proactive issue by the passed yshifu⇄Codex manager-debate consensus
(for *proactive* work the user's gate is at the north-star altitude; user-directed issues
still need the user's spec approval).

## v2 artifact chain (work/)
- One initiative = one dir: `work/<slug>/` holding `intent.md` → `spec.md` → `plan.md`.
Each artifact lands via its own PR and the operator's merge IS the gate: G1 accepts
the intent, G2 approves the spec, G3 approves the implementation PR (which carries
`plan.md` + code + tests). Details: `work/README.md`; review policy: `REVIEW.md`.
- Skills: `/intent-draft`, `/spec-draft`, `/plan-draft` hold the templates and stage rules.
- **Hash discipline:** `spec.md` frontmatter records `intent-blob` (`git hash-object` of
the intent it was drafted from); `plan.md` records `spec-blob`. On mismatch with main's
current upstream file, label the PR `stale` and stop — never build on a moved artifact.
- **Stage rules (autonomous lane):** the spec stage writes only `work/<slug>/spec.md`;
the implement stage never touches `intent.md`/`spec.md`; unattended agents never write
`.github/**` or `.claude/**` — such changes land as patches under `proposals/` that the
operator applies. Operator-driven sessions are exempt; Phase 3 hooks enforce this
mechanically via `YSTACK_STAGE`.
- Deterministic branches: `ystack/intent/<slug>`, `ystack/spec/<slug>`,
`ystack/impl/<slug>` — re-runs update the existing PR, never open a second.

## Reusability goal
- No hardcoded personal values (usernames, repo names) in shipped templates — keep the
reusable path parameterized. Personal config stays out of it.
Nothing else belongs here. Put changes in `AGENTS.md`.
Loading