diff --git a/.agents/skills/fleet-queue/SKILL.md b/.agents/skills/fleet-queue/SKILL.md index def6cf4..2ec4d2d 100644 --- a/.agents/skills/fleet-queue/SKILL.md +++ b/.agents/skills/fleet-queue/SKILL.md @@ -61,6 +61,11 @@ EOF Store the prompt verbatim: your summary of it is a lossy copy made at the moment you understood it least. +`topic add` also opens this run's log at +`orchestration/runs/-.md` and names it on stderr. You do not +have to make one, and you should not make a second — see **The run +log** below. + Then decompose. A topic is the unit of **intent**; a task is the unit of **work** — one repo, one branch, one thing a single worker can finish and validate on its own. The decomposition is yours. @@ -784,6 +789,35 @@ reasons unrelated to what it was asked. So `collect` names it whenever it closed a task that left a PR open, and `shepherd --json` is the seam anything else reads it through. +## The run log — the queue writes the facts, you write the judgement + +One log per topic, opened by `topic add`, refreshed by `dispatch`, `collect` +and `shepherd` as they go. It exists because it used to not: two consecutive +runs went unrecorded, one file surviving only because its lead was being +migrated and the other reconstructed from chat history at the end. The +instruction was there both times, which is what makes it a tool gap. + +```text + everything between the fences is GENERATED — the task + … table, what waited on what, what overlapped anyway, + and a timeline from the records' own timestamps +``` + +Outside the fence is yours and nothing ever rewrites it: **Goal** in your own +words, **Decisions worth keeping**, **What went wrong**, **Outcome**. That is +the half no record can produce, and it is the half worth having — write into it +while you still know it, not at the end from scrollback. + +- The block is **rewritten, not appended to**, so refreshing three times leaves + one file rather than three copies of a timeline. A refresh that changes + nothing prints nothing. +- `./scripts/queue.sh run []` is that refresh made explicit — for a + topic older than this feature, or when you just want the path. +- Delete the fence and the log is yours entirely: the queue reports it as + `left alone` and never writes into it again. +- Run logs are gitignored, so machine paths and session ids are fine in them. + `_TEMPLATE.md` beside them is tracked; keep that one generic. + ## 6. The views, and keeping your context clean ```bash @@ -868,5 +902,5 @@ six weeks later. them ticking, and §5d says what that does and does not change. 10. `plan` again. Review the PRs; the operator merges every one `shepherd` did not. Sessions release themselves once their pull requests land — `collect` - reaps, `reap --dry-run` shows you what it would do — and you record the run - in `orchestration/runs/` as it happens. + reaps, `reap --dry-run` shows you what it would do. The run log has been + recording itself since step 1; write your judgement into it. diff --git a/.agents/skills/thurbox-session/SKILL.md b/.agents/skills/thurbox-session/SKILL.md index cc0802f..76b1076 100644 --- a/.agents/skills/thurbox-session/SKILL.md +++ b/.agents/skills/thurbox-session/SKILL.md @@ -607,8 +607,10 @@ the message. ## 5. Collect and clean up -Record every session in the run log **as it happens** — name, repo(s), prompt -intent, outcome, PR/artifact. The run log is the source of truth. +For a session the queue dispatched, its facts refresh themselves in the run +log — see `fleet-queue`'s **The run log**. For one you spawned by hand, note +it there yourself; the judgement — goal, decisions, outcome — is always yours +to write. ```bash thurbox-cli session restart # kill window, re-spawn with --resume @@ -629,8 +631,8 @@ requests merged, and it reads this section's state table before it does — see ## Run loop 1. Clarify the goal. Pick or write a playbook in `orchestration/playbooks/`. -2. Open a run log from `orchestration/runs/_TEMPLATE.md`, named - `-.md`. +2. `./scripts/queue.sh topic add` opens this run's log — see `fleet-queue`'s + **The run log**. 3. Per unit of work: `session create` — with an `--on-existing` mode (§1c) and the run's profile flags (§1d) — → `session send`, unless `created` came back `false` → read the result file it writes → record. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a815524..8272c12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -178,6 +178,14 @@ jobs: # the same name an operator has locally. - name: Install lua run: | + # The runner image preinstalls a google-chrome-stable apt source we + # never use; when its index goes stale, `apt-get update` exits + # non-zero for the whole run and (with the runner's default `-e` + # shell) aborts this step before lua is even installed. Drop it so + # this step depends only on the repos it actually needs. Ubuntu + # 24.04 runners ship it as deb822 `google-chrome.sources` rather + # than the old `google-chrome.list`, so remove both forms. + sudo rm -f /etc/apt/sources.list.d/google-chrome.list /etc/apt/sources.list.d/google-chrome.sources sudo apt-get update -qq sudo apt-get install -y --no-install-recommends lua5.4 sudo ln -sf /usr/bin/lua5.4 /usr/local/bin/lua diff --git a/.gitignore b/.gitignore index 86afb95..a350c9c 100644 --- a/.gitignore +++ b/.gitignore @@ -44,7 +44,10 @@ !/registry/context/_TEMPLATE.md # WORKING STATE. One file per orchestration run — the record of what you did, -# when, and how it went. _TEMPLATE.md is the form and stays. +# when, and how it went. scripts/queue.sh opens one per topic and refreshes the +# facts inside it, so these carry machine paths and session ids freely; the +# judgement around those facts is the operator's. _TEMPLATE.md is the form, +# it is generic, and it stays. /orchestration/runs/*.md !/orchestration/runs/_TEMPLATE.md diff --git a/AGENTS.md b/AGENTS.md index 3a539a0..0fe9e4f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -67,7 +67,12 @@ names every path and the reason for each. verify, place, hide, remove, diagnose. - `orchestration/playbooks/.md` — reusable recipes for running thurbox. All tracked; write new ones here, from `_TEMPLATE.md`. -- `orchestration/runs/-.md` — a log per orchestration run. +- `orchestration/runs/-.md` — a log per orchestration run, one + per topic. **The queue writes it**: `topic add` opens it from `_TEMPLATE.md` + and the loop's own commands rewrite a fenced block of facts inside it. + Everything outside that fence is the lead's judgement and nothing ever + overwrites it. Gitignored, like everything a run produces; the template is + the one tracked file there. - `.agents/skills//SKILL.md` — agent skills, in one agent-agnostic tree. `.claude/skills` is a **symlink** to it, so Claude Code and opencode (which auto-discovers `.claude/skills`) both load the same copy. Never add a second @@ -115,7 +120,10 @@ The loop, driven by `./scripts/queue.sh`: `queue.sh watch` folds `thurbox-cli watch`'s event stream into each task's record and closes nothing; `queue.sh collect` reads the `result.md` the worker wrote and only that closes a task. A turn ending is not a task - finishing. Record the run in `orchestration/runs/` as it happens. + finishing. The run log records itself as this happens — `topic add` opened + it and each of these commands refreshes its facts — so what is left for you + is the half no record can hold: the goal in your words, the decisions, what + went wrong, the outcome. Write those into it while you still know them. 6. **Release is a third thing, and it is not manual.** `outcome: shipped` means a pull request is OPEN, or, for a task whose declared publish method is `push`, a commit already on the base branch — that session is the cheap way diff --git a/FLEET.md b/FLEET.md index 38d2819..ecc305c 100644 --- a/FLEET.md +++ b/FLEET.md @@ -39,7 +39,9 @@ registry/repos.generated.yaml Generated index of every repo. NEVER hand-edit; orchestration/queue// The task queue: one directory per topic, one per task inside it, each holding that task's own BRIEF.md. Driven by ./scripts/queue.sh. -orchestration/runs/-.md A log per orchestration run. +orchestration/runs/-.md A log per topic. `topic add` opens it + and `dispatch`/`collect`/`shepherd` keep its + facts current; you write the judgement. ``` **None of that is tracked.** `Thurbeen/fleet` is public and everything a @@ -79,8 +81,10 @@ YAML by hand. Nothing to push — the map is gitignored. 4. **`watch` on your own cadence, then `collect`.** The event stream says WHEN a turn ended; the worker's own result file says WHAT it concluded. A turn ending is not a task finishing, and only `collect` closes anything. -5. Open a run log from `orchestration/runs/_TEMPLATE.md` and record what - happened as it happens. It is gitignored and not backed up by the repo. +5. The run log records itself: `topic add` opened it and `dispatch`, `collect` + and `shepherd` keep its facts current as you run them. Write the goal, + decisions and outcome into it in your own words — that half never comes + from a record. It is gitignored and not backed up by the repo. 6. **`shepherd`, as reflexively as `collect`.** The pull request outlives the task, and `collect` names `shepherd` whenever it closed one that left a PR open. It asks the forge for every open PR on the queue's repos, not just diff --git a/orchestration/playbooks/_TEMPLATE.md b/orchestration/playbooks/_TEMPLATE.md index b8e1cb7..c94ccd8 100644 --- a/orchestration/playbooks/_TEMPLATE.md +++ b/orchestration/playbooks/_TEMPLATE.md @@ -33,7 +33,9 @@ How to decompose the goal into thurbox sessions. For each session, define: ## Run -1. Open a run log from `../runs/_TEMPLATE.md`. +1. `./scripts/queue.sh topic add` — which opens this run's log under + `../runs/` for you. Write the goal into it now; the facts arrive by + themselves. 2. Fast-forward each target repo's base branch, then `thurbox-cli session create` with `--parent "$THURBOX_SESSION"`, the chosen `--on-existing` mode, and the profile's flags from `./scripts/session-flags.sh`. @@ -43,9 +45,9 @@ How to decompose the goal into thurbox sessions. For each session, define: only when the spawn returned `created: true`; an adopted session is already working. `./scripts/queue.sh dispatch` does both steps for you. 4. Read the results the workers wrote (`./scripts/queue.sh watch` for the - timing, `collect` for the conclusions); record each outcome in the run log - as it lands. Do not have workers mail you — `message send` wakes the lead - and interrupts whoever is talking to it. + timing, `collect` for the conclusions) — `collect`'s refresh puts each + outcome in the run log's facts as it lands. Do not have workers mail you — + `message send` wakes the lead and interrupts whoever is talking to it. 5. Review artifacts (PRs). A queue-dispatched session releases itself once its pull request merges (`collect` reaps it; `reap --dry-run` first if unsure); `session delete --force` is for one you spawned by hand. diff --git a/orchestration/playbooks/cross-repo-sweep.md b/orchestration/playbooks/cross-repo-sweep.md index f8086be..b4d2630 100644 --- a/orchestration/playbooks/cross-repo-sweep.md +++ b/orchestration/playbooks/cross-repo-sweep.md @@ -34,7 +34,8 @@ One worker session **per repo**, all with the same prompt shape. ## Run -1. Build the repo list from the registry; write it into the run log up front. +1. `./scripts/queue.sh topic add` opens this run's log. Build the repo list + from the registry; write it into the run log's Goal section up front. 2. Fast-forward every target's base branch before spawning against it. A stale local `main` yields a worker that does correct work in a conflicting PR. 3. Launch in waves of `max_parallel`, each with `--parent "$THURBOX_SESSION"`, @@ -62,7 +63,7 @@ One worker session **per repo**, all with the same prompt shape. reports each one's `state`, and `blocked` is the word for that. Read the skill's session-state section before you act on any of those words — `idle` means the agent said it is at rest, and it is the only one that does. -5. Collect PR URLs and `NOT_APPLICABLE` into the run log's session table. +5. Collect PR URLs and `NOT_APPLICABLE` into the run log's Outcome section. 6. Review PRs in a batch. Each session goes when its pull request merges — `./scripts/queue.sh collect` reaps it, `reap --dry-run` says what it would do — so nothing is left holding a worktree per repo. @@ -71,7 +72,7 @@ One worker session **per repo**, all with the same prompt shape. - Prompts must be self-contained and repo-agnostic — workers don't share context with you or with each other. -- Record the profile in the run log alongside the repo list. Two sweeps of the +- The run log's facts name the profile without being asked. Two sweeps of the same goal under different settings are two different runs. - Log every repo that reported `NOT_APPLICABLE` so the sweep is auditable and not silently partial. This is the reason to prefer a result file over polling diff --git a/orchestration/playbooks/ship-feature.md b/orchestration/playbooks/ship-feature.md index d94909c..74c3e08 100644 --- a/orchestration/playbooks/ship-feature.md +++ b/orchestration/playbooks/ship-feature.md @@ -12,8 +12,8 @@ - `repo` — `owner/name`. - `base` — base branch (default the repo's default branch). - `profile` — the session profile the worker starts under, from - `../session-profiles.yaml` (default `default`). Record which one in the run - log: it is part of what produced the result. + `../session-profiles.yaml` (default `default`). It is part of what produced + the result, and the run log's facts name it without being asked. ## Sessions @@ -33,7 +33,8 @@ One worker session. 1. Read `registry/context/.md` for goals and gotchas; fold the relevant bits into the prompt. -2. Open a run log. +2. `./scripts/queue.sh topic add` opens this run's log; write the goal into it + now. 3. Fast-forward `base` in the target repo, then `session create --parent "$THURBOX_SESSION" --on-existing adopt` with the profile's flags → `session send`. @@ -46,7 +47,8 @@ One worker session. 4. Read the result file the worker wrote, when you choose. Do not ask it to mail you: `message send` wakes the lead and interrupts whoever is talking to it. `./scripts/queue.sh watch` gives the timing without interrupting anyone. -5. Review the PR; record it in the run log; merge or hand back. +5. Review the PR — `collect`'s refresh already has it in the run log's facts — + then merge or hand back. 6. `./scripts/queue.sh collect` reaps the session once the pull request merges. For a session you spawned outside the queue, `session delete --force` once merged or abandoned. diff --git a/orchestration/runs/_TEMPLATE.md b/orchestration/runs/_TEMPLATE.md index db84d49..9e4486d 100644 --- a/orchestration/runs/_TEMPLATE.md +++ b/orchestration/runs/_TEMPLATE.md @@ -1,30 +1,35 @@ # Run: `` — `` -> Copy to `orchestration/runs/-.md`. This log is the source of -> truth for what happened. +> One run, one topic. `./scripts/queue.sh topic add` opens this file, and +> `dispatch`, `collect`, `shepherd` and `run` refresh the fenced block below +> from the queue's own records as the run goes on. +> +> **Everything outside that fence is yours.** Nothing rewrites it, nothing +> generates it, and it is the reason the file exists — the block says what +> happened, and these sections say what you decided and what it cost. -- **Goal.** What this run is meant to achieve. -- **Playbook.** `../playbooks/.md` (or "ad hoc"). -- **Profile.** `../session-profiles.yaml` name the workers started under - (`default` if you did not pick one). Settings are part of what produced the - result, so a run that does not name one cannot be reproduced. -- **Started.** · **Status.** planning | running | done | abandoned + + -## Sessions +## Goal -Name each session as an imperative sentence describing the work, in sentence -case — no repo prefix, the repo has its own column. +What this run is meant to achieve, in your words. -| Session name | Repo | Intent | Status | Artifact / PR | -|---|---|---|---|---| -| `Document the customization surface` | `owner/repo` | one line | running / done | link | +## Playbook -## Timeline +`../playbooks/.md`, or "ad hoc". -- `