Files
cdsl-cad/backend/tests/test_autonomous_cdsl_generation.py
T

1902 lines
108 KiB
Python

from __future__ import annotations
import asyncio
import json
import shutil
import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import AsyncMock, patch
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "backend"))
from app.services.autonomous_cdsl_generation import ( # noqa: E402
AutonomousCdslGenerationRunner,
AutonomousGenerationError,
_format_correction_card,
_fragment_selector_tokens,
_requires_edge_selector_recovery,
_final_repair_card,
_checkpoint_token,
_geometry_fingerprint,
_has_material_volume_change,
_material_volume_tolerance,
_operation_contract_payload,
parse_completion_audit,
parse_completion_checklist,
_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_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_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])
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_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_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_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_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_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_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,
)
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,
)
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"])
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, ["submit_cdsl_fragment", "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, ["submit_cdsl_fragment", "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_json"]["type"], "string")
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, {"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,
)
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())}),
("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], {"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())})],
[("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")