Files
FreeCADWasmDev/tools/test_scheduler_efficiency.py
wangdequan 9a7c4f0335 Save FreeCAD Web workspace sources and user validation evidence
IFC native binding remains incomplete; preserve accurate test results.
2026-09-15 22:01:58 +08:00

978 lines
54 KiB
Python

#!/usr/bin/env python3
"""Regression tests for scheduler optimizations that must preserve gate behavior."""
from __future__ import annotations
import importlib.util
import contextlib
import io
import json
import os
import sys
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "tools"))
def load_module(name: str, relative: str):
spec = importlib.util.spec_from_file_location(name, ROOT / relative)
if spec is None or spec.loader is None:
raise RuntimeError(relative)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
packet = load_module("implementation_packet", "tools/prepare_module_implementation_packet.py")
pipeline = load_module("module_pipeline", "tools/run_module_batch_pipeline.py")
deferred_flush = load_module("deferred_build_flush", "tools/flush_deferred_module_builds.py")
planner = load_module("module_planner", "tools/prepare_full_parity_module_batch.py")
scheduler = load_module("scheduler", "tools/run_freecad_web_scheduler.py")
preflight = load_module("batch_preflight", "tools/preflight_current_module_batch.py")
feature_planner = load_module("feature_planner", "tools/prepare_feature_capability_batch.py")
capability_generator = load_module("capability_generator", "tools/generate_feature_capability_registry.py")
implementation_first = load_module("implementation_first_state", "tools/implementation_first.py")
continuation = load_module("continuation", "tools/continue_web_task_cycle.py")
class GateSelectionTests(unittest.TestCase):
def assert_gate(self, module: str, tier: str, family: str, expected: str) -> None:
slug = "".join(value for value in module.lower() if value.isalnum())
selected = packet.select_module_gate(module, slug, tier, family)
self.assertIsNotNone(selected)
self.assertEqual(selected.name, expected)
def test_draft_generic_gate(self) -> None:
self.assert_gate("Draft", "tier-3-stateful", "document-graph", "run_p6_draft_module_batch_gate.mjs")
def test_material_nonstandard_gate_name(self) -> None:
for family in ("generic-object", "property", "serialization"):
self.assert_gate("Material", "tier-3-stateful", family, "run_p6_material_object_module_batch_gate.mjs")
def test_selector_rejects_incompatible_mesh_object_gate(self) -> None:
self.assertIsNone(packet.select_module_gate("Mesh", "mesh", "tier-2-pure", "pure-function"))
def test_feature_batch_selects_split_desktop_target(self) -> None:
hint = packet.build_hint("Part", packet.probe_sources("Part"), "feature-workflow", "desktop-command-command")
self.assertEqual(hint.get("target"), "FreeCADWebPartDesktopCommandProbe")
def test_mesh_desktop_batch_prefers_runtime_target(self) -> None:
hint = packet.build_hint("Mesh", packet.probe_sources("Mesh"), "feature-workflow", "desktop-command-command")
self.assertEqual(hint.get("target"), "FreeCADWebMeshRuntimeProbe")
def test_packet_derives_configured_build_tree_before_first_artifact(self) -> None:
hint = packet.build_hint("MeshPart", packet.probe_sources("MeshPart"), "feature-workflow", "desktop-command-mixed")
self.assertEqual(hint.get("target"), "FreeCADWebMeshPartRuntimeProbe")
self.assertEqual(hint.get("tree"), "freecad-source/build-web/configure")
self.assertTrue(hint.get("runtime", "").endswith("FreeCADWebMeshPartRuntimeProbe.js"))
class BuildReuseTests(unittest.TestCase):
def test_deferred_build_flush_syncs_before_receipt(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
(root / "build/bin").mkdir(parents=True)
previous = (
deferred_flush.ROOT,
deferred_flush.PENDING_PATH,
deferred_flush.OUTPUT_PATH,
deferred_flush.run,
deferred_flush.record_implementation_batch,
)
calls: list[str] = []
def fake_run(command: list[str], _timeout: int) -> dict[str, object]:
if command[0] == "cmake":
(root / "build/bin/target.js").write_text("js", encoding="ascii")
(root / "build/bin/target.wasm").write_bytes(b"wasm")
calls.append("build")
elif command[0] == "node":
calls.append("sync")
elif command[-1].endswith("generate_web_business_completion_audit.py"):
calls.append("audit")
else:
calls.append("preflight")
return {"command": command, "seconds": 0.0, "exitCode": 0, "stdout": "", "stderr": ""}
def fake_receipt(*_args: object, **_kwargs: object) -> dict[str, object]:
calls.append("receipt")
return {}
try:
deferred_flush.ROOT = root
deferred_flush.PENDING_PATH = root / "pending.json"
deferred_flush.OUTPUT_PATH = root / "report.json"
deferred_flush.run = fake_run
deferred_flush.record_implementation_batch = fake_receipt
deferred_flush.PENDING_PATH.write_text(json.dumps({
"schema": "freecad-web-module-build-pending/v1",
"targets": {"build:target": [{"batchId": "b1"}]},
}), encoding="utf-8")
with contextlib.redirect_stdout(io.StringIO()):
self.assertEqual(deferred_flush.main([]), 0)
self.assertEqual(calls, ["preflight", "build", "sync", "receipt", "audit"])
state = json.loads(deferred_flush.PENDING_PATH.read_text(encoding="utf-8"))
self.assertEqual(state["targets"], {})
finally:
(
deferred_flush.ROOT,
deferred_flush.PENDING_PATH,
deferred_flush.OUTPUT_PATH,
deferred_flush.run,
deferred_flush.record_implementation_batch,
) = previous
def test_deferred_build_flush_honors_retry_backoff_without_subprocess(self) -> None:
with tempfile.TemporaryDirectory() as directory:
previous_pending = deferred_flush.PENDING_PATH
previous_output = deferred_flush.OUTPUT_PATH
previous_run = deferred_flush.run
try:
deferred_flush.PENDING_PATH = Path(directory) / "pending.json"
deferred_flush.OUTPUT_PATH = Path(directory) / "report.json"
deferred_flush.PENDING_PATH.write_text(json.dumps({
"schema": "freecad-web-module-build-pending/v1",
"targets": {"build:target": [{"batchId": "b1", "nextAttemptAt": 32503680000}]},
}), encoding="utf-8")
deferred_flush.run = lambda *_args, **_kwargs: self.fail("backoff target launched a subprocess")
with contextlib.redirect_stdout(io.StringIO()):
self.assertEqual(deferred_flush.main([]), 0)
report = json.loads(deferred_flush.OUTPUT_PATH.read_text(encoding="utf-8"))
self.assertEqual(report["status"], "retry_backoff")
self.assertEqual(report["skippedTargets"], 1)
finally:
deferred_flush.PENDING_PATH = previous_pending
deferred_flush.OUTPUT_PATH = previous_output
deferred_flush.run = previous_run
def test_scheduler_flushes_deferred_builds_before_idle(self) -> None:
source = (ROOT / "tools/run_freecad_web_scheduler.py").read_text(encoding="utf-8")
flush = source.index('run_local("tools/flush_deferred_module_builds.py", "--json")')
wait = source.index('status="waiting_for_batch"', flush)
self.assertLess(flush, wait)
def test_feature_link_batching_only_defers_when_same_family_remains(self) -> None:
batch = {
"selected": {"module": "Sketcher", "family": "desktop-command-command", "recordCount": 16},
"queue": [{"module": "Sketcher", "family": "desktop-command-command", "unresolved": 30}],
}
self.assertTrue(pipeline.feature_has_more_same_family(batch))
self.assertTrue(pipeline.should_defer_feature_build(batch, implementation_first=True, pending_count=0, artifacts_exist=True))
self.assertFalse(pipeline.should_defer_feature_build(batch, implementation_first=True, pending_count=1, artifacts_exist=True))
self.assertFalse(pipeline.should_defer_feature_build(batch, implementation_first=True, pending_count=0, artifacts_exist=False))
batch["selected"]["family"] = "runtime-issue"
self.assertFalse(pipeline.should_defer_feature_build(batch, implementation_first=True, pending_count=0, artifacts_exist=True))
batch["selected"]["family"] = "desktop-command-command"
batch["queue"][0]["unresolved"] = 16
self.assertFalse(pipeline.feature_has_more_same_family(batch))
def test_pending_build_state_round_trips(self) -> None:
with tempfile.TemporaryDirectory() as directory:
previous = pipeline.PENDING_BUILD_PATH
try:
pipeline.PENDING_BUILD_PATH = Path(directory) / "pending.json"
state = {"schema": "freecad-web-module-build-pending/v1", "targets": {"tree:target": [{"batchId": "b1"}]}}
pipeline.save_pending_builds(state)
self.assertEqual(pipeline.load_pending_builds()["targets"]["tree:target"][0]["batchId"], "b1")
finally:
pipeline.PENDING_BUILD_PATH = previous
def test_pending_build_status_advances_planner_without_closing_runtime_issue(self) -> None:
source = (ROOT / "tools/implementation_first.py").read_text(encoding="utf-8")
self.assertIn('"implementation_pending_build"', source)
self.assertIn('status == "implementation_ready"', source)
def test_file_signature_reuses_metadata_but_detects_content_change(self) -> None:
with tempfile.TemporaryDirectory(dir=ROOT / "docs/current") as directory:
path = Path(directory) / "input.txt"
path.write_text("same", encoding="ascii")
first, first_hit = pipeline.file_signature(path)
second, second_hit = pipeline.file_signature(path, first)
self.assertFalse(first_hit)
self.assertTrue(second_hit)
self.assertEqual(first["sha256"], second["sha256"])
path.write_text("diff", encoding="ascii")
os.utime(path, ns=(int(first["mtimeNs"]) + 1, int(first["mtimeNs"]) + 1))
changed, changed_hit = pipeline.file_signature(path, first)
self.assertFalse(changed_hit)
self.assertNotEqual(first["sha256"], changed["sha256"])
def test_failed_step_compaction_preserves_error_tail(self) -> None:
value = "configure\n" + ("noise\n" * 1000) + "fatal compiler error"
compacted = pipeline.compact(value, 400, preserve_tail=True)
self.assertIn("configure", compacted)
self.assertIn("fatal compiler error", compacted)
self.assertIn("failure tail follows", compacted)
def test_draft_direct_link_closure_is_tracked(self) -> None:
tree = ROOT / "freecad-source/build-web/configure"
dependencies = pipeline.direct_link_dependencies(tree, "FreeCADWebDraftRuntimeProbe")
self.assertGreaterEqual(len(dependencies), 40)
self.assertTrue(all(path.suffix == ".a" and path.is_file() for path in dependencies))
def test_part_desktop_cache_tracks_only_target_sources(self) -> None:
tree = ROOT / "freecad-source/build-web/configure"
sources = pipeline.target_source_dependencies(tree, "FreeCADWebPartDesktopCommandProbe")
names = {path.name for path in sources}
self.assertTrue({"PartDesktopCommandProbe.cpp", "PartDesktopCommands.cpp"}.issubset(names))
self.assertNotIn("PartBoxProbe.cpp", names)
def test_incomplete_feature_batch_is_not_build_ready(self) -> None:
batch = {"records": [["Part:desktop:A"], ["Part:desktop:B"]]}
capabilities = {"records": [
{"id": "Part:desktop:A", "implementationStatus": "routed"},
{"id": "Part:desktop:B", "implementationStatus": "implementation_required"},
]}
readiness = pipeline.static_readiness(batch, capabilities)
self.assertEqual(readiness["ready"], 1)
self.assertEqual(readiness["missingIds"], ["Part:desktop:B"])
def test_rejected_native_marker_reason_reaches_static_readiness(self) -> None:
batch = {
"selected": {"module": "Mesh"},
"records": [["Mesh:desktop:A", "A"], ["Mesh:desktop:B", "B"]],
}
capabilities = {
"records": [
{"id": "Mesh:desktop:A", "implementationStatus": "implementation_required"},
{"id": "Mesh:desktop:B", "implementationStatus": "implementation_required"},
],
"diagnostics": {
"rejectedDesktopMarkers": {"Mesh": ["A"]},
"rejectedMarkerReason": "requires command-specific exposed branch",
},
}
readiness = pipeline.static_readiness(batch, capabilities)
self.assertEqual(readiness["rejectedMarkers"], [{
"id": "Mesh:desktop:A",
"command": "A",
"reason": "requires command-specific exposed branch",
}])
def test_pipeline_allows_real_partial_batch_but_does_not_defer_its_build(self) -> None:
source = (ROOT / "tools/run_module_batch_pipeline.py").read_text(encoding="utf-8")
self.assertIn('readiness["missing"] and not readiness["ready"]', source)
self.assertIn('and not readiness.get("missing")', source)
class DesktopCommandArchitectureTests(unittest.TestCase):
def test_probe_module_resolution_uses_canonical_longest_prefix(self) -> None:
known = {"Fem", "Mesh", "MeshPart", "Part", "PartDesign"}
self.assertEqual(capability_generator.canonical_probe_module("FemGenericFamilyProbe", known), "Fem")
self.assertEqual(capability_generator.canonical_probe_module("MeshPartRuntimeProbe", known), "MeshPart")
self.assertEqual(capability_generator.canonical_probe_module("PartDesignRuntimeProbe", known), "PartDesign")
def test_implemented_marker_requires_command_specific_exposed_branch(self) -> None:
source = '''
FC_WEB_IMPLEMENTED_DESKTOP_COMMAND("Real")
FC_WEB_IMPLEMENTED_DESKTOP_COMMAND("First")
FC_WEB_IMPLEMENTED_DESKTOP_COMMAND("QtStyle")
FC_WEB_IMPLEMENTED_DESKTOP_COMMAND("CommandVariable")
FC_WEB_IMPLEMENTED_DESKTOP_COMMAND("Combined")
FC_WEB_IMPLEMENTED_DESKTOP_COMMAND("Boundary")
FC_WEB_IMPLEMENTED_DESKTOP_COMMAND("GenericOnly")
if (FC_WEB_DESKTOP_COMMAND("Real")) {
result.insert("status", "not_implemented");
}
if (FC_WEB_DESKTOP_COMMAND("First")) {
result.insert("status", "exposed");
}
else if (name == QStringLiteral("QtStyle")) {
result.insert(QStringLiteral("status"), QStringLiteral("exposed"));
}
else if (command == QStringLiteral("CommandVariable")) {
result.insert(QStringLiteral("status"), QStringLiteral("exposed"));
}
else if (commandName == QStringLiteral("Combined") || commandName == QStringLiteral("Sibling")) {
result.insert(QStringLiteral("status"), QStringLiteral("exposed"));
}
else if (FC_WEB_DESKTOP_COMMAND("Real")) {
result.insert("status", "exposed");
}
else if (FC_WEB_DESKTOP_COMMAND("Boundary")) {
result.insert("status", "not_implemented");
}
'''
self.assertEqual(capability_generator.implemented_desktop_commands(source), {"Combined", "CommandVariable", "First", "QtStyle", "Real"})
def test_part_desktop_commands_are_parameter_driven_and_split(self) -> None:
workbench = (ROOT / "web/src/workbench-shell.tsx").read_text(encoding="utf-8")
dispatcher = (ROOT / "web/src/desktop-command-dispatcher.ts").read_text(encoding="utf-8")
cmake = (ROOT / "freecad-source/src/WebAssembly/FreeCADCoreProbe/CMakeLists.txt").read_text(encoding="utf-8")
native = (ROOT / "freecad-source/src/WebAssembly/FreeCADCoreProbe/PartDesktopCommands.cpp").read_text(encoding="utf-8")
sync = (ROOT / "web/tools/sync-assets.mjs").read_text(encoding="utf-8")
self.assertNotIn('"ShapeA"', workbench)
self.assertNotIn('"ShapeB"', workbench)
self.assertIn("DesktopCommandDispatcher", workbench)
self.assertIn("record.sourcePath", dispatcher)
self.assertIn("PartDesktopCommands.cpp", cmake)
self.assertIn("args.shapes", native)
self.assertIn("FC_WEB_DESKTOP_COMMAND", native)
self.assertIn("FreeCADWeb[A-Za-z0-9]+DesktopCommandProbe", sync)
def test_desktop_routes_are_generated_from_native_ownership(self) -> None:
routes = json.loads((ROOT / "web/public/api/desktop-command-routes.json").read_text(encoding="utf-8"))
by_id = {row["id"]: row for row in routes["records"]}
self.assertEqual(by_id["PartDesign:desktop:PartDesign_AdditiveHelix"]["transport"], "direct-command")
self.assertEqual(by_id["PartDesign:desktop:PartDesign_AdditiveHelix"]["worker"], "partdesign-python.worker.ts")
self.assertEqual(by_id["Part:desktop:Boolean"]["transport"], "ui-container")
self.assertEqual(by_id["Part:desktop:Part_ColorPerFace"]["transport"], "family-batch")
self.assertEqual(by_id["Sketcher:desktop:Sketcher_CarbonCopy"]["module"], "Sketcher")
self.assertNotIn("PARTDESIGN_DESKTOP_DEFAULT_ARGS", (ROOT / "web/src/workbench-shell.tsx").read_text(encoding="utf-8"))
def test_registry_generator_has_no_module_specific_desktop_branch(self) -> None:
source = (ROOT / "tools/generate_web_module_registry.py").read_text(encoding="utf-8")
self.assertNotIn('if module == "PartDesign"', source)
self.assertIn("native_desktop_commands(module)", source)
def test_feature_registry_scans_implemented_markers_in_split_adapter(self) -> None:
source = (ROOT / "tools/generate_feature_capability_registry.py").read_text(encoding="utf-8")
desktop_loop = source[source.index('glob("*DesktopCommands.cpp")'):source.index('glob("*Probe.cpp")')]
self.assertIn("implemented_desktop_commands(text)", desktop_loop)
def test_feature_registry_scans_implemented_markers_in_inventory_api(self) -> None:
source = (ROOT / "tools/generate_feature_capability_registry.py").read_text(encoding="utf-8")
self.assertIn('glob("*InventoryApi.cpp")', source)
inventory = (ROOT / "freecad-source/src/WebAssembly/FreeCADCoreProbe/RobotInventoryApi.cpp").read_text(encoding="utf-8")
self.assertTrue(
{
"Robot_CreateTrajectory",
"Robot_Edge2Trac",
"Robot_InsertWaypoint",
"Robot_InsertWaypointPreselect",
}.issubset(capability_generator.implemented_desktop_commands(inventory))
)
def test_implemented_marker_accepts_desktop_name_branch(self) -> None:
source = '''
if (desktopName == QStringLiteral("MeshPart_Mesher")) {
FC_WEB_IMPLEMENTED_DESKTOP_COMMAND("MeshPart_Mesher");
result.insert(QStringLiteral("status"), QStringLiteral("exposed"));
}
'''
self.assertEqual(
capability_generator.implemented_desktop_commands(source),
{"MeshPart_Mesher"},
)
def test_implemented_marker_accepts_record_status_object(self) -> None:
source = '''
if (name == QStringLiteral("TemplatePyMod_Cmd1")) {
FC_WEB_IMPLEMENTED_DESKTOP_COMMAND("TemplatePyMod_Cmd1");
record.insert(QStringLiteral("status"), QStringLiteral("exposed"));
}
'''
self.assertEqual(
capability_generator.implemented_desktop_commands(source),
{"TemplatePyMod_Cmd1"},
)
def test_implemented_marker_accepts_start_ostringstream_status(self) -> None:
source = (ROOT / "freecad-source/src/WebAssembly/FreeCADCoreProbe/StartRuntimeProbe.cpp").read_text(encoding="utf-8")
self.assertIn("Start_Start", capability_generator.implemented_desktop_commands(source))
def test_implemented_marker_accepts_command_specific_native_execution_helper(self) -> None:
source = '''
#define FC_WEB_IMPLEMENTED_DESKTOP_COMMAND(name)
FC_WEB_IMPLEMENTED_DESKTOP_COMMAND("FEM_Analysis")
if (command == QStringLiteral("FEM_Analysis")) {
return executeObjectCommand(command, QStringLiteral("Fem::FemAnalysis"), args, result);
}
'''
self.assertEqual(capability_generator.implemented_desktop_commands(source), {"FEM_Analysis"})
def test_implemented_marker_accepts_single_statement_native_execution_helper(self) -> None:
source = '''
#define FC_WEB_IMPLEMENTED_DESKTOP_COMMAND(name)
FC_WEB_IMPLEMENTED_DESKTOP_COMMAND("FEM_SolverZ88")
if (command == QStringLiteral("FEM_SolverZ88")) return executeObjectCommand(command, QStringLiteral("Fem::FemSolverObject"), args, result);
'''
self.assertEqual(capability_generator.implemented_desktop_commands(source), {"FEM_SolverZ88"})
def test_start_direct_worker_synthesizes_route_without_duplicate_registry_entry(self) -> None:
source = (ROOT / "tools/generate_feature_capability_registry.py").read_text(encoding="utf-8")
self.assertIn('if exact is None and module not in desktop_adapter_modules:', source)
self.assertIn('"uiStatus": "generic-command-entry"', source)
routes = json.loads((ROOT / "web/public/api/desktop-command-routes.json").read_text(encoding="utf-8"))
start = next((row for row in routes.get("records", []) if row.get("id") == "Start:desktop:Start_Start"), None)
if start is not None:
self.assertEqual(start.get("transport"), "direct-command")
self.assertEqual(start.get("worker"), "start.worker.ts")
def test_templatepymod_desktop_contract_includes_final_command(self) -> None:
source = (ROOT / "web/src/templatepymod-python-family-facade.ts").read_text(encoding="utf-8")
declaration = next(line for line in source.splitlines() if "TEMPLATEPYMOD_DESKTOP_COMMANDS" in line)
self.assertIn('"TemplatePyMod_Cmd5"', declaration)
def test_workbench_groups_are_ui_containers_but_leaf_commands_are_not(self) -> None:
self.assertTrue(capability_generator.is_ui_container({
"command": "Constraints",
"sourcePath": "src/Mod/Sketcher/Gui/Workbench.cpp",
"sourceLine": 82,
}))
self.assertTrue(capability_generator.is_ui_container({
"command": "Boolean",
"sourcePath": "src/Mod/Part/Gui/Workbench.cpp",
"sourceLine": 93,
}))
self.assertFalse(capability_generator.is_ui_container({
"command": "Part_ColorPerFace",
"sourcePath": "src/Mod/Part/Gui/Workbench.cpp",
"sourceLine": 165,
}))
self.assertTrue(capability_generator.is_ui_container({
"command": "BIM_ArcTools",
"sourcePath": "src/Mod/BIM/InitGui.py",
"sourceLine": 390,
}))
self.assertTrue(capability_generator.is_ui_container({
"command": "Arch_RebarTools",
"sourcePath": "src/Mod/BIM/InitGui.py",
"sourceLine": 424,
}))
def test_desktop_transport_does_not_inflate_business_inventory(self) -> None:
registry = json.loads((ROOT / "docs/current/feature-capability-registry.json").read_text(encoding="utf-8"))
self.assertFalse(any(row["id"].endswith(".desktopCommand") for row in registry["records"]))
def test_implementation_packet_has_multiple_native_edit_anchors(self) -> None:
path = ROOT / "freecad-source/src/WebAssembly/FreeCADCoreProbe/SketcherProbe.cpp"
regions = packet.editable_excerpts(path, path.read_text(encoding="utf-8"), ["command-not-yet-registered"])
roles = {row["role"] for row in regions}
self.assertIn("desktop-handler", roles)
self.assertIn("command-registration", roles)
self.assertIn("unified-dispatch", roles)
def test_feature_packet_focuses_native_edits_on_dispatch_owner(self) -> None:
source = (ROOT / "tools/prepare_module_implementation_packet.py").read_text(encoding="utf-8")
self.assertIn("unified_dispatch_paths", source)
self.assertIn("path not in unified_dispatch_paths", source)
packet_payload = json.loads((ROOT / "docs/current/module-implementation-packet.json").read_text(encoding="utf-8"))
if packet_payload.get("module") == "Surface" and packet_payload.get("family", "").startswith("desktop-command"):
omitted = set(packet_payload.get("omittedStableSupport", []))
self.assertIn("freecad-source/src/WebAssembly/FreeCADCoreProbe/SurfaceRuntimeProbe.cpp", omitted)
def test_module_without_desktop_handler_keeps_family_or_abi_anchor(self) -> None:
path = ROOT / "freecad-source/src/WebAssembly/FreeCADCoreProbe/AssemblyRuntimeProbe.cpp"
regions = packet.editable_excerpts(path, path.read_text(encoding="utf-8"), ["Assembly_CommandNotRegistered"])
roles = {row["role"] for row in regions}
self.assertTrue(roles & {"family-record-handler", "family-dispatch", "unified-dispatch"})
def test_probe_discovery_is_case_insensitive_for_bim(self) -> None:
self.assertIn("BimRuntimeProbe.cpp", {path.name for path in packet.probe_sources("BIM")})
def test_probe_discovery_includes_suffixed_robot_native_units(self) -> None:
names = {path.name for path in packet.probe_sources("Robot")}
self.assertIn("RobotRuntimeProbeFixed.cpp", names)
self.assertIn("RobotInventoryApi.cpp", names)
def test_cpp_command_excerpt_resolves_activated_semantics(self) -> None:
path = ROOT / "freecad-source/src/Mod/Sketcher/Gui/CommandSketcherBSpline.cpp"
line, excerpt = packet.cpp_command_excerpt(path, "Sketcher_BSplineConvertToNURBS", 112)
self.assertGreater(line, 112)
self.assertIn("CmdSketcherConvertToNURBS::activated", excerpt)
self.assertIn("convertToNURBS", excerpt)
class BatchPreflightTests(unittest.TestCase):
def test_feature_batch_uses_feature_identity_and_ids(self) -> None:
batch = {
"schema": preflight.FEATURE_SCHEMA,
"strategy": "feature-first-deferred-validation",
}
ids = ["Sketcher:desktop:Sketcher_EditSketch"]
self.assertEqual(
preflight.batch_identity_parts(batch, "Sketcher", "feature-workflow", "desktop-command-command", ids),
["Sketcher", "desktop-command-command", *ids],
)
self.assertTrue(preflight.record_belongs_to_batch(batch, "Sketcher", ids[0]))
def test_legacy_batch_retains_tier_and_prefixed_ids(self) -> None:
batch = {"schema": "freecad-web-module-batch/v1"}
ids = ["py:Draft:Vector.add"]
self.assertEqual(
preflight.batch_identity_parts(batch, "Draft", "tier-2-pure", "geometry-value", ids),
["Draft", "tier-2-pure", "geometry-value", *ids],
)
self.assertTrue(preflight.record_belongs_to_batch(batch, "Draft", ids[0]))
self.assertFalse(preflight.record_belongs_to_batch(batch, "Part", ids[0]))
class AdaptiveBatchTests(unittest.TestCase):
def test_routed_record_without_receipt_remains_schedulable(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
previous = (
feature_planner.REGISTRY,
feature_planner.ISSUES,
feature_planner.STATE,
feature_planner.OUTPUT,
feature_planner.PIPELINE,
feature_planner.capability_source_fingerprint,
feature_planner.implementation_ready_ids,
)
try:
feature_planner.REGISTRY = root / "capabilities.json"
feature_planner.ISSUES = root / "issues.json"
feature_planner.STATE = root / "state.json"
feature_planner.OUTPUT = root / "batch.json"
feature_planner.PIPELINE = root / "pipeline.json"
feature_planner.capability_source_fingerprint = lambda: "fingerprint"
feature_planner.implementation_ready_ids = lambda: set()
feature_planner.REGISTRY.write_text(json.dumps({"records": [{
"id": "Start:desktop:Start_Start",
"module": "Start",
"command": "Start_Start",
"implementationStatus": "routed",
"uiStatus": "generic-command-entry",
"sourcePath": "src/Mod/Start/Gui/Manipulator.cpp",
"sourceLine": 42,
}]}), encoding="utf-8")
feature_planner.ISSUES.write_text('{"issues":[]}', encoding="utf-8")
feature_planner.STATE.write_text('{"active_task":"FW-P6-FPA-Y"}', encoding="utf-8")
with contextlib.redirect_stdout(io.StringIO()):
self.assertEqual(feature_planner.main(), 0)
batch = json.loads(feature_planner.OUTPUT.read_text(encoding="utf-8"))
self.assertEqual(batch["status"], "ready")
self.assertEqual(batch["records"][0][0], "Start:desktop:Start_Start")
finally:
(
feature_planner.REGISTRY,
feature_planner.ISSUES,
feature_planner.STATE,
feature_planner.OUTPUT,
feature_planner.PIPELINE,
feature_planner.capability_source_fingerprint,
feature_planner.implementation_ready_ids,
) = previous
def test_feature_batch_size_is_bounded_and_runtime_issues_stay_small(self) -> None:
self.assertGreater(feature_planner.FEATURE_BATCH_SIZE, 8)
self.assertLessEqual(feature_planner.FEATURE_BATCH_SIZE, 32)
self.assertLessEqual(feature_planner.RUNTIME_ISSUE_BATCH_SIZE, feature_planner.FEATURE_BATCH_SIZE)
def test_small_desktop_families_are_packed_within_one_module(self) -> None:
ordered = [
(("Sketcher", "desktop-command-a"), [{"id": "a1"}]),
(("Sketcher", "desktop-command-b"), [{"id": "b1"}, {"id": "b2"}]),
(("TechDraw", "desktop-command-c"), [{"id": "c1"}]),
]
module, family, rows, source_families = feature_planner.select_group_batch(ordered)
self.assertEqual(module, "Sketcher")
self.assertEqual(family, "desktop-command-mixed")
self.assertEqual([row["id"] for row in rows], ["a1", "b1", "b2"])
self.assertEqual(source_families, ["desktop-command-a", "desktop-command-b"])
def test_mixed_feature_batch_caps_heterogeneous_records(self) -> None:
ordered = [
(("CAM", "desktop-command-a"), [{"id": f"a{index}"} for index in range(4)]),
(("CAM", "desktop-command-b"), [{"id": f"b{index}"} for index in range(8)]),
]
module, family, rows, source_families = feature_planner.select_group_batch(ordered)
self.assertEqual(module, "CAM")
self.assertEqual(family, "desktop-command-mixed")
self.assertEqual(len(rows), feature_planner.MIXED_FEATURE_BATCH_SIZE)
self.assertEqual(source_families, ["desktop-command-a", "desktop-command-b"])
def test_runtime_issue_batch_is_never_mixed(self) -> None:
ordered = [
(("Part", "runtime-issue"), [{"id": "issue"}]),
(("Part", "desktop-command-a"), [{"id": "a1"}]),
]
_, family, rows, source_families = feature_planner.select_group_batch(ordered)
self.assertEqual(family, "runtime-issue")
self.assertEqual([row["id"] for row in rows], ["issue"])
self.assertEqual(source_families, ["runtime-issue"])
def test_feature_batch_deduplicates_records_at_final_boundary(self) -> None:
ordered = [
(("Part", "runtime-issue"), [{"id": "same"}, {"id": "same"}, {"id": "first"}]),
(("Part", "desktop-command-a"), [{"id": "same"}, {"id": "second"}]),
]
_, family, rows, _ = feature_planner.select_group_batch(ordered, 4)
self.assertEqual(family, "runtime-issue")
self.assertEqual([row["id"] for row in rows], ["same", "first"])
def test_stalled_zero_ready_feature_batch_shrinks_to_four(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
previous_output = feature_planner.OUTPUT
previous_pipeline = feature_planner.PIPELINE
previous_continuation_pipeline = continuation.MODULE_PIPELINE
previous_override = os.environ.get("FREECAD_IMPLEMENTATION_FIRST")
try:
feature_planner.OUTPUT = root / "batch.json"
feature_planner.PIPELINE = root / "pipeline.json"
continuation.MODULE_PIPELINE = feature_planner.PIPELINE
os.environ["FREECAD_IMPLEMENTATION_FIRST"] = "1"
old = "2020-01-01T00:00:00+00:00"
batch = {
"status": "ready",
"strategy": "feature-first-deferred-validation",
"batchId": "stalled",
"generatedAt": old,
"selected": {"recordCount": 16},
}
pipeline_state = {
"status": "implementation_incomplete",
"batchId": "stalled",
"staticReadiness": {"ready": 0, "missing": 16},
}
feature_planner.OUTPUT.write_text(json.dumps(batch), encoding="utf-8")
feature_planner.PIPELINE.write_text(json.dumps(pipeline_state), encoding="utf-8")
self.assertEqual(feature_planner.stalled_batch_limit(), 4)
self.assertEqual(continuation.feature_batch_replan_size(batch), 4)
ordered = [(('Mesh', 'desktop-command-command'), [{"id": str(index)} for index in range(16)])]
self.assertEqual(len(feature_planner.select_group_batch(ordered, 4)[2]), 4)
pipeline_state["staticReadiness"]["ready"] = 1
feature_planner.PIPELINE.write_text(json.dumps(pipeline_state), encoding="utf-8")
self.assertIsNone(feature_planner.stalled_batch_limit())
self.assertIsNone(continuation.feature_batch_replan_size(batch))
finally:
feature_planner.OUTPUT = previous_output
feature_planner.PIPELINE = previous_pipeline
continuation.MODULE_PIPELINE = previous_continuation_pipeline
if previous_override is None:
os.environ.pop("FREECAD_IMPLEMENTATION_FIRST", None)
else:
os.environ["FREECAD_IMPLEMENTATION_FIRST"] = previous_override
def test_existing_oversized_mixed_batch_replans_immediately(self) -> None:
previous_override = os.environ.get("FREECAD_IMPLEMENTATION_FIRST")
try:
os.environ["FREECAD_IMPLEMENTATION_FIRST"] = "1"
batch = {
"status": "ready",
"strategy": "feature-first-deferred-validation",
"selected": {"family": "desktop-command-mixed", "recordCount": 16},
}
self.assertEqual(continuation.feature_batch_replan_size(batch), continuation.MIXED_FEATURE_BATCH_SIZE)
finally:
if previous_override is None:
os.environ.pop("FREECAD_IMPLEMENTATION_FIRST", None)
else:
os.environ["FREECAD_IMPLEMENTATION_FIRST"] = previous_override
def setUp(self) -> None:
self.directory = tempfile.TemporaryDirectory()
planner.SCHEDULER_TIMING = Path(self.directory.name) / "timing.json"
planner.SCHEDULER_STATUS = Path(self.directory.name) / "status.json"
planner.SCHEDULER_STATUS.write_text("{}", encoding="ascii")
def tearDown(self) -> None:
self.directory.cleanup()
def write_samples(self, durations: list[int], failed: bool = False) -> None:
planner.SCHEDULER_TIMING.write_text(json.dumps({"cycles": [
{"effectiveCycleSeconds": duration, "batch": {"module": "Draft", "tier": "tier-2-pure", "family": "geometry-value", "records": 24}, "activity": {"commandFailures": 1 if failed and index == 0 else 0}}
for index, duration in enumerate(durations)
]}), encoding="utf-8")
def test_fast_family_grows_within_cap(self) -> None:
self.write_samples([60, 65, 70])
self.assertEqual(planner.adaptive_target_size(None, "tier-2-pure", "Draft", "geometry-value")[0], 32)
def test_failure_halves_batch(self) -> None:
self.write_samples([100, 110, 120], failed=True)
self.assertEqual(planner.adaptive_target_size(None, "tier-2-pure", "Draft", "geometry-value")[0], 12)
def test_slow_family_shrinks_gradually(self) -> None:
self.write_samples([450, 460, 470])
self.assertEqual(planner.adaptive_target_size(None, "tier-2-pure", "Draft", "geometry-value")[0], 20)
class SessionTests(unittest.TestCase):
def test_prompt_context_has_a_hard_byte_limit(self) -> None:
prompt = "rules\n" + ("source excerpt 数据\n" * 10_000) + "batchId=current-batch\n"
compacted = scheduler.bound_prompt_context(prompt, 12_000)
self.assertLessEqual(len(compacted.encode("utf-8")), 12_000)
self.assertIn("[CONTEXT_COMPACTED by unattended scheduler:", compacted)
self.assertIn("batchId=current-batch", compacted)
def test_provider_failure_discards_resumed_session(self) -> None:
with tempfile.TemporaryDirectory() as directory:
previous_path = scheduler.SESSION_CACHE_PATH
try:
scheduler.SESSION_CACHE_PATH = Path(directory) / "session.json"
scheduler.SESSION_CACHE_PATH.write_text(json.dumps({
"module": "Part",
"threadId": "old-thread",
"lastBatchId": "batch",
}), encoding="utf-8")
metadata = {
"threadId": "old-thread",
"streamFailed": True,
"providerFailureReason": "servers are currently overloaded",
}
scheduler.save_session_cache("Part", "batch", metadata, None, False)
self.assertFalse(scheduler.SESSION_CACHE_PATH.exists())
self.assertTrue(metadata["contextRotatedAfterProviderFailure"])
finally:
scheduler.SESSION_CACHE_PATH = previous_path
def test_partial_native_routing_replans_only_remaining_feature_records(self) -> None:
with tempfile.TemporaryDirectory() as directory:
previous_capability = continuation.CAPABILITY_REGISTRY
try:
continuation.CAPABILITY_REGISTRY = Path(directory) / "capability.json"
continuation.CAPABILITY_REGISTRY.write_text(json.dumps({
"records": [
{"id": "ReverseEngineering:desktop:Implemented", "implementationStatus": "routed"},
{"id": "ReverseEngineering:desktop:Remaining", "implementationStatus": "implementation_required"},
]
}), encoding="utf-8")
batch = {
"strategy": "feature-first-deferred-validation",
"records": [
["ReverseEngineering:desktop:Implemented", "Implemented"],
["ReverseEngineering:desktop:Remaining", "Remaining"],
],
}
self.assertEqual(
continuation.selected_routed_ids(batch),
{"ReverseEngineering:desktop:Implemented"},
)
finally:
continuation.CAPABILITY_REGISTRY = previous_capability
def test_stale_receipt_is_not_ready_when_capability_route_is_revoked(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
previous_registry = implementation_first.REGISTRY_PATH
previous_capability = implementation_first.CAPABILITY_PATH
previous_override = os.environ.get("FREECAD_IMPLEMENTATION_FIRST")
try:
implementation_first.REGISTRY_PATH = root / "registry.json"
implementation_first.CAPABILITY_PATH = root / "capability.json"
os.environ["FREECAD_IMPLEMENTATION_FIRST"] = "1"
implementation_first.REGISTRY_PATH.write_text(json.dumps({
"schema": "freecad-web-implementation-first-registry/v1",
"records": {
"Mesh:desktop:Real": {"status": "implementation_ready"},
"Mesh:desktop:Revoked": {"status": "implementation_ready"},
},
}), encoding="utf-8")
implementation_first.CAPABILITY_PATH.write_text(json.dumps({"records": [
{"id": "Mesh:desktop:Real", "implementationStatus": "routed"},
{"id": "Mesh:desktop:Revoked", "implementationStatus": "implementation_required"},
]}), encoding="utf-8")
self.assertEqual(implementation_first.implementation_ready_ids(), {"Mesh:desktop:Real"})
finally:
implementation_first.REGISTRY_PATH = previous_registry
implementation_first.CAPABILITY_PATH = previous_capability
if previous_override is None:
os.environ.pop("FREECAD_IMPLEMENTATION_FIRST", None)
else:
os.environ["FREECAD_IMPLEMENTATION_FIRST"] = previous_override
def test_rg_no_match_is_not_a_scheduler_failure(self) -> None:
command = "/bin/bash -lc \"rg -n 'Missing|StillMissing' file.cpp\""
self.assertTrue(scheduler.exploratory_no_match(command, 1, ""))
self.assertTrue(scheduler.exploratory_no_match('/bin/bash -lc "sed -n 1,20p file.cpp; rg Missing file.cpp"', 1, "source excerpt"))
self.assertFalse(scheduler.exploratory_no_match(command, 2, ""))
self.assertFalse(scheduler.exploratory_no_match('/bin/bash -lc "rg Missing file.cpp && build"', 1, ""))
def test_old_rg_no_match_failure_is_normalized(self) -> None:
state = {"failures": {"old": {"command": "rg Missing file.cpp", "lastExitCode": 1, "firstError": "", "resolved": False}}}
self.assertEqual(scheduler.normalize_exploratory_no_match_failures(state), 1)
self.assertFalse(state["failures"]["old"]["resolved"])
self.assertEqual(state["failures"]["old"]["diagnosticReview"], "rg_no_match")
scope_state = {"failures": {"scope": {"command": "rg Pattern web/src", "lastExitCode": 64, "firstError": "SCHEDULER_SEARCH_SCOPE_REJECTED", "resolved": False}}}
self.assertEqual(scheduler.normalize_exploratory_no_match_failures(scope_state), 1)
self.assertFalse(scope_state["failures"]["scope"]["resolved"])
self.assertEqual(scope_state["failures"]["scope"]["diagnosticReview"], "rg_scope")
compat_state = {"failures": {"compat": {"command": "python3 tools/fast_source_search.py P --paths web", "lastExitCode": 2, "firstError": "unrecognized arguments: --paths web", "resolved": False}}}
self.assertEqual(scheduler.normalize_exploratory_no_match_failures(compat_state), 1)
self.assertFalse(compat_state["failures"]["compat"]["resolved"])
self.assertEqual(compat_state["failures"]["compat"]["diagnosticReview"], "tool_compat")
def test_scheduler_rg_rejects_directories_and_normalizes_no_match(self) -> None:
source = (ROOT / "tools/scheduler-bin/rg").read_text(encoding="utf-8")
self.assertIn('-d "$argument"', source)
self.assertIn("SCHEDULER_RG_NO_MATCH", source)
self.assertIn("SCHEDULER_SEARCH_SCOPE_SKIPPED", source)
self.assertIn("--max-columns 240 --max-columns-preview", source)
self.assertIn("SCHEDULER_SOURCE_BUDGET_REACHED", source)
scheduler_source = (ROOT / "tools/run_freecad_web_scheduler.py").read_text(encoding="utf-8")
self.assertIn('environment["BASH_ENV"]', scheduler_source)
fast_source = (ROOT / "tools/fast_source_search.py").read_text(encoding="utf-8")
self.assertIn("output_lines[:global_limit]", fast_source)
self.assertIn('"--max-count", "--max-results"', fast_source)
self.assertIn('os.environ.get("FREECAD_SOURCE_BUDGET_MARKER"', fast_source)
def test_feature_read_budget_is_soft_until_edit_or_hard_limit(self) -> None:
self.assertFalse(scheduler.source_read_should_stop(4, 0, 4, 5))
self.assertTrue(scheduler.source_read_should_stop(5, 0, 4, 5))
self.assertTrue(scheduler.source_read_should_stop(5, 1, 4, 5))
def test_source_budget_marks_search_boundary(self) -> None:
self.assertTrue(scheduler.source_read_should_stop(5, 0, 4, 5))
self.assertTrue(scheduler.source_read_should_stop(6, 0, 4, 5))
self.assertFalse(scheduler.source_read_should_stop(3, 0, 4, 5))
def test_token_prediction_rotates_before_limit(self) -> None:
with tempfile.TemporaryDirectory() as directory:
scheduler.SESSION_CACHE_PATH = Path(directory) / "session.json"
first_metadata = {"threadId": "thread", "usage": {"input_tokens": 150_000, "cached_input_tokens": 100_000}}
scheduler.save_session_cache("Draft", "b1", first_metadata, None, True)
first = scheduler.load_session_cache()
self.assertEqual(first["predictedNextInputTokens"], 300_000)
second_metadata = {"threadId": "thread", "usage": {"input_tokens": 330_000, "cached_input_tokens": 200_000}}
scheduler.save_session_cache("Draft", "b2", second_metadata, first, True)
second = scheduler.load_session_cache()
self.assertEqual(second["predictedNextInputTokens"], 510_000)
self.assertIsNotNone(scheduler.resumable_session("Draft"))
second["predictedNextInputTokens"] = scheduler.SESSION_INPUT_TOKEN_LIMIT
scheduler.SESSION_CACHE_PATH.write_text(json.dumps(second), encoding="utf-8")
self.assertIsNone(scheduler.resumable_session("Draft"))
def test_pipeline_policy_keeps_budget_stopped_edits_eligible(self) -> None:
source = (ROOT / "tools/run_freecad_web_scheduler.py").read_text(encoding="utf-8")
self.assertIn("pipeline_eligible = return_code == 0 and not any((timed_out, stream_failed, agent_idle_stopped))", source)
self.assertIn("pipeline_eligible and session_clean if feature_turn", source)
self.assertIn('status="batch_static_pipeline_complete"', source)
def test_feature_session_reuses_only_same_batch(self) -> None:
with tempfile.TemporaryDirectory() as directory:
scheduler.SESSION_CACHE_PATH = Path(directory) / "session.json"
metadata = {"threadId": "feature-thread", "usage": {"input_tokens": 10_000}}
scheduler.save_session_cache("PartDesign", "batch-a", metadata, None, True)
self.assertIsNotNone(scheduler.resumable_session("PartDesign", "batch-a", same_batch_only=True))
self.assertIsNone(scheduler.resumable_session("PartDesign", "batch-b", same_batch_only=True))
def test_compile_recovery_reuses_same_batch_session(self) -> None:
source = (ROOT / "tools/run_freecad_web_scheduler.py").read_text(encoding="utf-8")
self.assertIn("same_batch_only=compile_recovery or feature_turn", source)
self.assertGreaterEqual(scheduler.COMPILE_RECOVERY_IDLE_LIMIT_SECONDS, 20.0)
self.assertGreaterEqual(scheduler.POST_EDIT_FINALIZE_SECONDS, 15.0)
def test_implementation_turn_blocks_wrapped_validation_commands(self) -> None:
self.assertTrue(scheduler.forbidden_implementation_command(
"/bin/bash -lc 'git diff --check -- file && npx --prefix web tsc --noEmit -p web/tsconfig.json'",
"source/read",
))
self.assertTrue(scheduler.forbidden_implementation_command(
"npm --prefix web run build",
"source/read",
))
self.assertTrue(scheduler.forbidden_implementation_command(
"git status --short -- web/src/module.ts",
"source/read",
))
def test_failed_pipeline_skip_requires_unchanged_support_files(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
pipeline_path = root / "pipeline.json"
packet_path = root / "packet.json"
support = root / "Probe.cpp"
support.write_text("source", encoding="ascii")
pipeline_path.write_text(json.dumps({
"status": "failed",
"batchId": "batch-a",
"failedStage": "build",
}), encoding="ascii")
packet_path.write_text(json.dumps({
"batchId": "batch-a",
"supportFiles": [{"path": "Probe.cpp"}],
}), encoding="ascii")
previous_pipeline = scheduler.MODULE_PIPELINE_PATH
previous_root = scheduler.ROOT
try:
scheduler.MODULE_PIPELINE_PATH = pipeline_path
scheduler.ROOT = root
self.assertTrue(scheduler.failed_pipeline_is_unchanged("batch-a", json.loads(packet_path.read_text(encoding="ascii"))))
support.write_text("changed", encoding="ascii")
os.utime(support, ns=(pipeline_path.stat().st_mtime_ns + 1, pipeline_path.stat().st_mtime_ns + 1))
self.assertFalse(scheduler.failed_pipeline_is_unchanged("batch-a", json.loads(packet_path.read_text(encoding="ascii"))))
finally:
scheduler.MODULE_PIPELINE_PATH = previous_pipeline
scheduler.ROOT = previous_root
def test_resumed_feature_turn_uses_low_reasoning(self) -> None:
source = (ROOT / "tools/run_freecad_web_scheduler.py").read_text(encoding="utf-8")
self.assertIn('"low" if feature_turn or batch_has_completed_evidence(batch)', source)
self.assertLess(scheduler.FEATURE_SOURCE_READS_PER_TURN, scheduler.MAX_SOURCE_READS_PER_TURN)
self.assertGreater(scheduler.FEATURE_SOURCE_READS_HARD_LIMIT, scheduler.FEATURE_SOURCE_READS_PER_TURN)
self.assertEqual(scheduler.FEATURE_PATCH_CONFLICT_LIMIT, 1)
def test_packet_resolves_plain_module_worker(self) -> None:
source = (ROOT / "tools/prepare_module_implementation_packet.py").read_text(encoding="utf-8")
self.assertIn('f"web/src/{slug}.worker.ts"', source)
self.assertIn("relative in worker_paths", source)
self.assertIn('glob(f"{slug}*python*family*facade.ts")', source)
self.assertIn('r"PythonFamily(?:Batch)?Request"', source)
self.assertIn("relative in facade_paths", source)
def test_packet_recommends_splitting_oversized_desktop_adapter(self) -> None:
source = (ROOT / "tools/prepare_module_implementation_packet.py").read_text(encoding="utf-8")
self.assertIn("DESKTOP_ADAPTER_SPLIT_BYTES", source)
self.assertIn('"desktopAdapterRecommended"', source)
self.assertIn('"oversizedNativeSources"', source)
self.assertIn('"desktopAdapterAction"', source)
self.assertIn('"desktopAdapterActive"', source)
self.assertIn("path != desktop_adapter", source)
def test_resumed_feature_prompt_reuses_thread_packet(self) -> None:
with tempfile.TemporaryDirectory() as directory:
previous_batch = scheduler.MODULE_BATCH_PATH
previous_pipeline = scheduler.MODULE_PIPELINE_PATH
try:
scheduler.MODULE_BATCH_PATH = Path(directory) / "batch.json"
scheduler.MODULE_PIPELINE_PATH = Path(directory) / "pipeline.json"
batch = {
"status": "ready",
"batchId": "resume-batch",
"selected": {"module": "Start", "family": "desktop-command", "recordCount": 1},
"records": [["Start:desktop:Start_Start", "Start_Start"]],
}
scheduler.MODULE_BATCH_PATH.write_text(json.dumps(batch), encoding="utf-8")
scheduler.MODULE_PIPELINE_PATH.write_text("{}", encoding="utf-8")
prompt = scheduler.build_prompt("FW-P6-FPA-Y", failures=[], resumed=True)
self.assertIn(str(batch["batchId"]), prompt)
self.assertIn("上一轮的完整工作包", prompt)
self.assertNotIn("official source excerpts", prompt)
self.assertNotIn("editable excerpts:", prompt)
self.assertLess(len(prompt.encode("utf-8")), 4_000)
finally:
scheduler.MODULE_BATCH_PATH = previous_batch
scheduler.MODULE_PIPELINE_PATH = previous_pipeline
def test_conflicted_feature_session_is_not_reused(self) -> None:
source = (ROOT / "tools/run_freecad_web_scheduler.py").read_text(encoding="utf-8")
self.assertIn('not int(turn_metadata.get("patchConflicts", 0) or 0)', source)
self.assertIn('"custom tool call output is missing" in lowered', source)
self.assertIn('session_clean = bool(turn_metadata.get("turnCompleted"))', source)
self.assertIn("source_budget_reached_event.set()", source)
self.assertNotIn('turn_metadata.get("safeBudgetStop")', source)
def test_status_liveness_checks_process_identity(self) -> None:
status_source = (ROOT / "tools/show_freecad_web_scheduler.py").read_text(encoding="utf-8")
self.assertIn("def process_matches", status_source)
self.assertIn("observedState: stale", status_source)
class EvidenceTests(unittest.TestCase):
def test_draft_compile_cache_does_not_replace_native_gate(self) -> None:
candidates = []
for path in (ROOT / "docs/p6").glob("draft-module-batch-*-runtime.json"):
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
continue
if isinstance(value.get("gateTiming"), dict) and value["gateTiming"].get("compileCacheHit") is True:
candidates.append(value)
self.assertTrue(candidates)
evidence = candidates[-1]
self.assertIn(evidence.get("status"), {"pass", "pass_with_boundaries"})
self.assertTrue(evidence.get("resources", {}).get("resourcesReleased"))
self.assertGreater(float(evidence["gateTiming"].get("workerReadySeconds", 0)), 0)
self.assertGreater(float(evidence["gateTiming"].get("dispatchSeconds", 0)), 0)
if __name__ == "__main__":
unittest.main(verbosity=2)