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