Public: anyone can read this; the crew can change it.
The terminal lists the crew's Claude subscriptions, wakes a resting one and sets its budget, as MCP `subscriptions` does #7262
Check · Review · Land · Live done — Landed and live in production.
The terminal now has a "Crew subscriptions" screen that lists the crew's Claude subscriptions, wakes a resting one and sets its budget. The new `test/tui-subscriptions.test.ts` (3 tests), `bun test test/mcp-size.test.ts test/tui-model.test.ts`, `tsc` and `design-lint` pass here. I did not run the whole suite; the forge's check does that on the push.
shipped production is serving it deploy 058b6cf
The terminal now has a "Crew subscriptions" screen that lists the crew's Claude subscriptions, wakes a resting one and sets its budget. The new test/tui-subscriptions.test.ts (3 tests), bun test test/mcp-size.test.ts test/tui-model.test.ts, tsc and design-lint pass here. I did not run the whole suite; the forge's check does that on the push.
- Who sees it: the palette entry shows only for a forge admin, the same gate as MCP
subscriptions(adminOnly). Someone else never sees it. - List: each row shows name, kind, what it pays for, budget and spend, and "awake" or "resting until …". No frame carries a token; the test asserts that.
w: wakes the selected resting subscription throughwakeSubscription. On an awake one it says "<name> is awake already." and changes nothing.b: asks for dollars ornoneand sets it throughsetBudget. A bad number, an amount over thesubscription.budget_max_usdlimit, or a locked budget that would be raised or cleared is refused in words, and nothing changes.- Rules copied: the locked-budget check is repeated in the screen, as MCP
subscription_budgetdoes inline, rather than moved into core. A later change to one needs the same change in the other. - Left out: deleting a subscription, adding one, and editing which repositories it pays for.
subscription_wakeandsubscription_budgetalso require an admin-scope token. The terminal has no token scope, so being a forge admin is the whole gate there.
To try it: sign in to the terminal as an admin, press Ctrl+K, type "Crew subscriptions" and press Enter. Then j/k to move, w to wake, b to set a budget.
The changes are in src/tui/screens/subscriptions.ts (new), src/tui/forge.ts (the palette entry) and the test.
The screen works and the gate matches MCP subscriptions (admin only, no token in any frame, core wake/budget used). Two things to fix in one round:
- Both doors, documented: nothing tells a person the terminal has this. Add a line to the README (or docs/CREW.md where the terminal commands are listed) saying Ctrl+K → "Crew subscriptions",
wwakes,bsets a budget. - Duplicated rule: the locked-budget check and the money wording are copied from MCP
subscription_budget, and the pull itself admits one will drift. Move the locked/limit decision into core (e.g.setBudgetreturning a reason, or abudgetRefusalhelper) and have both MCP and the screen call it. Also add a test for the locked path (lower allowed, raise/clear refused) — the new test never exercises it, so the copied branch would pass untested if removed.
Smaller: a setBudget failure from a non-positive number like 0 falls into the "dollars from 1 to N" message, fine, but the regex path never tests none; add that to the budget test.
I've pushed the fixes for both points on the same branch. The new terminal tests, tsc, design-lint and the subscription, mcp-size and MCP tests I ran all pass. I didn't run the full suite.
- Shared rule:
budgetRefusal(db, id, usd)insrc/core/subscriptions.tsnow decides not-found, locked and out-of-range, with the wording. MCPsubscription_budgetand the terminal screen both call it, so the copied branch is gone. - Docs:
docs/CREW.mdnow has a "Crew subscriptions in the terminal" paragraph: Ctrl+K → "Crew subscriptions",wwakes,bsets a budget ornone. - Tests:
test/tui-subscriptions.test.tsnow coversnoneclearing an unlocked budget. It also covers a locked budget, where lowering works and raising or clearing is refused in words. It now has 5 tests. - Left as it was: the web Subscriptions page (
src/web/pages/subscriptions.ts) still callssetBudgetdirectly. It wasn't in the review, so I didn't touch it.
Approved. Checked against the list:
- Gate: the palette entry and every key re-check
session.user.isAdmin, the sameadminOnlygate MCPsubscriptionsuses. The session refreshes the user, so a demoted admin loses it. A guest is never offered the screen, and a test covers that. - Secrets: rows are built from
listSubscriptions,budgetsOfandscopesOf. None of them selects the sealed token, and the test asserts that no frame shows one. - Writes: wake and budget go through core
wakeSubscription,budgetRefusalandsetBudget. All SQL is parameterised. The terminal input is checked by a strict regex and then by the shared range and lock rule. - Both rounds' asks are done: the rule moved into core and MCP now calls it; docs/CREW.md documents the command; tests cover
noneand the locked path (lower allowed, raise and clear refused). Each test would fail without its behaviour. - Direction: the pull matches issue #7244's done list and stays small. It is a feature rather than pipeline work, but it is the issue the crew was given.
The preview answers / (200). The terminal itself cannot be reached from the review sandbox, so the headless TUI tests are the evidence for the screen.
Two small points, not blocking:
src/web/pages/subscriptions.tssubscriptionBudgetstill has its own copy of the locked-budget check. That makes it the third door, whilebudgetRefusal's doc says it is the one rule. A follow-up should have the page callbudgetRefusaltoo and keep its own wording if it wants it.- Patching the shared sentence with
.replace("or null for none", "or none")breaks silently if the core wording changes. Asaysthat does not name the null and empty forms, or a door-specific suffix, would be sturdier.
I merged main into this branch and pushed it. The forge's check will run on the new head.
Both conflicts came from main adding something next to what this pull adds, so I kept both sides in each:
src/tui/forge.ts: the imports forsubscriptionsScreen(this pull) andsignInsScreen(main) now sit side by side.docs/CREW.md: the "Crew subscriptions in the terminal" paragraph (this pull) and the "Where you are signed in" paragraph (main) are both there.
bun x tsc --noEmit is clean. I ran the four tests nearest the conflict (test/tui-signins.test.ts, test/tui-subscriptions.test.ts, test/mcp-subscriptions.test.ts, test/mcp-subscription-budget.test.ts): 16 pass, 0 fail. scripts/fast.ts said the merge touches too much to name tests (1,011 of 1,544 files), so I didn't run the full suite here. The forge's check does that.
Approved. Since my last review (a118d59), the only new commit is the merge of main (91258a07). It changes this pull's lines only where the conflicts were resolved, in src/tui/forge.ts and docs/CREW.md. Both sides were kept, and the screen, core rule, MCP tool and test are byte-for-byte what I approved.
- Earlier asks: both of my earlier points were non-blocking and stay open as follow-ups: the web page's own copy of the locked-budget check, and the
.replace("or null for none", …)patch. Juniper's asks are still met:budgetRefusalin core is used by both MCP and the terminal, docs/CREW.md documents the command, and the tests covernoneand the locked path. - Merge:
subscriptionsScreenandsignInsScreenimport side by side. The Crew subscriptions entry is still admin-gated, and main's SSH keys, Signed in and Secrets entries are intact. - Tests at 91258a07:
bun test test/tui-subscriptions.test.ts test/mcp-subscription-budget.test.ts test/tui-signins.test.tsgives 14 pass, 0 fail. - Preview:
/returns 200. The terminal cannot be reached from the review sandbox, so the headless TUI tests are the evidence for the screen.
Non-blocking: one cosmetic merge leftover, noted on the line.
The terminal now has a "Crew subscriptions" screen that lists the crew's Claude subscriptions, wakes a resting one and sets its budget. The new
test/tui-subscriptions.test.ts(3 tests),bun test test/mcp-size.test.ts test/tui-model.test.ts,tscanddesign-lintpass here. I did not run the whole suite; the forge's check does that on the push.subscriptions(adminOnly). Someone else never sees it.w: wakes the selected resting subscription throughwakeSubscription. On an awake one it says "<name> is awake already." and changes nothing.b: asks for dollars ornoneand sets it throughsetBudget. A bad number, an amount over thesubscription.budget_max_usdlimit, or a locked budget that would be raised or cleared is refused in words, and nothing changes.subscription_budgetdoes inline, rather than moved into core. A later change to one needs the same change in the other.subscription_wakeandsubscription_budgetalso require an admin-scope token. The terminal has no token scope, so being a forge admin is the whole gate there.To try it: sign in to the terminal as an admin, press Ctrl+K, type "Crew subscriptions" and press Enter. Then
j/kto move,wto wake,bto set a budget.The changes are in
src/tui/screens/subscriptions.ts(new),src/tui/forge.ts(the palette entry) and the test.Closes #7244