Repository navigation
fix(runtime)!: write every agent task's prompt input index from the live task plan (#1234) - #1285
Conversation
…ive task plan (#1234) - materializePromptArtifactAuthority builds .ultrafuzz/authorities/<attempt>.json from the in-memory, post-expansion task plan (semanticArtifactTaskDeclarations), filtered to the admitted dependency directories, for every agent task; it no longer reads the execution snapshot's controls/tasks.json, so a deferred join (dedupe-findings, aggregate-test-files, final-report) gets the file its prompt names - the index lists every declared output of each admitted ancestor; the document drops `selectors`, and ancestor_artifact_path_authority lists its paths in the prompt - delete assertPromptArtifactAuthorityUnchanged, its call sites, the snapshot map, the sealed-selector equality check and the sealed-closure throw - add declaredAncestorOutputs beside declaredAncestorOutputsByContract - dynamic-lifecycle test: the execution snapshot's workflow, rendered in process, prepares a deferred join and runs its agent, and the index lists the generated child's findings.json and the planner's plan.json; the in-process render's inert agent gets a no-op generate Co-authored-by: mrthankyou <52643283+mrthankyou@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tors (#1234) - drop promptArtifactAuthoritySelectors from the task manifest schema (in place, ultrafuzz.smithers.workflow.v4) and the workflow task specs, with promptArtifactAuthoritySelectorsFor and the selector limits - drop the task specs' sourceTaskManifestPath, which only the index read - resume no longer refuses an edited prompt that names an authority its task was not compiled with - CHANGELOG: breaking entry for #1234 Co-authored-by: mrthankyou <52643283+mrthankyou@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…hot task manifest (#1234) - remove `selectorId` from the run plan's `ancestor_artifact_path_authority` reference (ultrafuzz.run-plan.v4, unreleased) and from the renderer, with the plan's self-hash check and promptArtifactAuthorityPathSelectorId; its readers (the manifest selectors and the refresh refusal) are gone - stop copying the compiled task manifest into the execution snapshot as controls/tasks.json; its only reader was the deleted authority read-back - delete parsePromptArtifactAuthorityBytes, which only tests called; tests check the written index with assertValidPromptArtifactAuthority - reword the comment, the unknown-contract diagnostic and the index errors that still described a sealed selector or selected outputs - the deferred-join test reads the launch task manifest from controls/runtime-base-tasks.json; reflow the in-process workflow doc comment - CHANGELOG: the #1234 entry notes the dropped `selectorId` Co-authored-by: mrthankyou <52643283+mrthankyou@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- stock prompts no longer call the index a sealed JSON authority or a sealed selector; final-report says the index lists every admitted ancestor output and that only the named report handoff paths are to be used - CHANGELOG: trim the #1234 entry and name the runs it affects (static prompts naming an authority, so every default-topology run) and their plan.json selectorId - tests: drop the dead taskManifestPath alternative from the base agent check, rename the prompt-structure authority test, and pin the reworded aggregation sentence Co-authored-by: mrthankyou <52643283+mrthankyou@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Resolve conflicts: keep this branch's removal of the sealed-selector test, and both CHANGELOG entries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
| @@ -2645,7 +2558,6 @@ function artifactAwareAgent( | |||
| Reflect.deleteProperty(unstructuredArgs, "ultrafuzzTaskRuntime"); | |||
| const result = await executionAgent.generate(unstructuredArgs); | |||
| assertDependencyArtifactAdmissionCurrent(task); | |||
There was a problem hiding this comment.
Index edits go undetected During a model attempt, the agent can replace its task-local input index, but the wrapper accepts the result without checking the index again. A schema-correction generation in the same attempt does not rewrite it, so that generation can read forged ancestor entries. Restore an integrity check between generations and before accepting the attempt.
How this was verified: The index is written in the agent’s workspace, and the post-generation path checks dependency artifacts but not the index.
Knowledge Base Used: Runtime orchestration
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/runtime/src/templates/smithers/workflows/workflow.tsx
Line: 2560
Comment:
**Index edits go undetected** During a model attempt, the agent can replace its task-local input index, but the wrapper accepts the result without checking the index again. A schema-correction generation in the same attempt does not rewrite it, so that generation can read forged ancestor entries. Restore an integrity check between generations and before accepting the attempt.
**How this was verified:** The index is written in the agent’s workspace, and the post-generation path checks dependency artifacts but not the index.
**Knowledge Base Used:** [Runtime orchestration](https://app.greptile.com/monad-foudnation/-/custom-context/knowledge-base/monad-developers/ultrafuzz/-/docs/runtime-orchestration.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| const workspaceRoot = realpathSync(task.workspacePath); | ||
| const authorityPath = prepareTaskLocalAuthorityPath(workspaceRoot, promptArtifactAuthorityRelativePath(task)); | ||
| writeFileDurable(authorityPath, expected); | ||
| const captured = readBoundedRegularArtifactSnapshot( | ||
| workspaceRoot, | ||
| authorityPath, | ||
| `artifact-contract failure: prompt artifact authority is unavailable ${task.attemptId}`, | ||
| MAX_PROMPT_ARTIFACT_AUTHORITY_BYTES, | ||
| true | ||
| ); | ||
| parsePromptArtifactAuthorityBytes(captured.bytes); | ||
| if (!captured.bytes.equals(expected)) { | ||
| throw new Error( | ||
| `artifact-contract failure: prompt artifact authority changed while materialized ${task.attemptId}` | ||
| ); | ||
| } | ||
| promptArtifactAuthoritySnapshotsByTask.set( | ||
| task.attemptId, | ||
| Object.freeze({ | ||
| path: captured.path, | ||
| bytes: Buffer.from(captured.bytes), | ||
| identity: captured.identity | ||
| }) | ||
| ); | ||
| } | ||
|
|
||
| function assertPromptArtifactAuthorityUnchanged(task: (typeof taskSpecs)[number]): void { | ||
| if (promptArtifactAuthoritySelectors(task).length === 0) return; | ||
| const expected = promptArtifactAuthoritySnapshotsByTask.get(task.attemptId); | ||
| if (expected === undefined) { | ||
| throw new Error(`artifact-contract failure: prompt artifact authority was not prepared ${task.attemptId}`); | ||
| } | ||
| const workspaceRoot = realpathSync(task.workspacePath); | ||
| const authorityPath = promptArtifactAuthorityPath(task, workspaceRoot); | ||
| const captured = readBoundedRegularArtifactSnapshot( | ||
| workspaceRoot, | ||
| authorityPath, | ||
| `artifact-contract failure: prompt artifact authority is unavailable ${task.attemptId}`, | ||
| MAX_PROMPT_ARTIFACT_AUTHORITY_BYTES, | ||
| true | ||
| ); | ||
| parsePromptArtifactAuthorityBytes(captured.bytes); | ||
| if ( | ||
| captured.path !== expected.path || | ||
| !sameImmutableFileIdentity(captured.identity, expected.identity) || | ||
| !captured.bytes.equals(expected.bytes) | ||
| ) { | ||
| throw new Error(`artifact-contract failure: prompt artifact authority was modified ${task.attemptId}`); | ||
| } | ||
| writeFileDurable(authorityPath, serializePromptArtifactAuthority(authority)); |
There was a problem hiding this comment.
Unused index can block tasks The runtime now serializes an index before every agent attempt, even when the prompt does not use one. If a valid fan-in has enough declared ancestor outputs to exceed the index’s 32 MiB limit, serialization fails before model work starts. That limit can stop an otherwise valid task whose prompt never needs the index.
Knowledge Base Used: Runtime orchestration
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/runtime/src/templates/smithers/workflows/workflow.tsx
Line: 965-967
Comment:
**Unused index can block tasks** The runtime now serializes an index before every agent attempt, even when the prompt does not use one. If a valid fan-in has enough declared ancestor outputs to exceed the index’s 32 MiB limit, serialization fails before model work starts. That limit can stop an otherwise valid task whose prompt never needs the index.
**Knowledge Base Used:** [Runtime orchestration](https://app.greptile.com/monad-foudnation/-/custom-context/knowledge-base/monad-developers/ultrafuzz/-/docs/runtime-orchestration.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| function renderAncestorArtifactPathAuthority(input: PromptRenderInput, paths: readonly string[]): string { | ||
| const listed = paths.map((artifactPath) => markdownCodeSpan(artifactPath)).join(", "); | ||
| return renderAncestorArtifactAuthority( | ||
| input, | ||
| paths.length === 1 ? `whose declared \`path\` is ${listed}` : `whose declared \`path\` is one of ${listed}` | ||
| ); | ||
| } | ||
|
|
There was a problem hiding this comment.
Path lists inflate prompts Exact-path authorities now put every requested path directly into the prompt. For a large path group, this uses substantial model context where the previous rendering stayed constant-size. Bound the list for prompt use or convey it compactly.
Knowledge Base Used: Prompt library
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/prompts/src/render.ts
Line: 1049-1056
Comment:
**Path lists inflate prompts** Exact-path authorities now put every requested path directly into the prompt. For a large path group, this uses substantial model context where the previous rendering stayed constant-size. Bound the list for prompt use or convey it compactly.
**Knowledge Base Used:** [Prompt library](https://app.greptile.com/monad-foudnation/-/custom-context/knowledge-base/monad-developers/ultrafuzz/-/docs/prompt-library.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| type: "object", | ||
| additionalProperties: false, | ||
| required: ["schema_version", "run_id", "attempt_id", "artifact_path_base", "selectors", "producers"], | ||
| required: ["schema_version", "run_id", "attempt_id", "artifact_path_base", "producers"], |
There was a problem hiding this comment.
Version hides incompatible index The document still identifies itself as
ultrafuzz.prompt-artifact-authority.v1, but it no longer has the formerly required selectors field and now rejects documents containing it. A consumer using that version cannot distinguish the incompatible shapes. Give the new shape a new schema version, even though current runtime code does not read the file back.
Knowledge Base Used: Artifact management
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/runtime/src/prompt-artifact-authority.ts
Line: 30
Comment:
**Version hides incompatible index** The document still identifies itself as `ultrafuzz.prompt-artifact-authority.v1`, but it no longer has the formerly required `selectors` field and now rejects documents containing it. A consumer using that version cannot distinguish the incompatible shapes. Give the new shape a new schema version, even though current runtime code does not read the file back.
**Knowledge Base Used:** [Artifact management](https://app.greptile.com/monad-foudnation/-/custom-context/knowledge-base/monad-developers/ultrafuzz/-/docs/artifact-management.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Closes #1234. @mrthankyou reproduced and diagnosed this bug in #1253 (closed), and the commits credit him as co-author.
Problem
A task that waits on a dynamic group has its prompt rendered at runtime. On the
defaulttopology these arededupe-findings,aggregate-test-filesandfinal-report. They were compiled withoutpromptArtifactAuthoritySelectors, somaterializePromptArtifactAuthorityreturned early. The.ultrafuzz/authorities/<attempt-id>.jsonfile their prompts name was never written. The selectors came only from plan-time renders, and the index was rebuilt from the launch-time task manifest (controls/tasks.json), which does not contain the children a dynamic group generates.Change
This implements the scope in the maintainer's latest comment on #1234.
materializePromptArtifactAuthoritybuilds the index from the in-memory task plan after dynamic expansion (semanticArtifactTaskDeclarations, through the newdeclaredAncestorOutputs), keeping only the admitted dependency directories. It no longer readstask.taskManifestPath.The index is written before every attempt of every agent task.
renderAncestorArtifactPathAuthoritylists its paths in the prompt. The contract and path placeholders render a sentence that tells the agent whichproducers[].outputsentries to use.Deleted:
assertPromptArtifactAuthorityUnchanged, its call sites, andpromptArtifactAuthoritySnapshotsByTaskprompt-artifact-authority.tspromptArtifactAuthoritySelectorsmanifest field andpromptArtifactAuthoritySelectorsForaddedAuthoritySelectorsrefresh refusal inprompt-refresh.tsThe agent-output gates (the semantic gates and the aggregation/report omission checks) are unchanged.
The stock prompts no longer call the index a "sealed JSON authority" or a "sealed selector".
final-report.mdno longer says the index leaves out unrelated patches, plans and bundles. It now says the index lists every admitted ancestor output and the agent should use only the named handoff paths.Where this departs from the #1234 scope
selectorsfield. The issue says "the file path and JSON shape stay the same". The file path, theproducers[].outputsstructure and the validator are unchanged, but the requiredselectorsfield is gone. After item 3, a runtime-rendered prompt has no selector data to put in it, and filling it in anyway would mean making up state.schema_versionstaysultrafuzz.prompt-artifact-authority.v1. Nothing in the runtime reads the file back (parsePromptArtifactAuthorityBytesis deleted), and the file is rewritten before every attempt, so I kept v1 rather than bump to v2. Maintainer: please confirm v1, or ask for a v2 bump.selectorId(and its SHA-256 self-check andpromptArtifactAuthorityPathSelectorId) is removed from the run plan'sancestor_artifact_path_authorityreference. Its only readers were the deleted manifest selectors and the refresh refusal. Run-plan v4 has not been released.controls/tasks.json. Its only reader was the deleted index read-back.Breaking changes
selectorsfield. It lists every declared output of the task's admitted ancestors.plan.jsonpath-authority references dropselectorId.smithers/tasks.json(stillultrafuzz.smithers.workflow.v4) dropspromptArtifactAuthoritySelectors.resumeno longer refuses an edited prompt that names a new authority.promptArtifactAuthoritySelectorsandselectorId, which this release's readers reject: those where a static prompt named an authority, which includes everydefaulttopology run. Finish such a run with the release that launched it. The CHANGELOG has the full entry.Tests
dynamic-lifecycle.test.ts: "a deferred join run from the execution snapshot gets a prompt input index listing its ancestors' outputs". It runs the execution snapshot's workflow in process, prepares a deferred join, runs its agent, and checks that the written index lists the generated child'sfindings.jsonand the planner'splan.json. It fails onunstable, where the file is never written.generated-workflow-verifier.test.ts: the index is rewritten for every attempt.prompt-artifact-authority.test.ts: derivation from the live plan, ordering, rejecting a directory outside the ancestor closure, and validation.runtime.test.ts: a refreshed prompt that names a new authority is applied.format:check,lint,lint:strict:ci,docs:check,typecheckandknippass.🤖 Generated with Claude Code
The PR should not merge until an agent’s edits to the task-local index are detected before another generation or an attempt is accepted.
Fix with agent prompt
Summary
The PR builds each agent’s prompt input index from the live, dynamically expanded task plan, writes it on attempts, and changes prompts and persisted references to use an unfiltered output index.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR Plan[Live expanded task plan] --> Closure[Declared ancestor outputs] Admission[Verified dependency admission] --> Filter[Admitted producers] Closure --> Filter Filter --> Index[Task-local JSON index] Index --> Agent[Agent and correction generations] Agent --> Result[Accepted attempt]Reviews (1) · Last reviewed commit: "Merge branch 'unstable' into refactor/12..."