Files
jesse 6e50f9718d
CI / quality (push) Failing after 1m9s
Aux-lane suppression + PA geometry fixes; probe/verify tooling
MapBuilder.luau
  - Suppress the aux lane across samples 525-550 (AUX_SUPPRESS_FROM/TO),
    honoring the user's hand deletion in Studio so a rebuild no longer
    regenerates it.
  - Smoothstep the width ramp over AUX_BLEND=5 samples on each side. A hard
    cut produced a 26-stud lateral fence jog; the blend removes it.
  - DO NOT remove this suppression. Re-enabling it resurrects geometry the
    user deleted by hand.

rokit.toml
  - rojo 7.5.1 -> 7.7.0 to match the auto-updating Studio plugin.
    Server older than plugin fails the handshake with
    "attempt to index number with 'protocolVersion'".

tools/
  - 34 read-only probe/verify/capture scripts used to measure the datamodel
    instead of reasoning from source.

Verified-good baseline at this commit:
  total parts       16788
  PAWestSlip        60 segs, 34.4 deg total bend, worst 1.4, 0 kinks >5
  PAEastSlip        60 segs, 30.7 deg total bend, worst 1.3, 0 kinks >5
  AuxLane parts     30 (samples 525-550 deliberately absent)
  barrier coverage  823 samples, 0 unguarded
  fence jogs >10st  0
2026-08-06 08:27:47 -04:00

124 lines
4.2 KiB
Luau

-- Is the fused aux run genuine continuous pavement (a weaving section), or
-- overlapping/duplicated parts? Checks per-sample coverage, duplicate indices,
-- and the width profile across both mouths.
local ws = game:GetService("Workspace")
local map = ws:FindFirstChild("Map")
if not map then return "NOMAP" end
local seen, dupes = {}, 0
local byIdx = {}
for _, d in ipairs(map:GetDescendants()) do
if d:IsA("BasePart") then
local i = tonumber(d.Name:match("^AuxLane_(%d+)$") or "")
if i then
if seen[i] then dupes += 1 end
seen[i] = true
byIdx[i] = d
end
end
end
local out = {}
local function say(s) table.insert(out, s) end
say(("duplicate AuxLane indices: %d -> %s"):format(dupes, dupes == 0 and "PASS" or "FAIL"))
-- Walk 505..570 and report which samples have lane and how wide.
local missing = {}
local prof = {}
for i = 505, 570 do
local p = byIdx[i]
if p then
table.insert(prof, ("%d:%.0f"):format(i, p.Size.X))
else
table.insert(missing, i)
table.insert(prof, ("%d:--"):format(i))
end
end
say("width profile (sample:width, -- = no lane):")
say(" " .. table.concat(prof, " "))
say("gaps in 505..570: " .. (#missing > 0 and table.concat(missing, ",") or "none"))
-- The only gaps should be the ramp noses (the ramp deck covers those).
-- Derive coverage from the ACTUAL ramp deck parts, but test the point where
-- the AUX LANE would sit (deck edge + half lane width) -- NOT the mainline
-- centreline. Testing proximity to the centreline marked 26 samples as "nose"
-- because the ramp spirals past them at other heights, which would mask any
-- real hole and make this check vacuously pass. The nose test must also match
-- in HEIGHT: a ramp 50 studs below the deck does not cover the deck's edge.
local samples = require(game:GetService("ReplicatedStorage")
:WaitForChild("Shared"):WaitForChild("Circuit")).samples
local decks = {}
for _, d in ipairs(map:GetDescendants()) do
if d:IsA("BasePart") and d.Name:match("Slip_Deck_") then
table.insert(decks, d)
end
end
local HALF, AUXW, SIDE = 30, 26, -1
local function auxPointAt(i)
local n = #samples
local a = samples[((i - 1) % n) + 1]
local b = samples[(i % n) + 1]
local f = b.pos - a.pos
f = Vector3.new(f.X, 0, f.Z).Unit
local r = Vector3.new(-f.Z, 0, f.X)
return a.pos + r * (SIDE * (HALF + AUXW / 2)), a.pos.Y
end
local NOSE, noseWhy = {}, {}
for _, i in ipairs(missing) do
local p, deckY = auxPointAt(i)
for _, d in ipairs(decks) do
local flat = (Vector3.new(d.Position.X, 0, d.Position.Z)
- Vector3.new(p.X, 0, p.Z)).Magnitude
local dy = math.abs(d.Position.Y - deckY)
-- Must overlap the lane footprint horizontally AND be at deck level.
if flat <= AUXW and dy <= 6 then
NOSE[i] = true
noseWhy[i] = ("%s flat=%.0f dy=%.0f"):format(d.Name, flat, dy)
break
end
end
end
local noseList = {}
for i in pairs(NOSE) do table.insert(noseList, i) end
table.sort(noseList)
say(("ramp-nose-covered gaps (%d): %s"):format(#noseList, table.concat(noseList, ",")))
for _, i in ipairs(noseList) do
say((" %d covered by %s"):format(i, noseWhy[i]))
end
local lo, hi = math.huge, -1
for k in pairs(byIdx) do
lo = math.min(lo, k); hi = math.max(hi, k)
end
local bad = {}
for _, i in ipairs(missing) do
-- Samples outside the lane's own extent are not holes; only flag gaps that
-- sit strictly between the first and last aux segment.
if i > lo and i < hi and not NOSE[i] then table.insert(bad, i) end
end
say(("lane run extent: %d..%d"):format(lo, hi))
say(("non-nose gaps inside the lane run: %s -> %s"):format(#bad > 0 and table.concat(bad, ",") or "none",
#bad == 0 and "PASS (only ramp noses uncovered)" or "FAIL (hole in pavement)"))
-- Between the two mouths, is the lane continuously full width (a real weaving
-- section) rather than tapering out and back in?
local weaveMin = math.huge
local weaveTested = 0
for i = 528, 548 do
local p = byIdx[i]
if p then
weaveTested += 1
weaveMin = math.min(weaveMin, p.Size.X)
end
end
if weaveTested == 0 then
say("weave section: FAIL nothing tested")
else
say(("weave section 528..548: %d segs, min width %.1f -> %s")
:format(weaveTested, weaveMin,
weaveMin >= 24 and "PASS (continuous full-width weaving lane)" or "TAPERS"))
end
return table.concat(out, "\n")