From bf1e3186b14eb5f898a16149f58a40efa50e7ce0 Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Wed, 8 Jul 2026 11:38:23 +0100 Subject: [PATCH] Resolve the release seam: one gate, in the Build Agent's release mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit release.sh re-implemented the readiness gate (fresh tick -> verdict -> GREEN/YELLOW(--force)/RED -> pre_build) that build.sh --mode release already owns. Plain `pyauto-brain release` is now a door that delegates gate + execution to the Build Agent's release mode; the release conductor keeps its own machinery — release-validation orchestration (rehearse.sh/validate.sh). The "may later split into a dedicated Release Agent" transitional prose is replaced by the resolved boundary in AGENTS.md. Verified: `release` -> build.sh --mode release -> RED -> abort (exit 3); health.sh's `release validate` path untouched. Co-Authored-By: Claude Fable 5 --- AGENTS.md | 23 +++++---- agents/conductors/build/build.sh | 2 +- agents/conductors/release/AGENTS.md | 26 +++++----- agents/conductors/release/release.sh | 74 ++++++++-------------------- bin/pyauto-brain | 4 +- skills/WORKFLOW.md | 2 +- 6 files changed, 46 insertions(+), 85 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 83121d0..41fe3f6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -94,10 +94,11 @@ humans invoke identically, so behaviour isn't re-derived from prose each time. the Brain coordinating *multiple* organs. Has `build` / `deploy` / `release` modes — release is isolated as a mode now, with a clean seam to the release conductor. -- **`agents/conductors/release/`** — reasons over `pyauto-heart readiness`, and on - green triggers the PyAutoBuild release executor (`autobuild pre_build` → - `release.yml`); also orchestrates release validation (`release rehearse` / - `release validate`) across the MCP boundary. +- **`agents/conductors/release/`** — the release door and release-validation + orchestrator: `release rehearse` / `release validate` drive the TestPyPI + rehearsal and the full Stages 0–3 validation; plain `release` delegates the + readiness gate and execution to the Build Agent's release mode (one gate + implementation, not two). - **`agents/conductors/health/`** — the *clinician*: runs the health loop with a human — assess (vitals) → triage → (on your go-ahead) dispatch a validation leg → re-judge — until Heart goes GREEN. Delegates all dispatch to the @@ -111,14 +112,12 @@ humans invoke identically, so behaviour isn't re-derived from prose each time. capability. The single component that talks to Heart; every conductor consults it rather than querying Heart directly. Never dispatches or mutates. -> **Build Agent vs. release mode vs. the release agent.** The Build Agent owns -> all execution orchestration and keeps release as one of its modes (broad build -> scope: generate, run, aggregate, package, tag). `agents/conductors/release/` is the older, -> narrower readiness→`pre_build` driver. The mature architecture splits a -> dedicated **Release Agent** out of the Build Agent's release mode — making -> release-specific decisions (versioning, changelogs, PyPI/tags, human approval), -> consulting the vitals faculty *more strictly*, then requesting execution from the -> Build Agent / PyAutoBuild. Until then: one agent now, clean seam for two later. +> **Build Agent vs. the release conductor — resolved.** The Build Agent's +> release mode owns the **single** readiness-gate + execution path +> (`build.sh --mode release` → `pre_build`). The release conductor owns +> release-*validation* orchestration (`rehearse` / `validate`) and, for plain +> releases, is a door that delegates through that mode. There is deliberately +> no second gate implementation. New agents are added on **demonstrated need, never for symmetry**. Place by tier: a side-effecting decider you drive → `agents/conductors//`; a diff --git a/agents/conductors/build/build.sh b/agents/conductors/build/build.sh index b8038a8..a45f4f2 100755 --- a/agents/conductors/build/build.sh +++ b/agents/conductors/build/build.sh @@ -146,7 +146,7 @@ plan_cmd="autobuild $action" plan=("$plan_cmd") if [[ "$mode" == "release" ]]; then - follow_up+=("Release reasoning lives in 'release mode'; it may later split into a dedicated PyAutoBrain Release Agent.") + follow_up+=("This release mode is the single gate+execution path; the release conductor delegates plain releases through it and owns rehearse/validate.") fi # ----- emit the BuildDecision (structured) ----- diff --git a/agents/conductors/release/AGENTS.md b/agents/conductors/release/AGENTS.md index d21fd93..1602e60 100644 --- a/agents/conductors/release/AGENTS.md +++ b/agents/conductors/release/AGENTS.md @@ -13,29 +13,25 @@ Brain → Heart (gate) → Build (execute) ## Responsibility -1. Refresh and read PyAutoHeart's authoritative readiness verdict - (`pyauto-heart readiness --json`). -2. **Reason over it** — block unless the verdict is green: - - **RED** → a real release blocker; refuse (exit 3). - - **YELLOW** → caution; refuse unless `--force` (exit 2). - - **GREEN** → proceed. -3. On green, delegate to the PyAutoBuild executor — `autobuild pre_build`, which - prepares the workspaces and dispatches `release.yml`. - -The agent holds **no health logic and no release mechanics of its own**. The -health decision is Heart's; the execution is Build's. The Brain only reasons -about the verdict and decides to proceed. +1. **Release-validation orchestration** (this conductor's own machinery): + `release rehearse` (Stage-2 TestPyPI rehearsal) and `release validate` (the + full Stages 0–3 orchestrator) — see `rehearse.sh` / `validate.sh` below. +2. **The release door**: plain `release [--force]` delegates the readiness gate + *and* execution to the Build Agent's release mode + (`build.sh --mode release`), the **single** gate implementation — GREEN + proceeds, YELLOW needs `--force`, RED blocks; on a pass `autobuild + pre_build` dispatches `release.yml`. This conductor holds no second copy of + that gate. ## Run ```bash -bin/pyauto-brain release # reason about readiness, release on green +bin/pyauto-brain release # release door → Build Agent release mode bin/pyauto-brain release --force # also proceed on yellow (cautions ack'd) bin/pyauto-brain release -- 2 # forward `2` (minor_version) to pre_build ``` -Exit codes: `0` released/delegated · `2` yellow (use --force) · `3` red blocked -· `1/4` could not obtain a verdict / unknown verdict. +Gate outcomes and exit codes are the Build Agent's (release mode). ## Release-validation rehearsal (M2) — `release rehearse` diff --git a/agents/conductors/release/release.sh b/agents/conductors/release/release.sh index 44cfbd2..bacec7f 100755 --- a/agents/conductors/release/release.sh +++ b/agents/conductors/release/release.sh @@ -1,24 +1,19 @@ #!/usr/bin/env bash -# agents/release/release.sh — the release agent (a PyAutoBrain reasoning agent). +# agents/release/release.sh — the release conductor. # -# Call chain: Brain -> Heart (gate) -> Build (execute). +# Two jobs: +# release rehearse|validate — release-VALIDATION orchestration, this +# conductor's own machinery (rehearse.sh / +# validate.sh). +# release [--force] [-- ] — the release door: delegates the readiness +# gate AND execution to the Build Agent's +# release mode (build.sh --mode release), +# the single gate implementation. GREEN +# proceeds, YELLOW needs --force, RED +# blocks; forward to pre_build. # -# 1. Ask PyAutoHeart for the authoritative readiness verdict. -# 2. Reason over it: block unless it is green (red = a real blocker; -# yellow = caution). -# 3. On green, delegate to the PyAutoBuild executor (`autobuild pre_build`, -# which prepares the workspaces and dispatches release.yml). -# -# This agent contains NO health logic and NO release mechanics of its own — it -# reasons over Heart's verdict and delegates execution to Build. The health -# decision is Heart's; the work is Build's. The Brain only decides whether and -# when to proceed. -# -# Usage: -# release.sh [--force] [-- ] -# -# --force Proceed on a YELLOW verdict (cautions acknowledged). RED always -# blocks. Use only when you know what you are doing. +# This conductor holds no second copy of the gate logic — one gate +# implementation lives in the Build Agent; this door routes through it. set -uo pipefail @@ -51,40 +46,11 @@ while [[ $# -gt 0 ]]; do esac done -echo "== release agent: reasoning over pyauto-heart readiness ==" - -heart="$(resolve_heart)" || exit $? - -# Refresh the verdict from a fresh tick so the gate is not stale, then read it. -"$heart" tick >/dev/null 2>&1 || echo " (warning: tick failed; using last cached state)" >&2 - -verdict="$(readiness_verdict)" || { echo "release agent: could not obtain readiness verdict" >&2; exit 1; } - -# Show the human-readable readiness block too. -"$heart" readiness || true - -case "$verdict" in - green) - echo "== readiness GREEN — delegating to PyAutoBuild executor ==" - ;; - yellow) - if [[ "$force" -eq 1 ]]; then - echo "== readiness YELLOW — proceeding (--force) ==" - else - echo "release agent: readiness is YELLOW (caution). Re-run with --force to proceed." >&2 - exit 2 - fi - ;; - red) - echo "release agent: readiness is RED — release blocked. Fix the blockers above." >&2 - exit 3 - ;; - *) - echo "release agent: unknown readiness verdict '$verdict' — refusing to release." >&2 - exit 4 - ;; -esac +echo "== release conductor: delegating gate + execution to the Build Agent (release mode) ==" -autobuild="$(resolve_autobuild)" || exit $? -echo "== exec: autobuild pre_build ${forward[*]:-} ==" -exec "$autobuild" pre_build "${forward[@]}" +# One gate implementation, not two: the Build Agent's release mode refreshes +# health via the vitals faculty, applies GREEN / YELLOW(--force) / RED, and on +# a pass runs `autobuild pre_build` (which dispatches release.yml). +args=(--mode release) +[[ "$force" -eq 1 ]] && args+=(--force) +exec bash "$HERE/../build/build.sh" "${args[@]}" -- "${forward[@]}" diff --git a/bin/pyauto-brain b/bin/pyauto-brain index e9aef4d..f02b478 100755 --- a/bin/pyauto-brain +++ b/bin/pyauto-brain @@ -14,7 +14,7 @@ # # pyauto-brain feature [args] (conductor) reason over PyAutoMind tasks, plan growth # pyauto-brain build [args] (conductor) coordinate execution: consult vitals, run Build -# pyauto-brain release [args] (conductor) reason about readiness, then release on green +# pyauto-brain release [args] (conductor) release door → Build Agent release mode (gate + pre_build) # (release rehearse: Stage-2 TestPyPI rehearsal; # release validate: full Stages 0-3 orchestrator) # pyauto-brain health [args] (conductor) the organism's clinician: run the health @@ -48,7 +48,7 @@ declare -A AGENT_DESC=( [feature]="Reason over PyAutoMind feature tasks: select, size, phase, plan for start_dev" [bug]="The immune system: classify a bug/regression/Heart finding, locate the fix, plan the repair" [build]="Coordinate execution: consult the vitals faculty, then delegate to PyAutoBuild" - [release]="Reason about readiness + run release on green; 'release rehearse'/'release validate' drive release validation" + [release]="Release door → the Build Agent release mode (single gate); 'release rehearse'/'release validate' drive release validation" [health]="The organism's clinician: run the health loop with a human, dispatch by dispatch, toward green" [vitals]="Read-only: read the Heart's pulse — the PyAutoHeart readiness verdict (consulted by the conductors)" ) diff --git a/skills/WORKFLOW.md b/skills/WORKFLOW.md index 3073829..0a951b7 100644 --- a/skills/WORKFLOW.md +++ b/skills/WORKFLOW.md @@ -47,7 +47,7 @@ Reasoning is delegated to PyAutoBrain agents (`PyAutoBrain/AGENTS.md` is authori ```bash bin/pyauto-brain feature [//.md] # classify + plan a task (Feature/Bug/Refactor/… routing) bin/pyauto-brain build [--dry-run] # consult vitals, then delegate execution to Build -bin/pyauto-brain release # reason over readiness, release on green +bin/pyauto-brain release # release door → Build Agent release mode (gate + pre_build) bin/pyauto-brain vitals # one health tick + the unified dashboard card ```