Today: V2 as written, version A
This is V2 as the PR writes it, drawn as one flow. One example runs through all 19 steps: source issue #12 asks the health check to say "healthy" instead of "ok". Press play, or pick a step. Arrow keys work when the map has focus.
Every box, one line
Every box of A's map, grouped by where it runs. "Gets" and "gives" name the data that moves in and out.
| Box | Who acts | Gets | Gives |
|---|---|---|---|
| Your laptop | |||
| Drafting agent | you and an agent | the source issue text | EARS records as YAML files |
| Local clone | Git | the files the agent writes | a branch to push |
| Validator | script, offline | the spec/ files and the base | errors, or none |
| GitHub repository | |||
| Spec PR | a human reviews and merges | spec files and the checklist | the approved spec on main |
| main | Git | merged PRs | the target spec, code and tests |
| Implementation PR | auto-merge | code and tests from the coder | a new main SHA |
| GitHub Issues | |||
| Source issue | a person writes it | an idea in prose | a hint for drafting, never a scope |
| Managed issue | the post-script writes it | the checked plan | requirement IDs and labels |
| Status issue | a human creates it once | run reports | the URL Fullsend needs |
| GitHub Actions · plain CI | |||
| Spec PR checks | script | the PR diff | gate result and validator result |
| Main evidence | script | main at one SHA | the evidence report |
| Evidence report | an artifact, not committed | test results and @req tags | per ID: tests and results |
| Impl PR checks | script | the PR diff | full suite and a candidate report |
| GitHub Actions · through Fullsend | |||
| Coordinator caller | a workflow in the repo | workflow_run: completed | one Fullsend run, one at a time |
| pre-script | script, read token | main, report, issues, runs, PRs | a snapshot with no credentials |
| Coordinator agent | AI agent in a sandbox | the snapshot | a plan as structured data |
| post-script | script, write token | the plan | issue, label and status writes |
| Fullsend coder | AI agent in a sandbox | issue scope, spec, report, repo | a commit; the runner opens the PR |
| Fullsend reviewer | AI agent | the PR and the EARS text | approve, or request changes |
The holes in A
Read A as three roles. Steps 1 to 6 are the analyst, steps 7 to 12 are the stand-up, and steps 13 to 19 are the team. The trouble is not a missing box. A gives the stand-up the analyst's job, and it gives all three roles one clock.
- Owns
- one ask, until it is a merged spec and a plan
- Pace
- as asks arrive, several at once
- In A
- drafting and the spec PR. No triage. No plan: the ask is forgotten after the merge.
- Owns
- the board of one repository
- Pace
- whenever anything changes on the board
- In A
- the coordinator. It also cuts the work, and it wakes only when main gets a new evidence report.
- Owns
- one managed issue, until its PR is merged
- Pace
- up to N at once, each at its own speed
- In A
- one coder writes the code and the tests, one reviewer. No cap, no attempt count, no way out.
A: one loop, one hub, one clock
- The hub does four jobs in one run. It compares spec with evidence, cuts the whole gap list into issues, admits, and closes. One sandboxed agent, one serialized run.
- One clock. The hub wakes only when
maingets a new evidence report. A failed coder run, a closed PR, a human who wants to reorder: nothing moves until the next merge or a manualworkflow_dispatch. - The ask disappears. After the spec merges, the hub regroups requirements across all asks. The person who wrote #12 cannot see what became of it, and the plan is invisible until the scope is frozen.
13 holes, pinned on A's map
Each red pin sits on the box where the hole is. Click a pin or a button under the map, or use Back and Next. Play tours all 13. The panel says what A does and what goes wrong. "How C closes it" jumps to step 4 of this page.
To-be: version C
C keeps every V2 rule about the spec, the evidence and the merge. It changes how the work is cut, queued, built and closed: five small workflows, each with its own clock, instead of one coordinator that one event wakes.
C: five workflows, each with its own clock
- Plan with the ask, before the queue. The plan for #12 is made right after its spec merges, from its own requirements, and it is a comment a person can read and
/replan. - The stand-up is code. No model decides what starts. The scheduler is a short script that runs on every signal: a new queued issue, a run that ended, a merge, a closed PR,
/implement, and every 10 minutes. - Programs gate, models propose, people decide. The same V2 split of authority, applied to the build: the PR checks on a draft PR with no secret, an attempt cap, a short deny list for the coder, and three named exits when the work cannot go on.
Nothing in C needs a new service, a database or a workflow engine. It is still GitHub Issues, Actions, branch protection and Fullsend.
main SHA, one row per requirement. V2's evidence report, written in the merge queue before the merge instead of on main after it.The whole flow, step by step
The same example as in step 1 runs through all 20 steps: source issue #12 asks the health check to say "healthy" instead of "ok". Press play, or pick a step. A dashed red box under a step says what changed against A, and names the hole it closes.
How C closes each hole
The same 13 pins, now green and on C's map. Each pin sits on the box that closes the hole. The panel says what C does, and links to the steps of C that show it.
All 13 holes in one table
H1 to H5 are in the analyst, H6 and H7 in the stand-up, H8 to H10 and H13 in the team, and H11 and H12 are bookkeeping. The last column names the steps of C that show the fix.
| Hole | In A | What goes wrong | In C | C step |
|---|
Four things in A stay as they are, on purpose: the spec PR as the one human gate, the immutable requirement text with supersedes, one report per exact main SHA, and closure only from that report. C moves the work around them, and writes the report before the merge instead of after it (H13).
Details of C
Reference for the review: what A needs and C does not, the five clocks, the seven Fullsend runs, and every box of C in one line. The labels are folded at the end.
What A needs and C does not
The holes table lists what A lacks. This section lists the opposite: two things A must have only because one coordinator wakes on one run. C drops both. Each part shows where the job went, so nothing is lost.
Where main evidence went
A runs the full suite on main after every merge and writes the evidence report. That run has no box in C's map. V2 needs it for three reasons, and C keeps all three. It only moves the measurement in front of the merge, where the merge queue already builds the commit that main will become.
| What the run on main solves | In A | In C |
|---|---|---|
A merged SHA nobody tested. A PR's checks run on its head against the main of that moment. Squash merge makes a new SHA. Two PRs that pass alone can fail together. | The run on main finds the break after it landed. main is red until a fix merges. | The merge queue builds main plus the PR as one commit and runs the suite on it. main fast-forwards to that commit only when it is green. The break is found in the queue. main is never red. |
Closure needs proof on main, not on a branch: "a managed issue closes only from latest-main evidence". | The coordinator reads the report of the run on main. | The scheduler reads the report of the queue check, keyed by the SHA that main now points to. Same rule, same SHA, same report format. One run instead of two. |
| The whole spec stays measured: which active requirements have no passing test. | The coordinator reads the same report and regroups the world. | The gap watch reads the same report after every merge. A spec-only merge runs no test: the report script copies the last results and adds the new IDs with 0 tests. |
| A clock. In A the run is the only event that wakes the coordinator. | workflow_run: completed of the run on main. | No clock here. The scheduler wakes on "PR merged", and on every other signal (H6). |
What it costs: a ruleset on main that requires the merge queue, squash, and the check on the merge_group event; a public repository or a Team plan; no direct push to main; and one more case, a text conflict in the queue, which the scheduler answers with one rebase run (step 16). What is gone: the main evidence workflow, a second full-suite run per merge, the window in which main is red, and the workflow_run wake-up. V2 does not mention the merge queue.
Where the status issue went
A needs one issue that a person creates by hand and that never closes: the coordinator status issue. C has no such issue. This is hole H12, and here is the why in one place.
| The question | In A | In C |
|---|---|---|
| Why does a run need an issue at all? | Fullsend's runner starts every run for one issue or PR. The event payload must carry issue.number and issue.html_url, and the runner posts its status and run links there (V2 §8.4, Appendix C). | The same rule. C does not change the runner. |
| Which run has no issue? | The coordinator. It wakes on workflow_run: completed of the run on main, not on any issue. It belongs to nothing, so a person creates a placeholder, stores its number in a repository variable, and the caller passes it on every run. | None. The coordinator is gone. Its four jobs went to runs that each belong to a real thing: the plan run to the source issue, which the spec PR names on its Source line; admission, closure and the gap watch to the scheduler, a plain Actions job that is not a Fullsend run and needs no issue. |
| What does the placeholder cost? | An issue that never closes. Every other rule must skip it: triage, the backlog, ready-to-code, assignment-active, closure (V2 line 641). The runner's status links point at no real work. A person can close it by mistake and stop the coordinator. | Nothing. The runner's status links land on the real issue, so a person who opens #12 sees the plan run there. No exclusion rule, no repository variable, no issue to protect. |
The one condition: a spec PR that adds a requirement must name its source issue, or its checks fail (step 5). Without that line the plan run would have no subject, and the placeholder would come back. Run subjects lists the subject of every run.
Five clocks
Each workflow owns one thing, starts on its own events, and hands on with a label or a signal. None waits for the next one.
| Workflow | Owns | Starts on | Who runs it | Where | Ends with | Steps |
|---|---|---|---|---|---|---|
| Analyze | one source issue, until it has a merged spec and a plan | an issue opened or edited; /triage; a person's drafting session; the spec PR merged; /replan | triage agent, you and the drafting agent, the conflict-check agent, the plan agent; the validator and the post-scripts | Fullsend for the agents, your laptop for drafting, CI for the checks | managed issues queued; the source issue planned | 1 to 7 |
| Schedule | the board of one repository | a managed issue labeled or edited; a coder or test-author run ended; a check run on a draft PR ended; a PR merged or closed; /implement; every 10 minutes | a script, no model | one Actions workflow in the repository, concurrency group of one, App token | labels: assignment-active + ready-for-tests, or a "waits for" reason; closed issues after a merge | 8 to 10, 18 to 20 |
| Implement | one managed issue, until its draft PR is ready for review | ready-for-tests; then ready-to-code; a re-dispatch after a red attempt | test author, coder; the PR checks with no secret; the scheduler marks the PR ready | Fullsend, in sandboxes; the checks in plain CI | a draft PR with Refs #101, marked ready; or blocked, needs-replan, spec-conflict | 11 to 13 |
| Review | one pull request, until it merges or closes | a PR marked ready, or pushed after that; /review; /fix | CI checks; the reviewer; the fix agent, at most 5 rounds | CI, then Fullsend | the merge queue: green, main fast-forwards; red, back to the fix agent; or needs-human, intent_gap, bad_plan | 14 to 16 |
| Settle | one main SHA | a PR merged: main moved to the commit the queue tested, and its report exists | the scheduler's settle part, a script. No run on main | CI | proven issues closed with the SHA; scope freed; gap issues for untested active requirements | 17 to 20 |
"Settle" is the second half of the scheduler script, not a sixth workflow: the same run that closes what is proven also admits what is next, like a stand-up that hears "done" before it assigns.
The Fullsend runs
Seven runs go through Fullsend's runner: triage, the conflict check, the plan run, the test author, the coder, the reviewer and the fix agent. Two things are the same for all of them. Each belongs to one issue or PR, and each is one CI job in three parts, where only the middle part is a model. First what each run belongs to, then the three parts.
What each run belongs to
Fullsend's runner starts a run for one issue or one PR. It needs the number and the URL in the event payload, and it posts its status and run links there (V2 §8.4 and Appendix C). A plain Actions job has no such rule. A's coordinator run belongs to nothing, so A needs a human-made status issue that never closes (H12). In C every run belongs to a real thing. The map shows it as ON #12 on each Fullsend box; this table says how the run finds it.
| Run | Subject | How the run gets it | Where its comments land | Step |
|---|---|---|---|---|
| Triage agent | source issue #12 | the event is on #12: opened, edited, or /triage. Stock Fullsend triage works this way. | #12: the route label and one comment | 2 |
| Conflict check | spec PR #56 | pull_request opened or pushed, like the review agent. It reads the Source #12 line from the PR body, but it never runs on #12. | PR #56: one comment for the human reviewer | 5 |
| Plan run | source issue #12 | three ways in. The spec PR merged: the caller reads Source #12 from the merged PR body, one run per source issue. /replan: a comment on #12. ready-to-plan: a label on #12, or on a gap issue #13. | #12: the plan comment and planned; the managed issues it writes carry Source #12 | 7 |
| Test author | managed issue #101 | ready-for-tests, a label on #101 | #101: the branch name and the test list; PR #57, opened as a draft with Refs #101 | 11 |
| Coder | managed issue #101 | ready-to-code, a label on #101 | #101: the attempt count and the fault class; PR #57: its pushes | 12, 13 |
| Reviewer, fix agent | implementation PR #57 | the PR marked ready, then every push; the review verdict. Stock Fullsend; the shim skips a draft. | PR #57: the review, the fix commits, the routes | 15 |
| Scheduler, spec PR checks, impl PR checks, the merge queue check | none | plain Actions jobs, not Fullsend runs. They need no issue. | labels and comments with the App token; the report as an artifact | 5, 8, 13, 14, 16 |
One rule makes this hold: a spec PR that adds a requirement must carry a Source #N line, and every new requirement a sources entry. The spec PR checks fail without them (step 5). So a new requirement with no issue cannot merge green, and every merge that adds one has a plan run with a subject. A spec PR with two sources starts two plan runs. A spec PR that adds no requirement, such as a wording fix in Vision.md or a retirement, needs no issue and starts no plan run. V2 does not have this rule; it calls the source issue a hint.
How each run goes: pre-script, agent, post-script
A Fullsend run is one CI job in three parts, and only the middle part is a model. The pre-script runs on the host before the sandbox exists: it checks the trigger and stages the inputs. The agent runs in a sandbox with a read-only token: it writes files and one result file, and it can never push, label or comment. After the sandbox is torn down, a validation loop checks the result file against a schema, at most twice. Then the post-script runs on the host with the write token: it checks the result again and does every write to GitHub. The map draws all seven runs this way. This table says what each part does: the stock Fullsend harnesses at v0.40.0, and what C adds or changes.
| Run | Pre-script · host, read token | Agent · sandbox, no write token | Post-script · host, write token |
|---|---|---|---|
| Triage agentstock triage.yaml · on #12 · step 2 | Checks the issue URL. Removes the old triage labels through the labels API, so the edit starts no second run. Stages nothing. C: unchanged. | Reads the issue, its comments, the spec on main, the open issues and spec PRs. Writes one action to agent-result.json: route, size, the touched IDs, at most 3 closed questions. | Checks the result again: a comment is required, "sufficient" with open questions is rejected. Sets one route label, posts one comment, closes a duplicate. C: TRIAGE_AUTO_CODE=off, so it never sets ready-to-code. The routes ready-to-plan and blocked are new labels for the post-script. It also adds priority:p3 when the issue has no priority:* label, so a re-triage keeps a person's choice. |
| Conflict checknew · review.yaml with a new prompt · on PR #56 · step 5 | Checks that the PR is open. Deepens the clone. Stages the PR body (the Source line, the decision list), the spec on main and the other open spec PRs. | Read-only clone. Compares the new text with main and with the other open spec PRs, and checks the decision list. Writes findings: replaced on purpose, still contradicts, duplicate, decision missing. | Posts one sticky comment on the PR, edited on each push. No label, no verdict, no block: the human reviewer decides. |
| Plan agentnew · on #12, or on a gap issue · step 7 | Reads Source #12 from the merged PR. Checks that main is still the merge SHA. Stages the requirements with sources: #12, the code and the latest report, with no token in the snapshot. | Proposes managed issues: at most 5 IDs each, likely paths (a hint, not a wall), dependencies, order, a one-line reason. Writes plan.json. | Checks the plan: schema, at most 5 IDs, every ID of #12 placed, main unchanged. Writes the managed issues as queued, each with the priority:* label of #12 (p3 if #12 has none), the plan comment on #12, the label planned. On /replan it replaces the open managed issues and keeps the finished parts. |
| Test authornew · on #101 · step 11 | Checks assignment-active and ready-for-tests on #101. Reads the scope, IDs and paths, from the issue. Makes the branch agent/101-… from main. Stages the EARS text and the code. | Writes tests tagged @req:REQ-… in the planned test paths, and no code. Commits with the trailer Role: test-author. | Pushes the branch and opens a draft PR with Refs #101: the stock post-code does this, minus Closes, plus draft. Writes the test list on #101. The PR checks then run the red-first rule with no secret: the new tagged tests must fail, the rest must pass. On green the scheduler sets ready-to-code. A test that passes on main fails the check, with a comment that says which. |
| Fullsend coderderived from code.yaml · on #101 · steps 12, 13 | Stock: checks the issue, skips the run when a human PR already closes it, installs the pre-commit tools. C: the preflight: assignment-active, the scope, at least one red test on the branch, attempt ≤ 3, the deny list: spec/, .github/, and every file in a commit with the trailer Role: test-author. On attempt 2 and 3 it stages the last fault class. No fresh branch from main: the run starts from the test author's branch. | Makes the tagged tests pass. May add or change any file that is not on the deny list. May read the tagged tests, may not change them. Commits with Role: coder. Or ends with one named exit in the result file: too-big, spec-conflict REQ-A REQ-B, blocked "…?". | Stock: secret scan, pre-commit, push, a PR with Closes, ready-for-review, a human assignee. C: reverts every write to a file on the deny list, then pushes to the draft PR. No Closes, no assignee, no ready-for-review. The PR checks do the rest with no secret. Green: the scheduler marks the PR ready. Red: a sanitized fault class on #101 and attempt:n+1; after 3, blocked. A named exit becomes its label. |
| Reviewerstock review.yaml · C routes in the prompt · on PR #57 · step 15 | Checks that the PR is open and its author is not on the skip list. Deepens the clone. The workflow mounts the prior review. C: stages the candidate report from step 14. | Read-only clone. Starts from the EARS text: are the tests a faithful reading, does the code meet every active ID, did the coder change a tagged test (commit trailers). Writes agent-result.json: verdict, findings, a route per finding. | Drops findings under the severity threshold. Downgrades an approval on a protected path. Posts the sticky comment and the formal review, APPROVED or CHANGES_REQUESTED, and a risk/* label. If the head moved, it asks for a new round. No result file: "not reviewed". C: the routes. patch: the verdict starts the fix agent. intent_gap: opens a source issue, needs-human on the PR. bad_plan: needs-replan on #101. |
| Fix agentstock fix.yaml · on PR #57 · step 15 | Checks the round: fails above 5 for a bot verdict, above 10 for a human /fs-fix. Records the head before the agent. Installs the pre-commit tools. The workflow mounts the review body. | Reads the review body, fixes on the PR branch, runs the tests, commits. Writes the result file: each finding fixed or disagreed. | Checks only the commits after the recorded head. Secret scan, pre-commit, push. Posts a summary comment. Adds needs-human one round before the cap. The push starts the next review round. C: no check here. A fix that changes a tagged test is caught only by the reviewer's explicit yes. |
There is no verify job. The suite runs as the PR checks (steps 13, 14 and 16), one script with no secret, as V2 §8.10 asks. It cannot run in a post-script, because the post-script holds the push token and the suite is code the agents wrote. It runs on a draft PR, because the shim .github/workflows/fullsend.yaml can skip the review for a draft with one if: line, as it already skips renovate/ branches; only a human PR may change that file. Sources: fullsend-ai/agents at v0.40.0, harness/*.yaml and scripts/*.src.sh; V2 §8.4 and appendix C for the credential boundary. What C adds is a sketch: none of it is built.
Every box, one line
Every box of C's map, grouped by where it runs. "Gets" and "gives" name the data that moves in and out. New or changed against A is marked. Every Fullsend box is one run, pre-script, agent, post-script: the run table says what each part does.
| Box | Who acts | Gets | Gives | vs A |
|---|---|---|---|---|
| Your laptop | ||||
| Drafting session | you and an agent | the triaged issue, the requirements it touches | EARS files with sources, one decision per affected requirement | changed |
| Local clone | Git | the files the agent writes | a branch to push | same |
| Validator | script, offline | the spec/ files and the base | errors, or none; three rules more | changed |
| GitHub repository | ||||
| Spec PR | a human reviews and merges | spec files, the checklist, the affected-requirement decisions | the approved spec on main; the plan run starts | changed |
| main | Git | merged PRs | the target spec, code and tests | same |
| Branch agent/101 | the post-scripts push | the test author's tests, then the coder's code | the PR's head | new |
| Implementation PR | the test author's post-script opens it as a draft; the scheduler marks it ready; auto-merge through the merge queue | tests and code, two authors | a new main SHA, tested before it lands | changed |
| GitHub Issues | ||||
| Source issue | a person writes it; triage, drafting, the plan run and the scheduler label it | an idea in prose | a route, a link comment, a state | changed |
| Managed issue | the plan run's post-script writes it; the scheduler labels it | the checked plan | ≤5 REQ IDs, paths, deps, order, a "waits for" line, attempts | changed |
| Gap issue | the scheduler's gap watch writes it | an active ID with no passing test and no owner | a source issue with kind:gap, into triage | new |
| GitHub Actions · plain CI | ||||
| Spec PR checks | script | the PR diff | gate result, validator result | changed |
| Scheduler | script, no model, one at a time | managed issues, live runs, open PRs, the latest report | closures, freed scope, admissions, "waits for" lines, gap issues | new |
| Impl PR checks | script, no secret: on every push to the draft, on the head, and again in the merge queue | the PR diff; then main plus the PR as one commit | green or a fault class for the scheduler; a candidate report for the reviewer; the report for the SHA main will become; the red-first result | changed: it replaces main evidence and the verify job |
| GitHub Actions · through Fullsend | ||||
| Triage agent | AI agent, read-only | the issue, the spec on main, open issues and spec PRs | one label, a comment with the touched IDs and questions | new |
| Conflict check | AI agent, read-only | the spec PR, main, the other open spec PRs | a comment: replaced on purpose, still contradicts, duplicate | new |
| Plan agent | AI agent in a sandbox | the merged IDs with sources: #12, the code, the latest report | managed issues, the link comment on #12 | new, replaces the coordinator |
| Test author | AI agent in a sandbox | the EARS text, the current code | tagged tests that fail today, on a draft PR | new |
| Fullsend coder | AI agent in a sandbox, deny list | scope, EARS text, the failing tests, the last fault class | a push to the draft PR; or too-big, spec-conflict, blocked | changed |
| Fullsend reviewer | AI agent | the PR, the EARS text, the two authors' commits | approve; or patch, intent_gap, bad_plan | changed |
| Fix agent | AI agent, stock Fullsend | the review threads | a push, at most 5 rounds | same |
Labels on the two kinds of issue source issue and managed issue · open
Labels are a view for people. The scheduler rebuilds its picture from them on every run, so a label a person changes by hand changes the queue. One label rides on both kinds of issue: priority:p1 to priority:p4. Triage sets p3 on a source issue that has none. The plan run copies the label of the source issue to each managed issue it writes, or p3 if there is none. The scheduler starts p1 first. A person can change the label on either issue at any time. The top strip is the normal path, the lower strip the exits.
The source issue: one ask
/readyThe managed issue: one cut of work
/retry or /replantoo-big from the coder, or bad_plan from the reviewer; the plan run splits, finished parts stayqueued, scope kept