feat: real fire on the boards, the LA office as a twin, and a night sky worth reading
The world stops being a simulation of California and starts being California. **THE PROMOTION GATE WAS THE FIRST COMMIT, BEFORE ANY ORANGE PIXEL EXISTED.** On today's live store the SoCal board contains 22 incidents. Every one has NULL acreage and fifteen are nameless LA County dispatch numbers. Drawn naively that is 22 orange marks over Los Angeles on a day nothing is burning — in a frame that contains no other warm colour, so one glyph would be the most salient object on the board and twenty-two would spend its credibility permanently. `acres >= 10 AND contained < 80 AND type != 'RX' AND last_seen = max(last_seen)` returns 0 on SoCal, exactly 5 on California, 0 on the Bay — same body, same day, three correct answers. The empty board is a deliverable, not a fallback: it says "No active fire on this board — CAL FIRE and WFIGS, just now", states that 21 records were gated and why, lists the largest fires burning OUTSIDE the frame with distances, and counts the hot pixels it is deliberately not drawing. **The privacy leak is structurally impossible rather than carefully avoided.** cloud-1 serves a projection; the four home-relative columns never leave that box. `observations.threat` was the one that nearly got through — it is `(16/distance)^2 x log10(acres) x momentum x containment x wind-alignment`, so with acreage and containment public it inverts to a distance circle around a house and three fires give an intersection. A grep of the built bundle for distance_km, bearing_deg, threat, 7762 and the street name returns nothing. **Deliberately not used, and both would have produced a confident wrong answer:** the store's `air` table retains only the last parameter of each poll, so all 78 rows read "Good" while the live feed reports ozone 101 "Unhealthy for Sensitive Groups" — haze driven off it would clear the sky during a smoke event. And `weather` is written only inside the NWS alerts loop, so a quiet day stores no wind at all. Tera's own per-region NWS wind is already correct and already what the clouds drift on. Satellite detections are drawn as evidence and never as incidents. The permanent industrial heat source 4.7 km from the owner's house is flagged persistent and dropped, asserted by a test that first proves it is present in the fixture. MODIS integer confidence and VIIRS string confidence are branched on `sat`. **The LA office is a twin.** Its entire authored second storey — Model Loft, Model Bay, The Materials Room, 430 lines nobody had ever stood in — is reachable on foot: a walker crosses level-1 to level-2 in 73 fixed steps, floorY 0 to 5, verified against the real pack rather than a synthetic plan. Its two studio devices read real hardware through a field-allowlisted bridge: mute, volume and reachability only. Never level, because there is no passive level upstream and obtaining one would record a room with people in it. Never dB, because upstream is gainPct across four different native scales. The bridge refuses all writes. Fixed at its root: an anonymous visitor was getting permanently at-rest instruments backing off against a 401. The tier moves into `createDeviceSource`, so anon gets the living simulator three file headers already promised. **Item 8 is closed, not fixed, and the correction is the point.** The Bay Area "stutter" was GPU power management — the card sat at 500 MHz of 2725 through every run that reproduced it, 4096/2048/1024/256 shadow maps all render in 1.21-1.31 ms, and two consecutive runs over a byte-identical dist gave 33.4 then 16.7. The allowance is removed and the cell is back to 16.7. Geometry is the gate; frame time is advisory. Item 7 was re-scoped after measuring: 1,069,006 of the Bay Area's 2,265,056 triangles were the second submission of the same buildings into the shadow pass. Mobile now has its own triangle caps and bay-area mobile draws 1,266,096. Also: bridges and the freeway corridor light up at night as emission, not lights — 1,614 deck lamps and 18 tower heads on the Bay in two draw calls. The single change that made US-101 legible was moving its edge lines from the lit material to the unlit one: retroreflective paint, the argument the SFO night frame already makes. California went 21,991 lamps to 4,051, clustered at the 17 town districts, because a rural interurban corridor genuinely is unlit. Tests 1137 -> 1340, server 280. All ten budget cells pass on first attempt with no cap raised. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,7 +26,10 @@ import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
import {
|
||||
CANONICAL_CAPABILITIES,
|
||||
DEVICE_CAPABILITIES,
|
||||
DEVICE_RANGES,
|
||||
deviceKindOfAssetId,
|
||||
deviceRange,
|
||||
validateDeviceDeclaration,
|
||||
type DeviceDeclaration,
|
||||
type DeviceKind,
|
||||
@@ -81,28 +84,56 @@ describe("both studios declare the hardware the product promises", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it(`${pack.id} says its readings are simulated, in words`, () => {
|
||||
it(`${pack.id} says where every reading comes from, in words`, () => {
|
||||
for (const device of declarations) {
|
||||
// The library check — provenance, disclosure wording, capability
|
||||
// vocabulary — restated here against the shipped packs rather than
|
||||
// against a fixture, because it is the shipped packs that get edited.
|
||||
assert.deepEqual(validateDeviceDeclaration(device), [], device.id);
|
||||
assert.equal(device.provenance, "simulated");
|
||||
assert.match(device.disclosure, /simulat/i);
|
||||
assert.ok(
|
||||
device.provenance === "simulated" || device.provenance === "first-party-sensor",
|
||||
`${device.id} has provenance "${device.provenance}"`,
|
||||
);
|
||||
if (device.provenance === "simulated") {
|
||||
assert.match(device.disclosure, /simulat/i, device.id);
|
||||
return;
|
||||
}
|
||||
// A live device says the opposite, and then has to carry a *second*
|
||||
// sentence for the path where the readings are not live after all: an
|
||||
// anonymous visitor cannot read the device route, so they get the local
|
||||
// simulator running this declaration. Without it the panel would print
|
||||
// "live, LA Studio" under a number invented a millisecond ago.
|
||||
assert.doesNotMatch(device.disclosure, /simulat/i, device.id);
|
||||
assert.match(device.simulatedDisclosure ?? "", /simulat/i, device.id);
|
||||
}
|
||||
});
|
||||
|
||||
it(`${pack.id} describes each instrument with the canonical capabilities`, () => {
|
||||
it(`${pack.id} describes every instrument in a vocabulary the system knows`, () => {
|
||||
// Not style: the panel builds its controls by walking this array and the
|
||||
// arena's observation width is the sum of them, so two studios authored
|
||||
// months apart disagreeing about what a mic can do changes the shape of an
|
||||
// RL observation without anybody editing the arena.
|
||||
//
|
||||
// The list is no longer required to *equal* `CANONICAL_CAPABILITIES`,
|
||||
// because the studio instruments now differ from it deliberately — see
|
||||
// "the LA studio's two live instruments" below. What every device still
|
||||
// owes is that each capability it claims is one the whole system knows and
|
||||
// one its own kind could plausibly have.
|
||||
for (const device of declarations) {
|
||||
assert.deepEqual(
|
||||
[...device.capabilities],
|
||||
[...CANONICAL_CAPABILITIES[device.kind]],
|
||||
device.id,
|
||||
);
|
||||
assert.ok(device.capabilities.length > 0, device.id);
|
||||
assert.equal(new Set(device.capabilities).size, device.capabilities.length, device.id);
|
||||
for (const capability of device.capabilities) {
|
||||
assert.ok(
|
||||
DEVICE_CAPABILITIES.includes(capability),
|
||||
`${device.id} declares unknown capability "${capability}"`,
|
||||
);
|
||||
assert.ok(
|
||||
// `mute` is the one addition: canonical omits it on a speaker and
|
||||
// both real and simulated speakers plainly have one.
|
||||
capability === "mute" || CANONICAL_CAPABILITIES[device.kind].includes(capability),
|
||||
`${device.id} claims "${capability}", which a ${device.kind} does not have`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -151,8 +182,12 @@ describe("Plan resolves a device onto its anchor prop", () => {
|
||||
[
|
||||
"la-front-mic@lobby/front-01",
|
||||
"la-front-speaker@lobby/front-01",
|
||||
"la-studio-mic@press/media-01",
|
||||
"la-studio-speaker@press/media-02",
|
||||
// The two studio instruments carry the *upstream's* ids, because
|
||||
// `server/src/devices/firstParty.ts` binds a reading to a declaration by
|
||||
// id and nothing else. A prettier id here is a device that declares
|
||||
// itself live and is permanently unreachable.
|
||||
"mic-yeti@press/media-01",
|
||||
"speaker@press/media-02",
|
||||
],
|
||||
);
|
||||
});
|
||||
@@ -177,6 +212,117 @@ describe("Plan resolves a device onto its anchor prop", () => {
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* The LA studio is the first pack in this repo whose readings are not invented,
|
||||
* and every one of these assertions is a refusal rather than a feature.
|
||||
*
|
||||
* The upstream's own `GET /api/la-studio/state` reports, per microphone,
|
||||
* `{ muted, gainPct, gainRaw, reachable, error, checkedAt }` — no level. A level
|
||||
* needs `POST /levels`, which records one and a half to three seconds of audio
|
||||
* per microphone to measure it. And `gainPct` is a percentage of four different
|
||||
* native mixer travels, which is not any number of decibels. So this file pins
|
||||
* the two omissions, because they are the sort of thing somebody adds back in
|
||||
* good faith to make a room feel alive.
|
||||
*/
|
||||
describe("the LA studio's two live instruments", () => {
|
||||
const live = declarationsOf(MATEO_COURT).filter(
|
||||
(device) => device.provenance === "first-party-sensor",
|
||||
);
|
||||
|
||||
it("promotes exactly the two studio instruments, by their upstream ids", () => {
|
||||
assert.deepEqual(live.map((device) => device.id), ["mic-yeti", "speaker"]);
|
||||
// The other two are reception's, they are invented, and they stay that way.
|
||||
const simulated = declarationsOf(MATEO_COURT).filter(
|
||||
(device) => device.provenance === "simulated",
|
||||
);
|
||||
assert.deepEqual(simulated.map((device) => device.id), ["la-front-mic", "la-front-speaker"]);
|
||||
});
|
||||
|
||||
it("declares no level meter on a microphone in a room with people in it", () => {
|
||||
for (const device of live.filter((entry) => entry.kind === "mic")) {
|
||||
assert.ok(!device.capabilities.includes("level"), device.id);
|
||||
}
|
||||
});
|
||||
|
||||
it("states its gain in the unit the upstream actually speaks", () => {
|
||||
const mic = live.find((device) => device.kind === "mic");
|
||||
assert.ok(mic);
|
||||
// Fail-closed and checked on both sides: without a declared non-decibel
|
||||
// range the bridge deliberately emits no gain at all, because the global
|
||||
// default is decibels and 68 % of a Yeti's travel is not any number of them.
|
||||
assert.deepEqual(mic.ranges?.gain, { min: 0, max: 100, initial: 60, unit: "%" });
|
||||
assert.equal(deviceRange(mic, "gain").unit, "%");
|
||||
assert.notEqual(deviceRange(mic, "gain").unit, DEVICE_RANGES.gain.unit);
|
||||
});
|
||||
|
||||
it("names the real hardware in one sentence and the simulator in another", () => {
|
||||
for (const device of live) {
|
||||
assert.match(device.disclosure, /live/i, device.id);
|
||||
assert.match(device.disclosure, /LA Studio/, device.id);
|
||||
assert.doesNotMatch(device.disclosure, /simulat/i, device.id);
|
||||
assert.match(device.simulatedDisclosure ?? "", /simulat/i, device.id);
|
||||
// Neither sentence may become a coordinate. The upstream store is centred
|
||||
// on somebody's home and the words that describe it are not ours to
|
||||
// publish; "the LA Studio" is the endpoint's own name for itself.
|
||||
assert.doesNotMatch(device.disclosure, /bedroom|bed\b|asleep|address/i, device.id);
|
||||
assert.doesNotMatch(device.simulatedDisclosure ?? "", /bedroom|asleep/i, device.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* SF has no hardware anywhere, and the pack says so in the sentence a viewer
|
||||
* reads rather than by omission.
|
||||
*/
|
||||
describe("the SF studio stays simulated and says what it is", () => {
|
||||
const declarations = declarationsOf(LUMBRIDGE_HQ);
|
||||
|
||||
it("declares nothing live", () => {
|
||||
for (const device of declarations) assert.equal(device.provenance, "simulated");
|
||||
});
|
||||
|
||||
it("says out loud that it is a room nobody is standing in", () => {
|
||||
for (const device of declarations) {
|
||||
assert.match(device.disclosure, /simulat/i, device.id);
|
||||
assert.match(device.disclosure, /not a room anybody is standing in/i, device.id);
|
||||
assert.match(device.disclosure, /no hardware in San Francisco/i, device.id);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The two *studio* instruments in each pack describe themselves identically,
|
||||
* so a viewer comparing SF with LA is comparing where the numbers come from
|
||||
* and nothing else. `mateo-court`'s reception desk is not a studio and keeps
|
||||
* the canonical list — including the level meter its simulator is free to
|
||||
* invent, which is the contrast the disclosure sentences are there to explain.
|
||||
*/
|
||||
it("uses the same capability vocabulary as the studio that is real", () => {
|
||||
const shape = (devices: readonly DeviceDeclaration[]) =>
|
||||
devices
|
||||
.map((device) => `${device.kind}:${[...device.capabilities].sort().join("+")}`)
|
||||
.sort();
|
||||
const sf = shape(declarationsOf(LUMBRIDGE_HQ));
|
||||
const la = shape(
|
||||
declarationsOf(MATEO_COURT).filter((device) => device.provenance === "first-party-sensor"),
|
||||
);
|
||||
assert.deepEqual(sf, la);
|
||||
assert.deepEqual(sf, ["mic:gain+mute+power", "speaker:mute+playback+power+volume"]);
|
||||
});
|
||||
|
||||
it("states its gain in decibels, because a simulated preamp really is decibels", () => {
|
||||
const mic = declarationsOf(LUMBRIDGE_HQ).find((device) => device.kind === "mic");
|
||||
assert.ok(mic);
|
||||
assert.equal(deviceRange(mic, "gain").unit, DEVICE_RANGES.gain.unit);
|
||||
assert.deepEqual(mic.ranges?.gain, DEVICE_RANGES.gain);
|
||||
});
|
||||
|
||||
it("declares no level meter either, though nothing stops it inventing one", () => {
|
||||
for (const device of declarationsOf(LUMBRIDGE_HQ)) {
|
||||
assert.ok(!device.capabilities.includes("level"), device.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("a broken device costs one device", () => {
|
||||
const CASES: readonly [string, (device: DeviceDeclaration) => DeviceDeclaration, RegExp][] = [
|
||||
[
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* The handshake between Tera's office packs and the room cloud-1 actually
|
||||
* measures.
|
||||
*
|
||||
* `GET /api/la-studio/tera/seats` was built so that a pack could be diffed
|
||||
* against the real room, seat id by seat id. Nothing has ever diffed against it.
|
||||
* A handshake nobody shakes is a comment, and this file is the difference: from
|
||||
* here on, every seat the upstream publishes is either **resolved by a shipped
|
||||
* pack** or **explicitly listed as unshipped, with a reason**. There is no third
|
||||
* state, which is what makes silent drift impossible — the day somebody authors
|
||||
* the pack, the list empties itself; the day the upstream adds a seat, this
|
||||
* fails and somebody has to decide what it is.
|
||||
*
|
||||
* ### Why all three are currently unshipped, and why that is the right answer
|
||||
*
|
||||
* The room is 3.9624 m square. Mateo Court is a 36 x 26 m courtyard block: 936 m²
|
||||
* against 15.7 m², sixty times the floor area, with a lobby, a press room and a
|
||||
* robotics lab that do not exist there. The two are not the same place and
|
||||
* cannot be made into each other, which is why this round twinned the two studio
|
||||
* *devices* — where the reading is measured and the placement is authored, and
|
||||
* the disclosure says which is which — and did not twin the room.
|
||||
*
|
||||
* Authoring the room itself is a separate decision, and it is the owner's rather
|
||||
* than an engineer's: `bed-01` is not a figure of speech. That is recorded here
|
||||
* rather than in a ticket, because this is the file somebody reads on the day
|
||||
* they decide to build it.
|
||||
*/
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
import { Plan } from "../../interiors/plan.ts";
|
||||
import FRONTIER_VALLEY from "../../offices/frontier-valley.ts";
|
||||
import LUMBRIDGE_HQ from "../../offices/lumbridge-hq.ts";
|
||||
import MATEO_COURT from "../../offices/mateo-court.ts";
|
||||
import { LA_STUDIO_SEATS } from "./laStudioSeats.ts";
|
||||
|
||||
/** Every pack this build ships, at full depth. */
|
||||
const SHIPPED = [LUMBRIDGE_HQ, MATEO_COURT, FRONTIER_VALLEY].map(
|
||||
(pack) => new Plan(pack, { warn: false }),
|
||||
);
|
||||
|
||||
/**
|
||||
* Seats the upstream publishes that no shipped pack resolves, and why.
|
||||
*
|
||||
* Every entry here is a promise that somebody looked at it. Deleting one means
|
||||
* a pack now carries that seat; adding one means a new seat appeared upstream
|
||||
* and somebody decided, on purpose, not to ship it yet.
|
||||
*/
|
||||
const UNSHIPPED: Readonly<Record<string, string>> = {
|
||||
"desk-01":
|
||||
"the LA Studio's own desk. No shipped pack is that room — Mateo Court is a " +
|
||||
"936 m² courtyard block and the studio is 15.7 m² — so binding this id to " +
|
||||
"one of its desks would put a measured address on an invented piece of " +
|
||||
"furniture.",
|
||||
"bed-01":
|
||||
"the room has a bed in it, and it is somebody's. Authoring a walkable, " +
|
||||
"publicly reachable twin of it is the owner's decision and has not been " +
|
||||
"made. Nothing here should make it by accident.",
|
||||
"floor-01":
|
||||
"the standing mark in the middle of the same room. Unshipped for the same " +
|
||||
"reason as the other two: the room is not authored.",
|
||||
};
|
||||
|
||||
describe("the LA Studio seat handshake", () => {
|
||||
it("captured a body with the shape the upstream documents", () => {
|
||||
assert.equal(LA_STUDIO_SEATS.officeId, "la-studio");
|
||||
assert.ok(Number.isInteger(LA_STUDIO_SEATS.frameRev));
|
||||
assert.ok(LA_STUDIO_SEATS.seats.length > 0);
|
||||
// The slab is the strongest claim in the whole body and the reason a twin of
|
||||
// this room would be worth building: it was measured, not typed.
|
||||
assert.equal(LA_STUDIO_SEATS.slab.provenance, "measured");
|
||||
assert.ok(LA_STUDIO_SEATS.slab.widthM > 0 && LA_STUDIO_SEATS.slab.depthM > 0);
|
||||
assert.ok(!Number.isNaN(Date.parse(LA_STUDIO_SEATS.slab.measuredAt)));
|
||||
for (const seat of LA_STUDIO_SEATS.seats) {
|
||||
assert.ok(seat.id.length > 0);
|
||||
assert.ok(Number.isFinite(seat.position.x) && Number.isFinite(seat.position.z));
|
||||
assert.ok(seat.position.x >= 0 && seat.position.x <= LA_STUDIO_SEATS.slab.widthM, seat.id);
|
||||
assert.ok(seat.position.z >= 0 && seat.position.z <= LA_STUDIO_SEATS.slab.depthM, seat.id);
|
||||
assert.ok(seat.pose === "sit" || seat.pose === "stand", seat.id);
|
||||
}
|
||||
});
|
||||
|
||||
it("carries no coordinate, no address and nothing home-relative", () => {
|
||||
// The upstream store this room belongs to is centred on somebody's home, and
|
||||
// several of its columns invert to a distance from it. None of them is on
|
||||
// this endpoint and none may ever be captured into this repository.
|
||||
const text = JSON.stringify(LA_STUDIO_SEATS);
|
||||
for (const forbidden of [
|
||||
"lat", "lng", "longitude", "latitude", "bearing", "distanceKm", "distance_km",
|
||||
"threat", "address", "street",
|
||||
]) {
|
||||
assert.ok(!text.includes(forbidden), `the seat fixture carries "${forbidden}"`);
|
||||
}
|
||||
});
|
||||
|
||||
it("accounts for every upstream seat: shipped, or listed as unshipped", () => {
|
||||
for (const seat of LA_STUDIO_SEATS.seats) {
|
||||
const resolved = SHIPPED.some((plan) => plan.seat(seat.id) !== null);
|
||||
const excused = Object.prototype.hasOwnProperty.call(UNSHIPPED, seat.id);
|
||||
assert.ok(
|
||||
resolved || excused,
|
||||
`upstream publishes seat "${seat.id}" and no shipped pack resolves it, and ` +
|
||||
"nothing in UNSHIPPED says why. Author it, or write down the reason.",
|
||||
);
|
||||
assert.ok(
|
||||
!(resolved && excused),
|
||||
`seat "${seat.id}" is both shipped and listed as unshipped — delete the ` +
|
||||
"UNSHIPPED entry, it is now a lie.",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps the unshipped list honest in both directions", () => {
|
||||
const published = new Set(LA_STUDIO_SEATS.seats.map((seat) => seat.id));
|
||||
for (const [id, reason] of Object.entries(UNSHIPPED)) {
|
||||
assert.ok(
|
||||
published.has(id),
|
||||
`UNSHIPPED excuses "${id}", which the upstream no longer publishes`,
|
||||
);
|
||||
// A reason, not a placeholder. This is the field that stops the list
|
||||
// becoming a way of silencing the check.
|
||||
assert.ok(reason.length > 40, `UNSHIPPED["${id}"] does not say anything`);
|
||||
}
|
||||
});
|
||||
|
||||
it("does not let a shipped pack quietly reuse an upstream seat id", () => {
|
||||
// The inverse drift: a pack author picks `desk-01` for a desk in Mateo Court
|
||||
// and, the day a presence feed is wired up, the LA Studio's occupants appear
|
||||
// in a fictional lobby. Seat ids are what `Presence` binds on.
|
||||
for (const plan of SHIPPED) {
|
||||
for (const seat of LA_STUDIO_SEATS.seats) {
|
||||
const collision = plan.seat(seat.id);
|
||||
assert.ok(
|
||||
collision === null,
|
||||
`${plan.office.id} declares seat "${seat.id}", which is an LA Studio id`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* The LA Studio's seat handshake, captured.
|
||||
*
|
||||
* `GET /api/la-studio/tera/seats` on cloud-1 exists for exactly one purpose: to
|
||||
* let a Tera office pack be *diffed* against the room it claims to be a twin of.
|
||||
* It has existed for a while and nothing has ever diffed against it, which is
|
||||
* how a handshake becomes decoration. This is the response, captured on
|
||||
* 2026-08-22 at `frameRev` 2, so that the next person to author that pack finds
|
||||
* a test already waiting rather than a guess.
|
||||
*
|
||||
* ### It is a fixture, not a pack
|
||||
*
|
||||
* Nothing outside `src/test/` imports this and nothing should. It is not in the
|
||||
* bundle, it is not an `Office`, and it is deliberately not one: authoring the
|
||||
* room this describes is a decision about a private space that has not been
|
||||
* made — see `src/test/packs/laStudioHandshake.test.ts`, which is the whole
|
||||
* reason the file exists.
|
||||
*
|
||||
* ### What is in it, and what is deliberately not
|
||||
*
|
||||
* Three seats and a slab, in the room's own coordinates, with the origin at a
|
||||
* floor corner. There is no latitude, no longitude, no address, no bearing to
|
||||
* anywhere and nothing home-relative in it. The upstream also serves an
|
||||
* occupancy endpoint and a live camera frame; neither is captured here and
|
||||
* neither belongs in a repository.
|
||||
*
|
||||
* `slab.provenance` is the upstream's own word and it is the interesting field:
|
||||
* this room was **measured**, on the date it says, which is a stronger claim
|
||||
* than any shipped pack makes about its own dimensions and the reason a twin of
|
||||
* it would be worth building.
|
||||
*/
|
||||
|
||||
export interface LaStudioSeatFixture {
|
||||
id: string;
|
||||
position: { x: number; z: number };
|
||||
facing: number;
|
||||
pose: "sit" | "stand";
|
||||
}
|
||||
|
||||
export interface LaStudioSeatsBody {
|
||||
frameRev: number;
|
||||
officeId: string;
|
||||
slab: {
|
||||
widthM: number;
|
||||
depthM: number;
|
||||
ceilingM: number;
|
||||
provenance: string;
|
||||
measuredAt: string;
|
||||
};
|
||||
seats: LaStudioSeatFixture[];
|
||||
}
|
||||
|
||||
/** Captured verbatim from `GET /api/la-studio/tera/seats`, 2026-08-22. */
|
||||
export const LA_STUDIO_SEATS: LaStudioSeatsBody = {
|
||||
frameRev: 2,
|
||||
officeId: "la-studio",
|
||||
slab: {
|
||||
widthM: 3.9624,
|
||||
depthM: 3.9624,
|
||||
ceilingM: 2.44,
|
||||
provenance: "measured",
|
||||
measuredAt: "2026-08-21T21:57:05-07:00",
|
||||
},
|
||||
seats: [
|
||||
{ id: "desk-01", position: { x: 2.1, z: 0.8 }, facing: 0, pose: "sit" },
|
||||
{ id: "bed-01", position: { x: 3.55, z: 2.5 }, facing: 1.5707963267948966, pose: "sit" },
|
||||
{ id: "floor-01", position: { x: 1.9812, z: 1.9812 }, facing: 3.141592653589793, pose: "stand" },
|
||||
],
|
||||
};
|
||||
@@ -377,3 +377,130 @@ describe("the pinned ids survive a content pass", () => {
|
||||
]) assert.ok(plan.prop(id), `robot station anchor ${id} is gone`);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* The upper floor, and whether anybody can get to it.
|
||||
*
|
||||
* Level 2 of this pack — the Model Loft, the Model Bay, the Materials Room, two
|
||||
* desk banks, twenty-four props and six viewpoints — was authored and then
|
||||
* unreachable on foot for as long as it has existed, because the walk controller
|
||||
* refused any state on a storey other than the one it spawned on. `README.md`
|
||||
* carried a warning telling pack authors not to bother modelling a staircase.
|
||||
*
|
||||
* The engine change is tested in `walker.test.ts` and `officeWalker.test.ts`.
|
||||
* What is tested *here* is the thing those two cannot see: that this building's
|
||||
* way up actually resolves, and that both ends of it are places a walker can
|
||||
* reach on foot. A transition that resolves onto a landing nobody can walk to is
|
||||
* a staircase in a locked room, and every test in the engine would still pass.
|
||||
*/
|
||||
describe("mateo-court's way upstairs", () => {
|
||||
const stair = plan.transition("stair");
|
||||
|
||||
it("resolves, with no problems and both storeys under it", () => {
|
||||
assert.deepEqual(plan.problems.filter((p) => p.where.startsWith("transitions")), []);
|
||||
assert.ok(stair, "the pack authors no way between its two storeys");
|
||||
assert.equal(stair.kind, "stair");
|
||||
assert.equal(stair.lower.levelId, "level-1");
|
||||
assert.equal(stair.upper.levelId, "level-2");
|
||||
// Floor to floor, which is the storey height plus the timber between.
|
||||
assert.ok(Math.abs(stair.rise - 5) < 1e-9, `${stair.rise}`);
|
||||
});
|
||||
|
||||
it("is a dog-leg with a flat half landing, and the treads follow it", () => {
|
||||
assert.ok(stair);
|
||||
assert.equal(stair.path.length, 4);
|
||||
const [foot, turn, landing, head] = stair.path;
|
||||
assert.equal(foot!.y, 0);
|
||||
assert.equal(head!.y, 5);
|
||||
// The half landing is flat: two flights of equal rise with a level between.
|
||||
assert.equal(turn!.y, landing!.y);
|
||||
assert.ok(Math.abs(turn!.y - 2.5) < 1e-9, `${turn!.y}`);
|
||||
// The first flight runs east, the landing turns south, the second runs west
|
||||
// and arrives through the gap in the balustrade.
|
||||
assert.ok(turn!.x > foot!.x, "the first flight does not run east");
|
||||
assert.ok(landing!.z > turn!.z, "the half landing does not turn");
|
||||
assert.ok(head!.x < landing!.x, "the second flight does not come back west");
|
||||
});
|
||||
|
||||
it("arrives through the gap in the balustrade and nowhere else", () => {
|
||||
assert.ok(stair);
|
||||
// The two rail segments leave 1.2 m of nothing between them at z 9.8-11.0 on
|
||||
// the yard's west line, and the upper footprint covers it. That is what stops
|
||||
// a walker who reaches the loggia strolling off a 5 m drop onto pavers.
|
||||
assert.ok(stair.upper.bounds.minZ > 9.8, `${stair.upper.bounds.minZ}`);
|
||||
assert.ok(stair.upper.bounds.maxZ < 11, `${stair.upper.bounds.maxZ}`);
|
||||
assert.ok(stair.upper.bounds.minX < 9.6 && stair.upper.bounds.maxX > 9.6);
|
||||
// And standing there is what triggers it, from either side.
|
||||
assert.ok(plan.transitionAt("level-2", stair.upper.landing));
|
||||
assert.ok(plan.transitionAt("level-1", stair.lower.landing));
|
||||
// The middle of the courtyard is not a way anywhere.
|
||||
assert.equal(plan.transitionAt("level-1", { x: 20, z: 13 }), null);
|
||||
assert.equal(plan.transitionAt("level-2", { x: 3, z: 3 }), null);
|
||||
});
|
||||
|
||||
it("puts both landings somewhere a walker can actually stand", () => {
|
||||
assert.ok(stair);
|
||||
for (const end of [stair.lower, stair.upper]) {
|
||||
assert.equal(
|
||||
plan.blocked(end.levelId, end.landing, end.landing, 0.3),
|
||||
false,
|
||||
`${end.levelId} landing is inside a wall`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The check the engine cannot do for itself: a route.
|
||||
*
|
||||
* A flood fill on a 0.15 m grid with the walker's own radius, using the same
|
||||
* `Plan.blocked` the controller sweeps against. It is slow and it is worth it
|
||||
* — the failure it catches is a staircase behind a wall, which looks perfect
|
||||
* in every unit test and in every screenshot of the courtyard.
|
||||
*/
|
||||
it("can be walked to from the arrival spawn, and leads to the Model Loft", () => {
|
||||
assert.ok(stair);
|
||||
const spawn = MATEO_COURT.viewpoints[0]!;
|
||||
assert.equal(spawn.levelId, "level-1", "the walk spawn moved off the ground floor");
|
||||
assert.ok(reachable("level-1", spawn.focus.at, stair.lower.landing), "the stair foot");
|
||||
// And from the head of it, the room this whole exercise was for.
|
||||
assert.ok(reachable("level-2", stair.upper.landing, { x: 13, z: 3 }), "the Model Loft");
|
||||
assert.ok(reachable("level-2", stair.upper.landing, { x: 3.6, z: 14.4 }), "the Materials Room");
|
||||
});
|
||||
});
|
||||
|
||||
/** Flood fill over one storey at the default walker radius. */
|
||||
function reachable(
|
||||
levelId: string,
|
||||
from: { x: number; z: number },
|
||||
to: { x: number; z: number },
|
||||
): boolean {
|
||||
const level = plan.level(levelId);
|
||||
if (!level) return false;
|
||||
const step = 0.15;
|
||||
const radius = 0.3;
|
||||
const key = (x: number, z: number) => `${Math.round(x / step)},${Math.round(z / step)}`;
|
||||
const queue: { x: number; z: number }[] = [{ ...from }];
|
||||
const seen = new Set([key(from.x, from.z)]);
|
||||
let visited = 0;
|
||||
while (queue.length > 0) {
|
||||
const at = queue.shift()!;
|
||||
visited += 1;
|
||||
if (Math.hypot(at.x - to.x, at.z - to.z) < 0.35) return true;
|
||||
// A courtyard block is about 60,000 cells; this is a guard against a bug
|
||||
// here, not against the building.
|
||||
if (visited > 200_000) return false;
|
||||
for (const [dx, dz] of [[step, 0], [-step, 0], [0, step], [0, -step]] as const) {
|
||||
const next = { x: at.x + dx, z: at.z + dz };
|
||||
if (
|
||||
next.x < level.bounds.minX + radius || next.x > level.bounds.maxX - radius ||
|
||||
next.z < level.bounds.minZ + radius || next.z > level.bounds.maxZ - radius
|
||||
) continue;
|
||||
const k = key(next.x, next.z);
|
||||
if (seen.has(k)) continue;
|
||||
if (plan.blocked(levelId, at, next, radius)) continue;
|
||||
seen.add(k);
|
||||
queue.push(next);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user