Use when the user asks to take a new task all the way: plan, implement, review, commit and push. Not when a ready task file already exists. Self-contained — execute the inlined steps directly, do NOT invoke other skills via the Skill tool.
59
74%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Low
Low-risk findings worth noting
Fix and improve this skill with Tessl
tessl review fix ./plugins/flowai/skills/ship/SKILL.mdSingle user-invoked command that drives a task end-to-end and reaches the remote in one invocation:
tasks role resolved from AGENTS.md.git push with upstream and divergence gates.Four explicit gates between phases:
git status MUST be non-empty.git status --porcelain MUST be empty, untracked files included, AND if pushing main/master with remote-ahead, the Push Phase will ask before pushing.<step_by_step>
Initialize
todo_write, todowrite) to create a plan based on these steps.YYYY-MM-DD format (e.g. via date +%Y-%m-%d or your environment's date primitive). Hold it as <DATE>. Derive <YYYY> and <MM> (zero-padded). Resolve the tasks role from AGENTS.md and then derive the eventual task file path from that role's layout.Deep Context & Uncertainty Resolution
SRS and SDS from AGENTS.md. If you don't know their current content, read the resolved files now.tasks role. For each found file, parse its YAML frontmatter implements: field. Keep only tasks whose implements: set has a non-empty intersection with the FR-IDs you are about to put in the new task's implements:. Cap at 10 by recency (newest first by frontmatter date); if more match, list IDs in chat without bodies and ask the user which to expand. Read the full body of each kept task before drafting the new task's body. List the loaded tasks in chat (one bullet per task: file path + matched FR-IDs + one-line summary). That list is the KEPT set and nothing else. Opening a candidate's frontmatter to test the intersection is not loading it: a task whose implements: shares no FR with yours must not appear in the list, must not be summarised, and must not reach related_tasks: — a rejected candidate presented as loaded context is the failure this filter exists to prevent. Name rejected candidates, if at all, in one separate line after the list (Checked, shares no FR: <path>), never as a list bullet. If no related tasks exist, say "No prior tasks share FRs with this one — drafting from scratch."Proactive Resolution from AGENTS.md: analyze prompt, codebase, search for gaps.glob, grep, ripgrep, search, webfetch) for unknowns.surface-scout): when the enumeration above ran AND your environment provides the pre-declared surface-scout agent, dispatch it now via your subagent tool (e.g. Task, Agent, task; on Codex the tool is spawn_agent — pass agent_type="surface-scout" and fork_turns="none" (a full-history fork cannot carry an agent type), then collect with wait_agent), naming surface-scout as the agent type. A generic exploration or search agent your host offers (Explore, general-purpose, and the like) is NOT a substitute: the cross-check's value comes from that agent's own instructions, and swapping it for a generic one silently turns the independent pass into an ordinary search. Input: the user's request text VERBATIM (plus quoted user clarification turns, if any) — NOTHING else. Do NOT include your own enumeration, your restatement of the request, a suspected fix location, or a preferred approach: the scout's value is independence, and seeding it with your framing destroys the cross-check. Ask for the report, not a launch — many subagent tools dispatch in the background by default and answer with a handle (an agent id plus token/duration counters) instead of the scout's findings. When your tool offers a foreground or blocking mode (a run_in_background: false parameter or equivalent), set it, so the call returns the report itself. If you nevertheless get back only a handle, CONTINUE THAT AGENT by its id — send it a message or resume it and ask it to state its findings — and use what it answers. Do NOT feed an agent id to a background-task polling tool: an agent id and a background-task id are different things, and the poll answers No task found with ID: <id> however long you give it. Redeeming that handle is a step you owe, not an option: a dispatch you launched and never collected has cost turns and bought nothing, and the pull to shrug and enumerate the tree yourself is strongest exactly there. Hold the collected report for step 3. You may quote the scout only from a message you can point to. If no message ever carried its text, write no quotation — say in one line that the report did not arrive, and let your own enumeration stand under your own name. Your enumeration is welcome in the surface list; it is a forgery under the scout's byline. Establish availability by LOOKING, never by assuming — list the agent directories (ls .claude/agents/ .cursor/agents/ .opencode/agent/ "${CODEX_HOME:-$HOME/.codex}/agents/" 2>/dev/null); a surface-scout file there — .md for most hosts, surface-scout.toml under the Codex home — means the agent is installed, so dispatch it. Running inside a sandbox or an automated harness is NOT evidence of missing subagents; decide from the listing, not from where you think you are. Doing the pass inline instead is the one outcome this step exists to prevent. Only an empty listing, or a dispatch that actually failed, skips it; step 3 then records the degradation visibly.Draft Framework (G-O-D)
mkdir -p or your environment's equivalent).date: YYYY-MM-DD, status: to do, implements: [FR-..., ...] (or []), tags: [...] (or []), related_tasks: [...] (or []). Write the empty list, never omit the key. Eliding one is a defect, and the two that get elided in practice are tags and related_tasks — the task-file example the accepted format carries shows only implements:, so a file copied from it looks finished while missing two keys.## Goal, ## Overview (with ### Context, ### Current State, ### Constraints), ## Definition of Done — seed one bullet per outcome the request states (behaviors, examples, acceptance conditions, deliverables), in the request's own terms; preserve stated expected results exactly (no paraphrase). Related outcomes may collapse into one bullet when a single acceptance check proves them all. No discrete outcomes stated → leave placeholder bullets. Acceptance tuples are added in step 5a.try-catch, and which tests prove error propagation is preserved.### Affected Surface under ## Overview (only when the affected-surface enumeration ran): FIRST the scout's collected report as a clearly delimited VERBATIM block (fenced, unedited — this is the raw record plan-critic, review, and the human recompute the divergence from). Only text the scout actually returned may go in that block: labelling your own enumeration as the scout's output is a fabricated quotation and a worse defect than having none — when you did not collect a report, omit the block and name the reason in one line instead. Copy it, do not retype it. Tidying is what breaks this in practice, and it does not read as tidying at the time: shortening absolute paths to repo-relative ones, dropping the scout's evidence lines because the bullet above already says it, merging two of its bullets, reflowing its headings. Each of those makes the block disagree with what the scout sent, and the whole point of the block is that a later reader can hold it against the scout's own words. Too long is not a reason to trim — a long block is the record working. If you find yourself improving the text, you are writing your own enumeration under someone else's byline. THEN one PLAIN bullet per surface item/class from the UNION of your list and the scout's — every row of the scout block gets its own bullet here, INCLUDING the rows the scout itself already marked not affected (copy its evidence into your bullet; the fenced block is the scout's record, the disposition list is yours, and a row present only inside the block has no disposition — observed 2026-09-06, plan-affected-surface-scout: the two scout rows for documents/ and .codex/ had no bullet and the critical item failed) — - <item> — <disposition>, where the disposition is one of: covered-by <DoD item / planned step>; not affected — <inspected evidence> (evidence forms by domain: code = path/lines; infrastructure = environment/service + config key; process = document/step/owner); deferred — human choice. Checkbox bullets (- [ ]/- [x]) are FORBIDDEN in this subsection — they would corrupt automatic task-status derivation. Items you are unsure about default to deferred — human choice and are surfaced at variant selection. Write the real content the moment you create the heading: a promise such as (surface enumeration will be added after variant analysis) is a FORBIDDEN placeholder — an empty heading reads as "nothing affected" to every downstream reader. Nothing to enumerate → omit the heading entirely rather than stub it.surface-scout file; the dispatch errored outright; or the dispatch ran but you could not obtain the scout's findings by any route. In every one of them the task file MUST carry, under ## Follow-ups, the line Surface cross-check (surface-scout) did not run — no subagent support in this environment. This is not optional bookkeeping: it is the only trace that the cross-check is missing, and its absence claims a verification that never happened. Create ## Follow-ups if the file has no such section yet.## Solution section yet.Strategic Analysis & Variant Selection
Variant Analysis from AGENTS.md.— partial: drops <constraint> and say the constraint is deferred rather than met. Name the debt in this variant's Cons in so many words — what it defers, and what it will cost to undo when the deferral runs out. "Simplest" and "fewest dependencies" are not that: they describe the build, not the bill, and a reader cannot weigh a cost nobody stated. Widening it until it needs no such marker deletes the archetype: the set then holds three correct designs and no fast one, and the trade-off the user was meant to weigh never reaches them.A., B., C., …); the option's own Essence, Pros, Cons, Risks and Best For sit under it, each on its own labelled line, in the layout the Question Format block above prescribes — never merged into one paragraph, and never folded into a single line. There is no separate variant write-up anywhere before the question — no ### Variant N sections, no bolded option headers followed later by a bare "which do you prefer?". Describing a variant twice is the defect this rule exists to prevent: the copies drift, and the reader reads the same thing twice to answer once.undefined for an input class), one variant MUST fix THAT helper — return the correct value for the input it mishandles — even when a team note discourages touching shared code. The note is an input to the ranking below, not a filter on the list: a plan that offers only consumer-side patches has already decided the question the user is asked to decide. (2026-09-06: plan-recommends-root-over-symptom offered three DB-side variants and none touched getTimezoneName().)— full scope when the variant covers all stated outcomes, or — partial: drops <dropped outcome(s)> when it does not. If the dropped list is too long for a title, use a count in the title (— partial: covers 2 of 4 stated outcomes) and name every dropped outcome in the variant's Cons. A bare partial marker without named outcomes or a count is a defect. Dropped outcomes MUST also be named in the variant's Cons. A stated outcome must appear in ≥1 variant or be explicitly named as deferred at selection time — a silent drop is a planning defect. The scope choice is made at variant selection on this visible information; after selection, record the chosen variant's dropped outcomes under ## Follow-ups in the task file. A partial marker is this rule working, not a defect it reports. Two things are defects: dropping an outcome silently, and enlarging a variant until it covers everything so that no marker is needed. The second one does not feel like a defect while you write it — it feels like refusing to offer a broken option — which is why it has to be named here. It deletes the choice the user was supposed to make, and it deletes it invisibly, because a set of uniformly full-scope variants still looks complete.Detail Solution (S) — execute immediately after user selects a variant
## Solution section placeholder with concrete implementation steps for the selected variant (follow the project's accepted task format).### Affected Surface exists: update its dispositions to the SELECTED variant — every covered-by must point at a step or DoD item of the chosen variant's Solution; items the chosen variant drops become deferred — human choice with a matching ## Follow-ups entry.
5a. Acceptance Tuple Check — execute immediately, no permission needed## Definition of Done. For each, confirm the tuple (FR-ID, Test path or Benchmark id, Evidence command) is present and concrete (no FR-ID for a requirement whose text is recorded nowhere — rule 8) (no placeholders like <TBD> or TODO). manual — <reviewer> is acceptable only with an explicit reviewer name.implements: that are absent from the resolved SRS, the task MUST contain an explicit DoD entry "add FR-XXX section to SRS with **Acceptance:** field filled".**Tasks:** Back-Pointer (FR-DOC-TASK-LINK) — execute immediately, no permission needed. This is a write step.implements: frontmatter, locate the heading ### <FR-ID>: in the resolved SRS.**Description:** bullet (- **Description:** ...). Look at the line(s) immediately following it within the same section.
- **Tasks:** [...] bullet already exists: append , [REF:task:<YYYY>-<MM>-<slug> | <slug>] to the comma-separated list. Idempotency: if the exact SALP REF is already in the list, do nothing for that FR.**Tasks:** bullet exists yet: insert a new line - **Tasks:** [REF:task:<YYYY>-<MM>-<slug> | <slug>] immediately AFTER the **Description:** bullet (before any other bullets in the section). The task: namespace id is <YYYY>-<MM>-<slug> (e.g. 2026-06-adopt-salp-anchors), derived from the task file's path.5b. Update Documentation Index (FR-DOC-INDEX) — execute immediately, no permission needed. This is a write step, not a planning step.
index from AGENTS.md. For every FR-ID in the task's implements: frontmatter, register a row there.index file does not exist, create it with a ## FR heading (additional sections like ## SDS, ## NFR may be added by other skills; do not pre-scaffold them here).## FR, ensure exactly one row per FR-ID. Row format (SALP):
- [REF:fr:<id> | <FR-ID>] — <one-line summary> — <status>
<id> — lower-kebab of the FR mnemonic (strip FR- prefix, lowercase, preserve . for hierarchical IDs like FR-DIST.MARKETPLACE → dist.marketplace). The reference resolves against the [ANC:fr:<id>] token next to the SRS heading. If the SRS section does not yet exist, write the REF anyway — develop/commit will add the matching ANC when the SRS section is added, at which point scripts/check-salp.ts will resolve it.<one-line summary> — pull from the SRS **Description:** first sentence if the section exists, otherwise reuse the task title (or a short paraphrase ≤80 chars).<status> — mirror the SRS **Status:** value if present, else [ ].## FR before writing.plan-critic): when the task file contains a ### Affected Surface scout block OR step 4 presented 2+ variants, AND your environment provides the pre-declared plan-critic agent — dispatch it via your subagent tool with the task file path as input (on Codex the tool is spawn_agent — pass agent_type="plan-critic" and fork_turns="none" (a full-history fork cannot carry an agent type), then collect with wait_agent), then COLLECT its report the same way as the scout in step 2 — ask for a foreground/blocking dispatch where the tool offers one, and if you get back only an agent id, continue that agent by its id rather than polling it as a background task. Present its returned objection list in chat VERBATIM as the critique for step 7. A fresh context is the point: do not summarize, soften, or pre-filter its objections. If the dispatch fails or the agent is not installed, fall back to self-critique below AND write one line under ## Follow-ups: Independent critique (plan-critic) did not run — no subagent support in this environment.## Follow-ups entry naming the deferral reason; additionally, every covered-by pointer in ### Affected Surface resolves to an existing Solution step or DoD item (no dangling pointers). Complements Rule 8 (items↔tuples); this check ensures nothing stated in the request is missing. The task file, not the chat, is the record.</step_by_step>
<step_by_step>
Re-read the Task File
tasks role from AGENTS.md and locate the matching task there. The user MUST provide either a path or an unambiguous task identifier; if it is missing, ask once.## Solution section. The implementation steps listed there are authoritative.Determine the Project Check Command
deno.json/deno.jsonc → deno task check; package.json → npm run check/test/lint; Makefile check target → make check; pyproject.toml → pytest / ruff check .; go.mod → go vet ./... && go test ./...) → "no automated checks configured".deno * creates deno.lock, any npm * resolves dependencies, etc.). Pre-flight artifacts in the working tree after verification are a bug.TDD Loop (per Solution step)
Repeat per Solution step
Final Verification
git status is non-empty (otherwise the Solution was a no-op — surface that and STOP).**6. Hand off to the next phase
</step_by_step>
git status is clean (no diff after Implement) → STOP. Nothing to review. Report "Implement Phase produced no changes — task may be doc-only or no-op; nothing to commit or push."git status shows changes outside Solution scope (tooling, formatting, unrelated docs) → STOP. Revert the out-of-scope edits and report.<step_by_step>
Empty Diff Guard — git diff --stat, git diff --cached --stat, git status --short. STOP only when ALL THREE are empty. A ?? entry in git status --short IS a change: two empty git diff --stat outputs plus one untracked file mean a non-empty review, so proceed. System gitStatus snapshot can be stale (hooks / parallel processes); if live status shows unauthored files clean per snapshot, ask user before staging.
Pre-flight Project Check
deno.json → deno task check/test; package.json → check/lint/test script; Makefile check → make check; pyproject.toml → pytest/ruff check .; go.mod → go vet ./... && go test ./...) → else "No automated checks configured" in the report and JiT subset disables (Rule 10). Do NOT guess. "Declares it" means the instructions record an invocation — a command line, or a task or script name that a manifest in the repository defines. A section that only names an interface (a "Standard Interface" list saying a check command exists) while the sections meant for the actual commands are empty declares nothing; fall through to manifest detection.deno task check without deno.json, no npm test without package.json, no make check without a Makefile. This rule chooses the PROJECT CHECK only. It does not stop step 4b from running a located test module with the runner that module is written for — deno test utils_test.ts, python -m pytest tests/test_x.py, go test ./pkg — whether or not a manifest exists.deno.lock, node_modules/, __pycache__/, .pytest_cache/). A leftover gets swept into the commit.[critical] and continue review. A command that never ran has not failed: exit 127, command not found, or a missing task or script name means the project declares no runnable check. Report it as [skipped] <command> — not runnable: <error> under Automated Checks, say "No automated checks configured", disable the JiT subset (Rule 10), and raise no [critical] for it.HEAD; commit-range → <range-start>^). Prefer git worktree add <SCRATCH>/jit-parent-<sid> <parent-sha> (full runnable tree); use
git show <parent-sha>:<path> fallback ONLY if worktree-add fails. BEFORE any JiT synthesis, run the SAME project test/check command from 2a inside the parent worktree to verify baseline is green. Two things do NOT satisfy this and are the usual substitutes: the run of that command you already did on the DIFF side (it says nothing about the parent), and running only your synthesized JiT probes against the parent (they are not the project's suite). The evidence this step wants is one invocation of the project's own command whose working directory is the parent tree. Fallback path OR red baseline → "JiT disabled — parent baseline unavailable/red" in Degradation Notes; review continues without the JiT subset.Gather Context
SRS, SDS, and tasks from AGENTS.md; read the resolved SRS/SDS files if their content is not already in context. A required role missing → report it and continue only with review steps that do not depend on that role. AGENTS.md is the FIRST document you open, and no conventional location is probed before it. The doc layout you have seen most often is one project's convention, not the shape of every project; a speculative cat <guessed-path> 2>/dev/null batched alongside the AGENTS.md read is still reading the wrong file first, and discarding its output afterwards does not undo that. Projects that keep their SRS and SDS somewhere else entirely exist, and they are exactly the ones this step is for.git diff (unstaged), git diff --cached (staged), or git log --oneline <base>..HEAD + git diff <base>..HEAD for branch-based changes.git diff does NOT show them — read each untracked file from step 1's git status and include it in scope.tasks role / task list).git log -1 --pretty=%B <parent-sha>..HEAD (or commit messages of the range). Optionally gh pr view --json body IF gh is available AND the branch has a PR; on missing/error proceed silently — PR body is a bonus.Parallel Delegation (after gathering context):
git diff --stat plus git diff --cached --stat plus wc -l over every untracked file from step 1. Count the lines before deciding; git diff --stat alone reports 0 for an untracked-only change set, and file COUNT is not the threshold (one 173-line file is a large diff, not a small one).Task, Agent, task; on Codex the tool is spawn_agent — pass agent_type="console-expert" and fork_turns="none" (a full-history fork cannot carry an agent type), then collect with wait_agent), each returning its result to you. Batching several tool calls in your own thread is NOT delegation: the point is to keep the check output out of your context, which only a separate context achieves. Establish availability by LOOKING, never by assuming: list the agent directories (ls .claude/agents/ .cursor/agents/ .opencode/agent/ "${CODEX_HOME:-$HOME/.codex}/agents/" 2>/dev/null). A file there — .md for most hosts, <name>.toml under the Codex home — means that agent is installed and dispatchable — dispatch it by name. Running inside a sandbox, a test harness, or an automated run is NOT evidence of missing subagents and is never a reason to skip: decide from the listing, not from where you think you are. Run the listing BEFORE any inline check, and quote its output in ### Degradation Notes whenever you claim delegation was unavailable. The claim is a factual report about this machine, and without the command's output behind it you are stating something you did not check: a review that ran the checks inline and wrote Parallel delegation unavailable on top of an installed agent directory reported a fact that was false. Inline is permitted only when the listing came back empty AND no subagent tool is in your toolset — both verified, not assumed. Do not silently call a batch of your own tool calls a delegation. Speed is not one of the two conditions. "Faster than spinning up a subagent", "the file is only 181 lines", "I have already read it" — none of these reopen the shortcut, which is decided once, by the line count in step 1, and not revisited per task. The point of delegation is to keep the check output out of YOUR context; doing the work yourself because it is quick spends exactly what the step exists to save. And it is per task, not per review: SA1 and SA2 are two dispatches, so delegating one and running the other inline is a partial degradation that must be recorded as such — it is not delegation performed.
console-expert); return pass/fail + full output.TODO, FIXME, HACK, XXX, console.log, temp_*, *.tmp, *.bak, hardcoded-secret patterns. Same delegation; return findings list.run_in_background: false parameter or equivalent), set it. If you still get back only a handle, continue that agent by its id — send it a message or resume it and ask for its result. Do NOT feed an agent id to a background-task polling tool: the two id kinds are different, and the poll answers No task found with ID: <id> however long you give it. Never write a delegated finding you did not actually receive. And never end your turn while a dispatch is outstanding. Sitting idle until a notification arrives is not waiting — the turn closes, the session ends there, and the review produces no verdict, no report and no findings at all: strictly worse than the inline run you were avoiding. Collecting is an action you take, not an event you receive.### Degradation Notes. No hard dependency on delegation success.QA: Task Completion
[critical] Missing.[critical] Phantom completion.SRS / SDS / README). A doc still describing behaviour the diff removed or changed → [warning] Doc drift naming the doc file:line and the contradicting hunk. Drift alone is NEVER [critical] and never blocks the verdict — the commit phase owns Documentation Sync, so escalating here would stop the workflow before the step that fixes it. Do NOT edit the doc in this review.4a. FR Coverage Audit (blocking gate — see Requirements Lifecycle in AGENTS.md)
SRS role resolves from AGENTS.md AND that AGENTS.md mandates FR traceability (acceptance references, [REF:fr:<id>] code markers). Neither present → skip 4a entirely, record FR coverage gate not applicable — project declares no requirements lifecycle in ### Degradation Notes, and NEVER emit a missing code marker / no acceptance reference finding. Inventing the convention for a project that does not use it turns every clean diff into Request Changes. The SRS role resolves but its file does not exist → skip 4a the same way: record FR coverage gate not applicable — <SRS path> does not exist in ### Degradation Notes, and NEVER emit a [critical] (or any blocking) finding for the missing document or for an acceptance reference it would hold. Creating the requirements document is init's job, not this review's; the commit gate reports the same absence in one line and commits.implements:; (b) FR sections added/modified in the diff to SRS; (c) [REF:fr:<id>] SALP markers touched in the diff.**Acceptance:** with a runnable ref (test path::name, benchmark id, command, or manual — <reviewer>); missing/placeholder → [critical] no acceptance reference. (2) Run the evidence command (or deno run -A scripts/check-fr-coverage.ts FR-<ID>); non-zero / failing / manual without reviewer → [critical] acceptance fails. (3) FR claimed implemented but no [REF:fr:<id>] marker in changed source → [critical] missing code marker. (4) DoD [x] with no evidence run/cached pass → [critical] Phantom completion. (5) Only when the project's AGENTS.md declares the mandatory **Rationale:** field (a Rationale-as-gate rule or an equivalent sentence): every FR section the diff ADDS or MODIFIES in SRS — scope (b) only, never an FR the diff merely cites — carries a non-empty **Rationale:** line saying why the requirement exists; missing or empty → [critical] no rationale, naming the FR. Report it; do not write the rationale yourself.Approve while any FR-gate issue remains.4b. QA: Existing-Suite Execution (blocking gate — Rule 13)
exclude list and any path it is scoped to. Each one can surface coverage the other two miss, and skipping the last two is how a suite that exists gets reported as absent.exclude or a scoped path argument skips — is precisely what this gate exists to reach: it was excluded for cost, not because it stopped being coverage. Before deciding what to run, read AGENTS.md for a section naming where such tests live and how to run them, and check the test-runner config for exclude entries and for scoped paths in the check task. If either points at a suite covering your changed symbols, run it. "The project check passed" is not an answer to this step; name the modules you located and the command you ran on each.[critical]; verdict cannot be Approve. A pre-existing test that contradicts the diff is a finding under Rule 12, not a file to update. Use the project's scoped test command when it has one; otherwise run the module with the runner it is written for (deno test <file>, python -m pytest <file>, go test ./<pkg>). A missing manifest does not stop this run — the manifest rule of step 2 picks the project check only — and step 2's cleanup applies to whatever the run leaves behind.[warning].### Degradation Notes; never fabricate a pass. The gate stays unsatisfied, yet nothing in the change has been shown wrong, so the verdict is Needs Discussion — not Request Changes and not Approve: the human decides whether to merge without that run.QA: Hygiene (use SA2 result if available; else inline)
Hygiene: clean (N patterns scanned over the diff).print output and hardcoded secrets or localhost URLs.TODO/FIXME/HACK/XXX introduced in this diff (distinguish from pre-existing ones).[warning] Entire directory deleted — confirm intentional; ask the user to verify before proceeding.Code Review: Design & Architecture
Code Review: Implementation Quality
[warning] (Rule 6).any, untyped parameters, missing return types (where project conventions require).Code Review: Readability & Style
8a. Mutant + Catching Test Synthesis (JiT) (skip on pure-deletion diff or JiT-disabled flag)
8b. Dual-Run + Filter (JiT) (skip if Step 8a skipped)
Mutant kill-rate skipped — single test invocation exceeded 30 s threshold (recorded N s) in Degradation Notes so the lost signal stays visible.Run Automated Checks (collect from step 2 and/or SA1)
Review Report — verdict on first line. Include JiT sections (Intents, Catching Tests, Uncovered Risks, Degradation Notes) only when the JiT subset ran (or was disabled — Degradation Notes then explains why). Section order:
## Review: [Approve | Request Changes | Needs Discussion] <!-- count your [critical] findings BEFORE writing this line; zero criticals with no surviving catching test and no unsatisfied applicable gate => Approve -->
### Verdict (plain language) — 2–4 sentences a non-coder acts on, answering ALL four questions explicitly, each in words of its own: (1) is the task complete; (2) is the design sound — say so even with no concern ("the design is sound"); (3) the key risks, or that there are none; (4) the next step. A blocking finding never replaces the design answer: a change can be well designed and still not mergeable. Accept WITHOUT reading the diff. MUST come first.
### Intents (≤5)
### QA Findings — [severity] file:line — description
### Code Review Findings — [severity] file:line — description
### Catching Tests (pass on parent, fail on diff) — name, intent #, mutant killed?, failure, file:line
### Uncovered Risks — risk + reason no test (non-deterministic / I/O / etc.)
### Existing-Suite Check — pre-existing modules (incl. caller tests) + run result, or why not run
### Automated Checks — [pass|fail|skipped] command — summary
### Degradation Notes — which JiT step was skipped and why
### Summary — requirements X/Y; catching tests N; hygiene scan result (findings or `clean`); critical/warning/nit counts
### Diff (optional) — offer diff/details for optional inspection; verdict stands without it; never block (Model B). MUST be the report's last section.This report is a progress message, not your final answer. The review is one phase of a longer run, and the report closes that phase only. Send it as an intermediate update — on a host that separates progress messages from the final answer, use the progress channel — and keep working in the same turn: finish step 11 — here the save / discard prompt does not pause you: discard the scratch directory without waiting and say in the report that you discarded it — then read the gate that follows this phase and carry on into the next phase. Ending the turn on this report ends the whole run with nothing committed, even after an Approve. Only a verdict the gate sends to STOP ends the run here.
After the verdict the review is over — including when the author asks you to fix what you found. A reviewer who repairs the code then reports on their own work, and the finding that made the verdict disappears with the repair, so nobody can check it afterwards. Answer that the fix is the author's to make, or belongs to a separate implement run that the author starts by invoking that workflow by name. A reply in this conversation — "yes", "go ahead", "fix it", "add the tests", an answer to a question your report asked — is not that invocation, however explicit, and does not hand the fix to you. Leave the verdict where it stands. This holds for production files, for tests, for the requirement documents in the diff, AND for pre-existing tests: when a pre-existing test contradicts the diff, the contradiction IS the finding, and editing either side deletes the evidence rather than resolving it. Continuing to run the project's checks or to explain a fix in words is fine; writing the fix is not.
Verdict selection (decide in this order, first match wins): (1) Request Changes — ≥1 [critical] finding, ≥1 surviving catching test, or an APPLICABLE blocking gate (4a FR coverage where 4a applies at all, 4b existing-suite, Rule 13) still unsatisfied — except a 4b module that could not run locally, which goes to (2). (2) Needs Discussion — no blocker, but a design decision genuinely needs the human before merging, or a located pre-existing module could not run locally (step 4b). (3) Approve — everything else, and this branch is compulsory once reached. [warning] and [nit] findings NEVER force Request Changes on their own: list them, hand them to the author, approve. An Approve carrying warnings is the normal outcome, not a contradiction. Rank findings top-5 by severity × uniqueness. No issues AND zero catching tests → "Changes look good. All requirements covered, no issues found, no behavioural regressions detected." (last clause only when JiT actually ran).
save <name> / save all / discard all. On save: propose destination beside file-under-test, confirm, git mv, stage. On discard all (default for timeout/ambiguous): delete entire scratch directory, leave no stray files. A scratch directory you made is yours to remove, and the verdict is not the end of the run while one is still on disk. This step goes missing in the same way every time: the report reads finished, the catching test has already done its job, and the leftover directory is invisible to you and permanent to everyone else — a run that names its scratch path in the report and then leaves it there without asking has documented the mess rather than cleaned it. Whether you wait for the answer depends on the report's role, stated right after the report template. Removing the parent worktree is a different cleanup and does not cover this.</step_by_step>
After completing the Review report:
Approve → the review report you just sent is not the end of the run. Continue in this same turn with the Commit Phase below and execute its steps in order. DO NOT commit yet — the commit belongs to the Commit Phase's own commit step, after its documentation sync and grouping.Request Changes or Needs Discussion → output the full report and STOP. Do NOT commit or push.<step_by_step>
git status -s to confirm nothing changed between phases.git diff is banned for the rest of this phase — every form of it. Not git diff, not --cached, not --stat, not a single file path, not "just to confirm what I staged" and not "to check the working tree matches the index". You have already read this content; git add moves files between index and working tree and cannot alter what is inside them, so a second read can only return what you already have, at the cost of the context this phase exists to save. git status -s answers every question you legitimately have here, because the only open question is WHICH files are staged.[REF:...] marker, so let me re-run the tests" re-pays the whole cost of the suite to re-confirm a verdict that is already in your context. If an edit in this phase COULD change behaviour, that edit does not belong in this phase.*_test.*, *.test.*), CI (.github/), acceptance tests (acceptance-tests/), formatting, or dev-environment (.devcontainer/). → Skip doc sync. Output: Documentation sync: skipped — infra-only changes (tests/CI/acceptance-tests).SRS, SDS, and index are ROLES, not filenames. Read ./AGENTS.md and take the path each role is bound to — a project may put its SDS at architecture/system.md or its SRS at specs/product.md, and the mapping below then means those files. Never go looking for design.md / requirements.md by name, and never create a file at the conventional path when the role already points somewhere else: that leaves the real document stale and adds a second one nobody reads. Take the WHOLE list the project declares, not just the three named roles — a documentation hierarchy that also binds, say, an API reference or an operations runbook is naming documents this step must keep in sync, and a change belonging to one of them does not become the SDS's problem because the SDS is the familiar name. A role you cannot resolve → say so in one line and sync only what you can. The same holds for a role whose bound path does not exist on disk: output Documentation sync: <role> → <path> is missing — not created, then continue — nothing in this workflow creates a project document, and a missing document is not a missing input../AGENTS.md has a ## Documentation Map section. If yes → use the path→document mapping from there. If no → use the default mapping:
grep the OLD string across the resolved documents and README, and take the set from the hits. Searching for the NEW name instead returns nothing, which reads as "docs are fine" — that is how a rename passes this step with every document left stale.tasks from AGENTS.md and read that file there. Do NOT scan all task files.Documentation sync: block with one line for EVERY document resolved
above — <path>: updated (<section or what changed>) or
<path>: no change — <reason>. The block is the step's output, not a
courtesy: a closing sentence such as "updated the SRS, SDS and README"
does not say which document was checked and why one was left alone, and
the user cannot tell a skipped document from an unaffected one.
(2026-09-02 and 2026-09-06: commit-doc-sync-gate synced all three
documents correctly in both runs and failed the same item both times
because the result was reported only as a summary sentence.)git status --short. When step 1 reused a diff from a prior phase rather than reading one, that is the whole check: staging moves files, it cannot change content you have already read, so do NOT run git diff in any form (--cached, --stat, per-file) to re-confirm it.tasks role with date: frontmatter (skip legacy flat-path), first check frontmatter status:. If it is superseded, require/keep superseded_by: and skip DoD derivation because the stale original DoD no longer maps to current reality. Otherwise count top-level - [ ]/- [x] items in ## Definition of Done. Derive status: K=0→"to do", 0<K<N→"in progress", K=N→"done" (warn if no DoD). Rewrite frontmatter and git add if it differs. Idempotent. Never downgrade done. Warn-only on parse errors.date: frontmatter or legacy flat-path) are persistent canonical records; commit MUST NOT delete them, regardless of DoD completion. The only lifecycle action is the status derivation in step 4.3; status: superseded records are preserved.git status to confirm all changes are committed.git status --porcelain MUST print NOTHING. Modified files, staged files and untracked files all count — an untracked leftover is not an "edit", and reading this gate as edits-only is how it gets passed. Any output → STOP, list the files, and do not push.main / master AND the remote is ahead, the Push Phase below will ask the user before pushing (per FR-ATOM-PUSH safety contract); do not pre-empt that gate here.<step_by_step>
Identify Target Branch
git rev-parse --abbrev-ref HEAD to identify the current branch as <CURRENT>.<CURRENT>. If different, STOP and ask "You typed <typed> but the current branch is <CURRENT>. Push <CURRENT> or check out <typed> first?". Wait for an answer.<CURRENT>.Resolve Upstream
git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null to find the upstream of <CURRENT>.<CURRENT> has no upstream. Should I run git push --set-upstream origin <CURRENT>?". Wait for an answer.
--set-upstream origin <CURRENT>.Safety Checks
origin/main or origin/master, run git fetch origin <CURRENT> then git rev-list --left-right --count HEAD...@{u}. If the right count (commits the remote has and local does not) is > 0, ASK the user with EXACTLY two options: "Remote <CURRENT> is ahead by N commit(s) the local branch does not have. Force is REFUSED on protected branches even with explicit authorization. Pull and rebase first, or abort?". --force / --force-with-lease are NOT options — never present them, never run them, even if the user volunteers "force", "overwrite", or "yes I want to overwrite the remote". If the user pushes back, restate the refusal and re-offer pull-rebase / abort.git push (which will fail safely if non-fast-forward). DO NOT proactively suggest force.--force request: if the user has explicitly asked for --force, decline. Explain --force-with-lease and proceed only with per-push user authorization (see Rule 2).Push
GIT_PAGER=cat git push [--set-upstream origin <CURRENT>]. Stream stdout + stderr to the user. Do NOT silence error output.--force or --force-with-lease. Surface the failure and STOP.Post-Push Verification
git rev-parse @{u} and git rev-parse HEAD. Compare.<remote>, HEAD = <local>. Investigate before retrying."Await CI (FR-ATOM-PUSH.CI-AWAIT)
## CI/CD section.
No CI declared in AGENTS.md — skipping CI await. and continue to step 7 (`7. Hand off to the next phaseskipped, green, or not reached — stopped earlier).ship and ship-task the Reflect Phase follows it, in this same turn.`).
Provider: or Status command:): STOP with ## CI/CD section is malformed — required keys: Provider, Status command. Found: <list of present keys>. Do NOT silently fall back. Do NOT continue to step 7.## CI/CD):
Poll interval: — seconds between status polls. Default 60. Accept integer seconds; a value <5 is treated as malformed and STOPs the atom.Wall-clock budget: — soft cap on total await time. Default 1800 (30 min). Accept integer seconds.ITER_CAP = max(1, ceil(<budget> / <poll interval>)). The iteration count — NOT wall-clock — bounds the cap so it stays deterministic across IDE-harness latencies. With defaults, ITER_CAP = 30; a project that sets Poll interval: 10 and Wall-clock budget: 60 gets ITER_CAP = 6.export SHA=$(git rev-parse HEAD). All CI commands below inherit $SHA from the agent's shell.Status command. Treat:
CI declared but no run was triggered by $SHA within 60 s — verify the workflow trigger configuration. Do NOT continue to step 7.ITER_CAP iterations × <poll interval> s sleep ≈ <wall-clock budget> wall-clock):
Status command.<poll interval> s. Re-invoke. Increment iteration counter.ITER_CAP iterations completed without a terminal status (exit 0 or 1), STOP with a loud single-line message: CI ANOMALY: <ITER_CAP> iterations × <poll interval>s = <budget>s elapsed without a terminal verdict. Run URL: <result of Run URL command if defined, otherwise "not available">. Last status exit: 2 (in-progress). Treat as an incident (hanging job, queue starvation, runner outage) — do NOT continue silently. Do NOT invoke investigate (no failed-job logs to feed it yet — the build is still running, not red). Do NOT continue to step 7. The user owns the next action: extend the budget by re-running the atom after raising Wall-clock budget, cancel the CI run, or investigate the runner.Logs command is defined in AGENTS.md, execute it. Capture stdout. Truncate to 12 KB (investigate can fetch more via the run URL if it needs to drill deeper).Run URL command is defined, execute it. Capture stdout as the run URL.@{u} == HEAD (step 5) proves only that the commit reached the remote, and a push leaves uncommitted work in place. The investigation therefore runs in a temporary worktree of the pushed commit, which is clean by construction; the handoff prompt below says so. Do NOT stash, reset, restore or commit the developer's uncommitted files to make the tree clean.investigate skill via the host IDE's skill-invocation primitive (Skill tool / /flowai:investigate slash command / inline expansion of its SKILL.md) with this prompt:
CI failed for commit $SHA on branch <CURRENT>. Run URL: <URL or "not available">. Failed-job logs (truncated to 12 KB):\n<LOGS or "not available">\nDiagnose the root cause in a temporary git worktree of $SHA (git worktree add <scratch dir> $SHA), not in the current working tree, and remove that worktree when done. Do not apply a fix; report findings.investigate returns its report, STOP. Do NOT continue to step 7 — the push succeeded but the build is broken; the user owns the remediation decision.**7. Hand off to the next phase
skipped, green, or not reached — stopped earlier).ship and ship-task the Reflect Phase follows it, in this same turn.**</step_by_step>
<step_by_step>
Decide whether this session earned a reflection
Session complexity: none detected — skipping reflection, or Detected retries and a user correction — reflecting. A silent skip is indistinguishable from a forgotten step.Audit the session
Criticise your own findings — before you touch a file
AGENTS.md keeps task notes out of it; it does not protect a wrong fact in it.Apply the corrective edits
AGENTS.md, **/CLAUDE.md, and the rule or skill files those point at. Confirm it: the path you edited must appear under git status.memory/ directory under your home, a personal notes file, anything outside the repository) feels like it captures the finding and does not: it is not versioned, it does not reach anyone else working here, and a fresh checkout has none of it. It is also how this step gets silently skipped — measured 2026-08-19, one run in three wrote to memory/ and reported there was nothing to commit. Use it in addition if you like, never instead.Show the edits and commit them — no question here
git status. Stage ONLY the files you edited in step 4, by explicit path — never git add -A, never .; a working tree you did not touch is not yours to commit.agent and a scope naming what you tightened: agent: apply reflect-suggested improvements, or narrower, e.g. agent(commit): tighten the doc-audit gate. The type is agent because the change is to the agent's own instructions — never docs:, chore: or feat:, whatever the file extension says. Never amend an existing commit.Ask whether to push it — and end your turn on the question
Committed as <sha> on <branch>. Push it? Reply "yes" to push, or "no" to drop the commit and the edits with it.yes: run git push with no flags, then verify git rev-parse @{u} matches HEAD and say so. Never --force, never --force-with-lease, never --set-upstream. If the branch has no upstream, or the remote has commits you do not, do NOT improvise: report it and tell the user to run the project's push workflow, which owns those decisions.no: the edits are not wanted. Undo the commit and the edits together — a declined improvement left sitting in the branch is the same unwanted change, one git log further away.
git rev-parse HEAD still equals the sha you reported — nothing landed on top of your commit. (b) git status --porcelain prints nothing — the tree carries no other work. Both hold by construction here: the earlier phases committed and pushed everything, and you committed your own edits in step 5.git reset --hard HEAD~1. That is the whole undo: the commit and the file contents go together, and there is nothing to restore by hand.--hard reset discards uncommitted work outright, and whatever made those checks fail is work you did not put there. Report what you found, leave your commit in place, and let the user decide.git log -1 that your commit is gone, and say in one line what you removed.TOTAL STOP
Output a combined summary:
korchasa/flowai-plugins
240d92b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.