Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .github/workflows/agent-task-contracts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ jobs:
- run: npm run test:bounded-runtime-plan
- run: npm run test:bounded-recipe-plan
- run: npm run test:bounded-recipe-plan-integration
- run: npm run test:recipe-step-continuation
- run: npm run test:disposable-mysql-mysqli-e2e
- run: npm run test:runtime-sources-playground-integration
- run: npm run test:playground-phpunit-readonly-cache-integration
Expand Down
47 changes: 47 additions & 0 deletions docs/recipe-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,53 @@ Use `inputs.workspace_preloads` for generic `agent-runtime/workspace-preload`
artifact contracts. WP Codebox materializes declared repositories as sandbox
workspace mounts; callers own the policy that decides which artifacts to pass.

## Bounded Step Continuation

A workflow step can repeat a JSON-producing command in the same runtime and map
values from one result into the next invocation. The contract uses RFC 6901 JSON
Pointers and always requires a finite `maxIterations`:

```json
{
"command": "wordpress.ability",
"args": [
"name=example/resumable-operation",
"input={\"url\":\"https://example.com/\"}"
],
"continuation": {
"maxIterations": 100,
"while": {
"pointer": "/result/continuation",
"equals": true
},
"inputMappings": [
{
"from": "/result/receipt",
"to": {
"arg": "input",
"pointer": "/receipt"
}
}
]
}
}
```

The runner evaluates pointers against `execution.result.json`, falling back to a
JSON object parsed from stdout. Each mapping updates one JSON-valued `key=value`
argument while preserving all unrelated arguments. Missing or malformed values,
ambiguous target arguments, command failures, and a still-matched predicate at
the iteration limit fail closed. Continuation is unavailable for synthetic and
host-executed recipe helpers such as workload collection, workload manifests,
fanout, bounded plans, checkpoints, and fuzz suites. A continuation accepts at
most 64 input mappings; argument names and JSON Pointers are length-bounded. The
terminal command result stays compatible with normal step consumers.
Per-iteration evidence records argument and result SHA-256 digests, byte counts,
and a result preview only when it is at most 4 KiB. Predicate equality evidence
follows the same 4 KiB preview bound. Failed and timed-out steps retain partial
bounded `wp-codebox/recipe-continuation-evidence/v1` evidence in their failure
record.

## Adversarial Campaigns

`adversarialCampaigns` additively declares deterministic corpus campaigns without
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
"test:bounded-runtime-plan": "tsx tests/bounded-runtime-plan.test.ts",
"test:bounded-recipe-plan": "tsx tests/bounded-recipe-plan.test.ts",
"test:bounded-recipe-plan-integration": "tsx tests/bounded-recipe-plan.integration.test.ts",
"test:recipe-step-continuation": "tsx tests/recipe-step-continuation.test.ts && tsx tests/recipe-step-continuation.integration.test.ts",
"test:fanout-execution": "tsx tests/fanout-execution.test.ts",
"test:agent-fanout-executor": "tsx tests/agent-fanout-executor.test.ts",
"test:fanout-aggregation-contract-parity": "tsx tests/fanout-aggregation-contract-parity.test.ts",
Expand Down
18 changes: 18 additions & 0 deletions packages/cli/src/commands/recipe-run-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,24 @@ export type RecipeExecutionResult = ExecutionResult & {
fuzzCaseIndex?: number
fuzzPhase?: WorkspaceRecipeFuzzCasePhase
fuzzStepIndex?: number
continuationEvidence?: RecipeContinuationEvidence
}

export interface RecipeContinuationEvidence {
schema: "wp-codebox/recipe-continuation-evidence/v1"
policy: {
maxIterations: number
while: { pointer: string; equalsBytes: number; equalsSha256: string; equals?: unknown; equalsTruncated?: boolean }
inputMappings: Array<{ from: string; to: { arg: string; pointer: string } }>
}
status: "completed" | "failed" | "exhausted"
iterations: number
executions: Array<{ iteration: number; exitCode: number; argsSha256: string; resultBytes: number; resultSha256: string; result?: unknown; resultTruncated?: boolean }>
diagnostics?: { code: string; message: string }
}

export type RecipeContinuationProgress = Pick<RecipeContinuationEvidence, "schema" | "policy" | "iterations" | "executions">

export interface RecipeWorkflowArgsEvidence {
schema: "wp-codebox/recipe-workflow-args/v1"
original: string[]
Expand Down Expand Up @@ -229,6 +245,7 @@ export interface RecipeAdvisoryFailure {
command: string
status: "failed"
error: RunOutput["error"]
continuationEvidence?: RecipeContinuationEvidence
}

export interface RecipeStepFailure {
Expand All @@ -243,6 +260,7 @@ export interface RecipeStepFailure {
classification: "timeout" | "error"
timeoutMs?: number
error: RunOutput["error"]
continuationEvidence?: RecipeContinuationEvidence
}

export interface RecipeBrowserEvidenceFileRef {
Expand Down
Loading
Loading