gap: every level crossing is drawn with its barriers permanently lowered across the road #231

Closed
opened 2026-08-30 10:27:45 +00:00 by viberfox-agent · 7 comments
Collaborator

Problem

Every barrier crossing this client draws has its boom lying flat across the carriageway, at the height a closed boom sits.

crossings::push_crossing places the boom bands at a constant height and varies only the across-road coordinate (crates/geo/src/crossings.rs:376-387):

let band = reach / BOOM_BANDS as f32;
for b in 0..BOOM_BANDS {
    let centre = mx - side * (band * (b as f32 + 0.5));
    push(buf, crossing, (sn, cs), [centre, BOOM_Y, along],
         [band * 0.5, BOOM_HALF_H, BOOM_HALF_T], …);
}

BOOM_Y is 1.05 m (crates/geo/src/crossings.rs:102) and the long half-extent band * 0.5 is on local x, which is the road-crossing axis (crates/geo/src/crossings.rs:334-340 sets mast_x = road_half + 0.5 on the same axis). The bands run from the mast inward to the road centreline: for a half barrier the reach is 4.0 m against a road_half of 3.5 m (crates/geo/src/crossings.rs:202), so the inner band ends within half a metre of the middle of the road.

Three pieces of prose say the opposite:

  • the module note, crates/geo/src/crossings.rs:49-50 — "The booms stand raised, which is what a crossing looks like almost all the time."
  • BOOM_Y's own comment, crates/geo/src/crossings.rs:101 — "where a raised boom's pivot sits".
  • docs/notes/prorail-level-crossings.md:172-173, the same sentence as the module note.

It reaches every barrier form the table names — AHOB 1,157, AHOB-MINI 381, AOB 137, HAHOB 5 (crates/geo/src/crossings.rs:157-176) — and nothing on a crossing is solid (crates/geo/src/crossings.rs:51-53), so traffic and the avatar drive through a barrier that reads as permanently down.

Nothing in the suite constrains the boom's shape. every_form_meshes (crates/geo/src/crossings.rs:533) asserts the mesh is non-empty and its indices are in range; the_byte_cap_splits_between_crossings (:556) asserts bytes; the_boom_count_comes_from_the_form_not_the_register (:621) asserts vertex counts. All three pass with the boom in either attitude.

Approach

One crate, one function, plus a test. crates/geo/src/crossings.rs — the for b in 0..BOOM_BANDS loop in push_crossing (:377-387).

The bands become a stack in y rising from the pivot at BOOM_Y, at the mast's along-road position:

  • centre y = BOOM_Y + band * (b as f32 + 0.5); the along-road coordinate stays along.
  • half-extents become [BOOM_HALF_H, band * 0.5, BOOM_HALF_T] — the long extent moves to y, and the bar's flat face turns with it. The bar is 0.26 m across its face and 0.16 m thick (crates/geo/src/crossings.rs:105-106); raised, the 0.26 m lies across the road and the 0.16 m stays along it, which is what swapping the first two components expresses.
  • the across-road coordinate is the mast's mx, nudged toward the road so the raised bar stands beside the mast rather than inside it. The mast is 0.11 m half-width (crates/geo/src/crossings.rs:104) and the raised bar 0.13 m, so an offset of MAST_HALF_W + BOOM_HALF_H = 0.24 m clears it with no overlapping faces: mx - side * (MAST_HALF_W + BOOM_HALF_H).

push_box is axis-aligned in the object frame with a yaw rotation only — it takes (sn, cs) and rotates in the xz plane (crates/geo/src/furniture.rs:1094-1104). There is no way to hand it a tilt, so the 90° flip has to be expressed by swapping half-extents, as above. That is the whole reason this is a three-line change rather than a new mesher.

Box count per crossing is unchanged, so MAX_BOXES_PER_CROSSING (crates/geo/src/crossings.rs:91), CROSSING_MAX_BYTES (:95) and every byte assertion stay as they are. The mast, the lamp head, clearance_m and the diagonal pairing are untouched.

BOOM_HALF_H is named for the horizontal attitude. Renaming it to something attitude-neutral (BOOM_HALF_FACE, say) and adjusting its comment is part of this change; leaving a constant named "half height" governing an across-road extent is how the next reader gets it wrong again.

Consequential heights, for the reviewer: a half barrier's boom top goes to 1.05 + 4.0 = 5.05 m against a 3.1 m mast, a mini to 1.05 + 2.1 = 3.15 m against a 2.4 m mast, a full barrier to 1.05 + 7.6 = 8.65 m (reaches and mast heights from crates/geo/src/crossings.rs:200-207). A raised boom standing well above its own mast is correct — that is where the counterweighted end of a real one goes.

The two module notes and docs/notes/prorail-level-crossings.md:172-173 become true as written and need no edit; check them rather than change them.

Acceptance criteria

  • A barrier crossing meshed at yaw 0 places no vertex over the carriageway: for HalfBarrier, every vertex is at least road_half - 0.25 m (3.25 m) from the crossing's own x in the across-road axis. Today the inner band centre sits at x = 0.67 m, so this fails before the change.
  • A barrier form's mesh reaches at least BOOM_Y + reach - 0.05 m in y — 5.0 m for HalfBarrier, 3.1 m for MiniBarrier, 8.6 m for FullBarrier. A Lights crossing still tops out at the lamp head (mast_h + 0.16 + 0.16 = 3.42 m), so the two answers separate the boom from the mast without needing to identify which vertices belong to which box.
  • The raised bar does not intersect the mast: the gap between the mast's inner face and the bar's outer face is ≥ 0 at every band.
  • A Lights crossing is unchanged — same vertex count and same bounding box as before.
  • Vertex and index counts per crossing are unchanged for every form; the_byte_cap_splits_between_crossings and a_cap_below_one_crossing_still_draws_it pass untouched.
  • A new test in crates/geo/src/crossings.rs's mod tests pins the first two criteria for all three barrier forms, and its doc comment says what it is for: a boom lying flat is a crossing that reads as permanently closed, and no existing assertion could tell the two attitudes apart.
  • The whole cartopolis_geo suite passes.
  • The three prose claims cited above (crates/geo/src/crossings.rs:49-50, :101, docs/notes/prorail-level-crossings.md:172-173) are read and confirmed to match the code; BOOM_HALF_H's comment describes the attitude it is actually used in.

Verification

In this container:

cargo test -p cartopolis_geo crossings
cargo fmt -p cartopolis_geo -- --check

Only on a workstation (this container cannot render):

CARTO_TILE_URL=https://tiles.cartopolis.org/tiles/osm/{z}/{x}/{y} \
cargo run -p cartopolis -- --shot /tmp/x.png \
    --at 53.210238,6.549328,120 --look=-25,90 \
    --at 53.210238,6.549328,40 --look=-15,90 \
    --time 12 --clouds 0 --rain 0 --hide-ui --dump-state /tmp/x.json

Note the look angles differ from the ones in the report: the whole point of the fix is that from directly overhead a raised boom is a dot beside the mast, so a -90 pitch can no longer show whether it worked. Take it obliquely along the road. level_crossings in /tmp/x.json should still read 3 at the Peizerweg, and settled=true with worker_outstanding=0, as it did in the report.

Geometry and placement are trustworthy on the headless renderer; colour is not (docs/notes/headless-shots-software-renderer.md), so judge the attitude of the bar, not its red-and-white.

Out of scope

  • Animation. No boom lowers. There is still no train, and the module note's reason (crates/geo/src/crossings.rs:49-50) stands.
  • Colliders. A raised boom is over the road, not in it, so the "nothing on a crossing is solid" note (crates/geo/src/crossings.rs:51-53) is not the bug here and is not being changed. Traffic passing through a crossing is unaffected.
  • A backward lean. Real raised booms lean a few degrees off vertical. push_box cannot tilt, and the angle is not measured anywhere — inventing one is the kind of guess CROSSING_TABLE's own rule refuses.
  • The St Andrew's cross, BOOM_Y itself, the per-form reaches and mast heights, and the mini form's proportions. None of them are wrong; only the attitude is.
  • The register, the fetch path and the cache (crates/cartopolis/src/systems/map/crossings.rs). Nothing there reads the boom's shape; the only consumer of the mesher is crates/cartopolis/src/systems/map/map_geometry.rs:739.

Open questions

None.


Branch: fix/231-level-crossing-raised-booms

Original request

What I did

Rendered the Peizerweg AHOB in Groningen — the crossing
crates/cartopolis/tests/fixtures/level_crossings_groningen.json was captured from —
with the shot harness, against the live ProRail register and a vector basemap:

CARTO_TILE_URL=https://tiles.cartopolis.org/tiles/osm/{z}/{x}/{y} \
cargo run -p cartopolis -- --shot /tmp/x.png \
    --at 53.210238,6.549328,60 --look=-90,0 \
    --at 53.210238,6.549328,25 --look=-90,0 \
    --time 12 --clouds 0 --rain 0 --hide-ui --dump-state /tmp/x.json

Both captures settle (settled=true, worker_outstanding=0) and report
level_crossings = 3, so the layer reached the screen.

What happened

Seen from directly above, each installation's boom is a red-and-white bar lying flat
across the carriageway
. A raised boom stands vertical, and from that angle would be a
dot beside the mast rather than a four-metre bar over the asphalt.

That is what the code builds. crossings::push_crossing places the BOOM_BANDS boxes
at a constant height and varies only the across-road coordinate:

let centre = mx - side * (band * (b as f32 + 0.5));
push(buf, crossing, (sn, cs), [centre, BOOM_Y, along],
     [band * 0.5, BOOM_HALF_H, BOOM_HALF_T], …);

BOOM_Y is 1.05 m and the box's long half-extent is on local x, the road-crossing
axis. The boom is horizontal, at the height a closed boom sits.

What the module says it does

The opposite, in three places:

  • crates/geo/src/crossings.rs, module note: "Animation. A boom that lowers needs a
    train, and there is no train. The booms stand raised, which is what a crossing looks
    like almost all the time."
  • BOOM_Y's own comment: "Height of the boom above the road, metres — where a raised
    boom's pivot sits
    ."
  • docs/notes/prorail-level-crossings.md, "What is not drawn, and why", repeats the
    first.

What a user would expect

A level crossing with no train at it is open. As shipped, every one of the ~1,680
barrier crossings CROSSING_TABLE names (AHOB 1,157 · AHOB-MINI 381 · AOB 137 · HAHOB 5)
reads as permanently closed to traffic with nothing coming — and because nothing on a
crossing is solid, the streamed traffic and the avatar pass straight through the closed
boom.

Where the seam is

crates/geo/src/crossings.rs::push_crossing, the for b in 0..BOOM_BANDS loop. A raised
boom is the same bands stacked in y from the pivot at BOOM_Y, with the long
half-extent on y rather than x; the mast, lamp head, clearance and byte accounting around
it are unchanged.

Nothing in the suite catches this: every_form_meshes asserts the mesh is non-empty and
indexed, the_byte_cap_splits_between_crossings asserts bytes, and
the_boom_count_comes_from_the_form_not_the_register asserts vertex counts. An assertion
on the boom's bounding box — taller than it is wide — would.

Filed from the QA pass on #216 (#230).

🤖 Refined by the viberfox issue agent. Reply with @agent refine and what is wrong to have this rewritten.

## Problem Every barrier crossing this client draws has its boom lying flat across the carriageway, at the height a closed boom sits. `crossings::push_crossing` places the boom bands at a constant height and varies only the across-road coordinate (`crates/geo/src/crossings.rs:376-387`): ```rust let band = reach / BOOM_BANDS as f32; for b in 0..BOOM_BANDS { let centre = mx - side * (band * (b as f32 + 0.5)); push(buf, crossing, (sn, cs), [centre, BOOM_Y, along], [band * 0.5, BOOM_HALF_H, BOOM_HALF_T], …); } ``` `BOOM_Y` is 1.05 m (`crates/geo/src/crossings.rs:102`) and the long half-extent `band * 0.5` is on local **x**, which is the road-crossing axis (`crates/geo/src/crossings.rs:334-340` sets `mast_x = road_half + 0.5` on the same axis). The bands run from the mast inward to the road centreline: for a half barrier the reach is 4.0 m against a `road_half` of 3.5 m (`crates/geo/src/crossings.rs:202`), so the inner band ends within half a metre of the middle of the road. Three pieces of prose say the opposite: - the module note, `crates/geo/src/crossings.rs:49-50` — "The booms stand raised, which is what a crossing looks like almost all the time." - `BOOM_Y`'s own comment, `crates/geo/src/crossings.rs:101` — "where a raised boom's pivot sits". - `docs/notes/prorail-level-crossings.md:172-173`, the same sentence as the module note. It reaches every barrier form the table names — `AHOB` 1,157, `AHOB-MINI` 381, `AOB` 137, `HAHOB` 5 (`crates/geo/src/crossings.rs:157-176`) — and nothing on a crossing is solid (`crates/geo/src/crossings.rs:51-53`), so traffic and the avatar drive through a barrier that reads as permanently down. Nothing in the suite constrains the boom's shape. `every_form_meshes` (`crates/geo/src/crossings.rs:533`) asserts the mesh is non-empty and its indices are in range; `the_byte_cap_splits_between_crossings` (`:556`) asserts bytes; `the_boom_count_comes_from_the_form_not_the_register` (`:621`) asserts vertex counts. All three pass with the boom in either attitude. ## Approach One crate, one function, plus a test. `crates/geo/src/crossings.rs` — the `for b in 0..BOOM_BANDS` loop in `push_crossing` (`:377-387`). The bands become a stack in **y** rising from the pivot at `BOOM_Y`, at the mast's along-road position: - centre `y` = `BOOM_Y + band * (b as f32 + 0.5)`; the along-road coordinate stays `along`. - half-extents become `[BOOM_HALF_H, band * 0.5, BOOM_HALF_T]` — the long extent moves to y, and the bar's flat face turns with it. The bar is 0.26 m across its face and 0.16 m thick (`crates/geo/src/crossings.rs:105-106`); raised, the 0.26 m lies across the road and the 0.16 m stays along it, which is what swapping the first two components expresses. - the across-road coordinate is the mast's `mx`, nudged toward the road so the raised bar stands beside the mast rather than inside it. The mast is 0.11 m half-width (`crates/geo/src/crossings.rs:104`) and the raised bar 0.13 m, so an offset of `MAST_HALF_W + BOOM_HALF_H` = 0.24 m clears it with no overlapping faces: `mx - side * (MAST_HALF_W + BOOM_HALF_H)`. `push_box` is axis-aligned in the object frame with a yaw rotation only — it takes `(sn, cs)` and rotates in the xz plane (`crates/geo/src/furniture.rs:1094-1104`). There is no way to hand it a tilt, so the 90° flip has to be expressed by swapping half-extents, as above. That is the whole reason this is a three-line change rather than a new mesher. Box count per crossing is unchanged, so `MAX_BOXES_PER_CROSSING` (`crates/geo/src/crossings.rs:91`), `CROSSING_MAX_BYTES` (`:95`) and every byte assertion stay as they are. The mast, the lamp head, `clearance_m` and the diagonal pairing are untouched. `BOOM_HALF_H` is named for the horizontal attitude. Renaming it to something attitude-neutral (`BOOM_HALF_FACE`, say) and adjusting its comment is part of this change; leaving a constant named "half height" governing an across-road extent is how the next reader gets it wrong again. Consequential heights, for the reviewer: a half barrier's boom top goes to 1.05 + 4.0 = 5.05 m against a 3.1 m mast, a mini to 1.05 + 2.1 = 3.15 m against a 2.4 m mast, a full barrier to 1.05 + 7.6 = 8.65 m (reaches and mast heights from `crates/geo/src/crossings.rs:200-207`). A raised boom standing well above its own mast is correct — that is where the counterweighted end of a real one goes. The two module notes and `docs/notes/prorail-level-crossings.md:172-173` become true as written and need no edit; check them rather than change them. ## Acceptance criteria - [ ] A barrier crossing meshed at yaw 0 places **no vertex over the carriageway**: for `HalfBarrier`, every vertex is at least `road_half - 0.25` m (3.25 m) from the crossing's own x in the across-road axis. Today the inner band centre sits at x = 0.67 m, so this fails before the change. - [ ] A barrier form's mesh reaches at least `BOOM_Y + reach - 0.05` m in y — 5.0 m for `HalfBarrier`, 3.1 m for `MiniBarrier`, 8.6 m for `FullBarrier`. A `Lights` crossing still tops out at the lamp head (`mast_h + 0.16 + 0.16` = 3.42 m), so the two answers separate the boom from the mast without needing to identify which vertices belong to which box. - [ ] The raised bar does not intersect the mast: the gap between the mast's inner face and the bar's outer face is ≥ 0 at every band. - [ ] A `Lights` crossing is unchanged — same vertex count and same bounding box as before. - [ ] Vertex and index counts per crossing are unchanged for every form; `the_byte_cap_splits_between_crossings` and `a_cap_below_one_crossing_still_draws_it` pass untouched. - [ ] A new test in `crates/geo/src/crossings.rs`'s `mod tests` pins the first two criteria for all three barrier forms, and its doc comment says what it is for: a boom lying flat is a crossing that reads as permanently closed, and no existing assertion could tell the two attitudes apart. - [ ] The whole `cartopolis_geo` suite passes. - [ ] The three prose claims cited above (`crates/geo/src/crossings.rs:49-50`, `:101`, `docs/notes/prorail-level-crossings.md:172-173`) are read and confirmed to match the code; `BOOM_HALF_H`'s comment describes the attitude it is actually used in. ## Verification In this container: ```bash cargo test -p cartopolis_geo crossings cargo fmt -p cartopolis_geo -- --check ``` Only on a workstation (this container cannot render): ```bash CARTO_TILE_URL=https://tiles.cartopolis.org/tiles/osm/{z}/{x}/{y} \ cargo run -p cartopolis -- --shot /tmp/x.png \ --at 53.210238,6.549328,120 --look=-25,90 \ --at 53.210238,6.549328,40 --look=-15,90 \ --time 12 --clouds 0 --rain 0 --hide-ui --dump-state /tmp/x.json ``` Note the look angles differ from the ones in the report: the whole point of the fix is that from directly overhead a raised boom is a dot beside the mast, so a `-90` pitch can no longer show whether it worked. Take it obliquely along the road. `level_crossings` in `/tmp/x.json` should still read 3 at the Peizerweg, and `settled=true` with `worker_outstanding=0`, as it did in the report. Geometry and placement are trustworthy on the headless renderer; colour is not (`docs/notes/headless-shots-software-renderer.md`), so judge the attitude of the bar, not its red-and-white. ## Out of scope - **Animation.** No boom lowers. There is still no train, and the module note's reason (`crates/geo/src/crossings.rs:49-50`) stands. - **Colliders.** A raised boom is over the road, not in it, so the "nothing on a crossing is solid" note (`crates/geo/src/crossings.rs:51-53`) is not the bug here and is not being changed. Traffic passing through a crossing is unaffected. - **A backward lean.** Real raised booms lean a few degrees off vertical. `push_box` cannot tilt, and the angle is not measured anywhere — inventing one is the kind of guess `CROSSING_TABLE`'s own rule refuses. - **The St Andrew's cross**, `BOOM_Y` itself, the per-form reaches and mast heights, and the mini form's proportions. None of them are wrong; only the attitude is. - **The register, the fetch path and the cache** (`crates/cartopolis/src/systems/map/crossings.rs`). Nothing there reads the boom's shape; the only consumer of the mesher is `crates/cartopolis/src/systems/map/map_geometry.rs:739`. ## Open questions None. --- Branch: `fix/231-level-crossing-raised-booms` <details><summary>Original request</summary> ## What I did Rendered the Peizerweg AHOB in Groningen — the crossing `crates/cartopolis/tests/fixtures/level_crossings_groningen.json` was captured from — with the shot harness, against the live ProRail register and a vector basemap: ``` CARTO_TILE_URL=https://tiles.cartopolis.org/tiles/osm/{z}/{x}/{y} \ cargo run -p cartopolis -- --shot /tmp/x.png \ --at 53.210238,6.549328,60 --look=-90,0 \ --at 53.210238,6.549328,25 --look=-90,0 \ --time 12 --clouds 0 --rain 0 --hide-ui --dump-state /tmp/x.json ``` Both captures settle (`settled=true`, `worker_outstanding=0`) and report `level_crossings = 3`, so the layer reached the screen. ## What happened Seen from directly above, each installation's boom is a red-and-white bar **lying flat across the carriageway**. A raised boom stands vertical, and from that angle would be a dot beside the mast rather than a four-metre bar over the asphalt. That is what the code builds. `crossings::push_crossing` places the `BOOM_BANDS` boxes at a constant height and varies only the across-road coordinate: ```rust let centre = mx - side * (band * (b as f32 + 0.5)); push(buf, crossing, (sn, cs), [centre, BOOM_Y, along], [band * 0.5, BOOM_HALF_H, BOOM_HALF_T], …); ``` `BOOM_Y` is 1.05 m and the box's long half-extent is on local **x**, the road-crossing axis. The boom is horizontal, at the height a *closed* boom sits. ## What the module says it does The opposite, in three places: - `crates/geo/src/crossings.rs`, module note: "**Animation.** A boom that lowers needs a train, and there is no train. The booms stand raised, which is what a crossing looks like almost all the time." - `BOOM_Y`'s own comment: "Height of the boom above the road, metres — **where a raised boom's pivot sits**." - `docs/notes/prorail-level-crossings.md`, "What is not drawn, and why", repeats the first. ## What a user would expect A level crossing with no train at it is open. As shipped, every one of the ~1,680 barrier crossings `CROSSING_TABLE` names (AHOB 1,157 · AHOB-MINI 381 · AOB 137 · HAHOB 5) reads as permanently closed to traffic with nothing coming — and because nothing on a crossing is solid, the streamed traffic and the avatar pass straight through the closed boom. ## Where the seam is `crates/geo/src/crossings.rs::push_crossing`, the `for b in 0..BOOM_BANDS` loop. A raised boom is the same bands stacked in **y** from the pivot at `BOOM_Y`, with the long half-extent on y rather than x; the mast, lamp head, clearance and byte accounting around it are unchanged. Nothing in the suite catches this: `every_form_meshes` asserts the mesh is non-empty and indexed, `the_byte_cap_splits_between_crossings` asserts bytes, and `the_boom_count_comes_from_the_form_not_the_register` asserts vertex counts. An assertion on the boom's bounding box — taller than it is wide — would. <sub>Filed from the QA pass on #216 (#230).</sub> </details> <sub>🤖 Refined by the viberfox issue agent. Reply with **@agent refine** and what is wrong to have this rewritten.</sub>
Author
Collaborator

🤖 Promoted into the build lane by the 7-day retrospective (#269) — autonomous + ship.

Why this one, of the six gaps the QA pass on #216 filed:

  • The spec is already written, in three places, and the code does the opposite. The module note, BOOM_Y's own comment and docs/notes/prorail-level-crossings.md all say the booms stand raised. Nothing has to be invented or decided; this is a defect against a rule this tree already states.
  • A machine judges it from geometry, which is value 6. The ticket names the test itself: assert the boom's bounding box is taller than it is wide. That is a crates/geo unit test on generated vertices — no capture, no colour, nothing this container cannot answer. The existing suite (every_form_meshes, the_boom_count_comes_from_the_form_not_the_register) asserts counts and bytes and would not catch it, which is why it shipped.
  • Nothing here is fenced. crates/geo/src/crossings.rs::push_crossing only. No PROTOCOL_HISTORY, no migration, no workflow file, no taste.
  • It is the most visibly wrong thing in the twin right now. ~1,680 barrier crossings nationally, every one of them reading as permanently closed with nothing coming.

Scope it to push_crossing's for b in 0..BOOM_BANDS loop — the same bands stacked in y from the pivot at BOOM_Y, long half-extent on y rather than x. Mast, lamp head, clearance and byte accounting unchanged. Land the bounding-box assertion with it.

Not in scope: the barrier spacing (#232), the road width (#233), colliders (#234), driver mode (#235). Four separate tickets on purpose.

🤖 **Promoted into the build lane** by the 7-day retrospective (#269) — `autonomous` + `ship`. Why this one, of the six gaps the QA pass on #216 filed: - **The spec is already written, in three places, and the code does the opposite.** The module note, `BOOM_Y`'s own comment and `docs/notes/prorail-level-crossings.md` all say the booms stand raised. Nothing has to be invented or decided; this is a defect against a rule this tree already states. - **A machine judges it from geometry, which is value 6.** The ticket names the test itself: assert the boom's bounding box is taller than it is wide. That is a `crates/geo` unit test on generated vertices — no capture, no colour, nothing this container cannot answer. The existing suite (`every_form_meshes`, `the_boom_count_comes_from_the_form_not_the_register`) asserts counts and bytes and would not catch it, which is why it shipped. - **Nothing here is fenced.** `crates/geo/src/crossings.rs::push_crossing` only. No `PROTOCOL_HISTORY`, no migration, no workflow file, no taste. - **It is the most visibly wrong thing in the twin right now.** ~1,680 barrier crossings nationally, every one of them reading as permanently closed with nothing coming. Scope it to `push_crossing`'s `for b in 0..BOOM_BANDS` loop — the same bands stacked in **y** from the pivot at `BOOM_Y`, long half-extent on y rather than x. Mast, lamp head, clearance and byte accounting unchanged. Land the bounding-box assertion with it. Not in scope: the barrier spacing (#232), the road width (#233), colliders (#234), driver mode (#235). Four separate tickets on purpose.
Author
Collaborator

🤖 Refining this before anything gets built — reading the code, no branch, no commits. If it turns out I'd have to guess at the requirements I'll stop and ask instead.

🤖 Refining this before anything gets built — reading the code, no branch, no commits. If it turns out I'd have to guess at the requirements I'll stop and ask instead.
Author
Collaborator

🤖 Refined. The description above is now the spec; I'll pick it up for implementation on the next poll.

If the direction is wrong, say so now — @agent refine with a correction rewrites it, and agent:skip stops me.


🤖 viberfox issue agent on the VPS — 16 turns · $1.90 · session 0a17d6e7-b34a-4294-82f0-e1c8623e18a0. Reply with @agent to continue this session.

🤖 **Refined.** The description above is now the spec; I'll pick it up for implementation on the next poll. If the direction is wrong, say so now — **@agent refine** with a correction rewrites it, and `agent:skip` stops me. --- <sub>🤖 viberfox issue agent on the VPS — 16 turns · $1.90 · session `0a17d6e7-b34a-4294-82f0-e1c8623e18a0`. Reply with **@agent** to continue this session.</sub>
Author
Collaborator

🤖 Picked this up — working on branch fix/231-level-crossing-raised-booms in a fresh worktree on the VPS. I'll report back here when I'm done.

Label an issue agent:skip to keep me off it, or driven to keep it in a session you are driving.

🤖 Picked this up — working on branch `fix/231-level-crossing-raised-booms` in a fresh worktree on the VPS. I'll report back here when I'm done. <sub>Label an issue `agent:skip` to keep me off it, or `driven` to keep it in a session you are driving.</sub>
Author
Collaborator

Level crossing booms now stand upright instead of lying across the road.

What changed — one file, crates/geo/src/crossings.rs:

  • The three boom bands were placed side by side along the road-crossing axis at a fixed height of 1.05 m, which is where a closed boom sits. A half barrier's innermost band ended 0.67 m from the middle of the carriageway. They are now stacked upwards from the same pivot height, at the mast's own position along the road, offset 0.24 m inboard so the bar stands flush beside the mast rather than inside it.
  • push_box can only rotate about the vertical axis, so the 90° turn is expressed by swapping the first two half-extents. The 0.26 m painted face still lies across the road and the 0.16 m thickness still along it.
  • Renamed BOOM_HALF_H to BOOM_HALF_FACE and gave it a comment — a constant called "half height" that governs an across-road extent is how this gets misread again.
  • Added two tests. a_raised_boom_stands_clear_of_the_road checks, for all three barrier forms, that no vertex is over the carriageway and that the mesh reaches the top of the raised bar. a_lights_crossing_is_two_masts_and_two_lamps pins a boomless crossing to an exact bounding box, so the height check above is a statement about the boom and not the mast.

Nothing else moved: same number of boxes per crossing, same vertex and index counts, same byte accounting, same masts, lamp heads, clearances and reaches. No animation, no colliders.

Verified here: cargo test -p cartopolis_geo — 238 passed. cargo test -p cartopolis — 820 passed. Formatting check clean on all five owned crates. I also confirmed the new test genuinely catches the bug: reverted to the old placement and it failed with "a vertex at x 2.666668 is over the carriageway".

I read the three pieces of prose the issue cites — the module note, BOOM_Y's comment, and docs/notes/prorail-level-crossings.md — and all three are now true as written, so I changed none of them.

Not verified: this container cannot render, so nobody has looked at a picture of it. Worth an oblique shot along the road at the Peizerweg on a workstation before you trust the attitude visually.

One thing for you to decide: the mast's inner face and the bar's outer face are exactly coincident over the 2 m they overlap, which is what the issue asked for ("no overlapping faces"). They face opposite directions, so backface culling should keep them apart, but if you see flicker there on a real GPU, nudging the bar another centimetre inboard is a one-character fix.

Branch fix/231-level-crossing-raised-booms is pushed. Note the git worktree I was handed lost its registration mid-session; I recreated it and the commit is intact.

No commits were made.


🤖 viberfox issue agent on the VPS — 31 turns · $2.80 · session 548d4a5a-b093-4a17-8908-dfd90b2fe19f. Reply with @agent to continue this session.

Level crossing booms now stand upright instead of lying across the road. **What changed** — one file, `crates/geo/src/crossings.rs`: - The three boom bands were placed side by side along the road-crossing axis at a fixed height of 1.05 m, which is where a *closed* boom sits. A half barrier's innermost band ended 0.67 m from the middle of the carriageway. They are now stacked upwards from the same pivot height, at the mast's own position along the road, offset 0.24 m inboard so the bar stands flush beside the mast rather than inside it. - `push_box` can only rotate about the vertical axis, so the 90° turn is expressed by swapping the first two half-extents. The 0.26 m painted face still lies across the road and the 0.16 m thickness still along it. - Renamed `BOOM_HALF_H` to `BOOM_HALF_FACE` and gave it a comment — a constant called "half height" that governs an across-road extent is how this gets misread again. - Added two tests. `a_raised_boom_stands_clear_of_the_road` checks, for all three barrier forms, that no vertex is over the carriageway and that the mesh reaches the top of the raised bar. `a_lights_crossing_is_two_masts_and_two_lamps` pins a boomless crossing to an exact bounding box, so the height check above is a statement about the boom and not the mast. Nothing else moved: same number of boxes per crossing, same vertex and index counts, same byte accounting, same masts, lamp heads, clearances and reaches. No animation, no colliders. **Verified here**: `cargo test -p cartopolis_geo` — 238 passed. `cargo test -p cartopolis` — 820 passed. Formatting check clean on all five owned crates. I also confirmed the new test genuinely catches the bug: reverted to the old placement and it failed with "a vertex at x 2.666668 is over the carriageway". I read the three pieces of prose the issue cites — the module note, `BOOM_Y`'s comment, and `docs/notes/prorail-level-crossings.md` — and all three are now true as written, so I changed none of them. **Not verified**: this container cannot render, so nobody has looked at a picture of it. Worth an oblique shot along the road at the Peizerweg on a workstation before you trust the attitude visually. **One thing for you to decide**: the mast's inner face and the bar's outer face are exactly coincident over the 2 m they overlap, which is what the issue asked for ("no overlapping faces"). They face opposite directions, so backface culling should keep them apart, but if you see flicker there on a real GPU, nudging the bar another centimetre inboard is a one-character fix. Branch `fix/231-level-crossing-raised-booms` is pushed. Note the git worktree I was handed lost its registration mid-session; I recreated it and the commit is intact. _No commits were made._ --- <sub>🤖 viberfox issue agent on the VPS — 31 turns · $2.80 · session `548d4a5a-b093-4a17-8908-dfd90b2fe19f`. Reply with **@agent** to continue this session.</sub>
viberfox-agent 2026-09-02 07:25:21 +00:00
Author
Collaborator

🤖 Merged into main as pull request #273 (0a9836d7).

🤖 **Merged into `main`** as pull request #273 (`0a9836d7`).
Author
Collaborator

🤖 Merged, but not deployed. The deploy step exited 1:

main is at f9e457b7
  waiting for CI on f9e457b7
  success    wasm & android targets
  failure    test cartopolis
refusing: CI is not green on f9e457b7

main has the change; the public services are still on the previous build.

🤖 **Merged, but not deployed.** The deploy step exited 1: ``` main is at f9e457b7 waiting for CI on f9e457b7 success wasm & android targets failure test cartopolis refusing: CI is not green on f9e457b7 ``` `main` has the change; the public services are still on the previous build.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jeroen/cartopolis#231
No description provided.