Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"name": "human-fsm",
"source": "./plugins/human-fsm",
"description": "Slash commands for the human's side of the issue→PR pipeline FSM: read the next ai:ready PR and check the vetter's verdict against the diff and the issue it closes, read the next ai:close-candidate flag and check the producer's reason against the issue and the code it claims about, read the next ai:design PR and check the raised question against the issue and the code it is about, read the next FSM leak and locate which of three defects left the PR in no modeled state, then rule on a close-candidate, send work back as needs-work, rule a design question, or keep an issue open — plus an observation command that forces a run, watches it, measures what its context cost and reads the retained trace corpus for what a run still hand-rolls. Every input comes from pr-review-report; none writes GitHub state itself.",
"version": "0.20.0",
"version": "0.20.1",
"author": { "name": "Rain Open Source Software Ltd" },
"keywords": [
"rainlanguage",
Expand Down
33 changes: 25 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1075,12 +1075,15 @@ than truncate.
### Every tool result is bounded, and going over is the tool's error

A state-load is a **page**, not a dump. `unvetted` and
`unvetted_close_candidates` return at most `limit` rows (default 10, max 25)
with the whole-queue `counts` alongside and `more` naming what the page left
behind; the vetter re-calls for the next page, and because each `record_verdict`
removes its subject from the queue, paging converges without an offset argument.
The page size is what makes the bound structural — the payload no longer grows
with the number of open PRs.
`unvetted_close_candidates` return at most `limit` rows — bounded by
`STATE_LOAD_PAGE_RANGE`, which is computed from `RUN_ITEM_CAP` and so is the run
budget itself (#288) — with the whole-queue `counts` alongside and `more` naming
what the page left behind. The page is an **allowance, not a window**: `more` is
the NEXT run's work, and the vetter does not re-call for a second page, because
each `record_verdict` removes its subject from the queue and the next run's
state-load starts where this one stopped. The page size is what makes the bound
structural — the payload no longer grows with the number of open PRs, and an
out-of-range `limit` is REFUSED rather than clamped.

Every result is then checked against **one byte budget, the same for every
tool** (36,000 bytes), and a result over budget is returned as a **tool error
Expand Down Expand Up @@ -2880,6 +2883,16 @@ uses `{{WORK_DIR}}` / `{{SCRATCH_DIR}}` / `{{INSTALL_DIR}}` / `{{ASSIGNEE}}` /
`{{OWNER_FLAGS}}` / `{{ORGS}}` placeholders that the runner substitutes at run
time.

One placeholder is **not** deployment-specific: `{{ITEM_CAP}}`, in both prompts,
is the per-run work-item cap, and the runners fill it from
`pr-review-report item-cap` — i.e. from `RUN_ITEM_CAP`, the single constant that
`STATE_LOAD_PAGE_DEFAULT` and `STATE_LOAD_PAGE_RANGE` are also computed from
(#288). It is a placeholder rather than a number in the prose because the cap is
a RISK bound the design intends to move, the prompts state it two dozen times,
and several of those statements are spelled as English words that a sweep for
the digit does not find. A run whose budget cannot be resolved to a positive
integer ABORTS rather than rendering a RUN BUDGET sentence with no number in it.

### The producer's scratch dir

Each producer run gets `$WORK_DIR/scratch/<run-id>`, created before the model
Expand Down Expand Up @@ -3533,8 +3546,12 @@ merged PR, never a finding that the issue is fixed — establishing that is

Per-subject is a **cost** decision, measured: the uncovered set is 617 issues
and the read is one GraphQL round trip each (~0.65 s over a 40-issue sample, so
~6.7 minutes of network per run), against a producer budget of 5 work items.
Folding it into the backlog buys ~612 answers per run that nothing reads.
~6.7 minutes of network per run), against a producer budget of a handful of work
items (`RUN_ITEM_CAP`, which moves). Folding it into the backlog buys an answer
for all 617 when only the budget's worth is ever read; running it over the
candidates costs one call each, so the whole cost is the budget. That gap is
three orders of magnitude at any cap this bound will plausibly take, which is
why the shape is per-subject rather than a figure to recompute on every raise.

It reads `timelineItems(CROSS_REFERENCED_EVENT)` and **not**
`closedByPullRequestsReferences(includeClosedPrs: true)`, which is the field
Expand Down
1 change: 1 addition & 0 deletions TRANSITIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ transition functions:
| `repair-qa-block <owner/repo> <n> --block-file <path>` | the RETROFIT of the same rule on an ALREADY-open PR: appends the §8 block to the body, every other byte identical, validated with `require-qa-block`'s predicate |
| `weaken-closes <owner/repo> <n> <issue>` | the LINKAGE repair a linkage `needs-work` names: `Closes #issue` → `Refs #issue`, every other byte identical, `## QA` untouched, DIRECTION-LOCKED so it can only ever remove a closing reference |
| `mcp [--profile vetter\|producer\|human]` | serve a role's transitions over MCP (stdio) — the FSM as a tool surface, not as prose |
| `item-cap` | prints the per-run WORK ITEM cap (`RUN_ITEM_CAP`) and nothing else. Not a transition — it is where the two runners read the number they substitute into their prompts' `{{ITEM_CAP}}`, so the budget a run is told to spend is the same constant the state-loads' `limit` range is computed from (#288) |
| `plugin-version-lockstep [--root <dir>]` | CI gate: every plugin `.claude-plugin/marketplace.json` lists resolves to a manifest of the same name carrying the same version |

## The layer a human types: slash commands as a plugin
Expand Down
Loading
Loading