Skip to content

Commit f98652a

Browse files
committed
🤖 Improve adversarial workflow continuity and closeout
1 parent 32b53ab commit f98652a

21 files changed

Lines changed: 496 additions & 43 deletions

File tree

‎examples/adversarial-implementation/README.md‎

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,42 @@ The roles use the normal agent permission policy. Running this example invokes
178178
paid coding agents and permits the assigned implementation work. Publication,
179179
issue/PR metadata and delivery remain with the Sidekick running Codex.
180180

181+
## Keep the original conversation
182+
183+
Every production role passes through `Phase.Session` inside its resolved Session,
184+
before its Prompt. The first call records the agent, logical name, resolved
185+
session key, cwd and provider-native identity under the work's `sessions`
186+
directory. Fresh placements that acquire their native identity during the turn
187+
retain it when that turn unwinds. Subsequent calls compare the resolved identity
188+
before expanding the prompt. A mismatch preserves the original receipt and saves
189+
the observed mismatch; it sends no prompt and grants no retry or fallback.
190+
Session resolution retains its existing provider setup behavior.
191+
192+
Ordinary Tickets and API foundation/migration work share their original identity
193+
directory across attempts. For standalone `phase.md` requests, retain
194+
`assignment.identityDirectory` and set `assignment.sessionEstablished=true` on
195+
continuations. An established continuation with a missing receipt holds; recover
196+
the original evidence rather than treating it as a fresh call. Adopting a runtime
197+
or adapter never proves native continuity from a logical session name alone.
198+
Independent review uses `delivery.directory/sessions` and requires its retained
199+
identity when an earlier review assignment exists. An explicitly prepared
200+
continuation can likewise require the original receipt with
201+
`delivery.sessionEstablished=true`.
202+
203+
## Select typing evidence and record amendments
204+
205+
When a shared type, required property or exported interface changes, planning
206+
identifies its typing consumers, declarations and test fixtures as well as
207+
runtime callers. Include the applicable Deno and Node typecheck in the focused
208+
evidence for affected shared shapes. Runtime affected-test selection follows
209+
imports and does not establish that every typing consumer still compiles.
210+
This does not require full runtime suites for ordinary changes.
211+
212+
Record user-directed amendments separately from implementation defects in plans,
213+
handoffs and cost reports, with their accepted scope and approval evidence.
214+
Both retain the existing correction allowances. Classification grants no extra
215+
call, resets no counter and does not turn an amendment into a defect metric.
216+
181217
## Complete delivery from recorded requirements
182218

183219
Use this after implementation feedback, for ordinary runs and REPL entries.
@@ -219,6 +255,23 @@ feedback ready -> independent review pending -> joined exact-commit verdict
219255
| confirmed merge -> delivered
220256
```
221257

258+
Independent review summaries allow 1500 characters; detailed observations belong
259+
in `findings`. The request states this bound alongside the exact revision and
260+
clause IDs. Ordinary phase and API-foundation summaries retain their own bounds.
261+
262+
Use `--props-action reconcile` when an original joined review has saved execution
263+
and raw reply but no accepted receipt. It checks the original assignment,
264+
joined execution, unchanged raw reply, current schema, clauses and source before
265+
creating the missing receipt. It never shortens text, changes a verdict, repeats
266+
a model call or overwrites a contradictory receipt. Missing or uncertain execution
267+
continues to hold. Status uses this same validation boundary to read receipts.
268+
269+
Use `--props-action closeout` to write `delivery/closeout.json` and
270+
`delivery/closeout.md` from the actual `Delivery.Status` result. The JSON retains
271+
the full status and evidence; the report names the revision, gates, review,
272+
findings and next action. Incomplete gates remain visible. This action starts no
273+
model, publishes nothing and grants no merge permission.
274+
222275
A complete review covers every recorded clause and has no blocking finding.
223276
The component writes its completion receipt after the role operation has returned
224277
and its scope has unwound. Status compares that receipt with the actual saved
@@ -533,7 +586,7 @@ installed.
533586
`ExactCorrection` and `Guide.ExactCorrection` package the existing local feedback
534587
workflow. The normal launch composes those owners from prepared scope while retaining
535588
validated foundations, role profiles, API-conflict repair, retirement and Codex
536-
editorial ownership. Delivery
589+
editorial ownership. Publication
537590
automation, task-specific attachment/routing programs and historical trial
538591
artifacts are outside this example.
539592

‎examples/adversarial-implementation/components/Adversarial/PlannerRole.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ props:
1212

1313
<Adversarial.Schema as="schemas" />
1414
<Phase.Profile role="planner" design={true} plannerProvider={props.assignment.plannerProvider} assessment={props.assignment.assessment} as="profile" />
15-
<Output><Agent name={profile.provider}><Session name={props.assignment.session} model={profile.model} effort={profile.effort}><Prompt throwOnError={true}>
15+
<Output><Agent name={profile.provider}><Session name={props.assignment.session} model={profile.model} effort={profile.effort}><Phase.Session directory={props.assignment.identityDirectory} name={props.assignment.session} requireExisting={props.assignment.sessionEstablished === true || props.correction !== null}><Prompt throwOnError={true}>
1616
<If condition={props.correction !== null}>
1717
You remain the original Planner. This is response-only correction {props.correction.ordinal} with {props.correction.remaining} remaining. Shorten only the rejected summary or handoff to its stated bound. Every other result value is frozen by the supplied schema, including the API, outcome, source revision, artifacts, assessment and command evidence.
1818

@@ -42,8 +42,10 @@ Return needs_architect only for a concrete structural question, needs_decision f
4242
Prepared assignment:
4343
<Json value={props.assignment} />
4444

45+
Keep summary within {schemas.foundation.properties.summary.maxLength} characters and handoff within {schemas.foundation.properties.handoff.maxLength} characters. Use artifact files for detailed reports and command receipts. Report the actual committed revision. Return only the fields declared by this foundation schema; do not add ordinary phase metadata. Check every required field against the contract before returning.
46+
4547
Return exactly one JSON object satisfying this contract. Evidence names actual command receipts and revisions; schema validation does not establish that tests passed.
4648
<Json value={schemas.foundation} />
4749
</Else>
4850
</If>
49-
</Prompt></Session></Agent></Output>
51+
</Prompt></Phase.Session></Session></Agent></Output>

‎examples/adversarial-implementation/components/Adversarial/Run.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ mkdir -- {quoted}
4343
<Loop max={(task.maxApiRepairs + 1) * 2}>
4444
<Ticket.Checkout repository={task.repository} as="checkout" />
4545
<Let as="attempt" value={task.directory + "/foundation-" + history.length} />
46-
<Let as="assignment" value={{work:task.work,repository:task.repository,revision:checkout.head,attempt,session:task.sessions.planner,plannerProvider:task.providers.planner,maxResponseCorrections:task.maxResponseCorrections,initialize:!initialized.planner,request:task.request,inputs:inputs.filter(input => !(deliveredInputs[task.sessions.planner] ?? []).includes(input.path)),api,conflict,contract,assessment}} />
46+
<Let as="assignment" value={{work:task.work,repository:task.repository,revision:checkout.head,attempt,identityDirectory:task.directory + "/sessions",sessionEstablished:initialized.planner,session:task.sessions.planner,plannerProvider:task.providers.planner,maxResponseCorrections:task.maxResponseCorrections,initialize:!initialized.planner,request:task.request,inputs:inputs.filter(input => !(deliveredInputs[task.sessions.planner] ?? []).includes(input.path)),api,conflict,contract,assessment}} />
4747
<Ticket.Capture as="execution"><Adversarial.Foundation assignment={assignment} guide={props.plannerGuide.content} as="foundation" /><Json value={foundation} /></Ticket.Capture>
4848
<Dir path={task.directory}><File path={"foundation-" + history.length + "-execution.json"}><Json value={execution} /></File></Dir>
4949
<Let as="history" value={[...history,{owner:"planner",attempt,status:execution.status}]} />
@@ -52,7 +52,7 @@ mkdir -- {quoted}
5252
<If condition={execution.status !== "completed"}><Let as="finished" value={true} /><Return value={{status:"hold",reason:execution.failure,history}} /><Break /></If>
5353
<Parse schema={{type:"object"}} as="foundation">{execution.output}</Parse>
5454
<If condition={foundation.result.outcome === "needs_architect"}>
55-
<Phase.Run maxResponseCorrections={task.maxResponseCorrections} phase="discovery" role="architect" session={task.sessions.architect} initialize={!initialized.architect} assignment={{phase:"discovery",work:task.work,repository:task.repository,revision:checkout.head,request:foundation.result.handoff,assessment:foundation.result.assessment,artifacts:foundation.result.artifacts,evidence:foundation.result.evidence,findings:[],changedPaths:[],contract}} attempt={task.directory + "/architecture-" + history.length} successor="discovery" nextRole="planner" as="architecture" />
55+
<Phase.Run maxResponseCorrections={task.maxResponseCorrections} phase="discovery" role="architect" session={task.sessions.architect} initialize={!initialized.architect} assignment={{identityDirectory:task.directory + "/sessions",sessionEstablished:initialized.architect,phase:"discovery",work:task.work,repository:task.repository,revision:checkout.head,request:foundation.result.handoff,assessment:foundation.result.assessment,artifacts:foundation.result.artifacts,evidence:foundation.result.evidence,findings:[],changedPaths:[],contract}} attempt={task.directory + "/architecture-" + history.length} successor="discovery" nextRole="planner" as="architecture" />
5656
<If condition={architecture.outcome !== "complete"}><Let as="finished" value={true} /><Return value={{status:"hold",reason:architecture.handoff,history}} /><Break /></If>
5757
<Let as="contract" value={architecture.contract} />
5858
<Let as="conflict" value={{question:foundation.result.handoff,answer:architecture.handoff,artifacts:architecture.artifacts}} />
@@ -68,15 +68,15 @@ mkdir -- {quoted}
6868
<Let as="acceptedInputs" value={[...inputs,{path:apiPath,content:apiText},{path:api.guide,content:usage}]} />
6969
<If condition={assessment.architecture}>
7070
<Ticket.ChangedPaths repository={task.repository} base={assignment.revision} head={api.revision} as="foundationChanges" />
71-
<Phase.Run maxResponseCorrections={task.maxResponseCorrections} phase="review" role="architect" session={task.sessions.architect} initialize={!initialized.architect} inputs={acceptedInputs} assignment={{phase:"review",work:task.work,repository:task.repository,revision:api.revision,priorRevision:assignment.revision,request:"Review the committed API foundation's structural impact before consumer migration. Inspect its validated examples and changed paths against the accepted outcome: " + task.request,assessment,contract,artifacts:[foundation.path,apiPath],evidence:foundation.validated.checks,findings:[],changedPaths:foundationChanges.paths}} attempt={task.directory + "/foundation-review-" + history.length} successor="ready" as="foundationReview" />
71+
<Phase.Run maxResponseCorrections={task.maxResponseCorrections} phase="review" role="architect" session={task.sessions.architect} initialize={!initialized.architect} inputs={acceptedInputs} assignment={{identityDirectory:task.directory + "/sessions",sessionEstablished:initialized.architect,phase:"review",work:task.work,repository:task.repository,revision:api.revision,priorRevision:assignment.revision,request:"Review the committed API foundation's structural impact before consumer migration. Inspect its validated examples and changed paths against the accepted outcome: " + task.request,assessment,contract,artifacts:[foundation.path,apiPath],evidence:foundation.validated.checks,findings:[],changedPaths:foundationChanges.paths}} attempt={task.directory + "/foundation-review-" + history.length} successor="ready" as="foundationReview" />
7272
<Let as="initialized" value={{...initialized,architect:true}} />
7373
<Dir path={task.directory}><File path={"foundation-review-" + history.length + "-result.json"}><Json value={foundationReview} /></File></Dir>
7474
<Ticket.Checkout repository={task.repository} as="reviewedFoundation" />
7575
<If condition={foundationReview.outcome !== "pass" || foundationReview.revision !== api.revision || reviewedFoundation.head !== api.revision || !reviewedFoundation.clean}><Let as="finished" value={true} /><Return value={{status:"hold",reason:"The structural API foundation requires Architect acceptance at its unchanged validated commit.",review:foundationReview,history}} /><Break /></If>
7676
</If>
7777
<Let as="currentReferences" value={[...acceptedInputs.map(input => input.path),props.plannerGuide.path,props.implementorGuide.path]} />
7878
<Let as="deliveredInputs" value={Object.fromEntries(Object.entries(deliveredInputs).map(([session,paths]) => [session,paths.filter(path => currentReferences.includes(path))]))} />
79-
<Let as="migration" value={{schema:"ticket-workflow.v2",work:task.work,directory:task.directory + "/migration-" + round,api,maxCorrections:task.maxCorrections,maxApiRepairs:task.maxApiRepairs,maxResponseCorrections:task.maxResponseCorrections,deliveredInputs,roles:{architect:{session:task.sessions.architect,provider:"codex",initialized:initialized.architect},planner:{session:task.sessions.planner,provider:task.providers.planner,initialized:true},implementor:{session:task.sessions.implementor,provider:task.providers.implementor,initialized:initialized.implementor}},acceptedInputs,roleInputs:{planner:[props.plannerGuide],implementor:[props.implementorGuide]},assignment:{phase:"implementation",work:task.work,repository:task.repository,revision:foundation.validated.revision,priorRevision:assignment.revision,request:api.migration.request,assessment,contract,artifacts:[foundation.path,apiPath],evidence:foundation.validated.checks,findings:[],changedPaths:[]}}} />
79+
<Let as="migration" value={{schema:"ticket-workflow.v2",work:task.work,directory:task.directory + "/migration-" + round,identityDirectory:task.directory + "/sessions",api,maxCorrections:task.maxCorrections,maxApiRepairs:task.maxApiRepairs,maxResponseCorrections:task.maxResponseCorrections,deliveredInputs,roles:{architect:{session:task.sessions.architect,provider:"codex",initialized:initialized.architect},planner:{session:task.sessions.planner,provider:task.providers.planner,initialized:true},implementor:{session:task.sessions.implementor,provider:task.providers.implementor,initialized:initialized.implementor}},acceptedInputs,roleInputs:{planner:[props.plannerGuide],implementor:[props.implementorGuide]},assignment:{phase:"implementation",work:task.work,repository:task.repository,revision:foundation.validated.revision,priorRevision:assignment.revision,request:api.migration.request,assessment,contract,artifacts:[foundation.path,apiPath],evidence:foundation.validated.checks,findings:[],changedPaths:[]}}} />
8080
<Let as="manifest" value={attempt + "/migration.json"} />
8181
<Dir path={attempt}><File path="migration.json"><Json value={migration} /></File></Dir>
8282
<Ticket.Run manifest={manifest} as="ticket" />
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
description: 'Save a deterministic delivery report from current validated status. Use <Delivery.Closeout delivery={delivery} number={123} as="status" />.'
3+
props:
4+
type: object
5+
properties:
6+
delivery: {type: object}
7+
number: {type: integer, minimum: 1}
8+
required: [delivery, number]
9+
additionalProperties: false
10+
returns: {type: object}
11+
---
12+
13+
<Delivery.Status delivery={props.delivery} number={props.number} as="status" />
14+
<Let as="reportRevision" value={status.revision ?? "Implementation feedback pending"} />
15+
<Dir path={props.delivery.directory}>
16+
<File path="closeout.json"><Json value={status} /></File>
17+
<File path="closeout.md">
18+
# Delivery {props.delivery.work}
19+
20+
Status: {status.status}
21+
22+
Revision: {reportRevision}
23+
24+
Next action: {status.nextAction}
25+
26+
<Each in={status.required} let="gate">
27+
- {gate.gate}: {gate.state}
28+
</Each>
29+
30+
<If condition={status.review != null}>
31+
Independent review: {status.review.result.verdict}
32+
33+
{status.review.result.summary}
34+
35+
<Each in={status.review.result.findings} let="finding">
36+
<Let as="findingKind" value={finding.blocking ? "Blocking" : "Non-blocking"} />
37+
- {findingKind}: {finding.description}
38+
</Each>
39+
</If>
40+
41+
Evidence: closeout.json retains the complete status, selected CI and main-health evidence, and review references.
42+
43+
This report starts no model call, publishes nothing and grants no merge permission.
44+
</File>
45+
</Dir>
46+
<Return value={status} />
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
description: 'Validate an original joined review and reconcile its receipt without prompting. Use <Delivery.Reconcile delivery={delivery} revision={revision} publish={true} as="receipt" />.'
3+
props:
4+
type: object
5+
properties:
6+
delivery: {type: object}
7+
revision: {type: string, pattern: '^[0-9a-f]{40}$'}
8+
publish: {type: boolean, default: false}
9+
required: [delivery, revision]
10+
additionalProperties: false
11+
returns: {type: object}
12+
---
13+
14+
<Delivery.Schema as="schemas" />
15+
<Let as="attempt" value={props.delivery.directory + "/review-" + props.revision} />
16+
<Let as="assigned" value={{work:props.delivery.work,revision:props.revision,reviewer:props.delivery.policy.reviewer,acceptance:props.delivery.policy.acceptance}} />
17+
<Ticket.Text path={attempt + "/assignment.json"} as="assignmentText" />
18+
<Parse schema={{type:"object"}} as="assignment">{assignmentText}</Parse>
19+
<If condition={JSON.stringify(assignment) !== JSON.stringify(assigned)}><Fail message="The original review assignment differs from the recorded delivery requirement. Reconcile it without dispatch." /></If>
20+
<Ticket.Text path={attempt + "/execution.json"} as="executionText" />
21+
<Parse schema={{type:"object",properties:{status:{const:"completed"},output:{type:"string"},failure:{type:"null"}},required:["status","output","failure"],additionalProperties:false}} as="execution">{executionText}</Parse>
22+
<Parse schema={{type:"object",properties:{reply:{type:"string"}},required:["reply"],additionalProperties:false}} as="returned">{execution.output}</Parse>
23+
<Ticket.Text path={attempt + "/raw.txt"} as="raw" />
24+
<If condition={raw !== returned.reply}><Fail message="The saved raw reply differs from the joined execution. Preserve both for reconciliation." /></If>
25+
<Phase.Extract text={raw} as="extracted" />
26+
<If condition={!extracted.ok}><Fail message={extracted.rejection.message} /></If>
27+
<Parse schema={schemas.review} as="result">{extracted.json}</Parse>
28+
<If condition={result.revision !== props.revision || result.acceptance.length !== props.delivery.policy.acceptance.length || props.delivery.policy.acceptance.some(clause => !result.acceptance.includes(clause.id)) || result.verdict === "pass" && result.findings.some(finding => finding.blocking)}><Fail message="The joined review must cover every recorded clause on the assigned commit and cannot pass with a blocking finding." /></If>
29+
<Let as="receipt" value={{...assigned,result,execution:attempt + "/execution.json"}} />
30+
<Dir path={props.delivery.directory}><Glob include={["review-" + props.revision + "/receipt.json"]} as="receipts" /></Dir>
31+
<If condition={receipts.length !== 0}>
32+
<Ticket.Text path={attempt + "/receipt.json"} as="receiptText" />
33+
<Parse schema={{type:"object"}} as="saved">{receiptText}</Parse>
34+
<If condition={JSON.stringify(saved) !== JSON.stringify(receipt)}><Fail message="The receipt differs from its original joined review. Preserve it for reconciliation." /></If>
35+
<Else>
36+
<If condition={!props.publish}><Fail message="The joined review has no completion receipt. Use the explicit reconcile action; do not repeat the review." /></If>
37+
</Else>
38+
</If>
39+
<If condition={props.publish}>
40+
<Ticket.Checkout repository={props.delivery.repository} as="checkout" />
41+
<If condition={checkout.head !== props.revision || !checkout.clean}><Fail message="Source changed since the review. Preserve its result; the current source needs its own evidence." /></If>
42+
<If condition={receipts.length === 0}><Dir path={attempt}><File path="receipt.json"><Json value={receipt} /></File></Dir></If>
43+
</If>
44+
<Return value={receipt} />

0 commit comments

Comments
 (0)