From e4754cde4070799c04edf02ffea12f20d6237419 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 29 Aug 2026 22:27:57 -0700 Subject: [PATCH] docs(goal-skills): resource hygiene for autonomous fan-out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parallel-work guidance cleans up its OWN orchestration artifacts (worktrees/branches/tmux) but is blind to resources the work units CREATE (containers, VMs, background servers, environments), and per-unit bounds (turns/wall-clock) do not bound cumulative resource accumulation. Close that gap across the three surfaces that shape autonomous runs. goal-batch SKILL.md: - Phase 1: clarify "no lane cap" governs LANES, not the resources lanes create; those need a cumulative batch-wide ceiling tracked in an orchestrator-owned ledger, since each lane sees only its own usage. - Phase 3: each lane's goal must enumerate non-git resources it provisions and include teardown (or explicit handoff) in its own DONE criteria; add resources[] to the DONE.json field list. - Phase 7: orchestrator verifies teardown as part of landing and extends the orphan sweep beyond tmux (docker ps / incus list / process list). goalify SKILL.md: - Add lint check L7: a goal that provisions infrastructure must include teardown-complete (or explicit handoff) in DONE; resources left running and unowned is abandoned, not finished. docs/GOAL_COMMAND.md: - In "What the cap does NOT bound": turn and wall-clock caps do not bound resources spawned by the work; unattended runs that provision anything need an external resource ledger and ceiling. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- .../data/skills/goal-batch/SKILL.md | 33 ++++++++++++++++--- .../data/skills/goalify/SKILL.md | 10 ++++++ docs/GOAL_COMMAND.md | 9 +++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/amplifier_app_cli/data/skills/goal-batch/SKILL.md b/amplifier_app_cli/data/skills/goal-batch/SKILL.md index e895e515..f352d2d7 100644 --- a/amplifier_app_cli/data/skills/goal-batch/SKILL.md +++ b/amplifier_app_cli/data/skills/goal-batch/SKILL.md @@ -86,7 +86,15 @@ Then do the work that decides whether this batch lands: - **Pin the base SHA** every lane branches from, and **record current test baselines** so "no regressions" is a number. -No lane cap. Width is bounded by the work. +No lane cap. Width is bounded by the work. **That no-cap rule governs *lanes*, +not the resources lanes create.** Anything a lane stands up that outlives a +process — containers, VMs, background servers, environments — does need a +cumulative ceiling for the whole batch, because no single lane can enforce one: +each lane sees only its own usage, never the total. The orchestrator is the only +actor with the cross-lane view, so it keeps a running resource ledger for the +batch — beside the manifest and goal files — and holds combined usage under a +declared ceiling. Unbounded lanes are fine; unbounded resource accumulation +across them is what exhausts the host. ## Phase 2 — Review and go @@ -170,6 +178,14 @@ Every goal carries: - **Host capability limits, stated plainly.** A lane that authors what this box cannot compile ships code that breaks on someone else's first build. Live shared services are read-only evidence to a lane; tests use fixtures. +- **Non-git resources the lane provisions, enumerated with their teardown.** + Anything a lane stands up that outlives a process — containers, VMs, + background servers, external environments — must be named in the goal, and + tearing each one down (or an explicit, named handoff to an owner) must be part + of the lane's own DONE criteria. File ownership bounds the *files* a lane + touches and says nothing about the *resources* it starts; a lane that exits + with resources still running has not finished, and Phase 7 cannot verify + teardown for what a lane never declared. - **The time bound**, and that exceeding it is a terminal `BUDGET` state, not a reason to rush the work or skip the commit. - **Add `DONE.json` to the repo's `.gitignore` before writing it.** Four of five @@ -178,7 +194,9 @@ Every goal carries: - **Write `DONE.json` in the worktree root as your final act** — the terminal marker. Without it, an exited session is indistinguishable from a killed one. Fields: `lane, session_id, verdict, branch, head, pushed, items[], - residuals[], pending_human[], suite`. Shape in `examples/`. + residuals[], pending_human[], resources[], suite`. `resources[]` names every + non-git resource the lane provisioned and whether it was torn down or handed + off, so Phase 7 can verify teardown instead of guessing. Shape in `examples/`. **`verdict` is exactly one of `COMPLETE` / `BLOCKED` / `PARTIAL`**, and `session_id` is this lane's own — two lanes in one batch wrote `"PASS"` and `"success"`, which no parser reads the same way, and a `DONE.json` without a @@ -366,8 +384,15 @@ honestly from a suite run that predated their own last file. 4. On red: fix at the cause, or revert that merge and report. Never weaken a test to pass — the gate is doing its job. 5. Push main. Then per lane: `git worktree remove`, delete branch local and - remote, `tmux kill-session`. Sweep for orphans: `tmux ls -F '#{session_name}' - | grep '^gb____'` must come back empty. + remote, `tmux kill-session`. Sweep for orphaned sessions: `tmux ls -F + '#{session_name}' | grep '^gb____'` must come back empty. **Then verify + teardown of everything the lanes provisioned** — teardown is part of landing, + not an afterthought. Read each lane's reported `resources[]`, confirm every + entry is actually gone, and extend the orphan sweep beyond tmux to whatever + the lanes could have started: check `docker ps`, `incus list`, or the process + list for survivors tied to this batch. Reconcile the total against the batch + resource ledger from Phase 1 — it must read zero still-running. A batch is not + landed while it is still holding resources open. 6. Report verdict-first: per-lane table (shipped, SHAs, suite results), what verification caught that lanes did not self-report, residuals, anything `PENDING-HUMAN`, the unowned-files list from Phase 1, and new baselines. diff --git a/amplifier_app_cli/data/skills/goalify/SKILL.md b/amplifier_app_cli/data/skills/goalify/SKILL.md index d08f792c..1b602ee6 100644 --- a/amplifier_app_cli/data/skills/goalify/SKILL.md +++ b/amplifier_app_cli/data/skills/goalify/SKILL.md @@ -125,6 +125,15 @@ scan for keywords in isolation and stop at the first clean-looking match. exists is not sufficient — confirm nothing else in the document is stricter than it. +- **L7 — Infrastructure provisioned without teardown or handoff in DONE.** + This one is not about whether the condition can terminate, but about the + state it leaves behind when it does. If the goal stands up anything that + outlives the run — containers, VMs, background servers, external + environments — the condition must make *teardown-complete*, or an explicit + named handoff to an owner, part of DONE. A goal whose satisfied state leaves + resources running and unowned is abandoned, not finished. Does not fire for + goals that provision nothing. + ### WARNINGS — advisory, do not block presentation - **L6 — Missing disjunctive exit.** The document should state achievement @@ -193,6 +202,7 @@ Wrote: `.amplifier/goals/.md` | L3 | no known pattern detected | ... | | L4 | no known pattern detected | ... | | L5 | no known pattern detected | ... | +| L7 | no known pattern detected | ... | | L6, W1–W4 | (list only the ones that fired) | ... | A clean table means no *known* failure pattern was detected — not that the diff --git a/docs/GOAL_COMMAND.md b/docs/GOAL_COMMAND.md index dc4ea030..cef874b0 100644 --- a/docs/GOAL_COMMAND.md +++ b/docs/GOAL_COMMAND.md @@ -241,6 +241,15 @@ reached because no turn ever ended. early. It cannot catch one that never stops. For unattended runs, bound the process externally (a wall-clock `timeout`, a CI job limit) in addition to `--max-turns`. +Neither bound touches **the resources the work spawns**, either. `--max-turns` counts +continuations and a wall-clock `timeout` counts seconds; inside one turn, inside one second +of that budget, a run can start containers, VMs, background servers, or environments that +outlive it entirely. The caps fence *the agent's activity*, never *what that activity leaves +running* — a run can exit cleanly at turn 5 having stood up a hundred containers that nothing +ever tears down, and no turn or wall-clock cap will reclaim them. For any unattended run that +provisions anything, keep an **external resource ledger with a cumulative ceiling**, and make +teardown (or an explicit handoff) part of the run itself — the caps will not do it for you. + --- ## What the evaluator can and cannot see