diff --git a/apps/web/src/services/ai/__tests__/model-profiles-element-tools.test.ts b/apps/web/src/services/ai/__tests__/model-profiles-element-tools.test.ts index f2b988665..adb1280d7 100644 --- a/apps/web/src/services/ai/__tests__/model-profiles-element-tools.test.ts +++ b/apps/web/src/services/ai/__tests__/model-profiles-element-tools.test.ts @@ -8,7 +8,7 @@ import type { ModelProfile } from '../model-profiles'; * Contract (per plan §3.1 — openpencil-docs * superpowers/plans/2026-04-21-element-tools-orchestrator-integration.md): * needsElementTools(p) === true - * iff ENABLE_ELEMENT_TOOLS_IN_ORCHESTRATOR env var is truthy + * iff VITE_ENABLE_ELEMENT_TOOLS env var is truthy * AND p.tier is 'basic' or 'standard' * * Why the tier gate: A/B v1 showed ceiling regression on the one @@ -17,7 +17,7 @@ import type { ModelProfile } from '../model-profiles'; * by default even when the global flag is on. */ -const FLAG = 'ENABLE_ELEMENT_TOOLS_IN_ORCHESTRATOR'; +const FLAG = 'VITE_ENABLE_ELEMENT_TOOLS'; function profile(tier: ModelProfile['tier']): ModelProfile { return { match: '', tier, label: `Test ${tier}` }; diff --git a/apps/web/src/services/ai/model-profiles.ts b/apps/web/src/services/ai/model-profiles.ts index 816b876f0..987ccc5f4 100644 --- a/apps/web/src/services/ai/model-profiles.ts +++ b/apps/web/src/services/ai/model-profiles.ts @@ -110,13 +110,22 @@ export function needsSimplifiedPrompt(profile: ModelProfile): boolean { * unchanged until the flag is explicitly flipped. Any truthy value * (`"1"`, `"true"`, `"yes"`, case-insensitive) enables the feature. * + * Name MUST start with `VITE_` — the sub-agent that reads this helper + * runs inside the Vite browser bundle, and Vite's default `envPrefix` + * only exposes `VITE_`-prefixed variables to `import.meta.env`. A + * bare name (e.g. `ENABLE_ELEMENT_TOOLS_IN_ORCHESTRATOR`) would be + * inlined as `undefined` at build time for the client, silently + * defeating the rollout flip. Server-side `process.env` reads work + * fine with any name, so this dual-compatible name is an alias that + * functions on both sides of the SSR boundary. + * * Rollout per plan §4 (openpencil-docs * superpowers/plans/2026-04-21-element-tools-orchestrator-integration.md): * Phase 1: ship with flag off (scaffolding only, zero behavior change) * Phase 2: flip on for basic + standard tiers in a canary env * Phase 3: remove the flag once stable across a monitoring window */ -const ELEMENT_TOOLS_FLAG_ENV = 'ENABLE_ELEMENT_TOOLS_IN_ORCHESTRATOR'; +const ELEMENT_TOOLS_FLAG_ENV = 'VITE_ENABLE_ELEMENT_TOOLS'; /** * Read the element-tools feature flag from env in a way that's safe diff --git a/apps/web/src/services/ai/orchestrator-sub-agent.ts b/apps/web/src/services/ai/orchestrator-sub-agent.ts index e88359001..6be893ab1 100644 --- a/apps/web/src/services/ai/orchestrator-sub-agent.ts +++ b/apps/web/src/services/ai/orchestrator-sub-agent.ts @@ -363,7 +363,7 @@ async function executeSubAgent( const hasDesignMdContent = designMdContent.length > 0; // Feature-flagged gate for N-tool element-surface integration. - // Only fires when ENABLE_ELEMENT_TOOLS_IN_ORCHESTRATOR env var is + // Only fires when VITE_ENABLE_ELEMENT_TOOLS env var is // truthy AND the model is in the basic/standard tier; full-tier // models stay on the legacy path per A/B v1 ceiling-effect finding // (Kimi K2.5 Δ M1 -12.5pp). Default production state is off, so @@ -517,8 +517,8 @@ async function executeSubAgent( error: `Element-tool output detected (${detectedName}) but the ` + `apply pipeline is not yet wired into the embedded ` + - `orchestrator. Set ENABLE_ELEMENT_TOOLS_IN_ORCHESTRATOR=0 ` + - `to fall back to the legacy JSONL path, or wait for ` + + `orchestrator. Unset VITE_ENABLE_ELEMENT_TOOLS (or set ` + + `it to 0) to fall back to the legacy JSONL path, or wait for ` + `integration plan §3.5 (openpencil-docs ` + `superpowers/plans/2026-04-21-element-tools-orchestrator-integration.md).`, };