Skip to content

Stage B: decouple large file bodies from the plan JSON#4

Closed
Anmolnoor wants to merge 1 commit into
stage-a/truncation-hardeningfrom
stage-b/decoupled-writes
Closed

Stage B: decouple large file bodies from the plan JSON#4
Anmolnoor wants to merge 1 commit into
stage-a/truncation-hardeningfrom
stage-b/decoupled-writes

Conversation

@Anmolnoor
Copy link
Copy Markdown
Owner

Context

The actual fix for the truncation crash. Root cause: file.write actions inlined the entire file body in the schema-constrained plan JSON, so a large write overflowed the structured-output budget and truncated mid-string, killing the turn.

This separates the control plane (the plan) from the data plane (file bytes) — the pattern production agents use.

Note: based on stage-a/truncation-hardening so the diff shows only Stage B. Merge #3 first; GitHub will retarget this to main automatically.

What changed

  • foundation.file.write accepts content XOR content_brief. content (literal) for tiny files; content_brief (a short description) for anything longer. New FileWriteBriefRequest model; planner validation enforces exactly-one.
  • Orchestrator materializes deferred writes before execution: for each write carrying content_brief, it makes a separate ProviderResponseFormat.TEXT call (unconstrained, honoring max_output_tokens from Stage A) to generate the body, then swaps content_brief → content. The plan JSON stays tiny, so grammar-constrained decoding never has to emit a huge string.
  • Graceful degradation: if body generation fails, the brief is left in place so the write becomes a normal failed action (the executor's strict model rejects the unknown field) — not a turn-killer.
  • Planner guidance: prompt + schema outline tell the model to use content_brief for anything beyond a few short lines.

Tests

2 new (387 total, ruff clean): brief materialized via a TEXT call and written verbatim while the plan call never carries the body; generation failure degrades to a failed action with no file written.

Sequencing

Stage B of A→B→C→D. A+B together fix the crash. C (user questions) and D (out-of-scope escalation) are the questioning feature, still to come.

🤖 Generated with Claude Code

The crash root cause: file.write actions inlined the entire file body in
the schema-constrained plan JSON, so a large write overflowed the
structured-output budget and truncated mid-string, killing the turn.

Separate the control plane (the plan) from the data plane (file bytes):

- foundation.file.write now accepts EITHER content (literal, for tiny files)
  OR content_brief (a short description). New FileWriteBriefRequest model;
  planner validation enforces the content-xor-content_brief convention.
- The orchestrator materializes deferred writes before execution: for each
  write carrying content_brief, it makes a separate ProviderResponseFormat.TEXT
  call (unconstrained, honoring max_output_tokens) to generate the body, then
  swaps content_brief -> content. The plan JSON stays tiny.
- If body generation fails, the brief is left in place so the write degrades
  to a normal failed action (the executor rejects the unknown field) rather
  than killing the turn.
- Planner prompt + schema outline instruct the model to use content_brief for
  anything beyond a few short lines.

Tests: brief materialized via a TEXT call and written verbatim while the plan
call never carries the body; generation failure degrades to a failed action
with no file written.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Anmolnoor
Copy link
Copy Markdown
Owner Author

Merged into main via commit 27c97b0 (stages A–G landed as one merge). See the summary on #9.

@Anmolnoor Anmolnoor closed this May 27, 2026
@Anmolnoor Anmolnoor deleted the stage-b/decoupled-writes branch May 30, 2026 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant