diff --git a/agents/conductors/hygiene/AGENTS.md b/agents/conductors/hygiene/AGENTS.md new file mode 100644 index 0000000..5dbbece --- /dev/null +++ b/agents/conductors/hygiene/AGENTS.md @@ -0,0 +1,76 @@ +# Hygiene agent + +> **Tier: conductor** — a front-door agent you *drive*. The *maintenance +> function* — the organism's sense of its own upkeep: it owns the code-quality +> debt that neither proves the organism works (that is Heart) nor measures the +> speed of modelling (that is profiling). It consults the read-only vitals +> faculty like every conductor, reasons over the quality signals, and emits a +> `HygieneDecision` the human/session executes — delegating the actual fixes to +> the dev-flow conductors. It never issues health verdicts and never releases. + +Grown from demonstrated need: the hygiene reasoning already exists, scattered — +the `repo_cleanup` skill (whose SKILL.md explicitly foreshadowed this "Cleanup +Agent"), `cli_noise_clean`, `dep_audit` and `audit_docs` — plus recurring manual +perf-hygiene work (slow unit tests, slow integration-mode scripts, import cost). +This conductor unifies and prioritises them. Design decision (conductor, **no +paired repo**, boundaries) recorded in PyAutoMind +`research/pyautobrain/hygiene_agent_decision.md` and the founding prompt +`feature/pyautobrain/hygiene_agent.md`. + +## Modes + +| Mode | Question | Emits | +|------|----------|-------| +| `perf` | Which unit tests / integration-mode workspace scripts / imports are slow, and what is the cheapest win? | timing findings + route (refactor/bug; JAX-adapt only on a clear win) — *phase 3* | +| `tidy` | What git debris (stale branches, stashes, `[gone]` refs, dirty checkouts) is safe to remove? | the `repo_cleanup` sweep — *phase 2* | +| `noise` | What CLI noise (warnings, stray prints, library chatter) do tests/scripts emit? | the `cli_noise_clean` audit — *phase 2* | +| `deps` | Which dependency caps drift behind PyPI, and at what risk? | the `dep_audit` summary — *phase 2* | +| `docs` | Which `docs/api/*.rst` module paths are stale? | the `audit_docs` findings — *phase 2* | +| *(default)* | Across all of the above, what is the single most useful hygiene action now? | a prioritised `HygieneDecision` worklist | + +``` +pyauto-brain hygiene # audit across modes → prioritised worklist +pyauto-brain hygiene perf # dev-loop timing (staged: phase 3) +pyauto-brain hygiene tidy # git debris (staged: phase 2) +pyauto-brain hygiene noise # CLI noise (staged: phase 2) +pyauto-brain hygiene deps # dependency-cap drift (staged: phase 2) +pyauto-brain hygiene docs # stale API docs (staged: phase 2) +pyauto-brain hygiene --json +``` + +> **Staged.** This is the phase-1 scaffold: the conductor is real, routable and +> bounded, but the modes are stubs — each prints its staged notice. Phase 2 +> absorbs `repo_cleanup` (`tidy`) and `cli_noise_clean` (`noise`) and consults +> `dep_audit` (`deps`) / `audit_docs` (`docs`); phase 3 builds `perf` and any +> PyAutoHeart observation legs. + +## Fundamental principles + +- **Find and prioritise; delegate the fix.** Hygiene surfaces quality debt and + ranks it; the actual repair is ordinary dev-flow work routed to `refactor` + (restructure), `bug` (regression-shaped), or a `feature` — shipped through + `ship_library` / `ship_workspace`. The conductor never reinvents fix machinery. +- **Measurement lives in Heart; hygiene acts.** Heart already observes and tracks + the dev-loop timing signals (`script_timing`, `test_run`); hygiene reads them + and acts, the same split the health conductor follows. New standing signals + (import cost, CLI noise) become Heart *legs*, not a new repo — hygiene has no + persistent artifact lifecycle of its own to house (the reason it is a + conductor with no paired organ). +- **Stdlib / bash only** in the conductor itself — like profiling, it must never + drag the JAX stack into the Brain. + +## Boundaries + +- **vs profiling** — split by *what is measured*. Profiling owns the product's + modelling / compute speed (likelihood on the science grid, GPU tiers, vram, + A100, baselines/pins); hygiene owns the *developer loop's* cost (unit tests, + `PYAUTO_TEST_MODE` / `PYAUTO_SMALL_DATASETS` integration scripts, import time). + Hunting generally-slow functions flagged by integration tests is hygiene's + `perf` mode (moved here from profiling's staged future modes). JAX-adaptation + is shared: hygiene flags a dev-loop function and delegates; a likelihood on the + science grid is profiling's call. +- **vs health** — Heart observes and verdicts; hygiene acts on the observations. + Consults the vitals faculty; never issues a health verdict. +- **vs bug / refactor** — hygiene finds and prioritises debt and *delegates* the + fix to them; they own the repair. +- **vs build** — hygiene is upkeep, not release; it never touches PyAutoBuild. diff --git a/agents/conductors/hygiene/hygiene.sh b/agents/conductors/hygiene/hygiene.sh new file mode 100755 index 0000000..e45fac9 --- /dev/null +++ b/agents/conductors/hygiene/hygiene.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# agents/conductors/hygiene/hygiene.sh — the Hygiene Agent (a PyAutoBrain +# reasoning conductor). The maintenance function — the organism's sense of its +# own upkeep: the code-quality debt that neither proves it works (that is Heart) +# nor measures the speed of modelling (that is profiling). +# +# Owns code-quality upkeep across the organism and emits a HygieneDecision the +# human/session executes, delegating the actual fixes to the dev-flow conductors +# (refactor/bug/feature) via ship_*. It reasons; it never edits source itself, +# and (like profiling) it stays stdlib/bash so it never drags the JAX stack into +# the Brain. +# +# Usage: +# hygiene.sh # audit across modes -> prioritised worklist (default) +# hygiene.sh perf # dev-loop timing: unit tests / integration scripts / imports (phase 3) +# hygiene.sh tidy # git debris: the repo_cleanup sweep (phase 2) +# hygiene.sh noise # CLI noise: the cli_noise_clean audit (phase 2) +# hygiene.sh deps # dependency-cap drift: dep_audit vs PyPI (phase 2) +# hygiene.sh docs # stale API docs: audit_docs over docs/api/*.rst (phase 2) +# hygiene.sh --json # machine-readable HygieneDecision +# +# PHASE 1 SCAFFOLD: the conductor is real, routable and bounded, but the modes +# are staged stubs — each reports where it lands. Per-mode behaviour and +# exit-code semantics arrive with the modes (phases 2-3). + +set -uo pipefail + +HERE="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" +source "$HERE/../../_common.sh" + +MODE_ORDER=(perf tidy noise deps docs) +declare -A MODE_PHASE=( + [perf]="phase 3" [tidy]="phase 2" [noise]="phase 2" [deps]="phase 2" [docs]="phase 2" +) +declare -A MODE_DESC=( + [perf]="dev-loop timing — unit tests / integration-mode scripts / import cost; route to refactor/bug" + [tidy]="git debris — the repo_cleanup sweep" + [noise]="CLI noise — the cli_noise_clean audit" + [deps]="dependency-cap drift — dep_audit vs PyPI" + [docs]="stale API docs — audit_docs over docs/api/*.rst" +) + +mode="default" +json=0 +for arg in "$@"; do + case "$arg" in + perf|tidy|noise|deps|docs) mode="$arg" ;; + default) mode="default" ;; + --json) json=1 ;; + -h|--help|help) mode="help" ;; + *) echo "hygiene: unknown argument '$arg' (modes: ${MODE_ORDER[*]}, --json)" >&2; exit 2 ;; + esac +done + +if [[ "$mode" == "help" ]]; then + # Print the "# Usage:" block from this script's own header (robust to the + # header moving — no hard-coded line numbers). + awk '/^# Usage:/{u=1;next} u{ if($0 ~ /^# /){sub(/^# /," "); print} else exit }' "$HERE/hygiene.sh" + exit 0 +fi + +# --- JSON footing (staged): a HygieneDecision shell the Brain session can read. +if [[ "$json" -eq 1 ]]; then + if [[ "$mode" == "default" ]]; then + printf '{"decision":"HygieneDecision","status":"staged-phase-1","mode":"default","modes":[' + sep="" + for m in "${MODE_ORDER[@]}"; do + printf '%s{"mode":"%s","status":"staged","lands":"%s"}' "$sep" "$m" "${MODE_PHASE[$m]}" + sep="," + done + printf ']}\n' + else + printf '{"decision":"HygieneDecision","status":"staged-phase-1","mode":"%s","lands":"%s"}\n' \ + "$mode" "${MODE_PHASE[$mode]}" + fi + exit 0 +fi + +# --- Human footing. +echo "== HygieneDecision (phase-1 scaffold — modes staged) ==" +echo "The hygiene conductor owns code-quality upkeep: developer-loop cost and repo" +echo "tidiness, distinct from Heart (proof-of-works) and profiling (modelling speed)." +echo "It finds and prioritises debt, then delegates the fix to refactor/bug/feature." +echo +if [[ "$mode" == "default" ]]; then + printf ' %-6s %-9s %s\n' "MODE" "LANDS" "SCOPE" + for m in "${MODE_ORDER[@]}"; do + printf ' %-6s %-9s %s\n' "$m" "${MODE_PHASE[$m]}" "${MODE_DESC[$m]}" + done + echo + echo "Run 'pyauto-brain hygiene ' for a mode once it lands; --json for the" + echo "machine footing. Design: PyAutoMind research/pyautobrain/hygiene_agent_decision.md." +else + echo " mode: $mode" + echo " scope: ${MODE_DESC[$mode]}" + echo " lands: ${MODE_PHASE[$mode]} — not yet implemented (phase-1 scaffold)." +fi +exit 0 diff --git a/agents/conductors/profiling/AGENTS.md b/agents/conductors/profiling/AGENTS.md index b0fae4c..4b14ccd 100644 --- a/agents/conductors/profiling/AGENTS.md +++ b/agents/conductors/profiling/AGENTS.md @@ -49,14 +49,19 @@ pyauto-brain profiling --json `profiling_drift` leg, PyAutoHeart#38); this agent runs the measurement lifecycle. Heart never dispatches campaigns; this agent never issues health verdicts. -- **vs hygiene** — performance measurement ≠ repo hygiene (the hygiene split: - Heart observes noise / Brain `repo_cleanup` mutates). +- **vs hygiene** — split by *what is measured*: profiling owns the product's + modelling / compute speed (likelihood on the science grid, GPU tiers, A100); + the **hygiene conductor** (`agents/conductors/hygiene/`) owns the developer + loop's cost (unit-test time, `PYAUTO_TEST_MODE` / `PYAUTO_SMALL_DATASETS` + scripts, import time) and repo tidiness. Hunting generally-slow functions + flagged by integration tests is hygiene's `perf` mode, not profiling's. - **vs build** — campaigns are not releases; `profile.yml`'s on-release runs stay CI/Build territory. ## Future modes (staged in the founding prompt) -JAX compilation-time profiling of likelihood functions; hunting -generally-slow functions flagged by integration tests. A read-only profiling -*faculty* (opine on regressions / optimization targets) splits out only on -demonstrated consult demand. +JAX compilation-time profiling of likelihood functions. (Hunting +generally-slow functions flagged by integration tests moved to the hygiene +conductor's `perf` mode — that is developer-loop cost, not modelling speed.) +A read-only profiling *faculty* (opine on regressions / optimization targets) +splits out only on demonstrated consult demand. diff --git a/bin/pyauto-brain b/bin/pyauto-brain index 0ceec7d..5c9d222 100755 --- a/bin/pyauto-brain +++ b/bin/pyauto-brain @@ -15,6 +15,7 @@ # pyauto-brain feature [args] (conductor) reason over PyAutoMind tasks, plan growth # pyauto-brain refactor [args] (conductor) plan behaviour-preserving restructuring (default-safe) # pyauto-brain profiling [args] (conductor) campaign/ingest/triage plans over the profiling workspace +# pyauto-brain hygiene [args] (conductor) code-quality upkeep: perf/tidy/noise/deps/docs — HygieneDecision (modes staged) # pyauto-brain clone [args] (conductor) v0: analyze a library+workspace against the reference assistant — CloneDecision (writes nothing) # pyauto-brain build [args] (conductor) coordinate execution: consult vitals, run Build # pyauto-brain release [args] (conductor) release door → Build Agent release mode (gate + pre_build) @@ -49,6 +50,7 @@ declare -A AGENT_SCRIPT=( [bug]="$CONDUCTORS_DIR/bug/bug.sh" [refactor]="$CONDUCTORS_DIR/refactor/refactor.sh" [profiling]="$CONDUCTORS_DIR/profiling/profiling.sh" + [hygiene]="$CONDUCTORS_DIR/hygiene/hygiene.sh" [clone]="$CONDUCTORS_DIR/clone/clone.sh" [build]="$CONDUCTORS_DIR/build/build.sh" [release]="$CONDUCTORS_DIR/release/release.sh" @@ -64,6 +66,7 @@ declare -A AGENT_DESC=( [bug]="The immune system: classify a bug/regression/Heart finding, locate the fix, plan the repair" [refactor]="The renewal function: plan behaviour-preserving restructuring — RefactorDecision; default-safe under --auto" [profiling]="The proprioceptive function — the organism's sense of its own effort: campaign/ingest/triage plans over the autolens_profiling workspace — ProfilingDecision" + [hygiene]="The maintenance function — the organism's sense of its own upkeep: code-quality debt (dev-loop cost + tidiness), delegating fixes — HygieneDecision" [clone]="The Mitosis Agent (v0: decision only): partition the reference assistant, analyze the domain, emit the CloneDecision — never writes" [build]="Coordinate execution: consult the vitals faculty, then delegate to PyAutoBuild" [release]="Release door → the Build Agent release mode (single gate); 'release rehearse'/'release validate' drive release validation; 'release nightly' is the scheduled-nightly driver" @@ -75,7 +78,7 @@ declare -A AGENT_DESC=( ) # Conductors are the front doors a human drives; faculties are consulted (and # runnable read-only). Both are dispatchable, but the menu groups them by tier. -CONDUCTOR_ORDER=(intake feature bug refactor profiling clone build release health) +CONDUCTOR_ORDER=(intake feature bug refactor profiling hygiene clone build release health) FACULTY_ORDER=(vitals review memory samplers) AGENT_ORDER=("${CONDUCTOR_ORDER[@]}" "${FACULTY_ORDER[@]}") diff --git a/skills/COMMANDS.md b/skills/COMMANDS.md index 2d8436b..152b789 100644 --- a/skills/COMMANDS.md +++ b/skills/COMMANDS.md @@ -1,7 +1,7 @@ # PyAutoBrain command surface — shared reference The short verb commands (`/intake`, `/feature`, `/build`, `/health`, `/bug`, -`/refactor`, `/profiling`, `/docs`, `/research`, `/route`, `/brain`) are a thin, human-friendly **veneer** +`/refactor`, `/profiling`, `/hygiene`, `/docs`, `/research`, `/route`, `/brain`) are a thin, human-friendly **veneer** over the PyAutoBrain router (`bin/pyauto-brain`). This file is the shared context every command file points at, so each command body stays a few lines long. @@ -34,6 +34,7 @@ readiness gate, or execution — those belong to the organs. | `/bug` | Bug Agent | `bin/pyauto-brain bug` → `start_dev` → `ship_*` (health mode → vitals + Heart issues) | | `/refactor` | Refactor Agent | `bin/pyauto-brain refactor` → `start_dev [--auto]` → `ship_*` (behaviour-preserving; default-safe) | | `/profiling` | Profiling Agent | `bin/pyauto-brain profiling` → campaign/ingest/triage plans over the autolens_profiling workspace | +| `/hygiene` | Hygiene Agent | `bin/pyauto-brain hygiene` → perf/tidy/noise/deps/docs upkeep plans; delegates fixes to refactor/bug/feature (modes staged) | | `/build` | Build Agent | `bin/pyauto-brain build` → vitals faculty → Heart → PyAutoBuild | | `/health` | Health Agent | `bin/pyauto-brain health` loop → vitals faculty → Heart → GREEN | diff --git a/skills/hygiene/SKILL.md b/skills/hygiene/SKILL.md new file mode 100644 index 0000000..db660ce --- /dev/null +++ b/skills/hygiene/SKILL.md @@ -0,0 +1,11 @@ +--- +name: hygiene +description: Audit and prioritise the organism's code-quality upkeep — slow tests/scripts/imports, CLI noise, dependency-cap drift, stale API docs, git debris — through the PyAutoBrain Hygiene Agent, which reasons and delegates fixes without editing source itself. +--- + +# Hygiene + +Follow [`hygiene.md`](hygiene.md) exactly. The Hygiene Agent finds and +prioritises code-quality debt (developer-loop cost + repo tidiness) and +delegates the fix to the dev-flow conductors — it never edits source directly. +Keep measurement in Heart and modelling-speed work in the Profiling Agent. diff --git a/skills/hygiene/hygiene.md b/skills/hygiene/hygiene.md new file mode 100644 index 0000000..20d73d0 --- /dev/null +++ b/skills/hygiene/hygiene.md @@ -0,0 +1,26 @@ +# /hygiene — keep the organism's code clean (via the Brain Hygiene Agent) + +Route code-quality upkeep — the debt that neither proves the organism works +(that is `/health`) nor measures modelling speed (that is `/profiling`) — +through PyAutoBrain's **Hygiene Agent**. You never name the Brain; this command +is the door. + +Shared routing context: `PyAutoBrain/skills/COMMANDS.md`. + +## Do + +1. Run `bin/pyauto-brain hygiene [perf | tidy | noise | deps | docs]` (no arg = + audit across modes → a prioritised worklist). This is a **dry run** — it emits + a `HygieneDecision`. Nothing is executed. +2. Execute the emitted plan through the normal dev workflow: hygiene *finds and + prioritises* quality debt and *delegates the fix* — restructuring to + `/refactor`, regressions to `/bug`, larger changes to `/feature` — shipped via + `ship_library` / `ship_workspace`. + +The Hygiene Agent **reasons; it never edits source.** Measurement lives in Heart +(the `script_timing` / `test_run` signals); hygiene acts on it. + +> **Staged (phase 1):** the conductor is real and bounded but its modes are +> stubs. `tidy` / `noise` / `deps` / `docs` land in phase 2 (absorbing +> `repo_cleanup` + `cli_noise_clean`, consulting `dep_audit` + `audit_docs`); +> `perf` in phase 3. diff --git a/skills/repo_cleanup/SKILL.md b/skills/repo_cleanup/SKILL.md index 95d2b52..8d6791a 100644 --- a/skills/repo_cleanup/SKILL.md +++ b/skills/repo_cleanup/SKILL.md @@ -22,11 +22,13 @@ Cleanup is not release work, so it never touches PyAutoBuild. It reads the PyAutoMind registry (`active.md`) to know what's claimed. Organ boundary + execution-environment model: [`../WORKFLOW.md`](../WORKFLOW.md). -> **Future Cleanup Agent.** This skill is the inline form; the natural home for -> the reasoning is a dedicated PyAutoBrain **Cleanup Agent** (alongside the -> Feature / Build / Health agents) that generalises hygiene across the organism. -> Until it exists, run the reasoning here per this file and record the agent as a -> follow-up — the same pattern WORKFLOW.md describes for the other skills. +> **The hygiene conductor.** This skill is the inline form; its reasoning is now +> owned by the PyAutoBrain **Hygiene Agent** (`agents/conductors/hygiene/`), +> which generalises hygiene across the organism and absorbs this sweep as its +> `tidy` mode. That conductor's phase-1 scaffold exists and documents the +> boundary; the `tidy` mode itself lands in phase 2. Until it does, run the +> reasoning here per this file — this remains the working form for git-debris +> cleanup. **Distinct from:** `/health worktrees` (Heart read-only diagnostic — consulted here, but this also mutates); post-merge cleanup in `CLAUDE.md` (once per shipped task —