Question
csi-layout-drift built a recalibration-cadence prior on a synthetic corridor with an invented furniture-displacement schedule. Does its shape survive contact with the room the fleet actually measured?
Its own review panel wrote the task. The red-team's ask, verbatim: "replay the same displacement schedule against a real furniture-moved CSI capture and show the shape survives." It proposed a public dataset as the minimum cross-check. We can do better than the minimum, because the capture is first-party and the room is the one in the geometry.
What we already know
The earlier result, honestly. On test-lab-synth-floor-0, occupancy error moved
on exactly one arm — losin, furniture slid into deep line-of-sight blockage,
0.29 → 0.93 persons. A 27 dB amplitude swing on the perpendicular-exit arm moved the
error not at all. And T*(ε=0.5) came out [0.25, 1.5, 0.25] m across three placement
seeds. The clean deliverable that campaign wanted did not survive its own corpus, and
the spread was the finding.
The substrate has changed completely.
| Then | Now |
|---|---|
test-lab-synth-floor-0.json, 6,383 bytes |
fiit-ground-0.json, 86,559 bytes, LiDAR-native |
| 4 synthetic sensors on a corridor | fiit-ground-fleet — 10 surveyed nodes, anchor fit 0.259 m RMS |
| invented displacement schedule | 4 dated FURNITURE arrangements, each from a scan |
| no real anchor | EXP-F2, 86 sessions over the same weeks, same room, same nodes |
The geometry forces a prediction, and it is the opposite of the one this brief
first carried. The morning draft read one arrangement — lidar-tables-2026-08-28,
sixteen tables at 59–75 cm against a 90 cm antenna plane — and concluded that
furniture in this room cannot reach the sensing plane. That generalised one tables-only
scan to the whole floor. The floor says otherwise:
| Arrangement | Pieces | Reach ≥ 90 cm | Height range (cm) | Role here |
|---|---|---|---|---|
lidar-tables-2026-08-28 |
16 | 0 | 59.3 – 75.2 | negative control |
lidar-silent-2026-08-19 |
31 | 13 | 74.6 – 190.0 | drift state |
lidar-open-transfer-2026-08-21 |
98 | 34 | 41.3 – 186.5 | drift state |
catalog-open-fill-2026-08-21 |
15 | 15 | 90.0 – 90.0 | excluded |
62 of 160 pieces reach the plane. Seats sit at 86–95 cm, cabinets at 131 and 135, a counter at 114, a wardrobe at 165, shelves at 182–186.
So the prediction is ordering, not flatness: the per-link amplitude change should
rank lidar-tables < lidar-silent < lidar-open-transfer, tracking how much of each
arrangement intersects 90 cm. This is a better test than the flat one, because the
negative control lives inside the same room and the same run set, and because a flat
result now refutes rather than confirms.
catalog-open-fill-2026-08-21 is excluded: fifteen pieces at exactly 90.0 cm is a
catalogue default sitting on the antenna plane, and it would return the biggest effect
in the set for the weakest reason.
What the pilot changed before any grid ran
Three findings, each costing about fifteen seconds of Metal-lane compute.
The briefed occupancy was below the instrument's resolution. At n_agents=4 every
one of 36 links.parquet rows carried n_occluders = 0, and four independent
placements produced the same channel to five decimals. Four people do not cross nine
specific lines through a 15 × 18 m room. The base is now n_agents ∈ {0, 16, 32}, and
a degeneracy gate checks occluder incidence before the grid rather than after.
mean_atten_db_per_person is unusable on this link set — +15.68, +95.72 and +15.13
dB/person at 8, 32 and 64 agents. Non-monotone, and 96 dB per person is not physics.
The metric regresses amplitude on occluder count across nine links whose path loss
spans 15.8 dB, so geometry swamps occlusion. The estimand is the paired per-link change.
Arrangement selection did not exist on this path. stage_scene built its bundle
with no arrangement, so from_floor staged the union of all four active arrangements
— 160 pieces, four overlapping scans of one room at once. And the opt-in was
unreachable regardless: SceneOverrides.furniture was typed list[dict] | None, so
the string from_floor that scene_stage.py has accepted since IP-113 failed
validation first. Both fixed; staging now returns 16 / 31 / 98 / 160 as expected.
The four-way outcome table
| Simulated | Measured (EXP-F2) | Reading |
|---|---|---|
| unordered / flat | flat | Layout is not the drift axis in this room. ble-periodic-calibration needs a different mechanism, and that is a substantive negative result. |
| flat | drifts | The drift is not geometric. The standing alternative is thermal — fleet_ops joins node temperature on (host, ts), and ImpairmentProfile has no thermal term. This is the most interesting cell. |
| steep | flat | The sim over-codes furniture the antennas cannot see. Scene-staging defect, reported as such. |
| steep | drifts | Agreement, but check it is not agreement by coincidence — the sim must have predicted the magnitude, not just the sign. |
Three of the four cells are reportable. One is an instrument bug. None of them is "the campaign worked", and the brief is written that way on purpose.
What the supervisor should plan
Step 0 — the corpus, then the cost. lake sync --apply first (63 sealed segments
not ingested, 593 awaiting decode, 1,232 decoded from superseded containers). Then the
pilot run, because fiit-ground-0 is an order of magnitude larger than the toy
corridor and Sionna is CPU-only on this host. Cost before grid.
Step 1 — resolve the arrangements. Export each FURNITURE arrangement to a
scene_overrides.furniture list — the schema takes an explicit list of
{id, xy, size_m, height_m, material}, not an arrangement name, so this is real work.
gis_placements(kind="furniture", arrangement=...) supplies position, height and
material. While exporting, settle the sequence-versus-partition question:
lidar-silent-2026-08-19 (31) and lidar-open-transfer-2026-08-21 (98) name different
zones, so at least part of the set is a partition. Order the states by what the export
shows, not by their filename dates.
Step 2 — the simulated arm. Four states x four seeds, occupancy held fixed so
furniture is the only thing that moves. Emit drift_curve.parquet with both analytic
estimators, and report T*(ε) as a spread.
Step 3 — declare, then read. Write down the flatness threshold. Only then compute the measured per-link amplitude change across EXP-F2 over the same window. Recompute session-level ratios from summed counts — a mean of per-segment ratios weights a two-minute segment like a six-hour one.
Step 4 — if the measured arm drifts. Do not stop at "drift exists". Join
fleet_ops on (host, ts) and report whether the drift is temperature-ordered. That
single query is the difference between a placeholder filled and a placeholder filled
with the right sentence.
Out of scope
- Answering RQ4 alone. This pairs a prediction with a measurement on one room. A cadence claim needs the second site the hypothesis defeater names.
- Running before c-csi-impairment-fit. The sim's amplitude scale is a literature prior until that campaign closes. Run this one anyway if time is short, but say so in the synthesis rather than quoting dB as if calibrated.
- Occupancy accuracy. There is no labelled occupancy in this corpus. The estimand is amplitude change, not count error, and the analytic estimator's occupancy error is descriptive only.
- Delivery, contention, channel loading. Sionna RT has no MAC, and the 22 August ladder showed those are what actually govern per-receiver spread.