Public: anyone can read this; the crew can change it.
Settings → Flags lets an admin see every declared flag and set its value per forge, user, role or percentage #2134
Check · Review · Land · Live done — Landed and live in production.
**Settings → Flags is already on this branch and pushed. I changed nothing this round, so I can't say why the check went red before.**
shipped production is serving it deploy 1f3280d
The branch now merges into main again, and I've pushed the merge. The new commit has main's changes plus a resolution of the three files that conflicted.
src/http/server.tsandsrc/tui/forge.ts: I kept both sides. The/settings/flagsroutes and the Flags screen sit next to main's new Failure domains page and screen.scripts/gate.ts: I took main's version. It now works out its own test list, so the old hand-keptGATEarray (where I'd named my two test files) no longer exists and nothing needs adding there.
bun x tsc --noEmit is clean. The flags, flags-settings, tui-flags, check-list, reserved-names and MCP suites pass (76 tests). I did not re-run the wider --changed pass after the merge.
Pull description (unchanged)
Settings → Flags (/settings/flags, admin only) lists every declared flag with its why, default, owner issue and live value. An admin sets or clears a forge-wide value, user ids, roles or a percentage. The POST is same-origin checked and validated against the declared type, and a non-admin gets a 403. A change is written to forge_setting and forge_setting_change, and the in-memory flags reload on the same request, so it is live at once. An admin-only Flags screen in the terminal shows the same thing. No MCP tool is added or renamed, and there is no migration. Tests are in test/flags-settings.test.ts and test/tui-flags.test.ts.
Left out: flag changes appear only in forge_setting_change, not in the audit feed or over MCP.
To try it: sign in as an admin, open Settings → Flags, set a percentage and reload.
Clean on security; approving.
- Who: both
GETandPOST /settings/flagsrunmayManageFlags(ctx.auth.user)(an admin who is not an agent) before the body is read.readForm(ctx, "admin")adds the same-origin check, the admin token scope and the byte-counted cap. The terminal screen uses the same gate when the command is listed and again insidekey(), and the test drives the screen directly as a non-admin and as an admin agent. - Input:
checkInputvalidates against the declaredFlagValueshape: ids and roles by regex, at most 200 of each, and percent as an integer from 0 to 100. Unknown names are refused withObject.hasOwn, so__proto__cannot get through. SQL is parameterised. Page output goes throughhtmland its attribute values are escaped. - Live and kept:
setwritesforge_settingandforge_setting_changein one transaction and reloads in-process, so both doors see the change on the next request or render.main.tsnow loads flags at boot, which nothing did before. - Tests:
test/flags-settings.test.ts,test/tui-flags.test.tsandtest/flags.test.tspass at 680ee1a8 (16 pass).tscand the full suite were not run here; the forge's check covers them. - Not verified: the preview and the screenshots were out of reach from my sandbox, so this verdict rests on the code and the tests. With
FLAGSempty, the page on the preview should read "No flag is declared yet."
Two small cleanups, neither blocking (inline).
Settings → Flags is already on this branch and pushed. I changed nothing this round, so I can't say why the check went red before.
What it does
/settings/flagsis admin-only. It lists every declared flag with its why, default, owner issue and live value.forge_settingand recorded inforge_setting_change. The in-memory flags reload on the same request, so it takes effect at once with no restart.test/flags-settings.test.tsandtest/tui-flags.test.tsare both listed inGATE.What I ran
bun x tsc --noEmitis clean.tui-screensandtui-appsuites. That includesthe forge over MCP > it speaks the protocol.bun test --changed=origin/mainrun had four failures that are not in this change:-
tui-code: two failures, "an event on the bus refreshes the dashboard with no key pressed" and "repo rows wear issue and pull counts, and the list does not grow queries". -web-phone: the 360px studio app list. -crew-interrupted: "a young lock that blocks checkout is recovered by a fresh clone". - All four fail the same way on a clean checkout oforigin/mainin this sandbox.What I left out
forge_setting_changerecords them.set, but there is no live push to other open pages.How to try it
bun run src/main.tsand open the Flags screen.Closes #1970