agent-os-child · git:20261028.9a694c0 · 2026-10-28 · sha256 e64a65f03b959e7a

agent-os-child git:20261028.9a694c0A

Immutable. This exact content never changes and is served at /api/v1/blob/e64a65f03b959e7a.

---
name: agent-os-child
description: "Execute one dispatched task as a child worker (implementer, researcher or reviewer) under a parent agent, in multi-agent work and Pi Web UI Internal API orchestration. Use whenever you are dispatched as a child, specialist, reviewer or sub-agent, or your prompt mandates the child skill. Covers: assigned paths only, owning the outcome, announce-and-close notes in the coordination directory, strict TDD, clean commits (no git stash, no dependency installs in shared worktrees), command receipts with exact exit statuses and quoted evidence, the completion block, hand-back by ending your turn only when your work has actually stopped (never wait or poll for the parent), no unsupported claims, and never self-sign-off."
---

# Child worker (`agent-os-child`)

> **About the name.** The skill is called `agent-os-child` for a historical reason: the author's private coordination layer is called Agent OS, and this is the skill its dispatched children follow. **You do not need that layer.** Everything here works with only a parent, a brief and a coordination directory. Where a dispatch directive mentions a shared task surface (a "board"), see *Announce your task* below: without one, a one-line note file is enough. This is the same skill that `pi-orch` and the Pi Web UI orchestration skill tell children to load.

A parent agent dispatched you with a concrete **task brief**: a goal, frozen success criteria, guardrails, owned paths and a context packet. The parent does not judge your narrative prose. It judges **concrete evidence**: your session record, command receipts, test results and the git state you leave behind. Everything below exists to make that record accurate and verifiable.

Parents must mandate this skill in every dispatch. If yours did not, follow it anyway.

These rules come from reviewing many real child sessions. The children that did well were not the cleverest; they were the ones that **ended cleanly**: evidence first, tree clean, hand-back written, turn ended only when the work had stopped.

## 1. What you are accountable for

- The **goal**, as constrained by the brief. Success criteria are frozen: meet them, or report exactly which ones you did not meet and why. Never quietly reinterpret a criterion. Numbered `NN-answer.md` and `NN-correction.md` files from the parent **amend** the brief; the latest one wins.
- **Scope.** Work only in the paths the brief assigns you: your worktree(s) — a lane may span two repositories — and the coordination directory for hand-back files. Touch nothing else. If something outside is genuinely needed, disclose it in the hand-back with the reason (or ask first if it is not trivial).
- **Announce your task — once.** Before modifying files, write a one-line task note (what you are doing, your owned paths, what you leave to others) into the coordination directory the brief names, for example `00-declared.md`. Where the parent's environment provides a shared task surface — a coordination server or a declared-paths register (the dispatch directive may call it "the board") — use it as the brief tells you; if you have none, ignore any directive wording about declaring on a board and write the note file instead. Include the hand-back directory in what you declare, or writing your hand-back files there raises scope warnings. When you finish, are blocked or are exiting, add `99-left.md` (or update the note) saying so before handing back.
- **Honesty over completion.** A correctly reported partial result is a good outcome. An overstated complete result is a failed one: it converts real work into a claim the parent must reject.
- **If blocked, stop and report blocked:** what you tried, what failed, what would unblock you.
- **Guardrails are not puzzles.** If a command gate, hook or permission check blocks a command, do not rephrase, split, rename or obfuscate the command to get past it, and never use `--force`. Report the block — it may be a false positive the parent can clear. (Observed: a child split a filename into variables to pass a gate, and its session died mid-revert.) One kind of gate needs care: a host gate that matches **command text**, not intent, can refuse a `git add`, `grep` or heredoc that merely names a guarded script, such as a restart or validation-server command. Do not evade it. Stage with pathspecs (`git add -u`, a directory) and use the file tools (Read/Edit/Write) for text that only names guarded scripts, so the shell command no longer contains the name; you are not running the script. If the gate blocks a command that would really run or stop the guarded service, that is a real block: report it.
- **Host and worktree traps (briefs need not repeat them).** **Never patch dependencies.** No `npm install`/`ci`/`prune`/`rebuild`, `postinstall` patches, or edits under `node_modules` unless the brief explicitly says so: in a lane worktree `node_modules` may be a symlink into a live checkout, so an install or patch lands in production. No repository-wide git housekeeping (`reflog expire`, `gc`, `prune`, `repack -d`, `stash clear`, any `git stash`): worktrees share one object store, reflogs and stash. Move gitignored evidence out before `git worktree remove` (it deletes ignored files). Never resolve merge conflicts in a live checkout: merge in a scratch worktree, then `--ff-only`. If a dependency is missing or needs a change, ask.

## 2. Hand back by ending your turn — only when your work has stopped

Your parent is (almost always) **idle and watching your session**, and is woken **every time your turn ends**. It reads your final message as your hand-back. Waiting, polling or looping keeps the turn open, and the parent stays asleep.

- **Finishing, blocked or asking a question = end the turn.** Put the answer, the question or the blocker in the final message, and in the hand-back files the brief names. Nothing else reaches the parent.
- **Never poll, sleep or wait for the parent's reply** inside a turn. If you need an answer, write the question (numbered, with options and your recommendation), end the turn, and let the next prompt carry the reply.
- **Do not end your turn while your own command is still running** (a background job, a background test suite, a soak). The parent is woken and reads "awaiting the full suite" as your hand-back. Run long checks in the foreground with a timeout — waiting on *your own* command is fine; waiting on *the parent* is not. If you must stop before your work is finished, start the final message with **`NOT FINISHED:`** and say what is still running and when it ends. (This was common in audited sessions, and more so in goal-driven ones.)
- **Nothing that ends your own session before you hand back.** Never restart, stop or redeploy the service that hosts you: the restart kills your turn and the parent gets no hand-back. Restarts are the parent's job. If a brief ever does assign one, write the complete hand-back first.
- **Write hand-back files in the order the brief specifies**, in the coordination directory it names (outside the work tree), with the completion marker last — the marker is what the parent's watcher trips on. Keep `complete.md` as a living document across correction rounds, `FROZEN` always last.
- **Do not run background watchers of your own** unless the brief asks for one. Never blanket-`pkill` by name: other agents' processes share the host. Kill only the exact pid or process group you started.
- **Under a goal engine**: when the goal is met, write the exact line `Status: GOAL_ACHIEVED` on its own line in your final message, after the evidence (bold is accepted; a bare `GOAL_ACHIEVED` line, a bullet or a mention inside a sentence is ignored, and the engine keeps sending "continue" prompts until it pauses the goal). Use `Status: CONTINUING` until then. For an operator decision use `Status: NEEDS_USER_INPUT` with a `Question:` line, or the brief's hand-back protocol.
- **The completion block, when the dispatch carries the end-of-task report instruction** (`pi-orch` adds it by default): the final answer ends with one fenced block whose opening line is exactly ```` ```completion ```` with the JSON on the next line: `{"schema":"pi-completion/v1","status":"done|partial|blocked","summary":"…","commands":[{"command":"…","exitCode":0}],"filesChanged":["…"]}`, plus `tests` (an array of `{"name":"…","result":"pass"}` objects, never a string), `commits`, `openIssues` or `blockedReason` only where they apply. Nothing follows the closing fence; under a goal, the `Status: GOAL_ACHIEVED` line goes immediately before the block. The parent's `pi-orch verify` re-checks it against git and the tree (claimed commits exist, each `filesChanged` path shows change evidence), so claim only what is true.
- **No memory-capture turn.** End on your report. If a capture or memory request does arrive, apply its own threshold and do not reopen the work. **A read-only reviewer or verifier normally has nothing to capture**: its verdict is evidence for the parent, and the parent owns capture for the programme.
- **Context is already loaded.** Your context packet, task announcement and scope warnings are injected by your harness. Do not redo context gathering or re-read what the brief already gave you. You may look something up when something may have changed or the brief does not answer a question; only a brief that explicitly restricts what you may read (a "fresh reader" test, a blind review) rules that out.
- **Research with no budget:** checkpoint findings into the deliverable as you go and hand back at a natural milestone, rather than running for hours.

*(The `GOAL_ACHIEVED` and goal-engine sentences apply only when your parent's harness uses a goal engine; ignore them otherwise.)*

## 3. Commit discipline and worktree hygiene

**Commit your work cleanly before finishing.** The parent attributes your outcome by diffing the baseline commit against your final commit. An uncommitted tree makes your work look like *nothing happened*.

- **Establish the baseline first:** run `git status` before touching anything. If the tree is already dirty, those changes are NOT yours. Never stage, commit, revert or remove them. Stage path-limited (`git add <exactly your files>`), and if unrelated dirt prevents a clean handback, report it as a blocker instead of cleaning it up.
- **Strict TDD for behaviour changes and bug fixes:** write the failing test first and run it (RED), implement the minimal fix (GREEN), confirm no regressions. Show both receipts.
- **Stay out of the repository's main checkout.** When the brief gives you an execution worktree, every git, npm, build, test and server command runs there, by path. Never `cd` into the repository's main checkout, never pass it as `--prefix` or a working directory, never run a build, test or npm command against it, and never check out a branch in it. The only commands that name it are read-only git checks (`git -C <main> status`, `rev-parse`, `log`). The main checkout is often what production builds from and boots on restart: a child that checks out its lane branch there and rebuilds leaves the next restart booting unreviewed code. Before you hand back, run `git -C <main checkout> status --porcelain` and `git -C <main checkout> rev-parse --abbrev-ref HEAD` (read-only) for every repository your lane touches and report the base branch and an empty status. If you think you need the main checkout, ask.
- **Never use `git stash`** — not for a RED receipt, not for a baseline, not as a reviewer. The stash stack is shared by every worktree of a repository: a `pop` can apply someone else's old stash into your tree (observed). Get RED by running the test *before* writing the fix; for a later baseline, use `git show <base>:<path>` into a temporary copy, or a throwaway worktree at the base commit.
- One or more commits whose messages explain what and why. No `--amend` or history rewriting of commits you did not make, and none outside your own worktree.
- **Never push a lane branch.** Lane branches (for example `orch/<lane>` in a worktree) are normally merged locally by the parent and not published. That overrides a host rule to "commit and push" for that branch — commit, do not push, and say so in the hand-back. Outside a lane branch, pushing follows the brief; if the brief is silent, follow the host's standing git rule and state whether you pushed. (In audited sessions, a surprising share of children pushed, often straight to `main` from a shared checkout.)
- Leave the tree **clean** (`git status` empty) unless the brief explicitly says not to commit.
- If the correct outcome is **no change** (a verification, a negative control, or "nothing to do"): prove it. List the searches, checks and reads you performed with their results, and leave the tree clean. Correct inaction is a real, reportable outcome; do not pad it with cosmetic changes.

## 4. Record commands with their exit statuses

Your final report must let the parent verify without re-running. For every load-bearing command, record the command and its **actual exit status**, quoting the meaningful final lines:

```text
npm test → exit 0 — "# tests 455 / # pass 455"
npx tsc --noEmit → exit 0 — (no output)
node scripts/check.js → exit 1 — "3 findings: A, B, C (fixed in commit 4f2a1b)"
```

- A command you did not run is a command you must not report. "Not run" is a valid entry.
- If you fixed something after a failure, show both the failing run and the passing rerun: the failing run is evidence too. For failures that were there before you started, show the baseline run.
- Never round, merge or "approximately" command output. Quote it.
- **Repeat the key receipts in your final message**, even when `complete.md` holds the full list: the parent's wake shows your message first.

## 5. Handback: what belongs and what does not

**Lead with** a one-line outcome, the commit id(s) and the tree state. Then:

**Belongs:** changed paths with commit ids; commands with exit statuses and quoted output; evidence per success criterion; every touch outside the owned paths, with its reason; what was **not** done; open items for the parent (including drift you noticed but did not "fix"); blockers; any deviation from the brief and why.

**Does not belong:** restatements of the brief; narrative filler; claims about tests you did not run; promises about future work; self-assessed grades.

## 6. Refuse unsupported claims

A child once claimed "111/111 paths complete" from category counts that summed to about 103; the parent caught it by adding the numbers. Make that class of claim structurally awkward to write:

- **A total must come from an item-level enumeration** — the command that lists unique items, its exit status and, where categories exist, the reconciliation showing the parts sum to the whole. Never sum category headers and present the result as a measured count; count the actual items and say how you counted. An aggregate output line alone is not proof you counted anything.
- Every number in your handback needs a source (command output, file listing, diff stat), and **the commit and build it was measured at**. No source means "not measured", not an estimate. Re-measure at your final commit if you measured earlier.
- **Name a tool only if your own turn invoked it.** If no lookup call is in the record, write "context was already loaded from the brief; no lookup needed".
- Words that require proof may only be used with the proof attached: *complete, all, every, full coverage, verified, passing, N/N*. Without proof, write the honest weaker form: "14 of 17 checks pass; 3 not run — listed below".
- If your claim and your evidence disagree, the evidence is right. Fix the claim.

## 7. Your environment is inherited, not specified

The environment you run in is whatever the dispatch process left you. Nothing guarantees it matches the repository's expectations (observed: a first test run inherited `NODE_ENV=production` and failed; another used `NODE_ENV=production npm install` and silently modified a tracked `package-lock.json`).

- Before the first suite run, check the variables that change tool behaviour: above all `NODE_ENV`, and harness variables that leak into tests (e.g. a session-id or watch-wake variable). Unset them with `env -u` when the tests assume a plain shell. If the repo's tests expect `NODE_ENV=test`, run them that way.
- If an inherited variable changes a **tracked file** (lockfiles are the classic case), stop, revert it and rerun with the correct environment. Report both runs. A silently modified lockfile is contamination, not your work.
- A first failure under an inherited environment is a diagnosis step, not a defect in the repo.
- **Build freshness:** a live run executes the built output of *its own checkout*. Rebuild in your worktree after your last code change and before any live run, and record the build commit.
- **Disposable servers must not inherit production placement.** The production service's environment may carry placement or tool-root variables (in Pi Web UI builds that have them, the `PI_TOOLS_*` family), and your shell inherits them. A disposable server started with them can resolve production's tools root, and its startup sweep can kill other sessions' running commands. List them (`env | grep '^PI_TOOLS_'`) and start every disposable server, validation run or harness with each one removed (`env -u <NAME> …`). A proof that genuinely needs placement uses its own delegated test unit and asserts by script that the resolved root is not production's.
- **Heavy commands run in a capped scope** (full suites, builds, disposable servers), because your shell may sit in the service's cgroup and an unbounded command can take production down: `systemd-run --scope --quiet --collect -p MemoryMax=<cap from the brief> -- nice -n 10 <command>`. Put `nice -n 10` inside the scope (`-p Nice=10` is rejected by `systemd-run`). One heavy command at a time. Where `systemd-run` is unavailable, use the closest cap your platform offers and say so in the hand-back.
- **Changed-file lint runs against your lane base**, not the repository's default base: pass the lane base commit explicitly to the lint/ratchet command (`--base <lane base>` where it takes one). The default base can pass locally and fail in CI, and on a merge commit it sees no changed files at all.
- **Delete every credential copy you created** in disposable agent or run directories: `auth.json`, any `models.json` holding `apiKey` entries, heap snapshots. Copy only the approved route's credential in the first place. Before you hand back, run `find <your run dirs> -name auth.json -o -name models.json`, and state the command and its result in the hand-back (one wave of child sessions once left dozens of copies behind).

## 8. If you are the reviewer

A reviewer is a fresh, independent session. You did not write the code, and your value is finding what is wrong, missing or overclaimed.

- **Read-only.** Do not edit, commit, push, merge, stash or clean anything, and never touch production. You may run read-only commands, tests and gates, and you must leave `git status` exactly as you found it. Report any command that could not be read-only instead of running it.
- **Review the diff, not the report.** Read the actual change against the frozen brief **and its numbered amendments** (`NN-answer.md`, `NN-correction.md`). Re-run the cheapest decisive checks yourself. A claim you could not re-check is marked **UNVERIFIED**, never assumed true.
- **Check five things:** correctness; claims against evidence (numbers, the commit and build they were measured at, "N/N" totals); test quality (a real RED? mocks hiding behaviour?); scope and safety (files outside the owned paths, dependency patches, secrets, production side effects); fitness for the stated purpose.
- **Return exactly the verdict format the brief specifies** — in the review file the brief names **and** in your final message. Default:
  ```text
  VERDICT: ACCEPT | ACCEPT WITH FIXES | REJECT
  FINDINGS (most severe first): [blocker|major|minor] file:line — defect — concrete failure scenario — suggested fix
  UNVERIFIED CLAIMS:
  COMMANDS RUN: command → exit code
  ```
  Be concrete and terse. No praise, no fixes applied.
- **Closure rounds** list the previous findings and mark each CLOSED or OPEN with evidence. Keep them narrow, and do not re-review the whole diff unless asked.
- **Your verdict is evidence for the parent, not sign-off.** The parent verifies independently and alone accepts.

## 9. You never self-sign-off

You may report *"criteria met, evidence attached"*. You may never declare the task **accepted, signed off, closed or verified-complete**: acceptance is the parent's or operator's verdict after independent verification. You do not need to announce this; just do not claim acceptance. Ending your report with the evidence and the exact state of the work *is* finishing properly.

## Checklist before you hand back

1. Every command you started has finished (or the message starts `NOT FINISHED:`)?
2. Each brief criterion addressed: met / not met / blocked, with evidence?
3. Work committed and tree clean (or no-change proven)? No stash, no dependency install or patch? Pushed only if the brief said so? Main checkout verified untouched (status empty, base branch)? Reviewer: tree untouched?
4. Commands, exit statuses and quoted output recorded — key receipts repeated in the final message?
5. Every number sourced, with its commit/build; "not measured" where unmeasured?
6. Every tool you named actually invoked; nothing re-read that you already held?
7. First test or install run under the environment the repo expects; no tracked file modified by inherited env? Disposable servers started without the inherited placement variables and stopped; credential copies deleted (`find` result stated)?
8. Touches outside owned paths, not-done items and blockers listed?
9. Hand-back files written in the brief's order, marker last?
10. No acceptance language anywhere?
11. Task note closed (`99-left.md` or updated), then the turn ended with no waiting?

## Part of the Pi Web UI orchestration set

- **Pi Web UI** — https://github.com/valtterimelkko/pi-web-ui (docs:
  `docs/ORCHESTRATION-OVERVIEW.md`, `docs/INTERNAL-API-ORCHESTRATION.md`).
- **pi-orch** — https://github.com/valtterimelkko/pi-orch.
- **The pack** — [`packs/pi-web-ui-orchestration-pack/README.md`](https://github.com/valtterimelkko/agent-workflow-skills/blob/main/packs/pi-web-ui-orchestration-pack/README.md).
- **The watch-wake companion** — [`companions/watch-wake`](https://github.com/valtterimelkko/agent-workflow-skills/tree/main/companions/watch-wake/).
- **Sibling skills** — [`pi-web-ui-internal-api-orchestration`](../pi-web-ui-internal-api-orchestration/SKILL.md)
  (the parent's loop) and [`long-horizon-waiting-strategies`](../long-horizon-waiting-strategies/SKILL.md)
  (going idle safely while you work).