Journey event  ·  10 Sep 2026

Multiple devices per space, movable gear and plants, and airflow that responds to placement

Operator ask 2026-09-10: a tent or room can hold more than one fan/light; allow adding multiple devices; let the user move and place gear in the space and move plants around; placi

  state copied from the tracker, never improved

  event date 2026-09-10

Event date 2026-09-10
Version found
Version fixed
Expected kit firmware
Release file
SHA-256
Served count
Source https://app.notion.com/3d62b4cda370813fafe0c5e0d5544b79

Operator ask 2026-09-10: a tent or room can hold more than one fan/light; allow adding multiple devices; let the user move and place gear in the space and move plants around; placing gear changes the way air moves. FULL PLAN: docs/design/plan-spatial-layout-2026-09-10.md (commit fef023a). KEY FINDING: the rig’s physical layout is not data, it is source code. RigScene.tsx places every real object with 36 hand-written calls including literal offsets and rotations; a plant’s position is DERIVED FROM ITS POT NUMBER (PlantInstances: pot 1-4 -> probe_1..probe_4), so a plant cannot be anywhere its pot slot is not; airflow knew exactly four fans via the CFM_SPECS 4-entry literal. So ‘add a second fan’ and ‘move the fan’ are ONE change: turn the layout into data and render from it. NOT blocked: space_device already has PRIMARY KEY (space_id, device_id), so N devices per space is already allowed by the data model, and space.room_id already exists. The literals were the blocker, not the schema. === S1 + S2 DONE (commit bd3caf5) === A fan was a NAME restated across SIX parallel four-entry literals: FAN_PCT_ENTITIES, CFM_SPECS, _CAL_PREFIX_PLATE, CAL_PREFIX_DEVICE, FAN_CAL_TARGETS, and CAL_DUCT_CM_HELPER/_DEFAULT in compose_ops. Adding a fifth meant editing all six in step, each an opportunity to pair the wrong duty with the wrong duct — same shape as the m/s-stored-as-CFM bug, one level up. A fan is now one frozen FanInstance carrying side, space, hub entity, pct/cfm sensors, nameplate helper, calibration prefix, duct helper and duct default. The old names survive as projections, so nothing outside computed_ops changed, but there is exactly one place to add a fan. fan_instances(space_id) is the seam the next slice needs — loading rows from space_device instead of declaring them is now a swap, not a rewrite. CAL_DUCT_CM_HELPER/_DEFAULT deleted outright (only readers were computed_ops itself and one test; a projection would have been indirection for its own sake). MIGRATION PROOF, per the plan’s own rule (‘a test, not a hope’): brain/tests/test_fan_registry_migration.py snapshots every airflow entity from the PRE-migration code against a fully-populated fleet, with a distinct duty per fan (so a swapped pair cannot still pass) and one real curve (so both the measured and proxy branches are covered). All 18 entities — state, honesty, model, basis_honesty, nameplate_share_pct — byte-identical across the change. Full brain suite 638 pass. === STILL OPEN === S3 add/remove a device with the three tiers; S4 placement record + RigScene renders from data (36 literals to port); S5 placement editor; S6 plants placed like everything else; S7 air changes per hour; S8 flow axis + placement warnings; S9 exchange shadow. TWO FIRM POSITIONS IN THE PLAN: 1. A device the brain can PLACE is not necessarily one it can DRIVE. The hub has a fixed number of PWM/relay channels; others are switched (Zigbee/Tuya plug) or merely known (an unplugged oscillating fan). All three cost money and move air; only one has a control. Three tiers, never blurred — reuse the existing oos prop on EntityToggle. 2. Air gets a SHAPE, not a NUMBER. No CFD: a tent is a turbulent obstacle-filled box, a velocity field cannot be validated with one anemometer, and it would be exactly the kind of number that looks measured and is not. What IS defensible: air changes per hour (pure arithmetic from calibrated CFM and tent volume), a flow axis from intake to exhaust, and placement warnings that are pure geometry. ppfdField.ts is the template throughout. OPERATOR DECISIONS 2026-09-10: start at S1+S2 (done); S5 ships list editing AND twin drag together (makes S5 the largest slice — build the list first inside it). ALSO NOTED: a calibration belongs to a fan INSTANCE, not a position — move a fan to another port and its curve is invalid because the run changed. Placement change must invalidate or flag the curve.

All events  ·  DSC  ·  CannaLib  ·  Site  ·  Firmware releases

Empty rows are honest

A blank cell means the tracker has no value for it. Nothing on this page is filled in to look complete.