Files
cdsl-cad/backend/tests/test_autonomous_cdsl_generation.py
T
2026-08-31 14:16:08 +08:00

2892 lines
162 KiB
Python

from __future__ import annotations
import asyncio
import json
import shutil
import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import AsyncMock, patch
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "backend"))
from app.services.autonomous_cdsl_generation import ( # noqa: E402
AutonomousCdslGenerationRunner,
AutonomousGenerationError,
_format_correction_card,
_fragment_atomic_ids,
_fragment_feature_count,
_fragment_selector_tokens,
_requires_edge_selector_recovery,
_final_repair_card,
_checkpoint_token,
_geometry_fingerprint,
_has_material_volume_change,
_global_geometry_violations,
_is_engine_geometry_failure,
_material_volume_tolerance,
_operation_contract_payload,
_public_modeling_plan_review,
parse_completion_audit,
parse_completion_checklist,
parse_modeling_plan,
_preferred_tool_call,
_is_author_quota_error,
_is_author_transport_error,
autonomous_tools,
build_candidate,
parse_tool_arguments,
)
from app.services.cdsl_fragment import ( # noqa: E402
AutonomousFragmentError,
autonomous_candidate_prompt_tokens,
autonomous_selector_tokens,
materialize_autonomous_fragment,
)
from app.services.engine_service import load_engine, validate_cdsl # noqa: E402
from app.services.storage import WorkspaceStore, write_json # noqa: E402
from app.settings import ProviderConfig, ProviderModel, Settings # noqa: E402
from app.services.agent_service import conversation_user_context # noqa: E402
from app.services.visual_review import VisualReviewError # noqa: E402
BACKEND = ROOT / "backend"
def settings(root: Path) -> Settings:
provider = ProviderConfig("test", "Test", "https://example.invalid/v1", "test-key", (ProviderModel("test-model"),))
return Settings(
task_root=root / "tasks",
conversation_root=root / "conversations",
library_root=BACKEND / "cdsl_library",
engine_root=BACKEND / "engine" / "cdsl_engine",
llm_base_url=provider.base_url,
llm_api_key=provider.api_key,
llm_model="test-model",
llm_timeout_s=1,
default_provider_id="test",
providers=(provider,),
)
def base_fragment() -> dict:
return {
"sketch": {
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, 1]},
"profile": {"type": "polygon", "vertices": [[-10, -5], [10, -5], [10, 5], [-10, 5]]},
},
"feature": {"atomic_id": "extrude_add_blind", "params": {"distance_mm": 4}},
}
def completion_checklist() -> str:
return "- [ ] rectangular plate has the frozen 20 x 10 x 4 mm envelope"
def completed_plate_audit() -> str:
return "- [x] rectangular plate has the frozen 20 x 10 x 4 mm envelope :: rebuilt bbox is 20 x 10 x 4 mm"
def accepted_plate_batch_review() -> dict[str, object]:
return {
"schema_version": "cad.candidate-review.v1",
"node_id": "candidate",
"model": "deepseek-v4-flash-vision-exp",
"verdict": "accept",
"confidence": 0.98,
"batch_goal": "Create the rectangular base plate.",
"batch_goal_status": "achieved",
"coverage": [{
"item": "rectangular plate has the frozen 20 x 10 x 4 mm envelope",
"status": "complete",
"evidence": "The canonical views show the requested rectangular plate.",
}],
"evidence": ["The batch goal is visibly achieved."],
}
class AutonomousStorageTests(unittest.TestCase):
def test_modeling_plan_template_is_validated_without_lowering_to_cdsl(self) -> None:
plan_text = "\n".join([
"# Structures", "[STRUCTURE base]", "role: body", "requirements:", "- rectangular plate", "depends_on: none",
"# Features", "[FEATURE profile]", "structure: base", "operation: sketch_profile", "purpose: define outline", "depends_on: none", "topology_sensitive: no", "evidence: measure_model",
"[FEATURE solid]", "structure: base", "operation: extrude_add_blind", "purpose: create solid", "depends_on: profile", "topology_sensitive: no", "evidence: measure_model",
"# Steps", "[STEP step_1]", "goal: create base", "features: profile, solid", "covers:", "- rectangular plate", "relationship: same sketch and extrusion target", "prerequisites: none", "expected_state_change: one solid", "evidence: measure_model",
])
plan = parse_modeling_plan(plan_text, checklist=["rectangular plate"], runtime_atomic_ids={"extrude_add_blind"})
self.assertEqual(plan["steps"][0]["feature_ids"], ["profile", "solid"])
self.assertEqual(plan["features"][1]["depends_on"], ["profile"])
def test_modeling_plan_keeps_semantic_coverage_for_independent_reviewer(self) -> None:
plan = parse_modeling_plan(
"[STRUCTURE base]\nname: body\n[FEATURE solid]\nstructure: base\noperation: extrude_add_blind with a circular sketch\npurpose: body\n[STEP step_1]\ngoal: body\nfeatures: solid\ncovers: required hole\nrelationship: same body\n",
checklist=["required hole"], runtime_atomic_ids={"extrude_add_blind"},
)
self.assertEqual(plan["steps"][0]["step_id"], "step_1")
self.assertIn("extrude_add_blind with a circular sketch", plan["features"][0]["operation"])
def test_modeling_plan_rejects_explicit_unsupported_runtime_operation(self) -> None:
with self.assertRaisesRegex(AutonomousGenerationError, "PLAN_OPERATION_UNSUPPORTED:.*extrude_add"):
parse_modeling_plan(
"[FEATURE bolt]\noperation: extrude_add\npurpose: bolt head\n",
checklist=["bolt head"],
runtime_atomic_ids={"extrude_add_blind"},
)
def test_public_plan_review_scrubs_unsupported_operation_names(self) -> None:
public = _public_modeling_plan_review({
"verdict": "revise",
"issues": [{"message": "Prefer extrude_add for the boss."}],
"unsupported_operations": ["extrude_add"],
})
self.assertNotIn("extrude_add", json.dumps(public))
self.assertIn("unsupported_runtime_operation", json.dumps(public))
def test_modeling_plan_accepts_free_text_and_optional_step_headings(self) -> None:
plan = parse_modeling_plan(
"先建立沿 X 轴的长轴,再添加法兰和套筒。\n\n## STEP 2 前端细节\n增加环槽和保持器。",
checklist=["长轴", "环槽"],
)
self.assertEqual([item["step_id"] for item in plan["steps"]], ["step_1", "step_2"])
self.assertEqual(plan["steps"][1]["title"], "前端细节")
def test_plain_prose_plan_is_semantic_only(self) -> None:
plan = parse_modeling_plan("先创建底座,再添加支撑和孔。", checklist=["底座"])
self.assertEqual(plan["mode"], "semantic_only")
self.assertEqual(plan["steps"][0]["step_id"], "semantic_plan")
def test_global_geometry_blocks_multiple_solids_and_height_drift(self) -> None:
violations = _global_geometry_violations(
{
"solid_count": 2,
"bbox_mm": {"dimensions": [100.0, 32.0, 99.5]},
},
requirements="整体为单个机械零件;支撑座总高度约 82 mm。",
checklist=["所有主要实体相互连接"],
)
self.assertEqual({item["code"] for item in violations}, {"SOLID_COUNT_MISMATCH", "DIMENSION_OUT_OF_TOLERANCE"})
def test_global_geometry_can_defer_final_height_for_initial_step(self) -> None:
violations = _global_geometry_violations(
{"solid_count": 1, "bbox_mm": {"dimensions": [120.0, 38.0, 20.0]}},
requirements="支撑座总高度约 95 mm。",
checklist=["存在水平长方体底座"],
check_dimensions=False,
)
self.assertEqual(violations, [])
def test_completion_checklist_is_free_markdown_but_has_unique_pending_items(self) -> None:
items = parse_completion_checklist(
"# Completion\n- [ ] one coherent solid\n- [ ] six holes in each flange\n"
)
self.assertEqual(items, ["one coherent solid", "six holes in each flange"])
with self.assertRaisesRegex(AutonomousGenerationError, "begin with every checklist item unchecked"):
parse_completion_checklist("- [x] already done", require_unchecked=True)
with self.assertRaisesRegex(AutonomousGenerationError, "duplicate"):
parse_completion_checklist("- [ ] one solid\n- [ ] One solid")
def test_completion_audit_requires_every_frozen_item_and_evidence(self) -> None:
checklist = ["one coherent solid", "six holes in each flange"]
audit = parse_completion_audit(
"- [x] one coherent solid :: engine reports one solid\n- [ ] six holes in each flange :: not yet modelled",
checklist,
)
self.assertEqual([item["status"] for item in audit], ["complete", "missing"])
with self.assertRaisesRegex(AutonomousGenerationError, "missing: six holes"):
parse_completion_audit("- [x] one coherent solid :: engine reports one solid", checklist)
with self.assertRaisesRegex(AutonomousGenerationError, "needs evidence"):
parse_completion_audit(
"- [x] one coherent solid\n- [x] six holes in each flange :: inferred",
checklist,
)
def test_conversation_context_preserves_earlier_user_intent_without_keyword_rules(self) -> None:
context = conversation_user_context({"messages": [
{"id": "u1", "role": "user", "parts": [{"type": "text", "text": "Build a V-shaped guide block with mounting holes."}]},
{"id": "a1", "role": "assistant", "parts": [{"type": "text", "text": "Incorrect generated summary."}]},
{"id": "u2", "role": "user", "parts": [{"type": "text", "text": "retry"}]},
]})
self.assertEqual(len(context), 1)
self.assertIn("V-shaped guide block", context[0]["content"])
self.assertIn("retry", context[0]["content"])
self.assertNotIn("Incorrect generated summary", context[0]["content"])
def test_multi_tool_response_prioritizes_completion_over_observation(self) -> None:
inspect = {"function": {"name": "inspect_model", "arguments": "{}"}}
complete = {"function": {"name": "complete_task", "arguments": "{\"self_review\":\"done\"}"}}
self.assertIs(_preferred_tool_call([inspect, complete]), complete)
def test_multi_tool_priority_never_bypasses_current_tool_state(self) -> None:
requirements = {"function": {"name": "write_requirements_document", "arguments": "{}"}}
fragment = {"function": {"name": "submit_cdsl_fragment", "arguments": "{}"}}
self.assertIs(
_preferred_tool_call([requirements, fragment], allowed_names={"write_requirements_document"}),
requirements,
)
def test_no_progress_does_not_hide_recovery_tools(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "complete a checked model")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Checked model")
store.update_task(task_id, {"revision_id": "rev_001", "status": "success", "visibility": "checkpoint"})
store.set_active_revision(task_id, "rev_001")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
loaded = store.read_task(task_id) or {}
tools = runner._author_tools(loaded, requirements_frozen=True, state={"no_progress": 3, "recent_events": [{"kind": "tool_error"}]})
names = {tool["function"]["name"] for tool in tools}
self.assertIn("inspect_topology", names)
self.assertIn("submit_cdsl_fragment", names)
self.assertIn("complete_task", names)
def test_format_correction_forces_a_direct_runtime_contract_rewrite(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
engine = load_engine(current_settings)
fragment = {
"sketch": base_fragment()["sketch"],
"feature": {"atomic_id": "extrude_cut_blind", "params": {"depth_mm": 4}},
}
card = _format_correction_card(
engine,
fragment_json=json.dumps(fragment),
error_message="CDSL schema violation at $.features[2].params: 'distance_mm' is a required property",
head="main:rev_002",
)
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
tools = runner._author_tools(
{"active_revision": "rev_002", "active_branch_id": "main"},
requirements_frozen=True,
state={"format_correction": card},
)
self.assertEqual(card["atomic_id"], "extrude_cut_blind")
self.assertEqual(card["required_params"], ["distance_mm"])
self.assertEqual(card["optional_params"], ["reverse"])
self.assertTrue(card["additional_params_forbidden"])
self.assertEqual(card["unsupported_attempted_params"], ["depth_mm"])
self.assertEqual(card["valid_feature_shape"]["params"], {"distance_mm": "positive number"})
self.assertEqual([tool["function"]["name"] for tool in tools], ["submit_cdsl_fragment"])
def test_shared_revolve_axis_missing_angle_gets_a_specific_correction(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"sketch": base_fragment()["sketch"],
"feature": {"atomic_id": "revolve_add", "params": {}},
}
card = _format_correction_card(
engine,
fragment_json=json.dumps(fragment),
error_message=(
"revolve_add requires params.angle_deg; revolve_axis (including shared_revolve_axis) "
"supplies only params.axis. Declare an explicit angle in degrees."
),
head="main:root",
shared_revolve_axis={"origin_mm": [0, 0, 0], "direction": [0, 0, 1]},
)
self.assertEqual(card["missing_author_params"], ["angle_deg"])
self.assertIn("shared_revolve_axis", card["shared_revolve_axis_note"])
self.assertIn("partial revolutions are valid", card["instruction"])
self.assertEqual(card["valid_feature_shape"]["params"]["angle_deg"], "positive number up to 360")
def test_hole_correction_reports_every_author_error_in_one_card(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"feature": {
"atomic_id": "hole_blind",
"params": {"positions_mm": [[0, 0, 0]], "direction": [0, 0, -1], "host_face": "top"},
},
}
card = _format_correction_card(
engine,
fragment_json=json.dumps(fragment),
error_message=(
"HOLE_FRAGMENT_INVALID: params.host_face is server-owned; use selector_tokens; "
"missing params: diameter_mm, depth_mm, positions; unsupported params: direction, positions_mm; "
"hole_blind requires exactly one face selector token for its host face"
),
head="main:rev_002",
)
self.assertEqual(card["server_owned_attempted_params"], ["host_face"])
self.assertEqual(card["unsupported_attempted_params"], ["direction", "host_face", "positions_mm"])
self.assertIn("missing params: diameter_mm, depth_mm, positions", card["issues"])
self.assertIn("unsupported params: direction, positions_mm", card["issues"])
self.assertEqual(card["valid_feature_shape"]["selector_tokens"], ["exactly one current face token"])
self.assertEqual(card["valid_feature_shape"]["params"]["positions"], [{"mm": "[x_mm, y_mm, z_mm]"}])
def test_analytic_contour_correction_exposes_the_actual_contour_shape(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"sketch": {
"workplane": base_fragment()["sketch"]["workplane"],
"profile": {
"type": "analytic_contours",
"contours": [
{"type": "circle", "center": [-45, 0], "radius_mm": 5},
{"type": "circle", "center": [45, 0], "radius_mm": 5},
],
},
},
"feature": {"atomic_id": "extrude_cut_blind", "params": {"distance_mm": 4}},
}
card = _format_correction_card(
engine,
fragment_json=json.dumps(fragment),
error_message="CDSL schema violation at $.geometry.sketches[3].profile: analytic_contours is not valid under any of the given schemas",
head="main:rev_002",
)
profile = card["profile_correction"]
self.assertEqual(profile["valid_profile_shape"]["contours"][0]["segments"][0]["type"], "circle")
self.assertIn("one outer contour per separate cut island", profile["rules"][1])
def test_multi_feature_fragment_requires_an_explicit_relationship(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], dict]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "batch relationship")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nBuild a bracket.")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = {"candidate_attempts_by_head": {}, "read_operation_contract_ids": []}
events, _ = await runner._execute_tool(
task_id,
"batch relationship",
state,
load_engine(current_settings),
"submit_cdsl_fragment",
{
"batch_goal": "Create two related bracket cuts.",
"fragment_json": json.dumps({"features": [{"atomic_id": "extrude_cut_blind"}, {"atomic_id": "extrude_cut_blind"}]}),
},
)
return events, state
events, state = asyncio.run(exercise())
self.assertIn("BATCH_RELATIONSHIP_REQUIRED", events[0][1]["message"])
self.assertEqual(_fragment_feature_count({"features": [{"atomic_id": "a"}, {"atomic_id": "a"}]}), 2)
self.assertEqual(state["format_correction"]["attempted_feature_count"], 2)
def test_first_use_of_an_operation_requires_its_contract(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], dict, list[str]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "contract gate")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nBuild a plate.")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state: dict = {"candidate_attempts_by_head": {}, "read_operation_contract_ids": []}
engine = load_engine(current_settings)
events, progressed = await runner._execute_tool(
task_id,
"contract gate",
state,
engine,
"submit_cdsl_fragment",
{"batch_goal": "Create the initial rectangular plate.", "fragment_json": json.dumps(base_fragment())},
)
self.assertFalse(progressed)
self.assertEqual(state["candidate_action_required"]["reason"], "operation_contract_required")
self.assertEqual(state["candidate_action_required"]["atomic_ids"], ["extrude_add_blind"])
allowed = [
tool["function"]["name"]
for tool in runner._author_tools(store.read_task(task_id) or {}, requirements_frozen=True, state=state)
]
self.assertEqual(allowed, ["get_cdsl_operation_contract"])
_, progressed = await runner._execute_tool(
task_id,
"contract gate",
state,
engine,
"get_cdsl_operation_contract",
{"atomic_id": "extrude_add_blind"},
)
self.assertTrue(progressed)
return events, state, [
tool["function"]["name"]
for tool in runner._author_tools(store.read_task(task_id) or {}, requirements_frozen=True, state=state)
]
events, state, tools = asyncio.run(exercise())
self.assertIn("OPERATION_CONTRACT_REQUIRED", events[0][1]["message"])
self.assertEqual(state["read_operation_contract_ids"], ["extrude_add_blind"])
self.assertEqual(state["candidate_action_required"], {})
self.assertEqual(tools, ["submit_cdsl_fragment"])
self.assertEqual(_fragment_atomic_ids({"features": [{"atomic_id": "extrude_add_blind"}, {"feature": {"atomic_id": "revolve_add"}}]}), ["extrude_add_blind", "revolve_add"])
def test_selector_fragment_requires_a_current_topology_observation(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], dict, list[str]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "selector observation gate")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nBuild a plate.")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state: dict = {"candidate_attempts_by_head": {}, "read_operation_contract_ids": ["hole_blind"]}
fragment = {
"feature": {
"atomic_id": "hole_blind",
"params": {"diameter_mm": 4, "depth_mm": 6, "positions": [{"mm": [0, 0, 0]}]},
"selector_tokens": ["sel_invented"],
},
}
events, progressed = await runner._execute_tool(
task_id,
"selector observation gate",
state,
load_engine(current_settings),
"submit_cdsl_fragment",
{"batch_goal": "Create one mounting hole on the observed face.", "fragment_json": json.dumps(fragment)},
)
tools = [
item["function"]["name"]
for item in runner._author_tools(store.read_task(task_id) or {}, requirements_frozen=True, state=state)
]
self.assertFalse(progressed)
return events, state, tools
events, state, tools = asyncio.run(exercise())
self.assertIn("TOPOLOGY_SNAPSHOT_OBSERVATION_REQUIRED", events[0][1]["message"])
self.assertEqual(state["format_correction"], {})
self.assertEqual(state["candidate_action_required"]["reason"], "topology_selector_recovery")
self.assertEqual(tools, ["inspect_topology", "rollback_checkpoint"])
def test_hole_failure_keeps_all_corrections_while_requiring_a_fresh_face_token(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], dict, list[str]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "aggregate hole recovery")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nBuild a plate.")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = {"candidate_attempts_by_head": {}, "read_operation_contract_ids": ["hole_blind"]}
fragment = {
"feature": {
"atomic_id": "hole_blind",
"params": {"positions_mm": [[0, 0, 0]], "direction": [0, 0, -1]},
},
}
events, _ = await runner._execute_tool(
task_id, "aggregate hole recovery", state, load_engine(current_settings), "submit_cdsl_fragment",
{"batch_goal": "Create one mounting hole on the observed face.", "fragment": fragment},
)
tools = [
item["function"]["name"]
for item in runner._author_tools(store.read_task(task_id) or {}, requirements_frozen=True, state=state)
]
return events, state, tools
events, state, tools = asyncio.run(exercise())
self.assertIn("HOLE_FRAGMENT_INVALID", events[0][1]["message"])
self.assertIn("missing params: diameter_mm, depth_mm, positions", events[0][1]["message"])
self.assertIn("unsupported params: direction, positions_mm", events[0][1]["message"])
self.assertEqual(state["candidate_action_required"]["reason"], "topology_selector_recovery")
self.assertIn("missing params: diameter_mm, depth_mm, positions", state["format_correction"]["issues"])
self.assertEqual(tools, ["inspect_topology", "rollback_checkpoint"])
def test_brep_and_step_failures_do_not_enter_format_correction(self) -> None:
self.assertTrue(_is_engine_geometry_failure("STEP tessellation produced no renderable triangles"))
self.assertTrue(_is_engine_geometry_failure("'NoneType' object has no attribute 'NbNodes'"))
self.assertFalse(_is_engine_geometry_failure("CDSL schema violation at $.features[0].params"))
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
task = {"active_revision": "rev_001", "active_branch_id": "main"}
state = {
"candidate_action_required": {
"working_head": "main:rev_001",
"reason": "engine_geometry_invalid",
},
}
names = {
item["function"]["name"]
for item in runner._author_tools(task, requirements_frozen=True, state=state)
}
self.assertIn("render_views", names)
self.assertIn("submit_cdsl_fragment", names)
self.assertIn("rollback_checkpoint", names)
def test_step_tessellation_failure_enters_geometry_recovery_not_format_correction(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], dict]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "tessellation recovery")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nBuild a plate.")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state: dict = {
"candidate_attempts_by_head": {},
"read_operation_contract_ids": ["extrude_add_blind"],
}
with patch(
"app.services.autonomous_cdsl_generation.build_candidate",
side_effect=AutonomousGenerationError("STEP tessellation produced no renderable triangles"),
):
events, progressed = await runner._execute_tool(
task_id,
"tessellation recovery",
state,
load_engine(current_settings),
"submit_cdsl_fragment",
{"batch_goal": "Create the initial rectangular plate.", "fragment_json": json.dumps(base_fragment())},
)
self.assertFalse(progressed)
return events, state
events, state = asyncio.run(exercise())
self.assertEqual(events[0][0], "candidate_result")
self.assertEqual(state["format_correction"], {})
self.assertEqual(state["candidate_action_required"]["reason"], "engine_geometry_invalid")
self.assertIn("tessellation", state["engine_geometry_failure"]["error"])
def test_identical_format_error_increments_only_its_own_signature(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = json.dumps({"feature": {"atomic_id": "extrude_cut_blind", "params": {"depth_mm": 4}}})
first = _format_correction_card(engine, fragment_json=fragment, error_message="missing distance_mm", head="main:rev_002")
second = _format_correction_card(engine, fragment_json=fragment, error_message="missing distance_mm", head="main:rev_002", previous=first)
changed = _format_correction_card(engine, fragment_json=fragment, error_message="distance must be positive", head="main:rev_002", previous=second)
self.assertEqual(first["repeat_count"], 1)
self.assertEqual(second["repeat_count"], 2)
self.assertEqual(changed["repeat_count"], 1)
def test_finish_selector_error_requires_one_edge_lookup_before_resubmission(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
engine = load_engine(current_settings)
correction = _format_correction_card(
engine,
fragment_json=json.dumps({"feature": {"atomic_id": "chamfer", "params": {"distance_mm": 1.5}}}),
error_message="chamfer requires 1..64 selector token(s)",
head="main:rev_002",
)
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
task = {"active_revision": "rev_002", "active_branch_id": "main"}
self.assertTrue(_requires_edge_selector_recovery(correction))
self.assertTrue(correction["selector_tokens_required"])
self.assertEqual(correction["selector_token_kind"], "edge")
self.assertIn("selector_tokens", correction["valid_feature_shape"])
lookup_tools = runner._author_tools(
task,
requirements_frozen=True,
state={"candidate_action_required": {"working_head": "main:rev_002", "reason": "edge_selector_recovery"}},
)
submit_tools = runner._author_tools(
task,
requirements_frozen=True,
state={"candidate_action_required": {"working_head": "main:rev_002", "reason": "edge_selector_recovery_submit"}},
)
self.assertEqual([tool["function"]["name"] for tool in lookup_tools], ["inspect_topology"])
self.assertEqual(
[tool["function"]["name"] for tool in submit_tools],
["submit_cdsl_fragment", "rollback_checkpoint"],
)
def test_finish_recovery_keeps_the_exact_edge_token_bank_in_author_context(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "finish recovery")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen")
store.update_task(task_id, {"revision_id": "rev_001", "status": "success", "visibility": "checkpoint", "branch_id": "main"})
store.set_active_revision(task_id, "rev_001", branch_id="main")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = {
"recent_events": [],
"initial_context_delivered": True,
"candidate_action_required": {"working_head": "main:rev_001", "reason": "edge_selector_recovery_submit"},
"edge_selector_recovery": {
"working_head": "main:rev_001",
"tokens": [{"token": "sel_exact_edge", "kind": "edge", "geometry": {"length_mm": 20}}],
},
}
prompt = runner._prompt_messages(task_id, state, load_engine(current_settings))[-1]["content"]
tools = runner._author_tools(store.read_task(task_id) or {}, requirements_frozen=True, state=state)
self.assertIn("sel_exact_edge", prompt)
self.assertEqual([tool["function"]["name"] for tool in tools], ["submit_cdsl_fragment", "rollback_checkpoint"])
self.assertEqual(_fragment_selector_tokens({"feature": {"selector_tokens": ["sel_exact_edge"]}}), ["sel_exact_edge"])
def test_multi_feature_fragment_feedback_states_the_batch_limit(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"features": [
{"atomic_id": "extrude_cut_blind", "params": {"distance_mm": 4}},
{"atomic_id": "extrude_cut_blind", "params": {"distance_mm": 4}},
],
}
card = _format_correction_card(
engine,
fragment_json=json.dumps(fragment),
error_message="A fragment may add at most 6 feature(s)",
head="main:rev_002",
)
self.assertEqual(card["attempted_feature_count"], 2)
self.assertEqual(card["max_features_per_fragment"], 6)
self.assertIn("at most 6 feature", card["instruction"])
def test_author_context_explains_that_one_hole_feature_can_hold_a_repeated_position_group(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "a repeated hole pattern")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nCreate a repeated hole group.")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = runner._state(
task_id,
request="a repeated hole pattern",
conversation_id="conv_abcdef123456",
provider=current_settings.providers[0],
model=current_settings.providers[0].models[0],
initial_messages=[],
frozen_attachment_ids=[],
fresh=True,
)
context = json.loads(runner._prompt_messages(task_id, state, load_engine(current_settings))[-1]["content"])
rules = context["cdsl_authoring_basics"]["rules"]
contract = _operation_contract_payload(load_engine(current_settings), "hole_blind")
self.assertTrue(any("multiple positions" in rule for rule in rules))
self.assertEqual(contract["author_required_params"], ["diameter_mm", "depth_mm", "positions"])
self.assertEqual(contract["server_injected_params"], ["host_face"])
self.assertEqual(contract["selector_rule"], {"kind": "face", "min_items": 1, "max_items": 1})
def test_final_repair_recommends_only_the_immediate_predecessor_for_a_bad_last_feature(self) -> None:
task = {
"active_branch_id": "main",
"active_revision": "rev_006",
"revisions": [
{"revision_id": "rev_005", "parent_revision_id": "rev_004"},
{"revision_id": "rev_006", "parent_revision_id": "rev_005"},
],
}
review = {
"verdict": "repair",
"confidence": 0.9,
"evidence": ["The last extrude_cut makes a large rectangular slot rather than the required circular holes."],
}
card = _final_repair_card(task, review)
self.assertEqual(card["working_head"], "main:rev_006")
self.assertTrue(card["rollback_guidance"]["recommended_checkpoint_token"].startswith("checkpoint_"))
self.assertIn("immediate predecessor", card["rollback_guidance"]["reason"])
def test_geometry_fingerprint_ignores_runtime_identities_but_detects_shape_changes(self) -> None:
base = {
"records": [{
"record_id": "body:feature_001:face:0",
"feature_id": "feature_001",
"body_id": "body:feature_001",
"owner_feature_ids": ["feature_001"],
"kind": "face",
"geometry": {"center_mm": [0.0, 0.0, 4.0000000001], "area_mm2": 20.0},
"synthetic": False,
}],
}
equivalent = {
"records": [{
"record_id": "body:feature_002:face:9",
"feature_id": "feature_002",
"body_id": "body:feature_002",
"owner_feature_ids": ["feature_002"],
"kind": "face",
"geometry": {"center_mm": [0.0, -0.0, 4.0], "area_mm2": 20.0},
"synthetic": False,
}],
}
changed = {"records": [{**equivalent["records"][0], "geometry": {"center_mm": [0.0, 0.0, 3.0], "area_mm2": 20.0}}]}
forward_edge = {"records": [{
"kind": "edge",
"geometry": {"curve_type": "circle", "start_mm": [2, 0, 0], "end_mm": [-2, 0, 0], "length_mm": 6.28},
"synthetic": False,
}]}
reverse_edge = {"records": [{
"kind": "edge",
"geometry": {"curve_type": "circle", "start_mm": [-2, 0, 0], "end_mm": [2, 0, 0], "length_mm": 6.28},
"synthetic": False,
}]}
self.assertEqual(_geometry_fingerprint(base), _geometry_fingerprint(equivalent))
self.assertNotEqual(_geometry_fingerprint(base), _geometry_fingerprint(changed))
self.assertEqual(_geometry_fingerprint(forward_edge), _geometry_fingerprint(reverse_edge))
def test_sub_tolerance_volume_noise_is_not_a_material_candidate_change(self) -> None:
health = {
"bbox_mm": {"dimensions": [70.0, 70.0, 120.0]},
"volume_mm3": 243855.7778136896,
}
tolerance = _material_volume_tolerance(health)
self.assertEqual(tolerance, 0.01)
self.assertFalse(_has_material_volume_change(health, 243855.78125688632))
self.assertTrue(_has_material_volume_change(health, 243855.9))
def test_no_geometry_change_candidate_is_rejected_before_checkpoint(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "unchanged candidate")
task_id = str(task["task_id"])
engine = load_engine(current_settings)
base_cdsl, audit = materialize_autonomous_fragment(None, base_fragment(), engine=engine, selector_tokens={}, max_features=1)
first = build_candidate(settings=current_settings, store=store, task_id=task_id, cdsl=base_cdsl, fragment_audit=audit, parent_revision_id="")
revision_dir = store.revision_dir(task_id, "rev_001")
shutil.copytree(store.candidate_dir(task_id, str(first["candidate_id"])), revision_dir)
store.update_task(task_id, {"revision_id": "rev_001", "status": "success", "visibility": "checkpoint"})
store.clear_active_candidate(task_id, str(first["candidate_id"]))
duplicate_cdsl, duplicate_audit = materialize_autonomous_fragment(base_cdsl, base_fragment(), engine=engine, selector_tokens={}, max_features=1)
with self.assertRaisesRegex(AutonomousGenerationError, "CANDIDATE_GEOMETRY_UNCHANGED"):
build_candidate(
settings=current_settings,
store=store,
task_id=task_id,
cdsl=duplicate_cdsl,
fragment_audit=duplicate_audit,
parent_revision_id="rev_001",
)
after = store.read_task(task_id) or {}
self.assertEqual([item["revision_id"] for item in after["revisions"]], ["rev_001"])
self.assertEqual(after["active_candidate_id"], "")
def test_repeated_identical_format_error_reaches_a_terminal_limit(self) -> None:
async def exercise() -> dict:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
current_settings = Settings(**{**current_settings.__dict__, "agent_format_error_repeat_limit": 2})
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "format loop")
provider, model = current_settings.resolve_model("test", "test-model")
invalid = {
"sketch": base_fragment()["sketch"],
"feature": {"atomic_id": "extrude_add_blind", "params": {"travel_mm": 4}},
}
responses = iter([
("write_requirements_document", {"markdown": "# Frozen\nBuild a plate."}),
("write_completion_checklist", {"markdown": completion_checklist()}),
("submit_cdsl_fragment", {"fragment_json": json.dumps(invalid)}),
("submit_cdsl_fragment", {"fragment_json": json.dumps(invalid)}),
])
async def complete(_messages: list[dict], _tools: list[dict], _provider: ProviderConfig, _model: ProviderModel, _forced: str | None) -> dict:
name, arguments = next(responses)
return {"choices": [{"message": {"tool_calls": [{"id": name, "function": {"name": name, "arguments": json.dumps(arguments)}}]}}]}
runner = AutonomousCdslGenerationRunner(current_settings, store, complete)
events = [item async for item in runner.run(
task_id=str(task["task_id"]), request="format loop", conversation_id="conv_abcdef123456",
provider=provider, model=model, initial_messages=[],
)]
return {"events": events, "task": store.read_task(str(task["task_id"])) or {}, "state": store.read_agent_state(str(task["task_id"])) or {}}
result = asyncio.run(exercise())
terminal = result["events"][-1]
self.assertEqual(terminal[0], "task_terminal")
self.assertEqual(terminal[1]["lifecycle"], "failed")
self.assertIn("AUTHORING_FORMAT_LOOP", terminal[1]["message"])
self.assertEqual(result["task"]["lifecycle"], "failed")
self.assertEqual(result["state"]["format_correction"]["repeat_count"], 2)
def test_transport_failure_selects_another_configured_author(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
primary = ProviderConfig("primary", "Primary", "https://primary.invalid", "key", (ProviderModel("primary-model"),))
alternate = ProviderConfig("alternate", "Alternate", "https://alternate.invalid", "key", (ProviderModel("alternate-model"),))
current_settings = settings(Path(temporary))
current_settings = Settings(
**{**current_settings.__dict__, "default_provider_id": "primary", "providers": (primary, alternate), "llm_model": "primary-model"},
)
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
fallback = runner._transport_fallback_author(primary, {"author_transport_failed_providers": ["primary"]})
self.assertIsNotNone(fallback)
assert fallback is not None
self.assertEqual((fallback[0].id, fallback[1].id), ("alternate", "alternate-model"))
def test_requirements_document_is_written_once_and_frozen(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
store = WorkspaceStore(settings(Path(temporary)))
task = store.ensure_task(None, "make a bracket")
task_id = str(task["task_id"])
path = store.write_requirements_document(task_id, "# Frozen requirements\nUse millimetres.")
self.assertEqual(path.name, "requirements.md")
self.assertEqual(store.read_source_requirements(task_id), "make a bracket\n")
self.assertEqual((store.task_dir(task_id) / "source-requirements.md").read_text(encoding="utf-8"), "make a bracket\n")
self.assertIn("Use millimetres", store.read_requirements_document(task_id))
with self.assertRaisesRegex(ValueError, "frozen"):
store.write_requirements_document(task_id, "replace it")
def test_legacy_task_is_not_migrated_into_a_resumable_autonomous_task(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
store = WorkspaceStore(settings(Path(temporary)))
task = store.ensure_task(None, "legacy task")
path = store.task_path(str(task["task_id"]))
record = json.loads(path.read_text(encoding="utf-8"))
record.update({"schema_version": "1.3", "lifecycle": "running", "generation_spec_path": "generation-spec.json"})
path.write_text(json.dumps(record), encoding="utf-8")
loaded = store.read_task(str(task["task_id"]))
self.assertEqual(loaded["schema_version"], "1.3")
self.assertEqual(loaded["lifecycle"], "running")
def test_root_rollback_creates_a_new_branch_and_supersedes_descendants(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
store = WorkspaceStore(settings(Path(temporary)))
task = store.ensure_task(None, "rollback")
task_id = str(task["task_id"])
for revision_id, parent in (("rev_001", ""), ("rev_002", "rev_001")):
store.update_task(task_id, {
"revision_id": revision_id,
"status": "success",
"parent_revision_id": parent,
"visibility": "checkpoint",
})
rolled = store.rollback_to_revision(task_id, "", branch_id="branch_test")
self.assertEqual(rolled["active_revision"], "")
self.assertEqual(rolled["active_branch_id"], "branch_test")
self.assertEqual({item["visibility"] for item in rolled["revisions"]}, {"superseded"})
def test_rollback_leaves_other_branch_checkpoints_intact(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
store = WorkspaceStore(settings(Path(temporary)))
task = store.ensure_task(None, "branch rollback")
task_id = str(task["task_id"])
for revision_id, parent, branch_id in (
("rev_001", "", "main"),
("rev_002", "rev_001", "main"),
("rev_003", "rev_001", "other_branch"),
):
store.update_task(task_id, {
"revision_id": revision_id,
"status": "success",
"parent_revision_id": parent,
"branch_id": branch_id,
"visibility": "checkpoint",
})
store.set_active_revision(task_id, "rev_002", branch_id="main")
rolled = store.rollback_to_revision(task_id, "rev_001", branch_id="branch_new")
visibility = {item["revision_id"]: item["visibility"] for item in rolled["revisions"]}
self.assertEqual(visibility["rev_002"], "superseded")
self.assertEqual(visibility["rev_003"], "checkpoint")
class AutonomousFragmentTests(unittest.TestCase):
def test_fragment_limit_allows_a_coherent_two_feature_batch_and_rejects_seven(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"sketches": [base_fragment()["sketch"]],
"features": [
base_fragment()["feature"],
{"atomic_id": "reference_axis", "params": {"axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]}}},
],
}
_, audit = materialize_autonomous_fragment(None, fragment, engine=engine, selector_tokens={}, max_features=6)
self.assertEqual(len(audit["assigned_feature_ids"]), 2)
with self.assertRaisesRegex(AutonomousFragmentError, "at most 6"):
materialize_autonomous_fragment(
None,
{"features": [{"atomic_id": "reference_axis", "params": {"axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]}}}] * 7},
engine=engine,
selector_tokens={},
max_features=6,
)
def test_candidate_health_counts_current_solids_not_feature_history(self) -> None:
from app.services.autonomous_cdsl_generation import _candidate_health
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
step_path = root / "model.step"
glb_path = root / "model.glb"
step_path.write_bytes(b"step")
glb_path.write_bytes(b"glb")
health = _candidate_health({
"bbox_mm": {"min": [0, 0, 0], "max": [10, 10, 2]},
"volume_mm3": 100,
"solid_count": 1,
"feature_results": [{"body_id": "body:base"}, {"body_id": "body:cut"}],
}, step_path, glb_path)
self.assertEqual(health["solid_count"], 1)
def test_author_cannot_provide_committed_identity_or_raw_selectors(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
engine = load_engine(current_settings)
invalid = base_fragment()
invalid["feature"]["id"] = "invented_feature"
with self.assertRaisesRegex(AutonomousFragmentError, "server-owned"):
materialize_autonomous_fragment(None, invalid, engine=engine, selector_tokens={}, max_features=1)
def test_one_free_fragment_materializes_to_valid_complete_cdsl(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
engine = load_engine(current_settings)
cdsl, audit = materialize_autonomous_fragment(None, base_fragment(), engine=engine, selector_tokens={}, max_features=1)
cdsl["part_id"] = "autonomous-test"
validate_cdsl(cdsl, engine)
self.assertEqual(audit["assigned_feature_ids"], ["feature_001"])
self.assertEqual(audit["assigned_sketch_ids"], ["sketch_001"])
self.assertEqual(cdsl["features"][0]["depends_on"], [])
def test_blind_extrude_depth_alias_is_normalized_before_cdsl_validation(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
engine = load_engine(current_settings)
fragment = base_fragment()
fragment["feature"]["params"] = {"depth_mm": 4}
cdsl, audit = materialize_autonomous_fragment(None, fragment, engine=engine, selector_tokens={}, max_features=1)
cdsl["part_id"] = "depth-alias-test"
validate_cdsl(cdsl, engine)
self.assertEqual(cdsl["features"][0]["params"], {"distance_mm": 4})
self.assertEqual(
audit["compatibility_fixes"],
[{"path": "feature.params", "from": "depth_mm", "to": "distance_mm", "action": "renamed_equivalent"}],
)
self.assertEqual(fragment["feature"]["params"], {"depth_mm": 4})
def test_conflicting_depth_and_distance_are_not_silently_overwritten(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = base_fragment()
fragment["feature"]["params"] = {"depth_mm": 4, "distance_mm": 6}
with self.assertRaisesRegex(AutonomousFragmentError, "CONFLICTING_PARAMETER_ALIASES"):
materialize_autonomous_fragment(None, fragment, engine=engine, selector_tokens={}, max_features=1)
def test_explicit_axis_origin_alias_is_normalized_without_guessing_an_axis(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"feature": {
"atomic_id": "reference_axis",
"params": {"axis": {"origin": [0, 0, 0], "direction": [0, 0, 1]}},
},
}
cdsl, audit = materialize_autonomous_fragment(None, fragment, engine=engine, selector_tokens={}, max_features=1)
self.assertEqual(cdsl["features"][0]["params"]["axis"], {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]})
self.assertEqual(audit["compatibility_fixes"][0]["from"], "origin")
def test_revolve_axis_is_author_required_and_materializes_without_axis_tokens(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
engine = load_engine(current_settings)
contract = _operation_contract_payload(engine, "revolve_cut")
base_cdsl, _ = materialize_autonomous_fragment(None, base_fragment(), engine=engine, selector_tokens={}, max_features=6)
fragment = {
"sketch": {
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 1, 0]},
"profile": {"type": "polygon", "vertices": [[-2, 1], [2, 1], [2, 3], [-2, 3]]},
},
"feature": {
"atomic_id": "revolve_cut",
"params": {"angle_deg": 360, "axis": {"origin_mm": [0, 0, 0], "direction": [1, 0, 0]}},
},
}
cdsl, _ = materialize_autonomous_fragment(base_cdsl, fragment, engine=engine, selector_tokens={}, max_features=6)
cdsl["part_id"] = "revolve-axis-authoring"
validate_cdsl(cdsl, engine)
self.assertEqual(contract["author_required_params"], ["angle_deg", "axis"])
self.assertEqual(contract["server_injected_params"], [])
self.assertIsNone(contract["selector_rule"])
self.assertEqual(cdsl["features"][-1]["params"]["axis"]["direction"], [1, 0, 0])
self.assertEqual(contract["fragment_template"]["feature"]["params"]["angle_deg"], 360)
self.assertEqual(contract["fragment_template"]["feature"]["params"]["axis"]["direction"], [0, 0, 1])
self.assertEqual(contract["shared_revolve_axis"]["fills"], "feature.params.axis only")
self.assertEqual(
contract["shared_revolve_axis"]["fragment_template"]["feature"]["params"],
{"angle_deg": 360},
)
def test_revolve_missing_angle_is_rejected_before_json_schema_validation(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"revolve_axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]},
"sketch": base_fragment()["sketch"],
"feature": {"atomic_id": "revolve_add", "params": {}},
}
with self.assertRaisesRegex(
AutonomousFragmentError,
r"revolve_add requires params\.angle_deg; revolve_axis \(including shared_revolve_axis\) supplies only params\.axis",
):
materialize_autonomous_fragment(None, fragment, engine=engine, selector_tokens={}, max_features=6)
def test_revolve_without_explicit_axis_is_rejected_before_schema_retry_loop(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"sketch": base_fragment()["sketch"],
"feature": {"atomic_id": "revolve_cut", "params": {"angle_deg": 360}},
}
with self.assertRaisesRegex(AutonomousFragmentError, "requires params.axis"):
materialize_autonomous_fragment(None, fragment, engine=engine, selector_tokens={}, max_features=6)
def test_revolve_axis_must_lie_in_its_sketch_plane_before_candidate_staging(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"sketch": base_fragment()["sketch"],
"feature": {
"atomic_id": "revolve_add",
"params": {"angle_deg": 180, "axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]}},
},
}
with self.assertRaisesRegex(AutonomousFragmentError, "REVOLVE_AXIS_NOT_IN_SKETCH_PLANE"):
materialize_autonomous_fragment(None, fragment, engine=engine, selector_tokens={}, max_features=1)
def test_cdsl_only_runtime_rejects_out_of_plane_revolve_axis(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
engine = load_engine(settings(root))
cdsl = {
"schema": "cad.cdsl.llm.v1",
"schema_version": "1.1.0",
"kind": "part",
"part_id": "runtime-axis-check",
"geometry": {"sketches": [{
"id": "sketch_001",
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, 1]},
"profile": {"type": "polygon", "vertices": [[-2, -1], [2, -1], [2, 1], [-2, 1]]},
}]},
"features": [{
"id": "feature_001",
"atomic_id": "revolve_add",
"depends_on": [],
"sketch_id": "sketch_001",
"selectors": [],
"params": {"angle_deg": 180, "axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]}},
}],
}
validate_cdsl(cdsl, engine)
with self.assertRaisesRegex(Exception, "REVOLVE_AXIS_NOT_IN_SKETCH_PLANE"):
engine.run_cdsl_only(cdsl, root / "invalid.step")
def test_revolve_common_axis_aliases_are_normalized_losslessly(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
engine = load_engine(current_settings)
fragment = {
"sketch": base_fragment()["sketch"],
"feature": {
"atomic_id": "revolve_add",
"params": {
"angle_degrees": 360,
"axis_point_mm": [0, 0, 0],
"axis_dir": [1, 0, 0],
},
},
}
cdsl, audit = materialize_autonomous_fragment(None, fragment, engine=engine, selector_tokens={}, max_features=6)
cdsl["part_id"] = "revolve-axis-aliases"
validate_cdsl(cdsl, engine)
self.assertEqual(cdsl["features"][0]["params"], {
"angle_deg": 360,
"axis": {"origin_mm": [0, 0, 0], "direction": [1, 0, 0]},
})
self.assertEqual(
[(item["from"], item["to"]) for item in audit["compatibility_fixes"]],
[("angle_degrees", "angle_deg"), ("axis_point_mm", "axis.origin_mm"), ("axis_dir", "axis.direction")],
)
def test_batch_revolve_axis_and_feature_local_sketches_materialize(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
engine = load_engine(current_settings)
fragment = {
"revolve_axis": {"origin_mm": [0, 0, 0], "direction": [1, 0, 0]},
"features": [
{
"atomic_id": "revolve_add",
"params": {"angle_deg": 360},
"sketches": [base_fragment()["sketch"]],
},
{
"atomic_id": "revolve_cut",
"params": {"angle_deg": 360},
"sketches": [{
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, 1]},
"profile": {"type": "polygon", "points": [[-5, 0], [5, 0], [5, 2], [-5, 2]]},
}],
},
],
}
cdsl, audit = materialize_autonomous_fragment(None, fragment, engine=engine, selector_tokens={}, max_features=6)
cdsl["part_id"] = "batch-revolve-axis"
validate_cdsl(cdsl, engine)
self.assertEqual(len(cdsl["geometry"]["sketches"]), 2)
self.assertEqual(len(cdsl["features"]), 2)
self.assertTrue(all(feature["params"]["axis"]["direction"] == [1, 0, 0] for feature in cdsl["features"]))
self.assertTrue(any(item["action"] == "copied_explicit_batch_axis" for item in audit["compatibility_fixes"]))
self.assertTrue(any(item["from"] == "points" and item["to"] == "vertices" for item in audit["compatibility_fixes"]))
def test_mixed_batch_wrapper_pairs_only_sketch_features(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
engine = load_engine(current_settings)
base_cdsl, _ = materialize_autonomous_fragment(
None, base_fragment(), engine=engine, selector_tokens={}, max_features=6
)
fragment = {
"features": [
{
"feature": {
"atomic_id": "sphere_add",
"params": {"radius_mm": 3, "center_mm": [0, 0, 0]},
},
},
{
"sketch": {
"workplane": {"origin_mm": [0, 0, 4], "x_dir": [1, 0, 0], "normal": [0, 0, -1]},
"profile": {"type": "circle", "center": [0, 0], "radius_mm": 1},
},
"feature": {
"atomic_id": "extrude_cut_blind",
"params": {"depth_mm": 2, "direction": [0, 0, -1]},
},
},
],
}
cdsl, audit = materialize_autonomous_fragment(
base_cdsl, fragment, engine=engine, selector_tokens={}, max_features=6
)
cdsl["part_id"] = "mixed-wrapper-batch"
validate_cdsl(cdsl, engine)
sphere, cut = cdsl["features"][-2:]
self.assertNotIn("sketch_id", sphere)
self.assertIn("sketch_id", cut)
self.assertEqual(cut["params"], {"distance_mm": 2})
self.assertTrue(any(item["action"] == "unwrapped_equivalent" for item in audit["compatibility_fixes"]))
def test_param_embedded_sketch_and_named_direction_are_normalized(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"features": [{
"atomic_id": "extrude_add_blind",
"params": {
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, 1]},
"profile": {"type": "circle", "center": [0, 0], "radius_mm": 5},
"depth_mm": 8,
"direction": "negative",
},
}],
}
cdsl, audit = materialize_autonomous_fragment(
None, fragment, engine=engine, selector_tokens={}, max_features=6
)
cdsl["part_id"] = "embedded-param-sketch"
validate_cdsl(cdsl, engine)
self.assertEqual(cdsl["features"][0]["params"], {"distance_mm": 8, "reverse": True})
self.assertEqual(cdsl["geometry"]["sketches"][0]["profile"]["type"], "circle")
self.assertTrue(any(item["from"] == "workplane/profile" for item in audit["compatibility_fixes"]))
def test_revolve_angle_radians_and_wrapper_shape_are_normalized(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"revolve_axis": {"origin_mm": [0, 0, 0], "direction": [1, 0, 0]},
"features": [{
"sketch": {
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 1, 0]},
"profile": {"type": "polygon", "vertices": [[-2, 1], [2, 1], [2, 3], [-2, 3]]},
},
"feature": {"atomic_id": "revolve_add", "params": {"angle_rad": 6.283185307179586}},
}],
}
cdsl, audit = materialize_autonomous_fragment(
None, fragment, engine=engine, selector_tokens={}, max_features=6
)
cdsl["part_id"] = "radian-revolve"
validate_cdsl(cdsl, engine)
self.assertAlmostEqual(cdsl["features"][0]["params"]["angle_deg"], 360)
self.assertEqual(cdsl["features"][0]["params"]["axis"]["direction"], [1, 0, 0])
self.assertTrue(any(item["action"] == "converted_unit" for item in audit["compatibility_fixes"]))
def test_concentric_circle_shorthand_is_expanded_to_analytic_contours(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"sketch": {
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, 1]},
"profile": {
"type": "analytic_contours",
"contours": [
{"type": "circle", "center": [0, 0], "radius_mm": 12},
{"type": "circle", "center": [0, 0], "radius_mm": 10.8},
],
},
},
"feature": {"atomic_id": "extrude_add_blind", "params": {"distance_mm": 1.2}},
}
cdsl, audit = materialize_autonomous_fragment(
None, fragment, engine=engine, selector_tokens={}, max_features=6
)
cdsl["part_id"] = "concentric-circle-ring"
validate_cdsl(cdsl, engine)
contours = cdsl["geometry"]["sketches"][0]["profile"]["contours"]
self.assertEqual([contour["role"] for contour in contours], ["outer", "inner"])
self.assertTrue(any(item["action"] == "expanded_equivalent" for item in audit["compatibility_fixes"]))
def test_sphere_add_materializes_without_a_placeholder_sketch(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"feature": {
"atomic_id": "sphere_add",
"params": {"radius_mm": 2.5, "center_mm": [3, -4, 5]},
},
}
cdsl, _ = materialize_autonomous_fragment(
None, fragment, engine=engine, selector_tokens={}, max_features=6
)
cdsl["part_id"] = "sphere-without-sketch"
validate_cdsl(cdsl, engine)
self.assertEqual(cdsl["geometry"]["sketches"], [])
self.assertNotIn("sketch_id", cdsl["features"][0])
def test_legacy_sphere_locator_sketch_is_dropped_without_changing_geometry(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"sketch": {
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, 1]},
"profile": {"type": "circle", "radius_mm": 1},
},
"feature": {
"atomic_id": "sphere_add",
"params": {"radius_mm": 2.5, "center_mm": [3, -4, 5]},
},
}
cdsl, audit = materialize_autonomous_fragment(
None, fragment, engine=engine, selector_tokens={}, max_features=6
)
cdsl["part_id"] = "legacy-sphere-locator"
validate_cdsl(cdsl, engine)
self.assertEqual(cdsl["geometry"]["sketches"], [])
self.assertTrue(any(item["action"] == "dropped_unused_legacy_locator" for item in audit["compatibility_fixes"]))
def test_submit_tool_exposes_a_structured_shared_revolve_axis(self) -> None:
tool = next(item for item in autonomous_tools() if item["function"]["name"] == "submit_cdsl_fragment")
axis = tool["function"]["parameters"]["properties"]["shared_revolve_axis"]
self.assertEqual(axis["required"], ["origin_mm", "direction"])
self.assertFalse(axis["additionalProperties"])
def test_geometry_conclusion_tool_declares_modify_plan_contract(self) -> None:
tool = next(item for item in autonomous_tools() if item["function"]["name"] == "record_geometry_conclusion")
parameters = tool["function"]["parameters"]
plan = parameters["properties"]["optimization_plan"]
self.assertEqual(plan["properties"]["action"]["minLength"], 1)
self.assertIn("skip", parameters["properties"]["decision"]["enum"])
self.assertEqual(plan["anyOf"], [{"required": ["action"]}, {"required": ["next_action"]}, {"required": ["reason"]}])
self.assertEqual(parameters["allOf"][0]["then"], {"required": ["optimization_plan"]})
def test_geometry_conclusion_can_skip_step_satisfied_by_prior_geometry(self) -> None:
async def exercise() -> tuple[dict, dict, list[tuple[str, dict]]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "skip satisfied step")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen")
state = {
"modeling_plan_enforced": True,
"modeling_plan_status": "approved",
"active_plan_step_id": "step_2",
"modeling_plan": {"steps": [
{"step_id": "step_1", "status": "complete", "feature_ids": ["base"]},
{"step_id": "step_2", "status": "pending", "feature_ids": ["bore"]},
{"step_id": "step_3", "status": "pending", "feature_ids": ["holes"]},
]},
"plan_step_status": {"step_1": "complete", "step_2": "pending", "step_3": "pending"},
"plan_feature_status": {"base": "complete", "bore": "pending", "holes": "pending"},
"geometry_rejection": {"working_head": "main:root", "geometry_fingerprint": "root", "conclusion_required": True},
"geometry_diagnoses_by_fingerprint": {"root": {"observations": {"measure": {"ref": "diag_root_measure"}}}},
}
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
events, progressed = await runner._execute_tool(
task_id, "skip satisfied step", state, load_engine(current_settings), "record_geometry_conclusion",
{"root_cause": "duplicate_feature", "evidence_refs": ["diag_root_measure"], "decision": "skip", "optimization_plan": {"reason": "prior revolve already contains the bore"}},
)
return state, {"progressed": progressed}, events
state, result, events = asyncio.run(exercise())
self.assertTrue(result["progressed"])
self.assertEqual(events[0][1]["decision"], "skip")
self.assertEqual(events[0][1]["planStepId"], "step_2")
self.assertEqual(events[0][1]["nextPlanStepId"], "step_3")
self.assertEqual(state["plan_step_status"]["step_2"], "skipped")
self.assertEqual(state["plan_feature_status"]["bore"], "satisfied_by_prior_step")
self.assertEqual(state["geometry_rejection"], {})
def test_geometry_conclusion_tool_enumerates_only_current_evidence_refs(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
state = {
"geometry_rejection": {
"working_head": "main:rev_001",
"geometry_fingerprint": "fingerprint",
"conclusion_required": True,
},
"geometry_diagnoses_by_fingerprint": {
"fingerprint": {
"observations": {
"inspect": {"ref": "diag_fingerprint_inspect"},
"measure": {"ref": "diag_fingerprint_measure"},
},
"conclusion": None,
},
},
}
tools = runner._author_tools(
{"active_revision": "rev_001", "active_branch_id": "main"},
requirements_frozen=True,
state=state,
)
conclusion = next(item for item in tools if item["function"]["name"] == "record_geometry_conclusion")
evidence_items = conclusion["function"]["parameters"]["properties"]["evidence_refs"]["items"]
self.assertEqual(evidence_items["enum"], ["diag_fingerprint_inspect", "diag_fingerprint_measure"])
self.assertIn("Copy evidence_refs exactly from the enum", conclusion["function"]["description"])
def test_verified_current_completion_exposes_direct_plan_skip(self) -> None:
async def exercise() -> tuple[list[str], dict, list[tuple[str, dict]], list[Path]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "skip redundant verified step")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen")
store.update_task(task_id, {
"revision_id": "rev_001",
"status": "success",
"visibility": "checkpoint",
"branch_id": "main",
})
store.set_active_revision(task_id, "rev_001", branch_id="main")
state = {
"modeling_plan_enforced": True,
"modeling_plan_status": "approved",
"active_plan_step_id": "step_2",
"active_plan_action_id": "step_2_action_1",
"modeling_plan": {"steps": [
{"step_id": "step_1", "status": "complete", "feature_ids": [], "actions": []},
{"step_id": "step_2", "status": "pending", "feature_ids": [], "actions": [
{"action_id": "step_2_action_1", "required": True, "status": "pending"},
]},
{"step_id": "step_3", "status": "pending", "feature_ids": [], "actions": [
{"action_id": "step_3_action_1", "required": True, "status": "pending"},
]},
]},
"plan_step_status": {"step_1": "complete", "step_2": "pending", "step_3": "pending"},
"plan_action_status": {"step_2_action_1": "pending", "step_3_action_1": "pending"},
"completion_ledger": {
"verified_revision": "rev_001",
"items": [{"item": "complete model", "status": "complete", "evidence": "Independent review passed."}],
},
}
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
loaded = store.read_task(task_id) or {}
tool_names = [
item["function"]["name"]
for item in runner._author_tools(loaded, requirements_frozen=True, state=state)
]
events, progressed = await runner._execute_tool(
task_id,
"skip redundant verified step",
state,
load_engine(current_settings),
"skip_satisfied_plan_step",
{"reason": "Current independent completion evidence already proves this step."},
)
candidate_files = list(store.task_dir(task_id).glob("candidates/*/candidate.json"))
return tool_names, {"state": state, "progressed": progressed}, events, candidate_files
tool_names, result, events, candidate_files = asyncio.run(exercise())
self.assertEqual(tool_names, ["skip_satisfied_plan_step"])
self.assertTrue(result["progressed"])
self.assertEqual(result["state"]["plan_step_status"]["step_2"], "skipped")
self.assertEqual(result["state"]["active_plan_step_id"], "step_3")
self.assertEqual(events[0][0], "plan_step_skipped")
self.assertEqual(events[0][1]["evidenceRef"], "completion_ledger:rev_001")
self.assertEqual(candidate_files, [])
def test_stale_completion_ledger_cannot_skip_plan_step(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
state = {
"modeling_plan_enforced": True,
"modeling_plan_status": "approved",
"active_plan_step_id": "step_2",
"modeling_plan": {"steps": [{"step_id": "step_2", "status": "pending", "actions": []}]},
"plan_step_status": {"step_2": "pending"},
"completion_ledger": {
"verified_revision": "rev_001",
"items": [{"item": "complete model", "status": "complete", "evidence": "Old review."}],
},
}
names = {
item["function"]["name"]
for item in runner._author_tools(
{"active_revision": "rev_002", "active_branch_id": "main"},
requirements_frozen=True,
state=state,
)
}
self.assertNotIn("skip_satisfied_plan_step", names)
def test_incomplete_completion_ledger_cannot_skip_plan_step(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "incomplete completion proof")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen")
store.write_completion_checklist(task_id, "- [ ] base exists\n- [ ] bore exists")
store.update_task(task_id, {
"revision_id": "rev_001", "status": "success", "visibility": "checkpoint", "branch_id": "main",
})
store.set_active_revision(task_id, "rev_001", branch_id="main")
state = {
"modeling_plan_enforced": True,
"modeling_plan_status": "approved",
"active_plan_step_id": "step_2",
"modeling_plan": {"steps": [{"step_id": "step_2", "status": "pending", "actions": []}]},
"plan_step_status": {"step_2": "pending"},
"completion_ledger": {
"verified_revision": "rev_001",
"items": [{"item": "base exists", "status": "complete", "evidence": "visible"}],
},
}
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
names = {
item["function"]["name"]
for item in runner._author_tools(
store.read_task(task_id) or {}, requirements_frozen=True, state=state,
)
}
self.assertNotIn("skip_satisfied_plan_step", names)
def test_contract_tool_uses_exact_runtime_operation_enum(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
runner = AutonomousCdslGenerationRunner(settings(Path(temporary)), WorkspaceStore(settings(Path(temporary))), AsyncMock())
tool = next(
item for item in runner._author_tools(
{"active_revision": "", "active_branch_id": "main"}, requirements_frozen=True, state={}
) if item["function"]["name"] == "get_cdsl_operation_contract"
)
schema = tool["function"]["parameters"]["properties"]["atomic_id"]
self.assertIn("revolve_add", schema["enum"])
self.assertNotIn("extrude_cut", schema["enum"])
def test_repeated_active_operation_contract_is_not_progress(self) -> None:
async def exercise() -> tuple[bool, bool, list[str], list[str]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "do not repeat the active contract")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen requirements")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state: dict = {}
engine = load_engine(current_settings)
_, first_progressed = await runner._execute_tool(
task_id,
"do not repeat the active contract",
state,
engine,
"get_cdsl_operation_contract",
{"atomic_id": "extrude_add_blind"},
)
state["candidate_action_required"] = {
"working_head": "main:root",
"reason": "engine_geometry_invalid",
}
contract_tool = next(
item for item in runner._author_tools(
store.read_task(task_id) or {}, requirements_frozen=True, state=state,
) if item["function"]["name"] == "get_cdsl_operation_contract"
)
selectable = contract_tool["function"]["parameters"]["properties"]["atomic_id"]["enum"]
_, repeated_progressed = await runner._execute_tool(
task_id,
"do not repeat the active contract",
state,
engine,
"get_cdsl_operation_contract",
{"atomic_id": "extrude_add_blind"},
)
event_kinds = [str(item.get("kind") or "") for item in state.get("recent_events") or ()]
return first_progressed, repeated_progressed, selectable, event_kinds
first_progressed, repeated_progressed, selectable, event_kinds = asyncio.run(exercise())
self.assertTrue(first_progressed)
self.assertFalse(repeated_progressed)
self.assertNotIn("extrude_add_blind", selectable)
self.assertTrue(selectable)
self.assertEqual(event_kinds[-1], "operation_contract_repeated")
def test_root_rollback_is_blocked_when_best_checkpoint_exists(self) -> None:
async def exercise() -> str:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "protect checkpoint")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen")
store.update_task(task_id, {"revision_id": "rev_001", "parent_revision_id": "", "status": "success", "branch_id": "main"})
store.update_task(task_id, {"revision_id": "rev_002", "parent_revision_id": "rev_001", "status": "success", "branch_id": "main"})
store.set_active_revision(task_id, "rev_002", branch_id="main")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = {"best_known_checkpoint": "rev_002"}
events, _ = await runner._execute_tool(
task_id, "protect checkpoint", state, load_engine(current_settings), "rollback_checkpoint",
{"checkpoint_token": "root", "reason": "retry the model"},
)
return events[0][1]["message"]
self.assertIn("ROLLBACK_WOULD_DISCARD_VALID_PROGRESS", asyncio.run(exercise()))
def test_conflicting_revolve_axis_aliases_are_not_guessed(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
fragment = {
"sketch": base_fragment()["sketch"],
"feature": {
"atomic_id": "revolve_add",
"params": {
"angle_deg": 360,
"axis": {"origin_mm": [0, 0, 0], "direction": [1, 0, 0]},
"axis_dir": [0, 1, 0],
},
},
}
with self.assertRaisesRegex(AutonomousFragmentError, "CONFLICTING_PARAMETER_ALIASES"):
materialize_autonomous_fragment(None, fragment, engine=engine, selector_tokens={}, max_features=6)
def test_selector_tokens_are_opaque_and_snapshot_scoped(self) -> None:
tokens = autonomous_selector_tokens({
"snapshot_id": "cad_test/rev_001",
"records": [{
"record_id": "body:base:edge:7",
"kind": "edge",
"feature_id": "base",
"owner_feature_ids": ["base"],
"geometry": {"curve_type": "line", "length_mm": 10},
"executable": True,
}],
})
token, record = next(iter(tokens.items()))
self.assertTrue(token.startswith("sel_"))
self.assertEqual(record["kind"], "edge")
self.assertNotIn("stable_id", {"token": token, "kind": record["kind"], "geometry": record["geometry"]})
def test_selector_prompt_tokens_exclude_runtime_only_geometry_details(self) -> None:
tokens = {
"sel_compact": {
"kind": "edge",
"geometry": {
"center_mm": [2, 3, 4],
"curve_type": "line",
"length_mm": 12.5,
"adjacency_signature": "expensive-runtime-detail",
"start_mm": [0, 0, 0],
"end_mm": [5, 5, 5],
},
},
}
prompt_tokens = autonomous_candidate_prompt_tokens(tokens)
self.assertEqual(prompt_tokens[0]["geometry"], {
"center_mm": [2, 3, 4], "curve_type": "line", "length_mm": 12.5,
})
self.assertNotIn("adjacency_signature", json.dumps(prompt_tokens))
self.assertNotIn("start_mm", json.dumps(prompt_tokens))
self.assertNotIn("end_mm", json.dumps(prompt_tokens))
def test_current_topology_token_bank_survives_event_trimming_and_expires_with_snapshot(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "topology token bank")
task_id = str(task["task_id"])
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
topology = {
"snapshot_id": f"{task_id}/main:root",
"records": [{
"record_id": "face:top",
"kind": "face",
"feature_id": "base",
"owner_feature_ids": ["base"],
"geometry": {"center_mm": [0, 0, 4], "normal": [0, 0, 1]},
"executable": True,
}],
}
bank = autonomous_candidate_prompt_tokens(autonomous_selector_tokens(topology))
runner._artifact_data = lambda _task_id, _task: ( # type: ignore[method-assign]
{"geometry": {"sketches": []}, "features": []}, topology, {}, "main:root"
)
state = {
"topology_observation": {
"working_head": "main:root",
"snapshot_id": topology["snapshot_id"],
"tokens": bank,
},
"recent_events": [
{"kind": "inspect_topology", "result": {"tokens": bank}},
{"kind": "get_cdsl_operation_contract", "result": {"atomic_id": "hole_blind"}},
],
}
context = json.loads(runner._prompt_messages(task_id, state, load_engine(current_settings))[-1]["content"])
self.assertEqual(context["current_topology_tokens"]["tokens"], bank)
self.assertEqual(context["current_topology_tokens"]["snapshot_id"], topology["snapshot_id"])
topology["snapshot_id"] = f"{task_id}/main:changed"
stale_context = json.loads(runner._prompt_messages(task_id, state, load_engine(current_settings))[-1]["content"])
self.assertIsNone(stale_context["current_topology_tokens"])
def test_submit_rejects_a_token_outside_the_current_token_bank(self) -> None:
async def exercise() -> list[tuple[str, dict]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "token bank submit gate")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nBuild a plate.")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
topology = {
"snapshot_id": f"{task_id}/main:root",
"records": [{
"record_id": "face:top", "kind": "face", "feature_id": "base", "owner_feature_ids": ["base"],
"geometry": {"center_mm": [0, 0, 4], "normal": [0, 0, 1]}, "executable": True,
}],
}
bank = autonomous_candidate_prompt_tokens(autonomous_selector_tokens(topology))
runner._artifact_data = lambda _task_id, _task: (None, topology, None, "main:root") # type: ignore[method-assign]
state = {
"candidate_attempts_by_head": {},
"read_operation_contract_ids": ["hole_blind"],
"topology_observation": {"working_head": "main:root", "snapshot_id": topology["snapshot_id"], "tokens": bank},
}
fragment = {
"feature": {
"atomic_id": "hole_blind",
"params": {"diameter_mm": 4, "depth_mm": 6, "positions": [{"mm": [0, 0, 4]}]},
"selector_tokens": ["sel_not_in_bank"],
},
}
events, _ = await runner._execute_tool(
task_id, "token bank submit gate", state, load_engine(current_settings), "submit_cdsl_fragment",
{"batch_goal": "Create one mounting hole on the observed face.", "fragment_json": json.dumps(fragment)},
)
return events
events = asyncio.run(exercise())
self.assertIn("TOPOLOGY_TOKEN_UNOBSERVED", events[0][1]["message"])
def test_forged_selector_token_is_rejected_before_candidate_staging(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
engine = load_engine(current_settings)
fragment = base_fragment()
fragment["feature"]["selector_tokens"] = ["sel_not_from_this_snapshot"]
with self.assertRaisesRegex(AutonomousFragmentError, "TOPOLOGY_TOKEN_INVALID"):
materialize_autonomous_fragment(None, fragment, engine=engine, selector_tokens={}, max_features=1)
def test_fragment_selector_token_reader_covers_every_feature(self) -> None:
fragment = {
"features": [
{"feature": {"atomic_id": "hole_blind", "params": {}, "selector_tokens": ["sel_face"]}},
{"atomic_id": "fillet", "params": {}, "selector_tokens": ["sel_edge"]},
],
}
self.assertEqual(_fragment_selector_tokens(fragment), ["sel_face", "sel_edge"])
def test_hole_uses_server_injected_host_face_without_a_sketch(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
engine = load_engine(current_settings)
base_cdsl, _ = materialize_autonomous_fragment(None, base_fragment(), engine=engine, selector_tokens={}, max_features=1)
face_tokens = autonomous_selector_tokens({
"snapshot_id": "cad_test/rev_001",
"records": [{
"record_id": "face:top", "kind": "face", "feature_id": "feature_001",
"owner_feature_ids": ["feature_001"], "geometry": {"center_mm": [0, 0, 4], "normal": [0, 0, 1]},
"executable": True,
}],
})
token = next(iter(face_tokens))
fragment = {
"feature": {
"atomic_id": "hole_blind",
"params": {"diameter_mm": 4, "depth_mm": 6, "positions": [{"mm": [0, 0, 4]}]},
"selector_tokens": [token],
},
}
cdsl, _ = materialize_autonomous_fragment(base_cdsl, fragment, engine=engine, selector_tokens=face_tokens, max_features=1)
cdsl["part_id"] = "hole-contract-test"
validate_cdsl(cdsl, engine)
self.assertNotIn("sketch_id", cdsl["features"][-1])
self.assertIn("host_face", cdsl["features"][-1]["params"])
def test_single_feature_accepts_safe_top_level_selector_token_shorthand(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
engine = load_engine(current_settings)
base_cdsl, _ = materialize_autonomous_fragment(None, base_fragment(), engine=engine, selector_tokens={}, max_features=1)
face_tokens = autonomous_selector_tokens({
"snapshot_id": "cad_test/rev_001",
"records": [{
"record_id": "face:top", "kind": "face", "feature_id": "feature_001",
"owner_feature_ids": ["feature_001"], "geometry": {"center_mm": [0, 0, 4], "normal": [0, 0, 1]},
"executable": True,
}],
})
token = next(iter(face_tokens))
fragment = {
"feature": {
"atomic_id": "hole_blind",
"params": {"diameter_mm": 4, "depth_mm": 6, "positions": [{"mm": [0, 0, 4]}]},
},
"selector_tokens": [token],
}
cdsl, _ = materialize_autonomous_fragment(base_cdsl, fragment, engine=engine, selector_tokens=face_tokens, max_features=1)
self.assertIn("host_face", cdsl["features"][-1]["params"])
def test_transport_error_is_retryable_but_quota_error_is_not_transport(self) -> None:
self.assertTrue(_is_author_transport_error(RuntimeError("LLM connection failed after 3 attempts: timeout")))
self.assertFalse(_is_author_transport_error(RuntimeError("LLM request failed (429): quota exhausted")))
def test_hole_raw_host_face_is_rejected_with_actionable_guidance(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
engine = load_engine(current_settings)
invalid = {
"feature": {
"atomic_id": "hole_blind",
"params": {"diameter_mm": 4, "depth_mm": 6, "positions": [{"mm": [0, 0, 0]}], "host_face": "top"},
},
}
with self.assertRaisesRegex(AutonomousFragmentError, "selector_tokens"):
materialize_autonomous_fragment(None, invalid, engine=engine, selector_tokens={}, max_features=1)
def test_hole_materialization_aggregates_missing_params_aliases_and_host_token(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
engine = load_engine(settings(Path(temporary)))
invalid = {
"feature": {
"atomic_id": "hole_blind",
"params": {"positions_mm": [[0, 0, 0]], "direction": [0, 0, -1]},
},
}
with self.assertRaisesRegex(
AutonomousFragmentError,
r"missing params: diameter_mm, depth_mm, positions; unsupported params: direction, positions_mm; hole_blind requires exactly one face selector token",
):
materialize_autonomous_fragment(None, invalid, engine=engine, selector_tokens={}, max_features=1)
def test_failed_candidate_build_never_creates_a_revision(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "invalid candidate")
malformed = base_fragment()
malformed["feature"]["params"] = {}
engine = load_engine(current_settings)
cdsl, audit = materialize_autonomous_fragment(None, malformed, engine=engine, selector_tokens={}, max_features=1)
with self.assertRaises(ValueError):
build_candidate(
settings=current_settings,
store=store,
task_id=str(task["task_id"]),
cdsl=cdsl,
fragment_audit=audit,
parent_revision_id="",
)
after = store.read_task(str(task["task_id"])) or {}
self.assertEqual(after["revisions"], [])
self.assertEqual(after["active_candidate_id"], "")
candidates = list(store.task_dir(str(task["task_id"])).glob("candidates/*/candidate.json"))
self.assertEqual(len(candidates), 1)
self.assertEqual(json.loads(candidates[0].read_text(encoding="utf-8"))["status"], "failed")
class AutonomousToolTests(unittest.TestCase):
def test_rejected_independent_review_keeps_checkpoint_unchanged_and_auditable(self) -> None:
async def exercise() -> tuple[dict, dict, dict, list[str], list[str]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "reject the wrong base")
task_id = str(task["task_id"])
store.start_generation(task_id, request="reject the wrong base")
store.write_requirements_document(task_id, "# Frozen\nBuild a plate.")
store.write_completion_checklist(task_id, completion_checklist())
provider, model = current_settings.resolve_model("test", "test-model")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = runner._state(
task_id, request="reject the wrong base", conversation_id="conv_abcdef123456", provider=provider,
model=model, initial_messages=[], frozen_attachment_ids=[], fresh=True,
)
state["read_operation_contract_ids"] = ["extrude_add_blind"]
rejected_review = {**accepted_plate_batch_review(), "verdict": "reject", "batch_goal_status": "failed", "evidence": ["The plate is visibly the wrong shape."]}
with patch("app.services.autonomous_cdsl_generation.render_checkpoint", return_value={"views": []}), patch(
"app.services.autonomous_cdsl_generation.review_candidate_batch",
AsyncMock(return_value=rejected_review),
):
events, progressed = await runner._execute_tool(
task_id, "reject the wrong base", state, load_engine(current_settings), "submit_cdsl_fragment",
{"batch_goal": "Create the rectangular base plate.", "fragment_json": json.dumps(base_fragment())},
)
candidates = list(store.task_dir(task_id).glob("candidates/*/candidate.json"))
tools = [item["function"]["name"] for item in runner._author_tools(
store.read_task(task_id) or {}, requirements_frozen=True, state=state,
)]
return store.read_task(task_id) or {}, json.loads(candidates[0].read_text(encoding="utf-8")), state, [name for name, _ in events], tools
task, candidate, state, events, tools = asyncio.run(exercise())
self.assertEqual(task["active_revision"], "")
self.assertEqual(task["active_candidate_id"], "")
self.assertEqual(candidate["status"], "rejected")
self.assertIn("candidate_review_path", candidate)
self.assertIn("candidate_review", events)
self.assertEqual(state["candidate_action_required"]["reason"], "candidate_review_rejected")
self.assertEqual(tools, ["submit_cdsl_fragment", "rollback_checkpoint"])
def test_reviewer_failure_is_reported_without_advancing_the_checkpoint(self) -> None:
async def exercise() -> tuple[dict, dict, list[tuple[str, dict]]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "review outage")
task_id = str(task["task_id"])
store.start_generation(task_id, request="review outage")
store.write_requirements_document(task_id, "# Frozen\nBuild a plate.")
store.write_completion_checklist(task_id, completion_checklist())
provider, model = current_settings.resolve_model("test", "test-model")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = runner._state(
task_id, request="review outage", conversation_id="conv_abcdef123456", provider=provider,
model=model, initial_messages=[], frozen_attachment_ids=[], fresh=True,
)
state["read_operation_contract_ids"] = ["extrude_add_blind"]
with patch("app.services.autonomous_cdsl_generation.render_checkpoint", return_value={"views": []}), patch(
"app.services.autonomous_cdsl_generation.review_candidate_batch",
AsyncMock(side_effect=VisualReviewError("review endpoint timed out")),
):
events, _ = await runner._execute_tool(
task_id, "review outage", state, load_engine(current_settings), "submit_cdsl_fragment",
{"batch_goal": "Create the rectangular base plate.", "fragment_json": json.dumps(base_fragment())},
)
candidate = json.loads(next(store.task_dir(task_id).glob("candidates/*/candidate.json")).read_text(encoding="utf-8"))
return store.read_task(task_id) or {}, candidate, events
task, candidate, events = asyncio.run(exercise())
self.assertEqual(task["active_revision"], "")
self.assertEqual(task["active_candidate_id"], "")
self.assertEqual(candidate["status"], "review_failed")
self.assertIn("CANDIDATE_REVIEW_FAILED", events[-1][1]["message"])
def test_completion_audit_blocks_publication_until_current_checkpoint_is_verified(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], dict, str | None]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "completion gate")
task_id = str(task["task_id"])
store.start_generation(task_id, request="completion gate")
store.write_requirements_document(task_id, "# Frozen\nBuild a checked plate.")
store.write_completion_checklist(task_id, "- [ ] checked plate")
store.update_task(task_id, {"revision_id": "rev_001", "status": "success", "visibility": "checkpoint", "branch_id": "main"})
store.set_active_revision(task_id, "rev_001", branch_id="main")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = runner._state(
task_id, request="completion gate", conversation_id="conv_abcdef123456",
provider=current_settings.providers[0], model=current_settings.providers[0].models[0],
initial_messages=[], frozen_attachment_ids=[], fresh=False,
)
events, _ = await runner._execute_tool(
task_id, "completion gate", state, load_engine(current_settings), "complete_task", {"self_review": "done"},
)
await runner._execute_tool(
task_id, "completion gate", state, load_engine(current_settings), "audit_completion_checklist",
{"markdown": "- [x] checked plate :: rebuilt bbox and single-solid report verified"},
)
loaded = store.read_task(task_id) or {}
return events, state, runner._completion_gate_error(task_id, loaded, state)
events, state, gate_error = asyncio.run(exercise())
self.assertIn("INDEPENDENT_REVIEW_REQUIRED", events[0][1]["message"])
self.assertIn("AUTHOR_SELF_REVIEW_DISABLED", (state.get("recent_events") or [])[-1]["message"])
self.assertIn("INDEPENDENT_REVIEW_REQUIRED", gate_error or "")
def test_author_completion_audit_is_disabled(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], dict, list[str]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "incomplete completion gate")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nBuild a checked plate.")
store.write_completion_checklist(task_id, "- [ ] checked plate")
store.update_task(task_id, {"revision_id": "rev_001", "status": "success", "visibility": "checkpoint", "branch_id": "main"})
store.set_active_revision(task_id, "rev_001", branch_id="main")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = {"completion_checklist_written": True, "completion_ledger": {}}
events, progressed = await runner._execute_tool(
task_id,
"incomplete completion gate",
state,
load_engine(current_settings),
"audit_completion_checklist",
{"markdown": "- [?] checked plate :: edge relief is not evidenced"},
)
tools = runner._author_tools(store.read_task(task_id) or {}, requirements_frozen=True, state=state)
self.assertFalse(progressed)
return events, state, [item["function"]["name"] for item in tools]
events, state, tools = asyncio.run(exercise())
self.assertIn("AUTHOR_SELF_REVIEW_DISABLED", events[0][1]["message"])
self.assertEqual(state["completion_ledger"], {})
self.assertIn("inspect_topology", tools)
def test_checkpoint_invalidates_a_previous_completion_audit(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "stale completion")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nBuild a plate.")
store.write_completion_checklist(task_id, "- [ ] checked plate")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = {"completion_ledger": {"items": [{"item": "checked plate", "status": "complete", "evidence": "old", "updated_at": ""}], "verified_revision": "rev_001", "audited_at": "now"}}
runner._invalidate_completion_audit(task_id, state, reason="checkpoint_changed")
self.assertEqual(state["completion_ledger"]["verified_revision"], "")
self.assertEqual(state["completion_ledger"]["items"][0]["status"], "complete")
def test_only_the_newest_detailed_tool_result_is_repeated_to_the_author(self) -> None:
state = {
"recent_events": [
{"kind": "measure_model", "message": "old measurement", "result": {"records": ["x" * 1000]}},
{"kind": "inspect_topology", "message": "new topology", "result": {"tokens": ["sel_current"]}},
],
}
events = AutonomousCdslGenerationRunner._author_events(state)
self.assertNotIn("result", events[0])
self.assertEqual(events[0]["message"], "old measurement")
self.assertEqual(events[1]["result"], {"tokens": ["sel_current"]})
def test_operation_contract_turn_goes_straight_to_authoring(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
tools = runner._author_tools(
{"active_revision": "rev_001", "active_branch_id": "main"},
requirements_frozen=True,
state={"recent_events": [{"kind": "get_cdsl_operation_contract"}]},
)
self.assertEqual(
[tool["function"]["name"] for tool in tools],
["submit_cdsl_fragment", "rollback_checkpoint", "complete_task"],
)
def test_current_checkpoint_is_not_a_valid_rollback_target(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], dict, dict]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "strict rollback")
task_id = str(task["task_id"])
store.start_generation(task_id, request="strict rollback")
store.write_requirements_document(task_id, "# Frozen\nRepair it.")
store.update_task(task_id, {"revision_id": "rev_001", "status": "success", "visibility": "checkpoint", "branch_id": "main"})
store.set_active_revision(task_id, "rev_001", branch_id="main")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = {"final_repair": {"working_head": "main:rev_001", "topology_observed": True}}
token = _checkpoint_token("main", "rev_001")
events, _ = await runner._execute_tool(
task_id, "strict rollback", state, load_engine(current_settings), "rollback_checkpoint",
{"checkpoint_token": token, "reason": "no-op rollback"},
)
return events, state, store.read_task(task_id) or {}
events, state, task = asyncio.run(exercise())
self.assertIn("strict ancestor", events[0][1]["message"])
self.assertEqual(task["active_revision"], "rev_001")
self.assertEqual(task["active_branch_id"], "main")
self.assertEqual(state["final_repair"]["working_head"], "main:rev_001")
def test_strict_rollback_preserves_final_repair_gate_on_the_new_branch(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], dict, dict]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "rollback repair")
task_id = str(task["task_id"])
store.start_generation(task_id, request="rollback repair")
store.write_requirements_document(task_id, "# Frozen\nRepair it.")
store.update_task(task_id, {"revision_id": "rev_001", "status": "success", "visibility": "checkpoint", "branch_id": "main"})
store.update_task(task_id, {"revision_id": "rev_002", "status": "success", "parent_revision_id": "rev_001", "visibility": "checkpoint", "branch_id": "main"})
store.set_active_revision(task_id, "rev_002", branch_id="main")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = {"final_repair": {"working_head": "main:rev_002", "topology_observed": True, "evidence": ["missing bolt group"]}}
token = _checkpoint_token("main", "rev_001")
events, _ = await runner._execute_tool(
task_id, "rollback repair", state, load_engine(current_settings), "rollback_checkpoint",
{"checkpoint_token": token, "reason": "remove bad feature"},
)
return events, state, store.read_task(task_id) or {}
events, state, task = asyncio.run(exercise())
self.assertEqual(events[0][0], "rollback")
self.assertEqual(task["active_revision"], "rev_001")
self.assertTrue(task["active_branch_id"].startswith("branch_"))
self.assertEqual(state["final_repair"]["working_head"], f"{task['active_branch_id']}:rev_001")
self.assertFalse(state["final_repair"]["topology_observed"])
self.assertTrue(state["final_repair"]["evidence_stale_after_checkpoint"])
def test_candidate_state_exposes_a_single_forced_review_transition(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
tools = runner._author_tools(
{"active_revision": "rev_001", "active_candidate_id": "candidate_0123456789ab"},
requirements_frozen=True,
state={"last_review": {}},
)
self.assertEqual([item["function"]["name"] for item in tools], ["rollback_checkpoint"])
def test_staged_candidate_precedes_final_repair_and_format_correction(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
tools = runner._author_tools(
{"active_revision": "rev_001", "active_branch_id": "main", "active_candidate_id": "candidate_0123456789ab"},
requirements_frozen=True,
state={
"last_review": {},
"format_correction": {"working_head": "main:rev_001"},
"final_repair": {"working_head": "main:rev_001", "topology_observed": True},
},
)
self.assertEqual([item["function"]["name"] for item in tools], ["rollback_checkpoint"])
def test_unchanged_geometry_requires_bounded_diagnostics_then_conclusion(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
task = {"active_revision": "rev_020", "active_branch_id": "branch_repair"}
state = {
"final_repair": {"working_head": "branch_repair:rev_020", "topology_observed": True},
"geometry_rejection": {"working_head": "branch_repair:rev_020", "geometry_fingerprint": "fingerprint", "conclusion_required": True},
"geometry_diagnoses_by_fingerprint": {"fingerprint": {"observations": {}, "conclusion": None}},
}
tools = runner._author_tools(task, requirements_frozen=True, state=state)
names = {item["function"]["name"] for item in tools}
self.assertNotIn("submit_cdsl_fragment", names)
self.assertIn("inspect_model", names)
self.assertIn("measure_model", names)
self.assertIn("render_views", names)
self.assertIn("record_geometry_conclusion", names)
self.assertNotIn("rollback_checkpoint", names)
state["geometry_diagnoses_by_fingerprint"]["fingerprint"]["observations"] = {
"inspect": {"ref": "diag_fingerprint_inspect"},
"measure": {"ref": "diag_fingerprint_measure"},
}
tools = runner._author_tools(task, requirements_frozen=True, state=state)
self.assertEqual(
[item["function"]["name"] for item in tools],
["render_views", "record_geometry_conclusion"],
)
state["geometry_diagnoses_by_fingerprint"]["fingerprint"]["conclusion"] = {"decision": "modify"}
self.assertEqual(
[item["function"]["name"] for item in runner._author_tools(task, requirements_frozen=True, state=state)],
["submit_cdsl_fragment", "rollback_checkpoint"],
)
def test_geometry_conclusion_requires_known_evidence_and_unlocks_only_its_decision(self) -> None:
async def exercise() -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "diagnose unchanged candidate")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen requirements")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = {
"geometry_rejection": {"working_head": "main:root", "geometry_fingerprint": "root", "conclusion_required": True},
"geometry_diagnoses_by_fingerprint": {"root": {"observations": {"inspect": {"ref": "diag_root_inspect"}}, "conclusion": None}},
}
engine = load_engine(current_settings)
events, progressed = await runner._execute_tool(
task_id, "", state, engine, "record_geometry_conclusion",
{"root_cause": "duplicate_feature", "evidence_refs": ["not-real"], "decision": "rollback"},
)
self.assertFalse(progressed)
self.assertIn("EVIDENCE_UNKNOWN", events[0][1]["message"])
diagnostic = events[0][1]["diagnostic"]
self.assertEqual(diagnostic["code"], "GEOMETRY_CONCLUSION_EVIDENCE_UNKNOWN")
self.assertEqual(diagnostic["stage"], "geometry_diagnosis")
self.assertEqual(diagnostic["path"], "evidence_refs")
self.assertEqual(diagnostic["evidence"]["allowed_evidence_refs"], ["diag_root_inspect"])
events, progressed = await runner._execute_tool(
task_id, "", state, engine, "record_geometry_conclusion",
{"root_cause": "duplicate_feature", "evidence_refs": ["diag_root_inspect"], "decision": "modify", "optimization_plan": {"action": "change the next feature", "reason": "the current geometry is unchanged"}},
)
self.assertTrue(progressed)
self.assertEqual(events[0][0], "geometry_conclusion")
tools = [item["function"]["name"] for item in runner._author_tools(store.read_task(task_id) or {}, requirements_frozen=True, state=state)]
self.assertEqual(tools, ["get_cdsl_operation_contract", "rollback_checkpoint"])
asyncio.run(exercise())
def test_incomplete_current_audit_reopens_a_noop_complete_decision(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
task = {"active_revision": "rev_020", "active_branch_id": "main"}
state = {
"completion_checklist_required": False,
"completion_checklist_written": True,
"completion_ledger": {
"verified_revision": "rev_020",
"items": [{"item": "four mounting holes", "status": "missing", "evidence": "not modelled"}],
},
"geometry_rejection": {"working_head": "main:rev_020", "geometry_fingerprint": "fingerprint", "conclusion_required": False},
"geometry_diagnoses_by_fingerprint": {
"fingerprint": {
"observations": {"measure": {"ref": "diag_fingerprint_measure"}},
"conclusion": {"decision": "complete"},
},
},
}
names = [item["function"]["name"] for item in runner._author_tools(task, requirements_frozen=True, state=state)]
self.assertEqual(names, ["record_geometry_conclusion"])
self.assertIsNone(state["geometry_diagnoses_by_fingerprint"]["fingerprint"]["conclusion"])
self.assertTrue(state["geometry_rejection"]["conclusion_required"])
def test_modify_geometry_conclusion_accepts_next_action_without_reason(self) -> None:
async def exercise() -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "conclusion spelling")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = {
"geometry_rejection": {"working_head": "main:root", "geometry_fingerprint": "root"},
"geometry_diagnoses_by_fingerprint": {"root": {"observations": {"inspect": {"ref": "diag_root_inspect"}}, "conclusion": None}},
}
events, progressed = await runner._execute_tool(
task_id, "", state, load_engine(current_settings), "record_geometry_conclusion",
{"root_cause": "unknown", "evidence_refs": ["diag_root_inspect"], "decision": "modify", "optimization_plan": {"next_action": "use another small feature"}},
)
self.assertTrue(progressed)
self.assertEqual(events[0][0], "geometry_conclusion")
conclusion = state["geometry_diagnoses_by_fingerprint"]["root"]["conclusion"]
self.assertEqual(conclusion["optimization_plan"]["action"], "use another small feature")
asyncio.run(exercise())
def test_final_repair_suppresses_repeated_no_argument_observation_and_bounds_evidence_phase(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
task = {"active_revision": "rev_020", "active_branch_id": "branch_repair"}
state = {
"final_repair": {"working_head": "branch_repair:rev_020", "topology_observed": True},
"completed_repair_observations_by_head": {"branch_repair:rev_020": ["inspect_model"]},
"repair_observation_counts_by_head": {"branch_repair:rev_020": 1},
}
names = {item["function"]["name"] for item in runner._author_tools(task, requirements_frozen=True, state=state)}
self.assertNotIn("inspect_model", names)
self.assertIn("read_cdsl_slice", names)
self.assertIn("submit_cdsl_fragment", names)
state["repair_observation_counts_by_head"]["branch_repair:rev_020"] = current_settings.agent_tool_calls_per_cycle
names = [item["function"]["name"] for item in runner._author_tools(task, requirements_frozen=True, state=state)]
self.assertEqual(names, ["get_cdsl_operation_contract", "submit_cdsl_fragment", "rollback_checkpoint"])
def test_duplicate_parameterized_repair_observation_requires_action(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
task = {"active_revision": "rev_020", "active_branch_id": "branch_repair"}
state = {
"final_repair": {"working_head": "branch_repair:rev_020", "topology_observed": True},
"repair_observation_counts_by_head": {},
"repair_observation_keys_by_head": {},
}
arguments = {"feature_ids": ["feature_003"], "sketch_ids": ["sketch_002"]}
self.assertTrue(runner._record_repair_observation(task, state, "read_cdsl_slice", arguments))
self.assertFalse(runner._record_repair_observation(task, state, "read_cdsl_slice", arguments))
self.assertEqual(state["candidate_action_required"]["reason"], "duplicate_repair_observation")
names = [item["function"]["name"] for item in runner._author_tools(task, requirements_frozen=True, state=state)]
self.assertEqual(names, ["get_cdsl_operation_contract", "rollback_checkpoint"])
def test_unchanged_candidate_attempts_are_not_compensated(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
state = {
"candidate_attempts_by_head": {"branch_repair:rev_020": 3},
"geometry_rejection": {
"working_head": "branch_repair:rev_020",
"repeat_count": 2,
},
}
self.assertEqual(state["candidate_attempts_by_head"]["branch_repair:rev_020"], 3)
def test_candidate_attempt_limit_is_not_a_format_retry(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
task = {"active_revision": "rev_020", "active_branch_id": "branch_repair"}
state = {
"format_correction": {
"working_head": "branch_repair:rev_020",
"error": "CANDIDATE_ATTEMPT_LIMIT: head branch_repair:rev_020 exhausted 3 candidate build attempts",
},
}
tools = runner._author_tools(task, requirements_frozen=True, state=state)
names = {item["function"]["name"] for item in tools}
self.assertEqual(state["format_correction"], {})
self.assertEqual(state["candidate_action_required"]["reason"], "candidate_attempt_limit")
self.assertNotIn("submit_cdsl_fragment", names)
self.assertIn("rollback_checkpoint", names)
self.assertIn("complete_task", names)
def test_author_cannot_commit_repair_candidate(self) -> None:
async def exercise() -> tuple[dict, dict, list[str]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "repair carry forward")
task_id = str(task["task_id"])
store.start_generation(task_id, request="repair carry forward")
store.write_requirements_document(task_id, "# Frozen\nRepair the model.")
engine = load_engine(current_settings)
cdsl, audit = materialize_autonomous_fragment(None, base_fragment(), engine=engine, selector_tokens={}, max_features=1)
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = runner._state(
task_id, request="repair carry forward", conversation_id="conv_abcdef123456",
provider=current_settings.providers[0], model=current_settings.providers[0].models[0],
initial_messages=[], frozen_attachment_ids=[], fresh=True,
)
await asyncio.to_thread(build_candidate, settings=current_settings, store=store, task_id=task_id, cdsl=cdsl, fragment_audit=audit, parent_revision_id="")
state["final_repair"] = {"working_head": "main:root", "topology_observed": True, "evidence": ["missing feature"]}
await runner._execute_tool(task_id, "repair carry forward", state, engine, "record_step_review", {"markdown": "Valid repair candidate.", "decision": "commit"})
loaded = store.read_task(task_id) or {}
tools = [item["function"]["name"] for item in runner._author_tools(loaded, requirements_frozen=True, state=state)]
return loaded, state, tools
task, state, tools = asyncio.run(exercise())
self.assertEqual(task["active_revision"], "")
self.assertEqual(state["final_repair"]["working_head"], "main:root")
self.assertTrue(state["final_repair"]["topology_observed"])
self.assertEqual(tools, ["rollback_checkpoint"])
def test_pending_candidate_submission_is_not_treated_as_a_format_loop(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], bool, dict, list[str]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "pending candidate")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nReview the staged candidate.")
store.new_candidate(task_id)
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = runner._state(
task_id, request="pending candidate", conversation_id="conv_abcdef123456",
provider=current_settings.providers[0], model=current_settings.providers[0].models[0],
initial_messages=[], frozen_attachment_ids=[], fresh=True,
)
events, progressed = await runner._execute_tool(
task_id, "pending candidate", state, load_engine(current_settings), "submit_cdsl_fragment",
{"fragment_json": json.dumps(base_fragment())},
)
loaded = store.read_task(task_id) or {}
tools = [item["function"]["name"] for item in runner._author_tools(loaded, requirements_frozen=True, state=state)]
return events, progressed, state, tools
events, progressed, state, tools = asyncio.run(exercise())
self.assertFalse(progressed)
self.assertEqual(events[0][0], "candidate_result")
self.assertEqual(state["format_correction"], {})
self.assertTrue(state["candidate_action_required"])
self.assertEqual(tools, ["rollback_checkpoint"])
def test_operation_contract_explains_countersunk_face_and_position_protocol(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
contract = _operation_contract_payload(load_engine(current_settings), "hole_countersink")
self.assertEqual(
contract["author_required_params"],
["diameter_mm", "depth_mm", "positions", "countersink_diameter_mm", "countersink_angle_rad"],
)
self.assertEqual(contract["server_injected_params"], ["host_face"])
self.assertEqual(contract["selector_rule"], {"kind": "face", "min_items": 1, "max_items": 1})
self.assertIn('positions is a non-empty array of objects', contract["position_format"])
self.assertIn("server_injected_params", contract["authoring_rule"])
def test_nonstandard_provider_tool_objects_are_recovered_without_evaluation(self) -> None:
self.assertEqual(parse_tool_arguments('{kind: "face", limit: 12}'), ({"kind": "face", "limit": 12}, True))
self.assertEqual(parse_tool_arguments("{'kind': 'edge', 'limit': 3}"), ({"kind": "edge", "limit": 3}, True))
self.assertEqual(parse_tool_arguments('{"kind":"face"}'), ({"kind": "face"}, False))
with self.assertRaisesRegex(ValueError, "Expecting property name"):
parse_tool_arguments('{kind: call_that_must_not_run()}')
def test_only_explicit_quota_exhaustion_is_eligible_for_author_failover(self) -> None:
self.assertTrue(_is_author_quota_error(RuntimeError("LLM request failed (429): API_KEY_QUOTA_EXHAUSTED")))
self.assertFalse(_is_author_quota_error(RuntimeError("LLM request failed (429): rate limit exceeded")))
self.assertFalse(_is_author_quota_error(RuntimeError("LLM request failed (500): quota backend unavailable")))
def test_authoring_tools_do_not_request_strict_provider_schema(self) -> None:
tools = autonomous_tools()
submit = next(tool for tool in tools if tool["function"]["name"] == "submit_cdsl_fragment")
self.assertEqual(submit["function"]["parameters"]["properties"]["fragment"]["type"], "object")
self.assertEqual(submit["function"]["parameters"]["properties"]["batch_relationship"]["minLength"], 12)
self.assertFalse(any(tool["function"].get("strict") for tool in tools))
self.assertEqual(tools[0]["function"]["name"], "write_requirements_document")
def test_frozen_requirements_writer_is_removed_from_follow_up_author_tools(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
first_turn = {item["function"]["name"] for item in runner._author_tools({}, requirements_frozen=False)}
root_follow_up = {item["function"]["name"] for item in runner._author_tools({}, requirements_frozen=True)}
checkpoint_follow_up = {
item["function"]["name"]
for item in runner._author_tools(
{"active_revision": "rev_001"},
requirements_frozen=True,
state={"recent_events": [{"kind": "checkpoint"}]},
)
}
self.assertIn("write_requirements_document", first_turn)
self.assertEqual(root_follow_up, {"get_cdsl_operation_contract", "submit_cdsl_fragment"})
self.assertNotIn("write_requirements_document", checkpoint_follow_up)
self.assertEqual(checkpoint_follow_up, {"inspect_topology", "complete_task"})
self.assertNotIn("inspect_model", checkpoint_follow_up)
def test_topology_observation_allows_the_next_fragment_and_renders(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
names = {
item["function"]["name"]
for item in runner._author_tools(
{"active_revision": "rev_001"},
requirements_frozen=True,
state={"recent_events": [{"kind": "inspect_topology"}]},
)
}
self.assertIn("submit_cdsl_fragment", names)
self.assertIn("render_views", names)
self.assertNotIn("inspect_model", names)
def test_staged_candidate_forces_review_then_its_decision(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
runner = AutonomousCdslGenerationRunner(current_settings, WorkspaceStore(current_settings), AsyncMock())
task = {"active_revision": "rev_001", "active_candidate_id": "candidate_0123456789ab"}
needs_review = runner._author_tools(task, requirements_frozen=True, state={"last_review": {}})
commit = runner._author_tools(
task,
requirements_frozen=True,
state={"last_review": {"candidate_id": "candidate_0123456789ab", "decision": "commit"}},
)
discard = runner._author_tools(
task,
requirements_frozen=True,
state={"last_review": {"candidate_id": "candidate_0123456789ab", "decision": "discard"}},
)
self.assertEqual({item["function"]["name"] for item in needs_review}, {"rollback_checkpoint"})
self.assertEqual({item["function"]["name"] for item in commit}, {"rollback_checkpoint"})
self.assertEqual({item["function"]["name"] for item in discard}, {"rollback_checkpoint"})
def test_empty_head_prompt_contains_a_format_reference_for_the_first_solid(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "root reference")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nBuild a 40 by 20 by 8 mm plate.")
provider, model = current_settings.resolve_model("test", "test-model")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = runner._state(
task_id, request="root reference", conversation_id="conv_abcdef123456", provider=provider, model=model,
initial_messages=[], frozen_attachment_ids=[], fresh=True,
)
prompt = str(runner._prompt_messages(task_id, state, load_engine(current_settings))[-1]["content"])
self.assertIn("root_fragment_reference", prompt)
self.assertIn("extrude_add_blind", prompt)
self.assertIn("polygon", prompt)
self.assertIn("cdsl_authoring_basics", prompt)
self.assertIn("get_cdsl_operation_contract", prompt)
self.assertNotIn("sketch_required_features", prompt)
self.assertNotIn("constant_thickness_bend_profiles", prompt)
def test_schema_preflight_does_not_consume_an_engine_candidate_attempt(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], dict, list[Path]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "schema preflight")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nBuild a plate.")
store.start_generation(task_id, request="schema preflight")
provider, model = current_settings.resolve_model("test", "test-model")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = runner._state(
task_id, request="schema preflight", conversation_id="conv_abcdef123456", provider=provider, model=model,
initial_messages=[], frozen_attachment_ids=[], fresh=True,
)
state["read_operation_contract_ids"] = ["extrude_add_blind"]
invalid = {
"sketch": {
"workplane": "XY",
"profile": {"type": "circle", "radius_mm": 5},
},
"feature": {"atomic_id": "extrude_add_blind", "params": {"distance_mm": 4}},
}
events, _ = await runner._execute_tool(
task_id, "schema preflight", state, load_engine(current_settings), "submit_cdsl_fragment",
{"batch_goal": "Create the initial circular plate.", "fragment_json": json.dumps(invalid)},
)
candidates = list(store.task_dir(task_id).glob("candidates/*/candidate.json"))
return events, state, candidates
events, state, candidates = asyncio.run(exercise())
self.assertEqual(state["candidate_attempts_by_head"], {})
self.assertEqual(candidates, [])
self.assertIn("workplane", events[0][1]["message"])
def test_author_step_review_cannot_commit_a_staged_candidate(self) -> None:
async def exercise() -> tuple[bool, list[str], dict]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "candidate progress")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nBuild a plate.")
store.start_generation(task_id, request="candidate progress")
provider, model = current_settings.resolve_model("test", "test-model")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = runner._state(
task_id, request="candidate progress", conversation_id="conv_abcdef123456", provider=provider, model=model,
initial_messages=[], frozen_attachment_ids=[], fresh=True,
)
engine = load_engine(current_settings)
cdsl, audit = materialize_autonomous_fragment(None, base_fragment(), engine=engine, selector_tokens={}, max_features=6)
await asyncio.to_thread(
build_candidate,
settings=current_settings,
store=store,
task_id=task_id,
cdsl=cdsl,
fragment_audit=audit,
parent_revision_id="",
)
review_events, reviewed = await runner._execute_tool(
task_id, "candidate progress", state, engine, "record_step_review",
{"markdown": "Candidate is valid.", "decision": "commit"},
)
return reviewed, [name for name, _ in review_events], store.read_task(task_id) or {}
reviewed, event_names, task = asyncio.run(exercise())
self.assertFalse(reviewed)
self.assertEqual(event_names, ["tool_call"])
self.assertTrue(task["active_candidate_id"])
self.assertEqual(task["active_revision"], "")
def test_context_budget_never_truncates_frozen_requirements(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
current_settings = Settings(**{**current_settings.__dict__, "agent_context_char_limit": 4000})
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "context")
task_id = str(task["task_id"])
requirements = "# Frozen\n" + ("exact engineering requirement\n" * 500)
store.write_requirements_document(task_id, requirements)
provider, model = current_settings.resolve_model("test", "test-model")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = runner._state(
task_id, request="context", conversation_id="conv_abcdef123456", provider=provider, model=model,
initial_messages=[], frozen_attachment_ids=[], fresh=True,
)
state["recent_events"] = [{"kind": "large", "message": "x" * 20000}]
messages = runner._prompt_messages(task_id, state, load_engine(current_settings))
prompt = str(messages[-1]["content"])
self.assertIn("exact engineering requirement", prompt)
self.assertIn("normal cross x_dir", prompt)
self.assertIn("get_cdsl_operation_contract for that exact atomic_id", prompt)
self.assertNotIn("context truncated", prompt)
def test_text_only_author_never_receives_render_image_content(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "text-only")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nBuild a plate.")
image_path = Path(temporary) / "render.png"
image_path.write_bytes(b"not-an-image-needed-for-this-routing-test")
provider, model = current_settings.resolve_model("test", "test-model")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = runner._state(
task_id, request="text-only", conversation_id="conv_abcdef123456", provider=provider, model=model,
initial_messages=[], frozen_attachment_ids=[], fresh=True,
)
state["pending_images"] = [str(image_path)]
messages = runner._prompt_messages(task_id, state, load_engine(current_settings))
self.assertFalse(any(
isinstance(message.get("content"), list)
and any(isinstance(part, dict) and part.get("type") == "image_url" for part in message["content"])
for message in messages
))
self.assertIn("text-only", str(messages[-1]["content"]))
def test_successful_candidate_cannot_be_discarded_without_a_step_review(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], bool]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "discard review")
task_id = str(task["task_id"])
store.write_requirements_document(task_id, "# Frozen\nBuild a plate.")
store.start_generation(task_id, request="discard review")
engine = load_engine(current_settings)
cdsl, audit = materialize_autonomous_fragment(None, base_fragment(), engine=engine, selector_tokens={}, max_features=1)
await asyncio.to_thread(
build_candidate,
settings=current_settings,
store=store,
task_id=task_id,
cdsl=cdsl,
fragment_audit=audit,
parent_revision_id="",
)
provider, model = current_settings.resolve_model("test", "test-model")
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = runner._state(
task_id, request="discard review", conversation_id="conv_abcdef123456", provider=provider, model=model,
initial_messages=[], frozen_attachment_ids=[], fresh=True,
)
events, progressed = await runner._execute_tool(task_id, "discard review", state, engine, "discard_candidate", {"reason": "retry"})
return events, progressed
events, progressed = asyncio.run(exercise())
self.assertFalse(progressed)
self.assertEqual(events[0][0], "tool_call")
self.assertIn("AUTHOR_SELF_REVIEW_DISABLED", events[0][1]["message"])
def test_runner_stages_then_commits_before_final_publication(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], dict, list[set[str]]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "make a plate")
provider, model = current_settings.resolve_model("test", "test-model")
calls = iter([
("write_requirements_document", {"markdown": "# Plate\nUse a 20 x 10 x 4 mm rectangular plate."}),
("write_completion_checklist", {"markdown": completion_checklist()}),
("submit_cdsl_fragment", {"batch_goal": "Create the rectangular base plate.", "fragment_json": json.dumps(base_fragment())}),
("get_cdsl_operation_contract", {"atomic_id": "extrude_add_blind"}),
("submit_cdsl_fragment", {"batch_goal": "Create the rectangular base plate.", "fragment_json": json.dumps(base_fragment())}),
("complete_task", {"self_review": "All frozen requirements are satisfied."}),
])
author_tool_names: list[set[str]] = []
async def complete(_messages: list[dict], _tools: list[dict], _provider: ProviderConfig, _model: ProviderModel, _forced: str | None) -> dict:
author_tool_names.append({item["function"]["name"] for item in _tools})
name, arguments = next(calls)
return {"choices": [{"message": {"tool_calls": [{"id": name, "function": {"name": name, "arguments": json.dumps(arguments)}}]}}]}
runner = AutonomousCdslGenerationRunner(current_settings, store, complete)
runner._final_review = AsyncMock(return_value=({"verdict": "pass", "confidence": 1, "evidence": []}, {"solid_count": 1})) # type: ignore[method-assign]
with patch("app.services.autonomous_cdsl_generation.render_checkpoint", return_value={"views": []}), patch(
"app.services.autonomous_cdsl_generation.review_candidate_batch",
AsyncMock(return_value=accepted_plate_batch_review()),
):
events = [item async for item in runner.run(
task_id=str(task["task_id"]), request="make a plate", conversation_id="conv_abcdef123456",
provider=provider, model=model, initial_messages=[{"role": "user", "content": "make a plate"}],
)]
return events, store.read_task(str(task["task_id"])) or {}, author_tool_names
events, task, author_tool_names = asyncio.run(exercise())
names = [name for name, _ in events]
self.assertIn("candidate_result", names)
self.assertIn("checkpoint", names)
self.assertEqual(events[-1][0], "task_terminal")
self.assertEqual(task["lifecycle"], "completed")
self.assertTrue(task["published_revision"])
self.assertTrue(task["revisions"][0]["candidate_review_path"].startswith("revisions/rev_001/reviews/"))
self.assertIn("write_requirements_document", author_tool_names[0])
self.assertEqual(author_tool_names[1], {"write_completion_checklist"})
self.assertEqual(author_tool_names[2], {"get_cdsl_operation_contract", "submit_cdsl_fragment"})
self.assertEqual(author_tool_names[3], {"get_cdsl_operation_contract"})
self.assertEqual(author_tool_names[4], {"submit_cdsl_fragment"})
self.assertTrue(all("write_requirements_document" not in names for names in author_tool_names[1:]))
def test_final_warning_keeps_task_running_and_returns_to_author(self) -> None:
async def exercise() -> tuple[list[tuple[str, dict]], dict, dict, list[str], list[str]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "final warning")
task_id = str(task["task_id"])
store.start_generation(task_id, request="final warning")
store.write_requirements_document(task_id, "# Frozen\nA complete plate.")
store.write_completion_checklist(task_id, "- [ ] complete plate")
engine = load_engine(current_settings)
cdsl, audit = materialize_autonomous_fragment(None, base_fragment(), engine=engine, selector_tokens={}, max_features=1)
runner = AutonomousCdslGenerationRunner(current_settings, store, AsyncMock())
state = runner._state(
task_id, request="final warning", conversation_id="conv_abcdef123456", provider=current_settings.providers[0],
model=current_settings.providers[0].models[0], initial_messages=[], frozen_attachment_ids=[], fresh=True,
)
candidate = await asyncio.to_thread(build_candidate, settings=current_settings, store=store, task_id=task_id, cdsl=cdsl, fragment_audit=audit, parent_revision_id="")
candidate_id = str(candidate["candidate_id"])
review_path = store.candidate_dir(task_id, candidate_id) / "reviews" / "candidate-review" / "candidate-review.json"
candidate_review = {
"verdict": "accept", "batch_goal_status": "achieved", "coverage": [{
"item": "complete plate", "status": "complete", "evidence": "single solid rebuilt",
}], "evidence": ["base plate accepted"],
}
write_json(review_path, candidate_review)
candidate_data = json.loads((store.candidate_dir(task_id, candidate_id) / "candidate.json").read_text(encoding="utf-8"))
candidate_data["candidate_review_path"] = f"candidates/{candidate_id}/reviews/candidate-review/candidate-review.json"
write_json(store.candidate_dir(task_id, candidate_id) / "candidate.json", candidate_data)
await runner._commit_reviewed_candidate(task_id, task, state, candidate_id, candidate_review=candidate_review)
runner._final_review = AsyncMock(return_value=({"verdict": "warning", "confidence": 0.1, "evidence": ["missing chamfer"]}, {"solid_count": 1})) # type: ignore[method-assign]
events, progressed = await runner._execute_tool(task_id, "final warning", state, engine, "complete_task", {"self_review": "done"})
loaded = store.read_task(task_id) or {}
first_tools = [item["function"]["name"] for item in runner._author_tools(loaded, requirements_frozen=True, state=state)]
state["final_repair"]["topology_observed"] = True
follow_up_tools = [item["function"]["name"] for item in runner._author_tools(loaded, requirements_frozen=True, state=state)]
return events, loaded, state, first_tools, follow_up_tools
events, task, state, first_tools, follow_up_tools = asyncio.run(exercise())
self.assertFalse(any(name == "task_terminal" for name, _ in events))
self.assertTrue(any(name == "agent_thinking" for name, _ in events))
self.assertEqual(task["lifecycle"], "running")
self.assertEqual(task["published_revision"], "")
self.assertEqual(state["final_repair"]["review_verdict"], "warning")
self.assertEqual(first_tools, ["inspect_topology", "rollback_checkpoint"])
self.assertEqual(
follow_up_tools,
[
"inspect_model", "read_cdsl_slice", "measure_model", "render_views", "render_section",
"get_cdsl_operation_contract", "submit_cdsl_fragment", "rollback_checkpoint",
],
)
def test_runner_defers_extra_calls_until_the_next_stateful_turn(self) -> None:
async def exercise() -> tuple[dict, list[str]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "defer calls")
provider, model = current_settings.resolve_model("test", "test-model")
responses = iter([
[
("write_requirements_document", {"markdown": "# Plate\nUse a 20 x 10 x 4 mm rectangular plate."}),
("submit_cdsl_fragment", {"batch_goal": "Create the rectangular base plate.", "fragment_json": json.dumps(base_fragment())}),
],
[("write_completion_checklist", {"markdown": completion_checklist()})],
[("submit_cdsl_fragment", {"batch_goal": "Create the rectangular base plate.", "fragment_json": json.dumps(base_fragment())})],
[("complete_task", {"self_review": "Complete."})],
])
async def complete(_messages: list[dict], _tools: list[dict], _provider: ProviderConfig, _model: ProviderModel, _forced: str | None) -> dict:
calls = [
{"id": name, "function": {"name": name, "arguments": json.dumps(arguments)}}
for name, arguments in next(responses)
]
return {"choices": [{"message": {"tool_calls": calls}}]}
runner = AutonomousCdslGenerationRunner(current_settings, store, complete)
runner._final_review = AsyncMock(return_value=({"verdict": "pass", "confidence": 1, "evidence": []}, {"solid_count": 1})) # type: ignore[method-assign]
with patch("app.services.autonomous_cdsl_generation.render_checkpoint", return_value={"views": []}), patch(
"app.services.autonomous_cdsl_generation.review_candidate_batch",
AsyncMock(return_value=accepted_plate_batch_review()),
):
events = [item async for item in runner.run(
task_id=str(task["task_id"]), request="defer calls", conversation_id="conv_abcdef123456",
provider=provider, model=model, initial_messages=[],
)]
return store.read_agent_state(str(task["task_id"])) or {}, [name for name, _ in events]
state, event_names = asyncio.run(exercise())
self.assertIn("extra_tool_calls_deferred", [item["kind"] for item in state["recent_events"]])
self.assertEqual(event_names.count("candidate_result"), 1)
self.assertEqual(event_names[-1], "task_terminal")
def test_staged_candidate_rejects_an_unexposed_observation_tool(self) -> None:
async def exercise() -> tuple[dict, list[str]]:
with tempfile.TemporaryDirectory() as temporary:
current_settings = settings(Path(temporary))
store = WorkspaceStore(current_settings)
task = store.ensure_task(None, "candidate state")
provider, model = current_settings.resolve_model("test", "test-model")
responses = iter([
[("write_requirements_document", {"markdown": "# Plate"})],
[("write_completion_checklist", {"markdown": completion_checklist()})],
[("submit_cdsl_fragment", {"batch_goal": "Create the rectangular base plate.", "fragment_json": json.dumps(base_fragment())})],
[("get_cdsl_operation_contract", {"atomic_id": "extrude_add_blind"})],
[("submit_cdsl_fragment", {"batch_goal": "Create the rectangular base plate.", "fragment_json": json.dumps(base_fragment())})],
[("inspect_model", {})],
[("complete_task", {"self_review": "done"})],
])
async def complete(_messages: list[dict], _tools: list[dict], _provider: ProviderConfig, _model: ProviderModel, _forced: str | None) -> dict:
calls = [{"id": name, "function": {"name": name, "arguments": json.dumps(arguments)}} for name, arguments in next(responses)]
return {"choices": [{"message": {"tool_calls": calls}}]}
runner = AutonomousCdslGenerationRunner(current_settings, store, complete)
runner._final_review = AsyncMock(return_value=({"verdict": "pass", "confidence": 1, "evidence": []}, {"solid_count": 1})) # type: ignore[method-assign]
with patch("app.services.autonomous_cdsl_generation.render_checkpoint", return_value={"views": []}), patch(
"app.services.autonomous_cdsl_generation.review_candidate_batch",
AsyncMock(return_value=accepted_plate_batch_review()),
):
events = [item async for item in runner.run(
task_id=str(task["task_id"]), request="candidate state", conversation_id="conv_abcdef123456",
provider=provider, model=model, initial_messages=[],
)]
return store.read_agent_state(str(task["task_id"])) or {}, [name for name, _ in events]
state, event_names = asyncio.run(exercise())
self.assertIn("tool_error", [item["kind"] for item in state["recent_events"]])
self.assertEqual(event_names.count("checkpoint"), 1)
self.assertEqual(event_names[-1], "task_terminal")