sf-grill
Skill1 starsChanged 40 days agoMITView on GitHub
Use when a stage skill must interview the owner before it drafts its artifact.
In this repository13 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- specs/CLAUDE.mdCLAUDE.md
- sf-handoffSkill
- sf-ideaSkill
- sf-improveSkill
- sf-ingestSkill
- sf-prdSkill
- sf-reviewSkill
- sf-specSkill
- sf-tddSkill
- sf-uxSkill
- specfactorySkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
44 rules. About 0 are common, 25 are distinctive, and 19 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈25
Distinctive · estimate
This author's own choices. Worth a look.
19
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 14
Read ${CLAUDE_PLUGIN_ROOT}/rubrics/INTERROGATION.md before the first question.
- Very specific: line 17
Read specfactory.config.yaml for answerer, fixture, and never_ask.
- Distinctive: line 46
When the fact you need fits none of those rows, it is a question for the owner or a gap to park, not an agent to invent.
- Very specific: line 50
Only the stakeholder-sim role, dispatched in fresh context, may open fixtures/<fixture>/answer-key.md.
- Very specific: line 51
Never open it yourself, and never open fixtures/<fixture>/expected.md.
- Very specific: line 54
Every fact you carry forward traces to an answer in the transcript, to the starting idea file, or to a file under context/.
- Distinctive: line 59
Every question states in one line why it matters and which decision it unlocks.
- Very specific: line 61
Never use a section heading or a requirement id alone as the question.
- Distinctive: line 81
Always ask about: user-visible behaviour, scope in or out, data kept or shared, security and privacy posture, money or budget, external …
- Distinctive: line 82
Never ask about: library or framework choice inside the approved stack, formatting, naming, file layout, test runner, tooling, or anything …
- Distinctive: line 85
Never ask what you can already read.
- Very specific: line 88
Before you compose a round, read the starting idea file and every file under context/ in full, and strike from the frontier every question …
- Distinctive: line 91
When a written fact looks wrong or is a year old, put that to them as a challenge quoting the line, which is a different question from …
- Very specific: line 93
When the owner says "stop asking about this kind of thing", add the topic to never_ask in specfactory.config.yaml and confirm you did.
- Distinctive: line 97
Never accept the first vague answer.
- Distinctive: line 102
Prefer "walk me through the last time this happened" over "how often does this happen".
- Very specific: line 113
When the starting idea, a context/ file, or the config already states the weight, that is the answer: record it with the line it came from …
- Distinctive: line 114
Ask only when nothing on the page settles it, or when what the page says and what the scope implies disagree, which is a question about the …
- Distinctive: line 121
Follow-ups count, pleasantries do not, and the questions in a queue-clearing batch do not.
- Distinctive: line 122
Keep the running count in the ledger every round, so you always know where in the band you are.
- Distinctive: line 130
If the domains genuinely cannot be covered inside the band, say so and propose raising the weight, which is a decision for the owner, not a …
- Very specific: line 134
Never talk to a person directly.
- Very specific: line 135
Ask through the answerer specfactory.config.yaml names.
- Very specific: line 140
Stop before the first question and return NOT_CONFIGURED: answerer is simulated but no fixture is set.
- Distinctive: line 146
Never answer a question yourself in either mode, with the one exception below.
- Very specific: line 157
Before a batch goes out in answerer: human mode, run python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_agreement.py --root ..
- Distinctive: line 162
When it says yes, the batch goes out first as one question whose text carries every item in full, its subject and its recommended answer, …
- Distinctive: line 169
When it says no, the batch goes out as today, one question per item.
- Distinctive: line 179
Keep exactly this shape, so the interview can be checked mechanically:
- Very specific: line 200
Every key is required on every item.
- Distinctive: line 213
Ask the owner to correct it once, at the exit announcement, where a correction is one more round.
- Very specific: line 225
Never re-ask a parked question downstream.
- Distinctive: line 230
Write it as what the transcript and the files support, and never in words the owner has not said: a recommendation that reproduces phrasing …
- Very specific: line 231
If the recommendation cannot be derived from the transcript, the starting idea file, or a file under context/, it is not a recommendation …
- Distinctive: line 278
When the index runs long, split it across as many calls as the tool allows per call, asking the question once per call, each call carrying …
- Distinctive: line 284
Check one omission yourself before the batch goes out, whichever answerer is configured: every constraint the owner stated, sequencing …
- Very specific: line 287
Every omission the owner names becomes a question in one more round, kind: substantive, and it counts against the band like any other.
- Distinctive: line 289
Only the omission questions it produces are items, and a read-through that names nothing leaves no item and says "Read-through: nothing …
- Distinctive: line 290
If the band is already full, the omission still gets its round and its rows.
- Distinctive: line 295
When the read-through names nothing, say "Read-through: nothing missing" and go on.
- Very specific: line 298
Never as a bare Read-through: line, because the transcript body is parsed as a YAML list and a bare line breaks it.
- Distinctive: line 305
Every domain in the seed is covered or explicitly parked.
- Very specific: line 308
No more than five rows in the queue are open or defaulted.
- Distinctive: line 310
If no answer changed the shape of the artifact and no challenge changed an answer, say so plainly, because that usually means the …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
<!-- The block under "The grilling loop, verbatim" is copied verbatim from github.com/mattpocock/skills, skills/productivity/grilling/SKILL.md, MIT licensed (verified 2026-09-02), Copyright (c) 2026 Matt Pocock. Everything else in this file is this project's own. -->sf-grill
The shared interview engine. A stage skill calls it with a seed: the artifact kind, its version, and the question domains to exhaust. It returns a ledger, and it leaves a transcript on disk that the evals and the interview judge read.
Read ${CLAUDE_PLUGIN_ROOT}/rubrics/INTERROGATION.md before the first question.
Three of its rules are superseded here: rule 1, one question per message, because the whole frontier goes out in one numbered round; rule 4, a challenge every third question, because a challenge is earned by the answer and not by a quota; and rule 6, a ledger wait every five questions, because the ledger rides with each round and waits only at the exit.
Its floors are superseded by the bands below, which are smaller.
Read specfactory.config.yaml for answerer, fixture, and never_ask.
The grilling loop, verbatim
Interview the user relentlessly until you reach a shared understanding. Map this as a design tree: every decision branches into the decisions that hang off it.
Work the tree in rounds. The frontier is every decision whose prerequisites are already settled: the questions you can ask now without guessing at answers you haven't heard yet. Ask the whole frontier in one round: number each question and give your recommended answer. Then wait for the user's answers before the next round.
Each round the user answers reshapes the tree: settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a later round, not this one.
Finding facts is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a helper in fresh context to find it (a subagent under Claude Code); don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the helper to report; ask the rest of the frontier now. The decisions are the user's: put each to them and wait.
The session is done when the frontier is empty: every branch of the design tree visited, nothing left silently assumed. Do not act on it until the user confirms you have reached a shared understanding.In this factory the user is never reached directly: every question goes through the answerer named in specfactory.config.yaml.
The "dispatch a helper" above names no role, and an unnamed dispatch gets the harness's untyped general-purpose one, which carries no model pin, no tool list, and no instructions about this factory; one run spent ten dollars that way. Dispatch by name or not at all:
| The fact you need | The agent |
| where something lives in a mounted codebase | repo-scout |
| what a platform, API, or vendor actually does | doc-scout |
an external document, ticket, or page to normalise into context/ | source-scout |
| a prior decision of this project | no agent: read the files under context/ yourself |
Nothing else is dispatchable from here. When the fact you need fits none of those rows, it is a question for the owner or a gap to park, not an agent to invent.
The owner's knowledge is the answers, and nothing else
Only the stakeholder-sim role, dispatched in fresh context, may open fixtures/<fixture>/answer-key.md.
Never open it yourself, and never open fixtures/<fixture>/expected.md.
They are the owner's mind and the grader's sheet: reading either turns a simulated run into a performance and destroys the only signal the run exists to produce.
Every fact you carry forward traces to an answer in the transcript, to the starting idea file, or to a file under context/.
A fact you cannot trace to one of those three is a question you have not asked yet.
Question format
Every question states in one line why it matters and which decision it unlocks.
Someone who has not seen the conversation must be able to answer from the question text alone.
Never use a section heading or a requirement id alone as the question.
The text of a question put to the owner carries the thing decided in full: a requirement as its id and its statement verbatim, a finding as its id, its severity, its finding and its recommendation verbatim, a queue row as its question text, a next move as the artifact path and the consequence.
An id or a number may follow the text in parentheses and never replaces it.
An option label names the choice, accept, reject, defer, take the recommendation, and never the item.
The question tool's header and option labels are short by design, so the substance goes in the question text itself, never only in a message printed above the dialog; a question is right when someone holding only its text can answer it.
sf_evals.py fails a transcript whose question text is a bare id or under eight words, so the transcript records the question as the owner saw it.
An answer space that can be enumerated gets two to five options, one line each, the recommended one first and labeled (Recommended), and that is most questions: a threshold has candidate values, a scope question has in or out, a policy question has the two or three policies anyone would consider.
options: [] is only for an answer nobody could enumerate, such as "walk me through the last time this happened".
A free-text question asks for five words or fewer where it can.
A round that is mostly free text is a round that made the owner do your thinking.
Elicitation, not direction
The interview pulls the owner's intent out; it does not insert your product vision. "I am assuming X works like Y, is that right?" is fine. Walking the owner through a tree of model-shaped choices about what the product should be is not. An agent that answers its own decisions has broken this skill.
Facts versus decisions
Always ask about: user-visible behaviour, scope in or out, data kept or shared, security and privacy posture, money or budget, external dependencies with licensing or vendor lock-in, anything irreversible. Never ask about: library or framework choice inside the approved stack, formatting, naming, file layout, test runner, tooling, or anything the template or the config already decides. Decide those by default and log the decision.
Never ask what you can already read.
For a spec, "already read" includes the PRD, the TDD, every ADR the TDD names, and the UX artifact.
A decision one of them states is carried with its citation and never re-asked.
Before you compose a round, read the starting idea file and every file under context/ in full, and strike from the frontier every question they already answer.
Carry the fact forward with its source instead, and spend the slot on what the fact leaves open: a starting idea that says "the archive is read-only after ninety days" has answered "can an entry be edited later", so ask what happens to an entry already open in a tab when the ninetieth day passes.
A question the owner has already answered in writing spends them for nothing and reads, to them, as not having been listened to.
When a written fact looks wrong or is a year old, put that to them as a challenge quoting the line, which is a different question from asking what the line says.
Anything on neither list is asked once, as multiple choice with a recommendation.
When the owner says "stop asking about this kind of thing", add the topic to never_ask in specfactory.config.yaml and confirm you did.
Pressure
Never accept the first vague answer. "probably", "maybe", "users", "somehow", "etc.", "we'll see", or a number with no source each force a follow-up that drills into exactly that vagueness. Name the assumption behind an answer and test it. Challenge a vague answer and every one-way door with a counterexample, an edge case, or "who would hate this". A challenge is earned by the answer, never by a quota, and a round of agreeable confirmations is a sign the questions were soft rather than a reason to add challenges to it. Prefer "walk me through the last time this happened" over "how often does this happen".
Weight is question zero
The first question of the first stage, before any floor applies, is weight, in plain English with the consequence on each option:
side: a few days of work for one engineer; one combined brief, about 8 questions, one reviewer.standard: a quarter-sized feature; separate PRD and TDD, about 14 questions, two reviewers.enterprise: a platform, or anything with money, auth, or regulation; the full chain, 20 or more questions with a break point offered, a full panel.
Recommend one from the idea sentence and any ingested context.
When the starting idea, a context/ file, or the config already states the weight, that is the answer: record it with the line it came from and do not ask, because the rule above outranks this one.
Ask only when nothing on the page settles it, or when what the page says and what the scope implies disagree, which is a question about the disagreement and not about the weight.
The answer goes in the artifact's front matter.
You may propose raising the weight later when answers reveal more scope; never lower it on your own.
The declared weight selects a band of substantive questions, and the band has two sides: side 6 to 10, standard 10 to 18, enterprise 20 or more with a break point offered.
A spec interview has its own band, floor 0 and ceiling 6, whatever the declared weight, because the PRD and TDD already made the decisions and the spec asks only what they leave open (see sf-spec step 4).
sf_evals.py counts a transcript whose header says artifact: spec against that band.
Follow-ups count, pleasantries do not, and the questions in a queue-clearing batch do not.
Keep the running count in the ledger every round, so you always know where in the band you are.
Below the floor is a failure: the interrogation was too shallow and the artifact rests on your assumptions. Stopping early because the owner seems busy is that failure wearing a polite face; offer "keep going or park here" instead.
At the top of the band, stop asking new substantive questions.
Anything still open is parked, with a why that names the decision the question would have settled, so the artifact carries the gap openly instead of buying it with an eleventh question.
At side weight an eleventh substantive question is a defect, not diligence: it says the weight was wrong or the questions were, and the transcript is counted against the band afterwards.
If the domains genuinely cannot be covered inside the band, say so and propose raising the weight, which is a decision for the owner, not a licence to keep asking.
Answer routing
Never talk to a person directly.
Ask through the answerer specfactory.config.yaml names.
answerer: human: use the question tool this harness provides, one call per round, plus the fast-track call when Fast track offers one, recommended option first.
answerer: simulated with fixture: unset is not a mode: nobody is present to ask, and there is no answer key to ask instead.
Stop before the first question and return NOT_CONFIGURED: answerer is simulated but no fixture is set.
answerer: simulated: dispatch the stakeholder-sim role in fresh context (the ${CLAUDE_PLUGIN_ROOT}/agents/ file of that name is its Claude Code binding), one dispatch per round (one Agent call under Claude Code).
The prompt carries the fixture name from fixture: and this round's questions, each numbered with its options and its recommendation.
The sim answers only from fixtures/<fixture>/answer-key.md.
An answer of "No preference, use your recommendation." means the key is silent: take the recommendation, and log the question to docs/factory/questions.md as a defaulted row.
Never answer a question yourself in either mode, with the one exception below.
The upstream answers a spec round in sf-spec's Autopilot section, and nowhere else: no other stage, and no spec round under spec_mode: drive.
The owner made their decisions in the PRD, TDD and UX, so each item is answered from those, the ADRs and context/, and nobody is asked.
An item a source settles takes that answer with answered_by: upstream and citation: naming the source and quoting the line.
An item no source settles takes the recommendation with answered_by: upstream and citation: "none; Q-n defaulted", and becomes a defaulted row the owner answers in the packet.
answerer stays what the config names, so sf_evals.py reads the transcript as it always has; sf_agreement.py never counts an answered_by: upstream item, because it is not the owner agreeing.
Fast track
A batch is one set of items put to the answerer together, each carrying a recommendation: an interview round, a queue-clearing batch, a disposition batch in sf-review.
Before a batch goes out in answerer: human mode, run python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_agreement.py --root ..
It prints one line, agreement: <agreed>/<answered> (<percent>%) threshold <t> minimum <m> fast_track: yes|no.
That is the share of recommended items a human has taken as recommended, computed from the transcripts and findings files on disk.
The threshold is fast_track_threshold (default 0.95) and the floor is fast_track_minimum (default 10), both in specfactory.config.yaml.
When it says yes, the batch goes out first as one question whose text carries every item in full, its subject and its recommended answer, numbered.
Its options are "Take every recommendation (Recommended)", "Answer them one at a time", and "Take all except the ones I name".
The third takes the item numbers in free text, and only those items then go out one at a time.
The fast-track question is its own call, and the items that still go out afterwards go out in the round's own call, so a fast-tracked round costs at most two calls and never one per item.
A taken recommendation is written to the transcript as its own item exactly as if the owner had chosen it, answer equal to recommended, answerer: human, plus fast_track: true, which is the one optional item key.
An accepted-by-fast-track disposition carries disposition_reason: accepted by fast track.
When it says no, the batch goes out as today, one question per item. The fast track is never offered in a simulated run: the sim answers every question from the key at no cost to anyone, and the fast track exists for the owner's time. The ledger line at the end of every round carries the agreement line, so the owner sees the rate move.
The transcript
Append every question to docs/factory/interviews/<artifact>-v<version>.md as the round is answered, never in one dump at the end.
<artifact> is the artifact kind, for example brief-v1.md.
A spec transcript is named spec-<FEAT-id>-v<version>.md, for example spec-FEAT-003-v1.md, because ten specs at one kind name would be one colliding file; the header still says artifact: spec.
The file is a YAML header and then one YAML list item per question, and nothing else.
Keep exactly this shape, so the interview can be checked mechanically:
---
artifact: brief
version: 1
weight: side
---
- q: 1
round: 1
text: "How much work is this worth: a few days, a quarter, or a platform?"
why: "It selects the interrogation floor and whether the chain collapses to one brief."
options: ["side, a few days for one engineer (Recommended)", "standard, a quarter-sized feature", "enterprise, a platform"]
recommended: "side"
answer: "side"
answerer: simulated
changed_artifact: null
kind: substantiveEvery key is required on every item.
fast_track: true is an optional key, written only on an item the fast track answered (see Fast track).
answered_by: upstream and citation: are the other two, written together and only on an item the upstream answered (see Answer routing).
answerer is human or simulated.
options is the list offered, and [] only for a genuinely free-text answer.
kind is substantive for interrogation, which is what the weight band counts, or clearing for a question in the batch that puts the queue's defaults back to the owner before the artifact can reach reviewed.
A clearing question is a re-ask, so it does not count against the band and is not a repeat of the question it re-asks.
changed_artifact is written null here and stays null: whether an answer changed the artifact cannot be known before the artifact exists, so the stage skill fills it in after the draft, by reading the draft (see sf-idea).
Quote every string value with double quotes and use single quotes inside it, so the file stays parseable.
Ledger
Post "Established / Still open / Parked by your choice" at the end of every round, in the same message as the next round's questions, never as a wait of its own.
Ask the owner to correct it once, at the exit announcement, where a correction is one more round; an owner who corrects it earlier is answering a question, and the answer goes in the transcript like any other.
The ledger is the draft's skeleton: the drafting step fills it in rather than starting fresh.
In answerer: human mode, the agreement line from sf_agreement.py rides with the ledger every round; see Fast track.
In a simulated run the ledger carries no agreement line.
Blocking versus queued questions
A question blocks only when it is a one-way door or the draft structurally cannot proceed without it.
Everything else becomes a numbered Q-n row in docs/factory/questions.md with a recommended answer and status open, and drafting proceeds under the recommendation with the marker DECIDED-BY-DEFAULT Q-n at the point of use.
Cap open and defaulted rows at 5: at the cap, stop and have the queue cleared before any more drafting, and a run that drafted past the cap fails that count afterwards.
An autopilot spec pass is the exception: it drafts past the cap without stopping, because its rows wait for the one packet at the end, and the count is taken once the packet is answered.
A row the owner parks gets status parked, is copied into the artifact's Open Questions section, and is listed in the artifact's open_questions until it is resolved.
Never re-ask a parked question downstream; reference it.
A parked row does not stop the artifact reaching reviewed, because parking is a decision the owner made; an open or defaulted row does, and so does an id in open_questions with no row in the queue at all.
Under spec_mode: autopilot a defaulted row does not stop a spec reaching reviewed, because the pass reviews under its defaults; sf_approve.py refuses every spec instead while any row stands defaulted, so the owner still answers it before anything is signed.
A queued row's recommended is your reasoning, not the owner's.
Write it as what the transcript and the files support, and never in words the owner has not said: a recommendation that reproduces phrasing you have not heard reads as an answer already given, and the clearing batch then confirms a decision nobody made.
If the recommendation cannot be derived from the transcript, the starting idea file, or a file under context/, it is not a recommendation and the row says so.
Ids are append-only.
Q-n is assigned once and never reused, and a row is never deleted: a question that turns out not to be the owner's, or that an answer settles, changes status and keeps its id, so the queue reads as the history of what was asked.
docs/factory/questions.md is one table, created on first use:
| id | artifact | question | recommended | answer | status |
|---|---|---|---|---|---|
| Q-1 | brief | Which browsers must the download work in? | current Chrome and Edge | | open |Status is exactly one of:
open: asked of nobody yet, or waiting on the owner; the draft runs on the recommendation.defaulted: put to the answerer, and the answer key held no answer, or, in an autopilot spec pass, no upstream source settled it. This is the measure of whether the interview asks things a stakeholder could not know, so nothing else ever carries this status.routed: a choice the router took without a human because the run is unattended. Never a question the owner was asked.answered: the owner answered it in the batch the stage skill runs before the artifact can reachreviewed; theanswercolumn holds their answer.parked: the owner deferred it; it is in the artifact's Open Questions and itsopen_questionslist.parked (was defaulted): it reached the answerer, the answer key could not cover it, and the clearing batch could not either, so it is parked into the artifact like any other parked row while staying countable as adefaultedquestion. A question the answerer could not answer never stays plaindefaultedonce the artifact is drafted, and never loses the fact that it was.
Obligations
A parked row is a question the owner deferred, and it never blocks anything.
A correction one artifact owes at a stated version is not a question and never goes in the queue: nobody has to decide it, so putting it to the owner asks them to answer something already settled.
It is a row in docs/factory/obligations.md, one table created on first use, columns | id | target | required_version | source | reason | status |, ids OBL-n append-only and rows never deleted, the same discipline as the queue.
The written status is open or waived, and a waiver carries "waived by <who>: <why>" in the reason; satisfied is never written, because sf_status.py computes it once the target reaches required_version and is reviewed or approved.
An open obligation is visible and enforced: the router prints the target as revision required (v<n>) in place of its computed status, sf_approve.py refuses that target below the required version, and sf_export.py refuses the handoff while any obligation is open.
source cites the transcript item or the finding that created the obligation, so the reader can see what made the correction owed.
The owner read-through
A stage skill calls this after its self-check and before sf-review, on every artifact, every time.
The interview asks what the owner knows; the read-through asks what the draft lost, which is a different question and the only one that catches a fact the owner gave and nobody wrote down.
Present the artifact's Requirement Index, one line per requirement, id and statement, in the order it carries them, and its BR-n rows where they live outside the index.
An artifact with no index presents its section headings and the one-line claim each section makes.
Present nothing else: not the prose, not the rationale, not what you think is weak.
Then ask, as one batch, this question:
Reading your own requirements back: what did you state that is missing here, weaker than you said it, or wrong?
The stage skill's queue-clearing batch goes out in this same message, so the owner is interrupted once between the draft and the review: the index read-back and its question first, then every open or defaulted row that belongs to this artifact, multiple choice with the recommendation first, under the rules in the stage skill's clearing step.
A clearing row is kind: clearing in the transcript and an omission is kind: substantive, exactly as they would be asked apart.
answerer: human: put the index lines inside the question text itself, one numbered line each carrying its id and its statement verbatim.
When the index runs long, split it across as many calls as the tool allows per call, asking the question once per call, each call carrying its own slice of the index, rather than trimming a line down to its number.
The line number stays as the way to point at a line, beside its text, never in place of it.
Reading the index back line by line is what makes an omission visible; handing over the whole document and asking for comments is not the same act, and neither is asking the question again at every line, which is one question asked twenty times.
answerer: simulated: dispatch the stakeholder-sim role in fresh context with the index and that question. The sim compares each line to the answer key and names, quoting the key line it comes from, every fact the key states that the index lacks or weakens.
Check one omission yourself before the batch goes out, whichever answerer is configured: every constraint the owner stated, sequencing above all, rollout order, pilot team, feature flag, kill criterion, and each line of the artifact's constraints section, has a BR-n row that carries it.
A constraint the owner stated that no BR-n carries is an omission, and it goes into the batch as one even when the owner does not name it, because a constraints section is prose nobody traces and a rule stated only there is a rule the specs never see.
Every omission the owner names becomes a question in one more round, kind: substantive, and it counts against the band like any other.
The read-through question itself is not a transcript item and is never written to the transcript, because it is one fixed question asked of every artifact and not an interrogation of the owner.
Only the omission questions it produces are items, and a read-through that names nothing leaves no item and says "Read-through: nothing missing" in the announcement as the section already requires.
If the band is already full, the omission still gets its round and its rows.
A fact the owner has already stated is not a new question you chose to ask: it is a fact you dropped, and the band exists to stop you spending the owner's time, not to license losing what they said.
Say so in the transcript when it happens, and say it in the announcement.
Fold each answer into the artifact before the review round, as a new requirement or as the correction to an existing one, and record it in the transcript with changed_artifact filled in against the amended draft.
When the read-through names nothing, say "Read-through: nothing missing" and go on; a silent read-through that was never run and one that found nothing look identical afterwards, so the artifact says which it was.
Record the read-through in the artifact's transcript as well, as a YAML comment line after its last item: # Read-through: nothing missing, or # Read-through: <n> omissions when it named some.
Never as a bare Read-through: line, because the transcript body is parsed as a YAML list and a bare line breaks it; a comment is the one line both the parser and a grep accept.
sf_status.py reads that line to know the read-through ran, and offers the readthrough move on an approved artifact whose transcript carries none.
For an artifact that is already approved the read-through cannot amend it in place: the router owns that path and it goes through a change proposal.
Exit criteria, all required before the stage skill drafts
Every domain in the seed is covered or explicitly parked.
Nothing is left under "Still open" in the ledger.
The substantive count is inside the band the declared weight selects, at or above the floor and at or below the ceiling.
No more than five rows in the queue are open or defaulted.
Announce "Interrogation complete, N questions, key decisions: ...".
If no answer changed the shape of the artifact and no challenge changed an answer, say so plainly, because that usually means the challenges were soft, and offer one more round; the owner decides, default no.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

