From 4fe3615d8c6e8de2e74158a868c1d4995b6e357d Mon Sep 17 00:00:00 2001 From: Marcus Gibson Date: Sun, 11 Oct 2026 16:35:24 -0500 Subject: [PATCH 1/2] brigade: show kind and goal on the skill command lines and name goals.tsv in the guide --- changes/pstack-t3%2Fd171.md | 1 + docs/guide.md | 2 +- skills/brigade-admin/SKILL.md | 2 +- skills/brigade/SKILL.md | 20 ++++++++++++-------- t3/added/brigade-admin/SKILL.md | 2 +- t3/added/brigade/SKILL.md | 20 ++++++++++++-------- 6 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 changes/pstack-t3%2Fd171.md diff --git a/changes/pstack-t3%2Fd171.md b/changes/pstack-t3%2Fd171.md new file mode 100644 index 00000000..b80036ea --- /dev/null +++ b/changes/pstack-t3%2Fd171.md @@ -0,0 +1 @@ +- The brigade skill and the brigade-admin skill show `--kind` on every `ticket add --summary` line. `--kind` is one of `feature`, `fix`, `polish`, `upkeep`, and `study`, and it is required with `--summary`. `--goal` on that line takes an open or paused goal, and a ticket added without it is unassigned. `ticket set --kind`, `--goal`, and `--no-goal` change a waiting ticket. They are refused on a ticket in another state and on a waiting ticket that an item in flight names, and `--goal` there takes an open or paused goal. `goal add` records an open goal and prints its id. `--share` on it is a whole number of 1 or more. A `goal set` move to `done` or `dropped` needs `--evidence` and is refused while a ticket of the goal is waiting, an item of the goal is in progress, in review, passed, sent back, blocked, or waiting to land, or an assigned ticket of the goal is not in an item of the goal. A done or dropped goal moves only to open. `--evidence` on open or paused is refused. `goal list` prints each open or paused goal, and each done or dropped goal that still has a waiting ticket, an item in flight, or a ticket riding along, then the counts of unassigned work. `--all` also prints each other done or dropped goal. A ticket riding along is an assigned ticket of that goal that is not in an item of that goal. `fire` takes `--kind` and `--ride-along`. The item takes the first ticket's goal, and that ticket's kind when it has one. A `--kind` that differs from that kind is refused. `fire` is refused when the first ticket has no kind, no `--kind` is passed, and the station is not `bug-fix`, `perf-issue`, or `investigation`. `ticket add --from-report` takes no `--kind` and no `--goal`. Each ticket takes the goal of the report's item, whatever that goal's state. When that item has a kind, the ticket is `polish`, because a follow-up is filed with no priority and a report ticket with no priority is low. When the item has no kind, the ticket has no kind. `status` prints a kinds line, a goals line, and an untagged line when each has something to count. The guide's list of empty store tables names `goals.tsv`. diff --git a/docs/guide.md b/docs/guide.md index 82a64497..68602e7a 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -199,7 +199,7 @@ The store holds these files. - `menu.md`, with the headings `Purpose`, `What good looks like`, `Off the menu`, and `Budget`. - `house-rules.md`. - `restaurant.json`. -- `rail.tsv`, `dishes.tsv`, `pass.tsv`, `86.tsv`, and `log.tsv`, each with a header and no rows. +- `rail.tsv`, `dishes.tsv`, `pass.tsv`, `86.tsv`, `goals.tsv`, and `log.tsv`, each with a header and no rows. The opener drafts `menu.md` from the README, AGENTS.md, open work in the repository's tracker, and recent threads, and asks you for the purpose when evidence does not settle it. `brigade.py brief` refuses to write a worker brief while `Purpose` is empty or still the template. The opener appends standing orders to `house-rules.md`. They name forbidden paths, the verification bar, intake sources, and the repository's hot shared files. They do not restate the landing mode, which lives only in the repository's landing contract. The worker cap is `open --workers`, and `brigade.py fire` enforces it. diff --git a/skills/brigade-admin/SKILL.md b/skills/brigade-admin/SKILL.md index 0aec113b..17ac7395 100644 --- a/skills/brigade-admin/SKILL.md +++ b/skills/brigade-admin/SKILL.md @@ -53,7 +53,7 @@ Every wake runs one service, whether a schedule, a message, or a user message. 1. Run `$B status`. [The script](../brigade/SKILL.md#the-script) describes its `thread` line and its `owner` token. When it names another thread, or prints `thread not recorded` because the admin was retired, end the turn with no action. Otherwise pass that token as `--owner` on every `$B` command, and `--owner .admin/@` on every `$L` ruling write, which is `lease reserve`, `lease unreserve`, `share`, and `contest`. `$L lease list`, `$L status`, and `$L land` take no `--owner`, and the admin never runs `land`. A write refused with `is stale` means another thread owns the store. End the service at once and run no further command, including the ruling's `$L` enforcement. 2. Read `menu.md`, `$B status`, `$B 86 list`, and `python3 /brigade/scripts/brigade.py walk --repo --stale-hours 3`. When the user's message answers a question `$B 86 list` prints, run `$B 86 answer Q --answer ""` before anything else, then act on it per [Escalate](#rulings). -3. `$B inbox take`, for tickets moved back. Then file each piece of work the user asks for in this service as `$B ticket add --summary "" --source user`, one ticket per request, with the `--ref` that [Filing tracker work](../brigade/SKILL.md#filing-tracker-work) gives it. That section's Refusals step says what a refused add means. Run that section's Match step for each request before its add, even when it names no ref. Route it per step 4 like any other ticket. A message that settles a conflict between coordinators, such as which one's item lands first, is a ruling per [The user's ruling](#rulings), not a ticket. Then intake from its owned sources with `$B ticket add --summary "..." --source --ref `, reading each source and filing each finding per [Filing tracker work](../brigade/SKILL.md#filing-tracker-work). [The script](../brigade/SKILL.md#the-script) says what `ticket move` in step 4 does with that ref. Then `$B request --republish`. +3. `$B inbox take`, for tickets moved back. Then file each piece of work the user asks for in this service as `$B ticket add --summary "" --kind --source user`, one ticket per request, with the `--ref` that [Filing tracker work](../brigade/SKILL.md#filing-tracker-work) gives it. That section's Refusals step says what a refused add means. Run that section's Match step for each request before its add, even when it names no ref. Route it per step 4 like any other ticket. A message that settles a conflict between coordinators, such as which one's item lands first, is a ruling per [The user's ruling](#rulings), not a ticket. Then intake from its owned sources with `$B ticket add --summary "..." --kind --source --ref `, reading each source and filing each finding per [Filing tracker work](../brigade/SKILL.md#filing-tracker-work). [The script](../brigade/SKILL.md#the-script) says what `ticket move` in step 4 does with that ref. Then `$B request --republish`. 4. Route each waiting ticket to the coordinator whose purpose fits with `$B ticket move --to `, then `t3_thread_send` to the thread it prints, with mode `"auto"`, the `ticket` line from Admin messages, and the handoff id `/.admin/T` as `clientRequestId`. A ticket that fits two purposes gets an ownership ruling. A ticket no purpose fits becomes `$B 86 add` with options and a default. 5. Read coordinator events from two sources. Run `$B sync`. It prints each coordinator's new store rows. The event table in [Reporting to an executive admin](../brigade/SKILL.md#reporting-to-an-executive-admin) says which lines leave a store row and which are message-only. A message-only line never appears in `sync`. Act on each one from the message that woke this service. Rule on each conflict either source shows, such as a block older than an hour, a `contest` or `appeal` line, or waiting work that needs a changed share, per [Rulings](#rulings). Record each ruling with `$B rule add` before carrying it out. Then reconcile every ruling `$B rule list --state in-force` prints, mark the ones whose condition ended with `$B rule set`, and carry out the rest again, which repeats nothing. Pass each coordinator's `decision` to the user. Never answer it. When the wake message is a coordinator's `reply` line, pass it to the user, including a decline and its reason, per [What the user hears from the admin](#what-the-user-hears-from-the-admin). A malformed-line failure from `sync` goes to the user as a failure. 6. When the repository sits at its cap while tickets wait for an hour, park a decision proposing a new cap, with a default. A coordinator that `walk` marks idle while it holds leases becomes a decision for the user, before its 6-hour leases lapse. diff --git a/skills/brigade/SKILL.md b/skills/brigade/SKILL.md index 775cd373..787b22a2 100644 --- a/skills/brigade/SKILL.md +++ b/skills/brigade/SKILL.md @@ -44,7 +44,7 @@ These words name files, commands, and steps. They never appear in speech. Replie ```bash B="python3 /brigade/scripts/brigade.py --at --owner @" # the token from status -$B status # thread or thread not recorded, then reporting level, landing mode, and counts, then the workers line, then reports to , mode , and owner @ when set +$B status # thread or thread not recorded, then reporting level, landing mode, and counts, then the workers line, then a kinds line, a goals line, and an untagged line when each has something to count, then reports to , mode , and owner @ when set $B set --thread [--replace] --schedule = $B set --reporting every-turn|milestones|digest $B set --schedule = drops that name from restaurant.json. A name that is not recorded is already absent, and the command still succeeds. Other names stay. @@ -54,17 +54,21 @@ $B set --autofire off|urgent|normal|low # the lowest ticket priority startab $B set --reports-to # the executive admin this restaurant reports through. --reports-to "" clears it $B set --mode light|full # this restaurant's mode from the next brief. --mode "" returns it to the roles files $B set --build-command "" --check-command "" # the two commands edits verify runs. none for one the project lacks -$B ticket add --summary "" --source user|github| [--ref ] [--request A] [--again] [--priority urgent|normal|low] [--paths src/a,src/b] [--decision] # prints T. --request refuses a second ticket for that request. --again lifts the same-summary refusal in Filing tracker work step 4 -$B ticket add --from-report reports/D2.md [--dry-run] # one waiting ticket per follow-up in that report, source report. Skips text already on the rail in any state. A run changes only the ticket table, the log, and `lastActivityAt`. --dry-run changes no table and no log +$B ticket add --summary "" --kind feature|fix|polish|upkeep|study [--goal G] --source user|github| [--ref ] [--request A] [--again] [--priority urgent|normal|low] [--paths src/a,src/b] [--decision] # prints T. --request refuses a second ticket for that request. --again lifts the same-summary refusal in Filing tracker work step 4. --kind is required with --summary. --goal takes an open or paused goal, and without it the ticket is unassigned +$B ticket add --from-report reports/D2.md [--dry-run] # one waiting ticket per follow-up in that report, source report. Skips text already on the rail in any state. A run changes only the ticket table, the log, and `lastActivityAt`. --dry-run changes no table and no log. It takes no --kind and no --goal. Each ticket takes the goal of the report's item, whatever that goal's state. When that item has a kind, the ticket is polish, because a follow-up is filed with no priority and a report ticket with no priority is low. When the item has no kind, the ticket has no kind $B ticket list [--state waiting|assigned|moved|done|dropped] $B ticket set T3 [--state dropped] [--priority urgent|normal|low] [--paths src/a,src/b] [--decision | --no-decision] # at least one flag. --paths "" clears the recorded paths +$B ticket set T3 --kind fix [--goal G1 | --no-goal] # changes a waiting ticket. Refused on a ticket in another state, and on a waiting ticket that an item in flight names. --goal takes an open or paused goal $B ticket move T3 --to # hands a waiting ticket to a sibling. Prints the thread to tell $B ticket take # files every ticket a sibling handed to this restaurant $B ticket paths [--dry-run] # on each waiting ticket that records no paths, records the tracked paths it finds in the summary's backtick quotes $B startable # the workers line, then a count line and each waiting ticket in start order with why it can or cannot start now, or no waiting tickets. It starts nothing $B inbox take # ticket take, then prints each request from the executive admin as A: $B inbox done A # records the request as acted on and deletes its file -$B fire --tickets T1,T3 --station bug-fix --summary "" --paths src/a,src/b [--timebox 60] [--mode full --reason ""] # claims the lease, prints D +$B goal add --name "" --purpose "" --done "" [--share ] # prints G. --share is a whole number of 1 or more +$B goal set G1 --state open|paused|done|dropped [--evidence ""] # a move to done or dropped needs --evidence and is refused while a ticket of the goal is waiting, an item of the goal is in progress, in review, passed, sent back, blocked, or waiting to land, or an assigned ticket of the goal is not in an item of the goal. A done or dropped goal moves only to open. --evidence on open or paused is refused +$B goal list [--all] # each open or paused goal, and each done or dropped goal that still has a waiting ticket, an item in flight, or a ticket riding along, with its state, name, counts, purpose, and done test, then the counts of unassigned work. A ticket riding along is an assigned ticket of that goal that is not in an item of that goal. --all also prints each other done or dropped goal +$B fire --tickets T1,T3 --station bug-fix --summary "" --paths src/a,src/b [--timebox 60] [--mode full --reason ""] [--kind ] [--ride-along T3] # claims the lease, prints D. The item takes the first ticket's goal, and its kind when it has one. A --kind that differs from that kind is refused $B brief D2 --fields # JSON file, or --fields - for stdin. Writes briefs/D2.md $B brief D2 --goal '...' --acceptance '...' [--acceptance ...] --contract '...' --verify '...' --base main [--context ...] # same brief. Single-quote every field $B contract D2 # prints the contract the last brief of D2 stored @@ -155,10 +159,10 @@ Coordinators and the executive admin follow this section before each `ticket add Every wake runs this: a user message, a worker's report-back, a verifier's completion, a schedule, or a `watch_pull_request` wake. 1. **Read.** `menu.md`, `house-rules.md`, `$B status`, `$B 86 list`. Re-read the menu every service. It is the purpose every decision answers to. `$B status` prints `thread ` first and `owner @` last. When it names another thread, end the turn with no further command. Otherwise set `$B` to `brigade.py --at --owner @`, and pass `--owner /@` on every `$L lease claim`, `lease renew`, `lease release`, and `submit`. When `status` prints no owner line and `restaurant.json` records this thread, run `$B set --thread ` once and read `status` again. A write refused with `is stale` means another thread replaced this one. End the service with no further command. -2. **Take tickets.** Start with `$B inbox take`. It files each ticket a sibling or the executive admin handed to this restaurant, then prints each request from the admin as `A: `. Act on each request per [Reporting to an executive admin](#reporting-to-an-executive-admin), then run `$B inbox done A`. Each user request or supplier finding becomes `$B ticket add`, with `--source user` or the intake name of the source it came from, such as the tracker AGENTS.md names, and the `--ref` that [Filing tracker work](#filing-tracker-work) gives it. Fetch only the sources in this restaurant's intake. A ticket that is a sibling's work goes to it with `$B ticket move --to `. Then call `t3_thread_send` to the thread it prints, with mode `"auto"`, the line `ticket : run ticket take`, and the handoff id as `clientRequestId`. The handoff id is this restaurant's store path, the last two parts of ``, then the ticket, such as `app/engine/T6`. Fetch supplier sources only when the house rules name them and this restaurant owns them. Read backlog from the tracker that [Filing tracker work](#filing-tracker-work) names. Read pull requests with `gh pr list` only when the house rules name pull requests and this restaurant owns that source. Read notifications only when the house rules name them and this restaurant owns that source. Drop a ticket that is off the menu with `$B ticket set --state dropped` and say why in the next report. At `digest` a dropped ticket is not a reply occasion. +2. **Take tickets.** Start with `$B inbox take`. It files each ticket a sibling or the executive admin handed to this restaurant, then prints each request from the admin as `A: `. Act on each request per [Reporting to an executive admin](#reporting-to-an-executive-admin), then run `$B inbox done A`. Each user request or supplier finding becomes `$B ticket add`, with `--source user` or the intake name of the source it came from, such as the tracker AGENTS.md names, and with `--kind`, and the `--ref` that [Filing tracker work](#filing-tracker-work) gives it. Fetch only the sources in this restaurant's intake. A ticket that is a sibling's work goes to it with `$B ticket move --to `. Then call `t3_thread_send` to the thread it prints, with mode `"auto"`, the line `ticket : run ticket take`, and the handoff id as `clientRequestId`. The handoff id is this restaurant's store path, the last two parts of ``, then the ticket, such as `app/engine/T6`. Fetch supplier sources only when the house rules name them and this restaurant owns them. Read backlog from the tracker that [Filing tracker work](#filing-tracker-work) names. Read pull requests with `gh pr list` only when the house rules name pull requests and this restaurant owns that source. Read notifications only when the house rules name them and this restaurant owns that source. Drop a ticket that is off the menu with `$B ticket set --state dropped` and say why in the next report. At `digest` a dropped ticket is not a reply occasion. 3. **Group before firing.** Read the waiting tickets together. Several reports of one cause are one dish. Fire a ticket alone only when it is urgent or unrelated to the rest. A ticket that is a whole program with a done predicate runs as one dish whose station is poteto-mode's Orchestrate playbook. 4. **Fire.** Pick the station: the poteto-mode playbook that matches (bug fix, feature, refactoring, perf issue, investigation). Then: - 1. `$B fire --tickets ... --station --summary "" --timebox --paths [--branch ]`. Pass `--mode full --reason ""` when the tickets name a lock, a lease, a race, a migration, or history rewriting. It claims a landing lease on those paths for holder `/` before it records anything. `fire` refuses when this restaurant's running workers are already at `--workers`. A refused claim names the holder and fires nothing. A refused `fire` leaves the tickets waiting. On the first refusal, create the liveness schedule from First service step 3 if `restaurant.json` has no `liveness` id. Fold the tickets into the holder's dish when it is this restaurant's, or leave them waiting until that lease is released. Never pass `.` for a dish that touches a few files. Size the timebox to the work, 30 to 90 minutes. The timebox orders the work. It never waives a playbook step. How, Architect, investigation, and the implementation delegate stay in the work. At the limit, the worker writes the report with what remains. + 1. `$B fire --tickets ... --station --summary "" --timebox --paths [--branch ]`. Pass `--mode full --reason ""` when the tickets name a lock, a lease, a race, a migration, or history rewriting. `fire` is refused when the first ticket has no kind, no `--kind` is passed, and the station is not `bug-fix`, `perf-issue`, or `investigation`. It claims a landing lease on those paths for holder `/` before it records anything. `fire` refuses when this restaurant's running workers are already at `--workers`. A refused claim names the holder and fires nothing. A refused `fire` leaves the tickets waiting. On the first refusal, create the liveness schedule from First service step 3 if `restaurant.json` has no `liveness` id. Fold the tickets into the holder's dish when it is this restaurant's, or leave them waiting until that lease is released. Never pass `.` for a dish that touches a few files. Size the timebox to the work, 30 to 90 minutes. The timebox orders the work. It never waives a playbook step. How, Architect, investigation, and the implementation delegate stay in the work. At the limit, the worker writes the report with what remains. 2. `$B brief --fields `. The file is the JSON object described after the command block. `--fields -` reads it from stdin. The flag form single-quotes every value. Use the file or those single quotes. A double-quoted value lets the shell run backticks and expand `$` before `brigade.py` starts. It assembles the brief from the menu, the tickets, the house rules, and the landing rules, names the dish branch, adds the exclusive-slot rule for measuring stations, and writes `briefs/.md`. The brief carries the item's `Mode:` lines and `Gate: brigade`. Read its `Mode:` value for steps 4.3 and 6. The report section first tells the worker never to end its turn while a child task it delegated is open, per [the runtime's Delegation section](../pstack-runtime/SKILL.md#delegation). It gives a thread the worker launched the same bounded wait, except a long-lived owner its playbook supervises, which follows [the runtime's Top-level threads section](../pstack-runtime/SKILL.md#top-level-threads). It then tells the worker to call `t3_thread_send` on the coordinator thread after the report file is written. It refuses when a field is missing, and when no coordinator thread is recorded. Run `$B set --thread` first. Fix the field. Never hand-write a brief. 3. Launch the worker with `t3_thread_launch`: title ` : `, `workspaceStrategy: {"type": "worktree", "baseRef": "", "branch": "", "startFromOrigin": true}` (local landing mode: `baseRef` `refs/landing/` and `startFromOrigin` false), and the brief file's contents as `message`. Resolve the station's seat with `roles.py show --brief-mode .md>` and no other mode flag, per [the runtime's Roles section](../pstack-runtime/SKILL.md#roles), so an item moved to full gets full seats. Build `modelSelection` from that seat per [the runtime's Top-level threads section](../pstack-runtime/SKILL.md#top-level-threads). Omit `modelSelection` for an `inherit` seat. Confirm the applied options per [the runtime's Delegation section](../pstack-runtime/SKILL.md#delegation) before step 4.4. Step 4.4 records the thread only after the readback matches. On a mismatch, stop the thread per the runtime, record nothing, and park the dish with `$B 86 add --dish --question ": could not pass the seat's options: . Move this coordinator to a provider that passes them?" --options "moved, keep parked" --default "keep parked"`. The item is then held. `$B 86 list` prints `Q for :`. `$B watch` prints `: open decision Q: ...` in place of its other lines. A second `$B 86 add --dish ` prints the same id and appends nothing, so never raise it again. Record every answer with `$B 86 answer Q --answer ""`. Only `moved` closes the decision, matched without regard to case or a trailing period, and the command prints `Q answered`. Any other answer, `keep parked` included, writes nothing and prints `Q still open`, so the item stays held. Only the user can move the coordinator to a provider that passes options. Never relaunch the worker without the options. 4. `$B dish --thread ` only after the readback in step 4.3 matches. @@ -169,7 +173,7 @@ Every wake runs this: a user message, a worker's report-back, a verifier's compl **Contested.** A worker whose brief says `Mode: light` stops at a verifiable point when it finds its design contested, and reports `Contested: `, per [the runtime's Modes section](../pstack-runtime/SKILL.md#modes). Do not mark the dish in review, and spawn no verifier. Call `t3_thread_interrupt` on the old worker, then `t3_thread_wait` on the run id it returned. When the wait returns `timedOut: true`, leave everything as it is and wait again on the next service. After a terminal state, finish with the old worker per [Git and PR housekeeping](#git-and-pr-housekeeping). Run `$B dish --mode full --reason "contested: "`. Create a new branch from the old branch's head and record it with `$B dish --branch `. Rerun `$B brief` with `--base ` and `--context` naming the report path. When the item is held, launch no replacement worker. Otherwise launch a fresh worker per steps 4.3 and 4.4 on the new branch and the full seat its brief names. The dish stays in progress and keeps its lease. Never release or claim the lease again for this replacement, so skip the lease commands `dish --branch` prints. The fresh worker writes its changelog bullet to the `changes/` file its brief's leased paths name. A contested report is not a send-back. It records no `pass.tsv` row and does not count toward the second send-back. 7. **Act on the verdict.** A held item gets no send-back worker and no bounce worker. - - `pass`: first run `$B ticket add --from-report reports/.md`. It files the follow-ups in the worker's report as waiting tickets and skips text already on the rail. It prints a `not filed` line for a paragraph that introduces a list and for a paragraph after a section's last list item. It prints one for text that follows the word on a follow-ups heading line. It prints one for a heading deeper than the follow-ups heading that does not itself start with follow-ups, with the heading's text when it has any. It also prints one for a block whose whole text is `None`, `None required`, `None needed`, `Nothing`, `Nothing needed`, `N/A`, `Not applicable`, or `No follow-ups`, in any letter case, with or without list markers, a final period, and `*`, `_`, or backtick marks around it. Read each `not filed` line, and file a real follow-up among them with `$B ticket add --summary`. Drop a ticket that is not worth an item with `$B ticket set --state dropped`, or fire it with related tickets as one dish per step 3. When the dish depends on another coordinator's item that has not landed and `$B status` prints `reports to `, never hold it outside the queue. First open a contest per [Contests](#reporting-to-an-executive-admin) and send the `contest` line. The contest holds this dish's entry until the admin orders the two. Then submit as below. Write the PR title and body (what changed, the measured effect, how it was verified) to `/prs/.md`, then `$L submit --holder / --branch --sha --lease L --reviewer --title "..." --body-file /prs/.md`, then `$B dish --state queued`, then `$L land`. When it lands, `$B dish --state merged`. In `merge` and `human` mode it opens a PR first. Record it with `$B dish --pr `, link it with `link_pull_request`, and call `watch_pull_request` on it per [Pull request watching](../pstack-runtime/SKILL.md#pull-request-watching). This head chef thread owns the PR. On each wake, run `$L land`. Keep the watch while the dish is queued or awaiting merge. A reply, or a wake with no reply, keeps the watch. Call `unwatch_pull_request` only when this thread stops driving that PR. Driving stops when the dish is dropped, when its PR closes, when the queue bounces it and leaves the PR open, or when the restaurant closes. If this thread is settled, call `t3_thread_organize` with `action: "unsettle"` before the next `watch_pull_request`. This thread is pinned, so a merge does not auto-settle it. Mark the dish merged when a later `land` reports it landed. + - `pass`: first run `$B ticket add --from-report reports/.md`. It files the follow-ups in the worker's report as waiting tickets and skips text already on the rail. It prints a `not filed` line for a paragraph that introduces a list and for a paragraph after a section's last list item. It prints one for text that follows the word on a follow-ups heading line. It prints one for a heading deeper than the follow-ups heading that does not itself start with follow-ups, with the heading's text when it has any. It also prints one for a block whose whole text is `None`, `None required`, `None needed`, `Nothing`, `Nothing needed`, `N/A`, `Not applicable`, or `No follow-ups`, in any letter case, with or without list markers, a final period, and `*`, `_`, or backtick marks around it. Read each `not filed` line, and file a real follow-up among them with `$B ticket add --summary --kind `. Drop a ticket that is not worth an item with `$B ticket set --state dropped`, or fire it with related tickets as one dish per step 3. When the dish depends on another coordinator's item that has not landed and `$B status` prints `reports to `, never hold it outside the queue. First open a contest per [Contests](#reporting-to-an-executive-admin) and send the `contest` line. The contest holds this dish's entry until the admin orders the two. Then submit as below. Write the PR title and body (what changed, the measured effect, how it was verified) to `/prs/.md`, then `$L submit --holder / --branch --sha --lease L --reviewer --title "..." --body-file /prs/.md`, then `$B dish --state queued`, then `$L land`. When it lands, `$B dish --state merged`. In `merge` and `human` mode it opens a PR first. Record it with `$B dish --pr `, link it with `link_pull_request`, and call `watch_pull_request` on it per [Pull request watching](../pstack-runtime/SKILL.md#pull-request-watching). This head chef thread owns the PR. On each wake, run `$L land`. Keep the watch while the dish is queued or awaiting merge. A reply, or a wake with no reply, keeps the watch. Call `unwatch_pull_request` only when this thread stops driving that PR. Driving stops when the dish is dropped, when its PR closes, when the queue bounces it and leaves the PR open, or when the restaurant closes. If this thread is settled, call `t3_thread_organize` with `action: "unsettle"` before the next `watch_pull_request`. This thread is pinned, so a merge does not auto-settle it. Mark the dish merged when a later `land` reports it landed. - Bounced by the queue: the lease is active again. Call `unwatch_pull_request` on the PR the bounce left open. Finish with the old worker per [Git and PR housekeeping](#git-and-pr-housekeeping). Then run `$B dish --state in-progress`, because `brief` refuses a queued dish. That command clears the recorded worker thread. Rerun `$B brief` with `--context` naming the bounce reason and current trunk. Launch a fresh worker per steps 4.3 and 4.4, unless the item is held. A conflict or a changed rebase needs a new review. - When `pass record` or `watch` prints ` send-backs, decision pending`, the dish owes its round decision per [The script](#the-script). Follow this bullet in place of the `send-back` bullet below. Stop patching and launch no fix worker. Finish with the old worker per [Git and PR housekeeping](#git-and-pr-housekeeping) when the dish is sent back. Run `$B 86 add --dish --round-budget` and raise that one decision in the reply. Record the user's choice with `$B 86 answer Q --answer "