Complete name-first MCP write-plan composition
This commit is contained in:
@@ -821,9 +821,9 @@ def validate_code_write_saved_state(
|
||||
expected_transport=expected_transport,
|
||||
expected_endpoint_url=expected_endpoint_url,
|
||||
)
|
||||
if require_write and report.get("status") == "skipped_missing_target":
|
||||
if require_write and report.get("status") in {"skipped_missing_target", "skipped_write_gate"}:
|
||||
failures.append({"code": "code_write_saved_state_required", "path": str(path), "status": report.get("status")})
|
||||
if report.get("status") not in {"ok", "skipped_missing_target"}:
|
||||
if report.get("status") not in {"ok", "skipped_missing_target", "skipped_write_gate"}:
|
||||
failures.append({"code": "code_write_saved_state_status_unexpected", "path": str(path), "status": report.get("status")})
|
||||
if report.get("failures"):
|
||||
failures.append({"code": "code_write_saved_state_failures_present", "path": str(path), "failures": report.get("failures")})
|
||||
|
||||
@@ -82,12 +82,25 @@ def discover_object(base_url: str, base_id: str, source_table: str, timeout: flo
|
||||
def resolve_object(base_url: str, selector: dict[str, Any], source_table: str, timeout: float) -> dict[str, Any]:
|
||||
if not any(selector.get(key) for key in ("ref", "kind", "name", "guid")):
|
||||
return discover_object(base_url, str(selector["base_id"]), source_table, timeout)
|
||||
resolved = rpc(base_url, "metadata.object.get", selector | {"table": source_table}, timeout)
|
||||
resolved_object = resolved.get("object") if isinstance(resolved.get("object"), dict) else {}
|
||||
if resolved.get("status") == "ok" and resolved_object.get("guid"):
|
||||
return resolved_object
|
||||
if selector.get("kind") or selector.get("name") or selector.get("guid"):
|
||||
listed = rpc(base_url, "metadata.objects.list", selector | {"limit": 5, "table": source_table}, timeout)
|
||||
objects = [item for item in listed.get("objects") or [] if isinstance(item, dict)]
|
||||
if objects:
|
||||
return objects[0]
|
||||
found = rpc(base_url, "metadata.definition.find", selector | {"areas": ["metadata"], "include_storage": True}, timeout)
|
||||
definition_query = str(selector.get("ref") or selector.get("guid") or selector.get("name") or "").strip()
|
||||
found = rpc(
|
||||
base_url,
|
||||
"metadata.definition.find",
|
||||
selector | {"query": definition_query, "areas": ["metadata"], "include_storage": True},
|
||||
timeout,
|
||||
)
|
||||
found_object = found.get("object") if isinstance(found.get("object"), dict) else {}
|
||||
if found_object.get("guid"):
|
||||
return found_object
|
||||
matches = [item for item in found.get("matches") or found.get("items") or [] if isinstance(item, dict)]
|
||||
for item in matches:
|
||||
if item.get("guid") or (item.get("object") or {}).get("guid"):
|
||||
|
||||
@@ -135,6 +135,13 @@ def first_unique_fragment(text: str) -> str:
|
||||
return text
|
||||
|
||||
|
||||
def is_optional_write_gate_block(result: dict[str, Any]) -> bool:
|
||||
return result.get("status") == "blocked" and result.get("error") in {
|
||||
"needs_repository_lock",
|
||||
"support_gate_blocked",
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Smoke-test agent-facing 1C code.write saved-state workflow.")
|
||||
parser.add_argument("--transport", choices=("rest", "mcp"), default="rest")
|
||||
@@ -146,6 +153,11 @@ def main() -> int:
|
||||
parser.add_argument("--object-name", default="t_Форма")
|
||||
parser.add_argument("--routine-name", default="ЗаменаДомена")
|
||||
parser.add_argument("--allow-missing-target", action="store_true")
|
||||
parser.add_argument(
|
||||
"--allow-blocked-write-gate",
|
||||
action="store_true",
|
||||
help="Skip successfully when the target exists but repository/support policy blocks the first write.",
|
||||
)
|
||||
parser.add_argument("--timeout", type=int, default=60)
|
||||
parser.add_argument("--report", type=str)
|
||||
parser.add_argument("--json", action="store_true")
|
||||
@@ -223,6 +235,33 @@ def main() -> int:
|
||||
"write_mode": write.get("write_mode"),
|
||||
}
|
||||
)
|
||||
if args.allow_blocked_write_gate and is_optional_write_gate_block(write):
|
||||
report = {
|
||||
"schema": "onec_code_write_saved_state_smoke.v1",
|
||||
"status": "skipped_write_gate",
|
||||
"skipped": True,
|
||||
"endpoint_url": endpoint_url,
|
||||
"transport": args.transport,
|
||||
"base_id": args.base_id,
|
||||
"target": {"object_type": args.object_type, "object_name": args.object_name, "routine_name": args.routine_name},
|
||||
"steps": steps,
|
||||
"write_gate": {
|
||||
"status": write.get("status"),
|
||||
"error": write.get("error"),
|
||||
"diagnostics": write.get("diagnostics"),
|
||||
},
|
||||
"failures": [],
|
||||
**({"mcp_initialize": initialize_result} if initialize_result and args.transport == "mcp" else {}),
|
||||
}
|
||||
if args.report:
|
||||
os.makedirs(os.path.dirname(os.path.abspath(args.report)), exist_ok=True)
|
||||
with open(args.report, "w", encoding="utf-8") as handle:
|
||||
json.dump(report, handle, ensure_ascii=False, indent=2)
|
||||
if args.json:
|
||||
print(json.dumps(report, ensure_ascii=False, indent=2))
|
||||
else:
|
||||
print(f"1C code.write saved-state smoke: {report['status']}")
|
||||
return 0
|
||||
write_mode = write.get("write_mode") if isinstance(write.get("write_mode"), dict) else {}
|
||||
require_saved_state_write(write, "code.write routine apply", failures)
|
||||
|
||||
|
||||
@@ -117,7 +117,11 @@ def first_write_plan_target_from_saved_state(result: dict[str, Any]) -> dict[str
|
||||
if not isinstance(stream, dict):
|
||||
continue
|
||||
target = stream.get("write_plan_target")
|
||||
if isinstance(target, dict) and target.get("module_ref") and target.get("expected_sha1"):
|
||||
if not isinstance(target, dict):
|
||||
continue
|
||||
has_internal_handle = bool(target.get("module_ref") and target.get("expected_sha1"))
|
||||
has_public_selector = bool(target.get("ref") and target.get("module"))
|
||||
if has_internal_handle or has_public_selector:
|
||||
return target
|
||||
return None
|
||||
|
||||
@@ -791,10 +795,21 @@ def build_live_report(
|
||||
issues.append({"code": "live_composed_write_plan_apply_method_mismatch", "actual": route.get("apply_method")})
|
||||
if hint.get("ready_for_apply_method") is not True:
|
||||
issues.append({"code": "live_composed_write_plan_hint_not_ready", "hint": hint})
|
||||
if hint_payload.get("module_ref") != write_plan_target.get("module_ref"):
|
||||
if write_plan_target.get("module_ref") and hint_payload.get("module_ref") != write_plan_target.get("module_ref"):
|
||||
issues.append({"code": "live_composed_write_plan_module_ref_mismatch", "expected": write_plan_target.get("module_ref"), "actual": hint_payload.get("module_ref")})
|
||||
if hint_payload.get("expected_sha1") != write_plan_target.get("expected_sha1"):
|
||||
if write_plan_target.get("expected_sha1") and hint_payload.get("expected_sha1") != write_plan_target.get("expected_sha1"):
|
||||
issues.append({"code": "live_composed_write_plan_expected_sha1_mismatch"})
|
||||
if write_plan_target.get("ref"):
|
||||
name_resolution = route.get("name_resolution") if isinstance(route.get("name_resolution"), dict) else {}
|
||||
resolution_selector = name_resolution.get("selector") if isinstance(name_resolution.get("selector"), dict) else {}
|
||||
if resolution_selector.get("ref") != write_plan_target.get("ref"):
|
||||
issues.append(
|
||||
{
|
||||
"code": "live_composed_write_plan_name_resolution_mismatch",
|
||||
"expected": write_plan_target.get("ref"),
|
||||
"actual": resolution_selector.get("ref"),
|
||||
}
|
||||
)
|
||||
else:
|
||||
steps.append({"name": "metadata.write.plan", "status": "skipped_no_saved_state_target", "from_write_plan_target": False})
|
||||
else:
|
||||
|
||||
@@ -358,10 +358,10 @@ function Assert-CodeWriteSavedStateReport {
|
||||
if ($report.schema -ne "onec_code_write_saved_state_smoke.v1") {
|
||||
throw "$Label report has unexpected schema '$($report.schema)': $Path"
|
||||
}
|
||||
if ($RequireWrite -and $report.status -eq "skipped_missing_target") {
|
||||
throw "$Label report skipped missing target in strict mode: $Path"
|
||||
if ($RequireWrite -and $report.status -in @("skipped_missing_target", "skipped_write_gate")) {
|
||||
throw "$Label report skipped required write in strict mode: $Path"
|
||||
}
|
||||
if ($report.status -notin @("ok", "skipped_missing_target")) {
|
||||
if ($report.status -notin @("ok", "skipped_missing_target", "skipped_write_gate")) {
|
||||
throw "$Label report has unexpected status '$($report.status)': $Path"
|
||||
}
|
||||
if ($report.status -eq "ok") {
|
||||
@@ -683,7 +683,7 @@ try {
|
||||
"--json"
|
||||
)
|
||||
if (-not $RequireCodeWriteSavedStateSmoke) {
|
||||
$codeWriteCommand += "--allow-missing-target"
|
||||
$codeWriteCommand += @("--allow-missing-target", "--allow-blocked-write-gate")
|
||||
}
|
||||
Invoke-CheckedCommand -Label "REST adapter code.write saved-state smoke ($currentBaseId)" -Command $codeWriteCommand
|
||||
Assert-CodeWriteSavedStateReport -Label "REST adapter code.write saved-state smoke ($currentBaseId)" -Path $codeWriteReport -RequireWrite:$RequireCodeWriteSavedStateSmoke
|
||||
@@ -855,7 +855,7 @@ try {
|
||||
"--json"
|
||||
)
|
||||
if (-not $RequireCodeWriteSavedStateSmoke) {
|
||||
$mcpCodeWriteCommand += "--allow-missing-target"
|
||||
$mcpCodeWriteCommand += @("--allow-missing-target", "--allow-blocked-write-gate")
|
||||
}
|
||||
Invoke-CheckedCommand -Label "MCP proxy code.write saved-state smoke ($currentBaseId)" -Command $mcpCodeWriteCommand
|
||||
Assert-CodeWriteSavedStateReport -Label "MCP proxy code.write saved-state smoke ($currentBaseId)" -Path $mcpCodeWriteReport -RequireWrite:$RequireCodeWriteSavedStateSmoke
|
||||
|
||||
Reference in New Issue
Block a user