| name | ready-check |
| description | The PM-side readiness gate — "airport security for PRDs." A PM (or anyone) gives a PRD as any source — JIRA / Linear / Notion / Confluence / Google Doc / Slack link / GitHub issue / Figma URL, pasted text, an image, or a plain description — and the agent scores it against the Definition of Ready, surfaces the blockers the PM can fix themselves in plain language, and (when ready) mints a tamper-evident gate code + a clean hand-off for dev. Runs identically in Claude Code and Cowork. Use when the user says "ready check", "is this PRD ready", "check my PRD before dev", "gate this requirement", or invokes `/ready-check <source>` / `/ready <source>`. This is Level 1; the dev-side deep pass is `/contract pickup`. |
Ready Check (PM-side gate)
The world this is built for: a PM has a PRD and is about to hand it to
engineering for grooming and estimation. Today, half-baked PRDs get through,
dev chases the PM for basics, and dev eats the delay. Ready Check is the cheap,
2-minute check the PM clears first. Green → a gate code + hand-off; anything
missing → the exact gaps, in plain language.
It is deliberately a floor, not the deep judge. The precise, code-grounded
review (regression, security, feasibility, estimate) is Level 2 —
skills/contract-pickup, dev-side. Read reference/READY-CHECK-RUBRIC.md for
the rubric and the PM-vs-dev dividing line.
Works for EVERY PRD — frontend, backend, API, data, infra
This gate is stack-neutral. It is not a UI-only tool, and there is no "this
is backend, the tool doesn't apply" escape. Every rubric item has a sensible
reading for a pure-backend or API PRD — treat the item, not the label:
| item | frontend reading | backend / API / data reading |
|---|
design | Figma / mock link | interface / contract spec — OpenAPI, proto, GraphQL schema, event schema |
states | empty / loading / done / error copy | response & failure contract — 2xx/4xx/5xx codes, error bodies, ret/idempotency |
flows | screens touched | endpoints, jobs, queues, consumers touched |
scope | platforms (iOS/Android/web) | services in scope, and what's explicitly out |
l10n | copy final & localised | response/payload format finalised (schema version, error codes) |
writer | writer/creator surface | downstream consumers (services, webhooks, subscribers) |
events | analytics events | metrics / logs / traces (latency, error-rate, throughput) |
A backend PRD is not cleared by N/A-ing the UI items — it's cleared by
answering the backend equivalents above. If a PM says "it won't work, this is
backend," that's the signal to run it with these readings, not to skip it.
Runs in both surfaces
This skill is plain markdown + one deterministic script, so it behaves
identically in Claude Code and Cowork. The offline web page
(gate/prd-readiness-gate.html) is the same rubric for PMs with no Claude
open; it mints the same gate-code format, so a code from any surface verifies
in all of them.
Opening & auto-syncing the live panel in Cowork (the artifact)
Prefer the chat flow. Running /ready-check <link> in chat is the primary,
most reliable experience — it renders a live, panel-like scorecard (Phase 4)
and fixes are a one-line reply. It has none of the sandbox limitations of the
artifact (no per-user connector wiring, no snapshot/version drift, no flaky
in-page bridge), so lead with it. The sidebar panel below is an optional
convenience for PMs who want an always-open surface; when a PM hits panel
trouble (a link won't fetch, a stale build), the answer is "run `/ready-check
` in chat" — it just works.
In Cowork, you can also give the user a persistent sidebar panel — the
artifact built from gate/ready-check-cowork.html, where the deterministic gate
runs in-page and the edge-case review is done by Claude via
window.cowork.askClaude (no backend). The plugin can't ship the artifact itself
(plugins bundle skills/commands, not artifacts), so create it on demand.
The pinned panel is a saved snapshot, not a live link to the plugin file.
Cowork stores a copy of the HTML when the artifact is created and never
re-reads the plugin file on its own — so the panel does not magically update
when the plugin updates. It only refreshes when THIS skill reconciles it with
mcp__cowork__update_artifact. To make that reliable, run the reconciliation
below at the start of every Cowork invocation of ready-check (not only
/ready-check panel / "open the panel") — that way the pinned page self-heals
to the current build whenever the PM uses Ready Check. It's cheap: one
list_artifacts plus at most one Read.
Reconcile (create-or-update, id ready-check):
-
List. Call mcp__cowork__list_artifacts; find the artifact whose id is
ready-check and note its path.
-
Compare build numbers — read them from the file contents, not the path.
Both the plugin's bundled gate/ready-check-cowork.html and the stored
artifact carry <meta name="ready-check-build" content="N">. Read the
bundled file and, if the artifact exists, Read the file at its path, and
parse the integer N from that meta tag in each. (Reading N "from the path
string" — the old instruction — never worked; that's why the panel went
stale. You must open the file and read the tag.)
-
Act on the comparison:
- No
ready-check artifact yet → create it (fields below).
- Bundled N > stored N → the plugin shipped a newer panel: this is the
step that was being skipped. Call
mcp__cowork__update_artifact with id
ready-check, html_path = the baked scratch file (the bundled panel with
__RC_ATLASSIAN_MCP__ replaced by THIS user's discovered prefix — see the bake
step under the create fields), a one-line update_summary (e.g. "Ready Check
panel → build N"), and re-pass mcp_tools with THIS user's Atlassian tools.
Both matter: the id is unique per user, so a panel that shipped with someone
else's id (or none) can't fetch links until it's re-baked + re-granted here.
Approve and the pinned page updates in place.
- Stored build unparseable / missing tag (a pre-build-tag artifact) → treat
as older and update it.
- Equal (or stored ≥ bundled) → do nothing; only mention "already current"
if the user explicitly asked to open/refresh the panel.
Never create a second artifact with the same id.
Discover this user's Atlassian prefix first — ToolSearch for
getJiraIssue and take its mcp__<id>__ prefix. The connector id is unique
per user, so this must be discovered in THIS user's session, never hardcoded.
BAKE that prefix into the panel HTML before creating/updating — this is
what makes link-fetch work reliably for every user (not just whoever's id was
shipped). Don't pass the bundled file directly:
, replace the literal token
with the discovered prefix (e.g. ), the result to a
scratch file, and use THAT scratch file as for both
and . (If Atlassian isn't registered, leave
the token as-is — the panel then cleanly prompts "paste the text / use
/ready-check in chat" instead of calling a wrong id.) A manual
in the panel console still overrides.
Bump the ready-check-build number in gate/ready-check-cowork.html whenever
you change that file, so the reconciliation above can tell "newer" from "same."
Two things gate whether a PM actually sees a new panel: (1) the plugin must be
updated in their Cowork — the pinned page is only ever as new as the bundled
gate/ready-check-cowork.html; if the plugin is still on an old version there's
nothing newer to push. (2) the skill must run so it can call
update_artifact — Cowork does not watch the file, so "auto" here means "on your
next /ready-check", not "instantly on plugin update." If a PM is stuck on an old
panel, have them update the Manifest plugin, then run /ready-check once (or
/ready-check panel) to pull the latest build.
Only works in Cowork (the artifact bridge is Cowork-only). In Claude Code there
is no sidebar panel — the /ready-check chat flow is the way. If the bundled
HTML can't be found, fall back to telling the user to open
gate/ready-check-cowork.html from the plugin folder directly.
Optional — JIRA/Confluence-link fetching in the panel. The panel can fetch a
pasted Atlassian link through the user's connector, but the connector-id is an
opaque per-install hash, so it must be discovered, not hard-coded. When you
create/update the artifact:
- Discover the prefix.
ToolSearch for getJiraIssue (and
getAccessibleAtlassianResources). Take the returned tool name and extract its
mcp__<connector-id>__ prefix.
- Pass the tools in
mcp_tools (…getJiraIssue,
…getAccessibleAtlassianResources, …getConfluencePage, and, if publishing,
…editJiraIssue / …createJiraIssue).
- Point the panel at that prefix. The panel reads the prefix from
localStorage['rc-atlassian-mcp'] (falling back to a documented default), and
exposes setAtlassianConnector('mcp__<connector-id>__') for a dev to set it by
hand from the console. Tell the user the one-liner if their links aren't being
picked up. Broaden-URL handling now also accepts sharable/Rovo links and links
with a little surrounding text, so a plain browse/KEY isn't required.
If no Atlassian connector is registered, Atlassian links simply prompt the PM to
paste the text or use /ready-check <link> in chat (which fetches via the
connector regardless of the panel).
The engine vs the judgement
Two layers, mirroring how contracts work (validate.mjs + critics):
- Deterministic engine —
scripts/ready-check.mjs. Scores presence +
specificity of every required item, mints/verifies the gate code, renders the
hand-off. Never an LLM guess. You MUST call it for the verdict and the code —
do not eyeball readiness or invent a code.
- Judgement — you. On top of the engine, run the text-judgeable critics
so the PM sees real blockers, not just empty fields: is the goal measurable,
are obvious edge cases missing, are the UI states named, is the metric a
number. This is what makes the check smart instead of a checklist.
Process (5 phases)
1 Fetch + expand Read the PRD from whatever source was given
2 Map to answers Fill the 11 items from the text; ask the PM only what's missing
3 Judge Run text-judgeable critics; flag blockers in plain language
4 Score + mint Call ready-check.mjs → verdict; if ready, gate code + hand-off
5 Hand off PM pastes the hand-off into the ticket / dev thread
Phase 1 — Fetch + expand
Accept any input: a URL (JIRA, Linear, Notion, Confluence, Google Doc, Slack
message, GitHub issue, Figma), pasted text, an image (screenshot of slides / a
Figma frame), or a plain description. Fetch it with the matching MCP or reader.
If it's a Figma link, treat that as the design artifact for item 3. If multiple
sources are pasted, use them all.
Fresh fetch, every run — never answer from a previous PRD. Each invocation is
about the source given right now. Follow this every time, with no shortcuts:
- Fetch the current source before you judge anything. Do not emit a verdict,
score, or gate code until you have actually fetched THIS source in THIS run and
can quote specifics from it. A confident-looking answer that isn't grounded in a
fresh fetch is the bug PMs reported ("it showed results for the previous PRD").
- Echo what you fetched back to the PM first — the issue key / page id and the
title (e.g. "Fetched ENG-1421 'FTC enhancement — pre-auth flow'; analysing this
one."). This lets a wrong or stale doc get caught immediately.
- A new link/source discards the previous one. If the PM gives a different PRD
than a moment ago, start clean — do not carry over the earlier PRD's answers,
findings, or verdict. When in doubt, treat it as a brand-new check.
- If the fetch fails, say so and stop — ask the PM to paste the text or connect
the source. Never fall back to answering from memory or an earlier run.
Discover the connector before deciding it's missing (Cowork especially). In
Cowork, connector tools are deferred — they do not appear in your tool list
until you load them with ToolSearch. So a Confluence/JIRA/Notion link does not
mean "not connected" just because you don't see the tool yet. When the source is
such a link:
- Search for the fetch tool first. Call
ToolSearch with the obvious
keywords before concluding anything — e.g. confluence page, getConfluencePage,
jira issue, getJiraIssue, notion page. If a matching tool comes back,
it's connected; load it and fetch. (Atlassian typically exposes
getConfluencePage, searchConfluenceUsingCql, getJiraIssue,
getAccessibleAtlassianResources under a mcp__<connector-id>__… prefix; the
<connector-id> is an opaque hash, so match by the tool name, not a guessed
prefix.)
- If ToolSearch returns nothing, then the connector genuinely isn't
registered — now ask the PM to paste the text or upload the doc, and mention
they can connect Atlassian in Cowork connector settings to skip this next time.
- Sharable JIRA links work too, not just
browse/<KEY>. Extract the issue
key by scanning the URL for the first [A-Z][A-Z0-9]+-\d+ (covers
selectedIssue=, /issues/<KEY>, browse/<KEY>), then getJiraIssue. See
reference/JIRA-SYNC.md.
- Confluence TINY links work too — the
…/wiki/x/<id> form PMs actually copy
from the Share button (e.g. …/wiki/x/Z4IElg) has no numeric page id. Don't
reject it: getConfluencePage accepts the tiny-link id as pageId directly.
Take /pages/(\d+) if present, else /wiki/x/([A-Za-z0-9_-]+), and pass
whichever you got.
- Confluence exposes no stable version number via this MCP — only a relative
lastModified ("yesterday at 5:35 AM"), which is useless as a freeze pin
because its meaning changes with time. For source.version on a Confluence
PRD, use a content hash of the fetched body instead:
node scripts/ready-check.mjs --hash <body.txt>. At pickup, re-fetch and
re-hash — any edit to the page changes the hash and trips STALE-SOURCE.
(JIRA is fine: use the issue's updated timestamp.)
- Never say "X isn't connected" without having run ToolSearch for it. That
was the failure mode — reporting a connector absent when it was only deferred.
If nothing usable is given, ask the PM the 11 questions directly — the rubric
doubles as an interview.
Phase 2 — Map the text to the 11 items
For each rubric item, pull the answer from the source if it's there. Build an
answers object:
{ "title": "<feature name>",
"items": {
"goal": { "detail": "..." },
"design": { "detail": "https://figma.com/..." }, // or { "na": true } if no UI
"oldbeh": { "na": true }, // brand-new feature
... } }
Only skippable items (design, oldbeh, states, writer) may take N/A — and N/A
is never a free pass. A bare { "na": true } no longer clears the item; it
only clears as { "na": true, "reason": "<why it genuinely doesn't apply>" }
with a reason ≥ 3 chars, which rides into the hand-off for dev to accept or push
back. Prefer the stack-appropriate answer over a skip: for a backend/API
change, design is the interface/contract spec (OpenAPI, proto, schema) and
states is the response & failure contract (status codes, error bodies) —
answer those rather than marking them N/A. Reserve N/A for items that truly have
no analogue (e.g. a pure internal cron with no consumers → writer N/A "no
downstream consumers"). For everything you can't find, ask the PM in plain words,
one short batch. Do not invent answers.
Waivers — when the PM genuinely can't answer a required item. Any required
item may be waived with a written reason (e.g. the metric is owned by another
team, a decision is pending). Record it as { "waived": true, "reason": "..." }:
"metric": { "waived": true, "reason": "owned by growth; target decided this week" }
A waiver clears the item so the PRD can move, but the engine hashes the reason
into the code and surfaces it in the hand-off under a "Waivers (dev to accept or
push back)" block. An empty/absent reason does not clear. Offer a waiver
only when the PM truly can't answer — not as a shortcut past a question they
could answer.
Phase 3 — Judge (make it smart — actually find the edge cases)
This is the heart of the check, and it must go beyond "is the field filled."
Actively run the text-judgeable critics on the PRD and surface real
findings the PM can fix themselves — do not just confirm presence.
- Edge cases — always run the
critic-edge-cases critic on the PRD.
Enumerate the failure/boundary cases this specific feature implies and
check each against the PRD: empty, offline, timeout, expired, concurrent
edit, permission denied, partial failure, mid-flow cancel, and any
domain-specific ones. List every case the PRD doesn't cover, one plain
line each. A PRD whose edge answer misses cases the critic finds is
not ready just because the field is non-empty — reopen it.
- Clarity (
critic-minimality) — is the goal a real problem and the
metric an actual number with a window? "Improve engagement" fails; "+6% D1
retention in 4 weeks" passes.
- UI states (
critic-comms-completeness) — for a user-facing change, is
there copy for nothing-yet / loading / done / error?
- Instrumentation (
critic-instrumentation) — will the stated events
actually let us measure the success metric? If not, name the gap.
- Scope / parity — explicit out-of-scope present? Behavior consistent
across the stated platforms?
- Design audit (when a Figma link is present). If the
design item is a
Figma URL and the figma-rest connector is available, call its
audit_design tool (tool name depends on install — e.g.
mcp__figma-rest__audit_design or mcp__Figma_REST_Ready_Check__audit_design;
discover it via the available tools). Read the file and fold the gaps in:
- Platforms — if the PRD scopes mobile but the file has no mobile-width
frames (only desktop/tablet), that's a blocker on
scope. Web-only when
the PRD implies mobile → flag it.
- UI states — if no error / empty / loading screen is found by name,
surface each as a warning on
states (frame-naming is heuristic — say
so, and ask the PM to confirm those states exist rather than hard-failing).
- Copy — any placeholder/lorem/TODO text layers are a blocker on
design ("N text layers still contain placeholder copy").
This is the design-side of Ready Check. It runs here, in chat (and at
dev-side /contract pickup) — the Cowork panel is a sandbox and can't reach
a local connector, so the panel defers the design audit to this flow. Say
which frames/counts you saw so the PM can trust it.
Present these as a short, plain-language blocker list the PM can act on
immediately. The point of Ready Check is that the PM sees — and fixes — the
edge cases before dev, not that dev finds them later.
Guardrail: don't push code-level decisions onto the PM (schema choices,
security design, perf strategy). Surface the product-level gap; if the answer
needs the repo, note it for Level 2. Finding a missing edge case is the PM's
job here; deciding its implementation is dev's.
Access modes — the depth scales with what's available
Run at the deepest mode you have access to; each is a strict superset:
- Text only (always works). Judge purely from the PRD. Catches most
product-level edge cases and every completeness gap. No setup.
- + code-context cache. If
.manifest/.cache/code-context.json exists,
call ready-check.mjs --cache-check <answers.json> .manifest/.cache/code-context.json and fold the notes in — event-naming
drift, a flow that touches more surfaces than the PRD lists, known past
failures on this surface. No repo credentials needed.
- + repo read access (opt-in). If the PM/session has read-only access to
the product repo(s) in
.manifest/repos.yml, ground the edge-case pass in
real code: check that the flows named actually match the surfaces in the
code, and pull historical edge cases from prior handling of the same screen.
This is the same evidence Level 2 uses, run earlier — so PMs can catch more
themselves. Grant this deliberately: read-only, per the team's policy;
it's optional, and the check degrades cleanly to the modes above without it.
Whatever mode you're in, say which one you used, so the PM knows how deep the
edge-case check went.
Phase 4 — Score + mint (deterministic), rendered as a live scorecard
Chat is the primary experience — render a panel-like scorecard, not a wall of
prose. The chat flow is the reliable one (full connector access, the real
model, no sandbox), so make it feel like the panel: one glance to read, one
reply to fix. Write the answers to a temp JSON and run the engine:
node scripts/ready-check.mjs --scorecard answers.json # readable panel-like scorecard
node scripts/ready-check.mjs --check answers.json # verdict JSON, exit 1 if not ready
node scripts/ready-check.mjs --handoff answers.json freeze.json # hand-off + freeze stamp
Always pass freeze.json when the PRD came from a fetched source. Write the
version you fetched in Phase 1:
{ "freeze": true,
"source": { "type": "jira", "id": "ENG-1234", "version": "<JIRA updated ts>" },
"design": { "version": "<figma file version, if any>" } }
(Confluence: "type":"confluence", id = page id, version = version.number.)
This stamps Source: … v<version> and PRD-Hash: H… into the hand-off, which is
what lets /contract pickup detect the PRD document being edited after
sign-off — not just the hand-off text. Without it, that drift check is silently
skipped. Omit freeze.json only when the PRD was pasted as raw text (no source
version to pin).
Show the --scorecard output as-is — it's already the panel: a progress
bar, the verdict, a numbered "To fix" list with a concrete prompt per gap,
the grouped "Answered / N/A / Waived" items, and (when ready) the gate code. Pass
meta.source (e.g. JIRA FTC-1421 / Confluence <id>) so the card names which
PRD it is. Then overlay the smart findings from Phase 3 (specific missing edge
cases / vague answers) as a line or two under the relevant numbered item, so the
PM sees what to fix, not just that something's missing.
The card already invites the fix — just run the loop. The scorecard ends with
a numbered gap list and an "↳ e.g. …" reply hint, so the PM can answer by number
or in free text. When they reply, fold their answers into the answers object,
re-run --scorecard, and show the updated card — the progress bar fills and the
score climbs. Accept free-text ("metric is +6% D1 in 4 weeks"), a numbered list
("2: …; 4: …"), or N/A: <item> — <reason> / waive <item> — <reason>. Keep
looping until it clears. Read like a panel, fix like a chat.
- Not ready — show the scorecard + the fix prompt above. Never mint a code.
- Ready — show the scorecard (now ✅), then the hand-off block from
--handoff. The gate code is in it.
Phase 4b — Write the PM's new answers back INTO the PRD (don't strand them)
When a PM unblocks a gap by answering in chat ("here are the analytics events"),
that answer exists only in the conversation and the hand-off block. The PRD
document itself is still missing it — so a dev who opens the Confluence page or
JIRA ticket never sees it. Always close that loop.
- Track what was added. Mark every item the PM supplied during the review with
"addedInReview": true in the answers object.
- Render just those answers:
node scripts/ready-check.mjs --addendum answers.json "<PM name>" — a short
markdown block of only the newly-supplied items, stamped with the gate code.
- Append it to the source — never replace. Show the PM the block and get an
explicit yes first. The PRD is their document, often rich with tables and images:
- Confluence →
updateConfluencePage, appending the block to the end of the
existing body (fetch the current body first and concatenate). Never overwrite.
- JIRA → append to
fields.description via editJiraIssue, or, if they'd
rather not touch the description, addCommentToJiraIssue.
- If the PM declines, say plainly that the answers will live only in the hand-off
and the PRD will stay incomplete for whoever reads it next.
⚠ Order matters — write back BEFORE you mint the hand-off. Editing the source
bumps its version/content, so if you stamp the freeze first and edit after, pickup
re-fetches, sees a different version, and reports STALE-SOURCE — a false alarm
caused by our own edit. Correct sequence: gather answers → append the addendum →
re-fetch the source version/hash → then --handoff answers.json freeze.json
with the post-edit version. Then the PRD, the hand-off, and the freeze pin all
agree.
Phase 5 — Hand off to dev
🚨 NEVER hand-write the hand-off block. It MUST be the verbatim stdout of
node scripts/ready-check.mjs --handoff answers.json freeze.json. Do not retype
it, reformat it, summarise it, drop the • [id] … | … item list, or reword the
footer. That item list is the content the gate code hashes — without it the
block is unverifiable: --verify can only recover a couple of items, so the
code can't be checked against anything and pickup reports NOT-READY. A
hand-written block looks official and proves nothing, which is worse than no
gate at all. Same rule for Source: — it must read <type> <id> v<version>;
without the v<version> the freeze stamp doesn't parse and drift-detection is
silently skipped. If you don't have the version, say so rather than emitting a
half-stamp.
Self-check before you paste: the block contains The basics, answered: followed
by one • [id] line per satisfied item, a PRD-Hash: line (when the PRD was
fetched), and a Source: … v… line. If any are missing, you didn't use the
engine — re-run it.
Tell the PM exactly where the block goes: the JIRA ticket, or the dev Slack
thread. If a Slack/JIRA MCP is connected and the PM asks, post it for them in
their voice. The block carries the design link and the gate code so the
feature and its design travel together.
Generate the PRD + publish to JIRA (the "right format")
Ready Check holds the structured answers, so it can also author a clean,
publish-ready PRD — the same 11-section format as gate/examples/PRD-good-*.md:
node scripts/ready-check.mjs --prd answers.json # prints the 11-section PRD (markdown)
The PRD carries the gate code and lists any waivers under "Waivers (dev to
accept)". Offer it once the PM has the basics in — even Not-Ready renders (it
marks missing sections), but it's most useful at Ready.
Publish (ask the PM which):
- Update the linked ticket — if the PRD came from a JIRA link, write the PRD
into that ticket's description via the Atlassian MCP
editJiraIssue
(fields.description, contentFormat: "markdown"). Safest — no new tickets.
- Create a new ticket —
createJiraIssue with projectKey, issueTypeName
(ask; default Story/Task), summary = the feature title, description = the
PRD. Confirm the project key with the PM first; never guess it.
- Copy only — hand the PM the PRD markdown to paste in themselves.
Always show the PM the PRD and confirm before writing to JIRA. (The Cowork panel
exposes the same three via a Generate PRD / Copy / Publish row.)
Enforcement — "no code, no grooming" (automatic)
The gate code is content-bound (a djb2 hash of the answers), so it's checkable,
not decorative. Dev runs nothing extra. When the dev runs /contract pickup <ticket>, pickup reads the Ready-Check: line from the ticket/hand-off
and verifies it automatically, then acts:
VALID → proceed; record readyCheck: RC-… in the contract frontmatter.
STALE (PRD changed after it cleared) → stop and ask the PM for a fresh
Ready Check.
INVALID / missing → refuse pickup: the PRD never passed the gate.
So the only person who ever thinks about the code is the PM (they paste the
hand-off once). Dev just picks up as usual and the gate enforces itself. The
manual ready-check.mjs --verify <handoff> exists as a fallback for anyone who
wants to check by hand, but the normal flow needs no command.
Verifying a hand-off — three ways, none of them need the panel
A dev should never have to open the Cowork panel to check a gate code. When someone
says "verify this hand-off" / /ready-check verify / pastes a block starting
✅ READY CHECK PASSED, do this:
Prefer the link — the dev shouldn't hand-copy anything. If they give a ticket
or page URL (or you already know it), fetch the source and save the whole
description / page body to a temp file; --verify locates the hand-off block
inside it automatically (extractHandoff). That's the best route: nothing to
paste, and no risk of clipping the • [id] lines. Pasting the block is the
fallback for when it lives somewhere unfetchable, like a Slack DM.
- Save the source verbatim to a temp file — the fetched ticket/page body, or
the pasted block including every
• [id] … | … line. That list is the content
the code hashes; dropping it makes the block unverifiable.
A gate code alone can never be verified — if someone offers only
RC-XXX-######, say so plainly and ask for the block or the ticket link.
- Run
node scripts/ready-check.mjs --verify <block>.txt and report plainly:
VALID → the answers are exactly what was signed off. Proceed.
STALE → the block was edited after the code was minted (or the PRD was
re-checked). Say what looks changed if you can, and ask for a fresh check.
INVALID → not a real gate code.
UNVERIFIABLE (exit 2) → the block has no item list, i.e. it was hand-written
rather than produced by --handoff. Treat it as no gate at all and ask the
PM to re-run Ready Check.
- Then check for drift, if the block carries a freeze stamp and the source is
reachable: re-fetch the PRD,
--hash the body (the addendum is stripped
automatically, so a written-back answer never counts as drift), re-read the Figma
version if present, and run --verify-freeze <block>.txt current.json →
VALID / STALE-SOURCE / STALE-DESIGN / STALE-CONTENT.
The three routes, in order of convenience:
| Route | Who | Needs |
|---|
| Paste the block in chat → "verify this" | anyone | nothing — this skill |
/contract pickup <ticket> | dev | runs steps 2–3 automatically |
ready-check.mjs --verify <block>.txt | dev in a terminal | the repo + Node |
Freeze check (v0.22) — did the PRD or design change after sign-off?
When the PRD was fetched from JIRA/Confluence (and, if Figma REST is wired, has a
design), the hand-off carries a freeze stamp:
Source: <type> <id> v<version>, optionally Design-Version: <n>, PRD-Hash: H….
At /contract pickup, after verifying the gate code:
- Re-fetch the current source version (JIRA
updated / Confluence
version.number) and, if applicable, the Figma file version
(mcp__figma-rest__get_file_version).
- Write them to a temp JSON
{ "version": …, "designVersion": …, "prdHash": … }
and run:
node scripts/ready-check.mjs --verify-freeze <handoff.txt> current.json
- It prints
VALID / STALE-SOURCE / STALE-DESIGN / STALE-CONTENT.
VALID → proceed.
- any
STALE-* → stop: the PRD doc or the design changed since it cleared.
Ask the PM to re-run Ready Check and re-sign.
- No freeze lines in the hand-off → skip (older hand-off; nothing to check).
This is what makes "the PRD/design can't quietly change after the gate" real —
you detect the drift at the moment dev picks it up.
Scenarios to handle
- Backend / API-only feature — do not just N/A the UI items. Answer their
backend equivalents:
design = the interface/contract spec (OpenAPI, proto,
schema), states = the response & failure contract (status codes, error
bodies, retry/idempotency). Only mark an item N/A when it has no analogue at all,
and then only with a reason (e.g. writer → N/A "internal job, no downstream
consumers"). Everything else is required, read the backend way.
- Brand-new feature — mark
oldbeh N/A (nothing exists yet).
- No writer/creator impact — mark
writer N/A with that reason.
- Figma-only hand-off — the Figma URL satisfies
design; still need the
other 10.
- Vague "make it better" PRD — expect Not Ready; the value is showing the
PM precisely what's missing.
- Re-check after edits — re-run; a changed answer re-mints the code, so an
old hand-off correctly goes
STALE.
- No code-context cache — the check runs on text alone; skip the cache
notes without erroring.
- Can't answer an item — waive it with a reason (
{ "waived": true, "reason": "..." }). It clears but shows in the hand-off's Waivers block for
dev to accept or push back. Empty reason doesn't clear.
- N/A a skippable item — same rule:
{ "na": true, "reason": "..." }. A bare
N/A no longer clears; the reason rides into the hand-off. Prefer the
backend-equivalent answer over N/A wherever one exists.
- Not a PM — a dev pre-checking their own pickup can run it too; same gate.
Anti-patterns
- Don't eyeball the verdict or hand-write a gate code — always run the engine.
- Don't let a PM N/A a non-skippable item to slip through — and don't accept a
bare N/A on a skippable one either; it needs a reason, or better, the
backend-equivalent answer.
- Don't treat a backend/API PRD as out of scope — run it with the backend
readings in "Works for EVERY PRD" above.
- Don't emit a verdict from a previous run — fetch the current source first and
echo back what you fetched (issue key + title) before judging.
- Don't demand implementation detail from the PM (that's Level 2's job) — keep
blockers to product decisions judgeable from text.
- Don't fetch source through non-approved means if an MCP fails — say so and ask
the PM to paste the text.