Converts the ad-hoc executeToolPhase → runAssistantTurn recursion into an explicit while (stepNumber < effectiveCap) loop. A step is one stream-and- tool-execute iteration; the loop terminates on non-tool finish, step-cap hit, doom-loop, budget exhaustion, abort, or synthesis success. MAX_STEPS = 200 hard ceiling (4x old effective limit from budget). Per-agent steps: field in AGENTS.md frontmatter sets tighter caps (Refactorer: 5, Architect: 20, others: unset = bounded only by MAX_STEPS). Resolution: effectiveCap = Math.min(agent.steps ?? Infinity, MAX_STEPS). executeToolPhase no longer recurses — returns ToolPhaseResult struct (action: 'continue' | 'paused' | 'synthesis_done') so the caller decides whether to continue or break. steps: 0 handled as "no tool calls allowed" via runTextOnlyTurn (one text-only stream phase, tool calls ignored with warn log). Step-cap hits produce a sentinel summary (reuses cap_hit kind so CapHitSentinel.tsx renders without frontend changes; text distinguishes "Step limit reached" from "Tool budget exhausted"). Doom-loop check migrated to top of loop body — same predicate, same threshold (3), break instead of return. step_start parts are in the schema CHECK but not emitted as message_parts — writing before the stream phase creates a sequence-0 collision with partsFromAssistantMessage. Structured log line emitted instead. Adversarial review caught the collision pre-deploy. 332/332 server tests passing. No frontend changes. No schema changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
openspec
Per-batch documentation convention adopted v1.13.15-openspec.
Lift source: Fission-AI/OpenSpec directory layout. No CLI dependency — just the folder shape. Full OpenSpec lifecycle adoption is a future v1.14+ batch.
Layout
openspec/
changes/
<slug>/ # one folder per shipped or planned batch
proposal.md # Why + scope summary
tasks.md # implementation step list
design.md # architecture / data-model decisions (optional)
specs/ # reserved for future OpenSpec CLI adoption
archived/ # snapshots of pre-v1.13.15 batch docs
<original-filename>.md
specs/ # global specs, future v1.14+ use
Conventions
- Slugs are lowercase-hyphenated derived from the batch title
(e.g.
v1-13-10-per-tool-cost,file-attachments-v3-5). - Already-shipped pre-v1.13.15 batches live in
changes/archived/as single-file snapshots. They were not split into proposal/tasks because the work was already complete; archiving preserves git history. - New v1.13.15+ batches should land directly in
changes/<slug>/proposal.md(+ tasks.md, + design.md when applicable). proposal.mdcarries the "Why" and scope.tasks.mdis the action list (numbered or checkbox).design.mdis for non-trivial architectural decisions worth recording separately.- A canonical dispatch brief (matching the v1.13.9 / v1.13.10 format) is most naturally split as proposal.md (Where we are, Why this matters, rationale sections) + tasks.md (Scope items, Build + smoke) + design.md (Attribution model, Filtering, Canonical mapping).