Implements plan §3.1-§3.5 of the tier-aware embedded-orchestrator
integration behind ENABLE_ELEMENT_TOOLS_IN_ORCHESTRATOR env var. With
the flag unset (default production state) this change is a no-op —
every path added here short-circuits on !needsElementTools(profile).
§3.1 model-profiles.ts:
- needsElementTools(profile) — returns true iff env flag truthy AND
tier in {basic, standard}. Full tier stays OFF per A/B v1 Kimi K2.5
ceiling-effect finding (Δ M1 -12.5pp).
- 20 unit tests cover the 2×3 flag × tier matrix + truthy-value
allow-list parsing.
§3.2 orchestrator-sub-agent.ts:
- Pass hasMcpTools: needsElementTools(modelProfile) into
resolveSkills('generation', ...) so elements.md auto-loads for
gated models, matching the A/B v1 treatment arm.
§3.3 orchestrator-sub-agent.ts:
- When flag fires, append ELEMENT_TOOL_OUTPUT_FORMAT block to the
sub-agent system prompt. Verbatim from
scripts/ab-corpus/build-prompt.ts::T_TOOL_CALL_INSTRUCTIONS so
production reproduces the measured behavior (PRIMARY element-tool
call / FALLBACK batch_design wrapped in op_tool).
§3.4 design-parser.ts:
- tryParseElementToolOutput(raw) wraps pen-ai-skills parseModelOutput
and returns a tagged union {kind:'element-tool'|'batch-design-dsl'}
when <op_tool> is detected, or null to route back through the
legacy extractJsonFromResponse flow.
- 9 unit tests cover happy-path detection, <think> stripping,
multi-tag preference (element tool wins over scaffold batch_design),
legacy passthrough, and malformed-tag graceful fallback.
§3.5 orchestrator-sub-agent.ts:
- STUB: when streaming applied zero nodes AND the completed response
is element-tool-shape, return a clear error pointing at plan §3.5
as the Phase 2 work item. Apply-path dispatch (server-side pen-mcp
handler invocation, live://canvas merge) is deferred to avoid
shipping a path that's untested against the live-canvas sync
machinery.
Tests: 1863/1863 (was 1834; +20 profile tests + 9 parser tests).
Format and tsc clean. No behavior change with flag off.