Two review findings, both root causes.
1. `_sync_header_to_dimstyle` copied the DIMSTYLE entry's ezdxf-internal
arrowhead spelling (OBLIQUE) into the DXF-facing $DIMBLK/$DIMBLK1/$DIMBLK2
header, so the saved file named a block it did not hold (the blocks are
_OBLIQUE / _DOT) and `system_get_variable("DIMBLK")` answered "OBLIQUE"
headlessly against "_OBLIQUE" on COM and in the same engine's own
`dimstyle_list` row. The header write now goes through
`reported_arrowhead` — the same read-side rule `_dimstyle_value` applies.
Pinned by header, `system_get_variable`, save/reopen (`ezdxf.readfile`
header equals the block the file holds) and the Restore-to-Standard
empty-string case in `test_builtin_arrowheads_render_save_and_report_one_spelling`.
2. The `mleaderstyle` capability refusal rested on a false engine boundary.
acax25enu.tlb (AutoCAD 2026) exposes IAcadMLeaderStyle with Name,
ArrowSize, LandingGap, TextHeight, TextStyle, ... — exactly what
`Dictionaries.Item("ACAD_MLEADERSTYLE").Item(i)` returns — and
IAcadDictionary.AddObject(keyword, "AcDbMLeaderStyle") creates one.
COM `mleaderstyle_list` now reads the four values off those objects
(`values_available: True`); COM `mleaderstyle_create` validates every value
(`validate_mleaderstyle`, shared with ezdxf), checks the clash and the text
style before AddObject, then writes the four properties. The contract
method is `@abstractmethod` again, the key is gone from both capability
maps, the fake ActiveX surface models the real call shape, and the spec
(§4.2, §8.1, §9) states the ActiveX route.
Live AutoCAD was not running on this machine (GetActiveObject: operation
unavailable; no acad.exe), so the COM route is type-library-verified and
fake-tested, not executed live — scripts/smoke_settings_com.py (Task 24)
owns that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
23 KiB
v1.6 Track E — AutoCAD settings and environment
Date: 2026-09-16 · Status: design approved in conversation; plan at
docs/superpowers/plans/2026-09-16-v1.6-settings-implementation.md · Parent:
2026-09-15-v1.6-roadmap-design.md (track E) · Baseline: main after
Track A (166 tools, 8 resources)
1. Summary
v1.5 lets a model read and write raw system variables and a friendly
drawing_settings facade, and nothing else about the environment a drawing
lives in: no dimension/text/leader styles (every dimension inherits
Standard), no page setup (a PDF is whatever the renderer's default is), no
templates beyond a layer list, one document at a time, no named views, UCS or
layer states, and no way to ask AutoCAD itself anything (preferences, document
properties, a point picked on screen).
This track adds those as 38 tools in three new sections, on both engines
wherever a headless file can hold the result, with an honest @capability
refusal where only a live AutoCAD can (preferences, document properties'
summary fields, launching, prompting the operator). Authored standards data —
ISO-25 and ANSI dimension styles, text styles, five drawing templates built by
the server's own tools — is pinned by tests. drawing_apply_standard makes the
common case one call.
The user's principle for this track (2026-09-16): tool count is not the
goal. Every tool here has a rich alias record, a docstring that names its
refusals, a pack, and a place in the lean profile decision; a near-duplicate
is folded into an action enum rather than registered twice.
Tool budget: +38 → 204 registered. Packs: styles, settings, page setup and
templates are core (drafting essentials); the environment and interactive
groups form the new settings pack.
2. Scope
In: sections 4–10. Track A hardening (wave 0 of this track's plan): the
review findings from Track A that are real defects — entity_create_block_ref
accepts an undefined block, block_explode drops ATTRIB text, pid_from_spec
lets NaN reach the drawing and does not roll back on BaseException, signal
lines receive a fabricated line number and label, a bubble's exit point ignores
the first waypoint, block_insert coerces attribute values with str(),
block_define writes NaN base points and dangling layer references — each a
fix with a test; the rest of the ~80 findings are filed in
docs/analysis/track-a-review-backlog.md with a one-line disposition each.
Out of 1.6 (recorded): TABLESTYLE objects (ezdxf cannot author them),
AutoCAD-native LAYERSTATE entries (our layer states are portable XRECORDs,
see §8.3), CUI/ribbon/workspace changes, plotter (.pc3) authoring, annotative
object creation (scales are settable; entities stay non-annotative), ANSI
inch templates (ANSI templates here are metric ANSI: ansi dimstyle with
decimal inches is a 1.7 item once an inch drawing benchmark exists).
3. Architecture
engineering/standards/
__init__.py
dimstyles.py ISO_25, ANSI presets as data; DIM_VARIABLE_WHITELIST; apply/describe
textstyles.py ISOCP, ISOCPEUR, ARIAL, ROMANS presets (font file, width, oblique)
mleaderstyles.py ISO / ANSI leader presets
sysvars.py SYSVAR_CATALOG (~60 entries: name, type, range, meaning, engines)
papers.py PAPER_SIZES (ISO A0–A4, ANSI A–E), CTB_CATALOG, scale vocabulary
templates.py TEMPLATE_CATALOG (5 bundled templates: what each contains)
engineering/environment/
layer_states.py portable layer-state snapshot <-> XRECORD codec
templates/ built artefacts: <name>.dxf (headless) + <name>.dwt (COM), README
scripts/build_templates.py, scripts/smoke_settings_com.py
backends/contracts/styles.py (new) dimstyle_*, textstyle_*, mleaderstyle_*
backends/contracts/pagesetup.py (new) page_setup_*, plot_style_list, batch_plot, template_save
backends/contracts/environment.py (new) document_*, layer_state_*, view_named_*, ucs_*,
preferences_*, drawing_properties_*, system_launch,
user_pick_point, user_select, system_prompt_message
backends/contracts/settings.py (edit) drawing_settings facade grows
server.py SECTION 18 Styles (10) · SECTION 19 Page setup & templates (6) · SECTION 20 Environment (22)
Every new contract method is @abstractmethod unless the table in §9 marks it
@capability. Both backends implement every method; COM paths are fake-tested
and executed once live by scripts/smoke_settings_com.py.
4. Styles (SECTION 18, 10 tools, pack core)
4.1 Authored presets (engineering/standards/dimstyles.py)
| Variable | iso-25 |
ansi (metric) |
Meaning |
|---|---|---|---|
| DIMTXT | 2.5 | 3.0 | text height (mm) |
| DIMASZ | 2.5 | 3.0 | arrow size |
| DIMEXE | 1.25 | 1.5 | extension beyond dim line |
| DIMEXO | 0.625 | 1.5 | extension line offset |
| DIMGAP | 0.625 | 1.0 | text gap |
| DIMTAD | 1 (above) | 0 (centred) | text vertical placement |
| DIMTIH / DIMTOH | 0 / 0 (aligned) | 1 / 1 (horizontal) | text orientation |
| DIMDEC | 2 | 2 | linear decimals |
| DIMDSEP | , |
. |
decimal separator |
| DIMLUNIT | 2 (decimal) | 2 | linear unit format |
| DIMZIN | 8 (trailing zeros suppressed) | 8 | zero suppression |
| DIMBLK | "" (closed filled) |
"" |
arrowhead |
| DIMTXSTY | ISOCP |
ROMANS |
text style (created if missing) |
| DIMLWD / DIMLWE | -2 (ByBlock) | -2 | line/extension lineweight |
| DIMSCALE | 1.0 | 1.0 | overall scale |
Source: ISO 129-1:2018 (text and arrow sizes for the 2.5 mm lettering row of
ISO 3098-1) and ASME Y14.2-2014 (line conventions and lettering: 3 mm / .12 in
lettering, .125 in arrowheads), metric values.
A provenance test asserts each preset's values against this table by name.
DIM_VARIABLE_WHITELIST = the 15 variables above plus DIMTOL/DIMTP/DIMTM/
DIMTOLJ/DIMTFAC/DIMLFAC/DIMRND/DIMATFIT/DIMTMOVE/DIMCLRD/DIMCLRE/DIMCLRT/
DIMSAH/DIMBLK1/DIMBLK2/DIMCEN — dimstyle_modify refuses anything else by
name.
4.2 Tools
dimstyle_list()→[{name, current, values: {15 whitelist vars}}]dimstyle_create(name, preset: "iso-25"|"ansi"|None = None, overrides: dict|None = None, set_current=False)— preset values then overrides; unknown override key or a value outside the variable's range (DIMDEC0–8, sizes > 0, DIMTAD 0–4, DIMDSEP one char) →ValueErrornaming it, nothing written; name clash →ValueError(usedimstyle_modify).dimstyle_modify(name, overrides)—changedreports only variables that actually moved (thehatch_editrule). Dimensions already using the style are listed indimensions_using_stylewithrerender_required: trueon the headless engine: AutoCAD re-renders them on the next regen, but a headless DIMENSION carries its rendered block, so the drawing shows the old style until the dimension is redrawn — stated rather than silent.dimstyle_set_current(name)—$DIMSTYLE; the dimension tools then use it (the existing per-dimension override path stays as is).textstyle_list(),textstyle_create(name, font: "isocp.shx"|"isocpeur.ttf"|"arial.ttf"|"romans.shx"|<file>, height=0.0, width_factor=1.0, oblique_deg=0.0, set_current=False),textstyle_set_current(name)—$TEXTSTYLE; a font file that is neither a bundled preset nor present in the engine's font path is written but reportedfont_resolved: false(AutoCAD substitutes at open; the headless renderer falls back), never refused, because DXF only stores the name.mleaderstyle_list(),mleaderstyle_create(name, preset: "iso"|"ansi", overrides)— arrow size, landing gap, text style, text height, both engines: ezdxf ondoc.mleader_styles; COM through theACAD_MLEADERSTYLEdictionary, whose items areIAcadMLeaderStyleobjects (ArrowSize/LandingGap/TextHeightin drawing units,TextStylea name — verified against the AutoCAD 2026 type library), created withDictionaries.Item("ACAD_MLEADERSTYLE").AddObject(name, "AcDbMLeaderStyle")followed by the property writes. ActiveX has noMLeaderStylescollection property, which is not an engine boundary — nomleaderstylecapability key exists.drawing_apply_standard(standard: "iso"|"ansi", layers: bool = True, units: bool = True)— one call: the dimstyle preset (created and set current), its text style,drawing_settings(units mm, decimal linear and angular units, LTSCALE 1, DIMSCALE 1; the decimal separator comes from the preset) and, withlayers, themechlayer set for both standards (ANSI layer naming is company-specific; the set reuses the mech names with ISO 128 lineweights — only styles and the separator differ between the two). Returns, per item, whether it was created or was already present.
5. Settings (SECTION 20 shares these; 3 tools + facade growth)
drawing_settingsfacade gains:limits([[xmin,ymin],[xmax,ymax]]→$LIMMIN/$LIMMAX),grid(bool → GRIDMODE) andgrid_spacing,snap(bool) andsnap_spacing,ortho(bool),polar(bool +polar_angle),psltscale(0/1),annotation_scale("1:50"→ CANNOSCALE string + CANNOSCALEVALUE),linear_units(decimal|engineering|architectural|fractional|scientific→ LUNITS 2/3/4/5/1),angular_units(degrees|dms|grads|radians|surveyor→ AUNITS),dimstyleandtextstyle(current names). Ranges enforced as today (_RANGES), unknown keys refused, and the read side reports every key.system_variable_describe(name=None, search=None)— fromSYSVAR_CATALOG:{name, type, range|enum, default, meaning, saved_in: drawing|registry|not_saved, engines: {ezdxf: bool, com: bool}, friendly_key: str|None};searchmatches names and meanings; unknown name →known: falseplus the nearest names, never an error. The catalog is authored (~60: the DIM* whitelist, LTSCALE, PSLTSCALE, INSUNITS, LUNITS, LUPREC, AUNITS, AUPREC, LIMMIN/LIMMAX, GRIDMODE/GRIDUNIT, SNAPMODE/SNAPUNIT, ORTHOMODE, POLARMODE/POLARANG, OSMODE, CANNOSCALE, TEXTSIZE, TEXTSTYLE, DIMSTYLE, CLAYER, CELTYPE, CECOLOR, CELWEIGHT, PDMODE/PDSIZE, FILLETRAD, MIRRTEXT, ATTREQ/ATTDIA, TILEMODE, CTAB, DWGNAME/DWGPREFIX, SAVETIME, FILEDIA, CMDECHO, HIGHLIGHT, REGENMODE, UCSNAME, VIEWCTR/VIEWSIZE, ANGBASE/ANGDIR, PLINEWID, HPNAME/HPSCALE/HPANG, LWDISPLAY, XREFCTL, PROXYSHOW, ISOLINES, FACETRES, DISPSILH).system_set_variablegains a soft check: a name in the catalog with a range refuses out-of-range values before writing; a name not in the catalog behaves as today.drawing_properties_get()/drawing_properties_set(title=None, subject=None, author=None, keywords=None, comments=None, custom: dict|None=None)— COM:SummaryInfo(+AddCustomInfo/SetCustomByKey); ezdxf:customthroughdoc.header.custom_vars($CUSTOMPROPERTYTAG/$CUSTOMPROPERTYpairs, both engines) and the five summary fields refused withcapability: "dwgprops"when any of them is passed headlessly (getreturns them asnullwithsummary_available: false).
6. Page setup, plotting, templates (SECTION 19, 6 tools, pack core)
page_setup_list(layout=None)→ per layout:{paper: "ISO_A3", size_mm: [420,297], orientation, plot_style, scale, plot_area, device, margins_mm}— read from LAYOUT plot settings (ezdxfdxf_layout.dxf.*; COMLayout.*).page_setup_apply(layout, paper: "ISO_A4"|"ISO_A3"|"ISO_A2"|"ISO_A1"|"ISO_A0"|"ANSI_A"|…|"ANSI_E", orientation: "landscape"|"portrait" = "landscape", plot_style: str = "monochrome.ctb", scale: "fit"|"1:1"|"1:2"|"1:5"|"1:10"|"1:20"|"1:50"|"1:100"|"2:1"|"5:1"|"10:1" = "fit", plot_area: "layout"|"extents" = "layout", device: str = "DWG To PDF.pc3", margins_mm=None, center=True)—PAPER_SIZESauthored (ISO 216 / ANSI Y14.1 sizes in mm),CTB_CATALOGnames the ctb files AutoCAD ships (monochrome.ctb,acad.ctb,Grayscale.ctb,Screening 100%.ctb, …); an unknown ctb is written and reportedplot_style_known: false(a missing ctb only matters at plot time). Returns the applied setup andchanged.plot_style_list()— the authored catalog plus, on COM, the files actually present inPreferences.Files.PrintStyleSheetPath(source: "catalog"|"installed").batch_plot(layouts: list[str]|None = None, output_dir: str, format: "pdf" = "pdf", file_pattern: "{drawing}-{layout}.pdf")— every named (default: every paper) layout through the existingdrawing_export_pdf(layout=)path; returns one row per layout{layout, path, bytes, mediabox_mm}wheremediabox_mmis read back from the PDF (/MediaBoxparsed from the file) so the sheet size is verified, not assumed. Model space is included only when named explicitly.drawing_template_list()→TEMPLATE_CATALOGrows{name, standard, sheet, layer_set, dimstyle, textstyle, title_block, files: {dxf, dwt}}.drawing_new(template=…)(existing tool, parameter grows): a catalog name resolves to the bundled file (templates/<name>.dxfheadless,templates/<name>.dwton COM); a path is used as today. Returnstemplate: {name|path, source: "bundled"|"path"}.drawing_template_save(path, name=None, description=None)— current drawing as a template: ezdxf writes DXF (a.dwtrequest headlessly is refused withcapability: "dwt_write"naming the DXF route); COMSaveAs(path, acTemplateDwg)and the description goes to the template's summary. Path validated for write.
Bundled templates (scripts/build_templates.py builds all five through the
server's own backend methods — layers, styles, settings, ISO 5457 frame from
titleblock_apply_iso_a3 where the sheet is A3, page setup — then saves DXF;
the .dwt twins are produced on the live machine by opening each DXF over COM
and SaveAs(..., acTemplateDwg), committed once, and a test asserts the DXF
side is byte-reproducible from the script):
| name | standard | sheet | layer set | styles | page setup |
|---|---|---|---|---|---|
iso_a3_mech |
ISO | A3 landscape | mech | ISO-25 / ISOCP | ISO_A3, monochrome.ctb, 1:1 |
iso_a1_arch |
ISO | A1 landscape | iso13567 | ISO-25 / ISOCP, CANNOSCALE 1:50 | ISO_A1, monochrome.ctb, fit |
iso_a3_pid |
ISO | A3 landscape | pid | ISO-25 / ISOCP | ISO_A3, monochrome.ctb, 1:1 |
ansi_b_mech |
ANSI | ANSI B landscape | mech | ANSI / ROMANS | ANSI_B, monochrome.ctb, 1:1 |
ansi_d_arch |
ANSI | ANSI D landscape | iso13567 | ANSI / ROMANS | ANSI_D, monochrome.ctb, fit |
7. Environment (SECTION 20, 22 tools, pack settings)
7.1 Documents (3, both engines)
document_list()→[{name, path, active, saved, entity_count}].document_activate(name_or_path)→ switches the backend's active document; every later tool call targets it. ezdxf: the backend keeps_docs: dict[key, DocState](document, path, dirty flag, undo stack, plan spec, transaction snapshot) and_docbecomes a property over the active key —drawing_new/drawing_openregister a new entry instead of replacing the only one;drawing_closeremoves the active entry and activates the most recent other one (or none). COM:Documents.Item(...).Activate().document_close(name_or_path, save=False)— refuses to discard unsaved changes unlessdiscard=True(the message names the unsaved document); closing the last document leaves the backend in the "no document" state every tool already handles.drawing_close(existing) becomes the active-document case ofdocument_close.
7.2 Layer states (4, both engines, portable)
layer_state_save(name, description=None), layer_state_restore(name, properties: list|None = None), layer_state_list(), layer_state_delete(name).
A state is the layer table snapshot {layer: {on, frozen, locked, color, linetype, lineweight, plot}} plus current_layer, stored as JSON chunks in an XRECORD under the drawing's named object dictionary ACADMCP_LAYERSTATES → <name> (ezdxf rootdict.add_new_dict / add_xrecord; COM Dictionaries.Add / AddXRecord / SetXRecordData with 1000-code chunks ≤ 255 chars). Restore applies only the requested properties (default all) and reports applied, missing_layers (in the state, not in the drawing — skipped, never created) and new_layers (in the drawing, not in the state — untouched). The docstring and system_capabilities say plainly that these states live in the file and travel with it but do not appear in AutoCAD's Layer States Manager.
7.3 Named views (3, both engines)
view_named_save(name, center=None, height=None, width=None) (defaults: the current view on COM; on ezdxf the drawing's extents), view_named_restore(name) (COM: ActiveViewport set; ezdxf: $VIEWCTR/$VIEWSIZE and the reported no-op rule of the other view tools), view_named_list(). Refuses height <= 0; a name clash replaces (replaced: true).
7.4 UCS (3, both engines)
ucs_list(), ucs_set(name, origin, x_axis, y_axis) (unit vectors normalised; non-orthogonal axes refused with the measured angle), ucs_restore(name|"world"). Tool coordinates stay WCS (the repository rule) — a UCS is stored and made current for the operator's benefit; no tool starts interpreting inputs in UCS. The docstring says so.
7.5 Application, preferences, properties, interactive (9)
system_launch(visible=True, open_path=None)— COM: connect or start the ProgID, optionally open a file; returns{launched: bool, attached: bool, version, document}; ezdxf:capability: "live_application".system_preferences_get(keys=None)/system_preferences_set(key, value)— whitelist (COMPreferences.*):OpenSave.SaveAsType(enum name ↔ value),OpenSave.AutoSaveInterval(0–600 min),OpenSave.CreateBackup,OpenSave.IncrementalSavePercent(0–100),Display.CursorSize(1–100),Drafting.AutoSnapMarkerSize(1–20),Drafting.AutoSnapTooltip,Selection.PickBoxSize(0–50),Output.DefaultPlotStyleTable,Output.DefaultOutputDevice, and read-onlyFiles.SupportPath,Files.TemplateDwgPath,Files.PrintStyleSheetPath,Files.PrinterConfigPath. Writes reportold/new; a read-only key or a value outside its range is refused by name; ezdxf:capability: "preferences".drawing_properties_get/set— §5.user_pick_point(prompt),user_select(prompt, mode: "single"|"multiple"),system_prompt_message(text)— COMUtility.GetPoint/GetEntity+SelectionSets.Add(...).SelectOnScreen/Utility.Prompt; a pick cancelled by the operator returns{cancelled: true}(not an error); a wall-clock timeout (COM_CALL_TIMEOUTapplies) reportstimed_out; ezdxf:capability: "interactive_prompt".
8. Cross-cutting
8.1 Capability keys (added to both maps)
dwgprops (ezdxf false: summary fields), dwt_write (ezdxf false),
live_application, preferences, interactive_prompt (ezdxf false).
tests/test_capability_contract.py enforces both maps. (mleaderstyle was
listed here as COM false; the ActiveX route exists — §4.2 — so there is no key.)
8.2 Packs, profiles, discovery
PACK_TOOL_NAMES["settings"] = the 22 SECTION 20 tools; SECTION 18/19 are
core. LEAN_TOOL_NAMES gains drawing_apply_standard, page_setup_apply,
batch_plot, dimstyle_set_current, textstyle_set_current (lean = 55).
Alias records for all 38 (AutoCAD commands: DIMSTYLE, DDIM, STYLE,
MLEADERSTYLE, PAGESETUP, PLOT, PUBLISH, NEW, QNEW, SAVEAS,
OPTIONS, DWGPROPS, LAYERSTATE, VIEW, UCS, UCSMAN, LIMITS,
GRID, SNAP, ORTHO, UNITS, CANNOSCALE, SYSVAR, SETVAR) and
shop-floor synonyms; the discovery golden set gains ≥ 10 queries for them
(tests/test_discovery_transform.py golden file).
8.3 Honesty rules specific to this track
- A style or setting write is reported with
changed(what moved) — re-setting a value to itself is not a change. - Page setup evidence is the PDF's own
/MediaBox, read back. - Layer states are ours, not AutoCAD's; documented in three places (tool
docstring,
system_capabilities, README). - Nothing here starts interpreting coordinates in UCS.
9. Testing and evidence
- Presets provenance:
tests/test_standards_presets.pyasserts every ISO-25 / ANSI value in §4.1 by name, every paper size against ISO 216 / ANSI Y14.1 (A0 841×1189 … A4 210×297; ANSI A 216×279 … E 864×1118), every catalog sysvar has type+meaning+engines. - Dual-engine tests for every tool (ezdxf real; COM fake-ActiveX asserting call
shapes:
DimStyles.Add,TextStyles.Add,Dictionaries.Item("ACAD_MLEADERSTYLE").AddObject(name, "AcDbMLeaderStyle"),Layout.ConfigName/…,Documents.Item().Activate,Dictionaries.Add+AddXRecord,Views.Add,UserCoordinateSystems.Add,Preferences.OpenSave.*,SummaryInfo.*,Utility.GetPoint). - Multi-document: two headless documents open, entity created in one is not in the other, activate switches, close-with-unsaved refuses, closing the last leaves "no document".
- Layer states: save → change layers → restore → table equals snapshot;
missing_layers/new_layersreported; the XRECORD survives save/reopen. - Page setup: apply ISO_A3 landscape →
batch_plot→ parsed/MediaBoxis 420×297 mm (±0.5 mm at 72 dpi units); ANSI_B → 432×279. - Templates:
scripts/build_templates.py --checkproves the five DXFs are reproducible;drawing_new(template="iso_a3_mech")yields the mech layers, ISO-25 current, ISO_A3 page setup; a.dwttwin exists for each and opens on COM in the smoke. drawing_apply_standard("iso")thendimension_linear→ the DIMENSION carries DIMTXT 2.5 / DIMDSEP,;"ansi"→ 3.0 /..- Live COM smoke
scripts/smoke_settings_com.py: styles, page setup + PDF mediabox, template.dwtopen, layer state round trip, named view, UCS, preferences get (+ one reversible set),SummaryInfo,system_prompt_message;user_pick_pointis exercised only if--interactiveis passed (it needs the operator). Result recorded in the CHANGELOG. - Benchmark v4 gains
page_setup_truth(benchmarks/tasks_v4.py, the same v3→v4 rule: competitorsnot run); correctness suite +3 (settings_dimstyle_iso25_values,settings_layer_state_roundtrip,settings_pdf_mediabox_a3). - Docs: CLAUDE.md sections 18–20 + packs, README rows + config table
(
TOOL_PACKS=settings), CHANGELOGUnreleased.
10. Milestones (input to the plan)
- Track A hardening (7 fixes + backlog file) — independent, parallel wave.
engineering/standards/data modules + provenance tests (pure).- Styles contract + both engines + SECTION 18 tools.
- Settings facade growth +
system_variable_describe+ properties. - Page setup + plot style list + batch_plot with mediabox read-back.
- Templates: build script, bundled DXF, catalog,
drawing_new(template=),drawing_template_save;.dwttwins from the live machine. - Multi-document on both engines (+
drawing_closealignment). - Layer states, named views, UCS.
- Launch, preferences, interactive tools, capability keys.
- Packs/lean/aliases/golden queries, benchmark + correctness, live smoke, docs, gates.
Groups 2, 4, 6+7 touch disjoint modules and get their own SECTIONs, so the
plan runs them in parallel worktrees (the Track A wave-1 pattern) and a merge
agent regenerates the three count files (docs/tool-inventory.json, README
snapshot line, tests/test_tool_registry.py) after merging.