Post-Mortem — Elevation Marker / Dimension Pipeline Refactor¶
Session date: 2026-05-05
Branch: main
Outcome: Elevation-marker stack on TB11 unified through dd-elev-flush; snake-tongue, duplicate TOC, WC/RL overlap, tiny right-side text, and \X literal-rendering all fixed.
Context¶
We started this session debugging a single visible defect on the right-hand elevation stack (duplicate TOC at panel top, WC competing with RL) and ended up rewriting the marker engine. By the end:
TOC, RL, FS, WC, DL, PL, PP, BP are unified through one panel-scoped accumulator (
dd-r-acc/dd-l-acc) and one staggering pass (dd-elev-flush).dim1is no longer used to draw individual elevation markers —dd-marker-l/dd-marker-rentmakethe SOLID arrow + horizontal + diagonal + through-line +INSERT ELEV+ MTEXT directly. This eliminated the “snake-tongue” artifact (dim1extends an arrow tail past the ELEV block when defpoints are coincident) and the “\X showing literally” artifact (\\Xis a dim-text-only code; plain MTEXT needs\\P).TOC uses the same
dd-sfx-formatted MTEXT as every other marker — no bespokeentmake TEXTfor the “T.O.C.” label any more.
The reason the work felt like weeks of churn (with dimensions appearing to be drawn 6×) is documented below, along with a punch list for the next round.
Why it took so long — root causes¶
These are not excuses, they are the things that should be fixed so the next change is fast.
Three different load paths with inconsistent semantics.
csv.lsp(production) loads modules by short name via SFSP.cv-auto-draw.lsp(headless test) loads by absolute path fromcv-base.cv-gui-draw.lsp(GUI test) was loading by short name — same as production — which meant the GUI silently picked up an olderdrawdim.lspfrom the AutoCAD support path instead of the file I was editing. Fix landed: changedcv-gui-draw.lspto absolute-path loading, matchingcv-auto-draw.lsp.Stale
acad-console.logmasquerading as fresh output. I burned ~30 min thinking my(princ)diagnostics weren’t firing because the file I was reading was from yesterday. The actual current log is per-session atreports/auto-test/CSB001_<hash>.log. The legacyacad-console.loglooks alive (right name, right location) but isn’t updated by the currentRun-ParityTest.ps1.Hidden cross-pass state.
elevmrkrwas called once per multi-itemdrawdiminvocation. Each call resetdd-cy-l/dd-cy-rto-99999at function entry, which meant items from different passes (FS/RL on the feature pass, WC on the connection pass, RL fallback on the perimeter pass) couldn’t see each other’s positions — they overlapped. The visual symptom was “RL on perimeter_dim landing within 1″ of WC on connections_dim”. This was invisible until you trace it; the comment “reset each draw” suggested it was correct.Nested
defunshadowing.drawdim.lspdeclares(defun basedim …),(defun drwbas …),(defun elevmrkr …)inside its own body. Standalonebasedim.lsp,drwbas.lsp,elevmrkr.lspexist in the same directory, are loaded bycsv.prj, but their definitions are silently overwritten bydrawdim’s nested versions on every(drawdim …)call. New behavior added to standalones is dead code; new behavior added to nested versions is invisible to anyone reading the standalone files. This is bug #1 cause of “I can’t find where this is drawn”.dim1does more than you think. WithDIMASSOC=0+dimblk1=elev+ arg1/arg2 0.001 apart,dim1emits a dim line, an arrowhead, a backstop, and an extension that overshoots leftward when the marker is staggered. The “snake-tongue” through the circle was that overshoot. None of it shows up unless the stagger is large enough to push the overshoot past the diagonal leader endpoint — which is why FS (no stagger) looked fine and the cluster around y=274 did not.The “drawn 6 times” feeling is real but mislabeled.
finpan.lspcallsdrawdimsix times — once per layer group (mpvar,fhvar/fvvar,sdvar/wcvar,bpvar/ppvar,tpvar/lbvar, then a full perimeter sweep). Each call clears no global state; the global accumulatorxNlstlists chain across calls. Combined with theelevmrkrreset bug, the same elevation could be staggered into existence multiple times on different layers.x32↔x64auto-sync.Run-ParityTest.ps1copies x64 → x32 when x64 is newer. This works, but I edited x32 first and burned a cycle when the test still loaded the (older) x64 copy. The canonical source ISsrc/x64/TB11-01x64/; x32 is a derived artifact for the production VM.
Where dimensions are made — current map¶
Dimension-emitting code, by file (canonical source: src/x64/TB11-01x64/):
File |
Lines |
Role |
What it emits |
|---|---|---|---|
|
~1100 |
Panel geometry |
Polylines, 3D solids, RL DASHED line, viewport. No dim primitives. |
|
2783 |
The dim engine |
All dimension and elevation primitives. 58 |
|
825 |
Orchestrator |
Calls |
|
– |
Wrapper |
|
|
– |
Dead |
Loaded but the nested |
|
– |
Dead |
Same — overwritten by nested copy. |
|
– |
Dead |
Same. The interesting recent fixes (RL L/R split, FS/RL/DL/PL routing, the snake-tongue bypass) live in |
|
– |
Duplicate routing |
Mirrors |
|
– |
Data reader |
Decodes the NOD XRecord (compact / source-mode formats). Sets |
|
– |
Defaults |
Fills |
|
– |
Decoders |
Compact-format and legacy-VLX decoders called from |
Feature files ( |
– |
Geometry |
Confirmed by grep: none of them emit dim primitives. They draw 3D solids / hatches / block geometry only; their dimensioning is handled by the |
Inside drawdim.lsp, the elevation-marker call graph is now:
(elevmrkr) ; called from drawdim's multi-item branch
├── PP/BP from ypplst/ybplst → cons onto dd-l-acc
├── PP-conflict-WC y1lst → cons onto dd-r-acc (connections_dim)
└── y2lst with PP/BP-proximity check → cons onto dd-r-acc or dd-l-acc
(dd-elev-flush) ; called once from finpan after the 6 drawdim passes
├── append FS/RL_R/DL/PL → dd-r-acc
├── append RL_L → dd-l-acc
├── append T.O.C. (cadr p3) → dd-r-acc
├── filter dd-r-acc: drop entries equal to panel-bottom
├── (dd-elev-multi dd-l-acc 'dd-cy-l "L") → dd-marker-l per item
├── (dd-elev-multi dd-r-acc 'dd-cy-r "R") → dd-marker-r per item
├── panel-bottom-L via dd-marker-l
├── DL/SD/FF (left) via dd-draw-elev → dd-marker-l
└── panel-bottom-R via direct csv_dim1 ← still bespoke; see punch list
Refactoring inventory (what we factored out)¶
drawdim.lsp already had ~10 dd-* helpers; this session added or modified:
dd-elev-flush(new) — single drain function, called fromfinpanafter the sixdrawdimpasses. Encapsulates panel-top/bottom filter, the unified left+rightdd-elev-multicalls, and the structural-marker draws.dd-marker-l/dd-marker-r(rewritten) — replacedcsv_dim1 + dd-fixmtext-xwith explicitentmakeof SOLID + 3 LINEs +INSERT ELEV+ MTEXT. Eliminates the snake-tongue artifact entirely.dd-sfx(modified) — switched the line separator from\X(dim-only) to\P(universal MTEXT) so the entmake’d labels render correctly. The\H0.7x;height directive still applies to the suffix.y1lstpanel-top/bottom filter — added drop step before accumulating todd-r-accso a PP at panel-top can no longer duplicate the TOC.y2lstascending sort — added before the conflict-check loop so a smaller WC can’t be staggered above a larger one drawn earlier in the list.dd-elev-flushsaves and restoresDIMASSOC,dimblk1/2,dimsd1/2,dimse1/2,dimtad,dimsahso it works in eithercsv_headless=T(DIMASSOC=0) or GUI (DIMASSOC=1) mode.cv-gui-draw.lsp— load loop changed from(load m)(short name, SFSP-resolved) to(load (strcat cv-base m ".lsp"))(absolute path) — matchescv-auto-draw.lsp.finpan.lsp— resetsdd-l-acc/dd-r-accalongside the existingdd-fs-elevs/dd-rl-elevs-*resets, and calls(dd-elev-flush)after the lastdrawdimpass.
Existing helpers that this work depends on (no change needed):
dd-stagger,dd-elev-multi,dd-elev-dim,dd-build-elevs,dd-accum,dd-dedupe-tier,dd-plate-dim,dd-fixmtext,dd-fixmtext-x,dd-draw-elev.
What’s still messy — punch list¶
These are the things that would make the next change fast.
Delete or merge the standalone shadow modules.
basedim.lsp,drwbas.lsp,elevmrkr.lspare dead. Either delete them and remove fromcsv.prj, or extract the nested versions back to standalone and remove the nesting. The current state actively misleads code readers.Issue #151 (DRY) and #152 (global pollution) cover the two next moves. From
gh issue list:#151 [Refactor] drawdim.lsp: ~275 lines of saveable duplication (DRY violations)#152 [Refactor] drawdim.lsp: global variable pollution (*dd-* constants + accumulator lists + dd-el-* state)These are the right framing — link them from the punch list when the next branch starts.
Single-item
condbranches indrawdim.lsp. ~20 sections (mpvar,rovar,wdvar,drvar,dlvar,fsvar,plvar,llvar,tsvar,ssvar,sbvar,rbvar,tpvar,lbvar,mivar,ch1-ch4,wcvar,sdvar,bpvar,ppvar,fhvar,fvvar) each get their own ~50-150 line block with inlinecsv_dim1/entmakecalls. This is the bulk of the 2783-line file. Candidate: per-feature handler files dispatched by table lookup.drawdimlst.lspis a near-duplicate ofdrawdim’s multi-item branch. Onlynbblock.lspcalls it. Either fold its sole caller intodrawdimor delete it.Six
drawdimcalls infinpan.lspcould be one. The layer routing is(setvar "clayer" …)before each call; the work could be one combined pass that routes per-section to the correct layer. Worth doing only after #3 above.Panel-bottom-R (in
dd-elev-flush) still uses bespokecsv_dim1 + dd-fixmtext. Everything else routes through the accumulator anddd-marker-r. This is a one-off because panel-bottom-R lives atcadr p1rather than at a feature elevation, but it could be added todd-r-accwith a “PB” suffix and letdd-elev-multidraw it.
Diagnostic-print audit¶
The source is noisier than it needs to be for a shipped build. Counts (Grep):
Tag |
Count |
Where |
Status |
|---|---|---|---|
|
~15 |
|
Should be removed — these were debug prints from this session that were partially reverted but the entries in the |
|
6 |
|
Useful — feature routing |
|
7 |
|
Useful as gated tracing — would prefer behind |
|
82 |
|
Excessive. Every feature entry/exit. Behind a |
|
9 |
|
Useful — entity counts per layer |
|
many |
|
Looks debug-only; fires per-overlap-hit |
|
many |
|
Useful for parsing-bug diagnosis but very verbose. Gate behind |
|
0 |
– |
Good — none left from this session. |
Recommended cleanup commit: introduce a csv_trace global (default nil) and gate the [DP-FEAT], [GREEN-DIAG], [COMPACT-DUMP], [FS-DECODE] blocks behind it. Keep [FP-2-DIAG] and [CV-AUTO]/[GUI] entry/exit lines — those are checkpoints, not traces.
Documentation status¶
What’s documented in source headers (good):
drawdim.lsp— 143-line header explaining nlst routing, x/y accumulator tiers, single-vs-multi-item handling.finpan.lsp— 76-line header listing the seven phases.drawpan.lsp— UPDATE block read condition, Bug 49 root cause.dd-elev-flushhas a comment block explaining “why a separate flush” — good.
What is not documented in headers (only in commit messages):
The panel-scoped accumulator architecture and why
dd-l-acc/dd-r-accsurvive acrossdrawdimcalls.The snake-tongue /
dim1-bypass rationale indd-marker-l/r.The
\X→\Pswitch indd-sfx.The RL left/right split (
dd-rl-elevs-lvsdd-rl-elevs-r).The
cv-gui-drawabsolute-path load fix.The standalone-module-is-dead policy.
Recommended doc commit: add a “Pipeline overview — Apr 2026 refactor” section near the top of drawdim.lsp cross-referencing finpan.lsp and cv-gui-draw.lsp. One paragraph each.
Deprecated code & risk artefacts (where they live)¶
docs/source/modernization-2026/05-risk-register.md— present.docs/source/modernization-2026/32-tb11-bug-tracker.md— present.docs-developer/grg-system.md— untracked; the GRG (grid reference guide) overlay system the user generated this session.GitHub issues:
#150(weldconn lesson learned),#151(DRY),#152(globals). All open before this post-mortem.
Verification¶
Both gates that matter:
Headless DXF parity (post-refactor):
powershell -ExecutionPolicy Bypass -File scripts/Run-ParityTest.ps1— current state isTest=391, Golden=350with the residual delta on window-cutout layer drift (feature_dimvsperimeter_dim) — pre-existing and out of scope of this refactor.GUI visual (per the standing memory rule): user runs
(c:cv-gui-draw)and visually confirms TOC + cluster + leaders. Current state per user’s own confirmation: ✅.
Going forward, the GUI test now loads from the same absolute path as headless, so DXF parity and GUI parity should agree.
Recommended next session sequence¶
If the next session picks this up, the cheapest wins in order:
Diagnostic-cleanup commit — add
csv_tracegate, silence[DP-FEAT]/[GREEN-DIAG]/[COMPACT-DUMP]by default. (~30 min, no risk)Header-doc commit — write the “Pipeline overview” paragraph in
drawdim.lsp. (~15 min)Delete
basedim.lsp/drwbas.lsp/elevmrkr.lspstandalone files; remove fromcsv.prj. (~30 min, run parity test after — if it passes, ship.)Address
#151/#152per the issue threads.Reconsider whether
drawdim.lspshould be split intodrawdim-core.lsp+ per-feature handler files.
Cross-references¶
DFMEA new failure modes added (doc 31, §9): NEW-DFMEA-A through NEW-DFMEA-F (see below).
Risk Register entries added (doc 05): R-LOAD-DIVERGE, R-STALE-LOG, R-NESTED-SHADOW, R-DIM1-OVERSHOOT, R-CROSS-PASS-STATE.
Validation Plan additions (doc 06): V-LOAD-PATH-PARITY, V-LOG-FRESHNESS, V-NESTED-DEFUN-AUDIT.
GH issues closed: any session-specific defect issues for snake-tongue / TOC duplicate / WC-RL overlap /
\Xliteral.GH issues opened: load-path divergence, stale log capture, nested-defun shadowing audit, diagnostic-print gating, dead-module deletion.