gap: every level crossing is drawn with its barriers permanently lowered across the road #231
Labels
No labels
agent
agent:ci
agent:done
agent:failed
agent:needs-input
agent:refined
agent:refining
agent:running
agent:shipped
agent:skip
autonomous
autopilot
driven
local
plan
proposal
qa
qa-gap
research
retro
ship
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jeroen/cartopolis#231
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Every barrier crossing this client draws has its boom lying flat across the carriageway, at the height a closed boom sits.
crossings::push_crossingplaces the boom bands at a constant height and varies only the across-road coordinate (crates/geo/src/crossings.rs:376-387):BOOM_Yis 1.05 m (crates/geo/src/crossings.rs:102) and the long half-extentband * 0.5is on local x, which is the road-crossing axis (crates/geo/src/crossings.rs:334-340setsmast_x = road_half + 0.5on 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 aroad_halfof 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:
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 —
AHOB1,157,AHOB-MINI381,AOB137,HAHOB5 (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— thefor b in 0..BOOM_BANDSloop inpush_crossing(:377-387).The bands become a stack in y rising from the pivot at
BOOM_Y, at the mast's along-road position:y=BOOM_Y + band * (b as f32 + 0.5); the along-road coordinate staysalong.[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.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 ofMAST_HALF_W + BOOM_HALF_H= 0.24 m clears it with no overlapping faces:mx - side * (MAST_HALF_W + BOOM_HALF_H).push_boxis 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_mand the diagonal pairing are untouched.BOOM_HALF_His 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-173become true as written and need no edit; check them rather than change them.Acceptance criteria
HalfBarrier, every vertex is at leastroad_half - 0.25m (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.BOOM_Y + reach - 0.05m in y — 5.0 m forHalfBarrier, 3.1 m forMiniBarrier, 8.6 m forFullBarrier. ALightscrossing 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.Lightscrossing is unchanged — same vertex count and same bounding box as before.the_byte_cap_splits_between_crossingsanda_cap_below_one_crossing_still_draws_itpass untouched.crates/geo/src/crossings.rs'smod testspins 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.cartopolis_geosuite passes.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:
Only on a workstation (this container cannot render):
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
-90pitch can no longer show whether it worked. Take it obliquely along the road.level_crossingsin/tmp/x.jsonshould still read 3 at the Peizerweg, andsettled=truewithworker_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
crates/geo/src/crossings.rs:49-50) stands.crates/geo/src/crossings.rs:51-53) is not the bug here and is not being changed. Traffic passing through a crossing is unaffected.push_boxcannot tilt, and the angle is not measured anywhere — inventing one is the kind of guessCROSSING_TABLE's own rule refuses.BOOM_Yitself, the per-form reaches and mast heights, and the mini form's proportions. None of them are wrong; only the attitude is.crates/cartopolis/src/systems/map/crossings.rs). Nothing there reads the boom's shape; the only consumer of the mesher iscrates/cartopolis/src/systems/map/map_geometry.rs:739.Open questions
None.
Branch:
fix/231-level-crossing-raised-boomsOriginal request
What I did
Rendered the Peizerweg AHOB in Groningen — the crossing
crates/cartopolis/tests/fixtures/level_crossings_groningen.jsonwas captured from —with the shot harness, against the live ProRail register and a vector basemap:
Both captures settle (
settled=true,worker_outstanding=0) and reportlevel_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_crossingplaces theBOOM_BANDSboxesat a constant height and varies only the across-road coordinate:
BOOM_Yis 1.05 m and the box's long half-extent is on local x, the road-crossingaxis. 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 atrain, 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 raisedboom's pivot sits."
docs/notes/prorail-level-crossings.md, "What is not drawn, and why", repeats thefirst.
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_TABLEnames (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, thefor b in 0..BOOM_BANDSloop. A raisedboom is the same bands stacked in y from the pivot at
BOOM_Y, with the longhalf-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_meshesasserts the mesh is non-empty andindexed,
the_byte_cap_splits_between_crossingsasserts bytes, andthe_boom_count_comes_from_the_form_not_the_registerasserts vertex counts. An assertionon 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.
🤖 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:
BOOM_Y's own comment anddocs/notes/prorail-level-crossings.mdall say the booms stand raised. Nothing has to be invented or decided; this is a defect against a rule this tree already states.crates/geounit 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.crates/geo/src/crossings.rs::push_crossingonly. NoPROTOCOL_HISTORY, no migration, no workflow file, no taste.Scope it to
push_crossing'sfor b in 0..BOOM_BANDSloop — the same bands stacked in y from the pivot atBOOM_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.
Stuck, waiting for you: nonewhile three tickets wait for a person #270🤖 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.
🤖 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:skipstops me.🤖 viberfox issue agent on the VPS — 16 turns · $1.90 · session
0a17d6e7-b34a-4294-82f0-e1c8623e18a0. Reply with @agent to continue this session.🤖 Picked this up — working on branch
fix/231-level-crossing-raised-boomsin a fresh worktree on the VPS. I'll report back here when I'm done.Label an issue
agent:skipto keep me off it, ordrivento keep it in a session you are driving.Level crossing booms now stand upright instead of lying across the road.
What changed — one file,
crates/geo/src/crossings.rs:push_boxcan 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.BOOM_HALF_HtoBOOM_HALF_FACEand gave it a comment — a constant called "half height" that governs an across-road extent is how this gets misread again.a_raised_boom_stands_clear_of_the_roadchecks, 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_lampspins 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, anddocs/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-boomsis 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.🤖 Merged into
mainas pull request #273 (0a9836d7).🤖 Merged, but not deployed. The deploy step exited 1:
mainhas the change; the public services are still on the previous build.mainwas already red, and every merge commit was red #282alreadyFiledonly ever sees 50 issues, so every autopilot dedupe key silently expires — #206 was QA'd twice #281