igneum/docs/plans/ember-heat.md
igneum-labs 1b912385ad ember-heat: the gate reader, the PC 1 four-hour runbook, the plan and the light and dark captures
tools/heat-gate.mjs reads the HEAT lines of an app log and passes a hold within 1 degree for 4 hours with the hash
following the slice; its self-test fires on a known hold, a drift, a hash through the rest, a short log, an empty log
and a log without readings, and sits on the one gate beside heat-region.test.mjs. docs/plans/ember-heat.md carries
the words, the loop, the sources, the per-tier table and the runbook for the project lead's desk (this lane never touches PC 1).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 0d5fc6d258dee4774ebe7ea760736c9f05026d06)
2026-10-07 10:50:23 +00:00

14 KiB
Raw Blame History

Ember Heat: heat mode, the region and price prompt, the cost-against-rent line

Mission item 7 (docs/analysis/mission/mission.md section 2.7; invent.md section 5; future.md sections 3, 4, 8 and 10). the project lead ordered it built on 7 October 2026, 10:1x UK. Branch ember-heat, worktree ../igneum-wt-ember-heat, based on the miner-ui-4 tip 5d9c55a2 in the 0.3.19 line. App only: no digest, no protocol, ships in any cut. The first-month lane (miner-ui-5) works beside it; this lane keeps to Ember, the Settings page and the Earnings cost line.

1. What a miner sees

Where What Words
First run, step 2 of 3 The region list, the price per kWh prefilled from the public table and editable, the restricted line for a listed region "Every £ figure in the app comes from the price you type here. Nothing is fetched."
A Russian region or China chosen The restricted line, the standing sentence first "Mining may be restricted where you are. You are responsible for checking. Mining is banned in the Moscow region from 15 August 2026 to 2032."
Settings > Electricity The same list and price, the source of the typical price "Typical: 26.32 p per kWh (Ofgem cap, 1 October to 31 December 2026). Type what you pay, in pence per kWh. Nothing is fetched."
Settings > Heat mode The switch, the set point, the schedule, a typed room reading, the live line "Holding 20.0 °C: room 19.6 °C from your reading 40 min ago. heating, 5 min to go. Heat 60% of the time."
Cards, the Ember strip The heat line "Heat mode: holding 20.0 °C · room 19.6 °C · heating 58% of the time · 448 W of heat"
Cards, each row's Ember line The duty and the watts "heat 58% · 448 W of heat", or "heat 58% · resting"
A resting card The row says why it shows 0 MH/s; the pill says Resting · heat mode; the big button keeps Stop mining "resting: the room is 20.1 °C, holding 20.0 °C (heat mode)"
Earnings The cost-against-rent row "0.064 p per MH/s-hour · your cost of hash at 26.32 p/kWh and 2.44 W per MH/s, about USD 0.0008. Rented hash: USD 0.0117 per MH/s-hour (RunPod community pods, 38 pods at list price, 1,748 MH/s for USD 20.44 an hour, 6 October 2026). Yours is 14x cheaper than rented hash."

The honest sentence on the switch, from invent.md section 5: "In heat mode your card is an electric heater that also earns; whether it beats your boiler depends on the network and the price, shown live."

2. Heat mode: the loop (app/igneum-app/src/heat.rs, engine.rs tick_heat)

Part Rule
Period 10 minutes: the heating slice first, the rest after it. The hash follows: the miners run in the slice and stop for the rest.
Duty Decided once per period on the reading at its start: duty = 0.5 per degree under the set point plus the integral; the integral moves 0.05 per degree per period and carries the steady share the room needs (starts at a half). Clamped 0 to 1.
Temperature source, in order A typed reading (the miner's own thermometer, Settings > Heat mode > Room now), fresh for two hours. Else the coolest card's sensor after 3 minutes of rest, minus the idle offset (8 degrees until learned; typing a reading while the cards rest teaches the real offset), with the cooling tail still in the sensor taken off by its slope (one 60 s time constant, approximate). Else nothing: the loop heats 70% of every period and says "no room reading yet".
Card-sensor periods The duty is capped at 70% so every period keeps the 3 minutes of rest the reading needs. A typed reading lifts the cap.
Schedule "06:00 20, 22:00 16": each entry holds until the next; before the first the last of the day holds; times in the window's clock (the offset is saved with the schedule). Blank = one set point all day.
Set point 5 to 30 degrees. A typed room reading is -20 to 50.
Rest mechanics heat_rest: the miners stop the way a remote job stops them (stop_miners, the rows say why, nothing restarts them); the release re-arms every slot with a fresh pack check. A tune never starts during a rest and a running tune aborts into it. Paused mining, a remote job holding the cards, or no enabled card: no rest is taken.
Log `HEAT t= phase=heating
State state.heat (phase, set point, room and its source and error, duty this period and over the last hour, watts now and when heating, seconds to the next change, the one line).
Chain Nothing. A heat-mode miner is a miner with a schedule (invent.md section 5); a thermostat that ramps over an hour is not a 10 percent removal (CLAUDE.md, the vote-weight rule).

Model result (the tests in heat.rs, a 400 kJ/K room losing 20 W/K to 10 degrees outside, a 300 W card): with a typed reading refreshed every half hour the room stays within 1 degree of 20 for the four hours after the first, the hash on exactly while the slice heats, the integral settling near the two-thirds share the room needs; with the card sensor alone, the same hold, the estimate inside its stated 3 degrees. A model is not the gate; section 6 is.

3. The region and price prompt (ui/app.js View.REGIONS, the first-run step, Settings > Electricity)

Rule Where it comes from
The typical prices: UK 26.32 p (Ofgem cap Q4 2026), Ireland 40.42, Germany 38.69, Belgium 34.99, EU average 28.96, Hungary 10.82, Malta 12.82, Bulgaria 13.55 (euro cents, Eurostat H2 2025), US 17.7 c (EIA H2 2025), Kazakhstan about 4 c, Paraguay 4.4 c (4.4 to 6), Iran about 1 c, Ethiopia 2 c (2 to 5.3), Nigeria 4.8 c, the last five secondary and labelled future.md 4.1
A typed price, never fetched: the table prefills, the miner edits, the engine stores the number; no network call for a price or a rate mission.md 2.7
The restricted entries, and only these: Russia, Moscow region (15 August 2026 to 2032); Russia, Irkutsk, Buryatia, Zabaikalsky (seasonal bans); Russia, other region (ten regions, 1 January 2025 to 15 March 2031; the app says it does not carry the list of ten); China (illegal, the joint Notice of 6 February 2026) future.md 4.4 and 8.3 item 7
The sentence, verbatim in substance: "Mining may be restricted where you are. You are responsible for checking." future.md 8.3 item 7
Regions the file calls "nothing" for a home miner (Norway, Sweden, Kazakhstan, Paraguay, Iran, US states) carry no line future.md 4.4
The currency follows the region: £ and pence, € and euro cents, $ and US cents (every £ figure on every page) this plan
The dollar rates used only to compare with the rent: USD 1.32 per pound, 1.08 per euro, the rates future.md 3.4 implies; approximate, dated 7 October 2026 future.md 3.4

The test ui/heat-region.test.mjs checks the restricted list against the research file when the file is in the tree (it is internal, outside the public export list, so a public tree skips that one test).

4. The cost-against-rent line (Earnings; View.rentLine, View.RENT)

cost per MH/s-hour = watts per MH/s / 1000 × price per kWh, in the region's minor unit; the live draw and rate of the mining cards, else the last tune's point ("the last tune"), else nothing. The rent is the bench log's measured USD 0.0117 per MH/s-hour (RunPod community pods, 6 October 2026); the test reads docs/bench-log.md and fails when the app's number drifts from the log's latest "Rental cost of hash" entry. The verdict: "Yours is Nx cheaper than rented hash", "Rented hash undercuts your card by Nx", or "Level with rented hash".

Per tier, at 26.32 p: a 5090 at 2.4 W per MH/s is 0.064 p (USD 0.0008), 14x under the rent; a 3.27 uJ card is USD 0.00114, 10x (future.md 3.4's row); an 8 or 12 GB card at 1.3 to 1.7x the 5090's uJ is 6 to 8x; the line flips the day idle datacentre hash on 5 c power sets the rent at USD 0.00016 (future.md 3.2 and 3.4), and the Earnings page shows it the morning it happens. Apple silicon reports no draw, so its line says "no reading yet" until a tune measures it.

5. Per tier

Tier Heat mode The prompt and the rent line
8 GB card at 150 W a 150 W heater for a small room; the card sensor path works (NVIDIA and AMD report a temperature) the dearest hash of the home tiers; the rent line shows it first
12 and 16 GB cards 160 to 220 W; the same the same
24 and 32 GB cards (5090 at 290 to 575 W) a large-room heater; the typed reading is the better source at full duty 10 to 14x under the rent at UK prices
A rig a space heater that should not be in a bedroom; the whole rig heats and rests together (one room) the rig's cost is the card's cost
A pool user the duty shows as a share-rate pattern the pool sees; nothing else changes the same
Windows, Linux the same the same
macOS, Apple silicon 21 to 38 W is not a heater; no power reading, so the heat line says "no draw reading"; the mode still works on time no cost line until a tune measures the draw
NVIDIA, AMD the sensor is nvidia-smi or igneum-gpu-telemetry, read while the cards rest the same

6. The gate: the PC 1 four-hour hold, a runbook for the project lead's desk

This lane never touches PC 1 (CLAUDE.md: a job never quits, pauses or restarts the installed app). The gate is run by hand at the desk, on the installed app once a cut carrying this branch is on PC 1. Expected time: 4 h 20 min of waiting, 5 minutes of doing.

  1. Put a room thermometer on the desk, out of the PC's exhaust. Note the room temperature.
  2. In Igneum Miner: Settings > Electricity: region United Kingdom, the price as billed. Settings > Heat mode: Hold at a set point 1 degree above the room now (so the loop has something to do), schedule blank, Save. Room now: type the thermometer's reading, Use it. Switch "Hold a room temperature" on. The line under it reads "Holding N °C: room M °C from your reading just now. heating, 10 min to go." or close.
  3. Every 30 minutes for 4 hours: read the thermometer and type it into Room now, Use it (a fresh reading keeps the typed source for the whole run). Note the Cards strip line once an hour: the share of the hour and the watts.
  4. After 4 hours: Settings > Logs > Copy the log, paste it into a file, and run from the main checkout: node tools/heat-gate.mjs <that file> --hours 4 PASS = the room stayed within 1 degree of the set point on every sample, the hash was on exactly while the slice heated (90 s forgiven after each change), and the measured heating share matched the planned duty within 15 points. The tool's self-test (--self-test, in the pre-push gate) fires on a known hold and on a known drift, a hash through the rest, a short log, an empty log and a log without readings.
  5. Switch heat mode off. The cards go back to mining whenever the node is synced.

What a FAIL means and what to do: a drift over 1 degree with a typed source is a gain problem (KP, KI in heat.rs; the model room is not the desk) and comes back here with the log; a hash on through the rest is a rest that did not hold (the log's heat: release lines say why); a missing room reading is a typed reading older than two hours.

The card-sensor path (no typed reading) is the second run, same steps without step 3; its gate is the same tool with the stated 3 degrees as the honest expectation of the estimate, and the band stays 1 degree on the thermometer.

7. Files

File What
app/igneum-app/src/heat.rs the loop, the room estimate, the schedule, the log line, the words; 8 tests with a model room
app/igneum-app/src/engine.rs tick_heat, heat_rest, heat_release, Cmd::Heat, Cmd::Region, the holds in tick_miners, tick_sweep and sweep_drive
app/igneum-app/src/config.rs, state.rs the settings (region, heat_*) and state.heat
app/igneum-app/src/server.rs POST /api/region {region, price_pence}, POST /api/heat {on, set_c, schedule, tz_min, room_c}
app/igneum-app/ui/index.html, app.js, app.css the step, the two Settings cards, the strip line, the row words, the rent row, `?theme=light
app/igneum-app/ui/heat-region.test.mjs the view tests (the Russian entry, the bench-log rate, the words)
tools/heat-gate.mjs the gate reader and its self-test
tools/ui-mock/server.mjs the heat and heat-rest scenarios
tools/ci/pre-push.sh the new test file and the gate reader's self-test on the one gate

8. Screenshots (the mock on port 4319, scenarios heat and heat-rest, the Mac host's snapshot mode)

docs/plans/ember-heat-shots/: h01-cards-dark-1200.png, h02-cards-light-1200.png (the strip's heat line, the rows' duty and watts), h03-cards-resting-dark-1200.png (the rest: the pill, the rows, the strip), h04-settings-dark-1200.png, h05-settings-light-1200.png (Electricity and Heat mode), h06-earnings-dark-1200.png, h07-earnings-light-1200.png (the cost-against-rent row under miner-ui-5's IGN-first rows; retaken after the rebase onto miner-ui-5 e9fe1106), h08-region-step-dark-1200.png, h09-region-step-light-1200.png (step 2 of 3 with the Moscow region chosen and its restricted line).

9. Owed

What Who
The PC 1 four-hour hold (section 6), on the first cut carrying this branch the project lead, at the desk
The card-sensor path's idle offset and cooling tail on a real 5090 and a 9070 XT (the model's 8 degrees and 60 s are approximate) the first run's log
A curtailment input (a webhook, a schedule, a price threshold; future.md 4.3) a later lane
The 2025/422 sustainability indicators (future.md 8.3 item 2) the site, not the app