scripts: land-work turns a resolved merge decision into a gated local commit (row 116, #1610)

Candidate manifest 8b4ab31014a124ec0e3abba44238976d6f4c9af48cbdb36c2c77d18f0137f888
from Darkwing's round 2 packet (0a4076e8e), on BASE 1223a0f37. No
manifest path changed between BASE and the landing target. Filbert
approved round 2 in 28468, closing C1 and D1 from round 1 (28464).
Sage reran the full gate at the landing target with the candidate
applied. The replay acceptance follows ruling sage-0204.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-14 14:24:57 -05:00
co-authored by Claude Opus 5.5
parent 922cb8170b
commit 1f2ac5b848
6 changed files with 1021 additions and 0 deletions
+29
View File
@@ -118,6 +118,34 @@ succeeded run → clean target tree → path allowlist → syntax gates →
apply → policy suites → attribution commit. Any failure reverts.
Push is never automatic.
## Conductor (landing approved work)
```bash
scripts/land-work.sh <decision-id> [--base refactor] [--dry-run] [--subject TEXT]
[--attribution LINE] [--business ID] [--project ID]
```
Turns a merge decision the human resolved into one gated local commit on
`refactor` (#1610). It reads `<dataRoot>/bus/bus.sqlite` read-only through
`scripts/land-work/read.mjs` and checks that the human picked the raise's
approval option, that the question names one `work/vikunja-<p>-<t>`, and
that the task's latest `review.verdict` approves a commit C that the work
ref in the working repository still points at. It then applies
`git diff $(merge-base refactor C) C` in a detached worktree, checks the
patch-id, runs every package suite except `queue` and every
`scripts/test-*.sh`, and commits. The subject is the approval option's
text unless `--subject` overrides it, and `--attribution` replaces the last
line, `Applied-under: scripts/land-work.sh (conductor)`. `--business` and
`--project` are needed only when the config has more than one business file
or the business more than one project. The tool prints the line for the PM
and doesn't send it. `--dry-run` runs every check and the gate and writes
no ref. A refusal names its reason and exits 2; the reasons are listed at
the top of the script. The worktree, its `node_modules` link and its Docker
network are removed on every exit. The gate log directory stays and the
tool names it. Keep `TMPDIR` short: the gate inherits it, the cli suite
puts unix sockets under it, and a 46-byte `TMPDIR` failed 8 of those tests.
Push is a separate act.
## Maintenance
| Command | Purpose | Notes |
@@ -128,6 +156,7 @@ Push is never automatic.
| `scripts/test-release.sh` | Release selftests | 14 cases |
| `scripts/test-conductor.sh` | Auto-apply selftests (sandboxed) | 17 cases |
| `scripts/test-auth.sh` | Auth checkpoint selftests (no Docker) | 13 cases |
| `scripts/test-land-work.sh` | Land-work selftests (sandboxed, fake Docker) | 84 cases |
| `scripts/gitea-api.sh <METHOD> <path> [body]` | Gitea API helper; reads the JSON credential file or a raw per-seat token file | Token never on argv/stdout |
## Seat launch with registration (`scripts/mosaic`)
+27
View File
@@ -43,6 +43,33 @@ tracked, versioned files whose changes arrive as reviewed commits.
- Verification is mechanical: suites + `node --check` / `bash -n` gates.
- Recursive decomposition = "fail → smaller task", never "hope."
## Landing approved work
A stack coder pushes `work/vikunja-<p>-<t>` to the working repository, the
reviewer approves a commit on it, and the PM raises a `git.merge.protected`
decision for the human. After the human resolves it with the merge option,
the conductor runs:
```bash
scripts/land-work.sh <decision-id> --dry-run
scripts/land-work.sh <decision-id>
```
The tool binds the commit to what was reviewed and decided. It reads the
decision, its raise and the latest verdict from the bus database
read-only, requires the work ref to be at the approved commit, squashes
the reviewed diff onto `refactor` in a detached worktree, checks the
patch-id, runs the full gate and commits once, locally. It refuses work
that adds a file at the repository root, in a new top-level directory, or
under `src/` or `v1/`. Each
refusal names its reason (`docs/TOOLS.md`). Don't work around one; fix the
cause or go back to the PM.
The conductor then reviews the commit, pushes it as a separate act and
sends the PM the line the tool printed. Only `refactor` lands this way.
Merges to `next` or `main`, re-pins and restarts keep their own authority,
and no stack role runs this tool.
## Worker task template
```json