v0.1.5: standalone-readability pass — delink --strip provenance mode ([TT-NNNN]), Tier-2 dangling refs neutralized, verify/review-code giant skills split into reference/ on-demand files; guards now runtime-neutral (bun|node)

This commit is contained in:
2026-09-25 00:09:37 +08:00
parent d6567b1842
commit 88204a90c8
74 changed files with 1417 additions and 1358 deletions
+58 -58
View File
@@ -33,7 +33,7 @@ triggers:
- launch session for issue
- launch session for ticket
- spawn session for ticket
# English — patrol mode (delayed self-wake polling loops, [org-internal #3937]).
# English — patrol mode (delayed self-wake polling loops, [TT-3937]).
- patrol loop
- patrol mode
- delayed wake
@@ -42,28 +42,28 @@ triggers:
- 拉起会话
- 自治会话
- 无头会话
# Chinese — patrol mode ([org-internal #3937]).
# Chinese — patrol mode ([TT-3937]).
- 巡检模式
- 延迟唤醒
- 会话不生成
- 会话卡死
- 为工单拉起会话
- 工单拉起会话
# English — lifecycle (hung rescue, stale-worldview correction, [org-internal #2459]).
# English — lifecycle (hung rescue, stale-worldview correction, [TT-2459]).
- abort session
- revive session
- session rescue
- fact baseline
# English — quota-wall recovery ([org-internal #3669]): worker killed by provider usage limit.
# English — quota-wall recovery ([TT-3669]): worker killed by provider usage limit.
- quota wall
- usage limit reached
- quota exhausted
- switch provider
# Chinese — lifecycle ops ([org-internal #2459]).
# Chinese — lifecycle ops ([TT-2459]).
- 唤醒会话
- 会话挂死
- 事实基线
# Chinese — quota-wall recovery ([org-internal #3669]).
# Chinese — quota-wall recovery ([TT-3669]).
- 配额墙
- 配额耗尽
- 用量上限
@@ -89,7 +89,7 @@ and worktree discipline are **orthogonal**: this skill only opens a session
record pointed at a directory; whether the launched session later creates its
own worktree is a _workflow_ decision (per the worktree-discipline rule) it
makes when it starts actual work — not something the launcher does. One
exception: the **ticket-driven recipe** below, where claim-first ([org-internal #2297])
exception: the **ticket-driven recipe** below, where claim-first ([TT-2297])
requires the launcher to push the workflow branch before launch, making a
pre-built worktree the claim carrier + ready workspace (see Pitfall #10
carve-out). So, unless a different repo / path is specifically targeted,
@@ -122,7 +122,7 @@ carve-out). So, unless a different repo / path is specifically targeted,
seed prompt, not the directory field (sidebar visibility, see bullet 1). The
owner-check (`GET /session?directory=$DIR`) is scoped per directory, so
rooting the session at the correct code checkout is also what makes the
duplicate-session guard meaningful. Server-side backstop ([org-internal #3190]): creating a
duplicate-session guard meaningful. Server-side backstop ([TT-3190]): creating a
session whose directory IS a linked git worktree answers with an
`X-Session-Directory-Warning` response header and a server warn log naming
the owning main repo — if you see that header, the session will be invisible
@@ -131,8 +131,8 @@ carve-out). So, unless a different repo / path is specifically targeted,
> **Authoritative reference:** `rules/headless-session-ops` wiki page
> (<<instance-base-url>/Octopus/octopus/wiki/rules%2Fheadless-session-ops>).
> This skill is its runtime carrier. Provenance: issue **[org-internal #1695]** (which
> corrected the misdiagnosed [org-internal #1691], see comment 9205). Code citations below were
> This skill is its runtime carrier. Provenance: issue **[TT-1695]** (which
> corrected the misdiagnosed [TT-1691], see comment 9205). Code citations below were
> verified against `<harness-package>` at HEAD.
## The `model` field — always provide a known-good model (read this first)
@@ -166,7 +166,7 @@ section). Two reasons:
`handlers/session.ts`) and never reaches the HTTP
caller — so the session looks healthy (HTTP 204, user message persisted) yet
**never generates**. This is exactly the "silent stall" observed empirically
in [org-internal #1695]: a session with a 1-message transcript (user only) and frozen
in [TT-1695]: a session with a 1-message transcript (user only) and frozen
`time.updated`.
| payload | result |
@@ -280,10 +280,10 @@ curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
queued behind a session that is generating with no live runner in this
process (wedge/zombie shape — stored and re-driven on restart drain or the
next runner), **202** when `delay_sec` defers delivery to an in-memory
delayed wake ([org-internal #3937] A — see "巡检模式 / Patrol mode" below), and **409**
delayed wake ([TT-3937] A — see "巡检模式 / Patrol mode" below), and **409**
when the session is paused (message NOT stored) or the maintenance-mode
hold queue is full.
- **Global route alternative ([org-internal #4307]):** when the client only knows the
- **Global route alternative ([TT-4307]):** when the client only knows the
sessionID (no `?directory=` routing), `POST /prompt_async` (no path prefix)
resolves the instance from the app database by the body's `sessionID` and
delegates to the same delivery core — same 204/202/409 semantics, same
@@ -294,17 +294,17 @@ curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
- `delay_sec` (optional, integer 0..86400, else 400) schedules the prompt for
a one-shot delayed self-wake: the server holds the prompt in memory and
fires it exactly once after `delay_sec` seconds, only for an idle session
at intake. **Precedence ([org-internal #3937] review):** `delay_sec` applies only when the
at intake. **Precedence ([TT-3937] review):** `delay_sec` applies only when the
intake would otherwise start processing now (immediate class). For the
queued-behind-wedge 202 member and under maintenance queue mode it is
stripped — retention semantics win (the server logs the drop; the delay is
NOT honored). Pending wakes are **in-memory only — a server restart abandons
them**; a patrol loop must therefore tolerate a missed wake and re-arm.
Never use `sleep <big>; curl ...` in a bash tool call to wait — that blocks
the turn with zero output (the [org-internal #3937] incident shape); the bash tool now
warns on bare sleeps > 120s (long-sleep guard, [org-internal #3937] C).
the turn with zero output (the [TT-3937] incident shape); the bash tool now
warns on bare sleeps > 120s (long-sleep guard, [TT-3937] C).
- `model` is `{ providerID, modelID }` or the shorthand string
`"providerID/modelID"` (split on the FIRST `/`; accepted since [org-internal #4307] —
`"providerID/modelID"` (split on the FIRST `/`; accepted since [TT-4307] —
e.g. `"zai-coding-plan/glm-5.2"`). **Do not omit it.** Fill the values from
your own session's model (see "Reuse the current session's model" above) —
never hardcode a specific model. A string without a `/` is rejected with
@@ -335,7 +335,7 @@ curl -s "$BASE/session/$SID/message?directory=$DIR&limit=1" \
Verdict: `latest_role == "assistant"` → the first generation completed (the
seed is `user`; the newest message flips to `assistant` only once the model
answered — an errored turn also persists an assistant message carrying the ⚠️
usage-limit part, [org-internal #2912], so check the tail text before declaring success).
usage-limit part, [TT-2912], so check the tail text before declaring success).
Stays `user` across 2–3 polls spaced 10–30 s → **apparent silent stall**;
almost certainly the model did not resolve to a usable one (fallback landed
badly, or you omitted `model`). Redeliver Step 2 with an explicit known-good
@@ -351,33 +351,33 @@ just never omit `limit`.
The common case: an agent (or cron/CI) spins up a fresh main session to work a
tracked issue — seed prompt composed **from the issue body**, launched on the
current session's model. **Step-0 owner check is MANDATORY ([org-internal #1803])**: four data
current session's model. **Step-0 owner check is MANDATORY ([TT-1803])**: four data
sources (session-title scan, assignee/claim, open PRs, remote branches); any
live claim → ABORT the launch (`session-scope-guard.md` points here for that
pre-step). Full recipe — owner-check scan script with `X-Total-Count` /
`X-Has-More` pagination handling ([org-internal #3190]), claim-first atomic 3-step ([org-internal #2297],
+ one-command provisioning via `script/claim-provision.sh`, [org-internal #3642]),
seed-prompt MUSTs, backend duplicate-ticket hard guard ([org-internal #1989], on by default
[org-internal #2350]), launch + poll commands: `reference/ticket-recipe.md` (read BEFORE
`X-Has-More` pagination handling ([TT-3190]), claim-first atomic 3-step ([TT-2297],
+ one-command provisioning via `script/claim-provision.sh`, [TT-3642]),
seed-prompt MUSTs, backend duplicate-ticket hard guard ([TT-1989], on by default
[TT-2350]), launch + poll commands: `reference/ticket-recipe.md` (read BEFORE
creating any ticket-driven session).
## Completion-report protocol (完成回报协议, [org-internal #2374])
## Completion-report protocol (完成回报协议, [TT-2374])
`prompt_async` is fire-and-forget **for the launcher too**: without this
protocol the orchestrator has NO push channel and must poll forever ([org-internal #2366]).
protocol the orchestrator has NO push channel and must poll forever ([TT-2366]).
The worker actively reports `status=done|blocked|handoff` back into the
orchestrator's session via `prompt_async`, with an issue-comment fallback.
Default report format is `branch=<ref>`-based — workers never open PRs
(TD-678/[org-internal #4425]); `pr=#N` appears only on `uncoordinated` self-opens.
(TD-678/[TT-4425]); `pr=#N` appears only on `uncoordinated` self-opens.
Hard constraints: a headless worker MUST NOT call the `question` tool
([org-internal #2378] — directional/irreversible decisions are `status=blocked` reports,
([TT-2378] — directional/irreversible decisions are `status=blocked` reports,
then STOP), and MUST NOT self-merge its PR / self-close the issue / touch
`main` ([org-internal #2386] — keep the PR open; the orchestrator merges and closes). Full
`main` ([TT-2386] — keep the PR open; the orchestrator merges and closes). Full
protocol — orchestrator-side seed block (verbatim template), worker-side
steps, decision-authority layering: `reference/completion-report.md` (read
BEFORE composing a seed prompt for any worker you need to hear back from).
## Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([org-internal #2459])
## Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([TT-2459])
Launching is half the job — a headless worker can hang silently or wake with
a stale worldview. Health check = two polls 30–60 s apart (`.state` + `progress`
@@ -386,19 +386,19 @@ samples = hung → abort FIRST (`POST /session/:id/abort`), then re-wake with a
known-good model + the anti-re-hang clause. Stale worldview → fact-baseline
injection (only the CURRENT authority injects; if YOU might be the stale one,
verify identity against the durable record first). Full runbooks — verdict
table + [org-internal #3215] dual-sample rule, abort + re-wake sequence, fact-baseline
table + [TT-3215] dual-sample rule, abort + re-wake sequence, fact-baseline
structure, authorization asymmetry: `reference/lifecycle-ops.md` (read when
managing a launched session). Quota wall — the worker's turn killed by a
terminal usage-limit error (looks like silent completion: no report, ticket
stalled) — has its own runbook: detection signals (finish=error ⚠️ tail,
provider quota markers), provider-switch re-wake, fact-baseline template,
pre-dispatch provider check: `reference/quota-wall-recovery.md` ([org-internal #3669], evidence
[org-internal #3627]; read when a worker goes silent mid-task).
pre-dispatch provider check: `reference/quota-wall-recovery.md` ([TT-3669], evidence
[TT-3627]; read when a worker goes silent mid-task).
## 巡检模式 / Patrol mode (delayed self-wake, [org-internal #3937])
## 巡检模式 / Patrol mode (delayed self-wake, [TT-3937])
A patrol loop periodically nudges a headless session without holding a
connection or burning a turn on a blocking sleep. Since [org-internal #3937] A the server
connection or burning a turn on a blocking sleep. Since [TT-3937] A the server
supports this natively via `delay_sec` on `prompt_async`.
```sh
@@ -427,16 +427,16 @@ Rules of the road:
- **Restart abandons pending wakes.** They are in-memory server fibers — no
persistence, no drain. After any server restart the patrol driver owns
re-arming the next wake; treat a missed wake as expected, not as an error.
- **Latest wake wins, per session ([org-internal #4069]).** Arming `delay_sec` on a session
- **Latest wake wins, per session ([TT-4069]).** Arming `delay_sec` on a session
that already has a pending wake SUPERSEDES it: the previous countdown is
cancelled, only the latest wake fires. Re-arming each turn is the intended
patrol pattern — a superseded wake never delivers its prompt and publishes
no error. (Pre-[org-internal #4069] stacks both FIFO — on old binaries, re-arm only after
no error. (Pre-[TT-4069] stacks both FIFO — on old binaries, re-arm only after
the previous wake fired.)
- **Never emulate a delay with the bash tool.** `sleep 900; curl ...` blocks
the turn with zero output and gets manually aborted as a hang (the [org-internal #3937]
the turn with zero output and gets manually aborted as a hang (the [TT-3937]
incident). The bash tool now prepends a `<shell_warning>` on bare sleeps
beyond 120s ([org-internal #3937] C) — treat that warning as a redirect to this section.
beyond 120s ([TT-3937] C) — treat that warning as a redirect to this section.
## Do NOT use this skill when
@@ -461,15 +461,15 @@ Rules of the road:
| 6 | Using synchronous `prompt` for headless launch | `prompt` blocks until done — wrong for fire-and-forget; use `prompt_async` |
| 7 | Shell-escaping errors in a multi-line seed prompt | Build the payload with `jq -n --rawfile` from a file |
| 8 | Verifying models via `GET /model` — no such endpoint (404) | Use `GET /provider` → `.connected` (provider live check); `list-models` tool also works. `GET /config/providers` lists _configured_ models only |
| 9 | Launching a session for a ticket another live session already owns → conflicting diffs, duplicate work ([org-internal #1744]/[org-internal #1753]) | Run the Step-0 owner check (`GET /session` title scan + worktree/branch/PR scan); ABORT and report on conflict |
| 10 | Creating a worktree / fresh checkout for the new session (unnecessary `bun install` + `.codegraph/` rebuild cost) | Don't — for _general_ launches: a session is a conversation pointed at a directory, so reuse the current workspace; worktree creation is a separate _workflow_ decision the launched session makes if/when it starts a workflow. **Ticket-recipe carve-out ([org-internal #2297]):** the ticket's work happens in a dedicated worktree anyway (worktree discipline), and claim-first pushes the workflow branch before launch — so the launcher pre-builds the worktree as the claim carrier + ready workspace, and the session reuses it (cost is front-loaded, not wasted). The worktree path goes in the claim comment + seed prompt; `?directory=` stays on the creator's workspace |
| 11 | Launching a worker and never hearing back — orchestrator polls a few times, stops, loses visibility ([org-internal #2374]) | Append the completion-report block to the seed prompt (Completion-report protocol section): worker `prompt_async`s a one-line status back into the orchestrator session on done/blocked/handoff, with issue-comment fallback |
| 12 | Headless worker calls the `question` tool → no attached client: the question deadlocks in a queue, or `auto_approve` silently auto-approves a direction-setting decision ([org-internal #2378]) | NEVER call `question` from a headless worker. Decision boundary (Completion-report protocol section): directional/irreversible decision → `status=blocked` report + one-line decision point, then STOP; the orchestrator is the sole human-decision entry point |
| 13 | Headless worker self-merges its PR / self-closes the issue once the PR looks ready, despite a seed instruction to keep it open ([org-internal #2386]; N-02 [org-internal #2367] / N-03 [org-internal #2368], commits `d1565c99` / `040f21b7`) | Self-merge / self-close / touching `main` are irreversible directional actions → blocked by the Decision boundary. PR ready → keep it OPEN, report `status=done branch=<ref>`; the orchestrator opens/admits the PR, merges and closes uniformly |
| 9 | Launching a session for a ticket another live session already owns → conflicting diffs, duplicate work ([TT-1744]/[TT-1753]) | Run the Step-0 owner check (`GET /session` title scan + worktree/branch/PR scan); ABORT and report on conflict |
| 10 | Creating a worktree / fresh checkout for the new session (unnecessary `bun install` + `.codegraph/` rebuild cost) | Don't — for _general_ launches: a session is a conversation pointed at a directory, so reuse the current workspace; worktree creation is a separate _workflow_ decision the launched session makes if/when it starts a workflow. **Ticket-recipe carve-out ([TT-2297]):** the ticket's work happens in a dedicated worktree anyway (worktree discipline), and claim-first pushes the workflow branch before launch — so the launcher pre-builds the worktree as the claim carrier + ready workspace, and the session reuses it (cost is front-loaded, not wasted). The worktree path goes in the claim comment + seed prompt; `?directory=` stays on the creator's workspace |
| 11 | Launching a worker and never hearing back — orchestrator polls a few times, stops, loses visibility ([TT-2374]) | Append the completion-report block to the seed prompt (Completion-report protocol section): worker `prompt_async`s a one-line status back into the orchestrator session on done/blocked/handoff, with issue-comment fallback |
| 12 | Headless worker calls the `question` tool → no attached client: the question deadlocks in a queue, or `auto_approve` silently auto-approves a direction-setting decision ([TT-2378]) | NEVER call `question` from a headless worker. Decision boundary (Completion-report protocol section): directional/irreversible decision → `status=blocked` report + one-line decision point, then STOP; the orchestrator is the sole human-decision entry point |
| 13 | Headless worker self-merges its PR / self-closes the issue once the PR looks ready, despite a seed instruction to keep it open ([TT-2386]; N-02 [TT-2367] / N-03 [TT-2368], commits `d1565c99` / `040f21b7`) | Self-merge / self-close / touching `main` are irreversible directional actions → blocked by the Decision boundary. PR ready → keep it OPEN, report `status=done branch=<ref>`; the orchestrator opens/admits the PR, merges and closes uniformly |
| 14 | Trusting `state=generating` as "alive" — a hung stream goes unrescued (session A, 2026-08-16: `generating` 6+ min, count frozen at 478) | Poll state AND message count twice 30–60 s apart; frozen count = hung → abort + re-wake (Post-launch lifecycle section) |
| 15 | Re-sending a wake prompt to a hung session and waiting — queued prompts are never consumed while the stream is dead | Abort FIRST (`POST /session/:id/abort` → 200, verify `idle`), THEN re-send; a prompt queued before the abort may still never fire — always re-deliver |
| 16 | A worker woken from restart / long idle acts on its stale worldview — re-dispatches superseded work or claims authority it no longer holds (2026-08-16: revived worker re-ran an already-published FAIL verify; ops session announced an inherited "orchestrator" takeover) | Fact-baseline injection (Post-launch lifecycle section): authoritative facts + invalidated assumptions + one concrete re-assignment. Only the CURRENT authority injects; a session that suspects IT is stale verifies identity against the durable record first (`rules/compact.md` identity clause) |
| 17 | Worker goes silent mid-task: provider quota wall killed the turn (usage limit / 429) — no completion-report, no blocked report; from the issue side indistinguishable from silent completion (2026-08-29 [org-internal #3627]: opencode-go `weekly usage limit reached` after the 13:18 Round-1 FAIL; ticket stalled until manually re-woken) | Detect via tail probe (`finish=error` + ⚠️ usage-limit part, `metadata.reason` in the quota family) + `GET /provider` quota markers; re-wake on a known-good provider with the fact-baseline template — `reference/quota-wall-recovery.md` ([org-internal #3669]). Prevent: pre-dispatch provider check + the seed's 配额自报 clause (`reference/completion-report.md`, [org-internal #3669]) — quota exhaustion is a `status=blocked reason=quota-exhausted` report, not a silent death |
| 17 | Worker goes silent mid-task: provider quota wall killed the turn (usage limit / 429) — no completion-report, no blocked report; from the issue side indistinguishable from silent completion (2026-08-29 [TT-3627]: opencode-go `weekly usage limit reached` after the 13:18 Round-1 FAIL; ticket stalled until manually re-woken) | Detect via tail probe (`finish=error` + ⚠️ usage-limit part, `metadata.reason` in the quota family) + `GET /provider` quota markers; re-wake on a known-good provider with the fact-baseline template — `reference/quota-wall-recovery.md` ([TT-3669]). Prevent: pre-dispatch provider check + the seed's 配额自报 clause (`reference/completion-report.md`, [TT-3669]) — quota exhaustion is a `status=blocked reason=quota-exhausted` report, not a silent death |
## Known limitation (separate from this workaround)
@@ -482,9 +482,9 @@ catches that cause (`forkPromptDelivery` in `handlers/session.ts`) so the HTTP
caller never sees it. The proper fix is to surface the error (or reject unusable
models at the boundary) so a stalled session is diagnosable. Until then, **always
provide an explicit known-good `model`** — this skill is the documented
workaround (see [org-internal #1695] "遗留 minor").
workaround (see [TT-1695] "遗留 minor").
**Update ([org-internal #2912], 2026-08-20)**: usage-limit turn failures (HTTP 429 /
**Update ([TT-2912], 2026-08-20)**: usage-limit turn failures (HTTP 429 /
FreeUsageLimitError / GoUsageLimitError) are no longer silent shells. When such
a turn terminates (retry schedule ends or the spin is aborted), the errored
assistant message persists `finish=error` plus a human-readable ⚠️ text part
@@ -494,23 +494,23 @@ the web UI like any assistant text. Pitfall #14's double-poll is still the
liveness check while a 429 retry spin is IN progress (the spin itself retries
with backoff until interrupted); the explicit error part only appears once the
turn ends. The general async-path observability gap above remains tracked by
[org-internal #1695].
[TT-1695].
## See also
- Source issue / full manual: **[org-internal #1695]** (this skill adds the ticket-driven recipe
- Source issue / full manual: **[TT-1695]** (this skill adds the ticket-driven recipe
- current-model reuse, and corrects the endpoint/claim against current code).
- Completion-report protocol: **[org-internal #2374]** (worker-side active report back to the
- Completion-report protocol: **[TT-2374]** (worker-side active report back to the
launching orchestrator session; fallback to issue comment).
- Quota-wall recovery recipe: **[org-internal #3669]** (evidence instance [org-internal #3627]) — detect a
- Quota-wall recovery recipe: **[TT-3669]** (evidence instance [TT-3627]) — detect a
worker killed by a provider usage limit, re-wake on a known-good provider,
pre-dispatch prevention.
- Decision boundary: **[org-internal #2378]** (headless worker MUST NOT call `question`;
- Decision boundary: **[TT-2378]** (headless worker MUST NOT call `question`;
directional decisions go through `status=blocked` reports to the
orchestrator) and **[org-internal #2386]** (the worker MUST NOT self-merge its PR,
orchestrator) and **[TT-2386]** (the worker MUST NOT self-merge its PR,
self-close the issue, or touch `main` — the orchestrator merges and closes
uniformly).
- Correction of the prior misdiagnosis: **[org-internal #1691]** (closed Invalid), comment 9205.
- Correction of the prior misdiagnosis: **[TT-1691]** (closed Invalid), comment 9205.
- Synchronous client always sends `model`:
`packages/app/src/components/prompt-input/submit.ts:158-165`.
- Code citations: `<harness-package>/src/session/prompt.ts:858,804-822,1399`,
@@ -525,15 +525,15 @@ turn ends. The general async-path observability gap above remains tracked by
specific ticket (#N)`): read BEFORE creating any session for issue #N —
Step-0 owner check, seed-prompt composition, launch + poll commands.
- `reference/completion-report.md` — completion-report protocol (完成回报协议,
[org-internal #2374]): read BEFORE composing a seed prompt for a worker you need to hear
[TT-2374]): read BEFORE composing a seed prompt for a worker you need to hear
back from — orchestrator block template, worker-side report steps, decision
boundary ([org-internal #2378] / [org-internal #2386]), quota self-report clause ([org-internal #3669]).
- `reference/lifecycle-ops.md` — post-launch lifecycle ([org-internal #2459]): read when
boundary ([TT-2378] / [TT-2386]), quota self-report clause ([TT-3669]).
- `reference/lifecycle-ops.md` — post-launch lifecycle ([TT-2459]): read when
managing a launched session — health-check polling, hung-stream diagnosis
(state=generating + frozen counters), abort + re-wake rescue,
fact-baseline injection, authorization asymmetry.
- `reference/quota-wall-recovery.md` — quota-wall recovery ([org-internal #3669], evidence
[org-internal #3627]): read when a worker goes silent mid-task (suspected provider usage
- `reference/quota-wall-recovery.md` — quota-wall recovery ([TT-3669], evidence
[TT-3627]): read when a worker goes silent mid-task (suspected provider usage
limit) or BEFORE dispatching a long-running worker — detection signals
(finish=error ⚠️ tail, provider quota markers, log grep patterns),
provider-switch re-wake + fact-baseline template, pre-dispatch provider
@@ -1,6 +1,6 @@
> Extracted from headless-session-ops/SKILL.md (Completion-report protocol (完成回报协议, [org-internal #2374])) — moved verbatim 2026-08-26, ticket [org-internal #3480].
> Extracted from headless-session-ops/SKILL.md (Completion-report protocol (完成回报协议, [TT-2374])) — moved verbatim 2026-08-26, ticket [TT-3480].
## Completion-report protocol (完成回报协议, [org-internal #2374])
## Completion-report protocol (完成回报协议, [TT-2374])
`prompt_async` is fire-and-forget **for the launcher too**: once the worker
session starts generating, the orchestrator receives NO event when it finishes
@@ -8,8 +8,8 @@ or blocks. The `task` tool auto-returns `task_result` for subagents; a
`prompt_async`-launched independent main session has no equivalent — the
orchestrator's only built-in channel is bare polling of
`GET /session/:id/message?limit=1` (tail probe — the no-`limit` form loads the
whole transcript server-side), which it eventually stops doing ([org-internal #2366]: worker ran
~1h past the orchestrator's last poll, merged PR [org-internal #2371] and closed [org-internal #2366] while
whole transcript server-side), which it eventually stops doing ([TT-2366]: worker ran
~1h past the orchestrator's last poll, merged PR [TT-2371] and closed [TT-2366] while
the orchestrator had zero visibility).
The protocol below closes that gap WITHOUT changing server semantics: the
@@ -24,21 +24,21 @@ parallel chunk work, any long-running delegated ticket), append a
completion-report block to the seed prompt. It MUST state:
- the orchestrator's **session id** (`$LAUNCHER_SID`) and **directory**;
- the report endpoint: `POST $BASE/session/$LAUNCHER_SID/prompt_async?directory=$DIR` (or, when the directory is unknown to the worker, the global route `POST $BASE/prompt_async` with the sessionID in the body — [org-internal #4307]);
- the report endpoint: `POST $BASE/session/$LAUNCHER_SID/prompt_async?directory=$DIR` (or, when the directory is unknown to the worker, the global route `POST $BASE/prompt_async` with the sessionID in the body — [TT-4307]);
- the exact report format (one-line, machine-greppable);
- the fallback (issue comment) if the report call fails.
Template (include verbatim in `seed.txt` when you are the launcher):
```text
## 完成回报协议 (mandatory, [org-internal #2374])
## 完成回报协议 (mandatory, [TT-2374])
你由编排会话 (orchestrator) 启动。完成、阻塞、或将工单移交他人时,必须向编排会话回报:
POST <BASE>/session/<LAUNCHER_SID>/prompt_async?directory=<DIR>
body: {"agent":"builder","model":{...},"parts":[{"type":"text","text":"<report>"}]}
<report> 格式(单行,machine-greppable):
[#<ticket>] [<node-or-slug>] status=done|blocked|handoff branch=<ref> [pr=#<N>] verify=<changed+typecheck> risk=<high|low> summary=<one line>
默认(TD-678 / [org-internal #4425]):worker 只推分支不开 PR——交付推送前置本地验证
默认(TD-678 / [TT-4425]):worker 只推分支不开 PR——交付推送前置本地验证
(<harness-package> 下 test:changed 全绿 + typecheck 0 error,verify 字段回执);
risk 按 risk-classifier 冻结表(HIGH_RISK_GLOBS)自报。PR 由编排按容量串行开
1:1(正文携 worker 报告),或 batch 模式 epic 经 land-batch 技能按迭代统一合成
@@ -46,15 +46,15 @@ risk 按 risk-classifier 冻结表(HIGH_RISK_GLOBS)自报。PR 由编排按
若回报调用失败(HTTP 非 2xx / 连接拒绝 — orchestrator 会话可能已结束或实例重启),
退而在源 issue(或父 Epic)发同内容评论作为兜底(fallback, 非主通道)。
回报是你的 mandatory step,与 issue-checklist-sync 并列;不得静默结束。
决策边界([org-internal #2378]):headless worker 禁止调用 `question` 工具 —— 遇方向性/不可逆
决策边界([TT-2378]):headless worker 禁止调用 `question` 工具 —— 遇方向性/不可逆
决策点 → `status=blocked` + 一行决策点描述后 STOP,勿调 question(无人应答死锁 /
被 auto_approve 静默自动批准)。
配额自报([org-internal #3669]):若收到配额/用量上限错误(usage limit / 429 / 402
配额自报([TT-3669]):若收到配额/用量上限错误(usage limit / 429 / 402
insufficient_quota),立即回报 status=blocked reason=quota-exhausted
summary=<provider 与 ⚠️ 部分中的重置提示>(branch=<ref> 必带)后停止
——勿自行换模型重试;换供应商重唤醒由编排者执行
(reference/quota-wall-recovery.md)。
PR 就绪后保持 open:不自行合并 PR、不关闭 issue、不动 main([org-internal #2386]);
PR 就绪后保持 open:不自行合并 PR、不关闭 issue、不动 main([TT-2386]);
合并与 issue 关闭由编排者统一执行。
```
@@ -73,7 +73,7 @@ final step (before idling):
done); `pr=#N` only for an `uncoordinated` self-open; use
`status=blocked` + the blocker description when stuck; use
`status=handoff` when deliberately transferring the ticket.
1. **Quota exhaustion is a blocked report, not a silent death ([org-internal #3669]).** A
1. **Quota exhaustion is a blocked report, not a silent death ([TT-3669]).** A
worker whose turn is killed by a usage-limit error (429 / 402 quota
family) cannot finish the turn — but per the seed's 配额自报 clause it
reports `status=blocked reason=quota-exhausted summary=<provider +
@@ -93,7 +93,7 @@ final step (before idling):
remains the stakeholder-facing record; the report is the
orchestrator-facing wake-up signal.
### Orchestrator side — serial PR admission on done reports ([org-internal #4425])
### Orchestrator side — serial PR admission on done reports ([TT-4425])
On a `status=done branch=<ref> verify=… risk=…` report:
@@ -103,16 +103,16 @@ On a `status=done branch=<ref> verify=… risk=…` report:
code; keep the queue shallow.
2. Open the 1:1 PR (title from the ticket node, body carrying the worker
report: branch / files / self-test / verify), apply the `Risk/*` label
from the report's `risk=` hint, merge via `script/pr-merge.sh` ([org-internal #3864]).
Batch-mode epics: one `land-batch` PR per iteration instead ([org-internal #3731]).
from the report's `risk=` hint, merge via `script/pr-merge.sh` ([TT-3864]).
Batch-mode epics: one `land-batch` PR per iteration instead ([TT-3731]).
3. A first red on the opened PR goes back through the report fallback
channel (issue comment) — the worker may already be idle; the issue
comment is the durable re-entry point.
Prose discipline on the orchestrator session only — no new daemon, no
mergeable-state automation (merge-coordinator retired, [org-internal #4385]).
mergeable-state automation (merge-coordinator retired, [TT-4385]).
### Decision boundary (决策边界, [org-internal #2378])
### Decision boundary (决策边界, [TT-2378])
The report channel defines how the worker *answers*; this subsection defines
the boundary of what it may *ask*. A headless worker session has NO attached
@@ -134,15 +134,15 @@ authority is layered instead:
point, then STOPs. The orchestrator is the sole human-decision entry point:
it interacts with the user, then replies to the worker (or acts on its
behalf).
- **No self-merge / no self-close / never touch main ([org-internal #2386])**: a headless
- **No self-merge / no self-close / never touch main ([TT-2386])**: a headless
worker MUST NOT merge its own PR, close the source issue, or rebase /
force-push `main`. These are irreversible directional actions of exactly
the class this boundary blocks — even when the seed prompt's instruction
to "keep the PR open" is absent or the worker judges the PR ready. When
the PR is ready the worker keeps it OPEN and reports
`status=done pr=#N` to the orchestrator; the orchestrator performs the
merge and the issue close uniformly. (Counter-example: N-02 [org-internal #2367] /
N-03 [org-internal #2368] self-merged their PRs — commits `d1565c99` / `040f21b7` —
merge and the issue close uniformly. (Counter-example: N-02 [TT-2367] /
N-03 [TT-2368] self-merged their PRs — commits `d1565c99` / `040f21b7` —
despite a seed instruction to keep them open.)
- **Local implementation decisions within the AC scope** (pattern choice,
helper extraction, test shape) → the worker decides autonomously and notes
@@ -150,7 +150,7 @@ authority is layered instead:
Rule of thumb: the worker's only way to "ask" is a `status=blocked` report.
A genuine ambiguity left by the seed prompt that would change scope is a
blocked report, NOT a `question` call. (Field sample: N-04 [org-internal #2369] — the
blocked report, NOT a `question` call. (Field sample: N-04 [TT-2369] — the
worker spontaneously did NOT merge its own PR and left merging to the
orchestrator; this subsection codifies that behavior.)
@@ -1,10 +1,10 @@
> Extracted from headless-session-ops/SKILL.md (Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([org-internal #2459])) — moved verbatim 2026-08-26, ticket [org-internal #3480].
> Extracted from headless-session-ops/SKILL.md (Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([TT-2459])) — moved verbatim 2026-08-26, ticket [TT-3480].
## Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([org-internal #2459])
## Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([TT-2459])
Launching is half the job — a headless worker can hang silently or wake up
with an outdated worldview. All three failure modes below were observed in
production on 2026-08-16 during Epic [org-internal #2459] (ops-restart window); the runbooks
production on 2026-08-16 during Epic [TT-2459] (ops-restart window); the runbooks
are extracted from that incident record.
### Health check — three states, one poll pattern
@@ -24,7 +24,7 @@ server-side:
| idle | `state=idle` AND `progress` absent-or-stale across both polls | turn ended — read the LAST message (same `limit=1` fetch): task unfinished → wake prompt; `status=done` report → harvest |
| **hung** | `state=generating` AND `progress.stepCount`/`lastStepAt` AND newest message id ALL frozen across both polls | the generation stream is dead; queued prompts will NEVER be consumed — rescue required |
`state` alone lies in BOTH directions ([org-internal #3215]): a hung session still reports
`state` alone lies in BOTH directions ([TT-3215]): a hung session still reports
`generating` (field sample: session A, 2026-08-16 — `state=generating` for 6+
minutes with the message count frozen at 478), and a healthy mid-step worker
can read `idle`/empty message tails in a single snapshot (2026-08-23 W3 wave:
@@ -32,7 +32,7 @@ can read `idle`/empty message tails in a single snapshot (2026-08-23 W3 wave:
discriminator; a single snapshot is NEVER an abort basis.**
`progress` (`stepCount` cumulative LLM-round counter, `lastStepAt` epoch-ms
heartbeat — [org-internal #3215]) moves on every round even when message tails are
heartbeat — [TT-3215]) moves on every round even when message tails are
transiently empty or `state` flickers; it is absent for sessions that never
ran since instance start (treat absent = no signal, fall back to count
deltas). `lastStepAt` freshness alone does NOT prove liveness (a long tool
@@ -77,7 +77,7 @@ claim authority it does not hold (field samples, 2026-08-16: a revived worker
re-ran an M-01 verify another session had already published as FAIL —
deduplicated via flag #comment-21590; an ops-notification session inherited an
"orchestrator" identity from a compaction summary and announced a takeover —
corrected via 勘误 #comment-21654/[org-internal #21668]).
corrected via 勘误 #comment-21654/[TT-21668]).
When you detect a stale-worldview session, do not wait for it to finish being
wrong — inject a fact-baseline prompt immediately (regular `prompt_async`),
@@ -1,6 +1,6 @@
> Added from headless-session-ops/SKILL.md (triggers `quota wall` / `配额墙`,
> Post-launch lifecycle section, pitfall #17, References) — new runbook
> 2026-08-29, ticket [org-internal #3669] (evidence instance [org-internal #3627]). Companion runbooks:
> 2026-08-29, ticket [TT-3669] (evidence instance [TT-3627]). Companion runbooks:
> `lifecycle-ops.md` (hung rescue, fact-baseline structure) and
> `completion-report.md` §"Orchestrator side" (the quota self-report clause);
> §Prevention is this file's own closing section.
@@ -14,15 +14,15 @@ it looks EXACTLY like silent completion: no completion-report, no
equals the orchestrator's polling interval, and without this runbook the
recovery was improvised.
**Evidence instance ([org-internal #3627], 2026-08-29)** — the shapes below are field-verified:
**Evidence instance ([TT-3627], 2026-08-29)** — the shapes below are field-verified:
|时刻 (+08)| 事件 |
|---|---|
| 08-29 13:18 | review-code Round-1 synthesis (FAIL) posted on [org-internal #3627]; worker session `ses_fb46e705bffe9kYbSoHayoJ1OQ` (builder, opencode-go model, branch `workflow/session/3627-wedge-promptasync`) entered its revision loop |
| 08-29 13:18 | review-code Round-1 synthesis (FAIL) posted on [TT-3627]; worker session `ses_fb46e705bffe9kYbSoHayoJ1OQ` (builder, opencode-go model, branch `workflow/session/3627-wedge-promptasync`) entered its revision loop |
| ~13:18–16:00 | worker hit the provider's `weekly usage limit reached` mid-revision — turn killed, **no** completion-report, **no** blocked report; issue side silent |
| detection | orchestrator noticed the `/session` state stall, then confirmed via the quota error in the session log (message tail carries the ⚠️ usage-limit part, [org-internal #2912]) |
| detection | orchestrator noticed the `/session` state stall, then confirmed via the quota error in the session log (message tail carries the ⚠️ usage-limit part, [TT-2912]) |
| recovery | re-wake via `prompt_async` on a known-good provider (`zhipuai-coding-plan`/glm-5.3 — since renamed `zai-coding-plan`) **plus a fact-baseline injection** (what happened / what is done / where to resume) |
| 16:26 / 16:51 | Round-2 synthesis PASS; `status=done pr=[org-internal #3675]` report received |
| 16:26 / 16:51 | Round-2 synthesis PASS; `status=done pr=[TT-3675]` report received |
## Detection — three signals, in this order
@@ -35,7 +35,7 @@ SID=<worker session id>
```
**Signal 1 — tail probe (primary; survives restarts, lives in the DB).** Since
[org-internal #2912]/[org-internal #3190] a quota-killed turn persists an assistant message with
[TT-2912]/[TT-3190] a quota-killed turn persists an assistant message with
`finish=error` plus a human-readable ⚠️ text part whose metadata names the
quota family. Probe the TAIL (`limit=1`, never the no-`limit` full transcript):
@@ -52,10 +52,10 @@ rate_limit}` (metadata also carries `provider`). The ⚠️ text is one of — a
strings observed in this repo's runtime (`session/retry.ts`):
- `` `weekly usage limit` reached. It will reset in … `` / `Go limit reached`
(GoUsageLimitError — the [org-internal #3627] shape; `weekly` is the body's `limitName`)
(GoUsageLimitError — the [TT-3627] shape; `weekly` is the body's `limitName`)
- `Free limit reached` (FreeUsageLimitError)
- `Account-level usage limit — switching API keys does not reset it. You can
continue after HH:mm.` ([org-internal #3407], zhipu/zai code 1308 — account-level)
continue after HH:mm.` ([TT-3407], zhipu/zai code 1308 — account-level)
- `Provider <id> rate limited (HTTP 429)` (generic 429)
- `Generation failed — …` with a 402 `insufficient_quota` body (balance, not
window — recovery is top-up, not provider switch)
@@ -73,9 +73,9 @@ curl -s "$BASE/session?directory=$DIR" | jq -r --arg s "$SID" \
30–60 s apart → the 429 retry spin is still running (it retries with backoff
until the schedule ends) OR the stream is hung — treat exactly like the hung
shape in `lifecycle-ops.md`: abort FIRST, then re-wake. A single snapshot is
never an abort basis ([org-internal #3215]).
never an abort basis ([TT-3215]).
**Signal 3 — provider-level confirmation ([org-internal #2911] quota markers).** `GET
**Signal 3 — provider-level confirmation ([TT-2911] quota markers).** `GET
/provider` overlays active usage-limit cooldowns, so the dead provider is
visible without reading logs:
@@ -86,7 +86,7 @@ date -d @$(($(curl -s "$BASE/provider?directory=$DIR" | jq '[.all[].quota.resetA
```
**Log grep (fallback; the dev log is restart-truncated).** Patterns actually
seen in the wild — [org-internal #3627] produced the first:
seen in the wild — [TT-3627] produced the first:
```sh
grep -E "usage limit reached|Go limit reached|Free limit reached|Account-level usage limit|GoUsageLimitError|FreeUsageLimitError|rate limited \(HTTP 429\)|insufficient_quota" \
@@ -96,7 +96,7 @@ grep -E "usage limit reached|Go limit reached|Free limit reached|Account-level u
```
The wall is only terminal after failover has exhausted the same-name pools
([org-internal #2911] failover); the `retrying … on …` WARNs above tell you the pool was
([TT-2911] failover); the `retrying … on …` WARNs above tell you the pool was
already draining before it died.
## Recovery — abort (only if needed), re-wake on a known-good provider, inject facts
@@ -130,7 +130,7 @@ curl -s "$BASE/provider?directory=$DIR" | jq -r \
# own current model — it is generating by definition).
```
([org-internal #3627]: opencode-go dead → re-woken on zhipuai-coding-plan/glm-5.3, since
([TT-3627]: opencode-go dead → re-woken on zhipuai-coding-plan/glm-5.3, since
renamed `zai-coding-plan`. Do NOT "wait for reset" as the default — weekly
windows mean days.)
@@ -159,9 +159,9 @@ curl -s "$BASE/provider?directory=$DIR" | jq -r \
**Step 3 — re-wake with a fact-baseline prompt.** Build the payload from a file
(`jq --rawfile`, SKILL.md pitfall #7), same wake shape as
`lifecycle-ops.md` but with the quota facts. The template also restates the
completion-report protocol ([org-internal #2374]) so the recovery is self-contained: it
completion-report protocol ([TT-2374]) so the recovery is self-contained: it
assumes the original seed still contains the protocol block — if you are
unsure it does (e.g. the worker was dispatched before [org-internal #3669] added the quota
unsure it does (e.g. the worker was dispatched before [TT-3669] added the quota
clause), the template's own block below carries it; fill
`<BASE>`/`<LAUNCHER_SID>`/`<DIR>` with the orchestrator session's values:
@@ -176,7 +176,7 @@ cat > wake.txt <<'EOF'
3. 从哪继续:<一件具体的下一步任务>。
4. 证据规则:逐条到引用来源(issue 评论 / PR / 工件)核实后再行动,
勿仅凭本基线行动。
## 完成回报协议(重申 [org-internal #2374]/[org-internal #3669];不确定原始 seed 是否含协议块时以本节为准)
## 完成回报协议(重申 [TT-2374]/[TT-3669];不确定原始 seed 是否含协议块时以本节为准)
完成、阻塞或移交时,必须回报编排会话(你的唤醒者):
POST <BASE>/session/<LAUNCHER_SID>/prompt_async?directory=<DIR>
body: {"agent":"builder","model":{...},"parts":[{"type":"text","text":"<report>"}]}
@@ -195,7 +195,7 @@ jq -n --rawfile p wake.txt '{agent:"builder",
Then poll the tail (`limit=1`) until an assistant message appears, and keep
expecting the completion-report per protocol — the re-woken worker owes you
`status=done|blocked` like any other (the restated block in the template
makes this hold even when the original seed predates [org-internal #3669]).
makes this hold even when the original seed predates [TT-3669]).
## Prevention — before dispatch, and in the seed
@@ -213,7 +213,7 @@ If the intended provider is already quota-marked, dispatch on a different one
**Quota self-report clause (turns a silent death into a harvestable
blocked).** The canonical clause text lives in `completion-report.md`
§"Orchestrator side" (配额自报, [org-internal #3669]) — append it VERBATIM to the seed
§"Orchestrator side" (配额自报, [TT-3669]) — append it VERBATIM to the seed
prompt's completion-report block. Single source of truth: do not fork or
restate the clause here; its blocked payload already follows the one-line
key=value spec (`status=blocked reason=quota-exhausted summary=<provider +
@@ -1,4 +1,4 @@
> Extracted from headless-session-ops/SKILL.md (Launch a session for a specific ticket (#N)) — moved verbatim 2026-08-26, ticket [org-internal #3480].
> Extracted from headless-session-ops/SKILL.md (Launch a session for a specific ticket (#N)) — moved verbatim 2026-08-26, ticket [TT-3480].
## Launch a session for a specific ticket (#N)
@@ -7,13 +7,13 @@ work a tracked issue. Compose the seed prompt **from the issue itself** so the
new session starts with real context, and launch it on **the current session's
model**.
0. **Owner check (MANDATORY pre-step, [org-internal #1803]).** Before creating anything,
0. **Owner check (MANDATORY pre-step, [TT-1803]).** Before creating anything,
verify no other live session already owns this ticket or code area —
duplicate ownership is how [org-internal #1744]/[org-internal #1753] collided. The check spans four
duplicate ownership is how [TT-1744]/[TT-1753] collided. The check spans four
data sources; any live claim → **ABORT the launch and report the
conflict**.
**Mechanized ([org-internal #3667])**: `bash script/session-conflicts.sh <N>` runs the
**Mechanized ([TT-3667])**: `bash script/session-conflicts.sh <N>` runs the
session-title scan (a), the branch scan (d), and the open-PR check (c)
in one read-only pass (exit 4 = conflict, `--json` for a machine
summary); the assignee/claim check (b) is `claim.sh`'s compare-and-swap
@@ -23,7 +23,7 @@ model**.
**a. Session-title scan (live session check).** The listing endpoint
returns at most `limit` (default 100) sessions — on busy repos (300+ live
sessions in one directory) that silently truncates and the scan misses
owners ([org-internal #3190]). Non-range listing responses carry `X-Total-Count` (full
owners ([TT-3190]). Non-range listing responses carry `X-Total-Count` (full
filter population, untruncated) and `X-Has-More: true` (only when the
page is truncated). Scan procedure: fetch page 1, and when
`X-Has-More: true` is present re-fetch once with `limit=$TOTAL` — treat
@@ -57,7 +57,7 @@ model**.
the launch and report the conflict** (issue comment naming the owning
session id). Do not launch a second session for the same ticket.
**b. Assignee / claim check ([org-internal #2297]).** Read the issue via
**b. Assignee / claim check ([TT-2297]).** Read the issue via
`工单 API(见 TERMINOLOGY)get(owner, repo, index: N)` (or `GET /api/v1/repos/$OWNER/$REPO/issues/$N`):
- If `assignee` is set and is **not** the launching agent → **ABORT** and
report (the ticket is already claimed).
@@ -68,7 +68,7 @@ model**.
`GET <gitea-base-url>/api/v1/repos/issues/search?q="#N"&type=pulls&state=open`
(API source) — and **ABORT** if an open PR already covers the ticket.
**d. Remote branch check ([org-internal #2297]).** A local branch is invisible to other
**d. Remote branch check ([TT-2297]).** A local branch is invisible to other
sessions — check the remote too:
`git ls-remote origin 'workflow/*'` and scan for a branch tail covering `#N`.
Also check the repo side locally: `git worktree list` +
@@ -76,7 +76,7 @@ model**.
- A stale hit (session idle for hours / clearly abandoned) → do NOT
hand-post a takeover comment (retired admin workaround — unauditable);
dead-claim takeover is mechanized ([org-internal #3668]): `bash script/claim.sh
dead-claim takeover is mechanized ([TT-3668]): `bash script/claim.sh
takeover <ticket> <branch> --session-id <id>`. It enforces the
evidence gate (branch ABSENT on origin AND (session 404 OR comment at
least CLAIM_TAKEOVER_STALE_DAYS old)) and exits 10 when the evidence
@@ -87,13 +87,13 @@ model**.
§"Dead-claim takeover" (claim.sh header is normative).
The runtime counterpart of this check is `core/rules/session-scope-guard.md`
("One task, one owner"), injected into every session's prompt.
- **Backend hard guard ([org-internal #1989]):** `POST /session` hard-rejects duplicate-ticket
session creation with HTTP 400. The backend guard is **on by default ([org-internal #2350])**;
- **Backend hard guard ([TT-1989]):** `POST /session` hard-rejects duplicate-ticket
session creation with HTTP 400. The backend guard is **on by default ([TT-2350])**;
set `enabled: false` in config to opt out. It fails open ONLY on defect
paths (DB / config errors), never as a configured disable. The front-end
soft check above is now backed by this backend hard check for defense in
depth.
- **Claim-first ([org-internal #2297]).** Claiming a ticket is one atomic 3-step action:
- **Claim-first ([TT-2297]).** Claiming a ticket is one atomic 3-step action:
(1) set the issue assignee to the working account, (2) post a claim comment
naming the workflow branch and session id, (3) push the workflow branch to
remote (`git push -u origin workflow/...`). The claim is valid ONLY when all
@@ -101,7 +101,7 @@ model**.
session until all three steps are done (see
`core/rules/session-scope-guard.md` §"Claim-first"). If a collision HAS
already happened, follow `rules/ownership-collision-runbook` (wiki, L2 on-demand).
- **Provisioning after claim ([org-internal #3642]) is ONE command** — claim + worktree +
- **Provisioning after claim ([TT-3642]) is ONE command** — claim + worktree +
Tier-1 runs scaffold:
`bash script/claim-provision.sh <N> workflow/<branch>` (runs claim.sh with
exit codes passed through, attaches the session worktree to the claimed
@@ -123,7 +123,7 @@ model**.
- restate the **goal** in one sentence;
- list the **constraints** and **acceptance criteria**;
- cite every **wiki artifact path** / related issue referenced in the body
(e.g. `{epic-slug}/dag`, `{epic-slug}/dag-nodes/{node-id}`, `[org-internal #1691]`);
(e.g. `{epic-slug}/dag`, `{epic-slug}/dag-nodes/{node-id}`, `[TT-1691]`);
- tell the new agent to follow the right pipeline skill for the work type
(implement / bugfix / design / …).
Write it to a file (handles newlines):
@@ -141,16 +141,16 @@ your change — classify (pre-existing/flaky), file a BF/FT issue per
core/rules/session-scope-guard.md, and continue this task. Do NOT fix
unrelated failing tests in place.
## 完成回报协议 (mandatory, [org-internal #2374])
## 完成回报协议 (mandatory, [TT-2374])
完成、阻塞、或移交时,向编排会话回报:
POST <BASE>/session/<LAUNCHER_SID>/prompt_async?directory=<DIR>
(编排目录未知时可用全局路由 POST <BASE>/prompt_async,body 携 sessionID,[org-internal #4307])
(编排目录未知时可用全局路由 POST <BASE>/prompt_async,body 携 sessionID,[TT-4307])
body: {"agent":"builder","model":{...},"parts":[{"type":"text","text":"<report>"}]}
<report>: [#N] [<node-or-slug>] status=done|blocked|handoff branch=<ref> [pr=#<PR>] verify=<changed+typecheck> risk=<high|low> summary=<one line>
约束:worker 只推分支不开 PR(PR 由编排串行开);交付推送前 test:changed+typecheck 须绿。若再遇配额/用量上限错误,立即回报 status=blocked reason=quota-exhausted summary=<provider 与重置提示>(branch=<ref> 必带)后停止,勿自行换模型重试。
回报失败(非 2xx / 连接拒绝)→ 在本 issue(或父 Epic)发同内容评论兜底。
决策边界([org-internal #2378]):遇方向性决策点 → status=blocked 回报,勿调 question。
决策边界([TT-2378]):遇方向性决策点 → status=blocked 回报,勿调 question。
EOF
```
@@ -179,4 +179,4 @@ EOF
6. **Add the completion-report block to the seed prompt** (see next section) so
the worker reports back when it finishes or blocks — without it, this
orchestrator has no push channel and must poll forever ([org-internal #2374]).
orchestrator has no push channel and must poll forever ([TT-2374]).