feat(task-9): SECTION 22 - std_part_list, std_part_insert, std_feature_draw

engineering/mech/stdparts.py turns the Task 8 tables into real blocks. catalogue()
searches 83 transcribed parts by designation, standard, size or family;
block_spec() emits the typed primitives and DESIG/STD/SIZE/MAT ATTDEFs
block_define takes, in side/top for fasteners and ISO 8826-1 simplified /
ISO 8826-2 detailed for bearings; insert_std_part() defines the block once,
inserts it and writes the std_part ACADMCP_MECH payload the skeleton pins, so
the parts list is a read of the drawing rather than a naming convention.
Threads follow ISO 6410 - minor at 0.8 x major, a thread-length limit line to
the major, a three-quarter minor arc in the end view.

std_feature_draw() adds a thread, a DIN 509 undercut, a DIN 471/472 groove, a
DIN 332 centre hole or an ISO 3601-2 O-ring groove to geometry that already
exists. Its dimensions come from the shared standards registry, never from this
module: an absent table, a size outside coverage or a row missing a dimension is
refused by name, and the DIN 509 relief is drawn from the profile its table
publishes rather than reconstructed here. DIN 332 form R is refused outright.
An unknown designation is refused with the nearest catalogue entries; nothing is
drawn approximately.

SECTION 22 registers the three tools in the new mech pack, with alias records
(INSERT plus shop-floor synonyms in English and Turkish), a regenerated tool
inventory, the README release snapshot at 207 tools in 24 groups, and the frozen
group-size snapshot gaining mech: 3.

Two plan corrections, both verified by running the code:

* the plan's @cad_tool summaries for std_part_insert (118 chars) and
  std_feature_draw (130) overran discovery.serialize.MAX_SUMMARY_CHARS = 110, so
  tests/test_tool_registry.py::test_no_authored_summary_is_long_enough_to_be_truncated
  failed - they would have shipped rendered as "...". Both are shortened to 99
  and 104 characters with the same content.
* the plan's file list omitted tests/test_tool_packs.py, which pins the pack
  registry, the tool_packs report and the group count. It now carries MECH_TOOLS
  and distinguishes ALL_PACKS (declaration order, what "available" reports) from
  ALL_PACKS_ENABLED (sorted, what "enabled" reports) - those two lists stopped
  being the same list when "mech" was appended after "settings".

No new ActiveX member is called: the module drives block_list / block_define /
block_insert / entity_set_xdata / entity_create_* only. The COM path was still
executed live once on AutoCAD 2026 in a scratch document created and closed by
the probe: the bolt block defined with layers CENTER+GEOMETRY, attributes read
back as DESIG/STD/SIZE/MAT = ISO 4014 - M12x60 / ISO 4014 / M12x60 / 8.8, the
ACADMCP_MECH std_part payload round-tripped through entity_get_xdata,
STD_ISO_15_6205_SIMPLIFIED inserted, and the M20 thread's three primitives
landing on GEOMETRY.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
Umut CAN
2026-09-22 19:26:49 +03:00
co-authored by Claude Opus 5
parent 6024723d6b
commit 84c1ef42fb
9 changed files with 1923 additions and 12 deletions
+22 -5
View File
@@ -5,6 +5,8 @@ v1.6 track E adds the `settings` pack: the 22 SECTION 20 environment tools.
Styles (SECTION 18) and page setup / templates (SECTION 19) are drafting
essentials and stay in `core`, so `TOOL_PACKS=core` hides the environment
surface and nothing else.
v1.6 tracks B+G add the `mech` pack: SECTION 22's three standard-parts tools.
"""
from __future__ import annotations
@@ -78,6 +80,12 @@ SETTINGS_TOOLS = {
"system_prompt_message",
}
MECH_TOOLS = {
"std_part_list",
"std_part_insert",
"std_feature_draw",
}
LEAN_SETTINGS_ESSENTIALS = {
"drawing_apply_standard",
"page_setup_apply",
@@ -86,7 +94,10 @@ LEAN_SETTINGS_ESSENTIALS = {
"textstyle_set_current",
}
ALL_PACKS = ["core", "pid", "settings"]
#: Declaration order - what `tool_packs["available"]` reports.
ALL_PACKS = ["core", "pid", "settings", "mech"]
#: `tool_packs["enabled"]` is sorted, which is no longer the same list.
ALL_PACKS_ENABLED = sorted(ALL_PACKS)
@pytest_asyncio.fixture(autouse=True)
@@ -97,11 +108,15 @@ async def _restore(monkeypatch):
def test_pack_registry_names_real_tools_and_only_them():
assert server.TOOL_PACK_NAMES == ("core", "pid", "settings")
assert server.TOOL_PACK_NAMES == ("core", "pid", "settings", "mech")
assert server.PACK_TOOL_NAMES["pid"] == frozenset(PID_TOOLS)
assert server.PACK_TOOL_NAMES["settings"] == frozenset(SETTINGS_TOOLS)
assert server.PACK_TOOL_NAMES["mech"] == frozenset(MECH_TOOLS)
assert len(SETTINGS_TOOLS) == 22
assert len(MECH_TOOLS) == 3
assert not (server.PACK_TOOL_NAMES["settings"] & server.PACK_TOOL_NAMES["pid"])
assert not (server.PACK_TOOL_NAMES["mech"] & server.PACK_TOOL_NAMES["pid"])
assert not (server.PACK_TOOL_NAMES["mech"] & server.PACK_TOOL_NAMES["settings"])
async def test_every_pack_member_is_a_registered_tool():
@@ -117,6 +132,7 @@ async def test_core_only_hides_pid_and_settings_but_keeps_styles_and_page_setup(
disabled = set(info["disabled_tools"])
assert PID_TOOLS <= disabled
assert SETTINGS_TOOLS <= disabled
assert MECH_TOOLS <= disabled
assert not (STYLE_TOOLS & disabled), "styles are core drafting essentials"
assert not (PAGE_SETUP_TOOLS & disabled), "page setup and templates are core"
assert "block_define" not in disabled and "entity_set_xdata" not in disabled
@@ -136,8 +152,8 @@ async def test_all_is_the_default_and_enables_every_pack(monkeypatch):
monkeypatch.setattr(config.settings, "tool_packs", "all")
info = await server._apply_tool_profile("full")
disabled = set(info["disabled_tools"])
assert not ((PID_TOOLS | SETTINGS_TOOLS) & disabled)
assert info["tool_packs"]["enabled"] == ALL_PACKS
assert not ((PID_TOOLS | SETTINGS_TOOLS | MECH_TOOLS) & disabled)
assert info["tool_packs"]["enabled"] == ALL_PACKS_ENABLED
async def test_unknown_pack_is_ignored_with_a_warning_and_core_cannot_be_dropped(
@@ -184,7 +200,8 @@ async def test_the_three_track_e_sections_file_under_their_own_groups():
assert set(groups["styles"]) == STYLE_TOOLS
assert set(groups["page_setup"]) == PAGE_SETUP_TOOLS
assert set(groups["environment"]) == SETTINGS_TOOLS
assert len(groups) == 23
assert set(groups["mech"]) == MECH_TOOLS
assert len(groups) == 24
async def test_system_about_reports_packs():