From e6ced4ef8ac08d96695e99ec454db1fc78a020a2 Mon Sep 17 00:00:00 2001 From: onevcat Date: Sat, 22 Aug 2026 09:17:02 +0900 Subject: [PATCH] docs(ai): retire prowl handoff instead of adapting it Drop the LegacyHandoffAdapter, seeded outputs, and destination-only binding in favour of one release of HANDOFF_RETIRED stubs carrying the replacement command; add --skip and the self-initiated run response rule; add the prowl.handoff-checkpoint built-in. Claude-Session: https://claude.ai/code/session_019jRM3FXUGguPFCqWAWu1Yb --- docs-ai/063-agent-workflows/000-plan.md | 86 ++++++++--------- docs-ai/063-agent-workflows/dsl-spec.md | 121 +++++++++--------------- 2 files changed, 85 insertions(+), 122 deletions(-) diff --git a/docs-ai/063-agent-workflows/000-plan.md b/docs-ai/063-agent-workflows/000-plan.md index a8f8a205..b154f3b8 100644 --- a/docs-ai/063-agent-workflows/000-plan.md +++ b/docs-ai/063-agent-workflows/000-plan.md @@ -84,8 +84,8 @@ contract governance of 060. | Term | Meaning | | --- | --- | | Workflow | A `prowl.workflow/v1` YAML document: id, inputs, roles, steps. Sources: bundle (`prowl.*` ids), user (`~/.prowl/workflows/*.yaml`), repo (`/.prowl/workflows/*.yaml`). | -| Role | A participant: `source: current` (the pane the run was started from; it must host a detected agent only if the runner will actually deliver a `message` to it — steps pre-skipped or completed by a seeded output at start do not count — so a bare shell can still be the source of a context-only or pre-briefed handoff), `pick` (an existing detected agent pane in the same worktree, chosen at start), or `launch` (a new agent Prowl starts). V1 launch roles are interactive (TUI in a tab/split); `kind: headless` is reserved for V2 (see Alternatives). | -| Binding | Role → concrete Agent Profile (or, for `pick`, an existing pane), resolved at start and frozen into the run; the only exception is the internal destination-only binding the legacy handoff adapter uses for `--no-launch` (agent token, no profile). | +| Role | A participant: `source: current` (the pane the run was started from; it must host a detected agent only if the runner will actually deliver a `message` to it — steps skipped at start via `--skip` / the start sheet do not count — so a bare shell can still be the source of a context-only handoff), `pick` (an existing detected agent pane in the same worktree, chosen at start), or `launch` (a new agent Prowl starts). V1 launch roles are interactive (TUI in a tab/split); `kind: headless` is reserved for V2 (see Alternatives). | +| Binding | Role → concrete Agent Profile (or, for `pick`, an existing pane), resolved at start and frozen into the run. | | Step | One verb: `message` (say something to a live role), `launch` (start a launch role), `action` (built-in Swift action), `notify`, `close`; plus `repeat` blocks. Each step has a `title` for the status slot and an optional `expect`. | | Expect | Only on `message` / `launch` steps: what must happen before the run advances — a named `output` delivered by the step's target role via the generated `prowl workflow done` command, optional `sections`/`format` validation, optional `verdict` enum (safe slugs), optional `timeout` / `on_timeout`. | | Run | One execution: state snapshot + artifacts under `/.prowl/workflow-runs//`. | @@ -183,9 +183,7 @@ Distribution is "the next instruction names the path"; Prowl never inlines one a output into another agent's input box, and every rendered line is re-validated as a single terminal line before injection (template values such as inputs or paths cannot smuggle a newline past the boundary). Outputs are agent-authored -content persisted at the agent's request — or, for the internal seeded outputs of the -legacy adapter, caller-supplied content validated against the step's `expect` — (default -cap 1 MiB, hard max 4 MiB, +content persisted at the agent's request (default cap 1 MiB, hard max 4 MiB, `OUTPUT_TOO_LARGE` otherwise; same bounds as `agents read`), kept until the user deletes the run folder (retention policy is a V2 item, as for `.prowl/handoff/archive`). Step ids and output names are restricted to safe slugs because they become path components; run @@ -285,9 +283,10 @@ writes. ### CLI (per 060's four-layer rule) -`prowl workflow list | run [source] [--role r=…] [--input k=v] | status [run] | done -[-|--file] [--verdict v] [--token t] [--run --step] [--force] | cancel | validate - | schema` — `[source]` is 060's `GenericTarget` (`pN`, `tN`, UUID, worktree ref); omitted, +`prowl workflow list | run [source] [--role r=…] [--input k=v] [--skip ] | status +[run] | done [-|--file] [--verdict v] [--token t] [--run --step] [--force] | cancel | +validate | schema` — `[source]` is 060's `GenericTarget` (`pN`, `tN`, UUID, +worktree ref); omitted, the source is the caller pane when the workflow has a `current` role, and a worktree reference is required otherwise; `--role` is source-specific (`launch` role → ``, `pick` role → `` in the source worktree, @@ -295,7 +294,8 @@ reference is required otherwise; `--role` is source-specific (`launch` role → prerequisites `prowl create pane --direction … [--profile --prompt -]` (#699 extended) and `prowl agents wait --until idle|done|blocked [--timeout]`. `prowl handoff -to|save` remain (see Open questions for alias vs. removal). +to|save` are **retired**: one release of non-executing stubs that answer `HANDOFF_RETIRED` +with the exact `prowl workflow run …` replacement, then removal (see Built-ins). ### Built-ins and distribution @@ -310,36 +310,20 @@ to|save` remain (see Open questions for alias vs. removal). `has_briefing`) → `launch receiver` (background tab, prompt from the action output) → notify. The receiver role carries **no `agents:` restriction**: any runtime whose adapter supports a seeded prompt is admissible (055 verified all but Amp, which the adapter - rejects itself); the 047-era claude/codex-only admission is retired. The deprecated - `prowl handoff` commands are served by a **`LegacyHandoffAdapter`** in the app (D3) that - maps every existing parameter onto the runner's internal start API and renders the - existing `prowl.cli.handoff.v2` response shape (schema-compatible; semantic differences - documented, not "byte-compatible"): `to ` with a requested launch → the - Recommended enabled profile of that runtime, else `PROFILE_NOT_FOUND` with guidance; - `to --no-launch` → no profile lookup, the receiver role is frozen as a typed - destination-only binding carrying the validated token and the `launch` step is - pre-skipped (so `prowl handoff to gemini --no-launch` keeps its archive/log/`to_agent` - behavior for every detected-agent token); source selectors / positional source → run - source; `--brief -` → the `brief` step completed by a *seeded output* (validated in - preflight, then materialized as a versioned `outputs/brief..md` with a `seeded` - record — no fabricated delivery or token; DSL spec §5); `--no-brief` → step pre-skipped - (absent briefing = context-only transition: archive, remove the stale `current.md`, - regenerate `context.md`, context-only kickoff); `--note` → `handoff.transition` input; - `save` → `prowl.handoff-checkpoint`. The adapter - preflights before any run or artifact exists, reproducing today's immediate errors - (`BRIEF_REQUIRED` when neither `--brief` nor `--no-brief` is given — the legacy path - never starts an agent-mediated brief step; `EMPTY_INPUT`; `INVALID_BRIEF`), and maps - action/transition failures to the legacy failure response. Because the - `brief` step is pre-supplied or pre-skipped, the `current` role needs no detected agent - — a bare shell pane remains a valid legacy source, as today. The adapter starts the run - with `failurePolicy: .fail` (the runner's generic `.attention` policy would hang a - synchronous CLI call): a launch/provision failure after the artifacts are written ends - the run as `failed`, keeps artifacts and log, and returns `HANDOFF_FAILED` exactly as the - current handler does. The adapter awaits run completion synchronously (no agent wait is - involved once the brief is supplied) and is covered by per-field socket parity tests - (the DSL spec §11 list is normative: no-agent source, `--no-brief`, `--no-launch`, - omitted brief choice, empty stdin, invalid sections, action/transition failure, profile - lookup failure, provisioning failure, launch failure). + rejects itself); the 047-era claude/codex-only admission is retired. Skipping the brief + (start sheet, `--skip brief`, or the panel) gives the context-only transition through + the Skip rule — the replacement for the old HUD's "Context Only". A second small + built-in, `prowl.handoff-checkpoint` (brief → `handoff.checkpoint`), covers "save + progress for a later successor". **The legacy `prowl handoff to|save` commands are + retired, not adapted** (decision 2026-08-22): for one release they are non-executing + stubs returning `HANDOFF_RETIRED` with the copy-pasteable replacement + (`prowl workflow run prowl.handoff [--role receiver=…] [--skip brief]` / + `prowl workflow run prowl.handoff-checkpoint`, briefing delivered with the returned + `prowl workflow done -`); afterwards the commands, `HandoffCommandHandler`, + `HandoffHudFeature`, `HandoffRequestRegistry`, and the `prowl.cli.handoff.v2` contract + are deleted. A self-initiated run returns the first step's instruction and completion + command in its response instead of typing them into the caller's own pane, so an agent's + self-handoff stays two commands. - `skills/prowl-workflows/SKILL.md`: how to author and run workflows; `prowl workflow schema` prints the machine-readable reference. @@ -406,7 +390,7 @@ built-ins land, handoff migrated). | 8 | **C2** Start sheet (bindings, suggestion-based profile creation, don't-ask-again) + entry points (capsule popover, palette, Active Agents context menu) | C | B3 | GUI-initiated runs | | 9 | **D1** `embed-skills`, `prowl-workflows` authoring skill, `docs/components/workflows.md`, Settings › Workflows complete (enable/validate/Reveal/New/Ask-agent/per-workflow auto) | D | B1, C2 | Distribution and docs | | 10 | **D2** `prowl.adversarial-review` built-in + reviewer skill + E2E self-verification | D | A2, C2, D1 | Proves the engine on a fresh flow before touching shipped behavior | -| 11 | **D3** `prowl.handoff` built-in + `handoff.transition`; `prowl handoff` → deprecated alias; remove `HandoffHudFeature`; rewrite `docs/components/handoff.md` | D | D2 | Migrate the shipped feature last | +| 11 | **D3** `prowl.handoff` + `prowl.handoff-checkpoint` built-ins + `handoff.transition`/`handoff.checkpoint` actions; `prowl handoff to|save` → `HANDOFF_RETIRED` stubs; remove `HandoffHudFeature`, `HandoffCommandHandler`, `HandoffRequestRegistry`; rewrite `docs/components/handoff.md` and the `prowl-cli` skill | D | D2 | Migrate the shipped feature last | | 12 | V2: fan-out (`count`, `wait all`), observe mode (`agents read` capture), run persistence/resume, cross-worktree roles, GUI editor | — | — | — | ## Alternatives & decisions @@ -447,15 +431,25 @@ built-ins land, handoff migrated). - **Run directory under the target root**, mirroring `.prowl/handoff/`: sandboxed agents read cwd-relative files most reliably; definitions live beside it in `/.prowl/workflows/` so a repo can ship its workflows. +- **Retire `prowl handoff` instead of emulating it.** Ten review rounds showed that + preserving every legacy semantic (`--no-brief`, `--no-launch`, bare-shell sources, + synchronous failure codes, the v2 payload) needed an adapter with its own preflight, + seeded outputs, a destination-only binding kind, and a parity matrix — all machinery + that exists only for compatibility. A `HANDOFF_RETIRED` stub with the exact replacement + command gives users the same migration in one line; `--skip brief` and the optional + action input cover the context-only case generically. ## Decisions recorded during design review (2026-08-21) -- **Handoff CLI**: `prowl workflow run prowl.handoff` is the primary invocation. The - shipped `prowl handoff to|save` stays as a **deprecated alias** (stderr warning per 060's - deprecation policy; the `prowl.cli.handoff.v2` response shape is kept - schema-compatible by the `LegacyHandoffAdapter`, with semantic differences documented) - and is retired afterwards. The `.prowl/handoff/` artifact contract survives inside the - `handoff.transition` action. +- **Handoff CLI** (revised 2026-08-22): `prowl workflow run prowl.handoff` is the only + invocation. The shipped `prowl handoff to|save` are **retired outright** — one release of + non-executing `HANDOFF_RETIRED` stubs carrying the exact replacement command, then + removal. The earlier plan to keep a schema-compatible alias through a + `LegacyHandoffAdapter` (with its preflight, seeded outputs, destination-only binding, + and `failurePolicy: .fail`) was dropped as not worth its complexity once migration + guidance proved to be a one-line message; review-round notes below that mention those + mechanisms are historical. The `.prowl/handoff/` artifact contract survives inside the + `handoff.transition` / `handoff.checkpoint` actions. - **Binding default**: built-ins use `bind: ask`. Users switch a workflow to `auto` without editing the file: a "Don't ask again for this workflow" toggle in the start sheet and a per-workflow toggle in Settings › Workflows store a local override next to the binding diff --git a/docs-ai/063-agent-workflows/dsl-spec.md b/docs-ai/063-agent-workflows/dsl-spec.md index 3edb093b..1f628b3b 100644 --- a/docs-ai/063-agent-workflows/dsl-spec.md +++ b/docs-ai/063-agent-workflows/dsl-spec.md @@ -65,7 +65,7 @@ roles: | Field | Rules | | --- | --- | -| `source` | At most one `current` role per workflow. A `current` role must host a detected agent only if the runner will actually **deliver** to it — i.e. at least one `message` step targeting it is neither pre-skipped nor completed by a seeded output (§5) at start; otherwise a bare shell pane is a valid source (context-only handoff, legacy `handoff save`, legacy `--brief -`). The check runs after the internal start API has applied its pre-skips/seeds. A workflow without a `current` role needs an explicit worktree at start. `pick` roles are chosen from the detected agents of the source worktree at start; a pane already in a run is not offered. | +| `source` | At most one `current` role per workflow. A `current` role must host a detected agent only if the runner will actually **deliver** to it — i.e. at least one `message` step targeting it is not skipped at start (`--skip ` / the start sheet's skip option, §9); otherwise a bare shell pane is a valid source (e.g. a context-only handoff). A workflow without a `current` role needs an explicit worktree at start. `pick` roles are chosen from the detected agents of the source worktree at start; a pane already in a run is not offered. | | `kind` | Only for `launch`. V1 accepts `interactive` only; `headless` is reserved (§12) because no executor/output protocol exists yet. | | `agents` | Tokens from the detected-agent catalog. Validator warns (not errors) when none is installed locally. | | `suggest` | Subset of profile preset fields (`agent`, `model`, `reasoning_effort`, `execution_mode`). Never a reference to a profile name or UUID. | @@ -84,10 +84,7 @@ override, is re-validated first (exists, enabled, satisfies `agents`, adapter su seeded prompt); a failing candidate falls through to the next tier. The chosen profile is frozen into the run together with its launch plan; later profile edits do not affect the run. CLI overrides are source-specific (§9): `--role =`, -`--role =`; `current` roles take no override. One -internal exception exists: the `LegacyHandoffAdapter` (§11) may freeze a launch role as a -*destination-only* binding (agent token, no profile) when its launch step is pre-skipped; -a generic resolver never performs profile lookup for such a role. +`--role =`; `current` roles take no override. ## 4. Steps @@ -173,8 +170,8 @@ JSON Schema): | Action | `with` inputs | Outputs | | --- | --- | --- | -| `handoff.transition` | `briefing` (path, optional — **absent = context-only transition**, exactly today's `handoff to --no-brief`: archive the outgoing `current.md`/`context.md`, then *remove* `current.md` so a stale briefing can never impersonate a fresh one, regenerate `context.md`, and select the context/archive-only kickoff prompt), `from` (role, required), `to` (role, required; its agent token comes from the role's frozen binding — a profile binding yields the profile's agent, a legacy *destination-only* binding (see §11) yields the recorded token), `note` (string, optional) | `kickoff_prompt` (string; briefing or context-only variant), `artifact_path` (path), `has_briefing` (bool) | -| `handoff.checkpoint` | `briefing` (path, optional — absent = context-only checkpoint: regenerate `context.md`, keep an earlier valid `current.md` if present, as today's `handoff save --no-brief`), `note` (string, optional) | `artifact_path` (path), `has_briefing` (bool, for this invocation) | +| `handoff.transition` | `briefing` (path, optional — **absent = context-only transition**: archive the outgoing `current.md`/`context.md`, then *remove* `current.md` so a stale briefing can never impersonate a fresh one, regenerate `context.md`, and select the context/archive-only kickoff prompt), `from` (role, required), `to` (role, required; its agent token is the frozen profile binding's agent), `note` (string, optional) | `kickoff_prompt` (string; briefing or context-only variant), `artifact_path` (path), `has_briefing` (bool) | +| `handoff.checkpoint` | `briefing` (path, optional — absent = context-only checkpoint: regenerate `context.md`, keep an earlier valid `current.md` if present), `note` (string, optional) | `artifact_path` (path), `has_briefing` (bool, for this invocation) | | `git.context` | `root` (path, optional; default worktree) | `path` (path to the generated markdown summary), `branch` (string) | ## 5. `expect` @@ -204,20 +201,9 @@ expect: several steps produce the same output name); `outputs/.md` is the "latest" view, replaced atomically (temp file + rename); `run.json` records the invocation → step / iteration / activation / file mapping. -- **Seeded outputs (internal, adapter-only).** The runner's internal start API lets an - in-app caller (today only the `LegacyHandoffAdapter`, §11) supply a body for a step's - output *before* any agent participates. The runner validates it against the target - step's `expect` (format, sections, verdict, size cap) exactly as a delivery; after that - check and the caller's own preflight succeed and the run directory exists, the runner - allocates a run-global ordinal, - atomically writes `outputs/..md` plus the latest view, and records the - step in `run.json` as `seeded` (no source-pane delivery, no activation, no token). The - step counts as completed and its `expect` is satisfied; templates resolve - `outputs..path` normally; later invocations continue the same monotonic ordinal - sequence. Seeding is not reachable from YAML or the public CLI. - Output bodies are capped (default 1 MiB, hard max 4 MiB → `OUTPUT_TOO_LARGE`). -- **Skip rule.** Skipping an `expect` (panel Skip, `on_timeout: skip`, or an internal - pre-skip) marks its output missing. A missing output is tolerated by exactly one kind of +- **Skip rule.** Skipping an `expect` (panel Skip, `on_timeout: skip`, or a skip chosen at + start via `--skip ` / the start sheet) marks its output missing. A missing output is tolerated by exactly one kind of consumer: a `with` input that the action's registry schema declares **optional** — the key is then absent from the action's effective input (this is how `handoff.transition` degrades to a context-only transition, i.e. the old HUD's "Context Only"). Every other @@ -272,7 +258,7 @@ non-optional consumer references (the run would end as `skipped`). ``` /.prowl/workflow-runs// - run.json # state snapshot: workflow id/version, frozen role bindings (profile UUID/name, pane ids, or a destination-only agent token), seeded/invocation records, + run.json # state snapshot: workflow id/version, frozen role bindings (profile UUID/name, pane ids), invocation records, # step states, timestamps; no env values, no extra arguments, no credentials log.md # human-readable, append-only instructions/..md # materialized `instruction` / `prompt` text, one per invocation (run-global ordinal, §5) @@ -288,10 +274,9 @@ and the run UUID, under canonical containment checks against `/.prowl/workflow-runs/` (no symlink leaf, resolved parent + leaf compared to the resolved base — the `AgentProfileHomeProvisioner` gate). Repo-scoped definitions are untrusted input; nothing from a workflow file is interpolated into a path except validated -slugs. Outputs are agent-authored content persisted at the agent's request — or, for -seeded outputs (§5), caller-supplied content validated against the step's `expect` — with -the size caps of §5; they are kept until the user removes the run folder (retention -policy: V2). Privacy +slugs. Outputs are agent-authored content persisted at the agent's request, within the +size caps of §5; they are kept until the user removes the run folder (retention policy: +V2). Privacy wording as in §10: Prowl-authored persisted and response metadata (`run.json`, `log.md`, the non-body fields of CLI payloads) carries profile UUID/name and agent tokens only — never extra arguments, environment values, home paths, or credentials; the agent-provided @@ -301,7 +286,7 @@ output body is excluded from that claim. ```bash prowl workflow list [--json] # sources, enabled, validation status -prowl workflow run [source] [--role r=]... [--input k=v]... [--json] # grammar is source-specific, see below +prowl workflow run [source] [--role r=]... [--input k=v]... [--skip ]... [--json] # grammar is source-specific, see below # [source]: 060 GenericTarget (pN | tN | UUID | worktree ref); omitted → caller pane # when the workflow has a `current` role (SOURCE_REQUIRED outside a pane), a # worktree reference otherwise @@ -337,9 +322,22 @@ unknown roles are `INVALID_ARGUMENT`; a missing override falls back to binding r # current roles take no override ``` -The `run` response records every frozen binding (launch: profile id/name/agent, or — for -the internal destination-only binding of §11 — agent token only; pick: pane id/handle and -detected agent). +The `run` response records every frozen binding (launch: profile id/name/agent; pick: pane +id/handle and detected agent). + +**`--skip `** (repeatable) marks a step skipped at start. It is accepted only for +steps whose `expect` output has no non-optional consumer (§5 Skip rule) — e.g. +`prowl workflow run prowl.handoff --skip brief` is a context-only handoff; anything else is +`INVALID_ARGUMENT` naming the dependent step. The start sheet offers the same choice +("Skip ") for such steps, which is also how a bare-shell pane can start a +handoff. + +**Self-initiated runs.** When `run` is invoked from the pane that becomes the `current` +role and the first step is a `message` to that role, the response carries that step's +rendered instruction (or pointer) and its completion command, and the runner does **not** +also type them into the caller's pane — the caller already has them. For an agent this +makes a self-handoff two commands: `prowl workflow run prowl.handoff`, then the returned +`… prowl workflow done -` with its briefing on stdin. Error codes: `WORKFLOW_NOT_FOUND`, `WORKFLOW_INVALID`, `RUN_NOT_FOUND`, `PANE_BUSY`, `ROLE_MISMATCH`, `STEP_NOT_EXPECTING`, `TOKEN_REQUIRED`, `TOKEN_INVALID`, @@ -384,55 +382,26 @@ changed` was requested. `background: true`, no `agents` restriction — any adapter with seeded-prompt support); steps: `message source` (brief, sections `## Objective`, `## Current State`, `## Next Steps`) → `action handoff.transition` (archive-first `.prowl/handoff/` - contract) → `launch receiver` (`prompt: "{{ actions.transition.kickoff_prompt }}"`) → - `notify`. The deprecated `prowl handoff` commands are served by a `LegacyHandoffAdapter` - that maps source selectors → run source, `--brief -` → the `brief` step completed by a - **seeded output** (§5: validated during adapter preflight, then materialized as - `outputs/brief..md` with a `seeded` record once the run exists, so - `{{ outputs.brief.path }}` resolves normally and no pane delivery or token is fabricated), - `--no-brief` → `brief` step pre-skipped / output missing — the built-in's - `with: { briefing: "{{ outputs.brief.path }}" }` then drops the key by the §5 Skip rule - (optional action input), so the run continues with context-only semantics, exactly as a - GUI user skipping the brief step gets "Context Only"; no adapter-specific overlay exists, - `--note` → `handoff.transition` input, `save` → - `prowl.handoff-checkpoint` (brief → `action handoff.checkpoint`), and `to ` by - launch intent: **when a launch is requested**, the receiver role is bound to the - Recommended enabled profile of that runtime (else `PROFILE_NOT_FOUND`); **with - `--no-launch`**, no profile lookup happens at all — the receiver role is frozen with a - typed *destination-only* binding (`legacyDestination(agent: )`, an internal run-state binding kind that carries no profile, pane, or plan) and - the `launch` step is pre-skipped. `handoff.transition` resolves `to` from that binding, so - archive names, the transition log line, and the `to_agent` response field keep the - recorded token exactly as today, and `prowl handoff to gemini --no-launch` keeps working - for every detected-agent token. The - adapter **preflights before creating any run or artifact**, reproducing the current - handler's immediate errors: neither `--brief` nor `--no-brief` → `BRIEF_REQUIRED` (the - legacy path never starts an agent-mediated brief step or a hidden model turn); empty - stdin → `EMPTY_INPUT`; a briefing missing the required sections → `INVALID_BRIEF`; - unknown agent token → the existing argument error. With the `brief` step pre-supplied or - pre-skipped the `current` role needs no detected agent, so a bare shell pane stays a - valid legacy source. Action/transition failures map to the documented legacy failure - response (never an attention state or partial success). The adapter starts the run with - `failurePolicy: .fail`: a provisioning/launch failure after the artifacts are written - ends the run as `failed` (artifacts and log kept) and returns `HANDOFF_FAILED` - immediately, as the current handler does. It awaits run completion synchronously and - renders the existing `prowl.cli.handoff.v2` response shape (schema-compatible; - differences documented and covered by per-field socket parity tests: no-agent source, - `to … --no-brief` with a stale `current.md` present (archived and removed, context-only - kickoff, `has_briefing: false`), `to gemini --no-launch` in both brief and context-only - variants (asserting `to_agent`, archive/log target, no profile lookup, no launch), - `to --brief -` and `save --brief -` (invalid brief rejected before any run directory or - `.prowl` artifact exists; valid brief yields a versioned seeded output, a `seeded` - `run.json` record, the rendered action input, and a later invocation continuing the - ordinal sequence), `to --no-brief` and `save --no-brief` completing (not `skipped`) with - the `briefing` input absent, `save --no-brief` with and without an existing valid - `current.md` (context-only checkpoint keeps it), every one of these four from a bare - shell source (no detected agent), omitted brief choice - → `BRIEF_REQUIRED`, empty stdin → `EMPTY_INPUT`, invalid sections → `INVALID_BRIEF`, - action/transition failure, profile lookup failure, provisioning failure, launch - failure). + contract, `with: { briefing: "{{ outputs.brief.path }}", from: source, to: receiver }`) + → `launch receiver` (`prompt: "{{ actions.transition.kickoff_prompt }}"`) → `notify`. + Skipping `brief` (start sheet, `--skip brief`, or the panel) yields the context-only + transition through the §5 Skip rule — the replacement for the old HUD's "Context Only". +- `prowl.handoff-checkpoint` — `message source` (brief) → `action handoff.checkpoint`; + the "save progress for a later successor" use case (no receiver, no launch). - `prowl.adversarial-review` — as in §4; interactive reviewer in a right split by default. +**Retirement of `prowl handoff`.** The shipped `prowl handoff to|save` commands are +retired rather than adapted: for one release they remain as *stubs* that execute nothing +and return the structured error `HANDOFF_RETIRED` (JSON envelope; plain text + stderr +otherwise) whose message is the copy-pasteable replacement — +`prowl workflow run prowl.handoff [--role receiver=] [--skip brief]` for `to`, +`prowl workflow run prowl.handoff-checkpoint` for `save`, plus the note that the briefing +is now delivered with the returned `prowl workflow done -` command. After that release the +commands, `HandoffCommandHandler`, `HandoffHudFeature`, `HandoffRequestRegistry`, and the +`prowl.cli.handoff.v2` contract are removed; the `.prowl/handoff/` artifact contract itself +lives on inside the two actions. `docs/components/handoff.md` and the `prowl-cli` skill are +rewritten around the workflow commands in the same change. + ## 12. Reserved for V2 `when:` (conditions on verdicts), `count:` / `wait: { all: […] }` (fan-out), -- 2.51.2