
可重复的代理程序加代码工作流程,作为一个技能封装,可存储在任何仓库中。确定性 P
Repeatable agents-plus-code workflows, packaged as one skill, stamped into any repo. Deterministic Python owns the graph. Coding agents are bounded nodes inside it.
Full breakdown on YouTube: Super Simple Software Factory
A software factory does one thing: it gives you more leverage on your prompt. How much leverage depends entirely on what you invest in it. At the low end you chain two agents together and hope. At the high end you build a system of agents plus code that runs without you, and does the job about as well as you would.
Everyone can get an agent to write code once. Almost nobody gets the same result twice. This fixes that by moving the control plane out of the prompt and into Python. An ADW script (AI Developer Workflow) owns sequencing, retries, and acceptance. Agents work inside named phases. Typed JSON envelopes carry context across the seams. Every event streams into SQLite while it is still happening. Agent proposes, code disposes.
[!NOTE] This branch is the skill alone, which is the thing you install. For a repo with the factory already stamped into it, a demo app it planned, built, tested, reviewed, and documented, and the real traces from those runs, see the
examplebranch.
Hand a capable model your whole SDLC and you get a machine with no seams. There is no phase boundary, so you cannot say which step failed. There is no acceptance criterion you can name, so "done" means "the agent stopped talking." A retry is a cold start that throws away everything the agent just learned. The only trace is a transcript you have to read like a novel. Run it twice, get two different systems.
The fix is not a better prompt. The fix is deciding, deliberately, that code owns sequencing, retries, and acceptance, and the agent owns only the work inside one bounded phase. Everything else falls out of that one line. Phases become the unit of the trace. Envelopes become the only way context crosses a seam. Gates become the definition of done. A correction becomes cheaper than a restart, because the session is still alive.
This is the part most engineers are going to skip, and pay for later.
Code costs nothing. It runs at the speed of light. You can change it in a second. And you actually own it, which is not true of any model you are renting by the token.
So when the invocation is already known, write it down. bun test is not a judgement call. Neither is ruff check. An agent rediscovering your test runner burns a context window to learn what a subprocess already knows, and it charges you for the privilege every single run. Worse, it puts a passing test suite into a context window, which buys you nothing at all.
Agents are for the parts that need reading and deciding. Everything else is a kind="code" phase. When code fails, the failure comes back to the builder as an envelope, through the same door an agent's report would have used. The repair loop is identical. You just stopped paying an agent to do arithmetic.
The bill for skipping this is not only tokens. It is cost, speed, and consistency, and you pay it on run one hundred and run one thousand, not on run one.
Same models. Same prompts. The difference is who owns the loop.
Two steps: get the skill into your repo, then stamp the factory.
Copy .claude/skills/sssf/ into the target repo and type /sssf install inside Claude Code. The skill is named sssf, so that is the skill name followed by the install argument. There is no bare /install command. The agent reads the skill's own cookbooks/install.md and does the rest.
Prereqs: uv, pi, sqlite3, and an API key for whichever providers your roster names (see below). bun only if you want the visualizer.
…
Re-running install.py is safe. It skips every file that already exists and reports what it skipped, so a second run doubles as a drift check. --force refreshes stamped code to the skill's current version, but it overwrites all stamped files including your sssf.config.yaml and your prompts, so commit first.
Green on the smoke test means the whole path works: config validated, session minted, Pi ran, envelope parsed, events landed in adws/adw_data/sssf.db. Fix it there before composing anything larger, because every multi-agent chain rides this exact path.
That depends on your roster, not on this repo. Every model: in sssf.config.yaml is written provider/model-id, and the provider half decides the key. Which key pi reads for a given provider comes from ~/.pi/agent/models.json.
The starter roster deliberately mixes providers to show the point, so out of the box it wants three:
| Model in the starter roster | Provider | Key |
|---|---|---|
google/gemini-3.6-flash (default, builder, scout) |
served via openrouter | OPENROUTER_API_KEY |
fireworks/accounts/fireworks/models/kimi-k3 (planner) |
fireworks | FIREWORKS_API_KEY |
openai/gpt-5.6-terra, openai/gpt-5.6-luna (reviewer, documenter) |
openai | OPENAI_API_KEY |
Want one key instead of three? Delete the per-agent model: lines and let every agent inherit defaults.model. The whole roster then runs on one provider. Cheapest way to get a first green run.
One sharp edge worth knowing: agents.validate() checks that a model is written as provider/id, not that the provider is reachable or that its key is set. A missing key does not fail at startup. It fails when that agent runs, partway into a chain.
Everything here is built to be observable, customizable, and reusable. Those are not adjectives, they are the reason the parts are shaped the way they are.
Observable. If you cannot measure your agents, you cannot improve them. Every event goes into SQLite as it happens, so you can watch a run mid-flight, not read about it afterwards.
Customizable. One YAML file sets the core four for every agent: context, model, prompt, tools. Different models at different price and speed points, in the same run. It is not about which model is best anymore, it is about which model is right for that one phase.
Reusable. The whole thing is a skill you stamp into any repo, then bend to fit. The tests it ships are not your tests. The prompts it ships are starters. It is designed to be edited.
There are three actors here, and the design keeps them separate on purpose: the engineer, the code, and the agents. The trick is not running more agents. The trick is using all three at the right moment.
Everything lives in .claude/skills/sssf/. SKILL.md carries the hard rules and routes each request to one of nine cookbooks. references/ holds the deep specs, scripts/ holds the generators, templates/ holds exactly what gets stamped.
| What lands in your repo | Where it comes from | Tracked |
|---|---|---|
adws/adw_sssf_config/sssf.config.yaml |
templates/sssf.config.yaml |
yes, it is your agent roster |
adws/adw_*.py |
templates/adws/ |
yes, twelve starter workflows |
adws/adw_modules/ |
templates/adws/adw_modules/ |
yes, all low-level logic |
adws/adw_data/prompt_engineering/ |
templates/prompt_engineering/ |
yes, your prompts live here |
adws/adw_data/harness_engineering/ |
templates/harness_engineering/ |
yes, pi extensions |
.env.sample |
templates/env.sample |
yes |
justfile |
templates/justfile |
yes, starter recipes to run and watch |
adws/adw_data/sessions/, sssf.db |
created at runtime | no, gitignored |
The prompts are yours the moment they land. Edit them in adws/adw_data/prompt_engineering/{agent}/, never back inside the skill.
There is no DSL here. No framework to learn. It is Python, YAML, agents, and a skill, which is exactly what these models are already trained on. Staying in distribution is a feature.
adws/adw_sssf_config/sssf.config.yaml answers one question per entry: who is this agent. One agent, one prompt, one purpose.
…
Five starter agents ship in the box: planner, builder, scout (read-only recon), reviewer, and documenter. There is no tester, because running a suite is a known command and therefore code.
Every agent gets its own model, thinking level, prompts, tools, and harness. That is the core four, and it is the whole surface you tune. Give the planner a frontier model and the builder a cheap fast one. Give the scout subagents. Give the reviewer no ability to write code at all.
tools is a capability list. writes is the boundary. They are not the same thing, and the difference matters: bash runs anything, including git checkout, and write reaches any path. So "this agent changes nothing" is enforced in code, after every call, by comparing the repo before and after. Unauthorized changes are rolled back and the phase fails. A read-only agent is read-only with respect to your repo, never unable to write its own report.
Config defines who an agent is. The ADW call site defines how it is used. That split is what lets one agent serve many different calls. ADW scripts never name a model, they name an agent.
Every run is a sequence of phases, and every phase is the same context manager no matter who owns it.
…
Three kinds, three swim lanes. engineer is the human lane. agent is ph.call(...): prompt in, typed envelope out, gates verified. code is a deterministic step that stands on its own, like a commit or a migration, and it is never buried inside an agent phase, so the trace shows exactly when code ran and when an agent was working.
That commit phase is the whole pattern in miniature. The builder proposes the message as a field on its envelope. Code decides whether to use it, falls back when it is empty, and performs the write. The agent never runs git commit itself.
Success must be earned. Every phase defaults to fail. A clean exit flips it, and an agent phase also needs its envelope to parse and every gate to come back green. run.finish(accepted=...) adds the second question, because phases passing is not the same as the run being acceptable: a test phase that ran a red suite did its job perfectly. One call settles the exit code, the session status, and the banner together, so they cannot disagree.
An agent has exactly two output channels: reference files written into context_handoff/, and a final valid-JSON response parsed against the output type the call declared. Code persists that response as envelope.json, records it, and injects it into the next agent's prompt. Context transfers in code, not in conversation.
class EnvelopeBase(BaseModel):
status: Literal["success", "fail"]
summary: str = ""
artifacts: list[str] = Field(default_factory=list)
notes_for_next_agent: str = ""
class BuildOutput(EnvelopeBase):
changed_files: list[str] = Field(default_factory=list)
commit_message: str = "" # consumed by the git commit phase
Determinism is wired into every step. Agents must return a specific structure, every time. If it does not parse, they get asked again until it does.
Gates verify claims, never predictions. Nobody knows which files an agent will touch before it finishes, so gates run after the fact against the envelope's own declarations: artifacts_exist, files_non_empty, json_parses, diff_matches_claims, tests_pass(...). A gate is a callable with the signature gate(envelope, run) -> GateReport, one `check(
暂无开放 Issues,或尚未同步最近议题。