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")