Skip to content

Commit 3d0b802

Browse files
committed
📝 Finish the value-component sweep and give the reviewer the whole PR
Two inconsistencies left by the return-contract fix. The migration to value components was described in some places and not others. The specification still said `Planning` and `Implementation` return derived `authorized` and `terminal` fields, which they stopped doing. And the specification, `start.md`, and `primitives.md` all still described every component body as split by `<Output>` into documentation under `throw` and a region under `output`. That is true only of the text components: a value component declares `returns`, renders nothing, cannot contain `<Output>` at all, and runs its entire body fail-fast, binding nothing when it fails. Each document now says which kind it is talking about, and `Planning`, `Implementation`, and `UserCheckpoint` describe their own bodies as value bodies. The pull-request reviewer could not see what it was reviewing. #295 resolves reviews, comments, and checks alongside the identities, and the prompt rendered only number, state, URL, head, and base — while `runtime.md` denies agent network access, so the planner had no way to recover the rest. An adversarial review that cannot see a failing check or an existing objection is uninformed rather than adversarial. Every category is now rendered explicitly into the review prompt and into the checkpoint material the user reads, each collection iterated with `<Each>` rather than stringified, and the prompt names the revision under review: the diff at `headSha` against `baseSha`, with a verdict describing that head alone. The member field names inside reviews, comments, and checks are #295's to settle; these documents depend on that schema rather than defining a competing one. What is settled here is that the planner receives the complete snapshot. The handle stays internal — `start.md` gates on the verdict and decision — so the earlier claim that nothing consumes it was wrong, and the PR description is corrected too. Discrimination adds a structured `<PullRequest>` stub carrying non-empty reviews, comments, and checks with distinct markers. It captures the actual rendered planner prompt and the checkpoint material and asserts every category, both head identities, and the diff instruction reach them. Omitting any one category reddens exactly its own assertions and leaves the others green. Nothing in the test uses agent network access. The six gate scenarios and the three earlier mutations still hold.
1 parent ee80200 commit 3d0b802

7 files changed

Lines changed: 160 additions & 66 deletions

File tree

‎specs/adversarial-implementation-workflow.md‎

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,13 @@ The planner reviews the plan against the user intent, repository evidence,
194194
instructions, specifications, and observable validation. A failed review returns
195195
a focused prompt that the implementor can apply directly.
196196

197+
A pull-request review needs the pull request's complete current state, and the
198+
planner cannot fetch it: the supervised exercise denies agent network access. So
199+
the review prompt renders every category the handle resolves — existing reviews,
200+
comments, and check results — rather than a subset, and names the revision under
201+
review: the diff at the head identity against the base. A verdict describes that
202+
head alone, and a moved head requires a fresh review.
203+
197204
Factual disagreement calls for more evidence. When evidence leaves more than one
198205
viable choice, the agents present the options, consequences, and recommendations
199206
to the user. Convergence means that the final plan reflects a shared
@@ -244,9 +251,10 @@ A stage that only produces material for the next prompt is a text component:
244251
`InstructionFiles` and `Discovery` render their result and a caller's `as` binds
245252
that text. A stage that resolves a user decision inside itself declares
246253
`returns` instead. `Planning` and `Implementation` each run a review loop that
247-
asks the user a question, so each returns its prose, its parsed verdict, the
248-
complete `UserDecision` it resolved, and the derived `authorized` and `terminal`
249-
its caller gates on. `<UserCheckpoint>` does the same for a single decision.
254+
asks the user a question, so each returns its prose, its parsed verdict's fields,
255+
and the complete `UserDecision` it resolved — the sources its caller gates on,
256+
and nothing derived from them. `<UserCheckpoint>` does the same for a single
257+
decision.
250258

251259
The rule is about where authority lives. A controller that resolves a decision
252260
and returns only a rendering of it has discarded the thing its caller needs: the
@@ -256,15 +264,27 @@ boundary as data or it does not cross at all.
256264

257265
### A stage fails rather than returning a half-record
258266

259-
A stage's `<Output>` region runs under the `output` error mode; everything
260-
outside it is documentation and runs under `throw`. An undecided error at either
261-
position fails the run rather than producing a result a caller would bind. The
262-
two modes differ in what a printing boundary may do about it: a `<PrintErrors>`
263-
region can print an `output` decision instead of failing, and `throw` is the one
264-
mode it cannot replace — a printed error in documentation is one nobody can
265-
read. Neither rescues a stage here, because every stage's parsing sits in
266-
documentation. That is what makes each stage's final `<Parse>` a gate: malformed
267-
agent output cannot reach control flow or a deterministic effect.
267+
The two component kinds fail differently, and neither can hand a caller a
268+
half-record.
269+
270+
A **text component** is split by its `<Output>` boundary: the region inside runs
271+
under the `output` error mode, everything outside is documentation and runs
272+
under `throw`. `InstructionFiles` and `Discovery` are the two here, and each
273+
puts its work in documentation, so an undecided error ends the run rather than
274+
printing into a region nobody reads.
275+
276+
A **value component** declares `returns`, so it renders nothing and cannot
277+
contain `<Output>` at all — that would be a structural error. Its entire body
278+
runs fail-fast, and a failure binds nothing: there is no partially validated
279+
return for a caller to gate on. `Planning`, `Implementation`, and
280+
`UserCheckpoint` are value components.
281+
282+
The two error modes differ in what a printing boundary may do about a failure: a
283+
`<PrintErrors>` region can print an `output` decision instead of failing, and
284+
`throw` is the one mode it cannot replace — a printed error in documentation is
285+
one nobody can read. Neither rescues a stage here. That is what makes each
286+
stage's final `<Parse>` a gate: malformed agent output cannot reach control flow
287+
or a deterministic effect.
268288

269289
A failing region keeps only what it had already rendered. That partial text
270290
reaches the output stream; nothing after the failure does. Continuing after a
@@ -305,7 +325,8 @@ Two are not implemented:
305325
establish is itself shipped and already inherited by files, globs, processes,
306326
and agents.
307327
- `<PullRequest>` creates or resolves the pull request for a branch and returns
308-
its identity and state (#295).
328+
a structured handle: number, URL, head and base identities, state, reviews,
329+
comments, and checks (#295).
309330

310331
Each environmental operation declares its inputs and preconditions, reconciles
311332
existing state, returns a structured handle, and records its observed effects. A

‎workflows/adversarial-implementation/Implementation.md‎

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -220,14 +220,38 @@ remote effects.
220220
#{pullRequest.number} ({pullRequest.state}) {pullRequest.url}
221221
head {pullRequest.headSha} onto base {pullRequest.baseSha}
222222

223+
Reviews:
224+
225+
<Each in={pullRequest.reviews} let="review">
226+
- {review.author} ({review.state}) on {review.headSha}: {review.body}
227+
</Each>
228+
229+
Comments:
230+
231+
<Each in={pullRequest.comments} let="comment">
232+
- {comment.author} on {comment.path}: {comment.body}
233+
</Each>
234+
235+
Checks:
236+
237+
<Each in={pullRequest.checks} let="check">
238+
- {check.name}: {check.status} / {check.conclusion} — {check.url}
239+
</Each>
240+
223241
Result contract:
224242

225243
{pullRequestVerdictSchema}
226244

227-
Review the pull request against the authorized plan and instruction
228-
content above. Classify every finding and include a focused revision
229-
prompt when the review fails. Return only JSON matching the supplied
230-
result contract.
245+
Review the diff at {pullRequest.headSha} against {pullRequest.baseSha},
246+
together with the authorized plan and instruction content above. The
247+
reviews, comments, and checks listed here are the complete current
248+
state; you have no network access, so do not attempt to fetch more.
249+
250+
A verdict is about one head. If the head moves, this verdict no longer
251+
describes the pull request and a fresh review is required.
252+
253+
Classify every finding and include a focused revision prompt when the
254+
review fails. Return only JSON matching the supplied result contract.
231255
</Prompt>
232256

233257
<Loop max={2}>
@@ -278,6 +302,24 @@ remote effects.
278302
#{pullRequest.number} ({pullRequest.state}) {pullRequest.url}
279303
head {pullRequest.headSha} onto base {pullRequest.baseSha}
280304

305+
### Reviews
306+
307+
<Each in={pullRequest.reviews} let="review">
308+
- {review.author} ({review.state}) on {review.headSha}
309+
</Each>
310+
311+
### Comments
312+
313+
<Each in={pullRequest.comments} let="comment">
314+
- {comment.author} on {comment.path}: {comment.body}
315+
</Each>
316+
317+
### Checks
318+
319+
<Each in={pullRequest.checks} let="check">
320+
- {check.name}: {check.status} / {check.conclusion}
321+
</Each>
322+
281323
## Planner review
282324

283325
Passed: {verdict.passed}
@@ -351,19 +393,41 @@ and nothing derived from them. The caller reads
351393
`decision.proceed && verdictPassed` directly, so there is no second copy of that
352394
answer to disagree with the first.
353395

354-
The pull-request handle is **not** returned. `<PullRequest>` (#295) resolves a
355-
structured handle carrying the number, URL, head and base identities, state,
356-
reviews, comments, and checks, and `start.md` consumes none of it — the artifact
357-
ledger (#291) records the effect and its handle independently. The `report`
358-
above renders the specific fields a reader needs. A return field typed `string`
359-
would be worse than useless here: a conforming `<PullRequest>` would perform its
360-
durable effects and only then fail this component's return validation. If a
361-
later caller genuinely needs the handle, it is added with #295's object schema,
362-
never a placeholder.
363-
364-
The agent, parsing, and control-flow syntax runs today. A value component's body
365-
runs fail-fast, so the final `<Parse>` in each repair loop ends the stage rather
366-
than passing malformed data to a durable effect.
396+
## The reviewer sees the complete pull request
397+
398+
`<PullRequest>` (#295) resolves a structured handle carrying the number, URL,
399+
head and base identities, state, reviews, comments, and checks. This stage
400+
consumes all of it, because the planner cannot recover any of it itself: agent
401+
network access is denied for the supervised exercise, so whatever the prompt does
402+
not render is invisible to the review. Every category is rendered explicitly —
403+
each collection iterated with `<Each>`, never stringified as an object — into
404+
both the planner prompt and the checkpoint material the user reads. A review that
405+
cannot see a failing check or an existing objection is not adversarial, it is
406+
uninformed.
407+
408+
The prompt names the revision under review: the planner reviews the diff at
409+
`headSha` against `baseSha`, and a verdict describes that head only. A moved head
410+
invalidates it and a fresh review is required — the same rule #295 states for a
411+
stored verdict.
412+
413+
The **member field names** used above — a review's `author`, `state`, `headSha`
414+
and `body`, a comment's `author`, `path` and `body`, a check's `name`, `status`,
415+
`conclusion` and `url` — are #295's to settle. This document depends on that
416+
schema rather than defining a competing one; what is settled here is that the
417+
planner receives the complete snapshot, not what each member is called.
418+
419+
The handle stays internal: it is not part of this component's declared return,
420+
because `start.md` gates on the verdict and decision rather than on pull-request
421+
state, and the artifact ledger (#291) records the effect and its handle
422+
independently. A return field typed `string` would be worse than useless — a
423+
conforming `<PullRequest>` would perform its durable effects and only then fail
424+
this component's return validation. If a later caller genuinely needs the handle,
425+
it is declared with #295's object schema, never a placeholder.
426+
427+
The agent, parsing, and control-flow syntax runs today. This component declares
428+
`returns`, so it contains no `<Output>` and its whole body runs fail-fast: the
429+
final `<Parse>` in each repair loop ends the stage rather than passing malformed
430+
data to a durable effect, and a failure binds nothing at all.
367431

368432
The user's decision outranks the verdict here too. `reviewCheckpoint.proceed` is
369433
read before `verdict.passed`, so a declined pull-request review leaves the loop

‎workflows/adversarial-implementation/Planning.md‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -254,12 +254,14 @@ presenting it as reviewed — a rejection is neither a revision request nor an
254254
acceptance. `<Break />` works from that nested position, so the two conditions
255255
compose without a flag binding between them.
256256

257-
A value component's body runs fail-fast, which is what makes the bounded repair
258-
turns a real gate: `<SafeParse>` absorbs a malformed verdict so the document can
259-
show the correction prompt, and the final `<Parse>` ends the stage if the
260-
candidate is still invalid. `throwOnError` on each `<Prompt>` is required for the
261-
same reason — a failed prompt without it records its failure and returns its
262-
text, raising nothing.
257+
This component declares `returns`, so it contains no `<Output>` and there is no
258+
documentation split: the whole body runs fail-fast and binds nothing if it
259+
fails. That is what makes the bounded repair turns a real gate — `<SafeParse>`
260+
absorbs a malformed verdict so the document can show the correction prompt, and
261+
the final `<Parse>` ends the stage if the candidate is still invalid, rather than
262+
returning something half-formed for a caller to branch on. `throwOnError` on each
263+
`<Prompt>` is required for the same reason: a failed prompt without it records
264+
its failure and returns its text, raising nothing.
263265

264266
**Outstanding gap: the terminal policy for exhaustion.** Reaching `max`
265267
completes the loop normally — exhaustion is not a failure and produces no

‎workflows/adversarial-implementation/UserCheckpoint.md‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,6 @@ from continuing after the bounded repair loop with malformed output. The schema
218218
is ordinary captured document content rather than a registry entry or
219219
`<Prompt schema>` prop.
220220

221-
A value component's body is not documentation in the ordinary sense — a value
222-
root and a value component both run fail-fast, so the assessment, the repair
223-
loop, and the elicitation all end the stage rather than binding something a
224-
caller would mistake for a decision.
225-
226221
`<Agent name={agent}>` reads the bare binding because that is the expression-
227222
prop spelling current main supports, while `{props.purpose}` in the prompt body
228223
is text interpolation; #305 unifies the two.

‎workflows/adversarial-implementation/artifacts.md‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,13 @@ else renders nothing — not an error, a value with no destination.
8686
### What a failing stage returns
8787

8888
Nothing partial. A text component's `<Output>` region runs under the `output`
89-
error mode and everything outside it under `throw`; a value component's whole
90-
body runs fail-fast. Either way an undecided error fails the run rather than
91-
producing a result the caller would bind, and a value component that fails binds
92-
nothing at all — there is no half-validated return. A failing `<Output>` region
93-
keeps only the text it had already rendered, and that text reaches the output
94-
stream; nothing after the failure does. A failed run is still a complete
89+
error mode and everything outside it under `throw`. A value component has no such
90+
split — it renders nothing, so `<Output>` inside one is a structural error — and
91+
its whole body runs fail-fast. Either way an undecided error fails the run rather
92+
than producing a result the caller would bind, and a value component that fails
93+
binds nothing at all: there is no half-validated return. A failing `<Output>`
94+
region keeps only the text it had already rendered, and that text reaches the
95+
output stream; nothing after the failure does. A failed run is still a complete
9596
record — replay restores its output and its failure without re-executing
9697
anything.
9798

‎workflows/adversarial-implementation/primitives.md‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,15 @@ structure and read where an error is raised:
6767
A failing region keeps what it had already rendered: that text reaches the
6868
output stream, and nothing after the failure does.
6969

70-
A component body is split by its `<Output>` boundary: the region inside runs
71-
under `output`, everything outside is documentation and runs under `throw`.
72-
Every stage in this workflow puts its prompts, parsing, and control flow outside
73-
`<Output>`, so a stage returns a complete validated result or it fails — it
74-
never returns a half-record. `<Retry>` and `<Result as>` would let a document
75-
handle a failure instead of ending on it; both are defined and unbuilt.
70+
A **text component**'s body is split by its `<Output>` boundary: the region
71+
inside runs under `output`, everything outside is documentation and runs under
72+
`throw`. A **value component** declares `returns` and renders nothing, so it
73+
cannot contain `<Output>` — that is a structural error — and its entire body
74+
runs fail-fast, binding nothing when it fails.
75+
76+
Either way a component returns a complete validated result or it fails; neither
77+
kind can hand a caller a half-record. `<Retry>` and `<Result as>` would let a
78+
document handle a failure instead of ending on it; both are defined and unbuilt.
7679

7780
**Missing: printing an `output` decision.** The `output` row above is the
7881
settled contract, and the engine does not meet it yet — an outer

‎workflows/adversarial-implementation/start.md‎

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -257,24 +257,30 @@ use the bare spelling in expression props, and #305 migrates them.
257257

258258
## Error modes in a stage
259259

260-
Every stage component is split by its `<Output>` boundary, and the two halves
261-
fail differently:
260+
How a component fails depends on which kind it is.
261+
262+
A **text component** — `InstructionFiles` and `Discovery` — is split by its
263+
`<Output>` boundary:
262264

263265
- Everything **outside** `<Output>` is documentation and runs under the `throw`
264266
error mode. The first error stops the body and fails the run, and no
265-
`<PrintErrors>` region can print it instead — which is what makes each
266-
stage's final `<Parse>` a real gate rather than a formality.
267+
`<PrintErrors>` region can print it instead.
267268
- The **`<Output>` region** runs under the `output` error mode: an undecided
268269
error there fails the run too, though a `<PrintErrors>` region may print it
269270
instead. Either way the region keeps what it had already rendered — that
270271
partial text reaches the output stream, and nothing after the failure does.
271272
Printing an `output` decision is the contract; the engine does not do it yet
272273
(#327).
273274

274-
So a stage either returns a complete, schema-validated result or it fails. It
275-
never returns a half-record. The `throwOnError` on each `<Prompt>` is
276-
load-bearing for the same reason: a failed prompt without it records its
277-
failure and returns its text, raising nothing for the error mode to decide.
275+
A **value component** — `UserCheckpoint`, `Planning`, `Implementation` — has no
276+
such split. Declaring `returns` means it renders nothing, so `<Output>` inside
277+
one is a structural error; its whole body runs fail-fast and a failure binds
278+
nothing at all. There is no partially validated return for a caller to gate on.
279+
280+
Either way a stage returns a complete, schema-validated result or it fails, and
281+
never a half-record. The `throwOnError` on each `<Prompt>` is load-bearing for
282+
the same reason: a failed prompt without it records its failure and returns its
283+
text, raising nothing for the error mode to decide.
278284

279285
## What runs today
280286

@@ -327,11 +333,13 @@ with nothing derived from them. The three checkpoints bind decisions. This
327333
document renders the human-readable reports from those returned fields rather
328334
than receiving them pre-rendered.
329335

330-
Neither stage returns the pull-request handle. `<PullRequest>` (#295) resolves a
331-
structured handle with the number, URL, head and base identities, state, reviews,
332-
comments, and checks; nothing here consumes it, and the artifact ledger (#291)
333-
records it independently. `Implementation` renders the fields a reader needs into
334-
its `report`.
336+
Neither stage returns the pull-request handle, but `Implementation` consumes it
337+
in full. `<PullRequest>` (#295) resolves the number, URL, head and base
338+
identities, state, reviews, comments, and checks; the planner has no network
339+
access, so `Implementation` renders every category explicitly into the review
340+
prompt and into the checkpoint material, and the artifact ledger (#291) records
341+
the effect independently. What `start.md` gates on is the verdict and the
342+
decision, so the handle itself never crosses the stage boundary.
335343

336344
## Details
337345

0 commit comments

Comments
 (0)