The three track-F branches are merged; this is the integration that makes the
twelve SECTION 25 tools one surface.
- engineering/arch/spec.py: a whole plan from one document - walls with their
openings, stairs, room labels measured off the walls just drawn, the
exterior chains and the schedules - inside one transaction. Everything the
model, the wall engine or the stair symbol can refuse is refused before the
transaction opens, by path; a room point in no closed face rolls it back,
and so does a cancelled request, under the anyio shield of pid/spec.py.
EXAMPLE_SPEC's rooms measure 4825 x 5750 and 3825 x 5750 - the net floor,
computed by hand in the tests.
- engineering/arch/critique.py: arch_room_unlabelled, arch_wall_gap (50 mm,
one issue per pair of walls, measured to the wall body so an L corner and a
T stem both read as joined) and arch_opening_clash. One index per critique
run; a drawing without an ACADMCP_ARCH record yields nothing. Each focus
has a positive and a negative fixture.
- TOOL_PACK_NAMES gains "arch" (the twelve SECTION 25 tools); lean gains
arch_wall, arch_opening, arch_room and arch_plan_from_spec and reaches 64;
the arch tag ranks after mech, so SECTION 25 files as one `architecture`
group - 241 tools in 26 groups.
- Six golden tuning cases, two of them Turkish, the arch_plan_from_spec alias
record, the shared-command allowlist reconciled with what the merged records
claim, the inventory regenerated, the registry snapshot retaken, and the
README figures taken from check_doc_numbers rather than typed.
- Two more Turkish golden cases close the reconciliation's alias gaps
("vaziyet planı çiz" -> arch_plan_from_spec, "oda alanlarını hesapla" ->
arch_rooms_detect); the arch records carry vaziyet / alan / oda / kat planı
in diacritic and ASCII-folded spellings, because the search tokenizer splits
on a non-ASCII letter. BOUNDARY and AREA stay with boundary_trace and
analysis_measure_entity on purpose, recorded in the arch alias comment.
Tuning top-1 73/84, top-3 83/84; holdout top-3 15/15.
- tests/test_token_suite.py: TOOL_PACKS=core,mech now drops the arch pack
too (measured 193 = 236 - 9 - 22 - 12).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
134 lines
3.5 KiB
Python
134 lines
3.5 KiB
Python
"""Premium drawing planning + critique data models.
|
|
|
|
PlanSpec is committed up-front by the LLM via `drawing_plan(...)` before any
|
|
geometry is created; CritiqueFocus is a closed enum to keep
|
|
`drawing_critique(...)` scope from drifting.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Literal
|
|
|
|
SheetSize = Literal["A4", "A3", "A2", "A1", "A0"]
|
|
LayerSetId = Literal["iso13567", "mech", "pid", "arch"]
|
|
DimStyle = Literal["chain", "baseline", "ordinate", "mixed"]
|
|
CritiqueFocus = Literal[
|
|
"iso128",
|
|
"layer_color",
|
|
"dim_overlap",
|
|
"untrimmed_corner",
|
|
"duplicate_entities",
|
|
"construction_left",
|
|
"gdt",
|
|
"pid_dangling_line",
|
|
"pid_duplicate_tag",
|
|
"pid_incompatible_connection",
|
|
"pid_untagged_instrument",
|
|
"pid_illegal_tag",
|
|
"pid_unconnected_equipment",
|
|
"mech_missing_centreline",
|
|
"mech_unhatched_section",
|
|
"mech_view_misaligned",
|
|
"mech_duplicate_dimension",
|
|
"mech_thread_unrepresented",
|
|
"mech_bom_balloon_mismatch",
|
|
"arch_room_unlabelled",
|
|
"arch_wall_gap",
|
|
"arch_opening_clash",
|
|
]
|
|
SnapType = Literal["end", "mid", "center", "quad", "int", "perp", "near"]
|
|
|
|
ALL_CRITIQUE_FOCUSES: tuple[CritiqueFocus, ...] = (
|
|
"iso128",
|
|
"layer_color",
|
|
"dim_overlap",
|
|
"untrimmed_corner",
|
|
"duplicate_entities",
|
|
"construction_left",
|
|
"gdt",
|
|
"pid_dangling_line",
|
|
"pid_duplicate_tag",
|
|
"pid_incompatible_connection",
|
|
"pid_untagged_instrument",
|
|
"pid_illegal_tag",
|
|
"pid_unconnected_equipment",
|
|
"mech_missing_centreline",
|
|
"mech_unhatched_section",
|
|
"mech_view_misaligned",
|
|
"mech_duplicate_dimension",
|
|
"mech_thread_unrepresented",
|
|
"mech_bom_balloon_mismatch",
|
|
"arch_room_unlabelled",
|
|
"arch_wall_gap",
|
|
"arch_opening_clash",
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class PlanSpec:
|
|
"""Drawing intent committed before geometry creation.
|
|
|
|
Returned by `drawing_plan(...)` and replayed by `drawing_critique(...)` to
|
|
verify the finished drawing matches the original intent.
|
|
"""
|
|
|
|
intent: str
|
|
sheet_size: SheetSize
|
|
scale: float
|
|
layer_set_id: LayerSetId
|
|
view_count: int = 1
|
|
dim_style: DimStyle = "chain"
|
|
notes: list[str] = field(default_factory=list)
|
|
requirements: dict = field(default_factory=dict)
|
|
spec_hash: str | None = None
|
|
preflight_status: Literal["ready", "skipped"] = "skipped"
|
|
|
|
def to_dict(self) -> dict:
|
|
return {
|
|
"intent": self.intent,
|
|
"sheet_size": self.sheet_size,
|
|
"scale": self.scale,
|
|
"layer_set_id": self.layer_set_id,
|
|
"view_count": self.view_count,
|
|
"dim_style": self.dim_style,
|
|
"notes": list(self.notes),
|
|
"requirements": dict(self.requirements),
|
|
"spec_hash": self.spec_hash,
|
|
"preflight_status": self.preflight_status,
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class Issue:
|
|
"""One problem reported by `drawing_critique(...)`."""
|
|
|
|
severity: Literal["error", "warning", "info"]
|
|
focus: CritiqueFocus
|
|
message: str
|
|
handles: list[str] = field(default_factory=list)
|
|
detail: dict = field(default_factory=dict)
|
|
|
|
def to_dict(self) -> dict:
|
|
return {
|
|
"severity": self.severity,
|
|
"focus": self.focus,
|
|
"message": self.message,
|
|
"handles": list(self.handles),
|
|
"detail": self.detail,
|
|
}
|
|
|
|
|
|
# ISO 128 lineweight set (mm). Any lineweight outside this set is flagged.
|
|
ISO_128_LINEWEIGHTS_MM: tuple[float, ...] = (
|
|
0.13,
|
|
0.18,
|
|
0.25,
|
|
0.35,
|
|
0.50,
|
|
0.70,
|
|
1.00,
|
|
1.40,
|
|
2.00,
|
|
)
|