agentleFS
mwellis81mwellis81/spec-factory/skills/specfactory/SKILL.md

specfactory

Skill1 starsChanged 40 days agoMITView on GitHub

Use when starting, resuming, or checking a spec factory run from any point.

The file

Show its rules and text

We split this skill into the rules it gives an agent

24 rules. About 0 are common, 7 are distinctive, and 17 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈7

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 17

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Read the run state: Run python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_status.py --root .

    line 13
  2. Distinctive:

    Run it in this session; a stage skill is never handed to a helper in fresh context (a subagent under Claude Code) or a workflow, since its …

    line 19
  3. Very specific:

    Stop when the recommended move is stop, when the handoff move has been run, when the owner says stop, or when the move needs a stage skill …

    line 22
  4. Very specific:

    When the move's target belongs to a stage skill that is not built, say so as NOT_CONFIGURED: <skill> is not built yet and stop.

    line 40
  5. Very specific:

    Read weight: from the first artifact that exists, and route by it.

    line 44
  6. Distinctive:

    Read the upstream's screens or surfaces section before offering it: when the feature adds no new screen, recommend skipping UX and say …

    line 51
  7. Very specific:

    Every stage skill drafts against a reviewed upstream, so a reviewed artifact never blocks the next one from starting: approve is listed for …

    line 88
  8. Very specific:

    Every agent move on a spec, draft, finish, correct and an agent revise, folds into this one move, and its reason lists them.

    line 98
  9. Distinctive:

    Read that section, then recommend drafting UX or skipping it and say which section you read to decide, per the weight section above.

    line 116
  10. Distinctive:

    Every artifact gets a read-through before it is reviewed, and that is the stage skill's step.

    line 131
  11. Very specific:

    Run sf-grill's read-through against the approved artifact: its Requirement Index, or its section list, and the one question about what the …

    line 135
  12. Very specific:

    When it names nothing, record it in the artifact's transcript as the comment line # Read-through: nothing missing after the last item, …

    line 136
  13. Distinctive:

    When it names something, the artifact is approved, so the fix is a change proposal and never an edit:

    line 138
  14. Distinctive:

    When that refusal comes back, withdraw the proposal rather than rewriting it: only the artifact that states requirements can be the target …

    line 184
  15. Very specific:

    When the owner asks to change one, do not edit it and do not refuse: offer to write a change proposal, and say what that costs.

    line 191
  16. Very specific:

    Run python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_trace.py --root .

    line 198
  17. Very specific:

    When fixture: is set, the starting idea is fixtures/<fixture>/idea.md, read relative to the root of this run.

    line 204
  18. Distinctive:

    If you have already opened either file, say so plainly and stop rather than drafting past it.

    line 213
  19. Very specific:

    Never set approved:; only a human does, by running python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_approve.py <path> --root ..

    line 229
  20. Very specific:

    Never edit approved_by, approved_version, upstream_hashes, or upstream_versions.

    line 230
  21. Very specific:

    Never reopen an approved spec and never edit a reshape: record.

    line 231
  22. Very specific:

    Never edit a ratified anchors section, and never edit a ratified requirement's text.

    line 232
  23. Very specific:

    Never delete an artifact. Status is computed by sf_status.py from what is on disk.

    line 233
  24. Very specific:

    Never recompute the next move from the chain table, from a PROGRESS file, from interview prose, or from a repository grep.

    line 235

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

specfactory

The one entry point. The engineer never needs to know the stage skills exist; this skill routes to the right one from wherever the work is.

The loop

  1. Read the run state. Run python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_status.py --root . and print everything it returns: the chain table, the proposals table, the obligations table, the ranked Moves: list, and the final Next: line, which names the recommended move. When no specfactory.config.yaml sits in the current directory or any directory above it, there is no run root yet: offer python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_init.py --prefix <ID> --root . as the first move, recommended, say what it makes (the config with answerer: human, the empty chain directories, specs/CLAUDE.md, the commit hook), and start this loop again once it has run.
  2. Ingest what the move names. When the recommended move is ingest, run sf-ingest over the sources that move names and nothing else, so the stage skills draft against context/ rather than against a raw drop and a source already normalised is left alone. When it hands back a document that is itself a chain artifact, run sf-improve on it.
  3. Offer the recommended move as the one question, except autopilot, which is never asked: the owner chose it by leaving spec_mode at its default, so say one line naming what the pass owes and run sf-spec (step 4). Otherwise offer the move as the one question, under sf-grill's Question format, the recommended option first. The question text carries the move's target path and its consequence, never a bare label: "docs/factory/brief.md is missing. Recommended: draft it now, about 8 questions." Print the owner moves the Moves: list carries beside it, as commands the owner runs outside this session - the approval command, the apply command - so they can see what is waiting on them while the agent move goes ahead. A next move is one question and never a batch, so sf-grill's fast track does not apply to it; it applies to the rounds, clearing batches and disposition batches the stage skills run.
  4. Run the chosen stage skill. Run it in this session; a stage skill is never handed to a helper in fresh context (a subagent under Claude Code) or a workflow, since its interview, its review dispatches and its transcripts belong to this context.
  5. Go back to step 1.

Stop when the recommended move is stop, when the handoff move has been run, when the owner says stop, or when the move needs a stage skill that is not built. Print the chain table and the handoff status.

Which stage skill

The move's targetSkillBuilt
idea or briefsf-ideayes
prdsf-prdyes
uxsf-uxyes
tdd and its ADRssf-tddyes
specsf-specyes
anything under inbox/, a pasted key, a URLsf-ingestyes
an existing document that is a chain artifactsf-improveyes
every spec approved, and the set has to leavesf-handoffyes

An artifact that is missing or draft is the stage skill's to work on. An artifact that is reviewed is waiting on a human: print the approval command, never run it. When the move's target belongs to a stage skill that is not built, say so as NOT_CONFIGURED: <skill> is not built yet and stop; never improvise the artifact yourself.

The chain the weight selects

Read weight: from the first artifact that exists, and route by it.

side collapses the idea, the PRD, and the technical design into one brief, so the chain is brief, then UX only when there is a new screen, then specs. prd and tdd are NOT_APPLICABLE at that weight, and their stage skills say so and return rather than drafting. standard and enterprise run the whole chain: idea, PRD, UX, TDD with its ADRs, then specs.

UX is the one stage that is skipped on the evidence rather than on the weight. Read the upstream's screens or surfaces section before offering it: when the feature adds no new screen, recommend skipping UX and say which section you read to decide. The test is a new screen, not a changed one. A control added to a screen that already exists, and a background job with no visible surface, are changes to a surface that exists; they belong in the spec's behaviour, not in a UX document. sf-ux refuses on the same evidence, so a wrong recommendation costs a refusal rather than an invented document.

What each move means

sf_status.py ranks the moves and names the recommended one; these are the reasons behind that order, not a second place to compute it.

ingest. sf-ingest, in this session, over the sources the move names. It ranks first because a source the run has not normalised changes what every artifact below it should say, and python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_ingest.py pending --root . is what decides which sources those are, so a file already normalised is never read twice.

apply. python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_apply.py <proposal> --root ., the owner's command, and the agent's under answerer: simulated. An approved proposal that has not been merged means the artifact on disk is not the artifact the owner approved, so every reading below it is provisional until it is applied.

revise. An open row in docs/factory/obligations.md: a correction one artifact owes at a stated version, which sf_status.py shows as revision required (v<n>) on that target. The agent's move where the target is draft or reviewed, which its stage skill corrects at the next version; the owner's where the target is approved, which goes through a change proposal against it. It outranks approving the superseded reviewed version, drafting a downstream spec, and the handoff, because the correction is already decided: approving or building on the version that lacks it buys work that has to be done a second time.

correct. A stale (affected) or stale (self) artifact, corrected by its own stage skill at the next version: the first write resets approved: false, approved_by: null, upstream_hashes: {} and bumps version, then the draft lifecycle runs again. The agent's move, and it ranks above a re-approval because an affected artifact names a requirement that actually moved.

reapprove. A stale (upstream only) artifact: python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_approve.py <path> --root ., the owner's. Nothing it names was changed, only something above it, so it costs a read rather than a correction. Staleness comes from sf_status.py, which splits it for you; never re-review the whole chain because one requirement moved, since the split exists so the owner spends review time where a requirement actually changed.

finish. A draft artifact: its stage skill picks up from the findings and the queue. The agent's move, and it ranks above approve for the same reason draft does.

approve. A reviewed artifact: python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_approve.py <path> --root ., the owner's, and the agent's under answerer: simulated with a fixture: name in the config, as --fixture. Every stage skill drafts against a reviewed upstream, so a reviewed artifact never blocks the next one from starting: approve is listed for the owner and draft is the agent's move, which is why it ranks below both finish and draft. With answerer: human, never run it: print the command for the owner, say what the artifact scored and which questions are parked, and offer to carry on drafting downstream while it waits.

readthrough. An approved artifact whose transcript carries no # Read-through: line: sf-grill's read-through, run in this session, as the section below sets out. Where the artifact has no transcript at all the script says NOT_RUN: no transcript rather than guessing that one never ran. It ranks right after ingest, before apply and before correct: it is one batch of questions, anything it finds can only add to the proposal about to be applied, and an omission found after that proposal is applied costs the whole stale cycle a second time over the same artifacts.

autopilot. The spec stage under spec_mode: autopilot, the default, once every upstream the weight gives the specs is approved; sf_status.py prints spec stage: autopilot or spec stage: drive with the reason. Every agent move on a spec, draft, finish, correct and an agent revise, folds into this one move, and its reason lists them. sf-spec runs its Autopilot section in this session to the packet without asking the owner anything, and the loop does not interrupt it to re-offer a move. With spec_mode: drive, or an upstream that is only reviewed, the per-spec moves appear as they always have.

answer-packet. A defaulted queue row stands and specs were reviewed under it: print python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_packet.py --root . and put its items to the owner as sf-spec's Autopilot section says, in one sitting. sf_approve.py refuses every spec while a row stands defaulted, so this always comes before approve-set.

approve-set. The per-spec approve moves under autopilot, folded into one: python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_approve.py --set specs/ --root ., the owner's, and the agent's as --fixture under answerer: simulated. It checks every spec before it writes any and approves none when one is refused, naming each refusal.

draft. The next artifact in chain order is missing and its upstream is reviewed or approved: the stage skill the table above names, in this session, with roughly the number of questions the weight's band implies. For specs, "missing" is an upstream requirement that no spec carries, read from python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_trace.py --check --root .'s orphan check, so the move names the requirements the new spec must cover.

decide-ux. The UX slot, offered as its own move because the script cannot judge the evidence: the test is a new screen, and only the upstream's screens or surfaces section settles it. Read that section, then recommend drafting UX or skipping it and say which section you read to decide, per the weight section above.

handoff. python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_trace.py --root . and then sf-handoff. It ranks last of the real moves because it is offered only when every required artifact is approved, no approved proposal is unapplied, and no obligation is open.

backfill-digest. A normalised source with no .requirements.md digest beside it, which only sf-ingest's extractor dispatch can write. It is optional and ranked last, and it is offered only while an artifact that reads digests - the idea, the brief, or the PRD - is not yet approved, because after that nothing reads it.

stop. Nothing above matched: the run has no move left and this loop ends.

The owner read-through on an approved artifact

Every artifact gets a read-through before it is reviewed, and that is the stage skill's step. An artifact approved before this rule existed never had one, and sf_status.py offers that as the readthrough move when the artifact's transcript carries no # Read-through: line; take it as the move says, and say what it costs: one batch of questions to the owner, and a change proposal for anything it finds. The script ranks it ahead of an unapplied proposal and of a stale downstream artifact, for the reason "What each move means" gives, so the move list already carries that priority and nothing here recomputes it.

Run sf-grill's read-through against the approved artifact: its Requirement Index, or its section list, and the one question about what the owner stated that the page is missing, weakens, or gets wrong. When it names nothing, record it in the artifact's transcript as the comment line # Read-through: nothing missing after the last item, which is what sf_status.py reads to know the read-through ran, and move on.

When it names something, the artifact is approved, so the fix is a change proposal and never an edit:

  1. The artifact's stage skill writes the proposal under docs/factory/changes/ per ${CLAUDE_PLUGIN_ROOT}/schemas/change.schema.json, naming the touched ids, with the owner's own words as the reason.
  2. Lint it. A proposal is one decision rather than a document, so lint-clean is the bar and no findings file is required; run sf-review on it only when the change is large enough that someone should argue with it, and disposition anything critical or high it raises.
  3. answerer: human: print python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_approve.py <proposal> --root . and stop; the owner approves it and runs python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_apply.py <proposal> --root .. answerer: simulated with a fixture: name: run python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_approve.py --fixture <proposal> --root . and then python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_apply.py <proposal> --root ..
  4. Re-run python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_status.py --root .. Applying the proposal makes the downstream stale, and the split says how: re-review and, in simulated mode, fixture-approve every stale (affected) artifact in chain order, upstream first, because an affected artifact names a requirement that moved. Print the stale (upstream only) ones for a read; nothing they name changed.
  5. Regenerate python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_trace.py --root . and run python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_trace.py --check --root ..

Everything the read-through finds in one pass goes into one proposal. A second proposal for the same read-through is the same edit twice, and it makes the stale split unreadable.

One change-proposal cycle per approved artifact per session

Applying a proposal re-stales everything below it, and every stale artifact costs a correction, a review round and an approval. So an approved upstream artifact takes at most one change-proposal cycle in a session. Once that cycle has been applied, the artifact is closed to proposals until the chain below it is approved again and a person says otherwise.

Anything found after that is queued rather than proposed, and what it is queued as depends on whether it is a question or a correction. A question for the owner, something nobody has decided, is a row in docs/factory/questions.md with status parked, naming the artifact, the requirement it would change, and what it would say, and you go on with the artifact as it stands. A correction an artifact owes, decided and not in doubt, is a row in docs/factory/obligations.md instead, naming the target and the version it must reach, which sf_status.py shows as revision required (v<n>) on that target and which blocks its approval and the handoff until it is satisfied. A parked row does not block reviewed or approval (R11), so the chain closes and the reader can see exactly what is outstanding. This holds for anything a stage skill, a review round or a read-through turns up, however good the finding is: a correct finding is worth queueing, and it is not worth a second rebuild of the whole downstream in the same session. The one exception is a defect that makes an artifact unapprovable on its own terms, which is fixed in that artifact and not by a proposal upstream. That fix reopens the artifact rather than editing it under its approval: sf_status.py calls an approved artifact whose version moved on without a reset stale (self), sf_approve.py refuses it however current its review, and the way out is the stale correction's own write, approved: false, approved_by: null, upstream_hashes: {} and the next version together, then review and approval at that version.

The same cap holds within one artifact. Correct a stale artifact once, review it, and approve it as soon as sf_status.py calls it reviewed. That is lint clean and nothing critical or high undispositioned, and it is the whole bar: the rubric score is a signal A7 prints and never a gate, so an artifact scoring under its threshold with every finding dispositioned is approved and the score is reported. Accepted findings still unfixed at that point are queued the same way and belong to a later version: a panel on a substantial document returns new high findings every round, so an artifact rewritten until a round comes back clean is an artifact that never gets approved. Two rounds on a version is the cap the review skill already carries, and a second corrected version in the same rebuild needs a reason beyond the panel having found more.

A change proposal, or the next version

The two are different moves and using the wrong one deadlocks the chain.

A change proposal changes a requirement in the artifact that states it, on the owner's word. It names ids that artifact carries, sf_apply.py merges it there, and everything downstream goes stale as a result.

Propagating that change downstream is not a proposal. A stale (affected) artifact is corrected by its own stage skill, at the next version. The first write carries the whole reset - approved: false, approved_by: null, upstream_hashes: {}, and version one higher - alongside the first content change, and the hook refuses the edit otherwise. From there the artifact goes through the draft lifecycle again: review at the new version, then re-approval. That is not an edit the owner has to authorise, because nothing about the owner's intent changed; the artifact is catching up with a decision they already made, which is what its staleness records. Leave approved, approved_by and upstream_hashes exactly as they stand while you do it: a stale artifact's review: is allowed to name the round it is being corrected past, sf_approve.py rewrites all three when it re-approves, and clearing them by hand is the hard stop, whatever it is in aid of. A proposal against a downstream artifact cannot work anyway: it would name an id the artifact only cites, and sf_apply.py will refuse it with "has no entry for the touched id", which is the tool saying the instrument is wrong rather than the change being wrong. When that refusal comes back, withdraw the proposal rather than rewriting it: only the artifact that states requirements can be the target of one, so no wording will make it apply. Where the correction it named is still owed, write an obligation against that artifact in docs/factory/obligations.md with the version it must reach and the sentences it must carry, so the next version of that artifact carries it and the reader can see what is outstanding meanwhile. That is also the answer when a review round below an approved artifact finds the artifact itself wrong, which is the one case the factory has no move for: say which of the two documents is right, write the obligation against the one that is not with the version it must reach and the sentences it must carry, and go on rather than stopping the chain on it.

Editing what is approved

An approved artifact is never edited in place on the owner's behalf. When the owner asks to change one, do not edit it and do not refuse: offer to write a change proposal, and say what that costs. "docs/factory/prd.md is approved, so the edit goes through a change proposal. Recommended: write one naming REQ-4, which you approve and apply, and which marks the specs that reference REQ-4 stale." Then write it under docs/factory/changes/ per ${CLAUDE_PLUGIN_ROOT}/schemas/change.schema.json, name the touched ids, and stop: the owner approves it and runs python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_apply.py <proposal> --root ., which is the only thing that merges it. A draft artifact is edited in place, with no proposal.

After the specs

Run python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_trace.py --root . so docs/factory/traceability.md matches what is on disk, and python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_trace.py --check --root . to see whether anything is orphaned. Report what it says rather than summarising it: an orphaned requirement is a gap in the decomposition, and the fix is in sf-spec, not in the table.

Where the idea comes from

Read specfactory.config.yaml first. When fixture: is set, the starting idea is fixtures/<fixture>/idea.md, read relative to the root of this run. A fixture with no idea.md starts from inbox/ instead: the run's material is whatever sf-ingest normalises, and step 2 is where it begins. Otherwise it is what the owner said.

fixtures/<fixture>/idea.md is the only file in that folder this session may open. answer-key.md belongs to the stakeholder-sim role alone, dispatched in fresh context, and expected.md to the grader; reading either one yourself, or passing either one's content to a stage skill, makes the run worthless.

Your job is the chain, and nothing else in the repository is your business. A run that goes looking for what it is supposed to produce, instead of producing it, has stopped being evidence of anything. If you have already opened either file, say so plainly and stop rather than drafting past it. An artifact written with the answers already in hand looks exactly like one that was interrogated for them, and only you know which it is.

Deciding without a human

answerer: human: ask step 3's question with the question tool this harness provides.

answerer: simulated: never call the question tool. Take the recommended move, and when the choice was a real decision rather than the only move available, append it to docs/factory/questions.md as a routed row: a choice taken without a human because the run is unattended. Where the Moves: list carries an owner move such as approve or apply, the simulated run performs it rather than printing it: python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_approve.py --fixture <path> --root ., as the read-through section already says for approval, and then python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_apply.py <proposal> --root . for an approved proposal. That flag is the only unattended approval path and it refuses outside a simulated fixture run. defaulted is not yours to write. It belongs to sf-grill, for a question that reached the answerer and the answer key could not answer. Then continue until the recommended move is stop or a stage skill returns with nothing left it can do.

What the router never does

Never set approved:; only a human does, by running python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_approve.py <path> --root .. Never edit approved_by, approved_version, upstream_hashes, or upstream_versions. Never reopen an approved spec and never edit a reshape: record; when the owner wants an approved spec reshaped, print python ${CLAUDE_PLUGIN_ROOT}/scripts/sf_reopen.py <spec> --reason "<what for>" --root . for them to run. Never edit a ratified anchors section, and never edit a ratified requirement's text; a change proposal under docs/factory/changes/ is the way. Never delete an artifact. Status is computed by sf_status.py from what is on disk; no artifact carries a status field. Never recompute the next move from the chain table, from a PROGRESS file, from interview prose, or from a repository grep; the move is what sf_status.py printed. These stops are this skill's own rules and hold in every harness; the hooks a harness installs (Claude Code's hooks/hooks.json, Codex's .codex/hooks.json) enforce them a second time, and a harness without hooks runs them as honour rules.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.