feat(understand): the plant pack, lean 65, five-language aliases, eight golden queries and the counts
The three track-H branches are merged; this is the integration that makes the six tools one surface. - TOOL_PACK_NAMES gains "plant" (pipe_takeoff, cable_takeoff); SECTION 26's four readers stay core. Lean gains drawing_understand and reaches 65 (55 with TOOL_PACKS=core). The `understand` and `plant` tags rank after `arch`, so the sections file as `understanding` (4) and `plant` (2): 247 tools in 28 groups. - discovery/aliases.py folds spec section 12's vocabulary into the six records the branches wrote - metraj, metre cetveli, boru / kablo listesi, fark, karsilastir, revizyon farki, olcek, birim in both Turkish spellings, BOQ, takeoff, scale check, COMPARE - without writing a phrase twice. The Russian terms are data only: the BM25 tokenizer keeps [a-z0-9], and no golden case pretends otherwise. - Eight golden tuning cases, four of them Turkish; the shared-command allowlist reconciled with what the merged records claim; the token-suite formula excludes the new pack; the inventory regenerated, the registry snapshot retaken and the README figures taken from check_doc_numbers. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8c1a6cb27c
commit
835bc4b65e
@@ -27,7 +27,7 @@ Live through COM on Windows, or headless through ezdxf anywhere — one typed co
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
> **v1.5 release snapshot:** 247 tools · 8 resources · 5 prompt templates · 4516 collected tests.
|
> **v1.5 release snapshot:** 247 tools · 8 resources · 5 prompt templates · 4942 collected tests.
|
||||||
> 247 is the **registered** count; a default install advertises 242 over `tools/list`,
|
> 247 is the **registered** count; a default install advertises 242 over `tools/list`,
|
||||||
> because `ENABLE_3D` is unset. `system_about` is the runtime authority.
|
> because `ENABLE_3D` is unset. `system_about` is the runtime authority.
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ Live through COM on Windows, or headless through ezdxf anywhere — one typed co
|
|||||||
|
|
||||||
**A big MCP server is expensive to be connected to.** The full catalog costs a client **78,772 tokens** before it has asked for anything. Discovery mode replaces it with two tools and costs **356**.
|
**A big MCP server is expensive to be connected to.** The full catalog costs a client **78,772 tokens** before it has asked for anything. Discovery mode replaces it with two tools and costs **356**.
|
||||||
|
|
||||||
**A drafter searches for `FILLET`, not `entity_fillet`.** Those command names appeared in no tool name or description — `df = 0` against a stock index, not badly ranked but *absent*. The fix was data: an authored corpus of **205 AutoCAD command names and 1318 synonym phrases** covering all 241 tools. A test refuses to let a tool exist without one.
|
**A drafter searches for `FILLET`, not `entity_fillet`.** Those command names appeared in no tool name or description — `df = 0` against a stock index, not badly ranked but *absent*. The fix was data: an authored corpus of **206 AutoCAD command names and 1439 synonym phrases** covering all 247 tools. A test refuses to let a tool exist without one.
|
||||||
|
|
||||||
| Advertised surface | Tools seen | Idle cost |
|
| Advertised surface | Tools seen | Idle cost |
|
||||||
|---|---:|---:|
|
|---|---:|---:|
|
||||||
@@ -108,7 +108,7 @@ Claude Desktop, Cursor, or any stdio MCP host. For HTTP: `autocad-mcp --transpor
|
|||||||
| Architecture | one plan model — walls with hosted doors and windows — and one engine: L / T / X junctions resolved, openings cut, poché by material; rooms measured from the faces the walls enclose (never typed) and read back from any plan with a confidence; door / window / room schedules as real TABLEs; a furniture and sanitary catalogue at nominal sizes; structural grid, north arrow, section / level / elevation marks; exterior dimension chains; English or Turkish labels (`lang="tr"`, decimal comma) |
|
| Architecture | one plan model — walls with hosted doors and windows — and one engine: L / T / X junctions resolved, openings cut, poché by material; rooms measured from the faces the walls enclose (never typed) and read back from any plan with a confidence; door / window / room schedules as real TABLEs; a furniture and sanitary catalogue at nominal sizes; structural grid, north arrow, section / level / elevation marks; exterior dimension chains; English or Turkish labels (`lang="tr"`, decimal comma) |
|
||||||
| P&ID | catalogue blocks with ports and tags (ISO 10628-2 / ISA-5.1), port-to-port lines with ISA-5.1 classes and line numbers, `pid_graph` reads any P&ID back with confidence, instrument index / line list / equipment list, `pid_from_spec` one-call sheets |
|
| P&ID | catalogue blocks with ports and tags (ISO 10628-2 / ISA-5.1), port-to-port lines with ISA-5.1 classes and line numbers, `pid_graph` reads any P&ID back with confidence, instrument index / line list / equipment list, `pid_from_spec` one-call sheets |
|
||||||
| Styles & standards | ISO-25 / ANSI dimension styles, ISOCP / ROMANS text styles, ISO / ANSI leader styles from authored presets; `drawing_apply_standard("iso")` sets all of it plus units and the `mech` layers in one call; `changed` reports only what moved |
|
| Styles & standards | ISO-25 / ANSI dimension styles, ISOCP / ROMANS text styles, ISO / ANSI leader styles from authored presets; `drawing_apply_standard("iso")` sets all of it plus units and the `mech` layers in one call; `changed` reports only what moved |
|
||||||
| Discovery | `search_tools` ranked over an AutoCAD command and synonym corpus — `FILLET`, `BPOLY`, `QSELECT`, `WBLOCK`, `OVERKILL`, `CHSPACE` each rank **#1** of the 236-tool advertised catalog |
|
| Discovery | `search_tools` ranked over an AutoCAD command and synonym corpus — `FILLET`, `BPOLY`, `QSELECT`, `WBLOCK`, `OVERKILL`, `CHSPACE` each rank **#1** of the 242-tool advertised catalog |
|
||||||
| Batching | `cad_batch` runs a step list in one round trip; `fields=` projects 11 result-heavy tools |
|
| Batching | `cad_batch` runs a step list in one round trip; `fields=` projects 11 result-heavy tools |
|
||||||
| Paper space | tab lifecycle, viewports, `entity_change_space` (CHSPACE), `page_setup_apply` (ISO 216 / ANSI Y14.1 paper, ctb, scale, device — on both engines), `batch_plot` with every sheet size read back from its PDF's `/MediaBox`, `drawing_export_pdf(layout=…)` |
|
| Paper space | tab lifecycle, viewports, `entity_change_space` (CHSPACE), `page_setup_apply` (ISO 216 / ANSI Y14.1 paper, ctb, scale, device — on both engines), `batch_plot` with every sheet size read back from its PDF's `/MediaBox`, `drawing_export_pdf(layout=…)` |
|
||||||
| Page setup & templates | ISO 216 / ANSI Y14.1 sheets, ctb catalog, `batch_plot` verified by the PDF's own `/MediaBox`, five bundled templates built by the server's own tools and pinned reproducible (`drawing_new(template="iso_a3_mech")`, `iso_a1_arch`, `iso_a3_pid`, `ansi_b_mech`, `ansi_d_arch`), save any drawing as a template (`.dwt` on live AutoCAD, `dwt_write` refused headlessly) |
|
| Page setup & templates | ISO 216 / ANSI Y14.1 sheets, ctb catalog, `batch_plot` verified by the PDF's own `/MediaBox`, five bundled templates built by the server's own tools and pinned reproducible (`drawing_new(template="iso_a3_mech")`, `iso_a1_arch`, `iso_a3_pid`, `ansi_b_mech`, `ansi_d_arch`), save any drawing as a template (`.dwt` on live AutoCAD, `dwt_write` refused headlessly) |
|
||||||
@@ -122,7 +122,7 @@ Claude Desktop, Cursor, or any stdio MCP host. For HTTP: `autocad-mcp --transpor
|
|||||||
| Quality loop | `drawing_preflight` → `drawing_plan` → `drawing_critique` → `drawing_refine` → `drawing_finalize` (0–100 score) |
|
| Quality loop | `drawing_preflight` → `drawing_plan` → `drawing_critique` → `drawing_refine` → `drawing_finalize` (0–100 score) |
|
||||||
| Delivery | `drawing_deliver`: DXF/PDF/PNG + SHA-256 manifest + reopen-parity checks |
|
| Delivery | `drawing_deliver`: DXF/PDF/PNG + SHA-256 manifest + reopen-parity checks |
|
||||||
|
|
||||||
<sub>247 tools in 26 groups; <code>TOOL_PACKS=core</code> hides the nine <code>pid_*</code> tools, the 22 environment tools, the 14 mechanical tools and the 12 architectural tools (<code>TOOL_PACKS=core,mech</code> keeps the mechanical ones, <code>TOOL_PACKS=core,arch</code> the architectural ones) from a client that needs none of them. Plus 8 resources that cost nothing in the tool budget (<code>autocad://drawing/info</code>, <code>layers</code>, <code>blocks</code>, <code>entities/stats</code>, <code>entities/{layer_name}</code>, <code>system/status</code>, <code>pid/symbols</code>, <code>standards/isa51</code>) and 5 prompt templates.</sub>
|
<sub>247 tools in 28 groups; <code>TOOL_PACKS=core</code> hides the nine <code>pid_*</code> tools, the 22 environment tools, the 14 mechanical tools, the 12 architectural tools and the two plant takeoffs (<code>TOOL_PACKS=core,mech</code> keeps the mechanical ones, <code>TOOL_PACKS=core,arch</code> the architectural ones, <code>TOOL_PACKS=core,plant</code> the takeoffs) from a client that needs none of them. Plus 8 resources that cost nothing in the tool budget (<code>autocad://drawing/info</code>, <code>layers</code>, <code>blocks</code>, <code>entities/stats</code>, <code>entities/{layer_name}</code>, <code>system/status</code>, <code>pid/symbols</code>, <code>standards/isa51</code>) and 5 prompt templates.</sub>
|
||||||
|
|
||||||
**Two rules worth knowing.** Every coordinate in and out of a tool is WCS on both engines — the one exception is TEXT `rotation`, which stays in the entity frame because a mirrored TEXT is mirror-imaged and no scalar angle expresses that. And never read vertices back and shoelace them: that loses **28.2%** of the area on a semicircular edge, silently. `analysis_measure_entity(handle)` reads the real geometry and states its own accuracy.
|
**Two rules worth knowing.** Every coordinate in and out of a tool is WCS on both engines — the one exception is TEXT `rotation`, which stays in the entity frame because a mirrored TEXT is mirror-imaged and no scalar angle expresses that. And never read vertices back and shoelace them: that loses **28.2%** of the area on a semicircular edge, silently. `analysis_measure_entity(handle)` reads the real geometry and states its own accuracy.
|
||||||
|
|
||||||
@@ -234,8 +234,8 @@ Nothing loads a `.env` file — export these, or set them in your MCP client's `
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `AUTOCAD_MCP_BACKEND` | `auto` | `auto`, `com`, or `ezdxf` |
|
| `AUTOCAD_MCP_BACKEND` | `auto` | `auto`, `com`, or `ezdxf` |
|
||||||
| `CAD_PROGID` | `AutoCAD.Application` | COM ProgID the live backend attaches to |
|
| `CAD_PROGID` | `AutoCAD.Application` | COM ProgID the live backend attaches to |
|
||||||
| `TOOL_PROFILE` | `full` | `lean` (64 curated tools, 54 with `TOOL_PACKS=core`) or `full` |
|
| `TOOL_PROFILE` | `full` | `lean` (65 curated tools, 55 with `TOOL_PACKS=core`) or `full` |
|
||||||
| `TOOL_PACKS` | `all` | Vertical packs to advertise: `core,pid,settings` (`core` always on) |
|
| `TOOL_PACKS` | `all` | Vertical packs to advertise: `core,pid,settings,mech,arch,plant` (`core` always on) |
|
||||||
| `DISCOVERY_MODE` | `off` | `search` replaces the catalog with `search_tools` + `call_tool` |
|
| `DISCOVERY_MODE` | `off` | `search` replaces the catalog with `search_tools` + `call_tool` |
|
||||||
| `ENABLE_3D` | `false` | Expose the opt-in `solid_*` tools (COM) |
|
| `ENABLE_3D` | `false` | Expose the opt-in `solid_*` tools (COM) |
|
||||||
| `LOG_LEVEL` | `INFO` | Python logging level |
|
| `LOG_LEVEL` | `INFO` | Python logging level |
|
||||||
|
|||||||
@@ -2580,6 +2580,150 @@ TOOL_ALIASES: dict[str, ToolAliases] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ── Track H: the five-language vocabulary of spec §12 ──────────────────────
|
||||||
|
#
|
||||||
|
# The six track-H records were written by three branches (SECTION 26 by groups
|
||||||
|
# U and D, SECTION 27 by group N). Spec §12's vocabulary - the words a plant
|
||||||
|
# engineer types when asking for a takeoff, a comparison or a scale check - is
|
||||||
|
# folded into them here, after the merge, so every branch's own phrases stay
|
||||||
|
# and nothing is written twice. A record missing here is a KeyError at import:
|
||||||
|
# a track-H tool without its branch's record is a merge that lost something.
|
||||||
|
#
|
||||||
|
# Turkish carries both spellings, diacritic and ASCII-folded, because the
|
||||||
|
# search tokenizer splits on a non-ASCII letter instead of folding it (the
|
||||||
|
# room records above say the same). The Russian terms are data for a client
|
||||||
|
# that searches this corpus itself: the BM25 tokenizer keeps only [a-z0-9], so
|
||||||
|
# it cannot index Cyrillic yet, and no golden case pretends it can.
|
||||||
|
#
|
||||||
|
# COMPARE is AutoCAD's drawing-compare command (the "DWG Compare" feature);
|
||||||
|
# "dwg compare" / "dwgcompare" are how drafters name the feature, so they are
|
||||||
|
# synonyms, not a command token.
|
||||||
|
_TRACK_H_VOCABULARY: dict[str, tuple[tuple[str, ...], tuple[str, ...]]] = {
|
||||||
|
"drawing_understand": (
|
||||||
|
(),
|
||||||
|
(
|
||||||
|
"what is in this drawing",
|
||||||
|
"what is in this drawing somebody sent me",
|
||||||
|
"understand a foreign drawing",
|
||||||
|
"read a drawing somebody else made",
|
||||||
|
"which units is this drawing in",
|
||||||
|
"drawing units check",
|
||||||
|
"birim",
|
||||||
|
"birim kontrolü",
|
||||||
|
"birim kontrolu",
|
||||||
|
"çizim birimi",
|
||||||
|
"cizim birimi",
|
||||||
|
"çizimi anla",
|
||||||
|
"cizimi anla",
|
||||||
|
"yabancı çizim",
|
||||||
|
"yabanci cizim",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"drawing_scale_check": (
|
||||||
|
(),
|
||||||
|
(
|
||||||
|
"scale check",
|
||||||
|
"is this drawing to scale",
|
||||||
|
"is the p&id to scale",
|
||||||
|
"schematic or to scale",
|
||||||
|
"compare distances between two drawings",
|
||||||
|
"ölçek",
|
||||||
|
"olcek",
|
||||||
|
"ölçek kontrolü",
|
||||||
|
"ölçek kontrolu",
|
||||||
|
"olcek kontrolu",
|
||||||
|
"ölçekli mi",
|
||||||
|
"olcekli mi",
|
||||||
|
"bu çizim ölçekli mi",
|
||||||
|
"bu cizim olcekli mi",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"drawing_diff": (
|
||||||
|
("COMPARE",),
|
||||||
|
(
|
||||||
|
"compare",
|
||||||
|
"compare two revisions",
|
||||||
|
"compare two revisions of this drawing",
|
||||||
|
"what changed between revisions",
|
||||||
|
"drawing diff",
|
||||||
|
"dwg compare",
|
||||||
|
"dwgcompare",
|
||||||
|
"fark",
|
||||||
|
"revizyon farkı",
|
||||||
|
"revizyon farki",
|
||||||
|
"karşılaştır",
|
||||||
|
"karsilastir",
|
||||||
|
"iki revizyonu karşılaştır",
|
||||||
|
"iki revizyonu karsilastir",
|
||||||
|
"сравнить чертежи", # compare drawings
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"drawing_topology_check": (
|
||||||
|
(),
|
||||||
|
(
|
||||||
|
"find dangling line ends",
|
||||||
|
"find dangling line ends and gaps",
|
||||||
|
"uçları açık çizgiler",
|
||||||
|
"uclari acik cizgiler",
|
||||||
|
"kopuk hat",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"pipe_takeoff": (
|
||||||
|
(),
|
||||||
|
(
|
||||||
|
"takeoff",
|
||||||
|
"pipe takeoff",
|
||||||
|
"pipe takeoff by diameter from the p&id",
|
||||||
|
"pipe quantities",
|
||||||
|
"bill of quantities",
|
||||||
|
"boq",
|
||||||
|
"metraj",
|
||||||
|
"metre cetveli",
|
||||||
|
"boru listesi",
|
||||||
|
"boru metrajı",
|
||||||
|
"boru metraji",
|
||||||
|
"boru metrajı çıkar",
|
||||||
|
"boru metraji cikar",
|
||||||
|
"спецификация", # specification: a bill of quantities
|
||||||
|
"ведомость", # statement: a quantity schedule
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"cable_takeoff": (
|
||||||
|
(),
|
||||||
|
(
|
||||||
|
"takeoff",
|
||||||
|
"cable takeoff",
|
||||||
|
"cable schedule",
|
||||||
|
"cable lengths to the panel",
|
||||||
|
"bill of quantities",
|
||||||
|
"boq",
|
||||||
|
"metraj",
|
||||||
|
"metre cetveli",
|
||||||
|
"kablo listesi",
|
||||||
|
"kablo metrajı",
|
||||||
|
"kablo metraji",
|
||||||
|
"kablo metrajı çıkar",
|
||||||
|
"kablo metraji cikar",
|
||||||
|
"кабельная ведомость", # cable schedule
|
||||||
|
"спецификация", # specification: a bill of quantities
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _fold_in(records: dict[str, ToolAliases], extra) -> None:
|
||||||
|
"""Append ``extra``'s commands and phrases to existing records, never twice."""
|
||||||
|
for name, (acad, synonyms) in extra.items():
|
||||||
|
record = records[name]
|
||||||
|
records[name] = ToolAliases(
|
||||||
|
acad=record.acad + tuple(c for c in acad if c not in record.acad),
|
||||||
|
synonyms=record.synonyms + tuple(s for s in synonyms if s not in record.synonyms),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
_fold_in(TOOL_ALIASES, _TRACK_H_VOCABULARY)
|
||||||
|
|
||||||
|
|
||||||
def aliases_for(tool_name: str) -> ToolAliases | None:
|
def aliases_for(tool_name: str) -> ToolAliases | None:
|
||||||
"""Return the alias record for ``tool_name``, or ``None`` if it has none."""
|
"""Return the alias record for ``tool_name``, or ``None`` if it has none."""
|
||||||
return TOOL_ALIASES.get(tool_name)
|
return TOOL_ALIASES.get(tool_name)
|
||||||
|
|||||||
+18
-12
@@ -6,10 +6,10 @@
|
|||||||
"tools": 247,
|
"tools": 247,
|
||||||
"resources": 8,
|
"resources": 8,
|
||||||
"prompts": 5,
|
"prompts": 5,
|
||||||
"groups": 26
|
"groups": 28
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
"analysis": 18,
|
"analysis": 12,
|
||||||
"architecture": 12,
|
"architecture": 12,
|
||||||
"batch": 3,
|
"batch": 3,
|
||||||
"blocks": 9,
|
"blocks": 9,
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
"mechanical": 14,
|
"mechanical": 14,
|
||||||
"page_setup": 6,
|
"page_setup": 6,
|
||||||
"pid": 9,
|
"pid": 9,
|
||||||
|
"plant": 2,
|
||||||
"premium": 12,
|
"premium": 12,
|
||||||
"sheet": 11,
|
"sheet": 11,
|
||||||
"solids": 5,
|
"solids": 5,
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
"system": 7,
|
"system": 7,
|
||||||
"templates": 2,
|
"templates": 2,
|
||||||
"transactions": 3,
|
"transactions": 3,
|
||||||
|
"understanding": 4,
|
||||||
"validation": 1,
|
"validation": 1,
|
||||||
"view": 4
|
"view": 4
|
||||||
},
|
},
|
||||||
@@ -545,7 +547,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "cable_takeoff",
|
"name": "cable_takeoff",
|
||||||
"group": "analysis",
|
"group": "plant",
|
||||||
"cost": "safe",
|
"cost": "safe",
|
||||||
"summary": "Cable takeoff: one row per load, Manhattan length to its panel, rounded up to the metre.",
|
"summary": "Cable takeoff: one row per load, Manhattan length to its panel, rounded up to the metre.",
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -914,12 +916,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "drawing_diff",
|
"name": "drawing_diff",
|
||||||
"group": "analysis",
|
"group": "understanding",
|
||||||
"cost": "mutate",
|
"cost": "mutate",
|
||||||
"summary": "Compare two revisions of a drawing: what moved, changed, was added or removed.",
|
"summary": "Compare two revisions of a drawing: what moved, changed, was added or removed.",
|
||||||
"tags": [
|
"tags": [
|
||||||
"analysis",
|
"analysis",
|
||||||
"query"
|
"query",
|
||||||
|
"understand"
|
||||||
],
|
],
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"title": "Drawing: Diff Two Revisions",
|
"title": "Drawing: Diff Two Revisions",
|
||||||
@@ -1147,12 +1150,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "drawing_scale_check",
|
"name": "drawing_scale_check",
|
||||||
"group": "analysis",
|
"group": "understanding",
|
||||||
"cost": "read",
|
"cost": "read",
|
||||||
"summary": "Is this drawing to scale against a reference? Tag distances, ratios, a verdict.",
|
"summary": "Is this drawing to scale against a reference? Tag distances, ratios, a verdict.",
|
||||||
"tags": [
|
"tags": [
|
||||||
"analysis",
|
"analysis",
|
||||||
"query"
|
"query",
|
||||||
|
"understand"
|
||||||
],
|
],
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"title": "Understand: Scale Check",
|
"title": "Understand: Scale Check",
|
||||||
@@ -1206,12 +1210,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "drawing_topology_check",
|
"name": "drawing_topology_check",
|
||||||
"group": "analysis",
|
"group": "understanding",
|
||||||
"cost": "read",
|
"cost": "read",
|
||||||
"summary": "Find dangling ends, near misses and unjoined crossings in a pipe or cable network.",
|
"summary": "Find dangling ends, near misses and unjoined crossings in a pipe or cable network.",
|
||||||
"tags": [
|
"tags": [
|
||||||
"analysis",
|
"analysis",
|
||||||
"query"
|
"query",
|
||||||
|
"understand"
|
||||||
],
|
],
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"title": "Drawing: Topology Check",
|
"title": "Drawing: Topology Check",
|
||||||
@@ -1220,12 +1225,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "drawing_understand",
|
"name": "drawing_understand",
|
||||||
"group": "analysis",
|
"group": "understanding",
|
||||||
"cost": "read",
|
"cost": "read",
|
||||||
"summary": "Read a foreign drawing: units, extents, clusters, layers, tags, rooms, languages.",
|
"summary": "Read a foreign drawing: units, extents, clusters, layers, tags, rooms, languages.",
|
||||||
"tags": [
|
"tags": [
|
||||||
"analysis",
|
"analysis",
|
||||||
"query"
|
"query",
|
||||||
|
"understand"
|
||||||
],
|
],
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"title": "Understand: Describe Drawing",
|
"title": "Understand: Describe Drawing",
|
||||||
@@ -2597,7 +2603,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pipe_takeoff",
|
"name": "pipe_takeoff",
|
||||||
"group": "analysis",
|
"group": "plant",
|
||||||
"cost": "safe",
|
"cost": "safe",
|
||||||
"summary": "Pipe takeoff from a P&ID and its layout: rows by room, service and diameter.",
|
"summary": "Pipe takeoff from a P&ID and its layout: rows by room, service and diameter.",
|
||||||
"tags": [
|
"tags": [
|
||||||
|
|||||||
@@ -692,6 +692,8 @@ _GROUP_TAG_PRIORITY = (
|
|||||||
"pid",
|
"pid",
|
||||||
"mech",
|
"mech",
|
||||||
"arch",
|
"arch",
|
||||||
|
"understand",
|
||||||
|
"plant",
|
||||||
"sheet",
|
"sheet",
|
||||||
"style",
|
"style",
|
||||||
"pagesetup",
|
"pagesetup",
|
||||||
@@ -723,6 +725,8 @@ _GROUP_TAG_LABELS = {
|
|||||||
"pid": "pid",
|
"pid": "pid",
|
||||||
"mech": "mechanical",
|
"mech": "mechanical",
|
||||||
"arch": "architecture",
|
"arch": "architecture",
|
||||||
|
"understand": "understanding",
|
||||||
|
"plant": "plant",
|
||||||
"sheet": "sheet",
|
"sheet": "sheet",
|
||||||
"style": "styles",
|
"style": "styles",
|
||||||
"pagesetup": "page_setup",
|
"pagesetup": "page_setup",
|
||||||
@@ -973,6 +977,12 @@ LEAN_TOOL_NAMES = frozenset(
|
|||||||
"arch_opening",
|
"arch_opening",
|
||||||
"arch_room",
|
"arch_room",
|
||||||
"arch_plan_from_spec",
|
"arch_plan_from_spec",
|
||||||
|
# Understanding (track H) - the one read a lean client needs before it
|
||||||
|
# touches a drawing somebody else made: units, extents, clusters,
|
||||||
|
# layers and their services, tags and rooms in one call. A core-pack
|
||||||
|
# tool, so TOOL_PACKS=core keeps it; with the four architectural ones
|
||||||
|
# above, lean is 65. The takeoffs are `plant`-pack, full-profile work.
|
||||||
|
"drawing_understand",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -986,7 +996,7 @@ SOLID_TOOL_NAMES = frozenset(
|
|||||||
# mechanically should not pay for the P&ID surface, and a client that never
|
# mechanically should not pay for the P&ID surface, and a client that never
|
||||||
# talks to a live seat should not pay for the environment surface. Styles and
|
# talks to a live seat should not pay for the environment surface. Styles and
|
||||||
# page setup (SECTION 18/19) are drafting essentials and stay in core.
|
# page setup (SECTION 18/19) are drafting essentials and stay in core.
|
||||||
TOOL_PACK_NAMES = ("core", "pid", "settings", "mech", "arch")
|
TOOL_PACK_NAMES = ("core", "pid", "settings", "mech", "arch", "plant")
|
||||||
PACK_TOOL_NAMES: dict[str, frozenset[str]] = {
|
PACK_TOOL_NAMES: dict[str, frozenset[str]] = {
|
||||||
"pid": frozenset(
|
"pid": frozenset(
|
||||||
{
|
{
|
||||||
@@ -1070,6 +1080,12 @@ PACK_TOOL_NAMES: dict[str, frozenset[str]] = {
|
|||||||
"arch_plan_from_spec",
|
"arch_plan_from_spec",
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
# SECTION 27 - the plant takeoffs: pipe and cable quantities whose topology
|
||||||
|
# comes from a P&ID and whose lengths come from its layout (track H spec
|
||||||
|
# §8-9, §12). SECTION 26's four readers stay in `core`: understanding,
|
||||||
|
# comparing and checking a drawing is not a vertical. Track D's Plant 3D
|
||||||
|
# readers join this pack.
|
||||||
|
"plant": frozenset({"pipe_takeoff", "cable_takeoff"}),
|
||||||
}
|
}
|
||||||
|
|
||||||
_active_tool_profile: dict | None = None
|
_active_tool_profile: dict | None = None
|
||||||
@@ -10569,7 +10585,7 @@ async def arch_plan_from_spec(
|
|||||||
)
|
)
|
||||||
@mcp.tool(
|
@mcp.tool(
|
||||||
annotations={"title": "Understand: Describe Drawing", "readOnlyHint": True},
|
annotations={"title": "Understand: Describe Drawing", "readOnlyHint": True},
|
||||||
tags={"analysis", "query"},
|
tags={"understand", "analysis", "query"},
|
||||||
)
|
)
|
||||||
async def drawing_understand(
|
async def drawing_understand(
|
||||||
path: Annotated[
|
path: Annotated[
|
||||||
@@ -10638,7 +10654,7 @@ async def drawing_understand(
|
|||||||
)
|
)
|
||||||
@mcp.tool(
|
@mcp.tool(
|
||||||
annotations={"title": "Understand: Scale Check", "readOnlyHint": True},
|
annotations={"title": "Understand: Scale Check", "readOnlyHint": True},
|
||||||
tags={"analysis", "query"},
|
tags={"understand", "analysis", "query"},
|
||||||
)
|
)
|
||||||
async def drawing_scale_check(
|
async def drawing_scale_check(
|
||||||
reference: Annotated[
|
reference: Annotated[
|
||||||
@@ -10800,7 +10816,7 @@ async def _diff_markup(backend, result: dict, new_snap, *, rev: str, description
|
|||||||
)
|
)
|
||||||
@mcp.tool(
|
@mcp.tool(
|
||||||
annotations={"title": "Drawing: Diff Two Revisions", "destructiveHint": False},
|
annotations={"title": "Drawing: Diff Two Revisions", "destructiveHint": False},
|
||||||
tags={"analysis", "query"},
|
tags={"understand", "analysis", "query"},
|
||||||
)
|
)
|
||||||
async def drawing_diff(
|
async def drawing_diff(
|
||||||
old_path: Annotated[
|
old_path: Annotated[
|
||||||
@@ -10932,7 +10948,7 @@ async def drawing_diff(
|
|||||||
)
|
)
|
||||||
@mcp.tool(
|
@mcp.tool(
|
||||||
annotations={"title": "Drawing: Topology Check", "readOnlyHint": True},
|
annotations={"title": "Drawing: Topology Check", "readOnlyHint": True},
|
||||||
tags={"analysis", "query"},
|
tags={"understand", "analysis", "query"},
|
||||||
)
|
)
|
||||||
async def drawing_topology_check(
|
async def drawing_topology_check(
|
||||||
layers: Annotated[
|
layers: Annotated[
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""Golden query set for tool discovery: English in, one right tool out.
|
"""Golden query set for tool discovery: English in, one right tool out.
|
||||||
|
|
||||||
Ninety-nine cases (84 tuning + 15 holdout) split into a **tuning** set and a
|
One hundred and seven cases (92 tuning + 15 holdout) split into a **tuning** set and a
|
||||||
**holdout** set. The split is
|
**holdout** set. The split is
|
||||||
the point of the file. Ranking work is measured against
|
the point of the file. Ranking work is measured against
|
||||||
:data:`TUNING_CASES` only; :data:`HOLDOUT_CASES` were written at the same time,
|
:data:`TUNING_CASES` only; :data:`HOLDOUT_CASES` were written at the same time,
|
||||||
@@ -204,6 +204,19 @@ TUNING_CASES: tuple[GoldenCase, ...] = (
|
|||||||
GoldenCase("merdiven çiz", "arch_stair", "synonym"),
|
GoldenCase("merdiven çiz", "arch_stair", "synonym"),
|
||||||
GoldenCase("vaziyet planı çiz", "arch_plan_from_spec", "synonym"),
|
GoldenCase("vaziyet planı çiz", "arch_plan_from_spec", "synonym"),
|
||||||
GoldenCase("oda alanlarını hesapla", "arch_rooms_detect", "synonym"),
|
GoldenCase("oda alanlarını hesapla", "arch_rooms_detect", "synonym"),
|
||||||
|
# v1.6 track H - reading drawings somebody else made. Written before the
|
||||||
|
# merged alias records were measured against them; a miss is fixed by
|
||||||
|
# adding vocabulary to that tool's record in discovery/aliases.py, never by
|
||||||
|
# moving the expectation. Half of them Turkish: a takeoff is asked for in
|
||||||
|
# the language of the shop that prices the job.
|
||||||
|
GoldenCase("boru metrajı çıkar", "pipe_takeoff", "synonym"),
|
||||||
|
GoldenCase("kablo metrajı", "cable_takeoff", "synonym"),
|
||||||
|
GoldenCase("iki revizyonu karşılaştır", "drawing_diff", "synonym"),
|
||||||
|
GoldenCase("bu çizim ölçekli mi", "drawing_scale_check", "synonym"),
|
||||||
|
GoldenCase("what is in this drawing somebody sent me", "drawing_understand", "paraphrase"),
|
||||||
|
GoldenCase("pipe takeoff by diameter from the p&id", "pipe_takeoff", "paraphrase"),
|
||||||
|
GoldenCase("compare two revisions of this drawing", "drawing_diff", "paraphrase"),
|
||||||
|
GoldenCase("find dangling line ends and gaps", "drawing_topology_check", "paraphrase"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -208,3 +208,64 @@ def test_alias_text_contains_commands_and_synonyms():
|
|||||||
for token in (*record.acad, *record.synonyms):
|
for token in (*record.acad, *record.synonyms):
|
||||||
assert token in text
|
assert token in text
|
||||||
assert alias_text("no_such_tool") == ""
|
assert alias_text("no_such_tool") == ""
|
||||||
|
|
||||||
|
|
||||||
|
# ── track H: spec §12's five-language vocabulary ───────────────────────────
|
||||||
|
#
|
||||||
|
# One row per term the spec names, in the record(s) a plant engineer means by
|
||||||
|
# it. Turkish comes in both spellings (diacritic and ASCII-folded): the search
|
||||||
|
# tokenizer splits on a non-ASCII letter instead of folding it.
|
||||||
|
|
||||||
|
TRACK_H_TERMS = {
|
||||||
|
"pipe_takeoff": (
|
||||||
|
"takeoff",
|
||||||
|
"boq",
|
||||||
|
"metraj",
|
||||||
|
"metre cetveli",
|
||||||
|
"boru listesi",
|
||||||
|
"boru metrajı",
|
||||||
|
"boru metraji",
|
||||||
|
"boru metrajı çıkar",
|
||||||
|
"boru metraji cikar",
|
||||||
|
"спецификация", # specification: a bill of quantities
|
||||||
|
"ведомость", # statement: a quantity schedule
|
||||||
|
),
|
||||||
|
"cable_takeoff": (
|
||||||
|
"takeoff",
|
||||||
|
"boq",
|
||||||
|
"metraj",
|
||||||
|
"metre cetveli",
|
||||||
|
"kablo listesi",
|
||||||
|
"kablo metrajı",
|
||||||
|
"kablo metraji",
|
||||||
|
),
|
||||||
|
"drawing_diff": (
|
||||||
|
"compare",
|
||||||
|
"dwgcompare",
|
||||||
|
"fark",
|
||||||
|
"karşılaştır",
|
||||||
|
"karsilastir",
|
||||||
|
"revizyon farkı",
|
||||||
|
"revizyon farki",
|
||||||
|
"iki revizyonu karşılaştır",
|
||||||
|
"iki revizyonu karsilastir",
|
||||||
|
),
|
||||||
|
"drawing_scale_check": (
|
||||||
|
"scale check",
|
||||||
|
"ölçek",
|
||||||
|
"olcek",
|
||||||
|
"bu çizim ölçekli mi",
|
||||||
|
"bu cizim olcekli mi",
|
||||||
|
),
|
||||||
|
"drawing_understand": ("birim", "çizim birimi", "cizim birimi"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_track_h_records_carry_the_five_language_vocabulary():
|
||||||
|
missing = {
|
||||||
|
tool: [term for term in terms if term not in TOOL_ALIASES[tool].synonyms]
|
||||||
|
for tool, terms in TRACK_H_TERMS.items()
|
||||||
|
}
|
||||||
|
missing = {tool: terms for tool, terms in missing.items() if terms}
|
||||||
|
assert not missing, f"track-H records miss spec §12 vocabulary: {missing}"
|
||||||
|
assert "COMPARE" in TOOL_ALIASES["drawing_diff"].acad
|
||||||
|
|||||||
@@ -163,9 +163,9 @@ def test_the_cache_arithmetic_follows_the_published_multipliers():
|
|||||||
|
|
||||||
def test_the_idle_lane_covers_the_five_advertised_surfaces(report):
|
def test_the_idle_lane_covers_the_five_advertised_surfaces(report):
|
||||||
"""Default, lean, search - and the two single-vertical clients:
|
"""Default, lean, search - and the two single-vertical clients:
|
||||||
TOOL_PACKS=core,mech (the default surface minus exactly the pid and settings
|
TOOL_PACKS=core,mech (the default surface minus exactly the pid, settings,
|
||||||
packs, and the arch pack) and TOOL_PACKS=core,arch (minus pid, settings and
|
arch and plant packs) and TOOL_PACKS=core,arch (minus pid, settings, mech
|
||||||
mech)."""
|
and plant)."""
|
||||||
variants = {row["variant"]: row for row in report["idle"]}
|
variants = {row["variant"]: row for row in report["idle"]}
|
||||||
assert set(variants) == {"default", "lean", "packs_core_mech", "packs_core_arch", "search"}
|
assert set(variants) == {"default", "lean", "packs_core_mech", "packs_core_arch", "search"}
|
||||||
assert variants["default"]["advertised_tools"] > 100
|
assert variants["default"]["advertised_tools"] > 100
|
||||||
@@ -177,12 +177,14 @@ def test_the_idle_lane_covers_the_five_advertised_surfaces(report):
|
|||||||
- len(packs["pid"])
|
- len(packs["pid"])
|
||||||
- len(packs["settings"])
|
- len(packs["settings"])
|
||||||
- len(packs["arch"])
|
- len(packs["arch"])
|
||||||
|
- len(packs["plant"])
|
||||||
)
|
)
|
||||||
assert variants["packs_core_arch"]["advertised_tools"] == (
|
assert variants["packs_core_arch"]["advertised_tools"] == (
|
||||||
variants["default"]["advertised_tools"]
|
variants["default"]["advertised_tools"]
|
||||||
- len(packs["pid"])
|
- len(packs["pid"])
|
||||||
- len(packs["settings"])
|
- len(packs["settings"])
|
||||||
- len(packs["mech"])
|
- len(packs["mech"])
|
||||||
|
- len(packs["plant"])
|
||||||
)
|
)
|
||||||
for narrowed in ("packs_core_mech", "packs_core_arch"):
|
for narrowed in ("packs_core_mech", "packs_core_arch"):
|
||||||
assert (
|
assert (
|
||||||
|
|||||||
+56
-10
@@ -16,6 +16,13 @@ v1.6 track F adds the `arch` pack: SECTION 25's twelve architectural tools -
|
|||||||
the wall network, openings, stairs, rooms and their reader, schedules, the
|
the wall network, openings, stairs, rooms and their reader, schedules, the
|
||||||
grid and symbols, the furniture and sanitary catalogue, the exterior chains
|
grid and symbols, the furniture and sanitary catalogue, the exterior chains
|
||||||
and the whole plan from one spec. Lean gains four of them.
|
and the whole plan from one spec. Lean gains four of them.
|
||||||
|
|
||||||
|
v1.6 track H adds the `plant` pack: SECTION 27's two takeoffs (`pipe_takeoff`,
|
||||||
|
`cable_takeoff`) - quantities for a plant job, the vertical track D's Plant 3D
|
||||||
|
readers join later. SECTION 26's four readers (`drawing_understand`,
|
||||||
|
`drawing_scale_check`, `drawing_diff`, `drawing_topology_check`) stay in
|
||||||
|
`core`: understanding, comparing and checking a drawing is not a vertical.
|
||||||
|
Lean gains `drawing_understand` and reaches 65.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -135,6 +142,20 @@ ARCH_TOOLS = {
|
|||||||
"arch_plan_from_spec",
|
"arch_plan_from_spec",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UNDERSTAND_TOOLS = {
|
||||||
|
"drawing_understand",
|
||||||
|
"drawing_scale_check",
|
||||||
|
"drawing_diff",
|
||||||
|
"drawing_topology_check",
|
||||||
|
}
|
||||||
|
|
||||||
|
PLANT_TOOLS = {
|
||||||
|
"pipe_takeoff",
|
||||||
|
"cable_takeoff",
|
||||||
|
}
|
||||||
|
|
||||||
|
LEAN_UNDERSTAND_ESSENTIALS = {"drawing_understand"}
|
||||||
|
|
||||||
LEAN_ARCH_ESSENTIALS = {
|
LEAN_ARCH_ESSENTIALS = {
|
||||||
"arch_wall",
|
"arch_wall",
|
||||||
"arch_opening",
|
"arch_opening",
|
||||||
@@ -159,7 +180,7 @@ LEAN_SETTINGS_ESSENTIALS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#: Declaration order - what `tool_packs["available"]` reports.
|
#: Declaration order - what `tool_packs["available"]` reports.
|
||||||
ALL_PACKS = ["core", "pid", "settings", "mech", "arch"]
|
ALL_PACKS = ["core", "pid", "settings", "mech", "arch", "plant"]
|
||||||
#: `tool_packs["enabled"]` is sorted, which is no longer the same list.
|
#: `tool_packs["enabled"]` is sorted, which is no longer the same list.
|
||||||
ALL_PACKS_ENABLED = sorted(ALL_PACKS)
|
ALL_PACKS_ENABLED = sorted(ALL_PACKS)
|
||||||
|
|
||||||
@@ -172,19 +193,26 @@ async def _restore(monkeypatch):
|
|||||||
|
|
||||||
|
|
||||||
def test_pack_registry_names_real_tools_and_only_them():
|
def test_pack_registry_names_real_tools_and_only_them():
|
||||||
assert server.TOOL_PACK_NAMES == ("core", "pid", "settings", "mech", "arch")
|
assert server.TOOL_PACK_NAMES == ("core", "pid", "settings", "mech", "arch", "plant")
|
||||||
assert server.PACK_TOOL_NAMES["pid"] == frozenset(PID_TOOLS)
|
assert server.PACK_TOOL_NAMES["pid"] == frozenset(PID_TOOLS)
|
||||||
assert server.PACK_TOOL_NAMES["mech"] == frozenset(MECH_TOOLS)
|
assert server.PACK_TOOL_NAMES["mech"] == frozenset(MECH_TOOLS)
|
||||||
assert server.PACK_TOOL_NAMES["settings"] == frozenset(SETTINGS_TOOLS)
|
assert server.PACK_TOOL_NAMES["settings"] == frozenset(SETTINGS_TOOLS)
|
||||||
assert server.PACK_TOOL_NAMES["arch"] == frozenset(ARCH_TOOLS)
|
assert server.PACK_TOOL_NAMES["arch"] == frozenset(ARCH_TOOLS)
|
||||||
|
assert server.PACK_TOOL_NAMES["plant"] == frozenset(PLANT_TOOLS)
|
||||||
assert len(SETTINGS_TOOLS) == 22
|
assert len(SETTINGS_TOOLS) == 22
|
||||||
assert len(MECH_TOOLS) == 14
|
assert len(MECH_TOOLS) == 14
|
||||||
assert len(ARCH_TOOLS) == 12
|
assert len(ARCH_TOOLS) == 12
|
||||||
|
assert len(PLANT_TOOLS) == 2
|
||||||
assert not (server.PACK_TOOL_NAMES["settings"] & server.PACK_TOOL_NAMES["pid"])
|
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["pid"])
|
||||||
assert not (server.PACK_TOOL_NAMES["mech"] & server.PACK_TOOL_NAMES["settings"])
|
assert not (server.PACK_TOOL_NAMES["mech"] & server.PACK_TOOL_NAMES["settings"])
|
||||||
for pack in ("pid", "settings", "mech"):
|
for pack in ("pid", "settings", "mech"):
|
||||||
assert not (server.PACK_TOOL_NAMES["arch"] & server.PACK_TOOL_NAMES[pack]), pack
|
assert not (server.PACK_TOOL_NAMES["arch"] & server.PACK_TOOL_NAMES[pack]), pack
|
||||||
|
for pack in ("pid", "settings", "mech", "arch"):
|
||||||
|
assert not (server.PACK_TOOL_NAMES["plant"] & server.PACK_TOOL_NAMES[pack]), pack
|
||||||
|
assert not (UNDERSTAND_TOOLS & set().union(*server.PACK_TOOL_NAMES.values())), (
|
||||||
|
"SECTION 26 reads any drawing: core, not a vertical pack"
|
||||||
|
)
|
||||||
assert not (SHEET_TOOLS & set().union(*server.PACK_TOOL_NAMES.values())), (
|
assert not (SHEET_TOOLS & set().union(*server.PACK_TOOL_NAMES.values())), (
|
||||||
"SECTION 24 is core drafting, not a vertical pack"
|
"SECTION 24 is core drafting, not a vertical pack"
|
||||||
)
|
)
|
||||||
@@ -205,7 +233,9 @@ async def test_core_only_hides_pid_and_settings_but_keeps_styles_and_page_setup(
|
|||||||
assert SETTINGS_TOOLS <= disabled
|
assert SETTINGS_TOOLS <= disabled
|
||||||
assert MECH_TOOLS <= disabled
|
assert MECH_TOOLS <= disabled
|
||||||
assert ARCH_TOOLS <= disabled
|
assert ARCH_TOOLS <= disabled
|
||||||
|
assert PLANT_TOOLS <= disabled
|
||||||
assert not (SHEET_TOOLS & disabled)
|
assert not (SHEET_TOOLS & disabled)
|
||||||
|
assert not (UNDERSTAND_TOOLS & disabled), "the readers of SECTION 26 are core"
|
||||||
assert not (STYLE_TOOLS & disabled), "styles are core drafting essentials"
|
assert not (STYLE_TOOLS & disabled), "styles are core drafting essentials"
|
||||||
assert not (PAGE_SETUP_TOOLS & disabled), "page setup and templates are core"
|
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
|
assert "block_define" not in disabled and "entity_set_xdata" not in disabled
|
||||||
@@ -221,11 +251,21 @@ async def test_settings_pack_alone_keeps_pid_hidden(monkeypatch):
|
|||||||
assert info["tool_packs"]["enabled"] == ["core", "settings"]
|
assert info["tool_packs"]["enabled"] == ["core", "settings"]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_the_plant_pack_alone_adds_the_takeoffs_to_core(monkeypatch):
|
||||||
|
monkeypatch.setattr(config.settings, "tool_packs", "plant")
|
||||||
|
info = await server._apply_tool_profile("full")
|
||||||
|
disabled = set(info["disabled_tools"])
|
||||||
|
assert not (PLANT_TOOLS & disabled)
|
||||||
|
assert not (UNDERSTAND_TOOLS & disabled)
|
||||||
|
assert PID_TOOLS <= disabled and ARCH_TOOLS <= disabled
|
||||||
|
assert info["tool_packs"]["enabled"] == ["core", "plant"]
|
||||||
|
|
||||||
|
|
||||||
async def test_all_is_the_default_and_enables_every_pack(monkeypatch):
|
async def test_all_is_the_default_and_enables_every_pack(monkeypatch):
|
||||||
monkeypatch.setattr(config.settings, "tool_packs", "all")
|
monkeypatch.setattr(config.settings, "tool_packs", "all")
|
||||||
info = await server._apply_tool_profile("full")
|
info = await server._apply_tool_profile("full")
|
||||||
disabled = set(info["disabled_tools"])
|
disabled = set(info["disabled_tools"])
|
||||||
assert not ((PID_TOOLS | SETTINGS_TOOLS | MECH_TOOLS | ARCH_TOOLS) & disabled)
|
assert not ((PID_TOOLS | SETTINGS_TOOLS | MECH_TOOLS | ARCH_TOOLS | PLANT_TOOLS) & disabled)
|
||||||
assert info["tool_packs"]["enabled"] == ALL_PACKS_ENABLED
|
assert info["tool_packs"]["enabled"] == ALL_PACKS_ENABLED
|
||||||
|
|
||||||
|
|
||||||
@@ -254,22 +294,26 @@ async def test_lean_intersects_with_packs(monkeypatch):
|
|||||||
async def test_lean_carries_the_five_settings_essentials_and_nothing_from_the_settings_pack(
|
async def test_lean_carries_the_five_settings_essentials_and_nothing_from_the_settings_pack(
|
||||||
monkeypatch,
|
monkeypatch,
|
||||||
):
|
):
|
||||||
"""Spec §8.2: lean = 64 (55 + the five mechanical/sheet essentials + track
|
"""Spec §8.2: lean = 65 (55 + the five mechanical/sheet essentials + track
|
||||||
F's four architectural ones). The five settings essentials are core-pack
|
F's four architectural ones + track H's `drawing_understand`). The five
|
||||||
tools, so `TOOL_PACKS=core` leaves them on a lean surface; no environment
|
settings essentials and `drawing_understand` are core-pack tools, so
|
||||||
tool is lean."""
|
`TOOL_PACKS=core` leaves them on a lean surface; no environment tool and no
|
||||||
|
takeoff is lean."""
|
||||||
assert LEAN_SETTINGS_ESSENTIALS <= server.LEAN_TOOL_NAMES
|
assert LEAN_SETTINGS_ESSENTIALS <= server.LEAN_TOOL_NAMES
|
||||||
assert LEAN_MECH_ESSENTIALS <= server.LEAN_TOOL_NAMES
|
assert LEAN_MECH_ESSENTIALS <= server.LEAN_TOOL_NAMES
|
||||||
assert LEAN_ARCH_ESSENTIALS <= server.LEAN_TOOL_NAMES
|
assert LEAN_ARCH_ESSENTIALS <= server.LEAN_TOOL_NAMES
|
||||||
assert server.LEAN_TOOL_NAMES & ARCH_TOOLS == LEAN_ARCH_ESSENTIALS
|
assert server.LEAN_TOOL_NAMES & ARCH_TOOLS == LEAN_ARCH_ESSENTIALS
|
||||||
|
assert server.LEAN_TOOL_NAMES & UNDERSTAND_TOOLS == LEAN_UNDERSTAND_ESSENTIALS
|
||||||
|
assert not (PLANT_TOOLS & server.LEAN_TOOL_NAMES)
|
||||||
assert not (SETTINGS_TOOLS & server.LEAN_TOOL_NAMES)
|
assert not (SETTINGS_TOOLS & server.LEAN_TOOL_NAMES)
|
||||||
assert len(server.LEAN_TOOL_NAMES) == 64
|
assert len(server.LEAN_TOOL_NAMES) == 65
|
||||||
monkeypatch.setattr(config.settings, "tool_packs", "core")
|
monkeypatch.setattr(config.settings, "tool_packs", "core")
|
||||||
info = await server._apply_tool_profile("lean")
|
info = await server._apply_tool_profile("lean")
|
||||||
disabled = set(info["disabled_tools"])
|
disabled = set(info["disabled_tools"])
|
||||||
assert not (LEAN_SETTINGS_ESSENTIALS & disabled)
|
assert not (LEAN_SETTINGS_ESSENTIALS & disabled)
|
||||||
assert not ({"sheet_frame", "titleblock_apply"} & disabled), "sheet tools are core"
|
assert not ({"sheet_frame", "titleblock_apply"} & disabled), "sheet tools are core"
|
||||||
assert info["enabled_count"] == 64 - 10, (
|
assert "drawing_understand" not in disabled, "the one-call report is core"
|
||||||
|
assert info["enabled_count"] == 65 - 10, (
|
||||||
"lean minus the three pid, three mech and four arch tools"
|
"lean minus the three pid, three mech and four arch tools"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -284,7 +328,9 @@ async def test_the_three_track_e_sections_file_under_their_own_groups():
|
|||||||
assert set(groups["mechanical"]) == MECH_TOOLS
|
assert set(groups["mechanical"]) == MECH_TOOLS
|
||||||
assert set(groups["sheet"]) == SHEET_TOOLS
|
assert set(groups["sheet"]) == SHEET_TOOLS
|
||||||
assert set(groups["architecture"]) == ARCH_TOOLS
|
assert set(groups["architecture"]) == ARCH_TOOLS
|
||||||
assert len(groups) == 26
|
assert set(groups["understanding"]) == UNDERSTAND_TOOLS
|
||||||
|
assert set(groups["plant"]) == PLANT_TOOLS
|
||||||
|
assert len(groups) == 28
|
||||||
|
|
||||||
|
|
||||||
async def test_system_about_reports_packs():
|
async def test_system_about_reports_packs():
|
||||||
|
|||||||
+13
-33
@@ -495,7 +495,7 @@ async def test_tool_groups_is_byte_identical_to_the_source_declared_grouping():
|
|||||||
|
|
||||||
|
|
||||||
async def test_tool_group_sizes_are_unchanged():
|
async def test_tool_group_sizes_are_unchanged():
|
||||||
"""Frozen snapshot of the surface (243 tools, 26 groups).
|
"""Frozen snapshot of the surface (247 tools, 28 groups).
|
||||||
|
|
||||||
Taken before @cad_tool landed at 131 tools; `batch` moved 2 -> 3 when
|
Taken before @cad_tool landed at 131 tools; `batch` moved 2 -> 3 when
|
||||||
v1.5.0's `cad_batch` joined `entity_batch_create`/`entity_batch_modify`, and
|
v1.5.0's `cad_batch` joined `entity_batch_create`/`entity_batch_modify`, and
|
||||||
@@ -592,41 +592,19 @@ async def test_tool_group_sizes_are_unchanged():
|
|||||||
had grown while the tag was unranked on them returned to its
|
had grown while the tag was unranked on them returned to its
|
||||||
pre-track-F value.
|
pre-track-F value.
|
||||||
|
|
||||||
Track H group U opened SECTION 26 (Understanding & QA) with
|
`understanding` and `plant` appeared with v1.6's track H: `understanding`
|
||||||
`drawing_understand`, tagged `analysis` and `query`: `analysis` outranks
|
(4, SECTION 26: `drawing_understand`, `drawing_scale_check`,
|
||||||
`query` in `_GROUP_TAG_PRIORITY`, so the reader files under `analysis`
|
`drawing_diff`, `drawing_topology_check` - the readers of drawings
|
||||||
(12 -> 13) beside the other whole-drawing queries, and
|
somebody else made, core pack) and `plant` (2, SECTION 27: `pipe_takeoff`
|
||||||
`drawing_scale_check`, tagged the same, followed it (13 -> 14). This
|
and `cable_takeoff`, exactly `PACK_TOOL_NAMES["plant"]`). The three
|
||||||
snapshot is branch-local: the track H merge task recomputes it once the
|
track-H branches tagged the six `analysis` and left the priority list
|
||||||
understand, network and diff branches have landed.
|
alone, so `analysis` grew 12 -> 18 on them; the integration tagged
|
||||||
|
SECTION 26 `understand` and ranked `understand` and `plant` directly
|
||||||
Track H group D opened its copy of SECTION 26 (Understanding & QA) with
|
after `arch` in `_GROUP_TAG_PRIORITY`, and `analysis` returned to 12.
|
||||||
`drawing_diff` and `drawing_topology_check`, both tagged `analysis` and
|
|
||||||
`query`; `analysis` outranks `query` in `_GROUP_TAG_PRIORITY`, so both file
|
|
||||||
under `analysis` (12 -> 14). This snapshot is branch-local: the track H
|
|
||||||
merge recomputes it once the understand, network and diff branches have
|
|
||||||
landed.
|
|
||||||
|
|
||||||
Merged, the two SECTION 26 halves file all four readers under `analysis`
|
|
||||||
(12 -> 16).
|
|
||||||
|
|
||||||
`analysis` moved 12 -> 13 when track H's SECTION 27 opened with
|
|
||||||
`pipe_takeoff` on the network branch. It is tagged `plant` and
|
|
||||||
`analysis`, and `plant` is not in `_GROUP_TAG_PRIORITY` on that
|
|
||||||
branch, so it files under `analysis`. This snapshot is branch-local:
|
|
||||||
the track H integration task gives `plant` its pack and its group
|
|
||||||
once every branch has landed.
|
|
||||||
|
|
||||||
It moved 13 -> 14 when `cable_takeoff` joined the same section, for
|
|
||||||
the same branch-local reason.
|
|
||||||
|
|
||||||
With SECTION 27 merged beside them, the four readers and the two
|
|
||||||
takeoffs all file under `analysis` (12 -> 18) until the track H
|
|
||||||
integration task ranks `understand` and `plant`.
|
|
||||||
"""
|
"""
|
||||||
sizes = {label: len(names) for label, names in (await server._tool_groups()).items()}
|
sizes = {label: len(names) for label, names in (await server._tool_groups()).items()}
|
||||||
assert sizes == {
|
assert sizes == {
|
||||||
"analysis": 18,
|
"analysis": 12,
|
||||||
"architecture": 12,
|
"architecture": 12,
|
||||||
"batch": 3,
|
"batch": 3,
|
||||||
"blocks": 9,
|
"blocks": 9,
|
||||||
@@ -643,6 +621,7 @@ async def test_tool_group_sizes_are_unchanged():
|
|||||||
"mechanical": 14,
|
"mechanical": 14,
|
||||||
"page_setup": 6,
|
"page_setup": 6,
|
||||||
"pid": 9,
|
"pid": 9,
|
||||||
|
"plant": 2,
|
||||||
"premium": 12,
|
"premium": 12,
|
||||||
"sheet": 11,
|
"sheet": 11,
|
||||||
"solids": 5,
|
"solids": 5,
|
||||||
@@ -650,6 +629,7 @@ async def test_tool_group_sizes_are_unchanged():
|
|||||||
"system": 7,
|
"system": 7,
|
||||||
"templates": 2,
|
"templates": 2,
|
||||||
"transactions": 3,
|
"transactions": 3,
|
||||||
|
"understanding": 4,
|
||||||
"validation": 1,
|
"validation": 1,
|
||||||
"view": 4,
|
"view": 4,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user