$npx -y skills add gtrabanco/agentic-workflow --skill orchestration-envelopeInternal contract of the agentic-workflow pack: the machine envelope — the fixed JSON block an external orchestrator parses to route the next step (which command, which model tier) without a human reading the chat. Skills do NOT emit it on their own (except workflow-status, the s
| 1 | # Machine envelope (internal contract) |
| 2 | |
| 3 | The **envelope** is one fenced `json` block, the **absolute last output** of |
| 4 | a turn, that lets an external orchestrator route on the outcome. **Who emits |
| 5 | it** (since feature 10 — see `docs/workflow/MIGRATION.md`): |
| 6 | |
| 7 | - **`workflow-status` — always**, as part of its own contract (emitting the |
| 8 | envelope *is* the sensor's function). |
| 9 | - **Every other skill — only when a driver asks for it**, by injecting the |
| 10 | canonical system-prompt snippet below into the headless invocation. In an |
| 11 | interactive/human session, no skill prints an envelope and none should be |
| 12 | expected. |
| 13 | - Internal skills (the review pack, the planning steps, this one) never emit |
| 14 | it in any mode: they return their fixed completion reports to the composing |
| 15 | caller; at most one envelope exists per turn, for the whole turn. |
| 16 | |
| 17 | **Parse contract for orchestrators:** take the **last fenced ```json block** |
| 18 | of the final assistant message. Exactly one envelope per turn; parse failure |
| 19 | → the repair loop below. |
| 20 | |
| 21 | ## Schema (all top-level keys ALWAYS present — use null / [] / 0 when n/a) |
| 22 | |
| 23 | ```json |
| 24 | { |
| 25 | "skill": "<emitting skill name>", |
| 26 | "state": "OK | CONTINUE | READY_FOR_REVIEW | READY_FOR_AUDIT | MERGE_READY | MERGED | NEEDS_FIXES | BLOCKED | NEEDS_INPUT | FAILED | HALT", |
| 27 | "summary": "<one plain-text sentence: what happened this turn>", |
| 28 | "unit": {"type": "feature | fix | docs | none", "id": "<NN-slug | N-topic | null>", "issue": null, "branch": "<branch | null>"}, |
| 29 | "phase": {"current": "<P2 | null>", "total": null, "completed": null}, |
| 30 | "pr": {"number": null, "url": null, "state": "open | merged | none", "head_sha": null, "merge_ready": null, "ci": "green | red | pending | none | null"}, |
| 31 | "gates": {"verification": "green | red | not-run | null", "review_pending": null, "audit_pending": null}, |
| 32 | "findings": {"fix_now": [], "issues_filed": [], "untriaged": 0, "decisions_recorded": 0}, |
| 33 | "blockers": [], |
| 34 | "dependencies": {"unmet": [], "build_order": []}, |
| 35 | "recommendations": {"product_audit": false, "reason": null}, |
| 36 | "needs_input": null, |
| 37 | "next": {"recommended": "<the → Next: block's recommended command>", "alternatives": [], "tier": "strong | cheap", "suggested": []}, |
| 38 | "detail": null |
| 39 | } |
| 40 | ``` |
| 41 | |
| 42 | Field rules — checkable, no interpretation: |
| 43 | |
| 44 | - **`state`** (the orchestrator's routing key — exactly one of the 11): |
| 45 | - `OK` — the skill's job finished; nothing pending from it. Follow `next`. |
| 46 | - `CONTINUE` — same unit has more of the same work (next phase, next loop |
| 47 | iteration). Re-invoke per `next.recommended`. |
| 48 | - `READY_FOR_REVIEW` — implementation checkpoint or unit end; `review-change` |
| 49 | is the mandatory next step (`gates.review_pending: true`). |
| 50 | - `READY_FOR_AUDIT` — review clean; `audit-pr` is next. |
| 51 | - `MERGE_READY` — audit passed; the human (or the documented auto-merge |
| 52 | policy) merges. `pr.merge_ready: true`. |
| 53 | - `MERGED` — an authorized auto-merge was executed this turn. |
| 54 | - `NEEDS_FIXES` — findings/blockers exist that fold into the CURRENT branch |
| 55 | (`findings.fix_now` non-empty); fold, then re-run the gate that sent them. |
| 56 | - `BLOCKED` — cannot proceed; `blockers` says why and `dependencies` gives |
| 57 | the build order when the cause is an unmet dependency. |
| 58 | - `NEEDS_INPUT` — a decision only the human can make; `needs_input.question` |
| 59 | + `needs_input.options` filled. Nothing was guessed. |
| 60 | - `FAILED` — an error the in-skill retries didn't clear (red gate past its |
| 61 | cap, unrunnable substrate). A human looks before anything continues. |
| 62 | - `HALT` — **stop-the-world**: a discovery that invalidates continuing ANY |
| 63 | unit (critical security hole in merged code, broken substrate invariant, |
| 64 | data-loss risk). Every `blockers[]` entry carries `"scope": "run"`. The |
| 65 | orchestrator must stop the whole run and surface it, not just park a unit. |
| 66 | - **`findings`** — `fix_now` is an array of objects |
| 67 | `{"ref": "F1", "title": "…", "file": "path:line"}` (they have no issue |
| 68 | numbers yet); `issues_filed` is an **array of issue numbers** (integers) |
| 69 | created/updated this turn; `untriaged` counts findings still without a |
| 70 | destination (must be 0 when the skill's own contract requires routing all). |
| 71 | - **`blockers[]`** — objects |
| 72 | `{"kind": "dependency | issue | gate | merge-conflict | substrate | input", "id": "<NN-s |