Skip to content

Commit 4addbd4

Browse files
committed
📝 Make the user's decision gate execution, and correct the result inventory
`<UserCheckpoint>` captured `elicitation.proceed` and then returned only rendered prose, so a caller had nothing to branch on. `start.md` invoked `Planning` and `Implementation` unconditionally and the two stages branched on the planner verdict alone: a user answering `proceed: false` could not stop anything. That contradicts the central user-authority contract and #290's acceptance. `<UserCheckpoint>` now declares `returns` and binds a schema-validated decision — `proceed` plus the assessment fields a later prompt quotes. One `decisionSchema` serves both paths: `<Elicit>` binds it when the assessment reports a material choice, and an explicit `<Parse>` binds `"proceed": true` with its reason when there is none, so continuation is represented rather than inferred from absent elicitation data. Every affected transition is gated — a declined handoff does not start Planning, a declined authorization does not start Implementation, a declined review neither revises nor accepts, and a declined acceptance finishes as rejected rather than falling into the accepted branch. Nesting is what the language supports; it is not stopping. The run still expands to `<Output>` and completes, with no clean halt at a stage boundary and no stop reason recorded for one. That is classified as missing under #298 over #289 rather than described as enforcement. Two engine limitations surfaced while proving this and are filed rather than worked around silently. `<Content />` is substituted only at a body's top level or directly inside `<Output>`, never nested inside a `<Prompt>` (#328) — `Discovery` and `UserCheckpoint` used exactly that shape and could not expand, so both now take the material as a prop. And a binding made inside a Markdown component's projected content does not escape to its caller, while the same binding inside a registered function component does; the harness had to account for that to test the real components. artifacts.md claimed every listed result was JSON declared inline. It is corrected into three kinds: `PlannerHandoff` and `ImplementationPlan` are prose with no schema and cannot gate anything; the four parsed results are described using only fields their inline schemas declare; and `UserDecision` is the checkpoint's declared return, with the actor and timestamp that would make it auditable marked missing under #291. Discrimination over the shipped subset, with a turn-recording stub agent: a declined checkpoint records 1 agent turn and neither stage runs; the authorized path records 2 and both run; a no-material-choice assessment records 2 and proceeds on its explicit `<Parse>` branch while a declining answer in scope is never consulted.
1 parent eb76b16 commit 4addbd4

8 files changed

Lines changed: 421 additions & 192 deletions

File tree

‎specs/adversarial-implementation-workflow.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ exists or when the agent is unsure. The current workflow supplies the planner,
4242
but the component does not encode that role. It may determine that user
4343
involvement is unnecessary, but only the user resolves a material choice.
4444

45+
That authority controls execution rather than describing it. A checkpoint binds
46+
a schema-validated decision, and every material transition is gated on its
47+
`proceed`: `proceed: false` never advances the workflow. A declined handoff does
48+
not start planning, a declined authorization does not start implementation, a
49+
declined review neither revises nor accepts, and a declined acceptance finishes
50+
as rejected. A checkpoint that found no material choice produces an explicit
51+
`proceed: true` recording why, so a transition never advances because a decision
52+
was missing. What remains missing is stopping *at* the boundary — halting
53+
cleanly where the user answered so a later invocation resumes there, with a
54+
recorded stop reason — which is `<Stage>` (#298) over `<Workflow>` (#289).
55+
4556
## Smallest complete path
4657

4758
User and planner discuss the change
@@ -212,6 +223,9 @@ by-reference binding.
212223

213224
Every stage component in this workflow is a text component, because each
214225
stage's output is also material a user reads at a checkpoint.
226+
`<UserCheckpoint>` is not a stage and is the one exception: it declares
227+
`returns` and binds a validated transition decision, because its result is
228+
branched on rather than read.
215229

216230
### A stage fails rather than returning a half-record
217231

‎workflows/adversarial-implementation/Discovery.md‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
---
2-
required: [instructions, planner]
2+
required: [instructions, planner, request]
33

44
props:
55
instructions: { type: string }
66
planner: { type: string }
7+
request: { type: string }
78
---
89

910
# Discovery
@@ -23,7 +24,7 @@ issue may enter directly at implementor planning.
2324

2425
User request:
2526

26-
<Content />
27+
{props.request}
2728

2829
Produce a user-validated design handoff and a falsifiable implementation
2930
theory. Distinguish user decisions from hypotheses the implementor must
@@ -51,8 +52,10 @@ selects the agent from a validated prop rather than a literal: the agent
5152
components take their props from a literal or from an expression that resolves
5253
to a string. An expression prop reads the bare binding, while the prompt body
5354
above interpolates `{props.instructions}` — the two spellings that #305 will
54-
unify. Its caller supplies the request as content and decides whether and where
55-
to persist the result. The handoff is a theory for investigation, not an
55+
unify. Its caller supplies the request as the `request` prop and decides whether
56+
and where to persist the result. It would read better as projected content, but
57+
`<Content />` does not substitute inside a `<Prompt>` (#328), so a prop carries
58+
it. The handoff is a theory for investigation, not an
5659
implementation plan that the implementor follows unquestioningly.
5760

5861
The prompt sits outside `<Output>`, so it runs under the `throw` error mode:

‎workflows/adversarial-implementation/Implementation.md‎

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ required: [plan, authorization, instructions, planner, implementor]
33

44
props:
55
plan: { type: string }
6-
authorization: { type: string }
6+
authorization:
7+
type: object
8+
properties:
9+
proceed: { type: boolean }
10+
assessment: { type: string }
11+
response: { type: string }
12+
rationale: { type: string }
13+
required: [proceed, assessment, response, rationale]
714
instructions: { type: string }
815
planner: { type: string }
916
implementor: { type: string }
@@ -91,7 +98,9 @@ remote effects.
9198

9299
Authorization record:
93100

94-
{props.authorization}
101+
{props.authorization.assessment}
102+
User response: {props.authorization.response}
103+
Rationale: {props.authorization.rationale}
95104

96105
Result contract:
97106

@@ -165,7 +174,9 @@ remote effects.
165174

166175
Authorization record:
167176

168-
{props.authorization}
177+
{props.authorization.assessment}
178+
User response: {props.authorization.response}
179+
Rationale: {props.authorization.rationale}
169180

170181
Pull request:
171182

@@ -228,11 +239,7 @@ remote effects.
228239
</If>
229240
</Each>
230241

231-
<UserCheckpoint
232-
purpose="resolve the pull-request review"
233-
agent={planner}
234-
as="reviewCheckpoint"
235-
>
242+
<Capture as="checkpointMaterial">
236243
## Authorized plan
237244

238245
{props.plan}
@@ -254,56 +261,96 @@ remote effects.
254261

255262
{finding.description}
256263
</Each>
257-
</UserCheckpoint>
258-
<If condition={verdict.passed}>
259-
<Break />
260-
<Else>
261-
<Prompt agent={implementor} session="implementor">
262-
Revise the implementation using this review:
264+
</Capture>
265+
<UserCheckpoint
266+
purpose="resolve the pull-request review"
267+
agent={planner}
268+
material={checkpointMaterial}
269+
as="reviewCheckpoint"
270+
/>
271+
<If condition={reviewCheckpoint.proceed}>
272+
<If condition={verdict.passed}>
273+
<Break />
274+
<Else>
275+
<Prompt agent={implementor} session="implementor">
276+
Revise the implementation using this review:
263277

264-
{verdict.review}
278+
{verdict.review}
265279

266-
Focused revision prompt:
280+
Focused revision prompt:
267281

268-
{verdict.revisionPrompt}
282+
{verdict.revisionPrompt}
269283

270-
User involvement record:
284+
User involvement record:
271285

272-
{reviewCheckpoint}
273-
</Prompt>
286+
{reviewCheckpoint.assessment}
287+
User response: {reviewCheckpoint.response}
288+
Rationale: {reviewCheckpoint.rationale}
289+
</Prompt>
290+
</Else>
291+
</If>
292+
<Else>
293+
<Break />
274294
</Else>
275295
</If>
276296
</Loop>
277297

278298
<Output>
279-
# Implementation result
299+
<If condition={reviewCheckpoint.proceed}>
300+
# Implementation result
280301

281-
## Pull request
302+
## Pull request
282303

283-
{pullRequest}
304+
{pullRequest}
284305

285-
## Planner review
306+
## Planner review
286307

287-
Passed: {verdict.passed}
308+
Passed: {verdict.passed}
288309

289-
{verdict.review}
310+
{verdict.review}
290311

291-
## Findings
312+
## Findings
292313

293-
<Each in={verdict.findings} let="finding">
294-
### {finding.title}
314+
<Each in={verdict.findings} let="finding">
315+
### {finding.title}
295316

296-
Disposition: {finding.disposition}
317+
Disposition: {finding.disposition}
297318

298-
{finding.description}
299-
</Each>
319+
{finding.description}
320+
</Each>
321+
<Else>
322+
# Pull-request review rejected
323+
324+
The user declined to continue at the pull-request review checkpoint. The
325+
implementation below was neither revised nor accepted.
326+
327+
{reviewCheckpoint.rationale}
328+
329+
## Pull request
330+
331+
{pullRequest}
332+
333+
## Planner review
334+
335+
Passed: {verdict.passed}
336+
337+
{verdict.review}
338+
</Else>
339+
</If>
300340
</Output>
301341

302342
The agent, parsing, and control-flow syntax in this component runs today, on
303343
the same terms as `Planning`: the body outside `<Output>` runs under the
304344
`throw` error mode, so the final `<Parse>` in each repair loop ends the stage
305345
rather than passing malformed data to a durable effect.
306346

347+
The user's decision outranks the verdict here too. `reviewCheckpoint.proceed` is
348+
read before `verdict.passed`, so a declined pull-request review leaves the loop
349+
without revising the implementation and without reporting it as reviewed. The
350+
stage is reached at all only because `start.md` gated it on
351+
`authorization.proceed`; a declined authorization means these prompts and the
352+
durable effects below them never run.
353+
307354
`<Commit>` (#294), `<PullRequest>` (#295), and `<Issue>` (#296) do not exist,
308355
and reconciling those durable GitHub effects idempotently is #297. Until they
309356
land, the loop above cannot expand at all — the three names resolve to nothing,

0 commit comments

Comments
 (0)