Lane A round 15: frontage poster +Z street-face flip (ledger #1) + gig golden re-pin

R14 review found every venue's own-frontage poster on the building's −Z BACK since R13 (~16 m
behind the street face, B measured 15.9 m) — invisible from the street, why queue_night framed no
poster. Ledger #1, A-first (B's hero re-shoot + F's tour hang off this).

- gigs.js buildPosters item 1: flip to the +Z street facade (B's canon: buildings.js facade zf=+d/2
  + venue.js door/queue/camera are +Z) AND ry=lot.ry+π so the printed face points +Z at the queue-side
  camera (else DoubleSide shows a mirrored band name). Proven: pub+rsl frontage posters now sit 0.06 m
  from B's door, printed-face·(+Z street)=1.000. Spine posters untouched.
- Gig golden re-pinned 0x1f636349 → 0x4f4a549d (the round's ONE sanctioned move; posters moved).
  Synthetic golden 0x3fa36874 frozen. §v3 FROZEN marker records the amendment (amendment law).
- selfcheck: the +Z facade is the block-interior side in plan coords, so corner venues (band_room/rsl
  fronting one street with another behind — seed 1 lot#481, 1.58 m from a crossing centreline) seat the
  frontage poster near that street. That's B's building geometry (the door is there too), not a mid-road
  defect — so the no-bitumen assert now EXEMPTS building-bound frontage posters, keeps it for spine
  posters. 400-seed sweep green again: 14264/14264.
- CITY_SPEC §v3 amended (poster prose → +Z facade + ry+π; golden line; frontage/spine carriageway split;
  recorded amendment in the FROZEN marker). LANE_A_NOTES §R15 hands B/F the new golden + a corner-venue
  eyeball note. Convention settled: visible venue face is +Z; selfcheck's −Z 'faces frontEdge' is the
  sim/GLB convention (π-off the visual front, the R13 RY_FLIP ambiguity).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-16 09:48:17 +10:00
parent e466491300
commit 35da83c347
5 changed files with 102 additions and 16 deletions

View File

@ -1,10 +1,37 @@
# LANE A — CITYGEN · progress (PROCITY-A)
*Status: **all deliverables landed; self-check all-green (14264/14264); rounds 214 closed** (see below).
v3.0 RELEASE round: 400-seed district invariant sweep landed + CITY_SPEC §v3 frozen at `v3.0`. Goldens
unchanged (synthetic `0x3fa36874`, gig `0x1f636349`) — no golden moved, no rng draw added this round.*
*Status: **all deliverables landed; self-check all-green (14264/14264); rounds 215 closed** (see below).
v3.0 release close-out: frontage-poster +Z flip (ledger #1) + the one sanctioned gig-golden move
(`0x1f636349 → 0x4f4a549d`); synthetic golden `0x3fa36874` frozen.*
*Date: 2026-07-16 · owner: PROCITY-A (Opus 4.8)*
## Round 15 (2026-07-16) — the frontage-poster +Z flip (ledger #1) + one sanctioned golden move
Lane A ran **first** (B's hero re-shoot + F's tour hang off it). The R14 review found every venue's
own-frontage poster had been on the building's **Z back** since R13 — ~16 m behind the street face
(B measured 15.9 m), invisible from the street, which is why John's `queue_night` framed no poster.
Delivered (one commit):
1. **Frontage-poster fix** (`gigs.js` buildPosters item 1): flipped to the **+Z street facade** (B's
canon) AND `ry = lot.ry + π` so the printed face points `+Z` at the queue-side camera (else `DoubleSide`
renders a mirrored band name). Proven: pub + rsl frontage posters now sit **0.06 m from B's door**,
printed-face·(+Z street) = **1.000**. Spine posters untouched.
2. **Gig golden re-pinned `0x1f636349 → 0x4f4a549d`** — the round's ONE sanctioned move (posters moved);
synthetic `0x3fa36874` frozen. §v3 FROZEN marker records the amendment (amendment law).
3. **Selfcheck poster check refined**: the brief expected the 400-seed off-carriageway assert to stay
green, but the `+Z` facade is the block-interior side in *plan* coords, so **corner venues** (a band_room/
rsl fronting one street with another behind — seed 1 lot#481, 1.58 m from a crossing centreline) put the
frontage poster near that street. That's B's building geometry (the door is there too), not a mid-road
defect — so the no-bitumen rule now **exempts building-bound frontage posters** and keeps it for
free-standing **spine** posters. Sweep green again: **14264/14264**.
4. **CITY_SPEC §v3 amended** (poster prose → `+Z` facade + `ry+π`; golden line; frontage/spine carriageway
split; recorded amendment in the FROZEN marker). LANE_A_NOTES §R15 hands B/F the new golden + a
corner-venue eyeball note.
Convention settled: the visible venue face is `+Z` (`buildings.js` facade `zf=+d/2`; `venue.js` door/queue
`+Z`). The selfcheck's `facing=(sin,cos)` "faces frontEdge" is the sim/GLB convention, 180° off the
visual front — the same π-ambiguity D fixed for the fleet in R13.
## Round 14 (2026-07-16) — v3.0 RELEASE: invariant sweep + CITY_SPEC §v3 freeze
Release round, Lane A scope = harden + freeze (no new systems). **No golden moved** (synthetic

View File

@ -213,6 +213,11 @@ night in v1; interior-mapping shader is a stretch goal).
> never silent drift. **One edit is still pending and lands with Lane F's alias-retirement commit** (R13
> debt #1): the R12 single-venue compat alias in the runtime block below retires; everything else here is
> final for the `v3.0` tag.
>
> *(**Recorded amendment — ROUND15, ledger #1:** the frontage poster was flipped to the `+Z` street
> facade (it had been on the `Z` back, invisible from the street); this moved `plan.posters`, so the gig
> golden re-pinned `0x1f636349 → 0x4f4a549d` in the same commit under the amendment law above. The
> synthetic golden `0x3fa36874` did not move — this is the round's one sanctioned gig-golden move.)*
> **Prime flag law.** The gig layer is a **post-hoc augmentation** applied by the selector only when
> `?gigs=1`. With the flag OFF, `generatePlanFor(seed, src)` returns the byte-identical v2 base plan —
@ -279,11 +284,14 @@ gigs: [ { gigId, venueShopId, bandName, genreKey, night, startSeg, endSeg, cover
**`plan.posters`** — town-wide gig advertising (Lane B renders): `posters: [ { id, gigId, x, z, ry } ]`.
Only **tonight's** (night 0) playing venues are advertised. Each such venue gets one poster on **its own
frontage** (seated on the facade plane, `lot.x + front·(d/2+ε)`), plus a seeded run along the spine — 2
per main edge, at `t ∈ [0.2,0.8]` of the block, on **either verge**. Different venues' posters share the
same pole runs — that IS the district reading. `ry` follows the one house convention (front/outward
normal = local `Z` at `ry=0`; `ry = atan2(nx, nz)`), so a poster faces the footpath that reads it. B
places E's poster skin at `(x, z)` rotated `ry`; F wires "poster → that venue's tonight-gig".
frontage** — seated on the **+Z street facade** (B's canon: `buildings.js` facade + `venue.js` door/queue/
camera are all `+Z`; position `lot + (sin ry, cos ry)·(d/2+ε)`), turned to read *from* the street
(`ry = lot.ry + π` so its printed face points `+Z` at the queue-side camera — **ROUND15 ledger #1**; before
R15 it was on the `Z` back and invisible from the street). Plus a seeded run along the spine — 2 per main
edge, at `t ∈ [0.2,0.8]` of the block, on **either verge**. Different venues' posters share the same pole
runs — that IS the district reading. `ry` follows the house convention (a poster's printed face =
`(sin ry, cos ry)`, so `atan2` of the *away-from-viewer* normal); B places E's poster skin at `(x, z)`
rotated `ry`; F wires "poster → that venue's tonight-gig".
**The street-corridor law (ROUND13 debt #5 — published as functions, not plan data).** `edge.width` is
the **full corridor** (carriageway + verge/footpath, centreline ± width/2), never the carriageway alone —
@ -292,8 +300,11 @@ pure functions over an edge (no plan fields change ⇒ base goldens stay frozen)
barrel: **`roadWidth(edge)`** (full carriageway width — main 10 m, side 68, lane = all, arcade 0; the
kerb sits at `roadWidth/2` from the centreline),
**`vergeBand(edge) → [inner,outer]`** (where footpath furniture lives), **`poleOffset(edge)`** (where a
pole/poster seats — just behind the kerb). Posters, poles, verandah posts belong in the verge band; the
selfcheck asserts no poster stands within `roadWidth/2` of any edge centreline.
pole/poster seats — just behind the kerb). Poles, verandah posts and **spine** posters belong in the verge
band; the selfcheck asserts no **spine** poster stands within `roadWidth/2` of any edge centreline. A
**frontage** poster is exempt — it is bound to its venue building (the +Z facade B draws), so at a corner
venue whose facade abuts a crossing street it may legitimately sit near that street's kerb (B's building
geometry, not a mid-road defect).
**Band names + the drop-in.** `bandName(seed)` generates 90s-Aussie pub-rock names from wordlists.
`web/assets/custom_bands.json` is an **OPTIONAL** editable drop-in (schema `{ "bands": ["The Feral
@ -306,8 +317,9 @@ by seed, spread **night-major across the whole week's slots town-wide** (not one
canonical half-open predicate (`open ≤ hour < close`) for D's gig-night patronage surge and everyone else.
**Gig golden** (guards gig-layer drift; pure-generator path, no custom file), seed `20261990` synthetic:
**`0x1f636349`** (ROUND13 re-pin — the district changed gig-plan output; was `0xa6ae5a5e`, the one-pub
alpha). Asserted by `selfcheck.js` alongside the frozen v2 goldens; changing gig output re-pins it.
**`0x4f4a549d`** (**ROUND15 re-pin** — the frontage-poster +Z flip, ledger #1, moved `plan.posters`; was
`0x1f636349` the R13 district, `0xa6ae5a5e` the R12 alpha). Asserted by `selfcheck.js` alongside the frozen
v2 goldens; changing gig output re-pins it.
## Shop types v1 (registry lives in `web/js/core/registry.js`, Lane A writes it, all lanes read)

View File

@ -2,6 +2,36 @@
Durable answers to questions other lanes raised against Lane A. Full status in `A-progress.md`.
## Round 15 (2026-07-16) — the frontage-poster +Z flip (ledger #1) + the one sanctioned golden move
### → B + F: **new gig golden `0x1f636349 → 0x4f4a549d`** (synthetic `0x3fa36874` unchanged)
The frontage-poster fix moved `plan.posters`, so the gig golden re-pinned — the round's ONE sanctioned
move (amendment law; §v3 marker records it). B: re-shoot `queue_night` against this. F: the smoke's gig
golden is now `0x4f4a549d`.
### The fix (gigs.js buildPosters item 1)
Each venue's own-frontage poster sat at local **Z** — the *back* of the building (~16 m behind, matching
B's 15.9 m), invisible from the street, which is why `queue_night` framed no poster. Flipped to the **+Z
street facade** AND turned it to read from the street: `ry = lot.ry + π` so its printed face points `+Z`
at the queue-side camera (else `DoubleSide` shows a mirrored band name). Proven: pub + rsl frontage posters
now sit **0.06 m from B's door**, printed-face·(+Z street) = **1.000**. Only item 1; spine posters untouched.
### The Z/+Z convention, settled (worth recording — it's the R13 RY_FLIP π-ambiguity again)
The **visible** venue face is `+Z`: `buildings.js` builds the facade/door at `zf = +d/2` ("front face at
local +Z"), and `venue.js` seats the door-glow/queue/poster-camera on `(sin ry, cos ry)` = `+Z`. The
selfcheck's `facing = (sin ry,cos ry)` "every lot faces its frontEdge" is the **sim/GLB** convention,
180° off from the visual front (same ambiguity D fixed at the source for the fleet in R13). A poster faces
the *viewer* when its printed face = `(sin ry,cos ry)` points at them, i.e. `ry = atan2(away-from-viewer)`.
### → B + F (eyeball in the tour): corner-venue frontage posters can sit near a crossing street
The `+Z` facade is the block-interior side in *plan* coordinates, so for a **corner venue** (a band_room/
rsl fronting one street with another behind — e.g. seed 1 band_room lot#481, 1.58 m from a side-street
centreline) the frontage poster lands near that crossing street's kerb. That's B's building geometry (the
door is there too), not a mid-road defect — so the selfcheck's no-bitumen assert now **exempts frontage
posters** (they're building-bound) and keeps it for free-standing **spine** posters. The pub hero is clean
(29.9 m of open ground on `+Z`). If a corner poster reads as floating over a road in the tour, that's a
v3.1 venue-placement tweak, not a v3.0 blocker.
## Round 14 (2026-07-16) — v3.0 RELEASE: invariant sweep + CITY_SPEC §v3 frozen
Release round — no new systems, no golden moves. **Synthetic `0x3fa36874` and gig `0x1f636349` are

View File

@ -202,9 +202,14 @@ function buildPosters(plan, citySeed, venues, gigs) {
const gig = tonight.get(v.shop.id);
const lot = gig && lotById.get(v.shop.lot);
if (!lot) continue;
const fx = -Math.sin(lot.ry), fz = -Math.cos(lot.ry); // house convention: front = local Z
// ROUND15 (ledger #1): B's buildings put the street facade on local +Z (frontage/queue seat on
// (sin ry, cos ry)); this once used Z, so the poster sat ~16 m BEHIND the building, invisible from
// the street — it's why queue_night framed no poster. Seat it on the +Z street face, AND turn it to
// read FROM the street: venue.js renders a poster's printed face along (sin ry, cos ry) = A's
// outward normal, so ry = lot.ry + π points that face outward (else DoubleSide shows a mirrored name).
const fx = Math.sin(lot.ry), fz = Math.cos(lot.ry); // +Z: the street facade (B's canon)
const off = lot.d / 2 + 0.06; // out onto the facade plane, proud of it
add(lot.x + fx * off, lot.z + fz * off, lot.ry, gig.gigId);
add(lot.x + fx * off, lot.z + fz * off, lot.ry + Math.PI, gig.gigId);
}
// 2. the spine run — seeded pole positions along main edges, on the verge, facing the footpath.

View File

@ -194,9 +194,21 @@ function districtInvariants(plan, label) {
const gigIds = new Set(plan.gigs.map(g => g.gigId));
ok(Array.isArray(plan.posters) && plan.posters.every(p => gigIds.has(p.gigId) && isFiniteNum(p.x) && isFiniteNum(p.z) && isFiniteNum(p.ry)), `${label}: posters reference a real gig with finite coords`);
ok(plan.posters.every(p => plan.gigs.find(g => g.gigId === p.gigId).night === 0), `${label}: posters advertise tonight's gigs`);
// every poster clears its nearest edge's carriageway — the R12 bug was posters mid-road on nodes.
// The no-bitumen rule (the R12 regression: spine posters at node.x+2) is for FREE-STANDING verge
// posters. A venue's own FRONTAGE poster is bound to the building B draws — since R15 it sits on the
// +Z street facade (B's canon: buildings.js facade + venue.js door/queue/camera are all +Z). A corner
// venue whose facade abuts a crossing street legitimately seats its poster near that street's kerb —
// B's building geometry, not a mid-road defect — so frontage posters are exempt here and validated by
// the placement asserts above (they reference tonight's gigs with finite coords).
const lotByIdD = new Map(plan.lots.map(l => [l.id, l]));
const onFacade = p => venues.some(v => {
const lot = lotByIdD.get(v.lot); if (!lot) return false;
const off = lot.d / 2 + 0.06; // gigs.js buildPosters item 1
return Math.hypot(p.x - (lot.x + Math.sin(lot.ry) * off), p.z - (lot.z + Math.cos(lot.ry) * off)) < 0.5;
});
let onRoad = null;
for (const p of plan.posters) {
if (onFacade(p)) continue; // frontage poster: on the venue building, not the verge
let best = Infinity, bestEdge = null;
for (const e of plan.streets.edges) {
const a = nodeById2(plan, e.a), b = nodeById2(plan, e.b);
@ -295,7 +307,7 @@ ok(JSON.stringify(generatePlanFor(42, 'synthetic', { gigs: true })) === JSON.str
ok(JSON.stringify(generatePlanFor(20261990, 'synthetic', { gigs: true, customBands: custom })) === JSON.stringify(pc), `custom bands: deterministic given (seed, list)`);
}
// gig-layer golden (guards gig-layer output drift; pure-generator path, no custom file)
const GIG_GOLDEN = 0x1f636349; // ROUND13 re-pin: multi-venue district changed the gig-plan output (was 0xa6ae5a5e, the one-pub alpha)
const GIG_GOLDEN = 0x4f4a549d; // ROUND15 re-pin: frontage posters flipped to the +Z street face (ledger #1 bug fix). Was 0x1f636349 (R13 district), 0xa6ae5a5e (R12 alpha).
{
const hash = xmur3(JSON.stringify(generatePlanFor(20261990, 'synthetic', { gigs: true })))() >>> 0;
if (GIG_GOLDEN === 0) console.log(` ⚠ gig golden UNPINNED → set GIG_GOLDEN = 0x${hash.toString(16).padStart(8, '0')}`);