feat(05-05): ?fixpair harness knob + pinned-corpus origin lookup (GREEN)
- route-profiles.ts: HARNESS-ONLY module — sentinel ROUTE_PROFILE_HARNESS_ONLY + getRouteProfileOrigin wrapping getPairOrigin; D-12 header (pinned PUBLIC origin only, no coordinate params) + BA5 exclusion note - index.html: ?fixpair=<pair-id> knob resolves the pair's pinned public origin as the mock's telegramFix (accuracy 12, heading null); unknown pairId keeps the default fix; knob documented in the profile comment - [Rule 1 - Bug] main.ts never passed bootMap's opts.onLoad — route sources never existed and setRouteDrawn's setData silently no-op'd (cards rendered, the line never painted; invisible to the RS fakes). Wiring added + RS2 boot-wiring gate pins it - [Rule 1 - Bug] consult raced the map load: harness session stubs resolve the location chain before WebGL/style init, so draw landed on missing sources and the post-load empty install discarded it (probe: ROUTE token fired before RAMS-BASEMAP-READY). Consult now awaits mapLayersReady, resolved in the load handler after installRouteLayers (never wedges — finally-resolved; cards-only boots resolve immediately) - Capture run: CAPTURE OK — 13/13; route line verified painted, alt swap renders ROUTE 2/2 +1 MIN VS ROUTE 1, pending card honest, dark PX-02 - npm test -w web: 76 passed (new RS2 boot-wiring gate included)
This commit is contained in:
+18
-2
@@ -9,7 +9,7 @@
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module">
|
||||
/* D-11 Mock-Telegram profile: ?mock=android|ios|tdesktop|macos|web + ?scheme=light|dark + ?pin=1 + ?locale=en|ms + ?fixdelay + ?sessiondelay + ?dest=<id>
|
||||
/* D-11 Mock-Telegram profile: ?mock=android|ios|tdesktop|macos|web + ?scheme=light|dark + ?pin=1 + ?locale=en|ms + ?fixdelay + ?sessiondelay + ?dest=<id> + ?fixpair=<pair-id>
|
||||
Harness-only: served from web/dist-harness, never deployed.
|
||||
The WHOLE block below is inside `if (__ARAHARAH_HARNESS__)` — Vite's `define`
|
||||
replaces that identifier with `false` in the production build, so the branch is
|
||||
@@ -38,6 +38,22 @@
|
||||
const sessionDelayMs = Number.parseInt(q.get("sessiondelay") || "0", 10);
|
||||
const fixDelayMs = Number.parseInt(q.get("fixdelay") || "0", 10);
|
||||
|
||||
/* The 05-05 route profile (?fixpair=<pair-id>): the mock's Telegram fix
|
||||
* becomes the PAIR'S PINNED PUBLIC ORIGIN, resolved from the committed
|
||||
* fixture corpus through the harness-only route-profiles module (D-12 —
|
||||
* the harness introduces no user coordinate; a ?lat/?lng knob is
|
||||
* deliberately absent). An unknown pairId keeps the default fix — the
|
||||
* boot must never wedge. The dynamic import lives inside this
|
||||
* __ARAHARAH_HARNESS__ branch, so production dead-code-eliminates the
|
||||
* module (BA5 greps web/dist for its sentinel). */
|
||||
const fixpair = q.get("fixpair");
|
||||
let telegramFix = { lat: 3.1513, lng: 101.6958, accuracyM: 12, headingDeg: null };
|
||||
if (fixpair) {
|
||||
const { getRouteProfileOrigin } = await import("/src/mock/route-profiles.ts");
|
||||
const origin = getRouteProfileOrigin(fixpair);
|
||||
if (origin) telegramFix = { lat: origin.lat, lng: origin.lng, accuracyM: 12, headingDeg: null };
|
||||
}
|
||||
|
||||
// Set BEFORE main.ts is imported — this is the marker main.ts's install guard checks.
|
||||
window.__ARAHARAH_MOCK__ = { profile, scheme, locale, dest };
|
||||
|
||||
@@ -73,7 +89,7 @@
|
||||
languageCode: locale,
|
||||
grantLocation: profile !== "tdesktop",
|
||||
deliveryDelayMs: fixDelayMs > 0 ? fixDelayMs : undefined,
|
||||
telegramFix: { lat: 3.1513, lng: 101.6958, accuracyM: 12, headingDeg: null },
|
||||
telegramFix,
|
||||
startParam: dest ?? undefined,
|
||||
});
|
||||
}
|
||||
|
||||
+35
-5
@@ -48,7 +48,7 @@ import { createFixtureRoutingSource } from "@araharah/routing/src/index.ts";
|
||||
import { createRouteController, type RouteState } from "./route/route.ts";
|
||||
import { buildRouteCard, buildRoutePendingCard } from "./route/route-card.ts";
|
||||
import { attachRouteEndpoints, removeRouteEndpoints } from "./route/route-markers.ts";
|
||||
import type { MapLike } from "./route/route-layers.ts";
|
||||
import { installRouteLayers, type MapLike } from "./route/route-layers.ts";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@@ -97,8 +97,37 @@ async function boot(): Promise<void> {
|
||||
mainEl.appendChild(mapEl);
|
||||
appRoot.appendChild(mainEl);
|
||||
|
||||
const map = bootMap(document.getElementById("map")!);
|
||||
if (!map) document.body.dataset.map = "absent"; // WebGL2 absent — the shell still renders
|
||||
// The route consult waits on this: the harness session stubs resolve the
|
||||
// location chain faster than WebGL/style init, so consult+setData could
|
||||
// land BEFORE the map's load event — the draw would hit sources that do
|
||||
// not exist yet and the empty post-load install would silently discard it
|
||||
// (found by the 05-05 capture legs: card rendered, line never painted).
|
||||
// Resolves in map.ts's ONE load handler, right after installRouteLayers;
|
||||
// a cards-only boot (map === null) resolves immediately. The Promise
|
||||
// executor runs synchronously, so the resolver is assigned before any
|
||||
// caller below reaches it.
|
||||
let resolveMapLayersReady!: () => void;
|
||||
const mapLayersReady: Promise<void> = new Promise((res) => {
|
||||
resolveMapLayersReady = res;
|
||||
});
|
||||
const map = bootMap(document.getElementById("map")!, {
|
||||
// The route layers install at map load, inside the ONE load handler
|
||||
// (after building-3d, before the ready log — map.ts hosts the hook).
|
||||
// 05-05 capture-leg fix: without this callback the sources never exist
|
||||
// and setRouteDrawn's setData silently no-ops — cards rendered, the
|
||||
// line never painted (the fakes in the RS gates could not see it).
|
||||
onLoad: (m) => {
|
||||
try {
|
||||
installRouteLayers(m as unknown as MapLike);
|
||||
} finally {
|
||||
resolveMapLayersReady(); // the consult never wedges, even on install failure
|
||||
}
|
||||
},
|
||||
});
|
||||
if (!map) {
|
||||
document.body.dataset.map = "absent"; // WebGL2 absent — the shell still renders
|
||||
resolveMapLayersReady(); // cards-only degradation: nothing to wait for
|
||||
}
|
||||
|
||||
let follow: ReturnType<typeof createFollowController> | null = null;
|
||||
const marker = createPositionMarker(); // created here, attached in the guarded block below
|
||||
@@ -426,10 +455,11 @@ async function boot(): Promise<void> {
|
||||
// Auto-consult (the state-coexistence law): the fixture RoutingSource is
|
||||
// consulted ONCE per boot, only when the location chain is live AND a
|
||||
// destination resolved — destination-unavailable and no-destination
|
||||
// boots never make a route request.
|
||||
// boots never make a route request. The consult itself waits for the
|
||||
// map's load (route sources installed) — see mapLayersReady above.
|
||||
if (destination !== null && !routeConsulted) {
|
||||
routeConsulted = true;
|
||||
void routeController.consult(fix, destination);
|
||||
void mapLayersReady.then(() => routeController.consult(fix, destination));
|
||||
}
|
||||
if (!tickStarted) startTick();
|
||||
// emitted UNCONDITIONALLY (outside the map guard above): a WebGL2-absent
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* route-profiles.ts — HARNESS-ONLY ?fixpair= origin lookup (05-05).
|
||||
* ----------------------------------------------------------------------------
|
||||
* D-12: the pinned origin is COMMITTED PUBLIC FIXTURE DATA. The pair corpus
|
||||
* is public-data routes between public places (RT2b proves every origin
|
||||
* equals a gazetteer landmark within 1e-4 deg), so the harness delivers no
|
||||
* user coordinate — it re-delivers a coordinate that is already committed
|
||||
* to the repo. The ?fixpair knob accepts a pairId ONLY; arbitrary lat/lng
|
||||
* query params are never read (T-05-14: a coordinate-parameter knob would
|
||||
* reintroduce the coordinate-in-URL surface the fixture world exists to
|
||||
* avoid).
|
||||
*
|
||||
* BA-EXCLUSION: this module is imported ONLY from the index.html harness
|
||||
* block, inside `if (__ARAHARAH_HARNESS__)` — Vite's define replaces that
|
||||
* identifier with `false` in the production build, so the dynamic import is
|
||||
* dead-code-eliminated and the module never ships. The
|
||||
* ROUTE_PROFILE_HARNESS_ONLY sentinel below joins BA5's token scan: if this
|
||||
* module ever leaks into web/dist, the build fails.
|
||||
*/
|
||||
import { getPairOrigin } from "@araharah/routing/src/index.ts";
|
||||
|
||||
/** BA5 sentinel — must NEVER appear in any web/dist artifact. */
|
||||
export const ROUTE_PROFILE_HARNESS_ONLY = true;
|
||||
|
||||
/** pairId -> the pair's pinned public origin, or null when the pairId is
|
||||
* unknown (the harness keeps its default fix — an unknown pairId must not
|
||||
* wedge the boot). Pure lookup over the committed corpus; the ONLY origin
|
||||
* source the harness can deliver. */
|
||||
export function getRouteProfileOrigin(pairId: string): { lat: number; lng: number } | null {
|
||||
return getPairOrigin(pairId);
|
||||
}
|
||||
@@ -142,6 +142,14 @@ test("RS2 map-law — zero setStyle in web/src/route; opts.onLoad sits after bui
|
||||
assert.ok(onLoad < ready, "the route install is BEFORE the RAMS-BASEMAP-READY log — sources exist when captures wait");
|
||||
});
|
||||
|
||||
test("RS2 boot wiring — main.ts passes opts.onLoad to bootMap installing the route layers (05-05 capture fix)", () => {
|
||||
const mainCode = codeText(path.join(SRC, "main.ts"));
|
||||
assert.ok(
|
||||
/bootMap\([\s\S]{0,400}?onLoad:\s*\(m\)\s*=>[\s\S]{0,200}?installRouteLayers\(/.test(mainCode),
|
||||
"main.ts must wire bootMap's opts.onLoad to installRouteLayers — without the callback the sources never exist and setRouteDrawn's setData silently no-ops (cards render, the line never paints; found by the 05-05 capture legs)",
|
||||
);
|
||||
});
|
||||
|
||||
/* ————— RS3 — fake-map layer law ————— */
|
||||
|
||||
test("RS3 fake-map layers — 2 sources + 4 layers in order, idempotent install, setData swaps, empty clears", () => {
|
||||
|
||||
Reference in New Issue
Block a user