| name | workflow-status |
| user-invocable | true |
| version | 1.7.0 |
| author | Gabriel Trabanco <gtrabanco@users.noreply.github.com> |
| license | MIT |
| argument-hint | [--json-only] [--last-envelope <json|path>] |
| description | Read-only sensor for orchestrating the workflow programmatically: computes the full state of the project — every feature and fix with its dependency closure (met/unmet), pending fixes, open/merged PRs and their audit state, findings awaiting triage, what is startable right now and in which build order, and whether a product-audit is due — and emits it as one machine envelope (fixed JSON). The sensor an external driver calls between steps instead of relying on ship-roadmap's conductor. Never edits anything. Triggers: "workflow status", "what can I build next", "dependency tree of the roadmap", "pending fixes", "state of the run", "workflow-status".
|
Workflow Status (the orchestrator's sensor)
One read-only pass over the project that answers, in a single fixed JSON
envelope: what exists, what is blocked on what, what is startable right now,
and what the recommended next command is. Built for external orchestrators
(see docs/workflow/ORCHESTRATION.md) but equally useful to a human asking
"where do we stand?".
Turn contract — verify before ending the turn
✓ Every claim comes from a RUN command or a READ file (git/forge output, roadmap,
fix index, feature folders) — nothing inferred from memory
✓ Nothing was edited, committed, pushed, or created — read-only, always
✓ `next.recommended` is non-bare (carries the unit's slug/NN, never a bare
`/plan-feature`) AND staged by the target unit's resolved status:
`idea`/undesigned → `/design-feature <slug>`; `defined` → `/plan-feature
<slug>`; `planned` → `/execute-phase <NN> P1`
✓ Every `design_candidates[].next` begins with `/design-feature ` — design
candidates always route to design, regardless of anything else
✓ When `--last-envelope` is supplied: the no-progress guard ran (crash-recovery
checklist) — a hint that recommended `/plan-feature`/`/design-feature` for a
unit still at its pre-advance status produces a `workflow_observations` note,
never a silently repeated bland recommendation
✓ `recommendations.product_audit` was computed by the step-16 mechanical
two-condition check (never guessed), and `next.tier` was derived from the
resolved `next.recommended` command via the command→tier map in
`## Machine envelope` (never guessed)
✓ Per-unit `review`/`closure`/`issues_born` (steps 10–12) were computed per
their fixed rules — `adversarial.ran`/`n` stayed `null` unless real
evidence exists, never guessed — and any fired `next.suggested[]` entries
(step 13) quote their owning skill's condition verbatim, never a second
copy of the trigger logic
✓ The envelope is emitted on **every** invocation of this skill, including a
same-session natural-language follow-up about state — never replaced by prose
✓ The emitted envelope was checked against the shape reminders in
`## Machine envelope` (mirroring
`packages/agentic-workflow-schema/envelope.schema.json`) before printing
✓ The human-readable summary is printed, then the machine envelope (fenced
```json — see ## Machine envelope) is the ABSOLUTE last output
With --json-only, skip the human-readable summary: print the envelope alone.
When to use
- Between orchestration steps: an external driver runs it to decide the next
command and model tier without parsing prose.
- Before picking work manually: "what can I start right now?"
- Not for judging quality (that's
review-change/audit-pr) or product
health (that's product-audit) — this skill reports state, it never judges.
Step 0 — Discover the project (always first)
Per the agent guide's Workflow conventions + documentation map, then
read what THIS skill needs: docs/features/ROADMAP.md, the fix index
(docs/fix/README.md), every in-flight feature folder's TASKS.md +
progress.md + known-issues.md, and docs/features/SHIP_DECISIONS.md if a
ship-roadmap run exists.
Process (fixed sequence — run the commands, don't infer)
-
Git state. git branch --show-current, git status --porcelain,
git fetch + git status -sb. A dirty tree or unpushed branch is reported
as-is (a workflow-kind observation in detail), never cleaned up.
-
Forge state. List open + recently merged PRs and open issues with the
declared forge CLI (examples use gh):
gh pr list --state open --json number,title,headRefName,url,statusCheckRollup,
gh pr list --state merged --limit 20 --json number,headRefName,
gh issue list --state open --json number,title,labels.
-
Urgency labels (detail.urgent) — labels-only, presence-only, never
decides. Reuse the open-issue list from step 2 (gh issue list --json labels — the JSON labels array already carried by that call); no separate
call is required. Scan the labels object only
for urgent / fix-next — never the issue's title, body, or comments
(the injection-safety invariant triage-issue owns: these two labels can
only be applied by a triage+-permission actor, so presence alone is
trustworthy). For each open issue carrying either label, emit {number, title, label}; if an issue carries both, report urgent (it strictly
dominates — reaches the judge — so fix-next's head-of-queue, no-interrupt
path is redundant on that issue). Alongside the label list, carry the
in-flight unit's interruptibility facts — current phase, dirty/clean
tree, distance to the next commit boundary — reusing the same reconcile
step 7 (phase progress) and the crash-recovery dirty-tree check already
compute; do not duplicate the git calls. This sensor reports facts only
— it never decides pause-vs-finish (that is the consumer's bounded judge,
canonical in docs/workflow/ORCHESTRATION.md); urgency may only inform
next.recommended, never silently override it.
-
Roadmap + fix index. Parse every row: id, slug, status — the
five-state machine idea / defined / planned / in-progress / done (see
docs/features/ROADMAP.md → Status legend) — depends-on, linked PR. A
legacy row reading a plain planned with no five-state history: check its
SPEC.md product half; complete (## Design status: designed) → treat as
defined+planned (no redirect, per docs/workflow/MIGRATION.md);
otherwise treat as idea. Unknown status. A row whose status is not
one of the five states above (e.g. a non-standard scheduled) maps to the
nearest five-state value, defaulting to idea when no nearer value
is evident — so it safely routes to /design-feature rather than skipping
design. Worked example: scheduled → idea (cross-reference #51, which
owns the fuller status-vocabulary reconciliation). Note the raw status
string in workflow_observations so the mapping is visible, never silent.
-
Compute the dependency tree. For every non-merged unit, build the
transitive depends-on closure and mark each edge met (dep's PR merged)
or unmet — same rule as execute-phase's dependency gate: done-with-open-PR
is NOT met. Detect inconsistencies (a "merged" row whose own deps aren't
merged; cycles) and report them as substrate blockers.
-
Classify readiness — startable_now requires status ≥ defined AND deps
met. For every unit in the roadmap/fix index:
- status
idea → list under design_candidates, next command
/design-feature <slug>. Never startable_now, regardless of deps.
- status
defined or planned, deps met → startable_now, with the next
command matched to the exact status: defined → /plan-feature <slug>,
planned → /execute-phase <NN> P1.
- deps unmet (any status ≥
defined) → blocked_units (unchanged).
-
Phase progress. For each in-progress feature, read TASKS.md: current
phase, total phases, per-phase checkbox completion.
-
Pending quality gates. For each unit with commits: has the mandatory
review-change for its current state run (review report present in the
feature folder — the unit's review-findings.md fold ledger, when
present, IS that artifact: its presence, with any rows at all, proves
review-change ran for the unit's current state)? Has audit-pr a
MERGE-READY bound to the PR's current head SHA (look for the audit
comment marker on the PR)? Derive review_pending / audit_pending /
merge_ready per unit.
-
Fix-now fold ledger → findings.fix_now[]. For each in-flight unit
(feature or fix) that has a review-findings.md ledger, read only its
folded: no rows and emit each as a structured item:
{id, file, axis, severity, class, route, suggested_tier} (file = the
ledger's file:line column value, verbatim). Derive suggested_tier from
this fixed table — mechanical, never guessed:
| Condition | suggested_tier |
|---|
severity == "high" | strong |
axis ∈ {security, correctness, logic, architecture, design, concurrency} | strong |
| anything else | cheap |
Reuses next.tier's strong/cheap vocabulary — next.tier's own
derivation (below) is unchanged, this is a separate, per-finding field.
No ledger for a unit → that unit contributes nothing to fix_now (not an
error); no unit in the run has one → findings.fix_now: [], same as
today. Read-only: this step only projects the ledger's current
unfolded rows — never writes, ticks folded, or judges.
-
Per-unit review signals (detail.*.review). For each in-flight
feature/fix, compute:
last_checkpoint_sha — feature-mode units only: read progress.md's
Last reviewed: <sha> header line (execute-phase's per-phase
checkpoint cadence, #77, "Last-reviewed marker"). Fix/single-pass
units have no phase checkpoints — the cadence section is scoped
"(feature mode)" only — so this is always null for them, not an
error.
unreviewed_diff: {lines, files} — git diff --stat <baseline>..HEAD
where <baseline> is last_checkpoint_sha if present, else
git merge-base <default-branch> HEAD — the identical fallback
execute-phase's cadence triggers define (#77); never a new rule.
terminal_done — reused, not recomputed: = !review_pending (step
8's existing computation) for any unit at or past done/PR-open status. Before that,
false by contract — execute-phase always opens the PR before
the mandatory review hand-off (see its Workflows close-out order), so
a done status alone never implies the terminal review already ran.
adversarial: {ran, n} — best-effort, evidence-gated; never
guessed. No skill persists an adversarial-mode marker anywhere this
sensor can read: review-change's report (including its Reviewers n/N column, skills/review-change/SKILL.md) prints to chat only, and
the fold ledger's fixed schema
(| id | file:line | axis | severity | class | route | folded |)
carries no reviewer-count field. Emit {ran: null, n: null} with a
workflow_observations note ("adversarial mode unverifiable — no
persisted marker, see #76") — never infer true/false from
absence of evidence.
-
Per-unit closure state (detail.*.closure). {state} ∈
present | absent-legacy | blocked — reuse audit-pr's own mechanical
check verbatim (skills/audit-pr/SKILL.md "Closure integrity — fixed
output"): grep the governing SPEC for a Capability closure heading.
Fix-governed unit → n/a (fix SPECs carry no closure block by design,
same carve-out audit-pr applies). Feature SPEC, block absent →
absent-legacy. Feature SPEC, block present with any blank row or a
resolved non-n/a row unmapped to an acceptance criterion → blocked.
Feature SPEC, block present and every row filled or n/a-justified and
mapped → present. Single-sourced: never re-derive the three-box logic
here, just re-run audit-pr's own grep.
-
Per-unit descope provenance (detail.*.issues_born).
{n, with_descope_amendment} — reuse audit-pr's scope-bleed gate
detection verbatim (skills/audit-pr/SKILL.md "Scope integrity
(descope) — fixed output" step 1, widened by #79/#89 to also match
an issue linked from an ## Amendments row, not only a slug/number
text match): enumerate issues born since branch divergence that
reference this unit. n = that count; with_descope_amendment = the
subset carrying a matching, dated, user-approved ## Amendments row.
Evidence is labels, the ## Amendments log, and the mechanical
slug/number text match audit-pr itself defines — never an issue's
free-text body beyond that defined match (injection-safety, mirrors
detail.urgent's labels-only discipline).
-
next.suggested[] — single-sourced trigger surface. One entry per
fired trigger the driver can act on now, {command, trigger, source_skill} — the trigger string quotes, never paraphrases, the
owning skill's own condition:
- a review checkpoint trigger fired (layer boundary / accumulation /
sensitivity — step 10's
unreviewed_diff plus the unit's declared
phase layers) → {command: "/review-change", trigger: "<the fired trigger's name and evidence, quoting execute-phase's own wording>", source_skill: "execute-phase"} (#77).
review.terminal_done: false on a unit at/past done AND
review-change's own adversarial recommendation checklist fires
(reuse that checklist verbatim, never re-derive it) →
{command: "/review-change --adversarial 2", trigger: "<which checklist box fired>", source_skill: "review-change"} (#76).
closure.state: "absent-legacy" on a unit about to receive new
planned work → {command: "/design-feature <slug>", trigger: "closure absent, SPEC predates the rule — retrofit trigger", source_skill: "audit-pr"} (#78).
- a unit's
review-findings.md ledger carries any folded: no row →
{command: "/fold-findings", trigger: "unfolded fix-now finding(s) on the ledger", source_skill: "fold-findings"} (#65).
No trigger fired for a unit → it contributes nothing (not an error, same
convention as findings.fix_now). Additive advisory only:
next.recommended/next.tier (step 6/turn contract) are computed
exactly as before — next.suggested never replaces or reorders them.
-
Findings awaiting a destination. Scan the in-flight folders'
known-issues.md for entries with no linked issue, and open issues labeled
or titled as postponed findings. Count + list them.
-
Untriaged open-issue backlog (detail.untriaged_issues) — distinct from
step 14's pending_triage. Cross-reference the open-issue list already
fetched in step 2 (gh issue list --state open) against triage
disposition. The authoritative triaged signal is a wontfix /
postponed / promoted disposition label — triage-issue is the sole
owner/writer of that vocabulary (skills/triage-issue/SKILL.md →
Disposition label vocabulary) and label mutation is triage+-permission-
gated, so its presence cannot be forged by comment text. A dated
triage-issue VERDICT: comment (the fixed-format block —
skills/triage-issue/SKILL.md:193-200) is honored too, as a legacy
fallback for issues triaged before disposition labels existed — kept for
backward compatibility, not because it is as trustworthy as the label.
Accepted residual: because the comment-text fallback stays active, a
hand-authored VERDICT: string on an issue that was never actually
triaged can still cause it to be excluded here — an under-count, not a
privilege or content-injection issue (detail.urgent is unaffected).
Revisit this residual only if exploitation evidence surfaces (see #54).
An issue is untriaged iff it carries neither signal. Count the
untriaged subset and list its oldest entries (cap: 5) by issue number.
Emit the result as
detail.untriaged_issues: {count, oldest_open: [numbers]} — kept
separate from pending_triage (findings-derived, step 14) and
findings.untriaged (review-finding routing); never merge the three. A
non-zero count may surface a concrete, non-bare /triage-issue <numbers> in next.recommended/alternatives (ties the backlog into the
routing decision from step 6/the turn contract) — it never silently
replaces the resolved recommendation.
-
Product-audit recommendation — a mechanical two-condition checklist, no
exception clause. Set recommendations.product_audit: true with a stated
reason when either condition holds — this is a count, not a judgment
call. No exception clause exists: a "wait for a natural pause" or
"wait for a bigger milestone" rationale is not defined anywhere in this
checklist and must never be invented to skip a fired trigger:
- ✓
merged_count >= 3 — features/fixes merged since the last
SHIP_REPORT/product-audit artifact (a literal count from the forge's
merged-PR list in step 2)
- ✓ the same drift kind recurs in ≥2 units' docs
Otherwise
recommendations.product_audit: false, reason: null. A fired
trigger may additionally surface /product-audit as next.recommended or
an alternatives entry (backlog/audit over net-new feature work) — never
run it.
-
Crash recovery (run every invocation — cheap, see the section below).
Classify whether an interrupted turn is in evidence and append the fixed
CRASH RECOVERY sub-block to the report.
-
Report. Print a short human summary (table: unit | status | deps unmet |
PR | next gate) plus a design candidates line (idea units and their
/design-feature next command) plus the CRASH RECOVERY sub-block, then
the envelope. With --json-only, envelope only.
Crash recovery (run every invocation)
A driver process can die mid-turn; on restart, the persisted state it holds is
a hint, never a source — everything below is recomputed from git, the
forge, and the docs. Nothing is cleaned up here (read-only stands): this
section classifies; the resume command it recommends does the acting.
Checklist:
- ✓ Working tree per unit branch. A dirty tree (
git status --porcelain)
or unpushed commits on a feat/*/fix/* branch → interrupted-turn
candidate. Cite branch + files. Checking unpushed commits: first check
the branch has an upstream (git rev-parse --abbrev-ref <branch>@{u} —
non-zero exit = no upstream). No upstream → every commit on the branch is
unpushed by definition, don't run git log @{u}.. (it errors with
fatal: no upstream configured, which is exactly the mid-crash
never-pushed case, not an error to surface). Has an upstream → use
git log @{u}.. --oneline / git status -sb as usual.
- ✓ Phase-ledger coherence. Compare the unit's
progress.md/TASKS.md
against the branch's actual commits: commits after the last closed phase
entry, or ticked tasks with no matching commit, are cited as evidence.
- ✓ Hint envelope (optional). With
--last-envelope <json|path>, diff the
caller's persisted envelope against the recomputed state and report the
divergence in one line. The hint never overrides recomputed state.
- ✓ No-progress guard (optional, requires
--last-envelope). When the
hint's next.recommended was /plan-feature <slug> or /design-feature <slug> for a given unit, and this run's own recomputed status for that
same unit is still at the same pre-advance status the hint expected
to move it off of (defined for a /plan-feature hint; idea for a
/design-feature hint) — either the recommended command ran but its status
write was dropped, or it never ran at all; this guard cannot distinguish the
two from the envelope alone, so the note names it as a suspected stall,
not a confirmed dropped write. Emit a workflow_observations note (see
## Machine envelope for the exact note shape). This is strictly additive:
the same next.recommended /
next.tier still fire per the normal classification (step 6) — the guard
only stops the silent, bland repeat by making the stall visible. Still
read-only: no write, no repair, no new persistence.
Classification (decision table — every row independently checkable; first
matching row wins per branch):
| Evidence | Verdict |
|---|
| Clean tree, ledger coherent with commits | CLEAN |
| Dirty/unpushed on a unit branch AND the ledger points to a unique next task/phase | RESUMABLE — resume command: execute-phase <NN> <phase> |
| Dirty/unpushed AND ledger contradiction (ticks ahead of commits, unknown branch, detached HEAD) | AMBIGUOUS — a human looks first |
Return exactly (appended to the report):
CRASH RECOVERY — verdict: CLEAN | RESUMABLE | AMBIGUOUS
| Branch | Evidence | Classification | Resume command |
|---|---|---|---|
| <branch> | <dirty: n files; ledger: <state>> | RESUMABLE | execute-phase <NN> <phase> |
Hint envelope: matched | diverged: <one line> | not provided
(CLEAN with no unit branches in play → the table body is a single
| — | clean tree, coherent ledgers | CLEAN | — | row.)
Multiple unit branches, multiple verdicts → one envelope state (fixed
precedence, worst wins): AMBIGUOUS > RESUMABLE > CLEAN. A human
decision pending on ANY branch outranks a mechanical resume on another, which
outranks an all-clean state. The report's per-branch table still lists every
verdict; only the envelope's single state is reduced to the worst one.
Machine envelope
Schema and placement per the installed orchestration-envelope skill. The
state maps 1:1 from the crash-recovery verdict — no new schema fields or
states (the schema package needs no release):
CLEAN → state: OK (the sensor default — even a broken substrate is
reported, as blockers with kind: substrate, while the envelope stays
OK).
RESUMABLE → state: CONTINUE, next.recommended = the resume command
from the decision table.
AMBIGUOUS → state: NEEDS_INPUT, needs_input.question = what is
contradictory, needs_input.options = the concrete choices (resume / redo
the phase / discard the dirty work), evidence in detail.crash_recovery.
next always carries the single best command for the project right now, and
detail the full tree (plus crash_recovery: {verdict, branches: [...]}).
design_candidates is a top-level array beside startable_now /
blocked_units — every idea-status unit, deps-agnostic (design happens
before dependency startability matters).
detail.urgent — the injection-safe urgency channel (feature 15):
{issues: [...], interruptibility: {...}}. issues lists every open
issue carrying urgent or fix-next, read only from the labels object
returned in step 2/3's gh issue list --state open … --json … ,labels call —
never from title, body, or comments; an issue with "URGENT" only in its text
never appears here (urgent wins when an issue somehow carries both labels).
Scoping the list to --state open means a shipped fix's issue drops out of
detail.urgent the moment it closes — automatically, on the next poll, with
no manual label strip required (there is nothing to reconcile: this field is
recomputed fresh every invocation, never persisted).
interruptibility carries the in-flight unit's facts — phase, dirty
(bool), tasks_from_boundary (count of unticked tasks left in the current
phase) — reusing the same phase-progress and crash-recovery reconcile, not a
new computation. This field is presence-only reporting; it never contains
a pause-vs-finish verdict — that decision belongs entirely to the consumer's
bounded judge (docs/workflow/ORCHESTRATION.md). An empty issues array
means no urgency signal is in play; next.recommended may still be
influenced by a non-empty one (e.g. surfaced as an alternatives entry), but
is never silently replaced by it.
detail.untriaged_issues — the plain open-issue backlog surfaced by
step 15: {count, oldest_open: [numbers]} (oldest-first, capped at 5 numbers).
detail is schema-unconstrained (envelope.schema.json:170, "detail": {}),
so this field needs no package change. Kept strictly distinct from
detail.pending_triage
(findings pulled from known-issues.md/postponed-labeled issues, step 14) and
findings.untriaged (review-finding routing) — none of the three subsumes
another. count: 0 means every open issue has a triage disposition; a
non-zero count may drive next.recommended/alternatives toward a
concrete /triage-issue <numbers> citing the listed issues.
Per-unit review/closure/issues_born (step 10–12) — carried on each
detail.features[]/detail.fixes[] entry, not as new top-level keys.
detail is schema-unconstrained (envelope.schema.json:170, "detail": {})
— same precedent as detail.urgent/detail.untriaged_issues (fix #52), so
these need no package change:
review: {last_checkpoint_sha, unreviewed_diff: {lines, files}, terminal_done, adversarial: {ran, n}} — step 10. adversarial.ran/n are
null unless real evidence exists (no skill persists that marker today —
never guessed).
closure: {state} ∈ present | absent-legacy | blocked (feature units)
or n/a (fix units) — step 11, reusing audit-pr's own grep verbatim.
issues_born: {n, with_descope_amendment} — step 12, reusing audit-pr's
scope-bleed detection (widened by #79/#89 to also match an issue
linked from an ## Amendments row).
next.suggested[] — step 13's trigger-attributed suggestion surface,
{command, trigger, source_skill}[], optional (mirrors
packages/agentic-workflow-schema 2.1.0's optional EnvelopeSuggestion[]).
Each trigger string quotes the owning skill's own condition — never a
second, drifting copy of that skill's logic. Advisory only: it rides beside
next.recommended/next.tier, never replaces them. No unit has a fired
trigger this run → next.suggested is omitted entirely (an empty/absent
field, not an error).
Envelope shape reminders (self-check before printing — mirrors
packages/agentic-workflow-schema/envelope.schema.json):
blockers[].scope ∈ {"unit","run"} — there is no "code" value;
doc/roadmap drift is always "unit"-scope (envelope.schema.json:111).
- A
"run"-scope blocker forces state ∈ {BLOCKED, HALT} — it is never
compatible with state: OK (see orchestration-envelope).
dependencies.unmet is an array of strings (unit ids / #issue refs) —
never an array of objects (envelope.schema.json:120); any richer detail
belongs in a blockers[].detail string instead.
next.tier derivation — a fixed command→tier map, never guessed:
| Command | Tier |
|---|
/plan-feature | strong |
/design-feature | strong |
/review-change | strong |
/audit-pr | strong |
/triage-issue | strong |
/product-audit | strong |
/execute-phase | cheap |
next.tier is read off this map by matching the resolved next.recommended
command's name (ignoring its arguments) — never guessed and never copied from
the invoking driver's own tier.
No-progress guard note (workflow_observations, requires --last-envelope,
see the crash-recovery checklist above) — when the hint's next.recommended
targeted /plan-feature <slug> or /design-feature <slug> and this run still
classifies that same unit at the same pre-advance status, append a note of the
exact shape:
"<slug> still 'defined' after the hint's /plan-feature <slug> recommendation — suspected dropped defined→planned write (see #51)"
(swap defined//plan-feature for idea//design-feature on the design
side). The recommendation itself is unaffected — this only adds visibility.
{
"skill": "workflow-status",
"state": "OK",
"summary": "2 features merged, 07 in-progress at P2/4 awaiting review, 05 startable, fix #43 pending triage, 08 needs design.",
"unit": {"type": "none", "id": null, "issue": null, "branch": "main"},
"phase": {"current": null, "total": null, "completed": null},
"pr": {"number": null, "url": null, "state": "none", "head_sha": null, "merge_ready": null, "ci": null},
"gates": {"verification": null, "review_pending": null, "audit_pending": null},
"findings": {"fix_now": [{"id": "F1", "file": "src/export/handler.ts:88", "axis": "security", "severity": "high", "class": "fix-now", "route": "fold into phase", "suggested_tier": "strong"}], "issues_filed": [], "untriaged": 2, "decisions_recorded": 0},
"blockers": [],
"dependencies": {"unmet": [], "build_order": []},
"design_candidates": [{"id": "08-billing-webhooks", "status": "idea", "next": "/design-feature 08-billing-webhooks"}],
"recommendations": {"product_audit": false, "reason": null},
"needs_input": null,
"next": {"recommended": "/review-change", "alternatives": ["/plan-feature 05"], "tier": "strong",
"suggested": [{"command": "/review-change", "trigger": "accumulation: 420 changed lines since last-reviewed sha", "source_skill": "execute-phase"}]},
"detail": {
"features": [
{"id": "07-csv-export", "status": "in-progress", "deps": ["01"], "deps_unmet": [],
"phase": {"current": "P2", "total": 4}, "pr": null,
"review_pending": true, "audit_pending": null, "merge_ready": null,
"review": {"last_checkpoint_sha": "a1b2c3d", "unreviewed_diff": {"lines": 420, "files": 9},
"terminal_done": false, "adversarial": {"ran": null, "n": null}},
"closure": {"state": "present"}, "issues_born": {"n": 0, "with_descope_amendment": 0}},
{"id": "05-auth", "status": "defined", "deps": [], "deps_unmet": [],
"phase": {"current": null, "total": null}, "pr": null,
"review_pending": null, "audit_pending": null, "merge_ready": null,
"review": {"last_checkpoint_sha": null, "unreviewed_diff": {"lines": null, "files": null},
"terminal_done": false, "adversarial": {"ran": null, "n": null}},
"closure": {"state": "absent-legacy"}, "issues_born": {"n": 0, "with_descope_amendment": 0}}
],
"fixes": [
{"id": "43-null-crash", "issue": 43, "status": "planned", "deps_unmet": [], "pr": null,
"review": {"last_checkpoint_sha": null, "unreviewed_diff": {"lines": null, "files": null},
"terminal_done": false, "adversarial": {"ran": null, "n": null}},
"closure": {"state": "n/a"}, "issues_born": {"n": 0, "with_descope_amendment": 0}}
],
"startable_now": ["05-auth", "fix-43"],
"blocked_units": {"09-billing": {"unmet": ["05-auth"], "build_order": ["05-auth", "09-billing"]}},
"open_prs": [{"number": 13, "unit": "07-csv-export", "ci": "green", "merge_ready": false}],
"pending_triage": [{"source": "docs/features/07-csv-export/known-issues.md", "title": "empty-file edge"}],
"untriaged_issues": {"count": 3, "oldest_open": [21, 33, 40]},
"workflow_observations": ["branch feat/07-csv-export is 1 commit ahead of origin"],
"urgent": {
"issues": [{"number": 51, "title": "prod webhook signature check bypassed", "label": "urgent"}],
"interruptibility": {"unit": "07-csv-export", "phase": "P2", "dirty": true, "tasks_from_boundary": 2}
},
"crash_recovery": {
"verdict": "CLEAN",
"branches": [
{"branch": "feat/07-csv-export", "evidence": "1 commit ahead of origin; ledger coherent", "verdict": "CLEAN", "resume_command": null}
]
}
}
}
startable_now, blocked_units (with build orders), design_candidates and
pending_triage are the keys an orchestrator routes on; every id in
startable_now/blocked_units must appear fully in features/fixes — an
idea unit appears ONLY in design_candidates (and detail.features), never
in startable_now, since it has no deps-met check to pass (design precedes
dependency startability). 05-auth above illustrates defined (not yet
planned): startable, next /plan-feature, phase fields null (no planning
artifacts yet).
Guardrails
- Read-only, always. No commit, push, issue, comment, label, or file edit —
not even fixing an obviously stale roadmap row (report it as a blocker of
kind
substrate instead; audit-docs is the fixer).
- Evidence discipline per the project's Workflow conventions: every status
comes from a command's output or a file's content; unverifiable →
null +
a workflow_observations note, never a guess.
- Forge unavailable → still report the git/docs view, with a
blockers entry
{"kind": "substrate", "id": "forge", "scope": "run"} so the orchestrator
knows PR-dependent states are unknown.
detail.urgent is presence-only and read-only, always. Derive it
exclusively from the labels object of gh issue list … --json … ,labels — never parse title, body, or comments, and never cross-check the
labeling actor's permission via the issue timeline (presence is already
triage+-gated by GitHub). This sensor never emits a pause-vs-finish
decision — only the facts the consumer's judge needs.
Portability (agents other than Claude Code)
The workflow is the contract; Claude Code features are conveniences. This
skill has no Claude Code dependency at all — it is the piece that lets ANY
driver (a shell loop, a CI job, another agent) orchestrate the workflow:
- No slash-command menu — open this
SKILL.md and follow it literally in
a fresh conversation, or invoke it headless (see
docs/workflow/ORCHESTRATION.md for per-agent invocation patterns).
- No per-skill
model:/effort: — this is mechanical reading and
counting: a cheap tier is enough; never spend a strong model here.
- No argument passing (
--last-envelope) — paste the persisted envelope
JSON into the invocation message: the skill treats the last fenced json
block of the request as the hint.
Relationship to other skills
- The sensor counterpart to
ship-roadmap's conductor: an external
orchestrator calls workflow-status → routes on the envelope → invokes
plan-feature / execute-phase / review-change / audit-pr /
triage-issue directly, choosing the model per step — the same loop without
the in-agent autopilot.
- Read-only sibling of
audit-docs (which judges coherence and can fix) and
product-audit (which judges health): this one only reports state.
- Schema owner:
orchestration-envelope (internal).
Done when
- Every roadmap/fix row, open PR, and in-flight folder was actually read, the
dependency closures are computed transitively, and inconsistencies are
reported (never repaired).
- The
CRASH RECOVERY sub-block was printed with a verdict from the decision
table, and the envelope state matches it (CLEAN→OK, RESUMABLE→CONTINUE,
AMBIGUOUS→NEEDS_INPUT).
- With
--last-envelope supplied: the no-progress guard ran — a stalled
/plan-feature//design-feature hint surfaces as a workflow_observations
note, never a silent bland repeat, with no new write path introduced.
- The human summary (unless
--json-only) and the envelope — with
design_candidates top-level and detail carrying features, fixes,
startable_now, blocked_units, open_prs, pending_triage, untriaged_issues
(count + oldest_open) and urgent (labels-only issue list +
interruptibility facts) — are printed, envelope last.
- Each
detail.features[]/detail.fixes[] entry additionally carries
review, closure, and issues_born (steps 10–12) — detail-scoped, no
schema change — and any fired triggers appear in a top-level
next.suggested[] (step 13), single-sourced from the owning skill's own
condition text.
- Nothing was modified anywhere.
→ Next: the envelope's next.recommended command — it is computed from the
actual state, so it IS the recommendation
· a human overview → read the printed table
· orchestrating programmatically → parse the last fenced json block