validate_host_contracts.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. """Validate fake records against the live parent Host and Agent contracts.
  2. Run from visualization/backend with both parent source roots and Host dependencies
  3. on PYTHONPATH. This remains a verification command, never a runtime dependency.
  4. """
  5. from __future__ import annotations
  6. from app.contracts import SCHEMA_CATALOG
  7. from app.fake_data import ARTIFACT_DIGESTS, GRAPH
  8. from agent.orchestration.wire import (
  9. AttemptView,
  10. OperationView,
  11. PlannerDecisionView,
  12. TaskSpecView,
  13. TaskView,
  14. ValidationView,
  15. )
  16. from script_build_host.domain.phase_three_artifacts import (
  17. canonicalize_legacy_projection,
  18. hydrate_root_delivery_manifest,
  19. root_delivery_input_closure_digest,
  20. )
  21. from script_build_host.domain.phase_two_artifacts import hydrate_phase_two_artifact
  22. from script_build_host.domain.task_contracts import ScriptTaskContractV1
  23. from script_build_host.infrastructure.legacy_tables import (
  24. script_build_element,
  25. script_build_paragraph,
  26. script_build_paragraph_element,
  27. )
  28. from script_build_host.infrastructure.tables import (
  29. artifact_version_table,
  30. input_snapshot_table,
  31. )
  32. from script_build_host.repositories.sqlalchemy import _snapshot_from_row
  33. WIRE_MODELS = {
  34. "TaskView": TaskView,
  35. "TaskSpecView": TaskSpecView,
  36. "OperationView": OperationView,
  37. "AttemptView": AttemptView,
  38. "ValidationView": ValidationView,
  39. "PlannerDecisionView": PlannerDecisionView,
  40. }
  41. PHASE_TWO_MODELS = {
  42. "StructureArtifactV1",
  43. "ParagraphArtifactV1",
  44. "ElementSetArtifactV1",
  45. "ComparisonArtifactV1",
  46. "StructuredScriptArtifactV1",
  47. "CandidatePortfolioArtifactV1",
  48. }
  49. def _assert_table_fields(model_name: str, table: object) -> None:
  50. columns = tuple(column.name for column in table.columns) # type: ignore[attr-defined]
  51. assert SCHEMA_CATALOG[model_name] == columns, (model_name, columns)
  52. def main() -> None:
  53. for node in GRAPH.nodes:
  54. if node.record.model_name in WIRE_MODELS:
  55. WIRE_MODELS[node.record.model_name].model_validate(node.record.payload)
  56. if node.record.model_name == "ScriptTaskContractV1":
  57. contract = ScriptTaskContractV1.from_payload(node.record.payload)
  58. if contract.task_kind.value in {"compose", "candidate-portfolio"}:
  59. contract.require_execution_ready()
  60. if node.record.model_name in PHASE_TWO_MODELS:
  61. hydrate_phase_two_artifact(node.record.payload)
  62. records = {node.record.model_name: node.record.payload for node in GRAPH.nodes}
  63. manifest = hydrate_root_delivery_manifest(records["RootDeliveryManifestV1"])
  64. structured = hydrate_phase_two_artifact(records["StructuredScriptArtifactV1"])
  65. canonical = canonicalize_legacy_projection(
  66. structured,
  67. direction=records["LegacyProjectionCanonicalV1"]["direction"],
  68. summary=records["LegacyProjectionCanonicalV1"]["summary"],
  69. )
  70. assert canonical.content_payload() == records["LegacyProjectionCanonicalV1"]
  71. assert manifest.legacy_projection_digest == canonical.canonical_sha256
  72. refs = records["RootDeliveryManifestV1"]
  73. assert manifest.input_closure_digest == root_delivery_input_closure_digest(
  74. direction_ref=refs["direction_ref"],
  75. direction_digest=ARTIFACT_DIGESTS[110],
  76. candidate_portfolio_ref=refs["candidate_portfolio_ref"],
  77. candidate_portfolio_digest=ARTIFACT_DIGESTS[150],
  78. structured_script_ref=refs["structured_script_ref"],
  79. structured_script_digest=ARTIFACT_DIGESTS[140],
  80. )
  81. snapshot_row = next(
  82. node.record.payload
  83. for node in GRAPH.nodes
  84. if node.record.model_name == "InputSnapshotRow"
  85. )
  86. hydrated_snapshot = _snapshot_from_row(snapshot_row)
  87. logical_snapshot = next(
  88. node.record.payload
  89. for node in GRAPH.nodes
  90. if node.record.model_name == "ScriptBuildInputSnapshotV1"
  91. )
  92. assert hydrated_snapshot.snapshot_id == logical_snapshot["snapshot_id"]
  93. assert hydrated_snapshot.canonical_sha256 == logical_snapshot["canonical_sha256"]
  94. _assert_table_fields("InputSnapshotRow", input_snapshot_table)
  95. _assert_table_fields("ArtifactVersionRow", artifact_version_table)
  96. _assert_table_fields("ParagraphRow", script_build_paragraph)
  97. _assert_table_fields("ElementRow", script_build_element)
  98. _assert_table_fields("ParagraphElementLinkRow", script_build_paragraph_element)
  99. print(f"validated {len(GRAPH.nodes)} fake nodes against live source contracts")
  100. if __name__ == "__main__":
  101. main()