204 lines
10 KiB
Markdown
204 lines
10 KiB
Markdown
---
|
|||
|
|
name: land-batch
|
||
|
|
description: >
|
||
|
|
Use ONLY when the orchestrator lands the finished task branches of one DAG
|
||
|
|
iteration as a single batch PR ([org-internal #3731] batch-landing pilot). Composes
|
||
|
|
path-disjoint member branches via the deterministic batch-compose script,
|
||
|
|
pre-validates locally, opens ONE PR for N tasks (one CI fan-out instead of
|
||
|
|
N on the single-runner pool), pastes each member's review synthesis into
|
||
|
|
the PR conversation, runs one delta-scoped review-code on the batch head,
|
||
|
|
then hands the labeled PR to the merge coordinator. NOT for hotfixes /
|
||
|
|
CI-infra fixes (fast lane: 1:1 PR), single tasks, or single-session
|
||
|
|
mono-branch batches (topology A — one session owns every ticket on one
|
||
|
|
branch from inception; hand-compose those, see Applicability).
|
||
|
|
stage: merge-pr
|
||
|
|
triggers:
|
||
|
|
- land batch
|
||
|
|
- batch land
|
||
|
|
- batch-compose
|
||
|
|
- 批量归并
|
||
|
|
- 批量提交 pr
|
||
|
|
role: Producer
|
||
|
|
---
|
||
|
|
|
||
|
|
# land-batch — batch PR composition for one DAG iteration
|
||
|
|
|
||
|
|
**Orchestrator-only.** The composing session is the epic's orchestrator
|
||
|
|
(single writer at iteration close — the same role that already owns uniform
|
||
|
|
merge/close). `workflow/batch/*` branches are NOT claim branches: no
|
||
|
|
`claim.sh` interaction, no assignee; the orchestrator is the only composer.
|
||
|
|
Pilot charter: the decision comment on [org-internal #3731] (scope, opt-in protocol, cap,
|
||
|
|
success criteria) — this skill implements that charter, not the epic's
|
||
|
|
frozen DAG v1 (which covers only the merge-coordinator nodes N-01..04).
|
||
|
|
|
||
|
|
## Applicability: post-hoc composition only (topology B)
|
||
|
|
|
||
|
|
Two batch shapes exist ([org-internal #3731] charter addendum, comment 39347):
|
||
|
|
|
||
|
|
- **Topology A — single-session mono-branch batch**: one session owns every
|
||
|
|
ticket from inception and writes them on ONE branch (instance: PR [org-internal #3861],
|
||
|
|
audit-h1). Hand composition is natural there — do NOT force this skill on
|
||
|
|
it; record the outcome on [org-internal #3731] as a manual-batch datum instead.
|
||
|
|
- **Topology B — parallel workers, post-hoc composition**: workers finish on
|
||
|
|
SEPARATE branches at different times; the orchestrator composes at
|
||
|
|
iteration close. This is the skill's ONLY applicability — cross-branch
|
||
|
|
conflict attribution, lockfile convergence, and composition ordering are
|
||
|
|
exactly the error-prone parts it mechanizes (path-disjoint admission,
|
||
|
|
fail-closed attribution, `--convergent` surgery, per-member commit
|
||
|
|
retention for revert granularity).
|
||
|
|
|
||
|
|
Quick discriminator: if every "member" would be a commit on a branch you are
|
||
|
|
already writing, that is topology A — keep going, skip this skill. If you
|
||
|
|
are landing OTHER workers' branches you never owned, that is topology B —
|
||
|
|
this skill applies.
|
||
|
|
|
||
|
|
## Preconditions (all mandatory, fail-closed)
|
||
|
|
|
||
|
|
1. **Pilot opt-in**: the epic runs in batch-landing mode — its workers were
|
||
|
|
seeded to report `status=done branch=<ref>` WITHOUT opening PRs (see the
|
||
|
|
pilot note in `headless-session-ops/reference/completion-report.md`).
|
||
|
|
Non-pilot epics keep the per-task 1:1 PR flow.
|
||
|
|
2. **Every member is done**: review-code PASS on its own branch, run bundle
|
||
|
|
archived (`gen-run-bundle.ts --slug <slug> --ticket <N>` — the bundle is
|
||
|
|
committed on the member branch, so composition carries it; the
|
||
|
|
merge-gate's presence check is per close-ref and expects it).
|
||
|
|
3. **2..7 members** — a single done task rides its own 1:1 PR; more than 7
|
||
|
|
(`batch-compose --max-members`, the pilot cap) splits into multiple
|
||
|
|
batches along the path-overlap report.
|
||
|
|
4. **No fast-lane cargo**: prod hotfixes and CI-infra fixes bypass batching
|
||
|
|
entirely (1:1 PR, land immediately) — batching latency must never gate
|
||
|
|
urgent work.
|
||
|
|
|
||
|
|
## Procedure
|
||
|
|
|
||
|
|
### 1. Compose (deterministic core)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
bun .gitea/scripts/batch-compose.ts \
|
||
|
|
--branch workflow/task/<a> --branch workflow/task/<b> … \
|
||
|
|
--out workflow/batch/<epic-slug>-iter<N> --dry-run
|
||
|
|
```
|
||
|
|
|
||
|
|
For batches whose members ALL rewrite a regenerable shared file — the
|
||
|
|
rule, not the exception, for dependency-bump batches (every `bun install`
|
||
|
|
rewrites the root `bun.lock`) — pass `--convergent bun.lock`: the file is
|
||
|
|
exempt from the overlap admission, and a merge that conflicts ONLY on it is
|
||
|
|
surgically resolved to the running head's version (dropped from the index
|
||
|
|
outright when the head no longer carries the path), with the JSON
|
||
|
|
reporting `converged: ["bun.lock"]` and a regeneration hint. A conflict
|
||
|
|
report carrying shapes the tool cannot attribute (rename/delete etc.)
|
||
|
|
fails CLOSED — convergent exemption never widens into a blind pass.
|
||
|
|
|
||
|
|
Exit codes / handling: `1 usage / no-changes-over-base` → drop the named
|
||
|
|
member or fix the invocation; `3 path-overlap` → split the batch along the
|
||
|
|
reported pairs (or land the overlapping member 1:1) and re-run per group;
|
||
|
|
`4 merge conflict` / `5 transport/git error` → fall
|
||
|
|
back to 1:1 for this batch; `0` → drop `--dry-run`
|
||
|
|
and push the composed branch. The script never touches the worktree or
|
||
|
|
index (keeper-style plumbing) and each member keeps its original commits —
|
||
|
|
revert granularity stays per-task.
|
||
|
|
|
||
|
|
### 2. Pre-validate locally (never enter CI red)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git fetch origin workflow/batch/<slug>
|
||
|
|
git switch --detach origin/workflow/batch/<slug>
|
||
|
|
cd packages/octopus && bun run test:changed
|
||
|
|
cd ../.. && git switch - # back to the orchestrator branch
|
||
|
|
```
|
||
|
|
|
||
|
|
If the compose output carried a `regenerate convergent files (…)` hint —
|
||
|
|
regenerate them FIRST, on the detached batch branch, and push the result
|
||
|
|
(the composed branch deliberately carries a stale convergent file: either
|
||
|
|
the surgically-resolved running-head version, or a member's clean-merged
|
||
|
|
one; either way it is deterministic only after regeneration):
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git switch --detach origin/workflow/batch/<slug>
|
||
|
|
bun install # regenerates bun.lock
|
||
|
|
git add bun.lock # NEVER `commit -am` here: -a stages the
|
||
|
|
# worktree's symlink stubs and trips the
|
||
|
|
# pre-commit symlink guard (core.symlinks=false)
|
||
|
|
git commit -m "[<epic-slug>][iter-N] chore(deps): regenerate bun.lock for batch <slug>"
|
||
|
|
git push origin HEAD:refs/heads/workflow/batch/<slug>
|
||
|
|
```
|
||
|
|
|
||
|
|
(On detached HEAD the commit-msg hook's format check is inert — the
|
||
|
|
`[slug][iter-N]` prefix above keeps the corpus contract anyway.)
|
||
|
|
|
||
|
|
Failure → fix on the offending member branch (it is the only place the
|
||
|
|
fault can live — paths are disjoint), re-compose, re-validate. Do NOT push
|
||
|
|
a known-red batch.
|
||
|
|
|
||
|
|
### 3. Open the batch PR (gitea-rest recipes)
|
||
|
|
|
||
|
|
Title: `[<epic-slug>][iter-N] batch: <task-a> / <task-b> / …`
|
||
|
|
|
||
|
|
Body constraints (each is a merge-gate requirement, not style):
|
||
|
|
|
||
|
|
- **English close keywords, one per member issue**: `Closes #A`, `Closes #B`,
|
||
|
|
… — Gitea auto-closes all of them; Chinese 关闭/修复 never auto-close.
|
||
|
|
- **Zero unchecked top-level `- [ ]` lines in the body** — the gate's regex
|
||
|
|
(`^-\s+\[ \]`) blocks on any unindented unchecked box, with no fence
|
||
|
|
stripping. Render deferred/pending items as
|
||
|
|
plain text (`· deferred: …`), never as checkboxes; the per-task live
|
||
|
|
checklists stay on their issues.
|
||
|
|
- Per-member summary (ticket, branch, one line) + bundle index.
|
||
|
|
|
||
|
|
### 4. Paste member syntheses into the PR conversation
|
||
|
|
|
||
|
|
Post one comment per member carrying its review-code verdict block
|
||
|
|
(`**Verdict**: PASS|WARN` + the fenced JSON synthesis, from the member
|
||
|
|
issue's review thread). **Why**: the merge-gate hunts FAIL verdicts only in
|
||
|
|
the PR's own comment stream — a batch PR without these comments passes
|
||
|
|
vacuously (a fake-green hole). Pasting restores FAIL visibility; a member
|
||
|
|
synthesis of FAIL blocks the gate exactly as a 1:1 PR's would.
|
||
|
|
|
||
|
|
### 5. One review-code pass on the batch head
|
||
|
|
|
||
|
|
Run review-code against the batch PR. Members already PASS'd individually —
|
||
|
|
scope the review delta to what composition added (merge seams, cross-member
|
||
|
|
integration the per-task reviews could not see). This posts the required
|
||
|
|
per-sha `pipeline/review-code` status; member statuses do NOT transfer to
|
||
|
|
the composed head.
|
||
|
|
|
||
|
|
### 6. Label and hand off
|
||
|
|
|
||
|
|
Label the PR `ready-to-merge`. From here the flow is stock: the merge
|
||
|
|
coordinator's double-green queue serially merges (CI/check +
|
||
|
|
pipeline/merge-gate on the batch head, bot approve, main-account merge).
|
||
|
|
Do NOT hand-merge, do NOT hand-sync main into the batch branch.
|
||
|
|
|
||
|
|
### 7. Post-merge expectations
|
||
|
|
|
||
|
|
Member issues auto-close via the English keywords; their `## 当前状态`
|
||
|
|
PR/代码评审/CI rows fan out to EVERY close ref (status-sync poller Wide
|
||
|
|
behavior — requires [org-internal #3867], merged 2026-08-31). `task done ≡ PR merged`
|
||
|
|
still holds, transitively
|
||
|
|
through the batch PR; verify milestones fire unchanged.
|
||
|
|
|
||
|
|
## Fallback matrix
|
||
|
|
|
||
|
|
| Failure | Action |
|
||
|
|
| ---------------------------- | ------------------------------------------------------------------------------------- |
|
||
|
|
| path-overlap (exit 3) | split batch along overlap pairs / 1:1 for the odd member |
|
||
|
|
| merge conflict (exit 4) | 1:1 for the whole batch |
|
||
|
|
| local pre-validation red | fix on the offending member, re-compose |
|
||
|
|
| batch CI red after mergeable | coordinator dequeues with trail; fix on member branches, re-compose as a new batch PR |
|
||
|
|
|
||
|
|
Any fallback preserves today's behavior — batch landing is an optimization,
|
||
|
|
never a dependency.
|
||
|
|
|
||
|
|
## Measurement (pilot duty — revised per [org-internal #3731] charter addendum 39347)
|
||
|
|
|
||
|
|
Per-iteration attribution only; the global 7-day ratio is trend context,
|
||
|
|
never a pass/fail judge (same-window confounders: test-slim [org-internal #3855] /
|
||
|
|
test-dedupe [org-internal #3854] / the pilot's own PRs). On the server,
|
||
|
|
`bun .gitea/scripts/ci-run-stats.ts --days 7 --json /tmp/<slug>-ci-stats.json`
|
||
|
|
(the `--json` flag takes a filename — bare, it is a silent no-op; the human
|
||
|
|
summary still prints). Same methodology as the checklist-merge-gate.yml
|
||
|
|
2026-08-27 note. Record on [org-internal #3731]: (a) this batch iteration's per-workflow
|
||
|
|
trigger counts vs a historically similar 1:1 iteration, and (b) one
|
||
|
|
fallback drill (construct a path-overlap group → exit-3 split executed).
|
||
|
|
Baseline, trend only: 2026-08-31, 5,081 runs / TOTAL 2.75x single runner.
|