Public: anyone can read this; the crew can change it.
The profit slider: one control per forge trading cost against responsiveness — placement, sleep windows, crew spend margin — with cost, revenue and the gap as board signal #513
Check · Review · Land · Live done — Landed and live in production.
torque/forge:torque/392-the-profit-slider-one-control-per-forge → main 1 commit fork
1 file
changed +85
−0
docs/PROFIT.md added +85
| @@ -0,0 +1,85 @@ | ||
| 1 | # The profit slider — cost against responsiveness | |
| 2 | ||
| 3 | Design for issue #392, the twin of the subscription governor (#341): the | |
| 4 | governor decides how much work runs now; the slider decides where it runs, | |
| 5 | how long it stays awake, and what it is charged. Each forge has one slider. | |
| 6 | It's built as linked issues; this page describes the design they share. | |
| 7 | ||
| 8 | The owner wants owned metal to run at full capacity, because it is paid for | |
| 9 | either way. Cloud takes the bursts. A forge charges for awake time, storage | |
| 10 | and crew spend, with a margin on the crew spend. | |
| 11 | ||
| 12 | ## The control | |
| 13 | ||
| 14 | `position` is an integer from 0 to 100, set once for the whole forge: | |
| 15 | ||
| 16 | - 0 is the cheapest setting: apps sleep early and nothing spills to cloud | |
| 17 | until owned metal is full. | |
| 18 | - 100 is the most responsive: apps stay warm and work spills to cloud early. | |
| 19 | ||
| 20 | `policyAt(position)` is a pure function that interpolates linearly between | |
| 21 | three anchor rows. Position 50 is today's behaviour exactly, so landing the | |
| 22 | slider changes nothing until someone moves it. | |
| 23 | ||
| 24 | | position | production sleeps after | preview sleeps after | idle pull stops after | cloud takes work above owned use | | |
| 25 | | -------- | ----------------------- | -------------------- | --------------------- | -------------------------------- | | |
| 26 | | 0 | 10 min | 5 min | 1 day | 100% (only when full) | | |
| 27 | | 50 | 24 h | 30 min | 3 days | 85% | | |
| 28 | | 100 | never | 24 h | never | 60% | | |
| 29 | ||
| 30 | The 50 row is the constants in `src/apps/runtime.ts` (`SLEEP_AFTER_MS`) and | |
| 31 | `FORGE_PREVIEW_IDLE_DAYS` in `src/main.ts`. "Never" counts as infinity in the | |
| 32 | interpolation, so any position above 50 interpolates toward a cap of 7 days | |
| 33 | instead. An app's `always_on: true` is the only override an app can set. | |
| 34 | ||
| 35 | The margin is a separate number that sits next to the slider and does not | |
| 36 | move with it. The margin sets price; the slider sets cost and | |
| 37 | responsiveness. The board's gap line shows whether the two together leave a | |
| 38 | profit. | |
| 39 | ||
| 40 | ## Units and formulas | |
| 41 | ||
| 42 | All money is in US dollars. Crew spend uses the list-price figure the usage | |
| 43 | ledger already records (`src/core/usage.ts`). | |
| 44 | ||
| 45 | - cost = owned box $/month, a fixed cost spread over the month | |
| 46 | + cloud awake-hours × cloud $/hour | |
| 47 | + GB-months × storage $/GB-month | |
| 48 | + crew `costUsd` | |
| 49 | - revenue = awake-hours × awake price | |
| 50 | + GB-months × storage price | |
| 51 | + crew `costUsd` × (1 + margin%) | |
| 52 | - gap = revenue − cost | |
| 53 | ||
| 54 | Rounding happens once, at the end, to the cent. Worked example: a $300 box, | |
| 55 | 40 h of cloud at $0.05, 12 GB-months at $0.02 and $57.10 of crew spend | |
| 56 | cost $359.34. | |
| 57 | ||
| 58 | ## The order (what waits for what) | |
| 59 | ||
| 60 | | issue | what | waits for | | |
| 61 | | ----- | ------------------------------------------------------------ | ---------- | | |
| 62 | | #504 | the position, `policyAt`, Settings → Economics, the API | — | | |
| 63 | | #505 | meter awake intervals and storage per app and environment | — | | |
| 64 | | #506 | sleep windows follow the slider, live | #504 | | |
| 65 | | #507 | cost model: owned amortised, cloud hourly, storage, crew | #504, #505 | | |
| 66 | | #508 | revenue statement with the margin, JSON/CSV/print | #507 | | |
| 67 | | #509 | placement policy: owned first, bursts spill | #504, #505 | | |
| 68 | | #510 | the board: cost, revenue and the gap, with a 30-day SVG | #507, #508 | | |
| 69 | | #511 | MCP `economics` read and its API twin | #510 | | |
| 70 | ||
| 71 | #391 (the `services` app kind) waits for #509. | |
| 72 | ||
| 73 | ## Choices | |
| 74 | ||
| 75 | - The slider is one control per forge, not one per app. The only per-app | |
| 76 | exception is `always_on`, and a `placement: metal|cloud` preference that | |
| 77 | the policy honours unless owned capacity is full. | |
| 78 | - Placement is decided here; where an app actually runs is up to the spawn | |
| 79 | seam's body (`src/spawn/placement.ts`, spike #339). If no cloud body is | |
| 80 | configured, the decision is always `owned`, and the board says that bursts | |
| 81 | queue. | |
| 82 | - The owned-node label (`plat.sh/site=vxrail` today) is a setting, not a | |
| 83 | constant. | |
| 84 | - Billing produces a statement, with no payment processor. A person can | |
| 85 | check every line by hand. |
new above
offline