fix(platform): remove the Environment page and unenforced feature flags - #3203
Merged
Conversation
larryro
marked this pull request as ready for review
September 4, 2026 02:52
Settings > Environment told users their variables and secrets are "injected into all of your sandboxes". Nothing ever did: `resolveUserEnvForInjection` had one definition and no caller, and the only readers of `app.sandbox_user_env` were the page's own CRUD module, its three `/api/app/sandbox/user-env` routes, and the itest lane. No sandbox session, agent turn, automation script or run_code lane touched the rows. Remove the promise instead of building the feature: the route, feature folder, rail/menu entries, contract + adapter rows, the backend module, constants and routes, the locale keys in en/de/fr, the itest lane, the docs-screenshot seed step and the manual test-plan rows. The shared EnvVarListEditor stays (project secrets use it). The table stays too — applied schema is never dropped — with a deprecation note on the migration header.
… flags The feature_flags policy stored `webSearch`, `codeExecution` and `fileUpload` per rule, the editor rendered a toggle and a table column for each, and `GET /governance/my/feature-flags` echoed them — but no server or client code ever read them. The only enforced field is `maxContextTokens` (the chat turn's context cap), and `inputGuardrailsActive` rides the same wire. Remove the toggles, the columns, the three locale keys (en/de/fr) and the three fields from the resolved flags and the frontend contract; word the section for what it does (cap the context window). The schema keeps the three keys optional and `@deprecated` so policy files written by earlier releases keep parsing, and the editor drops them from a rule on its next save. Guards: the resolver ignores the keys, the dialog offers no toggle, a legacy rule saves without them, and the itest parses the flags wire strictly.
The personal Environment page is gone (nothing injected its variables), so the "Where this fits" paragraph no longer points at it — en, de, fr.
larryro
force-pushed
the
fix/remove-unbacked-settings-promises
branch
from
September 4, 2026 05:46
103f742 to
87a6c3b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Two settings surfaces promised behaviour no code delivered. Larry's call: remove the promise, do not build the feature.
A. Settings > Environment (per-user sandbox env/secrets). The page told users their variables are "injected into all of your sandboxes". Zero-consumer proof:
resolveUserEnvForInjectionhad exactly one definition and no call site; the only readers ofapp.sandbox_user_envwere the CRUD module (domains/sandbox/user-env.ts), its three routes (GET/POST/DELETE /api/app/sandbox/user-env), the Settings page, and the itest lane. No sandbox session, agent turn, automation script, orrun_codelane referenced the table, the module, or the constants (services/sandbox/src/config.ts#userEnvis an unrelated uid:gid parser). Verdict: removed.B. Governance feature flags
webSearch/codeExecution/fileUpload. Stored in thefeature_flagspolicy file, resolved byevaluateFeatureFlags, echoed byGET /governance/my/feature-flags— and read nowhere else. Server: the only consumer of the resolved flags isgetContextCapForUser, which readsmaxContextTokens. Client:useMyFeatureFlagshas zero callers and noapp/code reads.webSearch/.codeExecution/.fileUploadoutside the editor that rendered the toggles. Verdict: the three toggles removed;maxContextTokens(enforced in the chat budget) andinputGuardrailsActivekept.What changed
Removed (promise A)
app/routes/dashboard/$id/settings/environment.tsx(+ regeneratedrouteTree.gen.ts),features/settings/user-env/**,components/env/use-env-editor-controller.ts(only the removed page used it; the sharedEnvVarListEditorstays — project secrets use it).sandbox/user_env:*and their adapter rows inlib/backend/settings.ts.domains/sandbox/user-env.ts(incl. the never-calledresolveUserEnvForInjection),core/sandbox/user_env_constants.ts(+ test), the three/user-envroutes.en/de/fr:userEnv.*,metadata.environment.*,navigation.environment,settings.menu.environment.*.DEMO_ENV_VARS; manual test plan rows (F10, B8, route table).Removed (promise B)
feature-flags-editor.tsx;emptyRule()no longer seeds them; rules are persisted throughtoPersistedRule, so a rule still carrying the keys from an older policy file loses them on its next save.ResolvedFeatureFlagsis now{ maxContextTokens?: number };GET /governance/my/feature-flagsno longer echoes the three booleans; the frontend contract dropped them.governance.featureFlags.webSearch|codeExecution|fileUploadinen/de/fr; section copy reworded to what the section does (cap the context window).user-env/ feature-flag fields inbackend/rest/**or the spec) — nothing to change.Deprecated, not dropped
app.sandbox_user_env(migration 0050) stays; a comment-only deprecation note was added to the migration header (the runner tracks migrations by filename, no checksum). No migration ships.featureFlagRuleSchema.webSearch|codeExecution|fileUploadstay optional with@deprecatedJSDoc so existingfeature-flags.ymlfiles keep parsing; a schema test locks that.Guards added
feature_enforcement.test.ts: a rule carrying the deprecated toggles resolves to{}(no property reappears).feature-flags-editor.test.tsx: the rule dialog offers no toggle; a loaded rule with the deprecated keys is saved without them./my/feature-flagswire is parsed.strict()— the retired keys cannot come back unnoticed.Docs
docs/{en,de,fr}/platform/member/preferences.md— dropped the hand-off sentence to the Environment page.docs/align-ghost-feature-pages), NOT touched here — to be appended there:docs/{en,de,fr}/platform/member/environment.md: delete; add"platform/member/environment": "platform/member/preferences"todocs/redirects.json; remove the entry fromdocs/nav.json(member group); drop the three*:platform/member/environmententries fromservices/docs/app/content/frontmatter.json(or regenerate it).docs/{en,de,fr}/platform/member/overview.mdline 19: drop "and Environment variables & secrets, the keys and variables injected into the sandboxes you run" — the personal group is Account and Personalization (+ Notifications).docs/{en,de,fr}/platform/agents/harnesses.mdline 62: delete the sentence "Your own environment variables and secrets are set in the container too, which is how a personal token or endpoint reaches the work without anyone else's session seeing it." (nothing sets them).docs/{en,de,fr}/platform/admin/governance/policies-and-limits.mdlines 6 and 22: "feature controls toggle web search, code execution, and file upload by scope" → "feature controls cap the context window per scope"; line 22 → "Feature controls cap the max context tokens for AI replies per user, team, or role. There are no per-feature toggles."services/platform/tests/docs-screenshots/manifest.ts: remove thesettings-environmentshot (route no longer exists);services/docs/public/images/manifest.json: remove its entry; deleteservices/docs/public/images/platform/settings-environment.webp(the docs image-manifest test requires every image to be referenced by a page).Gates (observed)
bunx tsc --noEmit: 0 errors.bunx oxlint --type-aware: exit 0, no findings.server(governance, schemas, sandbox + governance domains, agent_secrets,lib/i18nincl. messages parity/usage + e2e-keys): 15 files, 244 tests green.lib/backend): 43 files, 439 tests green (run through a transient fs-allow wrapper because the worktree'snode_modulesis a symlink; wrapper deleted).bunx knip: exit 0 (one pre-existingcron-parserconfig hint, unrelated).bunx oxfmt --checkon every touched file: clean.@tale/docsvitest: 30 files, 194 tests green.backend:integrationon fresh throwaway tale-db + MinIO: base (origin/main078ab62) and this branch, each on its own fresh tale-db + MinIO with an identical env. Both runs stop at the same pre-existing[itest] fatal: SyntaxErrorincheckAutomations(the two-factor lane rotates the shared session and every later lane 401s — the open draft test(platform): make a truncated itest run fail loudly #3198 fixes the harness). Up to that point: base 175 PASS / 7 FAIL, branch 174 PASS / 7 FAIL — the one fewer PASS is exactly the removedsandbox user-env CRUDlane, and the 7 FAIL names are byte-identical on both trees (all downstream of the rotated session). Thegovernance core: policy write/read + guards + flags/budget/modelslane, which now parses the flags wire.strict(), PASSes on the branch. To prove the lanes the fatal hides, a verification-only worktree of this branch with test(platform): make a truncated itest run fail loudly #3198's two harness commits cherry-picked on top (applied cleanly; my strict flags parse and the removed lane both intact) ran on another fresh tale-db + MinIO: 390/390 checks passed across 119/119 lanes, exit 0, zero FAIL ([itest] 390/390 checks passed across 119/119 lanes). Nothing from that overlay is committed here.