Files
autocad-mcp-pro/docs/superpowers/specs/2026-09-16-v1.6-settings-design.md
T
Umut CANandClaude Opus 5 39e6e2f9e4 docs(settings): track E shipped — CLAUDE.md sections 18-20, README, changelog with the live COM smoke record
CLAUDE.md gains items 18-20 (styles, page setup & templates, environment),
the grown drawing_settings facade on item 11, lean 55 / 52 and
TOOL_PACKS=all|core,pid,settings, and three conventions: layer states are
ours (ACADMCP_LAYERSTATES XRECORD, mode "xrecord"), page-setup evidence is
the PDF's own /MediaBox, nothing interprets coordinates in UCS.

README token table re-measured on this tree with benchmarks/token_suite.py
(ratio/v1 estimates): full 199 tools / 59,315 tokens, core,settings 190 /
55,593, core 168 / 48,825, lean 55 / 16,360, search 2 / 356; corpus
re-derived from discovery.aliases: 186 AutoCAD command names, 933 synonym
phrases, 204 tools. Styles & standards row, page setup & templates row,
final environment wording, and "Known limitations of the settings track".

CHANGELOG Unreleased: the track E intro, Added (styles, page setup &
templates, environment, drawing_settings growth, benchmark evidence),
Fixed (the Track A hardening list), Changed (three groups, packs, measured
idle cost, release snapshot 204 · 8 · 5 · 3284). Spec status: implemented.

Where the plan's text and the tree disagreed, the tree wins:
mleaderstyle_create is implemented on both engines through the
ACAD_MLEADERSTYLE dictionary and no `mleaderstyle` capability key exists
(spec §4, capability maps), so the docs say so instead of calling it
ezdxf-only; the Task 7 fix (tolerant ACADMCP_PID payloads, crossings
tested against the arc) joins the hardening list the plan left it out of.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
2026-09-22 13:38:14 +03:00

23 KiB
Raw Blame History

v1.6 Track E — AutoCAD settings and environment

Date: 2026-09-16 · Status: implemented 2026-09-22 (26 tasks of docs/superpowers/plans/2026-09-16-v1.6-settings-implementation.md; live COM smoke on AutoCAD 2026 recorded in CHANGELOG.md Unreleased, .dwt twins built by it; A/B vs v1.5.1 32 / 32) · 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 (DIMDEC 0–8, sizes > 0, DIMTAD 0–4, DIMDSEP one char) → ValueError naming it, nothing written; name clash → ValueError (use dimstyle_modify).
  • dimstyle_modify(name, overrides) — changed reports only variables that actually moved (the hatch_edit rule). Dimensions already using the style are listed in dimensions_using_style with rerender_required: true on 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 reported font_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 on doc.mleader_styles; COM through the ACAD_MLEADERSTYLE dictionary, whose items are IAcadMLeaderStyle objects (ArrowSize / LandingGap / TextHeight in drawing units, TextStyle a name — verified against the AutoCAD 2026 type library), created with Dictionaries.Item("ACAD_MLEADERSTYLE").AddObject(name, "AcDbMLeaderStyle") followed by the property writes. ActiveX has no MLeaderStyles collection property, which is not an engine boundary — no mleaderstyle capability 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, with layers, the mech layer 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_settings facade gains: limits ([[xmin,ymin],[xmax,ymax]] → $LIMMIN/$LIMMAX), grid (bool → GRIDMODE) and grid_spacing, snap (bool) and snap_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), dimstyle and textstyle (current names). Ranges enforced as today (_RANGES), unknown keys refused, and the read side reports every key.
  • system_variable_describe(name=None, search=None) — from SYSVAR_CATALOG: {name, type, range|enum, default, meaning, saved_in: drawing|registry|not_saved, engines: {ezdxf: bool, com: bool}, friendly_key: str|None}; search matches names and meanings; unknown name → known: false plus 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_variable gains 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: custom through doc.header.custom_vars ($CUSTOMPROPERTYTAG/$CUSTOMPROPERTY pairs, both engines) and the five summary fields refused with capability: "dwgprops" when any of them is passed headlessly (get returns them as null with summary_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 (ezdxf dxf_layout.dxf.*; COM Layout.*).
  • 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_SIZES authored (ISO 216 / ANSI Y14.1 sizes in mm), CTB_CATALOG names the ctb files AutoCAD ships (monochrome.ctb, acad.ctb, Grayscale.ctb, Screening 100%.ctb, …); an unknown ctb is written and reported plot_style_known: false (a missing ctb only matters at plot time). Returns the applied setup and changed.
  • plot_style_list() — the authored catalog plus, on COM, the files actually present in Preferences.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 existing drawing_export_pdf(layout=) path; returns one row per layout {layout, path, bytes, mediabox_mm} where mediabox_mm is read back from the PDF (/MediaBox parsed from the file) so the sheet size is verified, not assumed. Model space is included only when named explicitly.
  • drawing_template_list() → TEMPLATE_CATALOG rows {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>.dxf headless, templates/<name>.dwt on COM); a path is used as today. Returns template: {name|path, source: "bundled"|"path"}.
  • drawing_template_save(path, name=None, description=None) — current drawing as a template: ezdxf writes DXF (a .dwt request headlessly is refused with capability: "dwt_write" naming the DXF route); COM SaveAs(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 _doc becomes a property over the active key — drawing_new / drawing_open register a new entry instead of replacing the only one; drawing_close removes 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 unless discard=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 of document_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 (COM Preferences.*): 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-only Files.SupportPath, Files.TemplateDwgPath, Files.PrintStyleSheetPath, Files.PrinterConfigPath. Writes report old/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) — COM Utility.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_TIMEOUT applies) reports timed_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.py asserts 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_layers reported; the XRECORD survives save/reopen.
  • Page setup: apply ISO_A3 landscape → batch_plot → parsed /MediaBox is 420×297 mm (±0.5 mm at 72 dpi units); ANSI_B → 432×279.
  • Templates: scripts/build_templates.py --check proves the five DXFs are reproducible; drawing_new(template="iso_a3_mech") yields the mech layers, ISO-25 current, ISO_A3 page setup; a .dwt twin exists for each and opens on COM in the smoke.
  • drawing_apply_standard("iso") then dimension_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 .dwt open, layer state round trip, named view, UCS, preferences get (+ one reversible set), SummaryInfo, system_prompt_message; user_pick_point is exercised only if --interactive is 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: competitors not 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), CHANGELOG Unreleased.

10. Milestones (input to the plan)

  1. Track A hardening (7 fixes + backlog file) — independent, parallel wave.
  2. engineering/standards/ data modules + provenance tests (pure).
  3. Styles contract + both engines + SECTION 18 tools.
  4. Settings facade growth + system_variable_describe + properties.
  5. Page setup + plot style list + batch_plot with mediabox read-back.
  6. Templates: build script, bundled DXF, catalog, drawing_new(template=), drawing_template_save; .dwt twins from the live machine.
  7. Multi-document on both engines (+ drawing_close alignment).
  8. Layer states, named views, UCS.
  9. Launch, preferences, interactive tools, capability keys.
  10. 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.