Handoff — seg3d LAS for all A1 + A4_5 segments

2026-08-07 · for an agent running on battlebox · continuation of the 5-segment pilot

Summary

A 5-segment pilot is done and delivered. Your job is the full sweep: 285 segments — A1 branch_000 (131), A1 branch_001 (11), A4_5 (143) — each fused into a ReCap-ready segment_NNN_seg3d.las (LAS 1.4 PDRF 7, EPSG:25832, per-instance point_source_id, las_split/).

Do not start by launching 285 runs. Two inputs do not exist at scale (A1 surface meshes, A1 line-inference masks) and one dataset cannot get asphalt edges at all (branch_001). Resolve Blockers first, get a written decision from Miro on scope, then batch.

Current state

Done — pilot delivered 2026-08-07

Five segments fused, validated and copied to the N: share:

N:\02_Modelling\05_Rink\02_AI 3D modeling\03_Working folder\
  260803_recap_annotation_Abschnitt_1\pre_annotation\260807_latest_upstream\
    branch_000\segment_065  branch_000\segment_070  branch_001\segment_005
  260803_recap_annotation_Abschnitt_4_5\pre_annotation\260807_latest_upstream\
    segment_040  segment_100

Battlebox-side copies: D:\temp\recap_260807\{A1_branch_000,A1_branch_001,A45} (WSL: /mnt/d/temp/recap_260807/). The _DISCARD_A1_long folder there is a wrong-dataset run — ignore and delete when convenient. A README.md with provenance and caveats sits in each delivered folder; copy that pattern for the full run.

Datasets — use these exact roots

DatasetRootSegmentsTiles
A1 branch_000/mnt/d/a123-data/Abschnitt_1/branch_000/lane_points131 (segment_000…130)131
A1 branch_001/mnt/d/a123-data/Abschnitt_1/branch_001/lane_points11 (segment_000…010)11
A4_5/mnt/d/Data/02_AI 3D modeling/00_data/260416_Abschnitt_4_5/lane_points143 (segment_000…142)143

trap The A1 dataset is the branch-split one under /mnt/d/a123-data/, geoshift 694541.4428 / 5386372.3686 / 467.4083. 00_data/251017_Color_Abschnitt_1_long and _short are a different segmentation of the same survey (geoshift 695777.2088…, 2-digit segment_70 dirs). Miro: "Do not use short and long. Use branches 0 and 1." A whole pipeline pass was wasted on _long before this surfaced.

Package versions (all pulled to latest on 2026-08-07)

StageCheckoutRev
asphalt edges/home/ai/dev/ae-main-260807 (detached worktree of origin/main)8b8a3f6
guardrails / walls/home/ai/dev/3dai.iolabs.pointcloud.guardrailsmain 58d66f1
signs / gates / delineators / trees/home/ai/dev/3dai.iolabs.pointcloud.verticalsignsmaster f3e7151
ground (tablecloth)/home/ai/dev/3dai.iolabs.pointcloud.tableclothmain 3a8cedb
lane markings/home/ai/dev/3dai.iolabs.pointcloud.maskclusteringv0.2.4 / 2526ec6
step-5 inference/home/ai/dev/3dai.lanefinder, bundle models/ft_r34_260624
fusion/home/ai/dev/3dai.iolabs.pointcloud.3dsegmentation5dab3c8

note The user's own asphaltedge checkout sits on feature/zrange-evidence-channel, which is behind origin/main. Use the ae-main-260807 worktree; do not switch branches in their checkout. Re-pull everything before you start — this handoff may be days old by the time you run.

Environment

# every WSL heredoc that touches uv:
export PATH="$HOME/.local/bin:$PATH"
set -a; . ~/.env; set +a          # UV_INDEX_NEXUS_* — without it uv sync fails
                                  # with "Missing credentials for nexus.iolabs.ch"

24 cores, 94 GB RAM, RTX 5070 Ti (needed only for step-5 inference). Disk: /home/ai 807 GB free · /mnt/d 263 GB free (93 % full) — write intermediates and outputs to /home/ai, not D:.

Blockers first — resolve before batching

1. A1 has no run4 road surface, and step-9 meshes exist for 6 segments only blocker

Fusion takes pavement from the asphalt-edge corridor plus a road surface. A4_5 has *_run4_road_surface.npz everywhere; A1 has none, and the substitute (--surface-mesh-dirs, seg3d commit 5dab3c8) only has data for the pilot segments:

/mnt/d/a123-data/Abschnitt_1/surface_mesh_s9/branch_000  -> segment_060, 065, 070  (3 of 131)
/mnt/d/a123-data/Abschnitt_1/surface_mesh_s9/branch_001  -> segment_004, 005, 006  (3 of 11)

Producer is the filteringsurface repo, currently a redesign worktree at /home/ai/dev/wt-surface-mesh (branch worktree-surface-mesh-redesign, AI3D-347, battlebox-local dep pins re-applied by /home/ai/dev/sync-surface-mesh.sh). Work out its CLI, run one segment, and diff the output against surface_mesh_s9/branch_000/segment_070/segment_070_surface_mesh_{left,right}.ply before committing to 136 more. Measured effect of the mesh on the pilot: 41 % of points within 0.15 m on segment_065, 78 % on segment_070 — without it, A1 asphalt coverage drops materially.

2. A1 branch_001 has no road axis — asphalt edges cannot run there blocker

find /mnt/d/a123-data -name "cross_sections_run7_lanes_*.npz" returns exactly one file, branch_000's cross_sections_run7_lanes_20260708-000411.npz (13 450 m, 26 902 vertices). Projecting branch_001 segment_005 onto it gives median lateral offset 1 166 m — it is a genuinely different centreline, not a frame problem. Existing older-pipeline edges cover 3 of 11 segments (edges_s8/branch_001). Either the axis gets regenerated upstream (run7 / lanefinder), or 8 of branch_001's 11 segments ship without an asphalt corridor. Ask Miro.

3. A1 line-marking masks must be inferred for ~136 segments work

clusters_mask/ exists in the client tree for only 3 segments per A1 branch (package 0.2.2, written by an Azure ML run whose step-5 masks are not on this box). A4_5 has all 143 topdown_predictions/segment_NNN_intensity_mask.png already. Step-5 inference is cheap (~3 s/tile on cuda:0) and reproduces the Azure masks to sub-percent, so just run it for every A1 segment into a staging tree.

4. Never write into client data rule

/mnt/d/Data and /mnt/d/a123-data are read-only for us. Mask clustering writes clusters_mask/ inside the segment dir, so every dataset must be staged as real dirs holding symlinks to the client npz files. Pilot staging tree to copy: /home/ai/seg3d_stage_260807/{A1B0,A1B1,A45}/lane_points/ — builder script at /tmp/build_shim2.py on battlebox (may be gone; the pattern is: real segment_NNN/ dir, symlink each *_run3_points.npz, symlink root-level run3_geoshift.json / run3_planes.npz / cross-sections / run7 XML, real topdown_tiles/ and topdown_predictions/).

Per-stage recipe (verified on the pilot)

Asphalt edges — validate the axis or you get silent garbage
cd /home/ai/dev/ae-main-260807
uv run python scripts/run_segments.py \
  --data-dir <STAGE>/lane_points --segments 0-130 \
  --out /home/ai/seg3d_out/edges_<DS> -v

gotcha load_axis() picks sorted(glob("cross_sections_run7_lanes_*.npz"))[-1] — newest filename, never validated against the segment. Wrong axis ⇒ all stations clamp to 0 ⇒ ~8 KB npz with zero finite clean offsets ⇒ exit 0, no warning. Stage exactly one cross-sections file per run and pre-check by projecting points onto the axis (offsets in metres, station range spans the segment). This burned an 18-minute run in the pilot.

Other notes: --max-points defaults to 7 M and is silently hit on these clouds (raw 24 M); run4 is optional; no --config flag exists. Timing varies hugely with I/O: A1 ≈ 30 s/segment, A4_5 ≈ 320 s/segment (load dominates). Known defect: z == 0.0 sentinel values in *_polyline_points (up to 31 % of vertices on one pilot segment) — treat z == 0.0 as no-data downstream.

Guardrails / walls
cd /home/ai/dev/3dai.iolabs.pointcloud.guardrails
uv run python -m guardrails.detect \
  --data-dir <STAGE>/lane_points \
  --lane-xml <STAGE>/lane_points/<run7 lanes xml> \
  --segments 000,001,... --out /home/ai/seg3d_out/guardrails_<DS> \
  --dump-point-masks --log-level INFO

Lane XMLs: A1 b000 run7_lanes_branch_000_20260722-092426.xml, b001 …_branch_001_20260722-092434.xml, A4_5 run7_lanes_20260723-163130.xml. --lane-xml is effectively mandatory on current main. If a segment yields zero masked points and the log says "no Central Axis in segment", re-run it with --set lane_xml_zones_enabled=false --set precision_gate_enabled=false and keep that.

known bug With zone gating on, points whose occupancy cell was created by zone enrichment are dropped from point_masks.npz entirely — pilot measured 5 %, 17 % and 46 % of the JSON-reported point counts surviving. The per-point guardrail class is still sound; per-instance splitting under-segments. Worth fixing upstream before a 285-segment run if instance-level review matters.

run_summary.json is rewritten per invocation — copy it aside per batch.

Signs / gates / delineators / trees
cd /home/ai/dev/3dai.iolabs.pointcloud.verticalsigns   # MUST cd: models/ is CWD-relative
uv run verticalsigns-detect --data-dir <STAGE>/lane_points \
  --segments 000,001,... --out /home/ai/seg3d_out/signs_<DS> \
  --config tree_run.config.json --dump-point-masks --log-level INFO

Confirm both models load in the log (verifier-2026-07-11.joblib and vegetation-2026-07-11.joblib) — if a model fails to resolve the stage silently no-ops. ~45 s/segment. Yield is genuinely low on this corpus (~1 device/segment); the pilot's three A1 segments produced trees only.

Ground (tablecloth)
cd /home/ai/dev/3dai.iolabs.pointcloud.tablecloth
uv run python -m iolabs_point_cloud_tablecloth.filter \
  --data-dir <STAGE>/lane_points --segments 000,001,... \
  --out /home/ai/seg3d_out/ground_<DS>

For A1 the fresh masks came out bit-identical to the Azure set at /mnt/d/a123-data/masks_flat/Abschnitt_1_branch_00N/ (the Azure run already used the slope-aware defaults). At 285 segments, consider reusing masks_flat for A1 instead of recomputing — but verify a sample first, and note A4_5 has no equivalent.

disk The filter also writes full cleaned NPZ copies that seg3d never reads, with no flag to suppress them: ~275 MB/segment. 285 segments ≈ 78 GB. Budget it, or delete *_run3_points.npz from the output dirs after each batch.

Lane markings — step-5 inference then step-6c clustering
cd /home/ai/dev/3dai.lanefinder      # A1 only; A4_5 masks already exist
uv run python scripts/pipeline/run_5_line_bitmap_inference.py \
  --data-dir <STAGE> --bundle /home/ai/dev/3dai.lanefinder/models/ft_r34_260624 \
  --tile-size 512 --overlap 128 --device cuda:0

cd /home/ai/dev/3dai.iolabs.pointcloud.maskclustering
uv run iolabs-mask-cluster segment \
  --segment-dir <STAGE>/lane_points/segment_NNN \
  --mask <STAGE>/lane_points/topdown_predictions/segment_NNN_intensity_mask.png \
  --raster-metadata <STAGE>/lane_points/topdown_tiles/segment_NNN.json \
  --output-dir <STAGE>/lane_points/segment_NNN/clusters_mask --overwrite

The inference script's default subdirs (lane_points/topdown_tileslane_points/topdown_predictions) match the staging layout, so --data-dir pointed at the stage root is all it needs — no job config. Use the batch subcommand with an explicit jobs.json for volume; never use --from-segment/--to-segment, which compare segment names lexicographically. ~40–80 s/segment. Tile JSONs need geoshift.z; A1 branch tiles have it, the _long tiles did not.

Fusion → LAS
cd /home/ai/dev/3dai.iolabs.pointcloud.3dsegmentation
uv run seg3d-fuse \
  --dataset-root <STAGE> --segments 000-130 \
  --edges-dirs /home/ai/seg3d_out/edges_<DS> \
  --surface-mesh-dirs /mnt/d/a123-data/Abschnitt_1/surface_mesh_s9/branch_000 \   # A1 only
  --guardrail-masks /home/ai/seg3d_out/guardrails_<DS> \
  --signs-masks /home/ai/seg3d_out/signs_<DS> \
  --ground-masks /home/ai/seg3d_out/ground_<DS> \
  --lines-source clusters --las-split instance --render \
  --out /home/ai/seg3d_out/las_<DS>

~1–5 min/segment. Missing inputs skip their stage with a WARNING and still produce output — so grep every log for WARN and check run_summary.json skips per segment rather than trusting exit codes. Validate a sample with laspy: expect LAS 1.4, PDRF 7, VLR 2112 present, and classification codes from {1, 2, 5, 64, 65, 66, 67, 68, 69, 70, 71}.

Next steps, in order

  1. Re-pull every repo above; re-run uv sync. Confirm revs still match or note the drift.
  2. Get Miro's decision on the two blockers: (a) generate step-9 surface meshes for all A1 segments, or ship A1 with edge-corridor-only pavement? (b) branch_001 without asphalt edges (8 of 11 segments), or wait for a run7 axis?
  3. Build full staging trees for the three datasets (all segments). Verify counts: 131 / 11 / 143.
  4. Run step-5 inference for all A1 tiles (fast, GPU) → step-6c clustering for all 285.
  5. Run ground, signs, guardrails, edges. These are independent — parallelise across datasets, but cap concurrency: edges peaks ~6 GB RSS, fusion similar. 24 cores / 94 GB says 4–6 concurrent processes, not 24.
  6. Checkpoint per batch of ~10 segments. Write a per-batch manifest of what completed, so a crash does not restart the sweep. Nothing here has a native resume flag except mask clustering's --skip-existing.
  7. Fuse, validate a random sample of ~10 LAS with laspy, then assemble the delivery with a README following the pilot's format.
  8. Ask before copying to N: the full sweep is roughly 20–35 GB of LAS + las_split, and N: is a Google Drive mount — that is a large sync. Stage on /home/ai and confirm the destination with Miro.

Risks & open questions

Details

Suggested skills & working style
  • battlebox-ssh — connection contract. Always the heredoc form (ssh battlebox "bash -s" <<'EOF' … EOF); inline bash -c "…" gets mangled by Windows cmd. scp lands in C:\Users\ai\, not WSL.
  • Fan the five detector stages out as parallel subagents, one per stage, as the pilot did — each stage is independent given a staged tree. The pilot's workflow scripts are on the orchestrating machine under ~/.claude/projects/-home-miro-dev-3dai-iolabs-pointcloud-3dsegmentation/<session>/workflows/scripts/ (seg3d-detect-260807-*.js, seg3d-a1branch-260807-*.js) and are worth reading as templates.
  • Miro wants extremely concise reporting. Lead with what shipped and what broke.
Pilot results, for comparison
CloudPointsClasses
A1 b000 segment_0652.12 Mground, asphalt, solid, dashed, guardrail, tree
A1 b000 segment_0701.54 Msame
A1 b001 segment_0052.38 Msame
A4_5 segment_0401.52 Msame + delineator
A4_5 segment_1000.57 Mground, asphalt, solid, dashed, guardrail

LAS 55–86 MB each; las_split/ 15–42 files each. Use these as the sanity yardstick for the sweep: a segment producing a 5 MB LAS or 2 classes deserves inspection.

Reference docs
  • /home/ai/dev/3dai.iolabs.pointcloud.3dsegmentation/README.md — fusion CLI, class table, config keys.
  • …/docs/recap_annotation.md — LAS format decision, ReCap workflow, N: folder convention, round-trip import.
  • …/docs/point_masks_spec.md — the point_masks.npz contract every detector writes.
  • Pilot delivery README: /mnt/d/temp/recap_260807/README.md.