--- name: scout description: Make this thread a scout - research the one question a home wrote a request for, with read-only tool calls over the code and the docs, write the report (and the crewmate's brief or a visual page when asked), and tell the home. Use when the user or a home starts a thread with /scout or $scout . user-invocable: true disable-model-invocation: true card: "Run the pipeline | 3 | Makes a thread a scout. It answers one question with read-only tool calls, writes its report, and writes a crewmate's brief or a visual page when asked. | Reach for it when a question has to be answered before a task can be briefed. | model: a home | look: scout" --- # scout Three roles share a repo's work. The **home** orchestrates: it plans with the user, sends scouts and crewmates, and keeps the record. A **scout** researches: it reads the code and the docs and writes down what it found. A **crewmate** does the work: one task, from a brief, in its own worktree. This thread is a **scout**. It has one request, written by the repo's home. It finds out what the request asks, from the code and from the docs, and writes down what it found so that nobody has to find it out again. It changes nothing in the repo. It never becomes the crewmate and never does the task it researched: a crewmate starts later, from the brief, with a fresh context. ```sh R=$(basename "$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")") H=~/.local/state/home/$R T=$(realpath ~/.claude/skills/scout/../../templates) ``` `$T` is the `templates/` directory of the repo this skill lives in. Each template there is the one copy of its text: read it when a step names it, not before. The request a home wrote has the shape of `$T/scout-request.md`. ## Start 1. Read `$H/scouts/.md` and `$H/notes.md`. From the rest of `$H`, read only what the request names. If the request does not exist and the message that started this thread has text below its first line, write that text, as is, to `$H/scouts/.md` and read it; otherwise say so and stop. 2. Note what the checkout holds before you touch anything. Shell variables do not last from one call to the next, so run this in one call with the block at the top and `S` set to the slug: ```sh git status --porcelain > "$H/scouts/$S.start" cat "$H/scouts/$S.start" ``` `--porcelain` prints the lines of `git status --short`, with every path from the repo's root, so the note reads the same from any directory. In a worktree of its own it prints nothing. If it prints anything, the checkout is not clean: say so now. The lines are kept in `$H/scouts/.start`, since they are not yours and you leave them alone. Make no branch: a scout has nothing to commit. 3. See whether the request names a playbook. Run this in one call with the block at the top and `S` set to the slug: ```sh B="$H/scouts/$S.md" uv run --project "$T/.." jbus playbook scout "$B" ``` It prints nothing when the request says `none` or has no `Playbook:` line: go on with no playbook. On `no playbook`, do the same, and say what it printed in the report. On `not a scout's`, the file is for a crewmate or the home: do the same again. When its first line is a path, read that file now. Under the path it prints the playbook's numbered steps, a checklist: copy it into your todo list as printed, before any todo of your own (with no todo tool, write the list in this thread), and work the steps in order. A step you do not do stays in the list as `skip: `. A path the playbook names is from `$T/..`, the repo this skill lives in, also when the question is in another repo. A line the block prints under the path that says `has` or `lacks` is a line the playbook's `Run by:` asks the request to have. Last, it prints each principle the playbook cites, once and in full, under a line that names them: it is the text a step's citation points to, read it with the playbook. On `no principle`, a citation names a file `principles/` does not have: say what it printed in the report. On `lacks the line`, the request stands as the home wrote it: say what it printed in the report. The playbook adds to this skill and does not replace it: where the two disagree, this skill wins, and you say so in the report. Then see whether the request names skills. Run this with the block at the top and `S` set to the slug: ```sh B="$H/scouts/$S.md" uv run --project "$T/.." jbriefs --skills "$B" ``` It prints nothing when the request has no `Skills:` line or says `none`. Otherwise its first line, `loaded: `, names each skill and principle the line names, by its canonical name, and under it each one's file in full: read them now, and do what each says while you research. Last it prints the artifact each one owes, from `$T/skill-artifacts.md`: the report's `Skills run:` section has one item for each, `- : `, as `$T/scout-report.md` shows. A line `unknown skill: ` is a name jstack does not have: say it in the report, and go on with the rest. 4. Say in two lines what you are about to find out, then start. Do not wait for a go-ahead: the request is the go-ahead. ## Carry on Proceed on reversible work without asking: do it, then say what you did and why. Pause for the user's word before four things only: a force-push to a shared branch, a deploy, deleting data, and a message to a customer or to anyone outside. What the user's global instructions or the repo's own instructions tell a thread to confirm first is still confirmed first. When in doubt, act and write down what you did. ## Research - The **Questions** list is the whole target. Answer each one. A question that cannot be answered gets the reason and what would answer it, not a guess. - Read the code, and read the docs. Docs means the repo's own (README, `docs/`, comments, changelog) and the outside documentation for any library, API or tool the task depends on. Read the outside docs for the version the repo pins, from the source: the installed package, `--help`, the project's own site or repo. Do not answer from memory what a doc can settle. - Every tool call only reads: file reads and searches, `git log`, `git show` and `git diff`, `gh` views, web fetches and searches, and commands that print and change nothing. Install nothing, start nothing, and run nothing that writes into the repo or changes state outside it. If a question needs such a run to answer, say so in the report and leave the run to a crewmate. - A scout seldom meets a test slot, since a run that writes is a crewmate's. A command the request asks you to run under one, `jgauge test-slot --short --thread -- `, does not wait: `` is this thread's own id, which the `t3-code` server's `t3_thread_configuration` with no arguments gives as `threadId` (if your harness defers it, load it first). With every slot taken it registers this thread as a waiter and ends 76. Then end your turn with one line, `Waiting on a test slot: jbus will wake this thread when one is free.`, and run the same command again when jbus wakes you. Never poll for a slot, never wait in a background job, and never end the turn with nothing said. On a machine with no jbus nothing wakes a waiter: leave `--thread ` off, and the call waits in place. - For a wide search, use an Explore subagent and keep its conclusion, not its output; or, with no subagent tool, do it in this thread. - Keep the evidence as you go: the command and what it printed, the path and line, the URL and the part that matters. A finding without one is a guess, and the report says so where it is. - Keep what you ruled out, and why. It is what saves the crewmate from looking again. - Stay inside **Out of bounds**. A question that cannot be answered without crossing it is not answered: record that in the report, with what would answer it, and go on to the next question. The user may steer in this thread at any time. What they say here overrides the request, except in one thing: asked to do the task itself, say that a crewmate does it from the brief, and finish the report. Record the steer in the report. A question only the user can answer does not wait. A gate acts unless stopped: take the default you would recommend, act on it at once, carry on with everything, what depends on the default included, and park the question as a gate: read `$T/gates.md` now, the gate's lines and the rule under them, and fill the lines in. The one exception is a default that is irreversible or slow to revert, one of the six cases of the table in `$T/gates.md`: undoing it needs the user, or takes more than one ordinary pull request or command, or cannot restore what was there, or has already reached someone outside this machine. Do not act on that one. Do its fail-safe (the dry-run, the bundle, the plan or diff, the exact artifact), add the `Holds:` and `Fail-safe:` lines under `Undo:`, and carry on with what does not depend on it: the action waits for the user's answer. Leave its `` as it is: jbus numbers the question and is the one place it is kept. A gate goes to jbus and not into the home's thread: the user answers it wherever they are, and jbus closes it, or closes it with its default when the user has not answered in 2 hours and it has no `Holds:` line. Under the gate go one `Context:` line and one `Option:` line for every option you weighed, the default you took among them, which is the one you recommend, marked `(default, recommended)`, and the rest with no mark. Run this in this checkout, with the block at the top and the filled-in lines in place of the line in angle brackets: ```sh gate=$(cat << 'THE_GATE_ENDS_HERE' Context: Option: (default, recommended): THE_GATE_ENDS_HERE ) if ! command -v jbus > /dev/null; then echo "to the home: jbus is not installed here" elif said=$(printf '%s\n' "$gate" | jbus gate '' 2>&1); then echo "sent to jbus: $said" printf '%s\n' "$said" | sed -n 's/^[a-z ]*: question \([0-9][0-9]*\).*/Gate: question \1/p' else case $said in 'not sent: '*) echo "$said" ;; *) echo "to the home: jbus said: $said" ;; esac fi ``` `jbus gate` checks the lines against `$T/gates.md`, the one copy of their shape, and refuses what they lack with `not sent: `. It speaks for the project whose repo this checkout is of, and refuses a repo its config does not name. On `sent to jbus`, send the home nothing. The line under it, `Gate: question `, is all the report keeps of the gate: add it under "Findings", as printed, and no line of the gate itself. Where the first line says `already answered: question :`, nothing was asked: the same question was answered before, and the line gives that answer. Go by it, and take the default back where it differs. On `to the home`, jbus holds nothing: send the lines, under a first line `: gate`, to the thread the block in step 3 of "End" prints, and with no home there put them in the report under "Findings", the one place left that can hold them. On `not sent`, nothing was queued: fill in the line it names and run it again. An answer that differs from the default comes back as a message here, from jbus. Only the four pauses of "Carry on" are asked here and waited on. Besides a gate jbus could not take, a scout sends its home nothing before the End. ## Write it down Besides its note of the start and its note of what the home was told, a scout writes three kinds of file and no others, all under `$H`: its report, a brief, a page. The **Deliver** list says which. **The report, always.** Read `$T/scout-report.md` now. Append it, filled in, to `$H/scouts/.md` under `## Report`. Keep it to 40 lines unless the request sets another length: the home reads it at the start of a short thread. **A brief, when the request asks for one.** Write `$H/tasks/.md` in the brief's usual shape, `$T/brief.md`. The "Done when" list is checks a person or a command can run. Fill in the five lines the template keeps for a scout, `Read:` to `Ruled out:`, so that a fresh crewmate can trust the brief and not research again. Its `Playbook:` line is picked from the rows that the block in `$T/../skills/home/SKILL.md` "Handing a task to a crewmate" prints, one for each playbook: run that block and pick as the text under it says. Its `Home:` line is the request's, copied: the crewmate reports to the home that sent the scout. A request with no `Home:` line is the root home's: leave the line out of the brief, and never leave the template's placeholder in. Do not start the crewmate. The home does that. **A visual page, when the request asks for one.** Before building it, run this: ```sh if command -v lavish-axi > /dev/null; then echo "page: lavish-axi is installed" else echo "no page: lavish-axi is not installed" fi ``` On `no page`, build none. Write `Page: none, lavish-axi is not installed` in the report. Each open choice the page would have asked is a gate: park it as "Research" says for a question only the user can answer, and, home or no home, put its lines in the report under "Findings". On `page`, build one HTML file at `$H/reports/.html`, with anything it loads beside it, then serve it, in one call with the block at the top and `S` set to the slug. It prints the page's address: ```sh lavish-axi "$H/reports/$S.html" ``` `lavish-axi --help` gives the rules for the page and the playbooks to open before writing it. The page shows what the report says, with the numbers behind it, and puts each open choice as an input the user can answer on the page. A page that asks for picks is made from `$T/grill-page.html`, one card per choice with the recommended pick marked: copy it and write the choices into its `round-data` block as `$T/grill-round.schema.json` says. What the report says goes in sections of your own inside `
`, above the one with the id `settled-sec`. The script, the cards and the send bar stay as they are. ## End 1. Check the checkout against the note of the start, in one call with the block at the top and `S` set to the slug, and show what it prints: ```sh echo "There at the start:" git status --porcelain | grep -Fxf "$H/scouts/$S.start" echo "New since the start:" git status --porcelain | grep -Fxvf "$H/scouts/$S.start" ``` The repo is unchanged only if both lists are empty. Put back what this thread itself wrote into the repo, and nothing else: a line that was there at the start, or a change somebody else made while you worked, stays as it is. Name every line that is left, and whose it is, here and on the report's `Repo:` line, which you correct now if it says otherwise. A path that was already changed at the start cannot be shown untouched: say that, not "unchanged". Never commit, push or open a pull request. 2. Check that the report, and the brief and the page if asked for, are on disk. A page that `no page` ruled out is not one to check. 3. Tell the home that launched this thread, if there is one to tell. In one call with the block at the top and `B` set to the request, `$H/scouts/.md`: ```sh uv run --project "$T/.." jbus home "$B" ``` It prints the id in the `home-id` of the request's home, read now and never kept: `$H/home-id` for the root home, whose requests have no `Home:` line, and `$H//home-id` for a scoped home, whose requests name the scope, `Home: `. A request names no thread, so no home's start or handoff changes it. One written before this rule has a thread id on the line: an id that is in a scoped home's `home-id`, or on the `Was home:` line of its handoff, is that home, and any other id is the root home. `jbus home` is the one rule (`jbriefs.home`), and `jmerge`, jbus and the home skill's `whose` ask the same, so a brief has one home to all of them. Between a handoff and the next home's start the file is gone, or still holds the id that handed off, and the block prints nothing. Run it each time there is something to tell, and do not keep the id from an earlier run: the next home's start puts its own id in the file. If it prints a thread id, send a message of at most five lines to that thread, with the `t3-code` server's `t3_thread_send` (if your harness defers it, load it first): the status, the recommendation in one line, and the path of each file written. If it prints nothing, there is no home listening: skip the report and send nothing, and the next home reads the report from disk. Besides a gate jbus could not take, this is the one message a scout sends its home. Note the report it was told of, in one call with the block at the top and `S` set to the slug: ```sh sed -n '/^## Report/,$p' "$H/scouts/$S.md" | cksum > "$H/scouts/$S.told" ``` 4. Say the same thing in this thread in at most five lines. 5. See whether this thread settles itself. Its work is the report, which is on disk, so nothing is lost with the thread. In one call with the block at the top and `S` set to the slug. It is the only check: ```sh awk ' /^## Report/ {report = 1; done = 0; page = ""} report && $(1) == "Status:" {done = ($(2) == "done")} report && /(^|[ \t])Page:/ {page = $(0); sub(/.*Page:[ \t]*/, "", page); sub(/[ \t]+$/, "", page)} END { if (!report) print "stay: the request has no Report" else if (!done) print "stay: the Report does not say Status: done" else if (page != "" && page !~ /^none(,|$)/) print "page: settle once the user has ended its session" else print "settle" }' "$H/scouts/$S.md" ``` It reads the last report in the request, and a report that names no page has none, as the home's sweep reads it. On `settle`, settle this thread: the `t3-code` server's `t3_thread_organize` (if your harness defers it, load it first) with `{"action": "settle"}` and no `threadId`. It takes effect when the turn ends, so it is the last call, made only once the home is told. The worktree and the branch stay: the home's sweep removes them at its next start. On `stay`, or in a session with no such tool, settle nothing and say the reason here: the thread stays where the user can see what is left. On `page`, the page waits on the user: do what follows, and settle the same way once the user has ended the session and the second message, if there is one, is sent. Then stop, unless there is a page. With a page, stay, and hold the poll inside this turn. Run this as a tracked command, in one call with the block at the top and `S` set to the slug: ```sh lavish-axi poll "$H/reports/$S.html" ``` A tracked command is one whose end your harness gives back to this turn: the call in the foreground, or, where the harness cuts a foreground call short, its own background job with the longest limit it gives, whose output you wait for in this turn. In Claude Code that is the Bash tool with `run_in_background` and a two hour `timeout`, then the Monitor tool on the job's output file until it has an answer. Never end the turn while the poll runs: nothing wakes a thread whose turn has ended, so a poll left in the background of one is heard by nobody. A poll the harness stopped with no answer is run again, and the answers stay queued. When it returns, change the page as the user's feedback asks, update the report when a finding or the recommendation changes, and run it again until the session ends. Send the home nothing while the session lasts. A poll that returns `status: browser_disconnected` is a closed tab, and no answer: each choice the page still asked stays at its default in the report. End the session with `lavish-axi end "$H/reports/.html"`, go on as when the user ends it, and say here that the page opens again on their word. On that word, serve it again with the block of "Write it down" that runs `lavish-axi "$H/reports/$S.html"`, in one call with the block at the top and `S` set to the slug, then run the poll above. When the user ends it, run this, in one call with the block at the top and `S` set to the slug: ```sh if sed -n '/^## Report/,$p' "$H/scouts/$S.md" | cksum | cmp -s - "$H/scouts/$S.told"; then echo "send nothing: the report is the one the home was told of" else echo "tell the home: the report changed since it was told" fi ``` On `tell the home`, send the second message, the same way as the first, with what changed. On `send nothing`, there is no second message: a page that changed with the report as it was is not news for the home. This thread is finished after that; a follow-up is a new message from the user here, or a new request from the home.