release: 1.6.0

The v1.6 roadmap in five tracks: 154 -> 247 registered tools in 28 groups.

Evidence re-measured for the release:
- correctness A/B against v1.5.1: 26/48 -> 48/48, 22 fixed, 0 regressed
  (ab-v1.5.1-vs-v1.6.0.json, replacing the -dev report);
- the reference adapter on matrix v7: 21/21 (autocad-mcp-pro.json, which
  was the v3 run of 1.5); the published-report test follows it to v7;
- the 100-point rubric re-scored per category, every withheld point named:
  93 (the competitor rows are their July snapshots, not re-reviewed);
- the perf lane as medians of five alternating runs, beside a v1.4.0
  comparison on one interpreter (perf-v1.4.0-vs-v1.6.0.json).

Two roadmap criteria are not green, and the changelog, the README and the
roadmap say so: criterion 5 is withdrawn (track D, Plant 3D, dropped) and
criterion 7 is not met (creation 1.25x / 1.38x v1.4.0's time, still
1.17x / 1.20x with the timeout off). Carried to 1.7.

README rewritten for 1.6: what is new, the mechanical and architectural
sheets, the rubric and task-matrix charts, the measured limits (including
the four feature tables that ship empty), and the 1.7 roadmap. server.json
describes the 1.6 surface and declares TOOL_PACKS. CLAUDE.md corrects the
ISO 286 letter set and the empty feature tables.

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:
Umut CAN
2026-09-25 00:17:17 +03:00
co-authored by Claude Opus 5.5
parent 7e7fe11a71
commit 17bf3a39ce
28 changed files with 3843 additions and 2033 deletions
+4 -4
View File
@@ -29,9 +29,9 @@ ROOT = Path(__file__).resolve().parents[1]
#: The published A/B report the README and benchmarks/README quote. Bump this,
#: the two row labels below and the prose together, or the gate reports drift.
AB_REPORT = "benchmarks/results/published/ab-v1.5.1-vs-v1.6.0-dev.json"
AB_REPORT = "benchmarks/results/published/ab-v1.5.1-vs-v1.6.0.json"
AB_BASELINE_LABEL = r"v1\.5\.1"
AB_CURRENT_LABEL = r"v1\.6\.0-dev"
AB_CURRENT_LABEL = r"v1\.6\.0"
def _read(relative: str) -> str:
@@ -160,7 +160,7 @@ CHECKS: list[tuple[str, str, str, str]] = [
"README.md",
"A/B fixed count",
AB_CURRENT_LABEL
+ r"\*\* \*\(this branch\)\* \| \*\*\d+ / \d+\*\* \| \*\*\d+ %\*\* \| (\d+)",
+ r"\*\* \*\(this release\)\* \| \*\*\d+ / \d+\*\* \| \*\*\d+ %\*\* \| (\d+)",
"ab_fixed",
),
(
@@ -172,7 +172,7 @@ CHECKS: list[tuple[str, str, str, str]] = [
(
"benchmarks/README.md",
"A/B fixed count",
r"\*\*" + AB_CURRENT_LABEL + r"\*\* \(this branch\) \| \d+ / \d+ \| \d+ % \| (\d+)",
r"\*\*" + AB_CURRENT_LABEL + r"\*\* \(this release\) \| \d+ / \d+ \| \d+ % \| (\d+)",
"ab_fixed",
),
("CLAUDE.md", "A/B suite size", r"correctness_suite\.py` \((\d+) checks\)", "ab_total"),
+63
View File
@@ -0,0 +1,63 @@
"""Render the floor plan the README shows, using only the server's own tools.
The plan is ``EXAMPLE_SPEC`` from ``engineering/arch/spec.py`` - the document
``arch_plan_from_spec`` draws in one call: a 9 x 6 m brick ring split by an AAC
wall, an entrance door, an interior door, two windows, a straight stair, two
rooms whose areas are measured from the faces the walls enclose, the exterior
dimension chains and the door / window / room schedules. The ``arch_roundtrip``
benchmark task gates on the same spec, so a regression changes both.
Reproduce:
AUTOCAD_MCP_BACKEND=ezdxf uv run --frozen python scripts/render_readme_arch.py
"""
from __future__ import annotations
import argparse
import asyncio
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
DEFAULT_OUTPUT = ROOT / "docs" / "assets" / "arch-showcase.png"
async def render(output: Path) -> dict:
from backends.ezdxf_backend import EzdxfBackend
from engineering.arch.spec import EXAMPLE_SPEC, draw_plan_from_spec
from engineering.critique import run_critique
backend = EzdxfBackend()
await backend.connect()
try:
await backend.drawing_new()
result = await draw_plan_from_spec(backend, EXAMPLE_SPEC)
issues = await run_critique(backend, None)
png = await backend.view_screenshot()
if not png:
raise RuntimeError("view_screenshot returned no PNG (is matplotlib installed?)")
output.parent.mkdir(parents=True, exist_ok=True)
output.write_bytes(png)
return {
"output": str(output),
"bytes": len(png),
"omitted": len(result.get("omitted", [])),
"critique": len(issues),
}
finally:
await backend.disconnect()
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT)
args = parser.parse_args()
print(asyncio.run(render(args.output)))
return 0
if __name__ == "__main__":
raise SystemExit(main())
+63
View File
@@ -0,0 +1,63 @@
"""Render the mechanical sheet the README shows, using only the server's own tools.
The sheet is the one the ``mech_assembly`` benchmark task gates on - a flange
coupling: a hub with a full section, a flange with its bolt-circle end view, two
ISO 4014 bolts with ISO 4032 nuts, an ISO 7573 parts list read off the drawing,
linked ISO 6433 balloons, an ISO 5457 frame and an ISO 7200 title block. It is
built by the benchmark's own task, so the picture and the gate cannot drift
apart: if the view engine or the sheet standard regresses, both change.
Reproduce:
AUTOCAD_MCP_BACKEND=ezdxf uv run --frozen python scripts/render_readme_mech.py
"""
from __future__ import annotations
import argparse
import asyncio
import sys
import tempfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
DEFAULT_OUTPUT = ROOT / "docs" / "assets" / "mech-showcase.png"
async def render(output: Path) -> dict:
from benchmarks.adapters.autocad_mcp_pro import AutoCADMCPProAdapter
adapter = AutoCADMCPProAdapter(backend="ezdxf")
with tempfile.TemporaryDirectory(prefix="readme_mech_") as folder:
await adapter.setup(Path(folder))
try:
await adapter.backend.drawing_new() # the harness opens one before every task
passed, metrics = (await adapter._task_mech_assembly())[:2]
png = await adapter.backend.view_screenshot()
finally:
await adapter.cleanup()
if not png:
raise RuntimeError("view_screenshot returned no PNG (is matplotlib installed?)")
output.parent.mkdir(parents=True, exist_ok=True)
output.write_bytes(png)
return {
"output": str(output),
"bytes": len(png),
"gate_passed": passed,
"critique_issues": metrics["critique_issues"],
"score": metrics["score"],
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT)
args = parser.parse_args()
print(asyncio.run(render(args.output)))
return 0
if __name__ == "__main__":
raise SystemExit(main())