feat(desktop): set the pool's limits from Settings #371

Merged
admin merged 1 commits from pool-limits-panel into dev 2026-10-17 12:01:36 +00:00
Owner

The desktop half of #369, on top of #370 (now merged).

A Pool limits card in Settings → This node, right after Location discovery, built the same way as that card: load and refresh on connect, dirty tracking, save, the same restart notice, and the same note when the daemon is too old to have the calls.

Fields

  • Jobs per member and Tasks per member: counts, 0 to 2147483647.
  • Longest task run: a number with seconds, minutes or hours, stored as seconds. A saved value is shown in the largest unit it divides into, and decimals are accepted when they come to whole seconds (1.5 h = 5400 s).
  • Storage per member and Upload per task attempt: a number with MB, GB or TB, in decimal units (1 MB = 1,000,000 bytes, which the card says). A saved value is shown exactly in the largest unit it fills.
  • Empty means no limit, and every field shows "No limit" as its placeholder. Each field has its own error, and Save stays disabled until the errors are fixed.
  • The daemon's own refusals, such as a value too large to store, appear in the error toast.
  • Byte and second arithmetic is done in BigInt, so values round-trip exactly up to the uint64 maximum.
  • If the node doesn't run the coordinator role, a note says the limits do nothing until it does. Saving still works.
  • When a saved value differs from the running one, the field shows "In effect until restart: …". That also explains a startup flag overriding a saved value.

Every client type, as asked on #369

  • Desktop-managed daemon: IPC node:get-pool-limits / node:save-pool-limits. The main process checks the input strictly, then calls GetPoolLimits / SetPoolLimits over gRPC with the node's token. There's no Electron-side settings file and no new launch flags, since the daemon applies saved limits itself.
  • Electron connected to a separately run node: the same IPC handlers, against that node's address and API token.
  • Paired browser: the generated Connect client over the same-origin session, so the token never reaches page code.
  • A daemon without the calls falls back to a read-only note.

Tests

  • npm run typecheck, npm test (95 pass, 6 of them new: precision with bigint and string input, strict IPC checks, parsing, exact form round trips, error-message cleanup, and the same keys in all seven locales) and npm run build all pass.
  • npm run test:e2e (29 pass). The new e2e flow saves limits to a separately managed daemon, checks a refusal in the card and a refusal from the daemon, restarts the daemon, confirms the values, units and cleared restart notice, then opens a fresh profile that reads the limits from the daemon.
  • The paired-browser route is covered server-side by web_test.go in #370. I also tried it by hand against a daemon serving the built UI: the largest storage value loaded exactly, a save landed, and an over-limit value showed the daemon's message.

i18n: every string in all seven locales. Layout was checked in English and Hebrew; the Hebrew units note was reworded so its equations read in order, and the Arabic note uses Arabic unit names.

Docs: a Pool limits paragraph in the desktop README, and one sentence in docs/desktop-backend.md.

One small change to existing code: supportsLocationDiscoverySettings in node-api.ts is renamed daemonImplementsCall, since both cards now use it; it behaves the same.

@excho0 this is your side of the app, so I'd especially value your eye on the card and the copy. Push to the branch anything you'd change.

The desktop half of #369, on top of #370 (now merged). A **Pool limits** card in Settings → This node, right after Location discovery, built the same way as that card: load and refresh on connect, dirty tracking, save, the same restart notice, and the same note when the daemon is too old to have the calls. **Fields** - **Jobs per member** and **Tasks per member**: counts, 0 to 2147483647. - **Longest task run**: a number with seconds, minutes or hours, stored as seconds. A saved value is shown in the largest unit it divides into, and decimals are accepted when they come to whole seconds (1.5 h = 5400 s). - **Storage per member** and **Upload per task attempt**: a number with MB, GB or TB, in decimal units (1 MB = 1,000,000 bytes, which the card says). A saved value is shown exactly in the largest unit it fills. - Empty means no limit, and every field shows "No limit" as its placeholder. Each field has its own error, and Save stays disabled until the errors are fixed. - The daemon's own refusals, such as a value too large to store, appear in the error toast. - Byte and second arithmetic is done in BigInt, so values round-trip exactly up to the uint64 maximum. - If the node doesn't run the coordinator role, a note says the limits do nothing until it does. Saving still works. - When a saved value differs from the running one, the field shows "In effect until restart: …". That also explains a startup flag overriding a saved value. **Every client type, as asked on #369** - **Desktop-managed daemon:** IPC `node:get-pool-limits` / `node:save-pool-limits`. The main process checks the input strictly, then calls `GetPoolLimits` / `SetPoolLimits` over gRPC with the node's token. There's no Electron-side settings file and no new launch flags, since the daemon applies saved limits itself. - **Electron connected to a separately run node:** the same IPC handlers, against that node's address and API token. - **Paired browser:** the generated Connect client over the same-origin session, so the token never reaches page code. - A daemon without the calls falls back to a read-only note. **Tests** - `npm run typecheck`, `npm test` (95 pass, 6 of them new: precision with bigint and string input, strict IPC checks, parsing, exact form round trips, error-message cleanup, and the same keys in all seven locales) and `npm run build` all pass. - `npm run test:e2e` (29 pass). The new e2e flow saves limits to a separately managed daemon, checks a refusal in the card and a refusal from the daemon, restarts the daemon, confirms the values, units and cleared restart notice, then opens a fresh profile that reads the limits from the daemon. - The paired-browser route is covered server-side by `web_test.go` in #370. I also tried it by hand against a daemon serving the built UI: the largest storage value loaded exactly, a save landed, and an over-limit value showed the daemon's message. **i18n**: every string in all seven locales. Layout was checked in English and Hebrew; the Hebrew units note was reworded so its equations read in order, and the Arabic note uses Arabic unit names. **Docs**: a Pool limits paragraph in the desktop README, and one sentence in `docs/desktop-backend.md`. One small change to existing code: `supportsLocationDiscoverySettings` in `node-api.ts` is renamed `daemonImplementsCall`, since both cards now use it; it behaves the same. @excho0 this is your side of the app, so I'd especially value your eye on the card and the copy. Push to the branch anything you'd change.
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Sisyphus/Sisyphus#371