feat(stage2): implement S2_40 finalizer
Add deterministic validation, closed rendering, review resume, and content-addressed commit while keeping live and legal admission pending.
This commit is contained in:
+1
-1
@@ -192,7 +192,7 @@ Agent:
|
|||||||
# literal placeholders in the offline parity mirror and its unit tests.
|
# literal placeholders in the offline parity mirror and its unit tests.
|
||||||
INLINE_USER_HASH = "{{__user_hash__}}"
|
INLINE_USER_HASH = "{{__user_hash__}}"
|
||||||
INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}"
|
INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}"
|
||||||
EXPECTED_STAGE2_RELEASE_SHA256 = "579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81"
|
EXPECTED_STAGE2_RELEASE_SHA256 = "9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53"
|
||||||
INLINE_REQUEST_PATH = "stage2_control/s2_00_request.json"
|
INLINE_REQUEST_PATH = "stage2_control/s2_00_request.json"
|
||||||
INLINE_STAGE2_ASSET_ROOT = "Default_Agent/Stage_2_Clean"
|
INLINE_STAGE2_ASSET_ROOT = "Default_Agent/Stage_2_Clean"
|
||||||
INLINE_STAGE2_RELEASE_PATH = (
|
INLINE_STAGE2_RELEASE_PATH = (
|
||||||
|
|||||||
+1
-1
@@ -71,7 +71,7 @@ Agent:
|
|||||||
|
|
||||||
WORKFLOW_ID = "S2_20"
|
WORKFLOW_ID = "S2_20"
|
||||||
ALGORITHM_VERSION = "s2_20_relief_plan/1.0.0"
|
ALGORITHM_VERSION = "s2_20_relief_plan/1.0.0"
|
||||||
EXPECTED_STAGE2_RELEASE_SHA256 = "579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81"
|
EXPECTED_STAGE2_RELEASE_SHA256 = "9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53"
|
||||||
LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
||||||
WEAVIATE_MCP_URL = "https://weaviate.eroomai.com/mcp"
|
WEAVIATE_MCP_URL = "https://weaviate.eroomai.com/mcp"
|
||||||
MCP_PROTOCOL_VERSION = "2025-03-26"
|
MCP_PROTOCOL_VERSION = "2025-03-26"
|
||||||
|
|||||||
+1
-1
@@ -73,7 +73,7 @@ Agent:
|
|||||||
from typing import Any, Mapping
|
from typing import Any, Mapping
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_STAGE2_RELEASE_SHA256 = "579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81"
|
EXPECTED_STAGE2_RELEASE_SHA256 = "9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53"
|
||||||
LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
||||||
MCP_PROTOCOL_VERSION = "2025-03-26"
|
MCP_PROTOCOL_VERSION = "2025-03-26"
|
||||||
INLINE_USER_HASH = "{{__user_hash__}}"
|
INLINE_USER_HASH = "{{__user_hash__}}"
|
||||||
|
|||||||
+3575
File diff suppressed because it is too large
Load Diff
+101
-20
@@ -9,7 +9,7 @@ stage_bindings:
|
|||||||
agent_script_ref:
|
agent_script_ref:
|
||||||
asset_id: AGENT-S2_00-INLINE
|
asset_id: AGENT-S2_00-INLINE
|
||||||
path: agent_scripts/Stage_2_S2_00.yml
|
path: agent_scripts/Stage_2_S2_00.yml
|
||||||
sha256: 8d9583ce7b039ef848abfcbcdb27ac50a2e8a4e279a745b4779bcd90fc17cf64
|
sha256: 94c2744d71d222cfb5cc1b2a0d33a6cda20079439823de431eef378a2fa5c943
|
||||||
schema_id: liti_agent_yaml.v1
|
schema_id: liti_agent_yaml.v1
|
||||||
binding_status: BOUND
|
binding_status: BOUND
|
||||||
workflow_contract_ref:
|
workflow_contract_ref:
|
||||||
@@ -21,18 +21,18 @@ stage_bindings:
|
|||||||
inline_code_receipt_ref:
|
inline_code_receipt_ref:
|
||||||
asset_id: RECEIPT-S2_00-INLINE-CODE
|
asset_id: RECEIPT-S2_00-INLINE-CODE
|
||||||
path: manifest/s2_00_inline_code_receipt.json
|
path: manifest/s2_00_inline_code_receipt.json
|
||||||
sha256: ced01ef57a2e5341b1f7ddbf810ff3c6fb85fbb3cf7cf80836328a8a971835b4
|
sha256: 36af21949e5ef53a3d39df8ea9491c64da43abcc4f5e42e13db9bb391da843b8
|
||||||
schema_id: stage2_s2_00_inline_code_receipt.v2
|
schema_id: stage2_s2_00_inline_code_receipt.v2
|
||||||
binding_status: BOUND
|
binding_status: BOUND
|
||||||
stage2_release_ref:
|
stage2_release_ref:
|
||||||
asset_id: RELEASE-STAGE2-CLEAN
|
asset_id: RELEASE-STAGE2-CLEAN
|
||||||
path: manifest/stage2_release.json
|
path: manifest/stage2_release.json
|
||||||
sha256: 579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81
|
sha256: 9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53
|
||||||
schema_id: stage2_release.v2
|
schema_id: stage2_release.v2
|
||||||
binding_status: BOUND
|
binding_status: BOUND
|
||||||
expected_release_sha256: 579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81
|
expected_release_sha256: 9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53
|
||||||
agent_script_sha256: 8d9583ce7b039ef848abfcbcdb27ac50a2e8a4e279a745b4779bcd90fc17cf64
|
agent_script_sha256: 94c2744d71d222cfb5cc1b2a0d33a6cda20079439823de431eef378a2fa5c943
|
||||||
canonical_code_sha256: 612bf08c26a2b96c827a774d79789a9902ffbf18fdd181a157dae40e25b5f8ca
|
canonical_code_sha256: e3f87725d5a6496189e7c411ef940dd8a7b3a9ff5b00535803bf98fa0cc4373e
|
||||||
mcp_server_id: code-executor
|
mcp_server_id: code-executor
|
||||||
tool_name: run_code
|
tool_name: run_code
|
||||||
language: python
|
language: python
|
||||||
@@ -73,7 +73,7 @@ stage_bindings:
|
|||||||
agent_path: agent_scripts/Stage_2_S2_10.yml
|
agent_path: agent_scripts/Stage_2_S2_10.yml
|
||||||
s2_10_agent_sha256: 122fe890efb340ebbac299afe058bf9ad0ed87d4d0ccf2456393e9e5796ec272
|
s2_10_agent_sha256: 122fe890efb340ebbac299afe058bf9ad0ed87d4d0ccf2456393e9e5796ec272
|
||||||
llm_binding_path: deployment/stage2_s2_10_llm_binding.yml
|
llm_binding_path: deployment/stage2_s2_10_llm_binding.yml
|
||||||
s2_10_llm_binding_sha256: 9d20b264e753c2f52e8d096edab363ace0fecf2ec8357605d1a9cb47c9c2b930
|
s2_10_llm_binding_sha256: 9b5d69f1316a0b9fba7b41097ee92142cf42485c8deb0be876d9e9329c317a04
|
||||||
owner_binding_status: HASH_BOUND_LIVE_ADMISSION_PENDING
|
owner_binding_status: HASH_BOUND_LIVE_ADMISSION_PENDING
|
||||||
s2_00_override_allowed: false
|
s2_00_override_allowed: false
|
||||||
live_admission_status: PENDING_SECRET_BINDING
|
live_admission_status: PENDING_SECRET_BINDING
|
||||||
@@ -86,7 +86,7 @@ stage_bindings:
|
|||||||
agent_script_ref:
|
agent_script_ref:
|
||||||
asset_id: AGENT-S2_20-INLINE
|
asset_id: AGENT-S2_20-INLINE
|
||||||
path: agent_scripts/Stage_2_S2_20.yml
|
path: agent_scripts/Stage_2_S2_20.yml
|
||||||
sha256: ac81d164309e5301083d0971e1736d128691fd9b18558ac4ade3f0246a43c2d7
|
sha256: 3c4e6a7404f5b6a6b2403824c3cd9e71db566d4f2865bd4de3e6fd9d07824bd6
|
||||||
schema_id: liti_agent_yaml.v1
|
schema_id: liti_agent_yaml.v1
|
||||||
binding_status: BOUND
|
binding_status: BOUND
|
||||||
workflow_contract_ref:
|
workflow_contract_ref:
|
||||||
@@ -98,18 +98,18 @@ stage_bindings:
|
|||||||
inline_code_receipt_ref:
|
inline_code_receipt_ref:
|
||||||
asset_id: RECEIPT-S2_20-INLINE-CODE
|
asset_id: RECEIPT-S2_20-INLINE-CODE
|
||||||
path: manifest/s2_20_inline_code_receipt.json
|
path: manifest/s2_20_inline_code_receipt.json
|
||||||
sha256: 60000d2412a757e9b2ab2525eedb08ebda20ec02a49f5b1b0c4e89e6a0aeca2b
|
sha256: 65e29f10f065cdd77281aa47acaf341edb38b0595aab0aa9f04bbee3a4718608
|
||||||
schema_id: stage2_s2_20_inline_code_receipt.v1
|
schema_id: stage2_s2_20_inline_code_receipt.v1
|
||||||
binding_status: BOUND
|
binding_status: BOUND
|
||||||
stage2_release_ref:
|
stage2_release_ref:
|
||||||
asset_id: RELEASE-STAGE2-CLEAN
|
asset_id: RELEASE-STAGE2-CLEAN
|
||||||
path: manifest/stage2_release.json
|
path: manifest/stage2_release.json
|
||||||
sha256: 579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81
|
sha256: 9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53
|
||||||
schema_id: stage2_release.v2
|
schema_id: stage2_release.v2
|
||||||
binding_status: BOUND
|
binding_status: BOUND
|
||||||
expected_release_sha256: 579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81
|
expected_release_sha256: 9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53
|
||||||
agent_script_sha256: ac81d164309e5301083d0971e1736d128691fd9b18558ac4ade3f0246a43c2d7
|
agent_script_sha256: 3c4e6a7404f5b6a6b2403824c3cd9e71db566d4f2865bd4de3e6fd9d07824bd6
|
||||||
canonical_code_sha256: eeb353b837cf5c2ecfb07fc7375eca03ac4e5c3df9254266d5690cbf14df7138
|
canonical_code_sha256: 6f1f503378242cf64cf8f0838af2164ab1103bc7031c8a6873f4ee89be66f7fd
|
||||||
mcp_server_id: code-executor
|
mcp_server_id: code-executor
|
||||||
tool_name: run_code
|
tool_name: run_code
|
||||||
language: python
|
language: python
|
||||||
@@ -229,6 +229,87 @@ stage_bindings:
|
|||||||
downstream_handoff_owner: null
|
downstream_handoff_owner: null
|
||||||
live_admission_status: PENDING_SECRET_BINDING
|
live_admission_status: PENDING_SECRET_BINDING
|
||||||
legacy_fallbacks: []
|
legacy_fallbacks: []
|
||||||
|
- stage_id: S2_40
|
||||||
|
binding_id: S2-BINDING-S2_40-CODE-EXECUTOR-V1
|
||||||
|
workflow_id: S2_40
|
||||||
|
execution_class: NON-LLM-DETERMINISTIC
|
||||||
|
active_runtime_authority: false
|
||||||
|
agent_script_ref:
|
||||||
|
asset_id: AGENT-S2_40-INLINE
|
||||||
|
path: agent_scripts/Stage_2_S2_40.yml
|
||||||
|
sha256: 5cbe2ac9aa73d95a5fa4e6cf71d7d8f0ad83aa05536f573c13e42505ef5d46f7
|
||||||
|
schema_id: liti_agent_yaml.v1
|
||||||
|
binding_status: BOUND
|
||||||
|
workflow_contract_ref:
|
||||||
|
asset_id: WF-S2_40
|
||||||
|
path: workflows/S2_40_final_review_render_and_commit.yml
|
||||||
|
sha256: ab1e5f593db41c1f26f9510c00d657221da53d08f011b2c4cf341a764a18d874
|
||||||
|
schema_id: stage2_s2_40_final_review_render_and_commit.v1
|
||||||
|
binding_status: BOUND
|
||||||
|
inline_code_receipt_ref:
|
||||||
|
asset_id: RECEIPT-S2_40-INLINE-CODE
|
||||||
|
path: manifest/s2_40_inline_code_receipt.json
|
||||||
|
sha256: 9bd0cfaee8d9e78480ab80a2fa541414c7ad0565439fa0f4661701fcdfde6526
|
||||||
|
schema_id: stage2_s2_40_inline_code_receipt.v1
|
||||||
|
binding_status: BOUND
|
||||||
|
stage2_release_ref:
|
||||||
|
asset_id: RELEASE-STAGE2-CLEAN
|
||||||
|
path: manifest/stage2_release.json
|
||||||
|
sha256: 9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53
|
||||||
|
schema_id: stage2_release.v2
|
||||||
|
binding_status: BOUND
|
||||||
|
expected_release_sha256: 9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53
|
||||||
|
agent_script_sha256: 5cbe2ac9aa73d95a5fa4e6cf71d7d8f0ad83aa05536f573c13e42505ef5d46f7
|
||||||
|
canonical_code_sha256: e521e1a65294a769cd6bf20edb159f8720c105b60cb769885aa416c8c763dcef
|
||||||
|
mcp_server_id: code-executor
|
||||||
|
tool_name: run_code
|
||||||
|
language: python
|
||||||
|
network: agent-network
|
||||||
|
timeout_seconds: 300
|
||||||
|
runtime_image_digest: PENDING_SEQUENTIAL_BIND
|
||||||
|
runtime_image_status: PENDING_BACKEND_EVIDENCE
|
||||||
|
dependency_lock:
|
||||||
|
requirements: httpx==0.28.1
|
||||||
|
requirements_sha256: 5fadf5f6ea5bd1b141ea05745cb52449bdccde939c22e76231e7993c2afc91d0
|
||||||
|
lock_status: LIVE_BACKEND_PENDING
|
||||||
|
localdocs_contract:
|
||||||
|
endpoint: http://mcp-localdocs:8012/mcp
|
||||||
|
user_id_template: '{{__user_hash__}}'
|
||||||
|
workspace_id_template: '{{__workspace_hash__}}'
|
||||||
|
tool_allowlist:
|
||||||
|
- read_binary_doc
|
||||||
|
- write_binary_file
|
||||||
|
fixed_request_path: stage2_control/s2_40_request.json
|
||||||
|
read_path_allowlist:
|
||||||
|
- stage2_control/s2_40_request.json
|
||||||
|
- Default_Agent/Stage_2_Clean/manifest/stage2_release.json
|
||||||
|
- Default_Agent/Stage_2_Clean/manifest/module_manifest.json
|
||||||
|
- Default_Agent/Stage_2_Clean/manifest/stage2_deterministic_admission_receipt.json
|
||||||
|
- Default_Agent/Stage_2_Clean/manifest/s2_40_inline_code_receipt.json
|
||||||
|
- Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_40.yml
|
||||||
|
- Default_Agent/Stage_2_Clean/deployment/stage2_code_executor_binding.yml
|
||||||
|
- Default_Agent/Stage_2_Clean/schemas/ingress.schema.json
|
||||||
|
- Default_Agent/Stage_2_Clean/schemas/context.schema.json
|
||||||
|
- Default_Agent/Stage_2_Clean/schemas/s2_10.schema.json
|
||||||
|
- Default_Agent/Stage_2_Clean/schemas/domain_verdict.schema.json
|
||||||
|
- Default_Agent/Stage_2_Clean/schemas/relief_plan.schema.json
|
||||||
|
- Default_Agent/Stage_2_Clean/schemas/binding_retrieval.schema.json
|
||||||
|
- Default_Agent/Stage_2_Clean/schemas/calculation.schema.json
|
||||||
|
- Default_Agent/Stage_2_Clean/schemas/draft_atoms.schema.json
|
||||||
|
- Default_Agent/Stage_2_Clean/schemas/review_status.schema.json
|
||||||
|
- Default_Agent/Stage_2_Clean/schemas/package.schema.json
|
||||||
|
- Default_Agent/Stage_2_Clean/registry/review/review_policy_registry.yml
|
||||||
|
- Default_Agent/Stage_2_Clean/renderers/<renderer_id>.yml
|
||||||
|
- stage2_runs/by-binding/<run_binding_digest>/<barrier-enumerated-input-path>
|
||||||
|
- stage2_runs/by-binding/<run_binding_digest>/review/receipts/<receipt_id>.json
|
||||||
|
- stage2_runs/by-binding/<run_binding_digest>/control/run_status.json
|
||||||
|
write_root_rule: stage2_runs/by-binding/<run_binding_digest>/
|
||||||
|
external_mcp_contract: null
|
||||||
|
egress_profile_id: S2_40_LOCALDOCS_ONLY_V1
|
||||||
|
egress_profile_status: PENDING_LIVE_VERIFICATION
|
||||||
|
downstream_handoff_owner: null
|
||||||
|
live_admission_status: PENDING_SECRET_BINDING
|
||||||
|
legacy_fallbacks: []
|
||||||
legacy_fallbacks: []
|
legacy_fallbacks: []
|
||||||
embedded_task_bindings:
|
embedded_task_bindings:
|
||||||
- execution_unit_id: S2_30::Task_S2_30_dispatch_planner
|
- execution_unit_id: S2_30::Task_S2_30_dispatch_planner
|
||||||
@@ -242,30 +323,30 @@ embedded_task_bindings:
|
|||||||
agent_script_ref:
|
agent_script_ref:
|
||||||
asset_id: AGENT-S2_30-INLINE
|
asset_id: AGENT-S2_30-INLINE
|
||||||
path: agent_scripts/Stage_2_S2_30.yml
|
path: agent_scripts/Stage_2_S2_30.yml
|
||||||
sha256: 6da21b319d10268e5f1959d085173d22017e4910c7222fd5e6daf72628629773
|
sha256: d0804526941207395c9b64a314b124ff8e919c6852e828d829dc45a9feb3ccdd
|
||||||
schema_id: liti_agent_yaml.v1
|
schema_id: liti_agent_yaml.v1
|
||||||
binding_status: BOUND
|
binding_status: BOUND
|
||||||
workflow_contract_ref:
|
workflow_contract_ref:
|
||||||
asset_id: WF-S2_30
|
asset_id: WF-S2_30
|
||||||
path: workflows/S2_30_claim_group_draft_map.yml
|
path: workflows/S2_30_claim_group_draft_map.yml
|
||||||
sha256: 498715993d796f82a8c9e790a62bee6885c05813488fd0a11200f46c07665162
|
sha256: b266e7b7ae8939b087bfdb7b34fff8b9c6011c835560ab690991d856ced0cc15
|
||||||
schema_id: stage2_s2_30_claim_group_draft_map.v1
|
schema_id: stage2_s2_30_claim_group_draft_map.v1
|
||||||
binding_status: BOUND
|
binding_status: BOUND
|
||||||
inline_code_receipt_ref:
|
inline_code_receipt_ref:
|
||||||
asset_id: RECEIPT-S2_30-INLINE-CODE
|
asset_id: RECEIPT-S2_30-INLINE-CODE
|
||||||
path: manifest/s2_30_inline_code_receipt.json
|
path: manifest/s2_30_inline_code_receipt.json
|
||||||
sha256: 78fb197d50ec706be35cf1766bb9e9fee1767a8e61109603313821dd6040f552
|
sha256: 8d1618771cf1f72aa3c6a5a4544fe6b6e949ccc7ecfef56f166561971beee896
|
||||||
schema_id: stage2_s2_30_inline_code_receipt.v1
|
schema_id: stage2_s2_30_inline_code_receipt.v1
|
||||||
binding_status: BOUND
|
binding_status: BOUND
|
||||||
stage2_release_ref:
|
stage2_release_ref:
|
||||||
asset_id: RELEASE-STAGE2-CLEAN
|
asset_id: RELEASE-STAGE2-CLEAN
|
||||||
path: manifest/stage2_release.json
|
path: manifest/stage2_release.json
|
||||||
sha256: 579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81
|
sha256: 9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53
|
||||||
schema_id: stage2_release.v2
|
schema_id: stage2_release.v2
|
||||||
binding_status: BOUND
|
binding_status: BOUND
|
||||||
expected_release_sha256: 579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81
|
expected_release_sha256: 9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53
|
||||||
agent_script_sha256: 6da21b319d10268e5f1959d085173d22017e4910c7222fd5e6daf72628629773
|
agent_script_sha256: d0804526941207395c9b64a314b124ff8e919c6852e828d829dc45a9feb3ccdd
|
||||||
canonical_code_sha256: 7814e64bfdbf5992d8009556b5a81268d99a0300d0b8f419c8c6384d90a906c9
|
canonical_code_sha256: 1f7f3b842afbd3f1034f094d6341288effc9804a03920ada4bf8cfd512464e4c
|
||||||
mcp_server_id: code-executor
|
mcp_server_id: code-executor
|
||||||
tool_name: run_code
|
tool_name: run_code
|
||||||
language: python
|
language: python
|
||||||
|
|||||||
+1
-1
@@ -51,7 +51,7 @@ prompt_contract:
|
|||||||
selected_context_wrapper_sha256: c43c88cad59f2e9c88d944d4997551063e133207398dee0633af53e2bc5bcab6
|
selected_context_wrapper_sha256: c43c88cad59f2e9c88d944d4997551063e133207398dee0633af53e2bc5bcab6
|
||||||
cluster_payload_wrapper_sha256: aaffb47bb21a975a13075901f3152d7a41b6e1661fa1626f42e678ba8abfdda8
|
cluster_payload_wrapper_sha256: aaffb47bb21a975a13075901f3152d7a41b6e1661fa1626f42e678ba8abfdda8
|
||||||
projection_receipt_path: manifest/s2_10_inline_prompt_projection_receipt.json
|
projection_receipt_path: manifest/s2_10_inline_prompt_projection_receipt.json
|
||||||
projection_receipt_sha256: 7f64a192e7edda7c0e4025b9f1995dcd21c38aba15a018d232f9f05bcba35863
|
projection_receipt_sha256: b7fdef2ad05f251bdede9473d3b69c0f169ab7f4a059b393429ff76f739f703e
|
||||||
canonical_source_paths:
|
canonical_source_paths:
|
||||||
- prompts/P00_system_and_safety_contract.md
|
- prompts/P00_system_and_safety_contract.md
|
||||||
- prompts/P10_legal_resolution_contract.md
|
- prompts/P10_legal_resolution_contract.md
|
||||||
|
|||||||
+11
-11
@@ -2,18 +2,18 @@ schema_version: stage2_s2_30_llm_binding.v1
|
|||||||
binding_status: PENDING_EXTERNAL_PLATFORM_BINDING
|
binding_status: PENDING_EXTERNAL_PLATFORM_BINDING
|
||||||
agent_ref:
|
agent_ref:
|
||||||
path: agent_scripts/Stage_2_S2_30.yml
|
path: agent_scripts/Stage_2_S2_30.yml
|
||||||
sha256: 6da21b319d10268e5f1959d085173d22017e4910c7222fd5e6daf72628629773
|
sha256: d0804526941207395c9b64a314b124ff8e919c6852e828d829dc45a9feb3ccdd
|
||||||
workflow_ref:
|
workflow_ref:
|
||||||
path: workflows/S2_30_claim_group_draft_map.yml
|
path: workflows/S2_30_claim_group_draft_map.yml
|
||||||
sha256: 498715993d796f82a8c9e790a62bee6885c05813488fd0a11200f46c07665162
|
sha256: b266e7b7ae8939b087bfdb7b34fff8b9c6011c835560ab690991d856ced0cc15
|
||||||
size_bytes: 12932
|
size_bytes: 15221
|
||||||
schema_ref:
|
schema_ref:
|
||||||
path: schemas/draft_atoms.schema.json
|
path: schemas/draft_atoms.schema.json
|
||||||
sha256: 6cbb81f8cc857173efda0ef3a95c3d39c64f057a64ab1927cf6a5967c592d9ce
|
sha256: 5107b64f01ffb3633a829c10652747760a5035c9cc07227c546e3f543005eda9
|
||||||
size_bytes: 57750
|
size_bytes: 68470
|
||||||
planner_ref:
|
planner_ref:
|
||||||
path: runtime/s2_30_dispatch_planner.py
|
path: runtime/s2_30_dispatch_planner.py
|
||||||
sha256: 7814e64bfdbf5992d8009556b5a81268d99a0300d0b8f419c8c6384d90a906c9
|
sha256: 1f7f3b842afbd3f1034f094d6341288effc9804a03920ada4bf8cfd512464e4c
|
||||||
model:
|
model:
|
||||||
provider: openai
|
provider: openai
|
||||||
model_id: gpt-5.6-sol
|
model_id: gpt-5.6-sol
|
||||||
@@ -30,10 +30,10 @@ prompt_contract:
|
|||||||
- S30
|
- S30
|
||||||
inline_common_prompt_sha256: a90ac95f0b24ea938a16699f34ef7f17eb870edcbd8964ba4f6709e36e1bbcc0
|
inline_common_prompt_sha256: a90ac95f0b24ea938a16699f34ef7f17eb870edcbd8964ba4f6709e36e1bbcc0
|
||||||
inline_static_prompt_sha256: 577efa2c3334298ec60188b9f0066f834e072dc99f785b3c6ab4d05a47fac18a
|
inline_static_prompt_sha256: 577efa2c3334298ec60188b9f0066f834e072dc99f785b3c6ab4d05a47fac18a
|
||||||
projection_receipt_sha256: 2ea4d273eb3aa23550159dc4f821e1c3863fc457a48ed429a7c9522bfa293eac
|
projection_receipt_sha256: 1aa650d3c389ba27ce43e514295c7c2ce629583dc96220618fd97dea399d0757
|
||||||
runtime_external_prompt_read_allowed: false
|
runtime_external_prompt_read_allowed: false
|
||||||
planner_contract:
|
planner_contract:
|
||||||
inline_code_receipt_sha256: 78fb197d50ec706be35cf1766bb9e9fee1767a8e61109603313821dd6040f552
|
inline_code_receipt_sha256: 8d1618771cf1f72aa3c6a5a4544fe6b6e949ccc7ecfef56f166561971beee896
|
||||||
legal_reasoning_allowed: false
|
legal_reasoning_allowed: false
|
||||||
persistent_write_allowed: false
|
persistent_write_allowed: false
|
||||||
native_adapter:
|
native_adapter:
|
||||||
@@ -55,14 +55,14 @@ native_adapter:
|
|||||||
producer_contract:
|
producer_contract:
|
||||||
material:
|
material:
|
||||||
algorithm_id: S2_30-PRODUCER-CONTRACT-PROJECTION-V1
|
algorithm_id: S2_30-PRODUCER-CONTRACT-PROJECTION-V1
|
||||||
raw_model_output_schema_sha256: 6cbb81f8cc857173efda0ef3a95c3d39c64f057a64ab1927cf6a5967c592d9ce
|
raw_model_output_schema_sha256: 5107b64f01ffb3633a829c10652747760a5035c9cc07227c546e3f543005eda9
|
||||||
strict_validator_sha256: 30dff7cc1717c792a6da9bcbd3533c82c9bf3902f09fc9fe92a086c2e13694c6
|
strict_validator_sha256: 14e4eefb73d40d004c95017c82cdc23ac1c1c0a54ea34562dcf322f6364ae27a
|
||||||
strict_validator_algorithm_id: S2_30-OFFLINE-NATIVE-ADAPTER-ORACLE-V1
|
strict_validator_algorithm_id: S2_30-OFFLINE-NATIVE-ADAPTER-ORACLE-V1
|
||||||
atom_id_algorithm_id: S2_30-DETERMINISTIC-ATOM-ID-V1
|
atom_id_algorithm_id: S2_30-DETERMINISTIC-ATOM-ID-V1
|
||||||
two_pass_rewrite_algorithm_id: S2_30-TWO-PASS-LOCAL-REF-REWRITE-V1
|
two_pass_rewrite_algorithm_id: S2_30-TWO-PASS-LOCAL-REF-REWRITE-V1
|
||||||
context_materializer_algorithm_id: S2_30-EXACT-REF-MATERIALIZER-V1
|
context_materializer_algorithm_id: S2_30-EXACT-REF-MATERIALIZER-V1
|
||||||
native_capability_projection_sha256: b284d20dc950db8b8fe015578d10657beda479a55665d34d77319d4f81fa4a68
|
native_capability_projection_sha256: b284d20dc950db8b8fe015578d10657beda479a55665d34d77319d4f81fa4a68
|
||||||
s2_30_producer_contract_digest: 337fd978acb335e89ef3ee859bf2333403858b370cbc5466d601317ba027fe4a
|
s2_30_producer_contract_digest: b0d7e781e8a59ebd9e9ed8db6d04973640b2a127440f32b7bff605103cbc23e0
|
||||||
whole_binding_hash_in_material: false
|
whole_binding_hash_in_material: false
|
||||||
parent_or_child_hash_in_material: false
|
parent_or_child_hash_in_material: false
|
||||||
self_referential_digest_fields_excluded: true
|
self_referential_digest_fields_excluded: true
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
|||||||
{"authoring":{"path":"Stage_2_S2_00_v.2.yml","sha256":"8d9583ce7b039ef848abfcbcdb27ac50a2e8a4e279a745b4779bcd90fc17cf64","size_bytes":367573,"unique_key_parse":"PASS"},"authoring_rewritten":false,"build_kind":"OFFLINE_AUTHORING_PROJECTION","canonical_code":{"ast_status":"PASS","code_sha256":"612bf08c26a2b96c827a774d79789a9902ffbf18fdd181a157dae40e25b5f8ca","code_size_bytes":283196,"compile_status":"PASS","encoding":"UTF-8","external_python_source_ref_count":0,"external_url_count":0,"extraction_transform":"NONE","forbidden_dynamic_call_count":0,"forbidden_import_count":0,"imports":["__future__","argparse","base64","binascii","collections","contextlib","dataclasses","hashlib","httpx","io","itertools","json","math","os","pathlib","re","shutil","stat","sys","tempfile","typing","unicodedata"],"placeholder_count":0,"plaintext_secret_count":0,"yaml_pointer":"/Agent/Stages/0/tasks/0/parameters/code"},"deployment_projection":{"byte_identical_to_authoring":true,"canonical_task_semantics_sha256":"0ae62c60183e0cffbef198d8efce4a0fcbcaddef223133075b19ee19c50ea65d","path":"Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_00.yml","sha256":"8d9583ce7b039ef848abfcbcdb27ac50a2e8a4e279a745b4779bcd90fc17cf64","size_bytes":367573},"full_code_mirrors":[{"byte_identical_to_canonical_code":true,"path":"Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.py","sha256":"612bf08c26a2b96c827a774d79789a9902ffbf18fdd181a157dae40e25b5f8ca","size_bytes":283196},{"byte_identical_to_canonical_code":true,"path":"Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.txt","sha256":"612bf08c26a2b96c827a774d79789a9902ffbf18fdd181a157dae40e25b5f8ca","size_bytes":283196}],"parity_status":"PASS","schema_version":"stage2_s2_00_inline_code_receipt.v2","source_of_truth":"Stage_2_S2_00_v.2.yml","task_contract":{"agent":{"name":"Stage_2_S2_00_v2","version":"1.2.0"},"mcp_servers":{"code-executor":{"type":"streamable-http","url":"https://code-executor.mcp.eroomai.com/mcp"},"localdocs":{"type":"streamable-http","url":"http://mcp-localdocs:8012/mcp"}},"stage":{"name":"S2_00","nexts":[],"prevs":[]},"task":{"code_sha256":"612bf08c26a2b96c827a774d79789a9902ffbf18fdd181a157dae40e25b5f8ca","mcp":"code-executor","parameters":{"language":"python","network":"agent-network","requirements":"httpx==0.28.1","timeout":300},"task_name":"Task_S2_00_deterministic_ingress","tool_name":"run_code"},"task_procedure":{"IN":{"nexts":["Task_S2_00_deterministic_ingress"],"wait_until":[]},"OUT":{"nexts":[],"wait_until":["Task_S2_00_deterministic_ingress"]},"Task_S2_00_deterministic_ingress":{"nexts":["OUT"],"wait_until":["IN"]}}},"workflow_id":"S2_00"}
|
{"authoring":{"path":"Stage_2_S2_00_v.2.yml","sha256":"94c2744d71d222cfb5cc1b2a0d33a6cda20079439823de431eef378a2fa5c943","size_bytes":367573,"unique_key_parse":"PASS"},"authoring_rewritten":false,"build_kind":"OFFLINE_AUTHORING_PROJECTION","canonical_code":{"ast_status":"PASS","code_sha256":"e3f87725d5a6496189e7c411ef940dd8a7b3a9ff5b00535803bf98fa0cc4373e","code_size_bytes":283196,"compile_status":"PASS","encoding":"UTF-8","external_python_source_ref_count":0,"external_url_count":0,"extraction_transform":"NONE","forbidden_dynamic_call_count":0,"forbidden_import_count":0,"imports":["__future__","argparse","base64","binascii","collections","contextlib","dataclasses","hashlib","httpx","io","itertools","json","math","os","pathlib","re","shutil","stat","sys","tempfile","typing","unicodedata"],"placeholder_count":0,"plaintext_secret_count":0,"yaml_pointer":"/Agent/Stages/0/tasks/0/parameters/code"},"deployment_projection":{"byte_identical_to_authoring":true,"canonical_task_semantics_sha256":"a970635aa19b4ebca03819e30de32bb69f33ca455f7ecd6257a45636565b4e95","path":"Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_00.yml","sha256":"94c2744d71d222cfb5cc1b2a0d33a6cda20079439823de431eef378a2fa5c943","size_bytes":367573},"full_code_mirrors":[{"byte_identical_to_canonical_code":true,"path":"Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.py","sha256":"e3f87725d5a6496189e7c411ef940dd8a7b3a9ff5b00535803bf98fa0cc4373e","size_bytes":283196},{"byte_identical_to_canonical_code":true,"path":"Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.txt","sha256":"e3f87725d5a6496189e7c411ef940dd8a7b3a9ff5b00535803bf98fa0cc4373e","size_bytes":283196}],"parity_status":"PASS","schema_version":"stage2_s2_00_inline_code_receipt.v2","source_of_truth":"Stage_2_S2_00_v.2.yml","task_contract":{"agent":{"name":"Stage_2_S2_00_v2","version":"1.2.0"},"mcp_servers":{"code-executor":{"type":"streamable-http","url":"https://code-executor.mcp.eroomai.com/mcp"},"localdocs":{"type":"streamable-http","url":"http://mcp-localdocs:8012/mcp"}},"stage":{"name":"S2_00","nexts":[],"prevs":[]},"task":{"code_sha256":"e3f87725d5a6496189e7c411ef940dd8a7b3a9ff5b00535803bf98fa0cc4373e","mcp":"code-executor","parameters":{"language":"python","network":"agent-network","requirements":"httpx==0.28.1","timeout":300},"task_name":"Task_S2_00_deterministic_ingress","tool_name":"run_code"},"task_procedure":{"IN":{"nexts":["Task_S2_00_deterministic_ingress"],"wait_until":[]},"OUT":{"nexts":[],"wait_until":["Task_S2_00_deterministic_ingress"]},"Task_S2_00_deterministic_ingress":{"nexts":["OUT"],"wait_until":["IN"]}}},"workflow_id":"S2_00"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"agent_contract":{"agent_name":"Stage_2_S2_10","agent_version":"1.1.0","deterministic_task_count":0,"inline_common_prompt_sha256":"a90ac95f0b24ea938a16699f34ef7f17eb870edcbd8964ba4f6709e36e1bbcc0","inline_static_prompt_sha256":"894c30bedaa5f230aed117e81ef4cc06cac832928a1e29cd3ee8804cfd4dba1f","item_tokens":["{{item.selected_legal_context_json}}","{{item.cluster_case_payload_json}}"],"llm_task_count":1,"prompt_roles":["system","system","system","user"],"reduce_task_count":0,"stage_name":"S2_10","task_name":"Task_S2_10_resolve_cluster","tool_task_count":0},"authoring":{"path":"Stage_2_S2_10_v.1.yml","sha256":"122fe890efb340ebbac299afe058bf9ad0ed87d4d0ccf2456393e9e5796ec272","size_bytes":30984},"binding":{"path":"deployment/stage2_s2_10_llm_binding.yml","sha256":"9d20b264e753c2f52e8d096edab363ace0fecf2ec8357605d1a9cb47c9c2b930"},"child_release":{"path":"manifest/s2_10_release.json","release_digest":"262579cc93e89d1a90cd928bf24f21e820022226493ad0a05ae4dc44bef7debd","sha256":"73cf98c94117db12f04c298e5758431d5006c7a9ddb8dd46ff2e7b19313bdb9c"},"deployment":{"byte_parity":"PASS","path":"agent_scripts/Stage_2_S2_10.yml","sha256":"122fe890efb340ebbac299afe058bf9ad0ed87d4d0ccf2456393e9e5796ec272","size_bytes":30984},"external_admission_status":"PENDING","inline_prompt_receipt":{"path":"manifest/s2_10_inline_prompt_projection_receipt.json","sha256":"7f64a192e7edda7c0e4025b9f1995dcd21c38aba15a018d232f9f05bcba35863","size_bytes":2659},"receipt_digest":"32f7ba8b4fbe0502f9a44c6f13059ce2aba9855bb350c72ca22ba936cb552b60","receipt_status":"OFFLINE_AGENT_PROJECTION_VERIFIED","runtime_admission_effect":"NONE","schema":{"path":"schemas/s2_10.schema.json","sha256":"5233afde9ddb3c5868b9101816e8b3830a90b1399e3e5e89d9d7c7bf5beacaee","size_bytes":82703},"schema_version":"stage2_s2_10_agent_receipt.v2","workflow":{"path":"workflows/S2_10_domain_relief_resolution_map.yml","sha256":"f0fba48f1760cf4e233d7d698fd19090f96ffb89cabe4b2e2cae9af7c616be0f","size_bytes":15320}}
|
{"agent_contract":{"agent_name":"Stage_2_S2_10","agent_version":"1.1.0","deterministic_task_count":0,"inline_common_prompt_sha256":"a90ac95f0b24ea938a16699f34ef7f17eb870edcbd8964ba4f6709e36e1bbcc0","inline_static_prompt_sha256":"894c30bedaa5f230aed117e81ef4cc06cac832928a1e29cd3ee8804cfd4dba1f","item_tokens":["{{item.selected_legal_context_json}}","{{item.cluster_case_payload_json}}"],"llm_task_count":1,"prompt_roles":["system","system","system","user"],"reduce_task_count":0,"stage_name":"S2_10","task_name":"Task_S2_10_resolve_cluster","tool_task_count":0},"authoring":{"path":"Stage_2_S2_10_v.1.yml","sha256":"122fe890efb340ebbac299afe058bf9ad0ed87d4d0ccf2456393e9e5796ec272","size_bytes":30984},"binding":{"path":"deployment/stage2_s2_10_llm_binding.yml","sha256":"9b5d69f1316a0b9fba7b41097ee92142cf42485c8deb0be876d9e9329c317a04"},"child_release":{"path":"manifest/s2_10_release.json","release_digest":"8cfc30a40ad53b35fb44c56e3c53f86605f777622399a4b1dcd0b2082eb6e197","sha256":"cb49a4501116e46d935933b739b6679b11d961ff8e36abefea35dab6b25508fb"},"deployment":{"byte_parity":"PASS","path":"agent_scripts/Stage_2_S2_10.yml","sha256":"122fe890efb340ebbac299afe058bf9ad0ed87d4d0ccf2456393e9e5796ec272","size_bytes":30984},"external_admission_status":"PENDING","inline_prompt_receipt":{"path":"manifest/s2_10_inline_prompt_projection_receipt.json","sha256":"b7fdef2ad05f251bdede9473d3b69c0f169ab7f4a059b393429ff76f739f703e","size_bytes":2659},"receipt_digest":"9bd92db101c0bf6f0c48764c71f5091e573dc65cce307ce3121b5d0271efc36c","receipt_status":"OFFLINE_AGENT_PROJECTION_VERIFIED","runtime_admission_effect":"NONE","schema":{"path":"schemas/s2_10.schema.json","sha256":"5233afde9ddb3c5868b9101816e8b3830a90b1399e3e5e89d9d7c7bf5beacaee","size_bytes":82703},"schema_version":"stage2_s2_10_agent_receipt.v2","workflow":{"path":"workflows/S2_10_domain_relief_resolution_map.yml","sha256":"f0fba48f1760cf4e233d7d698fd19090f96ffb89cabe4b2e2cae9af7c616be0f","size_bytes":15320}}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"authoring_agent":{"path":"Stage_2_S2_10_v.1.yml","sha256":"122fe890efb340ebbac299afe058bf9ad0ed87d4d0ccf2456393e9e5796ec272","size_bytes":30984},"builder":{"path":"offline_build/build_s2_10_agent_projection.py","sha256":"4359364816c8a58eb414bafae8ccf24fc3766dc611d15f1aa09e48c8fedc8f88","size_bytes":61930},"canonicalization_algorithm_id":"S2_10-NFC-LF-RTRIM-CLAUSE-PROJECTION-V1","deployment_agent":{"byte_parity":"PASS","path":"agent_scripts/Stage_2_S2_10.yml","sha256":"122fe890efb340ebbac299afe058bf9ad0ed87d4d0ccf2456393e9e5796ec272","size_bytes":30984},"inline_scalars":[{"raw_scalar_sha256":"a90ac95f0b24ea938a16699f34ef7f17eb870edcbd8964ba4f6709e36e1bbcc0","role":"system","size_bytes":7364,"wrapper":"stage_2_common_cache_prefix","yaml_pointer":"/Agent/Stages/0/map_reduce/map/tasks/0/prompts/0/content"},{"raw_scalar_sha256":"894c30bedaa5f230aed117e81ef4cc06cac832928a1e29cd3ee8804cfd4dba1f","role":"system","size_bytes":12261,"wrapper":"s2_10_legal_resolution_static_prompt","yaml_pointer":"/Agent/Stages/0/map_reduce/map/tasks/0/prompts/1/content"}],"message_order":["INLINE_COMMON_SYSTEM","INLINE_STATIC_LEGAL_SYSTEM","SELECTED_CONTEXT_SYSTEM_DATA","CLUSTER_CASE_USER_DATA"],"raw_scalar_hash_algorithm_id":"SHA256-UTF8-PARSED-YAML-SCALAR-V1","receipt_digest":"6c5e9504d5921ee552023d05bcd61ed9752c3ac02ab93f0b75ed360390b85313","receipt_status":"OFFLINE_PROJECTION_VERIFIED","runtime_admission_effect":"NONE","schema_version":"stage2_s2_10_inline_prompt_projection_receipt.v1","source_projections":[{"inline_clause_projection_sha256":"c82cfc0b61adb1af7759fd321f78bcf7bebf7e5d2fc3a9c01533a80736c6d80e","inline_wrapper":"stage_2_common_cache_prefix","parity":"PASS","source":{"path":"prompts/P00_system_and_safety_contract.md","sha256":"c82cfc0b61adb1af7759fd321f78bcf7bebf7e5d2fc3a9c01533a80736c6d80e","size_bytes":7304},"source_clause_projection_sha256":"c82cfc0b61adb1af7759fd321f78bcf7bebf7e5d2fc3a9c01533a80736c6d80e"},{"hybrid_supersession_required":true,"inline_clause_projection_sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b","inline_wrapper":"s2_10_legal_resolution_static_prompt","parity":"PASS","source":{"path":"prompts/P10_legal_resolution_contract.md","sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b","size_bytes":8712},"source_clause_projection_sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b"}],"validation":{"dynamic_item_tokens":["{{item.selected_legal_context_json}}","{{item.cluster_case_payload_json}}"],"normalized_clause_projection":"PASS","prompt_order":"PASS","static_item_token_count":0,"static_pii_scan":"PASS","wrapper_integrity":"PASS"}}
|
{"authoring_agent":{"path":"Stage_2_S2_10_v.1.yml","sha256":"122fe890efb340ebbac299afe058bf9ad0ed87d4d0ccf2456393e9e5796ec272","size_bytes":30984},"builder":{"path":"offline_build/build_s2_10_agent_projection.py","sha256":"efdde6d70c723e9386f2ba1828119af7e68f7956c29bd3ba05370242a70ab62d","size_bytes":68381},"canonicalization_algorithm_id":"S2_10-NFC-LF-RTRIM-CLAUSE-PROJECTION-V1","deployment_agent":{"byte_parity":"PASS","path":"agent_scripts/Stage_2_S2_10.yml","sha256":"122fe890efb340ebbac299afe058bf9ad0ed87d4d0ccf2456393e9e5796ec272","size_bytes":30984},"inline_scalars":[{"raw_scalar_sha256":"a90ac95f0b24ea938a16699f34ef7f17eb870edcbd8964ba4f6709e36e1bbcc0","role":"system","size_bytes":7364,"wrapper":"stage_2_common_cache_prefix","yaml_pointer":"/Agent/Stages/0/map_reduce/map/tasks/0/prompts/0/content"},{"raw_scalar_sha256":"894c30bedaa5f230aed117e81ef4cc06cac832928a1e29cd3ee8804cfd4dba1f","role":"system","size_bytes":12261,"wrapper":"s2_10_legal_resolution_static_prompt","yaml_pointer":"/Agent/Stages/0/map_reduce/map/tasks/0/prompts/1/content"}],"message_order":["INLINE_COMMON_SYSTEM","INLINE_STATIC_LEGAL_SYSTEM","SELECTED_CONTEXT_SYSTEM_DATA","CLUSTER_CASE_USER_DATA"],"raw_scalar_hash_algorithm_id":"SHA256-UTF8-PARSED-YAML-SCALAR-V1","receipt_digest":"096cedfb69f915cef492cc37b78f8605386a091c47da9b51c9a499859c2edb00","receipt_status":"OFFLINE_PROJECTION_VERIFIED","runtime_admission_effect":"NONE","schema_version":"stage2_s2_10_inline_prompt_projection_receipt.v1","source_projections":[{"inline_clause_projection_sha256":"c82cfc0b61adb1af7759fd321f78bcf7bebf7e5d2fc3a9c01533a80736c6d80e","inline_wrapper":"stage_2_common_cache_prefix","parity":"PASS","source":{"path":"prompts/P00_system_and_safety_contract.md","sha256":"c82cfc0b61adb1af7759fd321f78bcf7bebf7e5d2fc3a9c01533a80736c6d80e","size_bytes":7304},"source_clause_projection_sha256":"c82cfc0b61adb1af7759fd321f78bcf7bebf7e5d2fc3a9c01533a80736c6d80e"},{"hybrid_supersession_required":true,"inline_clause_projection_sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b","inline_wrapper":"s2_10_legal_resolution_static_prompt","parity":"PASS","source":{"path":"prompts/P10_legal_resolution_contract.md","sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b","size_bytes":8712},"source_clause_projection_sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b"}],"validation":{"dynamic_item_tokens":["{{item.selected_legal_context_json}}","{{item.cluster_case_payload_json}}"],"normalized_clause_projection":"PASS","prompt_order":"PASS","static_item_token_count":0,"static_pii_scan":"PASS","wrapper_integrity":"PASS"}}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"binding_sha256":"9d20b264e753c2f52e8d096edab363ace0fecf2ec8357605d1a9cb47c9c2b930","finding_ids":[],"inline_prompt_projection_sha256":"7f64a192e7edda7c0e4025b9f1995dcd21c38aba15a018d232f9f05bcba35863","receipt_digest":"ca74b98b8da5252ecb063f561d0aba62013d259a4c8cfe1fd1cf77401771f38a","review_id":"S2_10-HYBRID-LEGAL-REVIEW-PENDING","review_scope_digest":"b9396e8b25be70be35032de9f621f1ac9329a419f868f31619f5ae871a28f8e1","reviewer_role":"KOREAN_ATTORNEY","s2_10_release_sha256":"73cf98c94117db12f04c298e5758431d5006c7a9ddb8dd46ff2e7b19313bdb9c","schema_version":"stage2_s2_10_legal_review_receipt.v2","signature_ref":null,"signed_at":null,"signed_by":null,"status":"PENDING_KOREAN_LAWYER_REVIEW"}
|
{"binding_sha256":"9b5d69f1316a0b9fba7b41097ee92142cf42485c8deb0be876d9e9329c317a04","finding_ids":[],"inline_prompt_projection_sha256":"b7fdef2ad05f251bdede9473d3b69c0f169ab7f4a059b393429ff76f739f703e","receipt_digest":"18a29c299f5587993c17f5a40b1d2c60d03556e4b37306c23352d671c4dcbb7c","review_id":"S2_10-HYBRID-LEGAL-REVIEW-PENDING","review_scope_digest":"c8675e22ff50ba9599ab3ac3108f13718b412fc0de2425133f3f49981edf0735","reviewer_role":"KOREAN_ATTORNEY","s2_10_release_sha256":"cb49a4501116e46d935933b739b6679b11d961ff8e36abefea35dab6b25508fb","schema_version":"stage2_s2_10_legal_review_receipt.v2","signature_ref":null,"signed_at":null,"signed_by":null,"status":"PENDING_KOREAN_LAWYER_REVIEW"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"benchmark_id":"S2_10-HYBRID-MODEL-BENCHMARK-PENDING","binding_sha256":"9d20b264e753c2f52e8d096edab363ace0fecf2ec8357605d1a9cb47c9c2b930","cache_telemetry_observed":null,"endpoint":"responses","latency_p95_ms":null,"model":"gpt-5.6-sol","observed_fixture_refs":[],"reasoning_effort":"xhigh","receipt_digest":"ab4b1993b19b61d5de82756dfeb8eb0d853f50e09e7325624da5292c043149b8","s2_10_release_sha256":"73cf98c94117db12f04c298e5758431d5006c7a9ddb8dd46ff2e7b19313bdb9c","schema_pass_rate":null,"schema_version":"stage2_s2_10_model_benchmark_receipt.v2","signature_ref":null,"signed_at":null,"signed_by":null,"status":"PENDING_MODEL_BENCHMARK","usage_telemetry_observed":null,"verbosity":"medium"}
|
{"benchmark_id":"S2_10-HYBRID-MODEL-BENCHMARK-PENDING","binding_sha256":"9b5d69f1316a0b9fba7b41097ee92142cf42485c8deb0be876d9e9329c317a04","cache_telemetry_observed":null,"endpoint":"responses","latency_p95_ms":null,"model":"gpt-5.6-sol","observed_fixture_refs":[],"reasoning_effort":"xhigh","receipt_digest":"61f34770c1ebbc4c84c53ea925b07416b92f4baf4f1740294423eacbc17895ce","s2_10_release_sha256":"cb49a4501116e46d935933b739b6679b11d961ff8e36abefea35dab6b25508fb","schema_pass_rate":null,"schema_version":"stage2_s2_10_model_benchmark_receipt.v2","signature_ref":null,"signed_at":null,"signed_by":null,"status":"PENDING_MODEL_BENCHMARK","usage_telemetry_observed":null,"verbosity":"medium"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"adapter_contract_version":"S2_10_NATIVE_RESULT_ADAPTER_V2","binding_sha256":"9d20b264e753c2f52e8d096edab363ace0fecf2ec8357605d1a9cb47c9c2b930","capabilities":[{"activation_binding_ref":null,"capability_id":"HOST_ATOMIC_SINGLE_FLIGHT_CAS_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"AGENTBACKEND_MAP_SOURCE_ITEMS_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"AGENTBACKEND_NO_REDUCE_RESULT_ADAPTER_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"S2_10_STRICT_SCHEMA_AND_REF_VALIDATOR_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"S2_10_DETERMINISTIC_ID_AND_IMMUTABLE_PERSIST_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"S2_10_ITEM_RETRY_CONTROLLER_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"}],"overall_status":"PENDING_EXTERNAL_PLATFORM_BINDING","parent_stage2_release_sha256":"579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81","receipt_digest":"d9d5aa86ba1f50a7ad6536c6907ab8faffee6f72a2f0f65f8422c8390a23c8d9","receipt_id":"S2_10-HYBRID-PLATFORM-ADAPTER-PENDING","s2_10_release_sha256":"73cf98c94117db12f04c298e5758431d5006c7a9ddb8dd46ff2e7b19313bdb9c","schema_version":"stage2_s2_10_platform_adapter_receipt.v2","signature_ref":null,"signed_at":null,"signed_by":null}
|
{"adapter_contract_version":"S2_10_NATIVE_RESULT_ADAPTER_V2","binding_sha256":"9b5d69f1316a0b9fba7b41097ee92142cf42485c8deb0be876d9e9329c317a04","capabilities":[{"activation_binding_ref":null,"capability_id":"HOST_ATOMIC_SINGLE_FLIGHT_CAS_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"AGENTBACKEND_MAP_SOURCE_ITEMS_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"AGENTBACKEND_NO_REDUCE_RESULT_ADAPTER_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"S2_10_STRICT_SCHEMA_AND_REF_VALIDATOR_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"S2_10_DETERMINISTIC_ID_AND_IMMUTABLE_PERSIST_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"S2_10_ITEM_RETRY_CONTROLLER_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"}],"overall_status":"PENDING_EXTERNAL_PLATFORM_BINDING","parent_stage2_release_sha256":"9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53","receipt_digest":"3c3a005c2ff658a53c37631e82e5dc536e038bd357cacaaefbd37453b2a99245","receipt_id":"S2_10-HYBRID-PLATFORM-ADAPTER-PENDING","s2_10_release_sha256":"cb49a4501116e46d935933b739b6679b11d961ff8e36abefea35dab6b25508fb","schema_version":"stage2_s2_10_platform_adapter_receipt.v2","signature_ref":null,"signed_at":null,"signed_by":null}
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
|||||||
{"authoring":{"path":"Stage_2_S2_20.yml","sha256":"ac81d164309e5301083d0971e1736d128691fd9b18558ac4ade3f0246a43c2d7","size_bytes":254756},"canonical_code":{"ast_status":"PASS","code_sha256":"eeb353b837cf5c2ecfb07fc7375eca03ac4e5c3df9254266d5690cbf14df7138","code_size_bytes":200269,"compile_status":"PASS","encoding":"UTF-8","endpoint_literals":["http://mcp-localdocs:8012/mcp","https://weaviate.eroomai.com/mcp"],"expected_parent_stage2_release_sha256":"579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81","external_python_source_ref_count":0,"extraction_transform":"NONE","forbidden_dynamic_call_count":0,"forbidden_import_count":0,"imports":["__future__","base64","binascii","concurrent","contextlib","datetime","decimal","hashlib","httpx","io","itertools","json","pathlib","re","sys","typing","unicodedata"],"plaintext_secret_count":0,"yaml_pointer":"/Agent/Stages/0/tasks/0/parameters/code"},"deployment_projection":{"path":"Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_20.yml","sha256":"ac81d164309e5301083d0971e1736d128691fd9b18558ac4ade3f0246a43c2d7","size_bytes":254756},"full_code_mirrors":["Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.py","Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.txt"],"parity_status":"PASS","schema_version":"stage2_s2_20_inline_code_receipt.v1","task_contract":{"agent":{"name":"Stage_2_S2_20","version":"1.0.0"},"authoring_rewritten":false,"canonical_task_semantics_sha256":"460bd442ece9b2e39a45d1df5fd333db39d5267da49bc1a63192a580b90e7c0f","code_mirrors_byte_identical":true,"exactly_one_code_executor_run_code":true,"exactly_one_stage":true,"exactly_one_task":true,"expected_parent_stage2_release_sha256":"579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81","internal_dag":"(C20||C21)->C25->C26->(C22||(C27->C28->C29))->C30->C35","mcp_servers":{"code-executor":{"type":"streamable-http","url":"https://code-executor.mcp.eroomai.com/mcp"},"localdocs":{"type":"streamable-http","url":"http://mcp-localdocs:8012/mcp"}},"projection_byte_identical_to_authoring":true,"stage":{"name":"S2_20","nexts":[],"prevs":[],"skip_confirm":true},"task":{"code_sha256":"eeb353b837cf5c2ecfb07fc7375eca03ac4e5c3df9254266d5690cbf14df7138","mcp":"code-executor","parameters":{"language":"python","network":"agent-network","requirements":"httpx==0.28.1","timeout":300},"task_name":"Task_S2_20_deterministic_relief_plan","tool_name":"run_code"},"task_procedure":{"IN":{"nexts":["Task_S2_20_deterministic_relief_plan"],"wait_until":[]},"OUT":{"nexts":[],"wait_until":["Task_S2_20_deterministic_relief_plan"]},"Task_S2_20_deterministic_relief_plan":{"nexts":["OUT"],"wait_until":["IN"]}}},"workflow_id":"S2_20"}
|
{"authoring":{"path":"Stage_2_S2_20.yml","sha256":"3c4e6a7404f5b6a6b2403824c3cd9e71db566d4f2865bd4de3e6fd9d07824bd6","size_bytes":254756},"canonical_code":{"ast_status":"PASS","code_sha256":"6f1f503378242cf64cf8f0838af2164ab1103bc7031c8a6873f4ee89be66f7fd","code_size_bytes":200269,"compile_status":"PASS","encoding":"UTF-8","endpoint_literals":["http://mcp-localdocs:8012/mcp","https://weaviate.eroomai.com/mcp"],"expected_parent_stage2_release_sha256":"9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53","external_python_source_ref_count":0,"extraction_transform":"NONE","forbidden_dynamic_call_count":0,"forbidden_import_count":0,"imports":["__future__","base64","binascii","concurrent","contextlib","datetime","decimal","hashlib","httpx","io","itertools","json","pathlib","re","sys","typing","unicodedata"],"plaintext_secret_count":0,"yaml_pointer":"/Agent/Stages/0/tasks/0/parameters/code"},"deployment_projection":{"path":"Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_20.yml","sha256":"3c4e6a7404f5b6a6b2403824c3cd9e71db566d4f2865bd4de3e6fd9d07824bd6","size_bytes":254756},"full_code_mirrors":["Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.py","Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.txt"],"parity_status":"PASS","schema_version":"stage2_s2_20_inline_code_receipt.v1","task_contract":{"agent":{"name":"Stage_2_S2_20","version":"1.0.0"},"authoring_rewritten":false,"canonical_task_semantics_sha256":"750bf364c9b73460349005590cbf65ae4c0235b5af0770937ccbbf669d159dd0","code_mirrors_byte_identical":true,"exactly_one_code_executor_run_code":true,"exactly_one_stage":true,"exactly_one_task":true,"expected_parent_stage2_release_sha256":"9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53","internal_dag":"(C20||C21)->C25->C26->(C22||(C27->C28->C29))->C30->C35","mcp_servers":{"code-executor":{"type":"streamable-http","url":"https://code-executor.mcp.eroomai.com/mcp"},"localdocs":{"type":"streamable-http","url":"http://mcp-localdocs:8012/mcp"}},"projection_byte_identical_to_authoring":true,"stage":{"name":"S2_20","nexts":[],"prevs":[],"skip_confirm":true},"task":{"code_sha256":"6f1f503378242cf64cf8f0838af2164ab1103bc7031c8a6873f4ee89be66f7fd","mcp":"code-executor","parameters":{"language":"python","network":"agent-network","requirements":"httpx==0.28.1","timeout":300},"task_name":"Task_S2_20_deterministic_relief_plan","tool_name":"run_code"},"task_procedure":{"IN":{"nexts":["Task_S2_20_deterministic_relief_plan"],"wait_until":[]},"OUT":{"nexts":[],"wait_until":["Task_S2_20_deterministic_relief_plan"]},"Task_S2_20_deterministic_relief_plan":{"nexts":["OUT"],"wait_until":["IN"]}}},"workflow_id":"S2_20"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"authoring_sha256":"6da21b319d10268e5f1959d085173d22017e4910c7222fd5e6daf72628629773","binding_sha256":"1d99d933aca1803a681b8fc70eab368fa3b11b5a4f7d384a6be6cd9cb5883512","byte_parity":"PASS","child_release_sha256":"90b96d0e00d0491f7dfafe3b9f2cec3a1055d64fe4979780fe00af849257e884","deployment_sha256":"6da21b319d10268e5f1959d085173d22017e4910c7222fd5e6daf72628629773","deterministic_helper_task_count":1,"llm_task_template_count":1,"receipt_digest":"5ee7d882c739ffc437e808b0d22580134ad4b3af0047fbe1e55eb46502f09ed0","receipt_status":"OFFLINE_AGENT_PROJECTION_VERIFIED","reduce_task_count":0,"schema_version":"stage2_s2_30_agent_receipt.v1","task_template_count":2}
|
{"authoring_sha256":"d0804526941207395c9b64a314b124ff8e919c6852e828d829dc45a9feb3ccdd","binding_sha256":"7cf19e7dd8d3ad58251e20edc56e11fde3bffb3936997538eb09301da06189dc","byte_parity":"PASS","child_release_sha256":"e6dd6abe44b39de462ea01f5e78aa39c70bbf47e0b75e08d224615a297d2135f","deployment_sha256":"d0804526941207395c9b64a314b124ff8e919c6852e828d829dc45a9feb3ccdd","deterministic_helper_task_count":1,"llm_task_template_count":1,"receipt_digest":"fac154c1a47a050c1d8cac98a6df7abd92b0410e024c9cb26664dcef4006f13d","receipt_status":"OFFLINE_AGENT_PROJECTION_VERIFIED","reduce_task_count":0,"schema_version":"stage2_s2_30_agent_receipt.v1","task_template_count":2}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"ast_policy_status":"PASS","authoring_agent_sha256":"6da21b319d10268e5f1959d085173d22017e4910c7222fd5e6daf72628629773","compile_status":"PASS","inline_code_sha256":"7814e64bfdbf5992d8009556b5a81268d99a0300d0b8f419c8c6384d90a906c9","inline_code_size_bytes":67777,"planner_mirror_paths":["runtime/s2_30_dispatch_planner.py","runtime/s2_30_dispatch_planner.txt"],"receipt_digest":"e1d46ddf49865d69715ba2f0c0a20290e9b755b5753709018c65c95c7486547c","receipt_status":"OFFLINE_PROJECTION_VERIFIED","runtime_admission_effect":"NONE","runtime_external_python_import_count":0,"schema_version":"stage2_s2_30_inline_code_receipt.v1"}
|
{"ast_policy_status":"PASS","authoring_agent_sha256":"d0804526941207395c9b64a314b124ff8e919c6852e828d829dc45a9feb3ccdd","compile_status":"PASS","inline_code_sha256":"1f7f3b842afbd3f1034f094d6341288effc9804a03920ada4bf8cfd512464e4c","inline_code_size_bytes":67777,"planner_mirror_paths":["runtime/s2_30_dispatch_planner.py","runtime/s2_30_dispatch_planner.txt"],"receipt_digest":"fe592796579be30899bde4006a0a3ba1d51b97b51a5b7049a3f67539158c71c9","receipt_status":"OFFLINE_PROJECTION_VERIFIED","runtime_admission_effect":"NONE","runtime_external_python_import_count":0,"schema_version":"stage2_s2_30_inline_code_receipt.v1"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"authoring_agent":{"path":"Stage_2_S2_30.yml","sha256":"6da21b319d10268e5f1959d085173d22017e4910c7222fd5e6daf72628629773","size_bytes":117060},"dynamic_item_tokens":["{{item.p32_common_authority_json}}","{{item.p31_rule_and_pack_json}}","{{item.s30_group_slice_json}}","{{item.compile_mode}}","{{item.s30_group_slice_sha256}}"],"inline_scalars":[{"parity":"PASS","raw_scalar_sha256":"a90ac95f0b24ea938a16699f34ef7f17eb870edcbd8964ba4f6709e36e1bbcc0","source":{"path":"prompts/P00_system_and_safety_contract.md","sha256":"c82cfc0b61adb1af7759fd321f78bcf7bebf7e5d2fc3a9c01533a80736c6d80e","size_bytes":7304},"wrapper":"stage_2_common_cache_prefix","yaml_pointer":"/Agent/Stages/0/tasks/1/prompts/0/content"},{"parity":"PASS","raw_scalar_sha256":"577efa2c3334298ec60188b9f0066f834e072dc99f785b3c6ab4d05a47fac18a","source":{"path":"prompts/P30_joint_drafting_contract.md","sha256":"5d790fccdacd1b7ce27e25cd52e4c301dbce24ba276def18bca608b5b16362aa","size_bytes":13681},"wrapper":"s2_30_joint_drafting_static_prompt","yaml_pointer":"/Agent/Stages/0/tasks/1/prompts/1/content"}],"message_order":["INLINE_COMMON_SYSTEM","INLINE_S2_30_STATIC_SYSTEM","P32_COMMON_AUTHORITY_SYSTEM_DATA","P31_RULE_AND_PACK_SYSTEM_DATA","S30_GROUP_CASE_USER_DATA"],"receipt_digest":"9dae633a55e091e5a8aa9173c2471b7ac24aaf3ddffda6cbd9ebbe6365d982d3","receipt_status":"OFFLINE_PROJECTION_VERIFIED","runtime_admission_effect":"NONE","schema_version":"stage2_s2_30_inline_prompt_projection_receipt.v1"}
|
{"authoring_agent":{"path":"Stage_2_S2_30.yml","sha256":"d0804526941207395c9b64a314b124ff8e919c6852e828d829dc45a9feb3ccdd","size_bytes":117060},"dynamic_item_tokens":["{{item.p32_common_authority_json}}","{{item.p31_rule_and_pack_json}}","{{item.s30_group_slice_json}}","{{item.compile_mode}}","{{item.s30_group_slice_sha256}}"],"inline_scalars":[{"parity":"PASS","raw_scalar_sha256":"a90ac95f0b24ea938a16699f34ef7f17eb870edcbd8964ba4f6709e36e1bbcc0","source":{"path":"prompts/P00_system_and_safety_contract.md","sha256":"c82cfc0b61adb1af7759fd321f78bcf7bebf7e5d2fc3a9c01533a80736c6d80e","size_bytes":7304},"wrapper":"stage_2_common_cache_prefix","yaml_pointer":"/Agent/Stages/0/tasks/1/prompts/0/content"},{"parity":"PASS","raw_scalar_sha256":"577efa2c3334298ec60188b9f0066f834e072dc99f785b3c6ab4d05a47fac18a","source":{"path":"prompts/P30_joint_drafting_contract.md","sha256":"5d790fccdacd1b7ce27e25cd52e4c301dbce24ba276def18bca608b5b16362aa","size_bytes":13681},"wrapper":"s2_30_joint_drafting_static_prompt","yaml_pointer":"/Agent/Stages/0/tasks/1/prompts/1/content"}],"message_order":["INLINE_COMMON_SYSTEM","INLINE_S2_30_STATIC_SYSTEM","P32_COMMON_AUTHORITY_SYSTEM_DATA","P31_RULE_AND_PACK_SYSTEM_DATA","S30_GROUP_CASE_USER_DATA"],"receipt_digest":"13b89691392616f6f70b2cb1a92a24ee4d891480b56c8c1b41b5bdabe88bbad1","receipt_status":"OFFLINE_PROJECTION_VERIFIED","runtime_admission_effect":"NONE","schema_version":"stage2_s2_30_inline_prompt_projection_receipt.v1"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"binding_sha256":"1d99d933aca1803a681b8fc70eab368fa3b11b5a4f7d384a6be6cd9cb5883512","child_release_sha256":"90b96d0e00d0491f7dfafe3b9f2cec3a1055d64fe4979780fe00af849257e884","evidence":[],"live_execution_attested":false,"parent_release_sha256":"579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81","receipt_digest":"6b97a3fd5bb2e3eb42a6e96296884e745f7c8feb2623a075c53b4b470da8ab41","runtime_admission_effect":"BLOCK_PRODUCTION","schema_version":"stage2_s2_30_legal_review_receipt.v1","status":"PENDING_KOREAN_LAWYER_REVIEW"}
|
{"binding_sha256":"7cf19e7dd8d3ad58251e20edc56e11fde3bffb3936997538eb09301da06189dc","child_release_sha256":"e6dd6abe44b39de462ea01f5e78aa39c70bbf47e0b75e08d224615a297d2135f","evidence":[],"live_execution_attested":false,"parent_release_sha256":"9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53","receipt_digest":"6fc276d58d8613c0f7a2f4cb31dce0fc12c04e0239168a969f77e8a3b85437cb","runtime_admission_effect":"BLOCK_PRODUCTION","schema_version":"stage2_s2_30_legal_review_receipt.v1","status":"PENDING_KOREAN_LAWYER_REVIEW"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"binding_sha256":"1d99d933aca1803a681b8fc70eab368fa3b11b5a4f7d384a6be6cd9cb5883512","child_release_sha256":"90b96d0e00d0491f7dfafe3b9f2cec3a1055d64fe4979780fe00af849257e884","evidence":[],"live_execution_attested":false,"parent_release_sha256":"579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81","receipt_digest":"0eaf5733b7605d8d2f79a99eea1cdd6d8447c0d679df41be00d04149ab699b3e","runtime_admission_effect":"BLOCK_PRODUCTION","schema_version":"stage2_s2_30_model_benchmark_receipt.v1","status":"PENDING_MODEL_BENCHMARK"}
|
{"binding_sha256":"7cf19e7dd8d3ad58251e20edc56e11fde3bffb3936997538eb09301da06189dc","child_release_sha256":"e6dd6abe44b39de462ea01f5e78aa39c70bbf47e0b75e08d224615a297d2135f","evidence":[],"live_execution_attested":false,"parent_release_sha256":"9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53","receipt_digest":"d8d231359c086ae13fec794d233ebf0ba5854c207da569923ed72d8c6ca90abc","runtime_admission_effect":"BLOCK_PRODUCTION","schema_version":"stage2_s2_30_model_benchmark_receipt.v1","status":"PENDING_MODEL_BENCHMARK"}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
["manifest/s2_30_parent_member_paths.json","offline_build/build_s2_30_agent_projection.py","offline_build/build_s2_30_agent_projection.txt","offline_build/validate_s2_30_contract.py","offline_build/validate_s2_30_contract.txt","prompts/P30_joint_drafting_contract.md","schemas/draft_atoms.schema.json","tests/fixtures/s2_30/adverse_fact_worknote_policy.json","tests/fixtures/s2_30/cache_prefix_drift.json","tests/fixtures/s2_30/context_reference_envelope.json","tests/fixtures/s2_30/counter_performance_and_declaration.json","tests/fixtures/s2_30/follower_batch_dispatch.json","tests/fixtures/s2_30/invalid_forbidden_output.json","tests/fixtures/s2_30/invalid_preassembled_prompt_item.json","tests/fixtures/s2_30/invalid_reference_output.json","tests/fixtures/s2_30/owner_singleton_dispatch.json","tests/fixtures/s2_30/partial_write_publish_barrier.json","tests/fixtures/s2_30/regression_manifest.json","tests/fixtures/s2_30/rule_requirement_admission_gap.json","tests/fixtures/s2_30/technical_failure.json","tests/fixtures/s2_30/valid_joint_draft_output.json","tests/s2_30/test_agent_contract.py","tests/s2_30/test_agent_contract.txt","tests/s2_30/test_dispatch_materialization.py","tests/s2_30/test_dispatch_materialization.txt","tests/s2_30/test_draft_atom_contract.py","tests/s2_30/test_draft_atom_contract.txt","tests/s2_30/test_prompt_cache_and_boundaries.py","tests/s2_30/test_prompt_cache_and_boundaries.txt","tests/s2_30/test_release_adapter_retry.py","tests/s2_30/test_release_adapter_retry.txt","workflows/S2_30_claim_group_draft_map.yml"]
|
["manifest/s2_30_parent_member_paths.json","offline_build/build_s2_30_agent_projection.py","offline_build/build_s2_30_agent_projection.txt","offline_build/validate_s2_30_contract.py","offline_build/validate_s2_30_contract.txt","prompts/P30_joint_drafting_contract.md","schemas/draft_atoms.schema.json","tests/fixtures/s2_30/adverse_fact_worknote_policy.json","tests/fixtures/s2_30/cache_prefix_drift.json","tests/fixtures/s2_30/context_reference_envelope.json","tests/fixtures/s2_30/counter_performance_and_declaration.json","tests/fixtures/s2_30/follower_batch_dispatch.json","tests/fixtures/s2_30/invalid_forbidden_output.json","tests/fixtures/s2_30/invalid_preassembled_prompt_item.json","tests/fixtures/s2_30/invalid_reference_output.json","tests/fixtures/s2_30/owner_singleton_dispatch.json","tests/fixtures/s2_30/partial_write_publish_barrier.json","tests/fixtures/s2_30/persisted_handoff_chain.json","tests/fixtures/s2_30/regression_manifest.json","tests/fixtures/s2_30/rule_requirement_admission_gap.json","tests/fixtures/s2_30/technical_failure.json","tests/fixtures/s2_30/valid_joint_draft_output.json","tests/s2_30/test_agent_contract.py","tests/s2_30/test_agent_contract.txt","tests/s2_30/test_dispatch_materialization.py","tests/s2_30/test_dispatch_materialization.txt","tests/s2_30/test_draft_atom_contract.py","tests/s2_30/test_draft_atom_contract.txt","tests/s2_30/test_prompt_cache_and_boundaries.py","tests/s2_30/test_prompt_cache_and_boundaries.txt","tests/s2_30/test_release_adapter_retry.py","tests/s2_30/test_release_adapter_retry.txt","workflows/S2_30_claim_group_draft_map.yml"]
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"binding_sha256":"1d99d933aca1803a681b8fc70eab368fa3b11b5a4f7d384a6be6cd9cb5883512","capabilities":[{"capability_id":"HOST_ATOMIC_GROUP_DISPATCH_CAS_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"AGENTBACKEND_TASK_PROCEDURE_WILDCARD_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"AGENTBACKEND_WILDCARD_RESULT_ADAPTER_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_CONTEXT_MATERIALIZER_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_CACHE_OWNER_SEQUENCE_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_STRICT_SCHEMA_REF_PROVENANCE_VALIDATOR_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_DETERMINISTIC_ATOM_ID_TWO_PASS_PERSIST_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_GROUP_RETRY_CONTROLLER_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_GROUP_BARRIER_COORDINATOR_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_CANARY_AUTHORIZATION_SIGNATURE_VERIFY_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"}],"child_release_sha256":"90b96d0e00d0491f7dfafe3b9f2cec3a1055d64fe4979780fe00af849257e884","evidence":[],"live_execution_attested":false,"overall_status":"PENDING_EXTERNAL_PLATFORM_BINDING","parent_release_sha256":"579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81","receipt_digest":"19ce93ff7d086826739ef57bcb766135b00f53a070952b449528bcfe0d7310ae","runtime_admission_effect":"BLOCK_PRODUCTION","schema_version":"stage2_s2_30_platform_adapter_receipt.v1"}
|
{"binding_sha256":"7cf19e7dd8d3ad58251e20edc56e11fde3bffb3936997538eb09301da06189dc","capabilities":[{"capability_id":"HOST_ATOMIC_GROUP_DISPATCH_CAS_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"AGENTBACKEND_TASK_PROCEDURE_WILDCARD_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"AGENTBACKEND_WILDCARD_RESULT_ADAPTER_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_CONTEXT_MATERIALIZER_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_CACHE_OWNER_SEQUENCE_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_STRICT_SCHEMA_REF_PROVENANCE_VALIDATOR_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_DETERMINISTIC_ATOM_ID_TWO_PASS_PERSIST_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_GROUP_RETRY_CONTROLLER_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_GROUP_BARRIER_COORDINATOR_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"capability_id":"S2_30_CANARY_AUTHORIZATION_SIGNATURE_VERIFY_V1","status":"PENDING_EXTERNAL_PLATFORM_BINDING"}],"child_release_sha256":"e6dd6abe44b39de462ea01f5e78aa39c70bbf47e0b75e08d224615a297d2135f","evidence":[],"live_execution_attested":false,"overall_status":"PENDING_EXTERNAL_PLATFORM_BINDING","parent_release_sha256":"9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53","receipt_digest":"31b81bdbef3bea6712c6ca2921af4e6b55e264f69b2ed341e1d83c770d3df1c5","runtime_admission_effect":"BLOCK_PRODUCTION","schema_version":"stage2_s2_30_platform_adapter_receipt.v1"}
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
|||||||
|
{"authoring":{"path":"Stage_2_S2_40.yml","sha256":"5cbe2ac9aa73d95a5fa4e6cf71d7d8f0ad83aa05536f573c13e42505ef5d46f7","size_bytes":249967,"unique_key_parse":"PASS"},"authoring_rewritten":false,"build_kind":"OFFLINE_AUTHORING_PROJECTION","canonical_code":{"ast_status":"PASS","code_sha256":"e521e1a65294a769cd6bf20edb159f8720c105b60cb769885aa416c8c763dcef","code_size_bytes":200946,"compile_status":"PASS","encoding":"UTF-8","expected_parent_stage2_release_sha256":"9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53","external_python_source_ref_count":0,"external_url_count":0,"extraction_transform":"NONE","forbidden_dynamic_call_count":0,"forbidden_import_count":0,"imports":["__future__","base64","binascii","contextlib","datetime","hashlib","httpx","io","itertools","json","pathlib","re","sys","typing","unicodedata"],"placeholder_count":0,"plaintext_secret_count":0,"yaml_pointer":"/Agent/Stages/0/tasks/0/parameters/code"},"deployment_projection":{"byte_identical_to_authoring":true,"canonical_task_semantics_sha256":"a8d6450b5a40308b3c2af5227fd3bc0c4f575dd8ea17b10b5bb7d6687ebc9d9e","path":"Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_40.yml","sha256":"5cbe2ac9aa73d95a5fa4e6cf71d7d8f0ad83aa05536f573c13e42505ef5d46f7","size_bytes":249967},"expected_parent_stage2_release_sha256":"9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53","full_code_mirrors":[{"byte_identical_to_canonical_code":true,"path":"Default_Agent/Stage_2_Clean/runtime/s2_40_commit.py","sha256":"e521e1a65294a769cd6bf20edb159f8720c105b60cb769885aa416c8c763dcef","size_bytes":200946},{"byte_identical_to_canonical_code":true,"path":"Default_Agent/Stage_2_Clean/runtime/s2_40_commit.txt","sha256":"e521e1a65294a769cd6bf20edb159f8720c105b60cb769885aa416c8c763dcef","size_bytes":200946}],"parity_status":"PASS","schema_version":"stage2_s2_40_inline_code_receipt.v1","source_of_truth":"Stage_2_S2_40.yml","task_contract":{"agent":{"name":"Stage_2_S2_40","version":"1.0.0"},"authoring_rewritten":false,"canonical_task_semantics_sha256":"a8d6450b5a40308b3c2af5227fd3bc0c4f575dd8ea17b10b5bb7d6687ebc9d9e","code_mirrors_byte_identical":true,"exactly_one_code_executor_run_code":true,"exactly_one_stage":true,"exactly_one_task":true,"mcp_servers":{"code-executor":{"type":"streamable-http","url":"https://code-executor.mcp.eroomai.com/mcp"},"localdocs":{"type":"streamable-http","url":"http://mcp-localdocs:8012/mcp"}},"projection_byte_identical_to_authoring":true,"stage":{"name":"S2_40","nexts":[],"prevs":[]},"task":{"code_sha256":"e521e1a65294a769cd6bf20edb159f8720c105b60cb769885aa416c8c763dcef","mcp":"code-executor","parameters":{"language":"python","network":"agent-network","requirements":"httpx==0.28.1","timeout":300},"task_name":"Task_S2_40_deterministic_finalizer","tool_name":"run_code"},"task_procedure":{"IN":{"nexts":["Task_S2_40_deterministic_finalizer"],"wait_until":[]},"OUT":{"nexts":[],"wait_until":["Task_S2_40_deterministic_finalizer"]},"Task_S2_40_deterministic_finalizer":{"nexts":["OUT"],"wait_until":["IN"]}}},"workflow_id":"S2_40"}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
[
|
||||||
|
"manifest/s2_40_parent_member_paths.json",
|
||||||
|
"offline_build/build_s2_40_inline_projection.py",
|
||||||
|
"offline_build/build_s2_40_inline_projection.txt",
|
||||||
|
"runtime/c45_document_assembler.py",
|
||||||
|
"runtime/c45_document_assembler.txt",
|
||||||
|
"runtime/rendering/closed_renderer.py",
|
||||||
|
"runtime/rendering/closed_renderer.txt",
|
||||||
|
"runtime/validation/invariant_runner.py",
|
||||||
|
"runtime/validation/invariant_runner.txt",
|
||||||
|
"schemas/migration_regression.schema.json",
|
||||||
|
"schemas/package.schema.json",
|
||||||
|
"schemas/review_status.schema.json",
|
||||||
|
"tests/fixtures/s2_40/candidate_digest_noncycle.json",
|
||||||
|
"tests/fixtures/s2_40/cost_provisional_execution_numbering.json",
|
||||||
|
"tests/fixtures/s2_40/exhibit_object_party_title_completeness.json",
|
||||||
|
"tests/fixtures/s2_40/part_set_missing_duplicate_or_cross_group.json",
|
||||||
|
"tests/fixtures/s2_40/partial_write_readback_barrier.json",
|
||||||
|
"tests/fixtures/s2_40/regression_manifest.json",
|
||||||
|
"tests/fixtures/s2_40/relief_cause_crossmatch_mutations.json",
|
||||||
|
"tests/fixtures/s2_40/renderer_ast_slot_branch_mutations.json",
|
||||||
|
"tests/fixtures/s2_40/review_policy_state_aggregation.json",
|
||||||
|
"tests/fixtures/s2_40/review_receipt_approve_resume.json",
|
||||||
|
"tests/fixtures/s2_40/review_receipt_content_change_supersede.json",
|
||||||
|
"tests/fixtures/s2_40/review_receipt_missing_or_invalid.json",
|
||||||
|
"tests/fixtures/s2_40/same_binding_idempotence_and_commit_conflict.json",
|
||||||
|
"tests/fixtures/s2_40/v01_v18_invariant_matrix.json",
|
||||||
|
"tests/fixtures/s2_40/valid_full_candidate_and_commit.json",
|
||||||
|
"tests/fixtures/s2_40/valid_status_only_diagnostic.json",
|
||||||
|
"tests/s2_40/test_agent_and_inline_parity.py",
|
||||||
|
"tests/s2_40/test_agent_and_inline_parity.txt",
|
||||||
|
"tests/s2_40/test_c40_reduce_and_conservation.py",
|
||||||
|
"tests/s2_40/test_c40_reduce_and_conservation.txt",
|
||||||
|
"tests/s2_40/test_c45_closed_render.py",
|
||||||
|
"tests/s2_40/test_c45_closed_render.txt",
|
||||||
|
"tests/s2_40/test_commit_barrier_and_idempotence.py",
|
||||||
|
"tests/s2_40/test_commit_barrier_and_idempotence.txt",
|
||||||
|
"tests/s2_40/test_release_closure.py",
|
||||||
|
"tests/s2_40/test_release_closure.txt",
|
||||||
|
"tests/s2_40/test_review_state_machine.py",
|
||||||
|
"tests/s2_40/test_review_state_machine.txt",
|
||||||
|
"tests/s2_40/test_v01_v18.py",
|
||||||
|
"tests/s2_40/test_v01_v18.txt",
|
||||||
|
"workflows/S2_40_final_review_render_and_commit.yml"
|
||||||
|
]
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"admission_status":"PENDING","backend_capability_receipt_sha256":"PENDING_SEQUENTIAL_BIND","embedded_task_admissions":[{"admission_scope":"CODE_EXECUTOR_HELPER_ONLY","binding_id":"S2-BINDING-S2_30-DISPATCH-PLANNER-V1","execution_unit_id":"S2_30::Task_S2_30_dispatch_planner","host_stage_id":"S2_30","inline_code_receipt_ref":{"asset_id":"RECEIPT-S2_30-INLINE-CODE","binding_status":"BOUND","path":"manifest/s2_30_inline_code_receipt.json","schema_id":"stage2_s2_30_inline_code_receipt.v1","sha256":"78fb197d50ec706be35cf1766bb9e9fee1767a8e61109603313821dd6040f552"},"task_name":"Task_S2_30_dispatch_planner"}],"executor_binding_sha256":"bfd84ddedb488e7391341041a739437d88372e3c9ed740e5dfb31ae3a138a936","parent_release_sha256":"579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81","present_deterministic_stage_ids":["S2_00","S2_20"],"schema_version":"stage2_deterministic_admission_receipt.v1","signature":"PENDING_EXTERNAL_SIGNATURE","signature_verification_status":"PENDING_EXTERNAL_SIGNATURE","signed_payload_sha256":"PENDING_SEQUENTIAL_BIND","stage_receipts":[{"asset_id":"RECEIPT-S2_00-INLINE-CODE","binding_status":"BOUND","path":"manifest/s2_00_inline_code_receipt.json","schema_id":"stage2_s2_00_inline_code_receipt.v2","sha256":"ced01ef57a2e5341b1f7ddbf810ff3c6fb85fbb3cf7cf80836328a8a971835b4"},{"asset_id":"RECEIPT-S2_20-INLINE-CODE","binding_status":"BOUND","path":"manifest/s2_20_inline_code_receipt.json","schema_id":"stage2_s2_20_inline_code_receipt.v1","sha256":"60000d2412a757e9b2ab2525eedb08ebda20ec02a49f5b1b0c4e89e6a0aeca2b"}]}
|
{"admission_status":"PENDING","backend_capability_receipt_sha256":"PENDING_SEQUENTIAL_BIND","embedded_task_admissions":[{"admission_scope":"CODE_EXECUTOR_HELPER_ONLY","binding_id":"S2-BINDING-S2_30-DISPATCH-PLANNER-V1","execution_unit_id":"S2_30::Task_S2_30_dispatch_planner","host_stage_id":"S2_30","inline_code_receipt_ref":{"asset_id":"RECEIPT-S2_30-INLINE-CODE","binding_status":"BOUND","path":"manifest/s2_30_inline_code_receipt.json","schema_id":"stage2_s2_30_inline_code_receipt.v1","sha256":"8d1618771cf1f72aa3c6a5a4544fe6b6e949ccc7ecfef56f166561971beee896"},"task_name":"Task_S2_30_dispatch_planner"}],"executor_binding_sha256":"a1062e9db242747c75a4362fb95eeba6c65f78ac647d4571ed774d2ee3af40f7","parent_release_sha256":"9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53","present_deterministic_stage_ids":["S2_00","S2_20","S2_40"],"schema_version":"stage2_deterministic_admission_receipt.v1","signature":"PENDING_EXTERNAL_SIGNATURE","signature_verification_status":"PENDING_EXTERNAL_SIGNATURE","signed_payload_sha256":"PENDING_SEQUENTIAL_BIND","stage_receipts":[{"asset_id":"RECEIPT-S2_00-INLINE-CODE","binding_status":"BOUND","path":"manifest/s2_00_inline_code_receipt.json","schema_id":"stage2_s2_00_inline_code_receipt.v2","sha256":"36af21949e5ef53a3d39df8ea9491c64da43abcc4f5e42e13db9bb391da843b8"},{"asset_id":"RECEIPT-S2_20-INLINE-CODE","binding_status":"BOUND","path":"manifest/s2_20_inline_code_receipt.json","schema_id":"stage2_s2_20_inline_code_receipt.v1","sha256":"65e29f10f065cdd77281aa47acaf341edb38b0595aab0aa9f04bbee3a4718608"},{"asset_id":"RECEIPT-S2_40-INLINE-CODE","binding_status":"BOUND","path":"manifest/s2_40_inline_code_receipt.json","schema_id":"stage2_s2_40_inline_code_receipt.v1","sha256":"9bd0cfaee8d9e78480ab80a2fa541414c7ad0565439fa0f4661701fcdfde6526"}]}
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+278
-18
@@ -15,7 +15,8 @@ import hashlib
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from pathlib import Path, PurePosixPath
|
from pathlib import Path, PurePosixPath
|
||||||
from typing import Any, Iterable
|
import re
|
||||||
|
from typing import Any, Iterable, Mapping
|
||||||
|
|
||||||
|
|
||||||
MODULE_MANIFEST_SCHEMA = "stage2_module_manifest.v1"
|
MODULE_MANIFEST_SCHEMA = "stage2_module_manifest.v1"
|
||||||
@@ -53,9 +54,145 @@ FORBIDDEN_PARENT_MEMBERS = frozenset(
|
|||||||
"manifest/s2_30_model_benchmark_receipt.json",
|
"manifest/s2_30_model_benchmark_receipt.json",
|
||||||
"manifest/s2_30_legal_review_receipt.json",
|
"manifest/s2_30_legal_review_receipt.json",
|
||||||
"manifest/s2_30_release.json",
|
"manifest/s2_30_release.json",
|
||||||
|
"agent_scripts/Stage_2_S2_40.yml",
|
||||||
|
"runtime/s2_40_commit.py",
|
||||||
|
"runtime/s2_40_commit.txt",
|
||||||
|
"manifest/s2_40_inline_code_receipt.json",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
STAGE_GENERIC_METADATA = {
|
||||||
|
"S2_20": {
|
||||||
|
"asset_version": "s2_20.1",
|
||||||
|
"module_id_prefix": "S2_20-ASSET",
|
||||||
|
"owner": "Stage_2_S2_20_owner",
|
||||||
|
"schema_version": "stage2_s2_20_generic_asset.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_20",
|
||||||
|
},
|
||||||
|
"S2_30": {
|
||||||
|
"asset_version": "s2_30.1",
|
||||||
|
"module_id_prefix": "S2_30-ASSET",
|
||||||
|
"owner": "Stage_2_S2_30_owner",
|
||||||
|
"schema_version": "stage2_s2_30_generic_asset.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_30",
|
||||||
|
},
|
||||||
|
"S2_40": {
|
||||||
|
"asset_version": "s2_40.1",
|
||||||
|
"module_id_prefix": "S2_40-ASSET",
|
||||||
|
"owner": "Stage_2_S2_40_owner",
|
||||||
|
"schema_version": "stage2_s2_40_generic_asset.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_40",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
S2_40_WORKFLOW_PATH = "workflows/S2_40_final_review_render_and_commit.yml"
|
||||||
|
S2_40_TRANSITIONED_STUB_VALUES = frozenset(
|
||||||
|
{
|
||||||
|
"WF-S2_40-STATUS-ONLY",
|
||||||
|
"s2_40.status_only.1",
|
||||||
|
"DRAFT_HANDOFF_STUB_HASH_BOUND",
|
||||||
|
"entrypoint_id == S2_40_STATUS_ONLY",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
S2_40_WORKFLOW_METADATA: dict[str, Any] = {
|
||||||
|
"asset_version": "s2_40.finalizer.1",
|
||||||
|
"authority_ids": [],
|
||||||
|
"consumed_schema_ids": [
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_00/context.schema.v2.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_00/ingress.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_10/s2_10.schema.v2.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_20/binding_retrieval.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_20/calculation.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_20/relief_plan.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_30/draft_atoms.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_40/package.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/shared/deployment.schema.v3.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/shared/review_status.schema.v1.json",
|
||||||
|
],
|
||||||
|
"dependency_module_ids": [
|
||||||
|
"SCHEMA-CONTEXT",
|
||||||
|
"SCHEMA-DEPLOYMENT",
|
||||||
|
"SCHEMA-INGRESS",
|
||||||
|
"SCHEMA-REVIEW-STATUS",
|
||||||
|
"SCHEMA-S2_10",
|
||||||
|
"S2_20-ASSET-809C3C709B4F2295",
|
||||||
|
"S2_20-ASSET-AA40CF5140DE8790",
|
||||||
|
"S2_20-ASSET-C9FC917D8CBF920E",
|
||||||
|
"S2_20-ASSET-E82839694073D431",
|
||||||
|
"S2_20-ASSET-FF1F03B3FC56E579",
|
||||||
|
"S2_30-ASSET-6A9B1F5EC854740E",
|
||||||
|
"S2_30-ASSET-E1FCF8BD63300F4D",
|
||||||
|
"S2_40-ASSET-5C4CAFFAB8D2F982",
|
||||||
|
"WF-S2_00",
|
||||||
|
"WF-S2_10",
|
||||||
|
],
|
||||||
|
"entry_routes": ["TO_S2_40", "TO_S2_40_STATUS_ONLY"],
|
||||||
|
"forbidden_contract_codes": [
|
||||||
|
"LEGACY-STAGE2-V0-V3",
|
||||||
|
"S2_40-DIRECTORY-SCAN",
|
||||||
|
"S2_40-FREE-TEXT-RENDER-FALLBACK",
|
||||||
|
"S2_40-LLM-CALL",
|
||||||
|
"S2_40-RUN-STATUS-NOT-LAST",
|
||||||
|
"S2_40-UNVERIFIED-READY",
|
||||||
|
],
|
||||||
|
"implementation_status": "IMPLEMENTED_OFFLINE_CONTRACT_LIVE_ADMISSION_PENDING",
|
||||||
|
"incompatible_module_ids": [],
|
||||||
|
"inputs": [
|
||||||
|
"ingress/ingress_status.json",
|
||||||
|
"ingress/technical_diagnostic.json",
|
||||||
|
"ingress/stage1_input_manifest.json",
|
||||||
|
"ingress/intake_report.json",
|
||||||
|
"review/issue_ledger.base.json",
|
||||||
|
"map_s2_10/s2_10_publish_status.json",
|
||||||
|
"plan/plan_publish_status.json",
|
||||||
|
"plan/canonical_relief_plan.json",
|
||||||
|
"plan/claim_groups.json",
|
||||||
|
"plan/case_type_bindings.json",
|
||||||
|
"map_s2_30/s2_30_publish_status.json",
|
||||||
|
"map_s2_30/artifact_manifest.json",
|
||||||
|
"map_s2_30/{draft_parts,issue_patches,worknote_parts,usage_parts}/<claim_group_id>.json",
|
||||||
|
"review/review_receipts/<request_id>.json",
|
||||||
|
"review/lawyer_judgment_record.json",
|
||||||
|
],
|
||||||
|
"legal_admission_status": "PENDING",
|
||||||
|
"live_admission_status": "PENDING",
|
||||||
|
"module_id": "WF-S2_40",
|
||||||
|
"module_kind": "WORKFLOW",
|
||||||
|
"outputs": [
|
||||||
|
"review/issue_ledger.final.json",
|
||||||
|
"review/assumption_ledger.json",
|
||||||
|
"review/llm_usage.jsonl",
|
||||||
|
"candidates/by-content-digest/<candidate_content_digest>/",
|
||||||
|
"review/review_requests/<request_id>.json",
|
||||||
|
"final/claim_relief.md",
|
||||||
|
"final/claim_cause.md",
|
||||||
|
"final/pleading_draft.md",
|
||||||
|
"final/stage2_package.json",
|
||||||
|
"commit/commit_intent.json",
|
||||||
|
"commit/stage2_commit_result.json",
|
||||||
|
"control/run_status.json",
|
||||||
|
],
|
||||||
|
"owner": "Stage_2_S2_40_owner",
|
||||||
|
"path": S2_40_WORKFLOW_PATH,
|
||||||
|
"produced_schema_ids": [
|
||||||
|
"stage2_s2_40_candidate_manifest.v1",
|
||||||
|
"stage2_s2_40_commit_intent.v1",
|
||||||
|
"stage2_s2_40_commit_result.v1",
|
||||||
|
"stage2_s2_40_package.v1",
|
||||||
|
"stage2_s2_40_run_status.v1",
|
||||||
|
],
|
||||||
|
"schema_version": "stage2_workflow_contract.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_40 and entry_route in {TO_S2_40,TO_S2_40_STATUS_ONLY}",
|
||||||
|
"semantic_review_status": "PENDING_LEGAL_AND_LIVE_ADMISSION",
|
||||||
|
"status_only_exact_inputs": [
|
||||||
|
"ingress/ingress_status.json",
|
||||||
|
"ingress/technical_diagnostic.json",
|
||||||
|
"ingress/stage1_input_manifest.json",
|
||||||
|
"ingress/intake_report.json",
|
||||||
|
"review/issue_ledger.base.json",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ReleaseBuildError(ValueError):
|
class ReleaseBuildError(ValueError):
|
||||||
"""Raised when a release input violates the canonical closure contract."""
|
"""Raised when a release input violates the canonical closure contract."""
|
||||||
@@ -151,7 +288,7 @@ def _mirror_for(root: Path, source_path: str) -> tuple[str, bytes] | None:
|
|||||||
return mirror_path, mirror_raw
|
return mirror_path, mirror_raw
|
||||||
|
|
||||||
|
|
||||||
def _generic_kind(relative: str) -> str:
|
def _generic_kind(relative: str, owner_stage: str = "S2_20") -> str:
|
||||||
if relative.startswith("tests/"):
|
if relative.startswith("tests/"):
|
||||||
return "TEST"
|
return "TEST"
|
||||||
if relative.startswith("schemas/"):
|
if relative.startswith("schemas/"):
|
||||||
@@ -168,13 +305,16 @@ def _generic_kind(relative: str) -> str:
|
|||||||
return "MANIFEST"
|
return "MANIFEST"
|
||||||
if relative.startswith("registry/"):
|
if relative.startswith("registry/"):
|
||||||
return "DECLARATIVE_REGISTRY"
|
return "DECLARATIVE_REGISTRY"
|
||||||
return "S2_20_ASSET"
|
return f"{owner_stage}_ASSET"
|
||||||
|
|
||||||
|
|
||||||
def _generic_module_row(relative: str) -> dict[str, Any]:
|
def _generic_module_row(relative: str, owner_stage: str = "S2_20") -> dict[str, Any]:
|
||||||
|
metadata = STAGE_GENERIC_METADATA.get(owner_stage)
|
||||||
|
if metadata is None:
|
||||||
|
raise ReleaseBuildError(f"UNSUPPORTED_PARENT_MEMBER_OWNER_STAGE:{owner_stage}")
|
||||||
identity = hashlib.sha256(relative.encode("utf-8")).hexdigest()[:16].upper()
|
identity = hashlib.sha256(relative.encode("utf-8")).hexdigest()[:16].upper()
|
||||||
return {
|
return {
|
||||||
"asset_version": "s2_20.1",
|
"asset_version": metadata["asset_version"],
|
||||||
"authority_ids": [],
|
"authority_ids": [],
|
||||||
"consumed_schema_ids": [],
|
"consumed_schema_ids": [],
|
||||||
"dependency_module_ids": [],
|
"dependency_module_ids": [],
|
||||||
@@ -182,18 +322,70 @@ def _generic_module_row(relative: str) -> dict[str, Any]:
|
|||||||
"implementation_status": "DEV_HASH_BOUND_OFFLINE_ONLY",
|
"implementation_status": "DEV_HASH_BOUND_OFFLINE_ONLY",
|
||||||
"incompatible_module_ids": [],
|
"incompatible_module_ids": [],
|
||||||
"inputs": [],
|
"inputs": [],
|
||||||
"module_id": f"S2_20-ASSET-{identity}",
|
"module_id": f"{metadata['module_id_prefix']}-{identity}",
|
||||||
"module_kind": _generic_kind(relative),
|
"module_kind": _generic_kind(relative, owner_stage),
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"owner": "Stage_2_S2_20_owner",
|
"owner": metadata["owner"],
|
||||||
"path": relative,
|
"path": relative,
|
||||||
"produced_schema_ids": [],
|
"produced_schema_ids": [],
|
||||||
"schema_version": "stage2_s2_20_generic_asset.v1",
|
"schema_version": metadata["schema_version"],
|
||||||
"scope_predicate": "workflow_id == S2_20",
|
"scope_predicate": metadata["scope_predicate"],
|
||||||
"semantic_review_status": "PENDING_LEGAL_AND_LIVE_ADMISSION",
|
"semantic_review_status": "PENDING_LEGAL_AND_LIVE_ADMISSION",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _s2_40_workflow_module_row() -> dict[str, Any]:
|
||||||
|
"""Return the authoritative full S2_40 workflow row.
|
||||||
|
|
||||||
|
The pre-S2_40 parent template carried a status-only stub row at the same
|
||||||
|
physical path. Preserving that semantic metadata while merely refreshing
|
||||||
|
its file hash would make the release closure describe a different workflow
|
||||||
|
than the bytes it seals.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return copy.deepcopy(S2_40_WORKFLOW_METADATA)
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_no_transitioned_s2_40_stub(rows: Iterable[Mapping[str, Any]]) -> None:
|
||||||
|
for row in rows:
|
||||||
|
if row.get("path") != S2_40_WORKFLOW_PATH:
|
||||||
|
continue
|
||||||
|
observed = {
|
||||||
|
row.get("module_id"),
|
||||||
|
row.get("asset_version"),
|
||||||
|
row.get("implementation_status"),
|
||||||
|
row.get("scope_predicate"),
|
||||||
|
}
|
||||||
|
stale = sorted(str(value) for value in observed & S2_40_TRANSITIONED_STUB_VALUES)
|
||||||
|
if stale:
|
||||||
|
raise ReleaseBuildError(f"S2_40_TRANSITIONED_STUB_METADATA_FORBIDDEN:{stale}")
|
||||||
|
drift = {
|
||||||
|
key: {"expected": expected, "observed": row.get(key)}
|
||||||
|
for key, expected in S2_40_WORKFLOW_METADATA.items()
|
||||||
|
if row.get(key) != expected
|
||||||
|
}
|
||||||
|
if drift:
|
||||||
|
raise ReleaseBuildError(
|
||||||
|
"S2_40_WORKFLOW_METADATA_MISMATCH:"
|
||||||
|
+ json.dumps(drift, ensure_ascii=False, sort_keys=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_generic_module_row(row: Mapping[str, Any]) -> bool:
|
||||||
|
schema_version = row.get("schema_version")
|
||||||
|
module_id = row.get("module_id")
|
||||||
|
return (
|
||||||
|
schema_version in {
|
||||||
|
metadata["schema_version"] for metadata in STAGE_GENERIC_METADATA.values()
|
||||||
|
}
|
||||||
|
or isinstance(module_id, str)
|
||||||
|
and any(
|
||||||
|
module_id.startswith(f"{metadata['module_id_prefix']}-")
|
||||||
|
for metadata in STAGE_GENERIC_METADATA.values()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _refresh_module_row(root: Path, row: dict[str, Any]) -> dict[str, Any]:
|
def _refresh_module_row(root: Path, row: dict[str, Any]) -> dict[str, Any]:
|
||||||
refreshed = copy.deepcopy(row)
|
refreshed = copy.deepcopy(row)
|
||||||
relative = _relative_path(refreshed.get("path"))
|
relative = _relative_path(refreshed.get("path"))
|
||||||
@@ -234,8 +426,9 @@ def build_module_manifest(
|
|||||||
root: Path,
|
root: Path,
|
||||||
template: dict[str, Any],
|
template: dict[str, Any],
|
||||||
explicit_paths: Iterable[str],
|
explicit_paths: Iterable[str],
|
||||||
|
path_owners: Mapping[str, str] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Preserve canonical rows, refresh physical bindings and append S2_20 rows."""
|
"""Refresh canonical rows and append stage-owned generic source rows."""
|
||||||
|
|
||||||
if template.get("schema_version") != MODULE_MANIFEST_SCHEMA:
|
if template.get("schema_version") != MODULE_MANIFEST_SCHEMA:
|
||||||
raise ReleaseBuildError("CANONICAL_MODULE_MANIFEST_V1_REQUIRED")
|
raise ReleaseBuildError("CANONICAL_MODULE_MANIFEST_V1_REQUIRED")
|
||||||
@@ -249,6 +442,14 @@ def build_module_manifest(
|
|||||||
forbidden = sorted(set(normalized_explicit) & FORBIDDEN_PARENT_MEMBERS)
|
forbidden = sorted(set(normalized_explicit) & FORBIDDEN_PARENT_MEMBERS)
|
||||||
if forbidden:
|
if forbidden:
|
||||||
raise ReleaseBuildError(f"NON_CYCLIC_PARENT_VIOLATION:{forbidden}")
|
raise ReleaseBuildError(f"NON_CYCLIC_PARENT_VIOLATION:{forbidden}")
|
||||||
|
normalized_owners: dict[str, str] = {}
|
||||||
|
for raw_path, owner_stage in (path_owners or {}).items():
|
||||||
|
relative = _relative_path(raw_path)
|
||||||
|
if relative not in normalized_explicit:
|
||||||
|
raise ReleaseBuildError(f"OWNER_PATH_NOT_IN_EXPLICIT_CLOSURE:{relative}")
|
||||||
|
if owner_stage not in STAGE_GENERIC_METADATA:
|
||||||
|
raise ReleaseBuildError(f"UNSUPPORTED_PARENT_MEMBER_OWNER_STAGE:{owner_stage}")
|
||||||
|
normalized_owners[relative] = owner_stage
|
||||||
|
|
||||||
result = copy.deepcopy(template)
|
result = copy.deepcopy(template)
|
||||||
refreshed_rows: list[dict[str, Any]] = []
|
refreshed_rows: list[dict[str, Any]] = []
|
||||||
@@ -263,9 +464,28 @@ def build_module_manifest(
|
|||||||
raise ReleaseBuildError(f"MODULE_ID_INVALID_OR_DUPLICATE:{module_id!r}")
|
raise ReleaseBuildError(f"MODULE_ID_INVALID_OR_DUPLICATE:{module_id!r}")
|
||||||
if relative in seen_paths:
|
if relative in seen_paths:
|
||||||
raise ReleaseBuildError(f"MODULE_PATH_DUPLICATE:{relative}")
|
raise ReleaseBuildError(f"MODULE_PATH_DUPLICATE:{relative}")
|
||||||
seen_ids.add(module_id)
|
owner_stage = normalized_owners.get(relative)
|
||||||
|
if relative == S2_40_WORKFLOW_PATH:
|
||||||
|
if owner_stage not in {None, "S2_40"}:
|
||||||
|
raise ReleaseBuildError(
|
||||||
|
f"S2_40_WORKFLOW_OWNER_STAGE_MISMATCH:{owner_stage}"
|
||||||
|
)
|
||||||
|
candidate = _s2_40_workflow_module_row()
|
||||||
|
else:
|
||||||
|
candidate = (
|
||||||
|
_generic_module_row(relative, owner_stage)
|
||||||
|
if owner_stage is not None and _is_generic_module_row(value)
|
||||||
|
else value
|
||||||
|
)
|
||||||
|
refreshed = _refresh_module_row(root, candidate)
|
||||||
|
refreshed_id = refreshed.get("module_id")
|
||||||
|
if not isinstance(refreshed_id, str) or not refreshed_id:
|
||||||
|
raise ReleaseBuildError(f"MODULE_ID_INVALID_OR_DUPLICATE:{refreshed_id!r}")
|
||||||
|
if refreshed_id in seen_ids:
|
||||||
|
raise ReleaseBuildError(f"MODULE_ID_INVALID_OR_DUPLICATE:{refreshed_id!r}")
|
||||||
|
seen_ids.add(refreshed_id)
|
||||||
seen_paths.add(relative)
|
seen_paths.add(relative)
|
||||||
refreshed_rows.append(_refresh_module_row(root, value))
|
refreshed_rows.append(refreshed)
|
||||||
|
|
||||||
explicit_set = set(normalized_explicit)
|
explicit_set = set(normalized_explicit)
|
||||||
for relative in sorted(normalized_explicit):
|
for relative in sorted(normalized_explicit):
|
||||||
@@ -278,13 +498,19 @@ def build_module_manifest(
|
|||||||
continue
|
continue
|
||||||
if relative in seen_paths:
|
if relative in seen_paths:
|
||||||
continue
|
continue
|
||||||
row = _refresh_module_row(root, _generic_module_row(relative))
|
candidate = (
|
||||||
|
_s2_40_workflow_module_row()
|
||||||
|
if relative == S2_40_WORKFLOW_PATH
|
||||||
|
else _generic_module_row(relative, normalized_owners.get(relative, "S2_20"))
|
||||||
|
)
|
||||||
|
row = _refresh_module_row(root, candidate)
|
||||||
if row["module_id"] in seen_ids:
|
if row["module_id"] in seen_ids:
|
||||||
raise ReleaseBuildError(f"GENERATED_MODULE_ID_COLLISION:{row['module_id']}")
|
raise ReleaseBuildError(f"GENERATED_MODULE_ID_COLLISION:{row['module_id']}")
|
||||||
seen_ids.add(row["module_id"])
|
seen_ids.add(row["module_id"])
|
||||||
seen_paths.add(relative)
|
seen_paths.add(relative)
|
||||||
refreshed_rows.append(row)
|
refreshed_rows.append(row)
|
||||||
|
|
||||||
|
_assert_no_transitioned_s2_40_stub(refreshed_rows)
|
||||||
result["modules"] = refreshed_rows
|
result["modules"] = refreshed_rows
|
||||||
mirrors = [entry for row in refreshed_rows if (entry := _documentation_mirror(row))]
|
mirrors = [entry for row in refreshed_rows if (entry := _documentation_mirror(row))]
|
||||||
mirror_sources = [str(entry["source_path"]) for entry in mirrors]
|
mirror_sources = [str(entry["source_path"]) for entry in mirrors]
|
||||||
@@ -312,6 +538,24 @@ def build_module_manifest(
|
|||||||
for row in refreshed_rows
|
for row in refreshed_rows
|
||||||
if row.get("schema_version") == "stage2_s2_20_generic_asset.v1"
|
if row.get("schema_version") == "stage2_s2_20_generic_asset.v1"
|
||||||
),
|
),
|
||||||
|
"s2_30_generic_module_count": sum(
|
||||||
|
1
|
||||||
|
for row in refreshed_rows
|
||||||
|
if row.get("schema_version") == "stage2_s2_30_generic_asset.v1"
|
||||||
|
),
|
||||||
|
"s2_40_generic_module_count": sum(
|
||||||
|
1
|
||||||
|
for row in refreshed_rows
|
||||||
|
if row.get("schema_version") == "stage2_s2_40_generic_asset.v1"
|
||||||
|
),
|
||||||
|
"generic_module_counts_by_stage": {
|
||||||
|
stage: sum(
|
||||||
|
1
|
||||||
|
for row in refreshed_rows
|
||||||
|
if row.get("schema_version") == metadata["schema_version"]
|
||||||
|
)
|
||||||
|
for stage, metadata in sorted(STAGE_GENERIC_METADATA.items())
|
||||||
|
},
|
||||||
"executable_agent_hash_included": False,
|
"executable_agent_hash_included": False,
|
||||||
"executor_binding_hash_included": False,
|
"executor_binding_hash_included": False,
|
||||||
"inline_runtime_code_hash_included": False,
|
"inline_runtime_code_hash_included": False,
|
||||||
@@ -431,8 +675,14 @@ def build_release_package(
|
|||||||
path_list: list[str],
|
path_list: list[str],
|
||||||
module_template: dict[str, Any],
|
module_template: dict[str, Any],
|
||||||
parent_template: dict[str, Any],
|
parent_template: dict[str, Any],
|
||||||
|
path_owners: Mapping[str, str] | None = None,
|
||||||
) -> tuple[dict[str, Any], dict[str, Any]]:
|
) -> tuple[dict[str, Any], dict[str, Any]]:
|
||||||
module_manifest = build_module_manifest(root, module_template, path_list)
|
module_manifest = build_module_manifest(
|
||||||
|
root,
|
||||||
|
module_template,
|
||||||
|
path_list,
|
||||||
|
path_owners=path_owners,
|
||||||
|
)
|
||||||
module_raw = _canonical_bytes(module_manifest)
|
module_raw = _canonical_bytes(module_manifest)
|
||||||
parent_release = build_parent_release(root, parent_template, module_raw)
|
parent_release = build_parent_release(root, parent_template, module_raw)
|
||||||
return module_manifest, parent_release
|
return module_manifest, parent_release
|
||||||
@@ -445,6 +695,13 @@ def _write_bytes(path: Path, raw: bytes) -> None:
|
|||||||
os.replace(temporary, path)
|
os.replace(temporary, path)
|
||||||
|
|
||||||
|
|
||||||
|
def _owner_stage_from_path_list(path: Path) -> str:
|
||||||
|
match = re.fullmatch(r"s2_(20|30|40)_parent_member_paths\.json", path.name)
|
||||||
|
if match is None:
|
||||||
|
raise ReleaseBuildError(f"PARENT_PATH_LIST_OWNER_UNRESOLVED:{path.as_posix()}")
|
||||||
|
return f"S2_{match.group(1)}"
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Reseal canonical Stage-2 module and parent release manifests"
|
description="Reseal canonical Stage-2 module and parent release manifests"
|
||||||
@@ -476,20 +733,22 @@ def main() -> int:
|
|||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
path_lists: list[list[str]] = []
|
path_lists: list[tuple[str, list[str]]] = []
|
||||||
for source in [args.path_list, *args.additional_path_list]:
|
for source in [args.path_list, *args.additional_path_list]:
|
||||||
loaded = _load_json(source)
|
loaded = _load_json(source)
|
||||||
if not isinstance(loaded, list) or not all(isinstance(row, str) for row in loaded):
|
if not isinstance(loaded, list) or not all(isinstance(row, str) for row in loaded):
|
||||||
raise ReleaseBuildError(f"PATH_LIST_MUST_BE_STRING_ARRAY:{source.as_posix()}")
|
raise ReleaseBuildError(f"PATH_LIST_MUST_BE_STRING_ARRAY:{source.as_posix()}")
|
||||||
path_lists.append(loaded)
|
path_lists.append((_owner_stage_from_path_list(source), loaded))
|
||||||
seen_paths: set[str] = set()
|
seen_paths: set[str] = set()
|
||||||
path_list: list[str] = []
|
path_list: list[str] = []
|
||||||
for rows in path_lists:
|
path_owners: dict[str, str] = {}
|
||||||
|
for owner_stage, rows in path_lists:
|
||||||
overlap = sorted(seen_paths & set(rows))
|
overlap = sorted(seen_paths & set(rows))
|
||||||
if overlap:
|
if overlap:
|
||||||
raise ReleaseBuildError(f"PARENT_PATH_LISTS_MUST_BE_DISJOINT:{overlap}")
|
raise ReleaseBuildError(f"PARENT_PATH_LISTS_MUST_BE_DISJOINT:{overlap}")
|
||||||
seen_paths.update(rows)
|
seen_paths.update(rows)
|
||||||
path_list.extend(rows)
|
path_list.extend(rows)
|
||||||
|
path_owners.update({row: owner_stage for row in rows})
|
||||||
path_list.sort()
|
path_list.sort()
|
||||||
module_template_path = args.module_template or args.root / "manifest/module_manifest.json"
|
module_template_path = args.module_template or args.root / "manifest/module_manifest.json"
|
||||||
parent_template_path = args.parent_template or args.root / "manifest/stage2_release.json"
|
parent_template_path = args.parent_template or args.root / "manifest/stage2_release.json"
|
||||||
@@ -499,6 +758,7 @@ def main() -> int:
|
|||||||
path_list,
|
path_list,
|
||||||
_load_json(module_template_path),
|
_load_json(module_template_path),
|
||||||
_load_json(parent_template_path),
|
_load_json(parent_template_path),
|
||||||
|
path_owners=path_owners,
|
||||||
)
|
)
|
||||||
_write_bytes(args.output, _canonical_bytes(module_manifest))
|
_write_bytes(args.output, _canonical_bytes(module_manifest))
|
||||||
_write_bytes(parent_output, _canonical_bytes(parent_release))
|
_write_bytes(parent_output, _canonical_bytes(parent_release))
|
||||||
|
|||||||
+278
-18
@@ -15,7 +15,8 @@ import hashlib
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from pathlib import Path, PurePosixPath
|
from pathlib import Path, PurePosixPath
|
||||||
from typing import Any, Iterable
|
import re
|
||||||
|
from typing import Any, Iterable, Mapping
|
||||||
|
|
||||||
|
|
||||||
MODULE_MANIFEST_SCHEMA = "stage2_module_manifest.v1"
|
MODULE_MANIFEST_SCHEMA = "stage2_module_manifest.v1"
|
||||||
@@ -53,9 +54,145 @@ FORBIDDEN_PARENT_MEMBERS = frozenset(
|
|||||||
"manifest/s2_30_model_benchmark_receipt.json",
|
"manifest/s2_30_model_benchmark_receipt.json",
|
||||||
"manifest/s2_30_legal_review_receipt.json",
|
"manifest/s2_30_legal_review_receipt.json",
|
||||||
"manifest/s2_30_release.json",
|
"manifest/s2_30_release.json",
|
||||||
|
"agent_scripts/Stage_2_S2_40.yml",
|
||||||
|
"runtime/s2_40_commit.py",
|
||||||
|
"runtime/s2_40_commit.txt",
|
||||||
|
"manifest/s2_40_inline_code_receipt.json",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
STAGE_GENERIC_METADATA = {
|
||||||
|
"S2_20": {
|
||||||
|
"asset_version": "s2_20.1",
|
||||||
|
"module_id_prefix": "S2_20-ASSET",
|
||||||
|
"owner": "Stage_2_S2_20_owner",
|
||||||
|
"schema_version": "stage2_s2_20_generic_asset.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_20",
|
||||||
|
},
|
||||||
|
"S2_30": {
|
||||||
|
"asset_version": "s2_30.1",
|
||||||
|
"module_id_prefix": "S2_30-ASSET",
|
||||||
|
"owner": "Stage_2_S2_30_owner",
|
||||||
|
"schema_version": "stage2_s2_30_generic_asset.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_30",
|
||||||
|
},
|
||||||
|
"S2_40": {
|
||||||
|
"asset_version": "s2_40.1",
|
||||||
|
"module_id_prefix": "S2_40-ASSET",
|
||||||
|
"owner": "Stage_2_S2_40_owner",
|
||||||
|
"schema_version": "stage2_s2_40_generic_asset.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_40",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
S2_40_WORKFLOW_PATH = "workflows/S2_40_final_review_render_and_commit.yml"
|
||||||
|
S2_40_TRANSITIONED_STUB_VALUES = frozenset(
|
||||||
|
{
|
||||||
|
"WF-S2_40-STATUS-ONLY",
|
||||||
|
"s2_40.status_only.1",
|
||||||
|
"DRAFT_HANDOFF_STUB_HASH_BOUND",
|
||||||
|
"entrypoint_id == S2_40_STATUS_ONLY",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
S2_40_WORKFLOW_METADATA: dict[str, Any] = {
|
||||||
|
"asset_version": "s2_40.finalizer.1",
|
||||||
|
"authority_ids": [],
|
||||||
|
"consumed_schema_ids": [
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_00/context.schema.v2.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_00/ingress.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_10/s2_10.schema.v2.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_20/binding_retrieval.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_20/calculation.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_20/relief_plan.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_30/draft_atoms.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_40/package.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/shared/deployment.schema.v3.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/shared/review_status.schema.v1.json",
|
||||||
|
],
|
||||||
|
"dependency_module_ids": [
|
||||||
|
"SCHEMA-CONTEXT",
|
||||||
|
"SCHEMA-DEPLOYMENT",
|
||||||
|
"SCHEMA-INGRESS",
|
||||||
|
"SCHEMA-REVIEW-STATUS",
|
||||||
|
"SCHEMA-S2_10",
|
||||||
|
"S2_20-ASSET-809C3C709B4F2295",
|
||||||
|
"S2_20-ASSET-AA40CF5140DE8790",
|
||||||
|
"S2_20-ASSET-C9FC917D8CBF920E",
|
||||||
|
"S2_20-ASSET-E82839694073D431",
|
||||||
|
"S2_20-ASSET-FF1F03B3FC56E579",
|
||||||
|
"S2_30-ASSET-6A9B1F5EC854740E",
|
||||||
|
"S2_30-ASSET-E1FCF8BD63300F4D",
|
||||||
|
"S2_40-ASSET-5C4CAFFAB8D2F982",
|
||||||
|
"WF-S2_00",
|
||||||
|
"WF-S2_10",
|
||||||
|
],
|
||||||
|
"entry_routes": ["TO_S2_40", "TO_S2_40_STATUS_ONLY"],
|
||||||
|
"forbidden_contract_codes": [
|
||||||
|
"LEGACY-STAGE2-V0-V3",
|
||||||
|
"S2_40-DIRECTORY-SCAN",
|
||||||
|
"S2_40-FREE-TEXT-RENDER-FALLBACK",
|
||||||
|
"S2_40-LLM-CALL",
|
||||||
|
"S2_40-RUN-STATUS-NOT-LAST",
|
||||||
|
"S2_40-UNVERIFIED-READY",
|
||||||
|
],
|
||||||
|
"implementation_status": "IMPLEMENTED_OFFLINE_CONTRACT_LIVE_ADMISSION_PENDING",
|
||||||
|
"incompatible_module_ids": [],
|
||||||
|
"inputs": [
|
||||||
|
"ingress/ingress_status.json",
|
||||||
|
"ingress/technical_diagnostic.json",
|
||||||
|
"ingress/stage1_input_manifest.json",
|
||||||
|
"ingress/intake_report.json",
|
||||||
|
"review/issue_ledger.base.json",
|
||||||
|
"map_s2_10/s2_10_publish_status.json",
|
||||||
|
"plan/plan_publish_status.json",
|
||||||
|
"plan/canonical_relief_plan.json",
|
||||||
|
"plan/claim_groups.json",
|
||||||
|
"plan/case_type_bindings.json",
|
||||||
|
"map_s2_30/s2_30_publish_status.json",
|
||||||
|
"map_s2_30/artifact_manifest.json",
|
||||||
|
"map_s2_30/{draft_parts,issue_patches,worknote_parts,usage_parts}/<claim_group_id>.json",
|
||||||
|
"review/review_receipts/<request_id>.json",
|
||||||
|
"review/lawyer_judgment_record.json",
|
||||||
|
],
|
||||||
|
"legal_admission_status": "PENDING",
|
||||||
|
"live_admission_status": "PENDING",
|
||||||
|
"module_id": "WF-S2_40",
|
||||||
|
"module_kind": "WORKFLOW",
|
||||||
|
"outputs": [
|
||||||
|
"review/issue_ledger.final.json",
|
||||||
|
"review/assumption_ledger.json",
|
||||||
|
"review/llm_usage.jsonl",
|
||||||
|
"candidates/by-content-digest/<candidate_content_digest>/",
|
||||||
|
"review/review_requests/<request_id>.json",
|
||||||
|
"final/claim_relief.md",
|
||||||
|
"final/claim_cause.md",
|
||||||
|
"final/pleading_draft.md",
|
||||||
|
"final/stage2_package.json",
|
||||||
|
"commit/commit_intent.json",
|
||||||
|
"commit/stage2_commit_result.json",
|
||||||
|
"control/run_status.json",
|
||||||
|
],
|
||||||
|
"owner": "Stage_2_S2_40_owner",
|
||||||
|
"path": S2_40_WORKFLOW_PATH,
|
||||||
|
"produced_schema_ids": [
|
||||||
|
"stage2_s2_40_candidate_manifest.v1",
|
||||||
|
"stage2_s2_40_commit_intent.v1",
|
||||||
|
"stage2_s2_40_commit_result.v1",
|
||||||
|
"stage2_s2_40_package.v1",
|
||||||
|
"stage2_s2_40_run_status.v1",
|
||||||
|
],
|
||||||
|
"schema_version": "stage2_workflow_contract.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_40 and entry_route in {TO_S2_40,TO_S2_40_STATUS_ONLY}",
|
||||||
|
"semantic_review_status": "PENDING_LEGAL_AND_LIVE_ADMISSION",
|
||||||
|
"status_only_exact_inputs": [
|
||||||
|
"ingress/ingress_status.json",
|
||||||
|
"ingress/technical_diagnostic.json",
|
||||||
|
"ingress/stage1_input_manifest.json",
|
||||||
|
"ingress/intake_report.json",
|
||||||
|
"review/issue_ledger.base.json",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ReleaseBuildError(ValueError):
|
class ReleaseBuildError(ValueError):
|
||||||
"""Raised when a release input violates the canonical closure contract."""
|
"""Raised when a release input violates the canonical closure contract."""
|
||||||
@@ -151,7 +288,7 @@ def _mirror_for(root: Path, source_path: str) -> tuple[str, bytes] | None:
|
|||||||
return mirror_path, mirror_raw
|
return mirror_path, mirror_raw
|
||||||
|
|
||||||
|
|
||||||
def _generic_kind(relative: str) -> str:
|
def _generic_kind(relative: str, owner_stage: str = "S2_20") -> str:
|
||||||
if relative.startswith("tests/"):
|
if relative.startswith("tests/"):
|
||||||
return "TEST"
|
return "TEST"
|
||||||
if relative.startswith("schemas/"):
|
if relative.startswith("schemas/"):
|
||||||
@@ -168,13 +305,16 @@ def _generic_kind(relative: str) -> str:
|
|||||||
return "MANIFEST"
|
return "MANIFEST"
|
||||||
if relative.startswith("registry/"):
|
if relative.startswith("registry/"):
|
||||||
return "DECLARATIVE_REGISTRY"
|
return "DECLARATIVE_REGISTRY"
|
||||||
return "S2_20_ASSET"
|
return f"{owner_stage}_ASSET"
|
||||||
|
|
||||||
|
|
||||||
def _generic_module_row(relative: str) -> dict[str, Any]:
|
def _generic_module_row(relative: str, owner_stage: str = "S2_20") -> dict[str, Any]:
|
||||||
|
metadata = STAGE_GENERIC_METADATA.get(owner_stage)
|
||||||
|
if metadata is None:
|
||||||
|
raise ReleaseBuildError(f"UNSUPPORTED_PARENT_MEMBER_OWNER_STAGE:{owner_stage}")
|
||||||
identity = hashlib.sha256(relative.encode("utf-8")).hexdigest()[:16].upper()
|
identity = hashlib.sha256(relative.encode("utf-8")).hexdigest()[:16].upper()
|
||||||
return {
|
return {
|
||||||
"asset_version": "s2_20.1",
|
"asset_version": metadata["asset_version"],
|
||||||
"authority_ids": [],
|
"authority_ids": [],
|
||||||
"consumed_schema_ids": [],
|
"consumed_schema_ids": [],
|
||||||
"dependency_module_ids": [],
|
"dependency_module_ids": [],
|
||||||
@@ -182,18 +322,70 @@ def _generic_module_row(relative: str) -> dict[str, Any]:
|
|||||||
"implementation_status": "DEV_HASH_BOUND_OFFLINE_ONLY",
|
"implementation_status": "DEV_HASH_BOUND_OFFLINE_ONLY",
|
||||||
"incompatible_module_ids": [],
|
"incompatible_module_ids": [],
|
||||||
"inputs": [],
|
"inputs": [],
|
||||||
"module_id": f"S2_20-ASSET-{identity}",
|
"module_id": f"{metadata['module_id_prefix']}-{identity}",
|
||||||
"module_kind": _generic_kind(relative),
|
"module_kind": _generic_kind(relative, owner_stage),
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"owner": "Stage_2_S2_20_owner",
|
"owner": metadata["owner"],
|
||||||
"path": relative,
|
"path": relative,
|
||||||
"produced_schema_ids": [],
|
"produced_schema_ids": [],
|
||||||
"schema_version": "stage2_s2_20_generic_asset.v1",
|
"schema_version": metadata["schema_version"],
|
||||||
"scope_predicate": "workflow_id == S2_20",
|
"scope_predicate": metadata["scope_predicate"],
|
||||||
"semantic_review_status": "PENDING_LEGAL_AND_LIVE_ADMISSION",
|
"semantic_review_status": "PENDING_LEGAL_AND_LIVE_ADMISSION",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _s2_40_workflow_module_row() -> dict[str, Any]:
|
||||||
|
"""Return the authoritative full S2_40 workflow row.
|
||||||
|
|
||||||
|
The pre-S2_40 parent template carried a status-only stub row at the same
|
||||||
|
physical path. Preserving that semantic metadata while merely refreshing
|
||||||
|
its file hash would make the release closure describe a different workflow
|
||||||
|
than the bytes it seals.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return copy.deepcopy(S2_40_WORKFLOW_METADATA)
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_no_transitioned_s2_40_stub(rows: Iterable[Mapping[str, Any]]) -> None:
|
||||||
|
for row in rows:
|
||||||
|
if row.get("path") != S2_40_WORKFLOW_PATH:
|
||||||
|
continue
|
||||||
|
observed = {
|
||||||
|
row.get("module_id"),
|
||||||
|
row.get("asset_version"),
|
||||||
|
row.get("implementation_status"),
|
||||||
|
row.get("scope_predicate"),
|
||||||
|
}
|
||||||
|
stale = sorted(str(value) for value in observed & S2_40_TRANSITIONED_STUB_VALUES)
|
||||||
|
if stale:
|
||||||
|
raise ReleaseBuildError(f"S2_40_TRANSITIONED_STUB_METADATA_FORBIDDEN:{stale}")
|
||||||
|
drift = {
|
||||||
|
key: {"expected": expected, "observed": row.get(key)}
|
||||||
|
for key, expected in S2_40_WORKFLOW_METADATA.items()
|
||||||
|
if row.get(key) != expected
|
||||||
|
}
|
||||||
|
if drift:
|
||||||
|
raise ReleaseBuildError(
|
||||||
|
"S2_40_WORKFLOW_METADATA_MISMATCH:"
|
||||||
|
+ json.dumps(drift, ensure_ascii=False, sort_keys=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_generic_module_row(row: Mapping[str, Any]) -> bool:
|
||||||
|
schema_version = row.get("schema_version")
|
||||||
|
module_id = row.get("module_id")
|
||||||
|
return (
|
||||||
|
schema_version in {
|
||||||
|
metadata["schema_version"] for metadata in STAGE_GENERIC_METADATA.values()
|
||||||
|
}
|
||||||
|
or isinstance(module_id, str)
|
||||||
|
and any(
|
||||||
|
module_id.startswith(f"{metadata['module_id_prefix']}-")
|
||||||
|
for metadata in STAGE_GENERIC_METADATA.values()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _refresh_module_row(root: Path, row: dict[str, Any]) -> dict[str, Any]:
|
def _refresh_module_row(root: Path, row: dict[str, Any]) -> dict[str, Any]:
|
||||||
refreshed = copy.deepcopy(row)
|
refreshed = copy.deepcopy(row)
|
||||||
relative = _relative_path(refreshed.get("path"))
|
relative = _relative_path(refreshed.get("path"))
|
||||||
@@ -234,8 +426,9 @@ def build_module_manifest(
|
|||||||
root: Path,
|
root: Path,
|
||||||
template: dict[str, Any],
|
template: dict[str, Any],
|
||||||
explicit_paths: Iterable[str],
|
explicit_paths: Iterable[str],
|
||||||
|
path_owners: Mapping[str, str] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Preserve canonical rows, refresh physical bindings and append S2_20 rows."""
|
"""Refresh canonical rows and append stage-owned generic source rows."""
|
||||||
|
|
||||||
if template.get("schema_version") != MODULE_MANIFEST_SCHEMA:
|
if template.get("schema_version") != MODULE_MANIFEST_SCHEMA:
|
||||||
raise ReleaseBuildError("CANONICAL_MODULE_MANIFEST_V1_REQUIRED")
|
raise ReleaseBuildError("CANONICAL_MODULE_MANIFEST_V1_REQUIRED")
|
||||||
@@ -249,6 +442,14 @@ def build_module_manifest(
|
|||||||
forbidden = sorted(set(normalized_explicit) & FORBIDDEN_PARENT_MEMBERS)
|
forbidden = sorted(set(normalized_explicit) & FORBIDDEN_PARENT_MEMBERS)
|
||||||
if forbidden:
|
if forbidden:
|
||||||
raise ReleaseBuildError(f"NON_CYCLIC_PARENT_VIOLATION:{forbidden}")
|
raise ReleaseBuildError(f"NON_CYCLIC_PARENT_VIOLATION:{forbidden}")
|
||||||
|
normalized_owners: dict[str, str] = {}
|
||||||
|
for raw_path, owner_stage in (path_owners or {}).items():
|
||||||
|
relative = _relative_path(raw_path)
|
||||||
|
if relative not in normalized_explicit:
|
||||||
|
raise ReleaseBuildError(f"OWNER_PATH_NOT_IN_EXPLICIT_CLOSURE:{relative}")
|
||||||
|
if owner_stage not in STAGE_GENERIC_METADATA:
|
||||||
|
raise ReleaseBuildError(f"UNSUPPORTED_PARENT_MEMBER_OWNER_STAGE:{owner_stage}")
|
||||||
|
normalized_owners[relative] = owner_stage
|
||||||
|
|
||||||
result = copy.deepcopy(template)
|
result = copy.deepcopy(template)
|
||||||
refreshed_rows: list[dict[str, Any]] = []
|
refreshed_rows: list[dict[str, Any]] = []
|
||||||
@@ -263,9 +464,28 @@ def build_module_manifest(
|
|||||||
raise ReleaseBuildError(f"MODULE_ID_INVALID_OR_DUPLICATE:{module_id!r}")
|
raise ReleaseBuildError(f"MODULE_ID_INVALID_OR_DUPLICATE:{module_id!r}")
|
||||||
if relative in seen_paths:
|
if relative in seen_paths:
|
||||||
raise ReleaseBuildError(f"MODULE_PATH_DUPLICATE:{relative}")
|
raise ReleaseBuildError(f"MODULE_PATH_DUPLICATE:{relative}")
|
||||||
seen_ids.add(module_id)
|
owner_stage = normalized_owners.get(relative)
|
||||||
|
if relative == S2_40_WORKFLOW_PATH:
|
||||||
|
if owner_stage not in {None, "S2_40"}:
|
||||||
|
raise ReleaseBuildError(
|
||||||
|
f"S2_40_WORKFLOW_OWNER_STAGE_MISMATCH:{owner_stage}"
|
||||||
|
)
|
||||||
|
candidate = _s2_40_workflow_module_row()
|
||||||
|
else:
|
||||||
|
candidate = (
|
||||||
|
_generic_module_row(relative, owner_stage)
|
||||||
|
if owner_stage is not None and _is_generic_module_row(value)
|
||||||
|
else value
|
||||||
|
)
|
||||||
|
refreshed = _refresh_module_row(root, candidate)
|
||||||
|
refreshed_id = refreshed.get("module_id")
|
||||||
|
if not isinstance(refreshed_id, str) or not refreshed_id:
|
||||||
|
raise ReleaseBuildError(f"MODULE_ID_INVALID_OR_DUPLICATE:{refreshed_id!r}")
|
||||||
|
if refreshed_id in seen_ids:
|
||||||
|
raise ReleaseBuildError(f"MODULE_ID_INVALID_OR_DUPLICATE:{refreshed_id!r}")
|
||||||
|
seen_ids.add(refreshed_id)
|
||||||
seen_paths.add(relative)
|
seen_paths.add(relative)
|
||||||
refreshed_rows.append(_refresh_module_row(root, value))
|
refreshed_rows.append(refreshed)
|
||||||
|
|
||||||
explicit_set = set(normalized_explicit)
|
explicit_set = set(normalized_explicit)
|
||||||
for relative in sorted(normalized_explicit):
|
for relative in sorted(normalized_explicit):
|
||||||
@@ -278,13 +498,19 @@ def build_module_manifest(
|
|||||||
continue
|
continue
|
||||||
if relative in seen_paths:
|
if relative in seen_paths:
|
||||||
continue
|
continue
|
||||||
row = _refresh_module_row(root, _generic_module_row(relative))
|
candidate = (
|
||||||
|
_s2_40_workflow_module_row()
|
||||||
|
if relative == S2_40_WORKFLOW_PATH
|
||||||
|
else _generic_module_row(relative, normalized_owners.get(relative, "S2_20"))
|
||||||
|
)
|
||||||
|
row = _refresh_module_row(root, candidate)
|
||||||
if row["module_id"] in seen_ids:
|
if row["module_id"] in seen_ids:
|
||||||
raise ReleaseBuildError(f"GENERATED_MODULE_ID_COLLISION:{row['module_id']}")
|
raise ReleaseBuildError(f"GENERATED_MODULE_ID_COLLISION:{row['module_id']}")
|
||||||
seen_ids.add(row["module_id"])
|
seen_ids.add(row["module_id"])
|
||||||
seen_paths.add(relative)
|
seen_paths.add(relative)
|
||||||
refreshed_rows.append(row)
|
refreshed_rows.append(row)
|
||||||
|
|
||||||
|
_assert_no_transitioned_s2_40_stub(refreshed_rows)
|
||||||
result["modules"] = refreshed_rows
|
result["modules"] = refreshed_rows
|
||||||
mirrors = [entry for row in refreshed_rows if (entry := _documentation_mirror(row))]
|
mirrors = [entry for row in refreshed_rows if (entry := _documentation_mirror(row))]
|
||||||
mirror_sources = [str(entry["source_path"]) for entry in mirrors]
|
mirror_sources = [str(entry["source_path"]) for entry in mirrors]
|
||||||
@@ -312,6 +538,24 @@ def build_module_manifest(
|
|||||||
for row in refreshed_rows
|
for row in refreshed_rows
|
||||||
if row.get("schema_version") == "stage2_s2_20_generic_asset.v1"
|
if row.get("schema_version") == "stage2_s2_20_generic_asset.v1"
|
||||||
),
|
),
|
||||||
|
"s2_30_generic_module_count": sum(
|
||||||
|
1
|
||||||
|
for row in refreshed_rows
|
||||||
|
if row.get("schema_version") == "stage2_s2_30_generic_asset.v1"
|
||||||
|
),
|
||||||
|
"s2_40_generic_module_count": sum(
|
||||||
|
1
|
||||||
|
for row in refreshed_rows
|
||||||
|
if row.get("schema_version") == "stage2_s2_40_generic_asset.v1"
|
||||||
|
),
|
||||||
|
"generic_module_counts_by_stage": {
|
||||||
|
stage: sum(
|
||||||
|
1
|
||||||
|
for row in refreshed_rows
|
||||||
|
if row.get("schema_version") == metadata["schema_version"]
|
||||||
|
)
|
||||||
|
for stage, metadata in sorted(STAGE_GENERIC_METADATA.items())
|
||||||
|
},
|
||||||
"executable_agent_hash_included": False,
|
"executable_agent_hash_included": False,
|
||||||
"executor_binding_hash_included": False,
|
"executor_binding_hash_included": False,
|
||||||
"inline_runtime_code_hash_included": False,
|
"inline_runtime_code_hash_included": False,
|
||||||
@@ -431,8 +675,14 @@ def build_release_package(
|
|||||||
path_list: list[str],
|
path_list: list[str],
|
||||||
module_template: dict[str, Any],
|
module_template: dict[str, Any],
|
||||||
parent_template: dict[str, Any],
|
parent_template: dict[str, Any],
|
||||||
|
path_owners: Mapping[str, str] | None = None,
|
||||||
) -> tuple[dict[str, Any], dict[str, Any]]:
|
) -> tuple[dict[str, Any], dict[str, Any]]:
|
||||||
module_manifest = build_module_manifest(root, module_template, path_list)
|
module_manifest = build_module_manifest(
|
||||||
|
root,
|
||||||
|
module_template,
|
||||||
|
path_list,
|
||||||
|
path_owners=path_owners,
|
||||||
|
)
|
||||||
module_raw = _canonical_bytes(module_manifest)
|
module_raw = _canonical_bytes(module_manifest)
|
||||||
parent_release = build_parent_release(root, parent_template, module_raw)
|
parent_release = build_parent_release(root, parent_template, module_raw)
|
||||||
return module_manifest, parent_release
|
return module_manifest, parent_release
|
||||||
@@ -445,6 +695,13 @@ def _write_bytes(path: Path, raw: bytes) -> None:
|
|||||||
os.replace(temporary, path)
|
os.replace(temporary, path)
|
||||||
|
|
||||||
|
|
||||||
|
def _owner_stage_from_path_list(path: Path) -> str:
|
||||||
|
match = re.fullmatch(r"s2_(20|30|40)_parent_member_paths\.json", path.name)
|
||||||
|
if match is None:
|
||||||
|
raise ReleaseBuildError(f"PARENT_PATH_LIST_OWNER_UNRESOLVED:{path.as_posix()}")
|
||||||
|
return f"S2_{match.group(1)}"
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Reseal canonical Stage-2 module and parent release manifests"
|
description="Reseal canonical Stage-2 module and parent release manifests"
|
||||||
@@ -476,20 +733,22 @@ def main() -> int:
|
|||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
path_lists: list[list[str]] = []
|
path_lists: list[tuple[str, list[str]]] = []
|
||||||
for source in [args.path_list, *args.additional_path_list]:
|
for source in [args.path_list, *args.additional_path_list]:
|
||||||
loaded = _load_json(source)
|
loaded = _load_json(source)
|
||||||
if not isinstance(loaded, list) or not all(isinstance(row, str) for row in loaded):
|
if not isinstance(loaded, list) or not all(isinstance(row, str) for row in loaded):
|
||||||
raise ReleaseBuildError(f"PATH_LIST_MUST_BE_STRING_ARRAY:{source.as_posix()}")
|
raise ReleaseBuildError(f"PATH_LIST_MUST_BE_STRING_ARRAY:{source.as_posix()}")
|
||||||
path_lists.append(loaded)
|
path_lists.append((_owner_stage_from_path_list(source), loaded))
|
||||||
seen_paths: set[str] = set()
|
seen_paths: set[str] = set()
|
||||||
path_list: list[str] = []
|
path_list: list[str] = []
|
||||||
for rows in path_lists:
|
path_owners: dict[str, str] = {}
|
||||||
|
for owner_stage, rows in path_lists:
|
||||||
overlap = sorted(seen_paths & set(rows))
|
overlap = sorted(seen_paths & set(rows))
|
||||||
if overlap:
|
if overlap:
|
||||||
raise ReleaseBuildError(f"PARENT_PATH_LISTS_MUST_BE_DISJOINT:{overlap}")
|
raise ReleaseBuildError(f"PARENT_PATH_LISTS_MUST_BE_DISJOINT:{overlap}")
|
||||||
seen_paths.update(rows)
|
seen_paths.update(rows)
|
||||||
path_list.extend(rows)
|
path_list.extend(rows)
|
||||||
|
path_owners.update({row: owner_stage for row in rows})
|
||||||
path_list.sort()
|
path_list.sort()
|
||||||
module_template_path = args.module_template or args.root / "manifest/module_manifest.json"
|
module_template_path = args.module_template or args.root / "manifest/module_manifest.json"
|
||||||
parent_template_path = args.parent_template or args.root / "manifest/stage2_release.json"
|
parent_template_path = args.parent_template or args.root / "manifest/stage2_release.json"
|
||||||
@@ -499,6 +758,7 @@ def main() -> int:
|
|||||||
path_list,
|
path_list,
|
||||||
_load_json(module_template_path),
|
_load_json(module_template_path),
|
||||||
_load_json(parent_template_path),
|
_load_json(parent_template_path),
|
||||||
|
path_owners=path_owners,
|
||||||
)
|
)
|
||||||
_write_bytes(args.output, _canonical_bytes(module_manifest))
|
_write_bytes(args.output, _canonical_bytes(module_manifest))
|
||||||
_write_bytes(parent_output, _canonical_bytes(parent_release))
|
_write_bytes(parent_output, _canonical_bytes(parent_release))
|
||||||
|
|||||||
+216
-43
@@ -12,6 +12,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import importlib.util
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -45,6 +46,12 @@ MODULE_MANIFEST_PATH = DEPLOYMENT_ROOT / "manifest" / "module_manifest.json"
|
|||||||
PLATFORM_RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_10_platform_adapter_receipt.json"
|
PLATFORM_RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_10_platform_adapter_receipt.json"
|
||||||
MODEL_RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_10_model_benchmark_receipt.json"
|
MODEL_RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_10_model_benchmark_receipt.json"
|
||||||
LEGAL_RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_10_legal_review_receipt.json"
|
LEGAL_RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_10_legal_review_receipt.json"
|
||||||
|
RELEASE_BUILDER_PATH = DEPLOYMENT_ROOT / "offline_build" / "build_release_manifests.py"
|
||||||
|
CUMULATIVE_PARENT_PATH_LISTS = (
|
||||||
|
("S2_20", DEPLOYMENT_ROOT / "manifest" / "s2_20_parent_member_paths.json"),
|
||||||
|
("S2_30", DEPLOYMENT_ROOT / "manifest" / "s2_30_parent_member_paths.json"),
|
||||||
|
("S2_40", DEPLOYMENT_ROOT / "manifest" / "s2_40_parent_member_paths.json"),
|
||||||
|
)
|
||||||
|
|
||||||
EXPECTED_AGENT_NAME = "Stage_2_S2_10"
|
EXPECTED_AGENT_NAME = "Stage_2_S2_10"
|
||||||
EXPECTED_STAGE_NAME = "S2_10"
|
EXPECTED_STAGE_NAME = "S2_10"
|
||||||
@@ -276,6 +283,75 @@ def _load_json(path: Path) -> dict[str, Any]:
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _load_release_builder() -> Any:
|
||||||
|
spec = importlib.util.spec_from_file_location(
|
||||||
|
"stage2_cumulative_release_builder",
|
||||||
|
RELEASE_BUILDER_PATH,
|
||||||
|
)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise BuildError("CUMULATIVE_RELEASE_BUILDER_IMPORT_FAILED", _logical_path(RELEASE_BUILDER_PATH))
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def cumulative_parent_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
|
"""Recompute the current owner-aware cumulative parent in memory only.
|
||||||
|
|
||||||
|
S2_10 no longer owns ``module_manifest.json`` or ``stage2_release.json``.
|
||||||
|
This oracle delegates to the canonical S2_20+S2_30+S2_40 builder and is
|
||||||
|
used solely to detect drift; it never writes either parent file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
release_builder = _load_release_builder()
|
||||||
|
combined: list[str] = []
|
||||||
|
path_owners: dict[str, str] = {}
|
||||||
|
seen: set[str] = set()
|
||||||
|
counts: dict[str, int] = {}
|
||||||
|
for owner_stage, source in CUMULATIVE_PARENT_PATH_LISTS:
|
||||||
|
raw = _require_file(source)
|
||||||
|
try:
|
||||||
|
values = json.loads(raw.decode("utf-8"))
|
||||||
|
except json.JSONDecodeError as exc:
|
||||||
|
raise BuildError("CUMULATIVE_PARENT_PATH_LIST_INVALID", f"{_logical_path(source)}: {exc}") from exc
|
||||||
|
if not isinstance(values, list) or not all(isinstance(value, str) for value in values):
|
||||||
|
raise BuildError("CUMULATIVE_PARENT_PATH_LIST_INVALID", _logical_path(source))
|
||||||
|
if values != sorted(values) or len(values) != len(set(values)):
|
||||||
|
raise BuildError("CUMULATIVE_PARENT_PATH_LIST_NOT_SORTED_UNIQUE", _logical_path(source))
|
||||||
|
overlap = sorted(seen & set(values))
|
||||||
|
if overlap:
|
||||||
|
raise BuildError("CUMULATIVE_PARENT_PATH_LIST_OVERLAP", repr(overlap))
|
||||||
|
seen.update(values)
|
||||||
|
combined.extend(values)
|
||||||
|
path_owners.update({value: owner_stage for value in values})
|
||||||
|
counts[owner_stage] = len(values)
|
||||||
|
combined.sort()
|
||||||
|
try:
|
||||||
|
module, parent = release_builder.build_release_package(
|
||||||
|
DEPLOYMENT_ROOT,
|
||||||
|
combined,
|
||||||
|
_load_json(MODULE_MANIFEST_PATH),
|
||||||
|
_load_json(PARENT_RELEASE_PATH),
|
||||||
|
path_owners=path_owners,
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
raise BuildError(
|
||||||
|
"CUMULATIVE_PARENT_ORACLE_FAILED",
|
||||||
|
f"{type(exc).__name__}:{exc}",
|
||||||
|
) from exc
|
||||||
|
module_raw = release_builder._canonical_bytes(module)
|
||||||
|
parent_raw = release_builder._canonical_bytes(parent)
|
||||||
|
return {
|
||||||
|
MODULE_MANIFEST_PATH: module_raw,
|
||||||
|
PARENT_RELEASE_PATH: parent_raw,
|
||||||
|
}, {
|
||||||
|
"owner_path_counts": counts,
|
||||||
|
"module_count": len(module["modules"]),
|
||||||
|
"module_manifest_sha256": sha256_bytes(module_raw),
|
||||||
|
"parent_release_sha256": sha256_bytes(parent_raw),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _mapping(value: Any, code: str) -> dict[str, Any]:
|
def _mapping(value: Any, code: str) -> dict[str, Any]:
|
||||||
if not isinstance(value, dict):
|
if not isinstance(value, dict):
|
||||||
raise BuildError(code, repr(value)[:200])
|
raise BuildError(code, repr(value)[:200])
|
||||||
@@ -1221,31 +1297,8 @@ def _atomic_write(path: Path, payload: bytes) -> None:
|
|||||||
temporary.unlink()
|
temporary.unlink()
|
||||||
|
|
||||||
|
|
||||||
def build() -> dict[str, Any]:
|
def _compare_outputs(outputs: Mapping[Path, bytes]) -> list[dict[str, Any]]:
|
||||||
# Phase 1 closes the authoring projection, inline receipt and binding.
|
drift: list[dict[str, Any]] = []
|
||||||
outputs, report = expected_outputs()
|
|
||||||
for path, payload in outputs.items():
|
|
||||||
_atomic_write(path, payload)
|
|
||||||
if report["phase"] in {"PREREQUISITE_PROJECTION", "PREREQUISITE_MODULE_MANIFEST"}:
|
|
||||||
outputs, report = expected_outputs()
|
|
||||||
for path, payload in outputs.items():
|
|
||||||
_atomic_write(path, payload)
|
|
||||||
if report["phase"] == "PREREQUISITE_MODULE_MANIFEST":
|
|
||||||
outputs, report = expected_outputs()
|
|
||||||
for path, payload in outputs.items():
|
|
||||||
_atomic_write(path, payload)
|
|
||||||
if report["phase"] != "COMPLETE":
|
|
||||||
raise BuildError("BUILD_DID_NOT_REACH_COMPLETE_PHASE", repr(report))
|
|
||||||
return {
|
|
||||||
"status": "BUILT_OFFLINE_HYBRID_PACKAGE",
|
|
||||||
"written_paths": [_logical_path(path) for path in outputs],
|
|
||||||
**report,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def check() -> dict[str, Any]:
|
|
||||||
outputs, report = expected_outputs()
|
|
||||||
drift = []
|
|
||||||
for path, expected in outputs.items():
|
for path, expected in outputs.items():
|
||||||
observed = path.read_bytes() if path.is_file() and not path.is_symlink() else None
|
observed = path.read_bytes() if path.is_file() and not path.is_symlink() else None
|
||||||
if observed != expected:
|
if observed != expected:
|
||||||
@@ -1256,31 +1309,100 @@ def check() -> dict[str, Any]:
|
|||||||
"observed_sha256": sha256_bytes(observed) if observed is not None else None,
|
"observed_sha256": sha256_bytes(observed) if observed is not None else None,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if report["phase"] != "COMPLETE" or drift:
|
return drift
|
||||||
raise BuildError("OFFLINE_PACKAGE_DRIFT", json.dumps({"report": report, "drift": drift}, ensure_ascii=False))
|
|
||||||
|
|
||||||
|
def _current_cumulative_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
|
"""Return the complete S2_10-owned and cumulative downstream oracle.
|
||||||
|
|
||||||
|
The owner-aware release builder is the sole producer of the cumulative
|
||||||
|
module manifest and parent release. The legacy ``expected_outputs``
|
||||||
|
function remains only as a compatibility helper for older callers; the
|
||||||
|
default build/check path must never use its S2_10-only parent projection.
|
||||||
|
"""
|
||||||
|
|
||||||
|
prerequisite, prerequisite_report = prerequisite_outputs()
|
||||||
|
parent, parent_report = cumulative_parent_outputs()
|
||||||
|
child, child_report = cumulative_child_outputs(prerequisite, parent)
|
||||||
|
overlap = (set(prerequisite) & set(parent)) | (set(prerequisite) & set(child)) | (set(parent) & set(child))
|
||||||
|
if overlap:
|
||||||
|
raise BuildError(
|
||||||
|
"CUMULATIVE_OUTPUT_PATH_OVERLAP",
|
||||||
|
repr(sorted(_logical_path(path) for path in overlap)),
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
"status": "OFFLINE_HYBRID_PACKAGE_CHECK_PASS",
|
**prerequisite,
|
||||||
|
**parent,
|
||||||
|
**child,
|
||||||
|
}, {
|
||||||
|
"prerequisite": prerequisite_report,
|
||||||
|
"cumulative_parent": parent_report,
|
||||||
|
"downstream_child": child_report,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build() -> dict[str, Any]:
|
||||||
|
"""Rebuild S2_10-owned artifacts without rewriting the cumulative parent.
|
||||||
|
|
||||||
|
A cumulative parent drift requires the owner-aware release builder and its
|
||||||
|
explicit reseal sequence. Silently rebuilding an obsolete S2_10-only
|
||||||
|
parent here would discard downstream S2_20/S2_30/S2_40 closure.
|
||||||
|
"""
|
||||||
|
|
||||||
|
prerequisite, prerequisite_report = prerequisite_outputs()
|
||||||
|
for path, payload in prerequisite.items():
|
||||||
|
_atomic_write(path, payload)
|
||||||
|
|
||||||
|
parent, parent_report = cumulative_parent_outputs()
|
||||||
|
parent_drift = _compare_outputs(parent)
|
||||||
|
if parent_drift:
|
||||||
|
raise BuildError(
|
||||||
|
"CUMULATIVE_PARENT_RESEAL_REQUIRED",
|
||||||
|
json.dumps(
|
||||||
|
{"report": parent_report, "drift": parent_drift},
|
||||||
|
ensure_ascii=False,
|
||||||
|
sort_keys=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
child, child_report = child_only_outputs()
|
||||||
|
for path, payload in child.items():
|
||||||
|
_atomic_write(path, payload)
|
||||||
|
return {
|
||||||
|
"status": "S2_10_OWNED_ARTIFACTS_REBUILT_CUMULATIVE_PARENT_PRESERVED",
|
||||||
|
"written_paths": [_logical_path(path) for path in {**prerequisite, **child}],
|
||||||
|
"prerequisite": prerequisite_report,
|
||||||
|
"cumulative_parent": parent_report,
|
||||||
|
"downstream_child": child_report,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def check() -> dict[str, Any]:
|
||||||
|
outputs, report = _current_cumulative_outputs()
|
||||||
|
drift = _compare_outputs(outputs)
|
||||||
|
if drift:
|
||||||
|
raise BuildError(
|
||||||
|
"OFFLINE_PACKAGE_DRIFT",
|
||||||
|
json.dumps({"report": report, "drift": drift}, ensure_ascii=False, sort_keys=True),
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"status": "S2_10_CUMULATIVE_PACKAGE_CHECK_PASS",
|
||||||
"checked_paths": [_logical_path(path) for path in outputs],
|
"checked_paths": [_logical_path(path) for path in outputs],
|
||||||
**report,
|
**report,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def child_only_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
def _downstream_child_outputs(
|
||||||
"""Reseal only the downstream S2_10 child chain against a frozen parent.
|
*,
|
||||||
|
authoring_raw: bytes,
|
||||||
The canonical parent/module builder owns the raw parent closure. This mode
|
binding_raw: bytes,
|
||||||
therefore never rewrites ``module_manifest.json``, ``stage2_release.json``,
|
inline_receipt_raw: bytes,
|
||||||
the Agent projection, prompt receipt, or LLM binding.
|
parent_raw: bytes,
|
||||||
"""
|
) -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
|
contract = extract_agent_contract(
|
||||||
authoring_raw = _require_file(AUTHORING_PATH)
|
_load_yaml(authoring_raw, _logical_path(AUTHORING_PATH))
|
||||||
if _require_file(PROJECTION_PATH) != authoring_raw:
|
)
|
||||||
raise BuildError("S2_10_AGENT_PROJECTION_DRIFT", _logical_path(PROJECTION_PATH))
|
|
||||||
contract = extract_agent_contract(_load_yaml(authoring_raw, _logical_path(AUTHORING_PATH)))
|
|
||||||
binding_raw = _require_file(BINDING_PATH)
|
|
||||||
binding = _load_yaml(binding_raw, _logical_path(BINDING_PATH))
|
binding = _load_yaml(binding_raw, _logical_path(BINDING_PATH))
|
||||||
inline_receipt_raw = _require_file(INLINE_RECEIPT_PATH)
|
|
||||||
parent_raw = _require_file(PARENT_RELEASE_PATH)
|
|
||||||
child = build_child_release(parent_raw, binding)
|
child = build_child_release(parent_raw, binding)
|
||||||
child_raw = canonical_json_bytes(child)
|
child_raw = canonical_json_bytes(child)
|
||||||
outputs = {
|
outputs = {
|
||||||
@@ -1310,6 +1432,57 @@ def child_only_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def cumulative_child_outputs(
|
||||||
|
prerequisite: Mapping[Path, bytes],
|
||||||
|
parent: Mapping[Path, bytes],
|
||||||
|
) -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
|
"""Build downstream receipts from the expected current parent material.
|
||||||
|
|
||||||
|
This is the critical distinction from the bounded ``--child-only`` mode:
|
||||||
|
the default ``--check`` validates the child chain that *would* result from
|
||||||
|
the current owner-aware cumulative parent and current prerequisite oracle,
|
||||||
|
not a self-consistent but obsolete child bound to observed stale bytes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
required = {
|
||||||
|
BINDING_PATH: prerequisite,
|
||||||
|
INLINE_RECEIPT_PATH: prerequisite,
|
||||||
|
PARENT_RELEASE_PATH: parent,
|
||||||
|
}
|
||||||
|
missing = [
|
||||||
|
_logical_path(path)
|
||||||
|
for path, mapping in required.items()
|
||||||
|
if path not in mapping
|
||||||
|
]
|
||||||
|
if missing:
|
||||||
|
raise BuildError("CUMULATIVE_CHILD_MATERIAL_MISSING", repr(sorted(missing)))
|
||||||
|
return _downstream_child_outputs(
|
||||||
|
authoring_raw=_require_file(AUTHORING_PATH),
|
||||||
|
binding_raw=prerequisite[BINDING_PATH],
|
||||||
|
inline_receipt_raw=prerequisite[INLINE_RECEIPT_PATH],
|
||||||
|
parent_raw=parent[PARENT_RELEASE_PATH],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def child_only_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
|
"""Reseal only the downstream S2_10 child chain against a frozen parent.
|
||||||
|
|
||||||
|
The canonical parent/module builder owns the raw parent closure. This mode
|
||||||
|
therefore never rewrites ``module_manifest.json``, ``stage2_release.json``,
|
||||||
|
the Agent projection, prompt receipt, or LLM binding.
|
||||||
|
"""
|
||||||
|
|
||||||
|
authoring_raw = _require_file(AUTHORING_PATH)
|
||||||
|
if _require_file(PROJECTION_PATH) != authoring_raw:
|
||||||
|
raise BuildError("S2_10_AGENT_PROJECTION_DRIFT", _logical_path(PROJECTION_PATH))
|
||||||
|
return _downstream_child_outputs(
|
||||||
|
authoring_raw=authoring_raw,
|
||||||
|
binding_raw=_require_file(BINDING_PATH),
|
||||||
|
inline_receipt_raw=_require_file(INLINE_RECEIPT_PATH),
|
||||||
|
parent_raw=_require_file(PARENT_RELEASE_PATH),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def prerequisite_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
def prerequisite_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
"""Build only the parent-owned S2_10 projection prerequisites."""
|
"""Build only the parent-owned S2_10 projection prerequisites."""
|
||||||
|
|
||||||
|
|||||||
+216
-43
@@ -12,6 +12,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import importlib.util
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -45,6 +46,12 @@ MODULE_MANIFEST_PATH = DEPLOYMENT_ROOT / "manifest" / "module_manifest.json"
|
|||||||
PLATFORM_RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_10_platform_adapter_receipt.json"
|
PLATFORM_RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_10_platform_adapter_receipt.json"
|
||||||
MODEL_RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_10_model_benchmark_receipt.json"
|
MODEL_RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_10_model_benchmark_receipt.json"
|
||||||
LEGAL_RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_10_legal_review_receipt.json"
|
LEGAL_RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_10_legal_review_receipt.json"
|
||||||
|
RELEASE_BUILDER_PATH = DEPLOYMENT_ROOT / "offline_build" / "build_release_manifests.py"
|
||||||
|
CUMULATIVE_PARENT_PATH_LISTS = (
|
||||||
|
("S2_20", DEPLOYMENT_ROOT / "manifest" / "s2_20_parent_member_paths.json"),
|
||||||
|
("S2_30", DEPLOYMENT_ROOT / "manifest" / "s2_30_parent_member_paths.json"),
|
||||||
|
("S2_40", DEPLOYMENT_ROOT / "manifest" / "s2_40_parent_member_paths.json"),
|
||||||
|
)
|
||||||
|
|
||||||
EXPECTED_AGENT_NAME = "Stage_2_S2_10"
|
EXPECTED_AGENT_NAME = "Stage_2_S2_10"
|
||||||
EXPECTED_STAGE_NAME = "S2_10"
|
EXPECTED_STAGE_NAME = "S2_10"
|
||||||
@@ -276,6 +283,75 @@ def _load_json(path: Path) -> dict[str, Any]:
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _load_release_builder() -> Any:
|
||||||
|
spec = importlib.util.spec_from_file_location(
|
||||||
|
"stage2_cumulative_release_builder",
|
||||||
|
RELEASE_BUILDER_PATH,
|
||||||
|
)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise BuildError("CUMULATIVE_RELEASE_BUILDER_IMPORT_FAILED", _logical_path(RELEASE_BUILDER_PATH))
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def cumulative_parent_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
|
"""Recompute the current owner-aware cumulative parent in memory only.
|
||||||
|
|
||||||
|
S2_10 no longer owns ``module_manifest.json`` or ``stage2_release.json``.
|
||||||
|
This oracle delegates to the canonical S2_20+S2_30+S2_40 builder and is
|
||||||
|
used solely to detect drift; it never writes either parent file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
release_builder = _load_release_builder()
|
||||||
|
combined: list[str] = []
|
||||||
|
path_owners: dict[str, str] = {}
|
||||||
|
seen: set[str] = set()
|
||||||
|
counts: dict[str, int] = {}
|
||||||
|
for owner_stage, source in CUMULATIVE_PARENT_PATH_LISTS:
|
||||||
|
raw = _require_file(source)
|
||||||
|
try:
|
||||||
|
values = json.loads(raw.decode("utf-8"))
|
||||||
|
except json.JSONDecodeError as exc:
|
||||||
|
raise BuildError("CUMULATIVE_PARENT_PATH_LIST_INVALID", f"{_logical_path(source)}: {exc}") from exc
|
||||||
|
if not isinstance(values, list) or not all(isinstance(value, str) for value in values):
|
||||||
|
raise BuildError("CUMULATIVE_PARENT_PATH_LIST_INVALID", _logical_path(source))
|
||||||
|
if values != sorted(values) or len(values) != len(set(values)):
|
||||||
|
raise BuildError("CUMULATIVE_PARENT_PATH_LIST_NOT_SORTED_UNIQUE", _logical_path(source))
|
||||||
|
overlap = sorted(seen & set(values))
|
||||||
|
if overlap:
|
||||||
|
raise BuildError("CUMULATIVE_PARENT_PATH_LIST_OVERLAP", repr(overlap))
|
||||||
|
seen.update(values)
|
||||||
|
combined.extend(values)
|
||||||
|
path_owners.update({value: owner_stage for value in values})
|
||||||
|
counts[owner_stage] = len(values)
|
||||||
|
combined.sort()
|
||||||
|
try:
|
||||||
|
module, parent = release_builder.build_release_package(
|
||||||
|
DEPLOYMENT_ROOT,
|
||||||
|
combined,
|
||||||
|
_load_json(MODULE_MANIFEST_PATH),
|
||||||
|
_load_json(PARENT_RELEASE_PATH),
|
||||||
|
path_owners=path_owners,
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
raise BuildError(
|
||||||
|
"CUMULATIVE_PARENT_ORACLE_FAILED",
|
||||||
|
f"{type(exc).__name__}:{exc}",
|
||||||
|
) from exc
|
||||||
|
module_raw = release_builder._canonical_bytes(module)
|
||||||
|
parent_raw = release_builder._canonical_bytes(parent)
|
||||||
|
return {
|
||||||
|
MODULE_MANIFEST_PATH: module_raw,
|
||||||
|
PARENT_RELEASE_PATH: parent_raw,
|
||||||
|
}, {
|
||||||
|
"owner_path_counts": counts,
|
||||||
|
"module_count": len(module["modules"]),
|
||||||
|
"module_manifest_sha256": sha256_bytes(module_raw),
|
||||||
|
"parent_release_sha256": sha256_bytes(parent_raw),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _mapping(value: Any, code: str) -> dict[str, Any]:
|
def _mapping(value: Any, code: str) -> dict[str, Any]:
|
||||||
if not isinstance(value, dict):
|
if not isinstance(value, dict):
|
||||||
raise BuildError(code, repr(value)[:200])
|
raise BuildError(code, repr(value)[:200])
|
||||||
@@ -1221,31 +1297,8 @@ def _atomic_write(path: Path, payload: bytes) -> None:
|
|||||||
temporary.unlink()
|
temporary.unlink()
|
||||||
|
|
||||||
|
|
||||||
def build() -> dict[str, Any]:
|
def _compare_outputs(outputs: Mapping[Path, bytes]) -> list[dict[str, Any]]:
|
||||||
# Phase 1 closes the authoring projection, inline receipt and binding.
|
drift: list[dict[str, Any]] = []
|
||||||
outputs, report = expected_outputs()
|
|
||||||
for path, payload in outputs.items():
|
|
||||||
_atomic_write(path, payload)
|
|
||||||
if report["phase"] in {"PREREQUISITE_PROJECTION", "PREREQUISITE_MODULE_MANIFEST"}:
|
|
||||||
outputs, report = expected_outputs()
|
|
||||||
for path, payload in outputs.items():
|
|
||||||
_atomic_write(path, payload)
|
|
||||||
if report["phase"] == "PREREQUISITE_MODULE_MANIFEST":
|
|
||||||
outputs, report = expected_outputs()
|
|
||||||
for path, payload in outputs.items():
|
|
||||||
_atomic_write(path, payload)
|
|
||||||
if report["phase"] != "COMPLETE":
|
|
||||||
raise BuildError("BUILD_DID_NOT_REACH_COMPLETE_PHASE", repr(report))
|
|
||||||
return {
|
|
||||||
"status": "BUILT_OFFLINE_HYBRID_PACKAGE",
|
|
||||||
"written_paths": [_logical_path(path) for path in outputs],
|
|
||||||
**report,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def check() -> dict[str, Any]:
|
|
||||||
outputs, report = expected_outputs()
|
|
||||||
drift = []
|
|
||||||
for path, expected in outputs.items():
|
for path, expected in outputs.items():
|
||||||
observed = path.read_bytes() if path.is_file() and not path.is_symlink() else None
|
observed = path.read_bytes() if path.is_file() and not path.is_symlink() else None
|
||||||
if observed != expected:
|
if observed != expected:
|
||||||
@@ -1256,31 +1309,100 @@ def check() -> dict[str, Any]:
|
|||||||
"observed_sha256": sha256_bytes(observed) if observed is not None else None,
|
"observed_sha256": sha256_bytes(observed) if observed is not None else None,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if report["phase"] != "COMPLETE" or drift:
|
return drift
|
||||||
raise BuildError("OFFLINE_PACKAGE_DRIFT", json.dumps({"report": report, "drift": drift}, ensure_ascii=False))
|
|
||||||
|
|
||||||
|
def _current_cumulative_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
|
"""Return the complete S2_10-owned and cumulative downstream oracle.
|
||||||
|
|
||||||
|
The owner-aware release builder is the sole producer of the cumulative
|
||||||
|
module manifest and parent release. The legacy ``expected_outputs``
|
||||||
|
function remains only as a compatibility helper for older callers; the
|
||||||
|
default build/check path must never use its S2_10-only parent projection.
|
||||||
|
"""
|
||||||
|
|
||||||
|
prerequisite, prerequisite_report = prerequisite_outputs()
|
||||||
|
parent, parent_report = cumulative_parent_outputs()
|
||||||
|
child, child_report = cumulative_child_outputs(prerequisite, parent)
|
||||||
|
overlap = (set(prerequisite) & set(parent)) | (set(prerequisite) & set(child)) | (set(parent) & set(child))
|
||||||
|
if overlap:
|
||||||
|
raise BuildError(
|
||||||
|
"CUMULATIVE_OUTPUT_PATH_OVERLAP",
|
||||||
|
repr(sorted(_logical_path(path) for path in overlap)),
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
"status": "OFFLINE_HYBRID_PACKAGE_CHECK_PASS",
|
**prerequisite,
|
||||||
|
**parent,
|
||||||
|
**child,
|
||||||
|
}, {
|
||||||
|
"prerequisite": prerequisite_report,
|
||||||
|
"cumulative_parent": parent_report,
|
||||||
|
"downstream_child": child_report,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build() -> dict[str, Any]:
|
||||||
|
"""Rebuild S2_10-owned artifacts without rewriting the cumulative parent.
|
||||||
|
|
||||||
|
A cumulative parent drift requires the owner-aware release builder and its
|
||||||
|
explicit reseal sequence. Silently rebuilding an obsolete S2_10-only
|
||||||
|
parent here would discard downstream S2_20/S2_30/S2_40 closure.
|
||||||
|
"""
|
||||||
|
|
||||||
|
prerequisite, prerequisite_report = prerequisite_outputs()
|
||||||
|
for path, payload in prerequisite.items():
|
||||||
|
_atomic_write(path, payload)
|
||||||
|
|
||||||
|
parent, parent_report = cumulative_parent_outputs()
|
||||||
|
parent_drift = _compare_outputs(parent)
|
||||||
|
if parent_drift:
|
||||||
|
raise BuildError(
|
||||||
|
"CUMULATIVE_PARENT_RESEAL_REQUIRED",
|
||||||
|
json.dumps(
|
||||||
|
{"report": parent_report, "drift": parent_drift},
|
||||||
|
ensure_ascii=False,
|
||||||
|
sort_keys=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
child, child_report = child_only_outputs()
|
||||||
|
for path, payload in child.items():
|
||||||
|
_atomic_write(path, payload)
|
||||||
|
return {
|
||||||
|
"status": "S2_10_OWNED_ARTIFACTS_REBUILT_CUMULATIVE_PARENT_PRESERVED",
|
||||||
|
"written_paths": [_logical_path(path) for path in {**prerequisite, **child}],
|
||||||
|
"prerequisite": prerequisite_report,
|
||||||
|
"cumulative_parent": parent_report,
|
||||||
|
"downstream_child": child_report,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def check() -> dict[str, Any]:
|
||||||
|
outputs, report = _current_cumulative_outputs()
|
||||||
|
drift = _compare_outputs(outputs)
|
||||||
|
if drift:
|
||||||
|
raise BuildError(
|
||||||
|
"OFFLINE_PACKAGE_DRIFT",
|
||||||
|
json.dumps({"report": report, "drift": drift}, ensure_ascii=False, sort_keys=True),
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"status": "S2_10_CUMULATIVE_PACKAGE_CHECK_PASS",
|
||||||
"checked_paths": [_logical_path(path) for path in outputs],
|
"checked_paths": [_logical_path(path) for path in outputs],
|
||||||
**report,
|
**report,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def child_only_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
def _downstream_child_outputs(
|
||||||
"""Reseal only the downstream S2_10 child chain against a frozen parent.
|
*,
|
||||||
|
authoring_raw: bytes,
|
||||||
The canonical parent/module builder owns the raw parent closure. This mode
|
binding_raw: bytes,
|
||||||
therefore never rewrites ``module_manifest.json``, ``stage2_release.json``,
|
inline_receipt_raw: bytes,
|
||||||
the Agent projection, prompt receipt, or LLM binding.
|
parent_raw: bytes,
|
||||||
"""
|
) -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
|
contract = extract_agent_contract(
|
||||||
authoring_raw = _require_file(AUTHORING_PATH)
|
_load_yaml(authoring_raw, _logical_path(AUTHORING_PATH))
|
||||||
if _require_file(PROJECTION_PATH) != authoring_raw:
|
)
|
||||||
raise BuildError("S2_10_AGENT_PROJECTION_DRIFT", _logical_path(PROJECTION_PATH))
|
|
||||||
contract = extract_agent_contract(_load_yaml(authoring_raw, _logical_path(AUTHORING_PATH)))
|
|
||||||
binding_raw = _require_file(BINDING_PATH)
|
|
||||||
binding = _load_yaml(binding_raw, _logical_path(BINDING_PATH))
|
binding = _load_yaml(binding_raw, _logical_path(BINDING_PATH))
|
||||||
inline_receipt_raw = _require_file(INLINE_RECEIPT_PATH)
|
|
||||||
parent_raw = _require_file(PARENT_RELEASE_PATH)
|
|
||||||
child = build_child_release(parent_raw, binding)
|
child = build_child_release(parent_raw, binding)
|
||||||
child_raw = canonical_json_bytes(child)
|
child_raw = canonical_json_bytes(child)
|
||||||
outputs = {
|
outputs = {
|
||||||
@@ -1310,6 +1432,57 @@ def child_only_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def cumulative_child_outputs(
|
||||||
|
prerequisite: Mapping[Path, bytes],
|
||||||
|
parent: Mapping[Path, bytes],
|
||||||
|
) -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
|
"""Build downstream receipts from the expected current parent material.
|
||||||
|
|
||||||
|
This is the critical distinction from the bounded ``--child-only`` mode:
|
||||||
|
the default ``--check`` validates the child chain that *would* result from
|
||||||
|
the current owner-aware cumulative parent and current prerequisite oracle,
|
||||||
|
not a self-consistent but obsolete child bound to observed stale bytes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
required = {
|
||||||
|
BINDING_PATH: prerequisite,
|
||||||
|
INLINE_RECEIPT_PATH: prerequisite,
|
||||||
|
PARENT_RELEASE_PATH: parent,
|
||||||
|
}
|
||||||
|
missing = [
|
||||||
|
_logical_path(path)
|
||||||
|
for path, mapping in required.items()
|
||||||
|
if path not in mapping
|
||||||
|
]
|
||||||
|
if missing:
|
||||||
|
raise BuildError("CUMULATIVE_CHILD_MATERIAL_MISSING", repr(sorted(missing)))
|
||||||
|
return _downstream_child_outputs(
|
||||||
|
authoring_raw=_require_file(AUTHORING_PATH),
|
||||||
|
binding_raw=prerequisite[BINDING_PATH],
|
||||||
|
inline_receipt_raw=prerequisite[INLINE_RECEIPT_PATH],
|
||||||
|
parent_raw=parent[PARENT_RELEASE_PATH],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def child_only_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
|
"""Reseal only the downstream S2_10 child chain against a frozen parent.
|
||||||
|
|
||||||
|
The canonical parent/module builder owns the raw parent closure. This mode
|
||||||
|
therefore never rewrites ``module_manifest.json``, ``stage2_release.json``,
|
||||||
|
the Agent projection, prompt receipt, or LLM binding.
|
||||||
|
"""
|
||||||
|
|
||||||
|
authoring_raw = _require_file(AUTHORING_PATH)
|
||||||
|
if _require_file(PROJECTION_PATH) != authoring_raw:
|
||||||
|
raise BuildError("S2_10_AGENT_PROJECTION_DRIFT", _logical_path(PROJECTION_PATH))
|
||||||
|
return _downstream_child_outputs(
|
||||||
|
authoring_raw=authoring_raw,
|
||||||
|
binding_raw=_require_file(BINDING_PATH),
|
||||||
|
inline_receipt_raw=_require_file(INLINE_RECEIPT_PATH),
|
||||||
|
parent_raw=_require_file(PARENT_RELEASE_PATH),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def prerequisite_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
def prerequisite_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
"""Build only the parent-owned S2_10 projection prerequisites."""
|
"""Build only the parent-owned S2_10 projection prerequisites."""
|
||||||
|
|
||||||
|
|||||||
+112
-4
@@ -59,9 +59,9 @@ EXPECTED_BUILD_ORDER = (
|
|||||||
"S2_10_PROJECTION_PREREQUISITES",
|
"S2_10_PROJECTION_PREREQUISITES",
|
||||||
"MODULE_MANIFEST",
|
"MODULE_MANIFEST",
|
||||||
"PARENT_STAGE2_RELEASE",
|
"PARENT_STAGE2_RELEASE",
|
||||||
"INJECT_PARENT_HASH_INTO_S2_30_S2_00_S2_20",
|
"INJECT_PARENT_HASH_INTO_S2_30_S2_00_S2_20_S2_40",
|
||||||
"S2_30_AGENT_PROJECTION_PROMPT_CODE_RECEIPTS_BINDING",
|
"S2_30_AGENT_PROJECTION_PROMPT_CODE_RECEIPTS_BINDING",
|
||||||
"S2_00_S2_20_PARENT_HASH_PROJECTIONS",
|
"S2_00_S2_20_S2_40_PARENT_HASH_PROJECTIONS",
|
||||||
"SHARED_CODE_EXECUTOR_BINDING",
|
"SHARED_CODE_EXECUTOR_BINDING",
|
||||||
"S2_10_AND_S2_30_CHILD_RELEASES_AND_RECEIPTS",
|
"S2_10_AND_S2_30_CHILD_RELEASES_AND_RECEIPTS",
|
||||||
"CHECK_AND_REGRESSION",
|
"CHECK_AND_REGRESSION",
|
||||||
@@ -153,6 +153,7 @@ PARENT_INDEPENDENT_PATHS = (
|
|||||||
"tests/fixtures/s2_30/context_reference_envelope.json",
|
"tests/fixtures/s2_30/context_reference_envelope.json",
|
||||||
"tests/fixtures/s2_30/technical_failure.json",
|
"tests/fixtures/s2_30/technical_failure.json",
|
||||||
"tests/fixtures/s2_30/partial_write_publish_barrier.json",
|
"tests/fixtures/s2_30/partial_write_publish_barrier.json",
|
||||||
|
"tests/fixtures/s2_30/persisted_handoff_chain.json",
|
||||||
"tests/fixtures/s2_30/regression_manifest.json",
|
"tests/fixtures/s2_30/regression_manifest.json",
|
||||||
"manifest/s2_30_parent_member_paths.json",
|
"manifest/s2_30_parent_member_paths.json",
|
||||||
)
|
)
|
||||||
@@ -919,6 +920,100 @@ def _bound_asset_ref(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _s2_40_stage_binding(parent_sha: str) -> dict[str, Any]:
|
||||||
|
"""Build the detached, offline-only S2_40 deterministic-stage row."""
|
||||||
|
|
||||||
|
agent = ROOT / "agent_scripts" / "Stage_2_S2_40.yml"
|
||||||
|
workflow = ROOT / "workflows" / "S2_40_final_review_render_and_commit.yml"
|
||||||
|
receipt = ROOT / "manifest" / "s2_40_inline_code_receipt.json"
|
||||||
|
code = ROOT / "runtime" / "s2_40_commit.py"
|
||||||
|
return {
|
||||||
|
"stage_id": "S2_40",
|
||||||
|
"binding_id": "S2-BINDING-S2_40-CODE-EXECUTOR-V1",
|
||||||
|
"workflow_id": "S2_40",
|
||||||
|
"execution_class": "NON-LLM-DETERMINISTIC",
|
||||||
|
"active_runtime_authority": False,
|
||||||
|
"agent_script_ref": _bound_asset_ref(
|
||||||
|
"AGENT-S2_40-INLINE",
|
||||||
|
"agent_scripts/Stage_2_S2_40.yml",
|
||||||
|
agent,
|
||||||
|
"liti_agent_yaml.v1",
|
||||||
|
),
|
||||||
|
"workflow_contract_ref": _bound_asset_ref(
|
||||||
|
"WF-S2_40",
|
||||||
|
"workflows/S2_40_final_review_render_and_commit.yml",
|
||||||
|
workflow,
|
||||||
|
"stage2_s2_40_final_review_render_and_commit.v1",
|
||||||
|
),
|
||||||
|
"inline_code_receipt_ref": _bound_asset_ref(
|
||||||
|
"RECEIPT-S2_40-INLINE-CODE",
|
||||||
|
"manifest/s2_40_inline_code_receipt.json",
|
||||||
|
receipt,
|
||||||
|
"stage2_s2_40_inline_code_receipt.v1",
|
||||||
|
),
|
||||||
|
"stage2_release_ref": {
|
||||||
|
"asset_id": "RELEASE-STAGE2-CLEAN",
|
||||||
|
"path": "manifest/stage2_release.json",
|
||||||
|
"sha256": parent_sha,
|
||||||
|
"schema_id": "stage2_release.v2",
|
||||||
|
"binding_status": "BOUND",
|
||||||
|
},
|
||||||
|
"expected_release_sha256": parent_sha,
|
||||||
|
"agent_script_sha256": sha256_bytes(_require_file(agent)),
|
||||||
|
"canonical_code_sha256": sha256_bytes(_require_file(code)),
|
||||||
|
"mcp_server_id": "code-executor",
|
||||||
|
"tool_name": "run_code",
|
||||||
|
"language": "python",
|
||||||
|
"network": "agent-network",
|
||||||
|
"timeout_seconds": 300,
|
||||||
|
"runtime_image_digest": "PENDING_SEQUENTIAL_BIND",
|
||||||
|
"runtime_image_status": "PENDING_BACKEND_EVIDENCE",
|
||||||
|
"dependency_lock": {
|
||||||
|
"requirements": "httpx==0.28.1",
|
||||||
|
"requirements_sha256": sha256_bytes(b"httpx==0.28.1"),
|
||||||
|
"lock_status": "LIVE_BACKEND_PENDING",
|
||||||
|
},
|
||||||
|
"localdocs_contract": {
|
||||||
|
"endpoint": "http://mcp-localdocs:8012/mcp",
|
||||||
|
"user_id_template": "{{__user_hash__}}",
|
||||||
|
"workspace_id_template": "{{__workspace_hash__}}",
|
||||||
|
"tool_allowlist": ["read_binary_doc", "write_binary_file"],
|
||||||
|
"fixed_request_path": "stage2_control/s2_40_request.json",
|
||||||
|
"read_path_allowlist": [
|
||||||
|
"stage2_control/s2_40_request.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/manifest/stage2_release.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/manifest/module_manifest.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/manifest/stage2_deterministic_admission_receipt.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/manifest/s2_40_inline_code_receipt.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_40.yml",
|
||||||
|
"Default_Agent/Stage_2_Clean/deployment/stage2_code_executor_binding.yml",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/ingress.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/context.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/s2_10.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/domain_verdict.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/relief_plan.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/binding_retrieval.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/calculation.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/draft_atoms.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/review_status.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/package.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/registry/review/review_policy_registry.yml",
|
||||||
|
"Default_Agent/Stage_2_Clean/renderers/<renderer_id>.yml",
|
||||||
|
"stage2_runs/by-binding/<run_binding_digest>/<barrier-enumerated-input-path>",
|
||||||
|
"stage2_runs/by-binding/<run_binding_digest>/review/receipts/<receipt_id>.json",
|
||||||
|
"stage2_runs/by-binding/<run_binding_digest>/control/run_status.json",
|
||||||
|
],
|
||||||
|
"write_root_rule": "stage2_runs/by-binding/<run_binding_digest>/",
|
||||||
|
},
|
||||||
|
"external_mcp_contract": None,
|
||||||
|
"egress_profile_id": "S2_40_LOCALDOCS_ONLY_V1",
|
||||||
|
"egress_profile_status": "PENDING_LIVE_VERIFICATION",
|
||||||
|
"downstream_handoff_owner": None,
|
||||||
|
"live_admission_status": "PENDING_SECRET_BINDING",
|
||||||
|
"legacy_fallbacks": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def shared_executor_outputs() -> dict[Path, bytes]:
|
def shared_executor_outputs() -> dict[Path, bytes]:
|
||||||
if yaml is None:
|
if yaml is None:
|
||||||
raise BuildError("PYYAML_REQUIRED", "shared executor build")
|
raise BuildError("PYYAML_REQUIRED", "shared executor build")
|
||||||
@@ -943,9 +1038,22 @@ def shared_executor_outputs() -> dict[Path, bytes]:
|
|||||||
"receipt": ROOT / "manifest" / "s2_20_inline_code_receipt.json",
|
"receipt": ROOT / "manifest" / "s2_20_inline_code_receipt.json",
|
||||||
"code": ROOT / "runtime" / "s2_20_reduce.py",
|
"code": ROOT / "runtime" / "s2_20_reduce.py",
|
||||||
},
|
},
|
||||||
|
"S2_40": {
|
||||||
|
"agent": ROOT / "agent_scripts" / "Stage_2_S2_40.yml",
|
||||||
|
"workflow": ROOT / "workflows" / "S2_40_final_review_render_and_commit.yml",
|
||||||
|
"receipt": ROOT / "manifest" / "s2_40_inline_code_receipt.json",
|
||||||
|
"code": ROOT / "runtime" / "s2_40_commit.py",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if {row.get("stage_id") for row in rows if isinstance(row, dict)} != set(stage_sources):
|
observed_stage_ids = [row.get("stage_id") for row in rows if isinstance(row, dict)]
|
||||||
|
if len(observed_stage_ids) != len(rows) or len(observed_stage_ids) != len(set(observed_stage_ids)):
|
||||||
raise BuildError("SHARED_STAGE_BINDING_SET", repr(rows))
|
raise BuildError("SHARED_STAGE_BINDING_SET", repr(rows))
|
||||||
|
if not set(observed_stage_ids).issubset(set(stage_sources)) or not {"S2_00", "S2_20"}.issubset(observed_stage_ids):
|
||||||
|
raise BuildError("SHARED_STAGE_BINDING_SET", repr(rows))
|
||||||
|
rows = [row for row in rows if isinstance(row, dict) and row.get("stage_id") != "S2_40"]
|
||||||
|
rows.append(_s2_40_stage_binding(parent_sha))
|
||||||
|
rows.sort(key=lambda row: str(row["stage_id"]))
|
||||||
|
wrapper["stage_bindings"] = rows
|
||||||
for row in rows:
|
for row in rows:
|
||||||
if not isinstance(row, dict):
|
if not isinstance(row, dict):
|
||||||
raise BuildError("SHARED_STAGE_BINDING_ROW", repr(row))
|
raise BuildError("SHARED_STAGE_BINDING_ROW", repr(row))
|
||||||
@@ -1068,7 +1176,7 @@ def shared_executor_outputs() -> dict[Path, bytes]:
|
|||||||
"schema_version": "stage2_deterministic_admission_receipt.v1",
|
"schema_version": "stage2_deterministic_admission_receipt.v1",
|
||||||
"parent_release_sha256": parent_sha,
|
"parent_release_sha256": parent_sha,
|
||||||
"executor_binding_sha256": sha256_bytes(shared_raw),
|
"executor_binding_sha256": sha256_bytes(shared_raw),
|
||||||
"present_deterministic_stage_ids": ["S2_00", "S2_20"],
|
"present_deterministic_stage_ids": ["S2_00", "S2_20", "S2_40"],
|
||||||
"stage_receipts": [row["inline_code_receipt_ref"] for row in rows],
|
"stage_receipts": [row["inline_code_receipt_ref"] for row in rows],
|
||||||
"embedded_task_admissions": [
|
"embedded_task_admissions": [
|
||||||
{
|
{
|
||||||
|
|||||||
+112
-4
@@ -59,9 +59,9 @@ EXPECTED_BUILD_ORDER = (
|
|||||||
"S2_10_PROJECTION_PREREQUISITES",
|
"S2_10_PROJECTION_PREREQUISITES",
|
||||||
"MODULE_MANIFEST",
|
"MODULE_MANIFEST",
|
||||||
"PARENT_STAGE2_RELEASE",
|
"PARENT_STAGE2_RELEASE",
|
||||||
"INJECT_PARENT_HASH_INTO_S2_30_S2_00_S2_20",
|
"INJECT_PARENT_HASH_INTO_S2_30_S2_00_S2_20_S2_40",
|
||||||
"S2_30_AGENT_PROJECTION_PROMPT_CODE_RECEIPTS_BINDING",
|
"S2_30_AGENT_PROJECTION_PROMPT_CODE_RECEIPTS_BINDING",
|
||||||
"S2_00_S2_20_PARENT_HASH_PROJECTIONS",
|
"S2_00_S2_20_S2_40_PARENT_HASH_PROJECTIONS",
|
||||||
"SHARED_CODE_EXECUTOR_BINDING",
|
"SHARED_CODE_EXECUTOR_BINDING",
|
||||||
"S2_10_AND_S2_30_CHILD_RELEASES_AND_RECEIPTS",
|
"S2_10_AND_S2_30_CHILD_RELEASES_AND_RECEIPTS",
|
||||||
"CHECK_AND_REGRESSION",
|
"CHECK_AND_REGRESSION",
|
||||||
@@ -153,6 +153,7 @@ PARENT_INDEPENDENT_PATHS = (
|
|||||||
"tests/fixtures/s2_30/context_reference_envelope.json",
|
"tests/fixtures/s2_30/context_reference_envelope.json",
|
||||||
"tests/fixtures/s2_30/technical_failure.json",
|
"tests/fixtures/s2_30/technical_failure.json",
|
||||||
"tests/fixtures/s2_30/partial_write_publish_barrier.json",
|
"tests/fixtures/s2_30/partial_write_publish_barrier.json",
|
||||||
|
"tests/fixtures/s2_30/persisted_handoff_chain.json",
|
||||||
"tests/fixtures/s2_30/regression_manifest.json",
|
"tests/fixtures/s2_30/regression_manifest.json",
|
||||||
"manifest/s2_30_parent_member_paths.json",
|
"manifest/s2_30_parent_member_paths.json",
|
||||||
)
|
)
|
||||||
@@ -919,6 +920,100 @@ def _bound_asset_ref(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _s2_40_stage_binding(parent_sha: str) -> dict[str, Any]:
|
||||||
|
"""Build the detached, offline-only S2_40 deterministic-stage row."""
|
||||||
|
|
||||||
|
agent = ROOT / "agent_scripts" / "Stage_2_S2_40.yml"
|
||||||
|
workflow = ROOT / "workflows" / "S2_40_final_review_render_and_commit.yml"
|
||||||
|
receipt = ROOT / "manifest" / "s2_40_inline_code_receipt.json"
|
||||||
|
code = ROOT / "runtime" / "s2_40_commit.py"
|
||||||
|
return {
|
||||||
|
"stage_id": "S2_40",
|
||||||
|
"binding_id": "S2-BINDING-S2_40-CODE-EXECUTOR-V1",
|
||||||
|
"workflow_id": "S2_40",
|
||||||
|
"execution_class": "NON-LLM-DETERMINISTIC",
|
||||||
|
"active_runtime_authority": False,
|
||||||
|
"agent_script_ref": _bound_asset_ref(
|
||||||
|
"AGENT-S2_40-INLINE",
|
||||||
|
"agent_scripts/Stage_2_S2_40.yml",
|
||||||
|
agent,
|
||||||
|
"liti_agent_yaml.v1",
|
||||||
|
),
|
||||||
|
"workflow_contract_ref": _bound_asset_ref(
|
||||||
|
"WF-S2_40",
|
||||||
|
"workflows/S2_40_final_review_render_and_commit.yml",
|
||||||
|
workflow,
|
||||||
|
"stage2_s2_40_final_review_render_and_commit.v1",
|
||||||
|
),
|
||||||
|
"inline_code_receipt_ref": _bound_asset_ref(
|
||||||
|
"RECEIPT-S2_40-INLINE-CODE",
|
||||||
|
"manifest/s2_40_inline_code_receipt.json",
|
||||||
|
receipt,
|
||||||
|
"stage2_s2_40_inline_code_receipt.v1",
|
||||||
|
),
|
||||||
|
"stage2_release_ref": {
|
||||||
|
"asset_id": "RELEASE-STAGE2-CLEAN",
|
||||||
|
"path": "manifest/stage2_release.json",
|
||||||
|
"sha256": parent_sha,
|
||||||
|
"schema_id": "stage2_release.v2",
|
||||||
|
"binding_status": "BOUND",
|
||||||
|
},
|
||||||
|
"expected_release_sha256": parent_sha,
|
||||||
|
"agent_script_sha256": sha256_bytes(_require_file(agent)),
|
||||||
|
"canonical_code_sha256": sha256_bytes(_require_file(code)),
|
||||||
|
"mcp_server_id": "code-executor",
|
||||||
|
"tool_name": "run_code",
|
||||||
|
"language": "python",
|
||||||
|
"network": "agent-network",
|
||||||
|
"timeout_seconds": 300,
|
||||||
|
"runtime_image_digest": "PENDING_SEQUENTIAL_BIND",
|
||||||
|
"runtime_image_status": "PENDING_BACKEND_EVIDENCE",
|
||||||
|
"dependency_lock": {
|
||||||
|
"requirements": "httpx==0.28.1",
|
||||||
|
"requirements_sha256": sha256_bytes(b"httpx==0.28.1"),
|
||||||
|
"lock_status": "LIVE_BACKEND_PENDING",
|
||||||
|
},
|
||||||
|
"localdocs_contract": {
|
||||||
|
"endpoint": "http://mcp-localdocs:8012/mcp",
|
||||||
|
"user_id_template": "{{__user_hash__}}",
|
||||||
|
"workspace_id_template": "{{__workspace_hash__}}",
|
||||||
|
"tool_allowlist": ["read_binary_doc", "write_binary_file"],
|
||||||
|
"fixed_request_path": "stage2_control/s2_40_request.json",
|
||||||
|
"read_path_allowlist": [
|
||||||
|
"stage2_control/s2_40_request.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/manifest/stage2_release.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/manifest/module_manifest.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/manifest/stage2_deterministic_admission_receipt.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/manifest/s2_40_inline_code_receipt.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_40.yml",
|
||||||
|
"Default_Agent/Stage_2_Clean/deployment/stage2_code_executor_binding.yml",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/ingress.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/context.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/s2_10.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/domain_verdict.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/relief_plan.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/binding_retrieval.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/calculation.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/draft_atoms.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/review_status.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/schemas/package.schema.json",
|
||||||
|
"Default_Agent/Stage_2_Clean/registry/review/review_policy_registry.yml",
|
||||||
|
"Default_Agent/Stage_2_Clean/renderers/<renderer_id>.yml",
|
||||||
|
"stage2_runs/by-binding/<run_binding_digest>/<barrier-enumerated-input-path>",
|
||||||
|
"stage2_runs/by-binding/<run_binding_digest>/review/receipts/<receipt_id>.json",
|
||||||
|
"stage2_runs/by-binding/<run_binding_digest>/control/run_status.json",
|
||||||
|
],
|
||||||
|
"write_root_rule": "stage2_runs/by-binding/<run_binding_digest>/",
|
||||||
|
},
|
||||||
|
"external_mcp_contract": None,
|
||||||
|
"egress_profile_id": "S2_40_LOCALDOCS_ONLY_V1",
|
||||||
|
"egress_profile_status": "PENDING_LIVE_VERIFICATION",
|
||||||
|
"downstream_handoff_owner": None,
|
||||||
|
"live_admission_status": "PENDING_SECRET_BINDING",
|
||||||
|
"legacy_fallbacks": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def shared_executor_outputs() -> dict[Path, bytes]:
|
def shared_executor_outputs() -> dict[Path, bytes]:
|
||||||
if yaml is None:
|
if yaml is None:
|
||||||
raise BuildError("PYYAML_REQUIRED", "shared executor build")
|
raise BuildError("PYYAML_REQUIRED", "shared executor build")
|
||||||
@@ -943,9 +1038,22 @@ def shared_executor_outputs() -> dict[Path, bytes]:
|
|||||||
"receipt": ROOT / "manifest" / "s2_20_inline_code_receipt.json",
|
"receipt": ROOT / "manifest" / "s2_20_inline_code_receipt.json",
|
||||||
"code": ROOT / "runtime" / "s2_20_reduce.py",
|
"code": ROOT / "runtime" / "s2_20_reduce.py",
|
||||||
},
|
},
|
||||||
|
"S2_40": {
|
||||||
|
"agent": ROOT / "agent_scripts" / "Stage_2_S2_40.yml",
|
||||||
|
"workflow": ROOT / "workflows" / "S2_40_final_review_render_and_commit.yml",
|
||||||
|
"receipt": ROOT / "manifest" / "s2_40_inline_code_receipt.json",
|
||||||
|
"code": ROOT / "runtime" / "s2_40_commit.py",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if {row.get("stage_id") for row in rows if isinstance(row, dict)} != set(stage_sources):
|
observed_stage_ids = [row.get("stage_id") for row in rows if isinstance(row, dict)]
|
||||||
|
if len(observed_stage_ids) != len(rows) or len(observed_stage_ids) != len(set(observed_stage_ids)):
|
||||||
raise BuildError("SHARED_STAGE_BINDING_SET", repr(rows))
|
raise BuildError("SHARED_STAGE_BINDING_SET", repr(rows))
|
||||||
|
if not set(observed_stage_ids).issubset(set(stage_sources)) or not {"S2_00", "S2_20"}.issubset(observed_stage_ids):
|
||||||
|
raise BuildError("SHARED_STAGE_BINDING_SET", repr(rows))
|
||||||
|
rows = [row for row in rows if isinstance(row, dict) and row.get("stage_id") != "S2_40"]
|
||||||
|
rows.append(_s2_40_stage_binding(parent_sha))
|
||||||
|
rows.sort(key=lambda row: str(row["stage_id"]))
|
||||||
|
wrapper["stage_bindings"] = rows
|
||||||
for row in rows:
|
for row in rows:
|
||||||
if not isinstance(row, dict):
|
if not isinstance(row, dict):
|
||||||
raise BuildError("SHARED_STAGE_BINDING_ROW", repr(row))
|
raise BuildError("SHARED_STAGE_BINDING_ROW", repr(row))
|
||||||
@@ -1068,7 +1176,7 @@ def shared_executor_outputs() -> dict[Path, bytes]:
|
|||||||
"schema_version": "stage2_deterministic_admission_receipt.v1",
|
"schema_version": "stage2_deterministic_admission_receipt.v1",
|
||||||
"parent_release_sha256": parent_sha,
|
"parent_release_sha256": parent_sha,
|
||||||
"executor_binding_sha256": sha256_bytes(shared_raw),
|
"executor_binding_sha256": sha256_bytes(shared_raw),
|
||||||
"present_deterministic_stage_ids": ["S2_00", "S2_20"],
|
"present_deterministic_stage_ids": ["S2_00", "S2_20", "S2_40"],
|
||||||
"stage_receipts": [row["inline_code_receipt_ref"] for row in rows],
|
"stage_receipts": [row["inline_code_receipt_ref"] for row in rows],
|
||||||
"embedded_task_admissions": [
|
"embedded_task_admissions": [
|
||||||
{
|
{
|
||||||
|
|||||||
+894
@@ -0,0 +1,894 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Build the version-free S2_40 Agent projection from its sole authoring YAML.
|
||||||
|
|
||||||
|
This is an offline build tool. It never executes a matter, imports project
|
||||||
|
runtime code, rewrites the authoring YAML, or invokes a subprocess. The YAML
|
||||||
|
parser-returned ``parameters.code`` string is encoded directly as UTF-8; no
|
||||||
|
dedent, newline normalization, or source transformation is permitted.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import ast
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from typing import Any, Iterable, Mapping
|
||||||
|
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except ImportError: # pragma: no cover - exercised only on an incomplete build host
|
||||||
|
yaml = None # type: ignore[assignment]
|
||||||
|
|
||||||
|
|
||||||
|
DEPLOYMENT_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
MAIN_WORKING_DIRECTORY = DEPLOYMENT_ROOT.parent.parent
|
||||||
|
AUTHORING_PATH = MAIN_WORKING_DIRECTORY / "Stage_2_S2_40.yml"
|
||||||
|
PROJECTION_PATH = DEPLOYMENT_ROOT / "agent_scripts" / "Stage_2_S2_40.yml"
|
||||||
|
MIRROR_PY_PATH = DEPLOYMENT_ROOT / "runtime" / "s2_40_commit.py"
|
||||||
|
MIRROR_TXT_PATH = DEPLOYMENT_ROOT / "runtime" / "s2_40_commit.txt"
|
||||||
|
RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_40_inline_code_receipt.json"
|
||||||
|
|
||||||
|
EXPECTED_TASK_NAME = "Task_S2_40_deterministic_finalizer"
|
||||||
|
EXPECTED_AGENT_NAME = "Stage_2_S2_40"
|
||||||
|
EXPECTED_AGENT_VERSION = "1.0.0"
|
||||||
|
EXPECTED_PARAMETERS = {
|
||||||
|
"language": "python",
|
||||||
|
"requirements": "httpx==0.28.1",
|
||||||
|
"network": "agent-network",
|
||||||
|
"timeout": 300,
|
||||||
|
}
|
||||||
|
EXPECTED_LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
||||||
|
EXPECTED_CODE_EXECUTOR_URL = "https://code-executor.mcp.eroomai.com/mcp"
|
||||||
|
REQUIRED_CODE_TOKENS = (
|
||||||
|
EXPECTED_LOCALDOCS_URL,
|
||||||
|
"{{__user_hash__}}",
|
||||||
|
"{{__workspace_hash__}}",
|
||||||
|
"read_binary_doc",
|
||||||
|
"write_binary_file",
|
||||||
|
"stage2_control/s2_40_request.json",
|
||||||
|
"run_status.json",
|
||||||
|
"reduce_and_render",
|
||||||
|
"invariant_results",
|
||||||
|
"candidate_material",
|
||||||
|
"publish_candidate",
|
||||||
|
"validate_review_receipts",
|
||||||
|
"final_status",
|
||||||
|
)
|
||||||
|
REQUIRED_CODE_TOKEN_ALTERNATIVES = (
|
||||||
|
("EXPECTED_STAGE2_RELEASE_SHA256",),
|
||||||
|
)
|
||||||
|
ALLOWED_NON_STDLIB_IMPORTS = frozenset({"httpx"})
|
||||||
|
FORBIDDEN_IMPORT_ROOTS = frozenset(
|
||||||
|
{
|
||||||
|
"ftplib",
|
||||||
|
"http",
|
||||||
|
"importlib",
|
||||||
|
"smtplib",
|
||||||
|
"socket",
|
||||||
|
"subprocess",
|
||||||
|
"telnetlib",
|
||||||
|
"urllib",
|
||||||
|
"xmlrpc",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
FORBIDDEN_CALL_NAMES = frozenset({"__import__", "compile", "eval", "exec", "open"})
|
||||||
|
FORBIDDEN_ATTRIBUTE_CALLS = frozenset(
|
||||||
|
{
|
||||||
|
("os", "popen"),
|
||||||
|
("os", "spawnl"),
|
||||||
|
("os", "spawnle"),
|
||||||
|
("os", "spawnlp"),
|
||||||
|
("os", "spawnlpe"),
|
||||||
|
("os", "spawnv"),
|
||||||
|
("os", "spawnve"),
|
||||||
|
("os", "spawnvp"),
|
||||||
|
("os", "spawnvpe"),
|
||||||
|
("os", "system"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
PLACEHOLDER_COMMENT_RE = re.compile(
|
||||||
|
r"(?im)^\s*#\s*(?:TODO|FIXME|TBD|PLACEHOLDER|OMITTED\s+BODY|IMPLEMENT\s+ME)\b"
|
||||||
|
)
|
||||||
|
PLAINTEXT_SECRET_RES = (
|
||||||
|
re.compile(r"(?i)\bAuthorization\s*:\s*Bearer\s+\S+"),
|
||||||
|
re.compile(r"(?i)\bBearer\s+[A-Za-z0-9+/=_-]{12,}"),
|
||||||
|
re.compile(
|
||||||
|
r"(?i)\b(?:MCP_API_KEY|API_KEY|ACCESS_TOKEN|AUTH_TOKEN|CLIENT_SECRET)\s*=\s*['\"][^'\"]+['\"]"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
URL_RE = re.compile(r"https?://[^\s'\"]+")
|
||||||
|
PYTHON_SOURCE_REF_RE = re.compile(r"(?i)(?:^|[/\\])[^\r\n'\"]+\.py(?:$|[?#])")
|
||||||
|
ALLOWED_IDENTIFIER_URL_PREFIXES = ("https://schemas.liti-agent.local/",)
|
||||||
|
HTTP_NETWORK_METHODS = frozenset({"delete", "get", "head", "options", "patch", "post", "put", "request", "stream"})
|
||||||
|
HTTP_CLIENT_FACTORIES = frozenset({"Client", "AsyncClient"})
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectionError(RuntimeError):
|
||||||
|
"""A controlled build failure with a stable reason code."""
|
||||||
|
|
||||||
|
def __init__(self, code: str, detail: str) -> None:
|
||||||
|
super().__init__(f"{code}: {detail}")
|
||||||
|
self.code = code
|
||||||
|
self.detail = detail
|
||||||
|
|
||||||
|
|
||||||
|
class AuthoringMissingError(ProjectionError):
|
||||||
|
def __init__(self, path: Path) -> None:
|
||||||
|
super().__init__("AUTHORING_YAML_MISSING", path.as_posix())
|
||||||
|
|
||||||
|
|
||||||
|
if yaml is not None:
|
||||||
|
|
||||||
|
class UniqueKeySafeLoader(yaml.SafeLoader):
|
||||||
|
"""SafeLoader variant that rejects duplicate mapping keys recursively."""
|
||||||
|
|
||||||
|
def construct_mapping(self, node: Any, deep: bool = False) -> dict[Any, Any]:
|
||||||
|
if not isinstance(node, yaml.MappingNode):
|
||||||
|
raise ProjectionError("YAML_MAPPING_REQUIRED", repr(node)[:200])
|
||||||
|
self.flatten_mapping(node)
|
||||||
|
result: dict[Any, Any] = {}
|
||||||
|
for key_node, value_node in node.value:
|
||||||
|
key = self.construct_object(key_node, deep=deep)
|
||||||
|
try:
|
||||||
|
duplicate = key in result
|
||||||
|
except TypeError as exc:
|
||||||
|
raise ProjectionError("YAML_UNHASHABLE_KEY", repr(key)[:200]) from exc
|
||||||
|
if duplicate:
|
||||||
|
mark = getattr(key_node, "start_mark", None)
|
||||||
|
location = f" line {mark.line + 1}" if mark is not None else ""
|
||||||
|
raise ProjectionError("YAML_DUPLICATE_KEY", f"{key!r}{location}")
|
||||||
|
result[key] = self.construct_object(value_node, deep=deep)
|
||||||
|
return result
|
||||||
|
|
||||||
|
else: # pragma: no cover - type placeholder for hosts without PyYAML
|
||||||
|
|
||||||
|
class UniqueKeySafeLoader: # type: ignore[no-redef]
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def sha256_bytes(value: bytes) -> str:
|
||||||
|
return hashlib.sha256(value).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def canonical_json_bytes(value: Any) -> bytes:
|
||||||
|
return (
|
||||||
|
json.dumps(
|
||||||
|
value,
|
||||||
|
ensure_ascii=False,
|
||||||
|
allow_nan=False,
|
||||||
|
sort_keys=True,
|
||||||
|
separators=(",", ":"),
|
||||||
|
)
|
||||||
|
+ "\n"
|
||||||
|
).encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _logical_path(path: Path) -> str:
|
||||||
|
try:
|
||||||
|
return path.resolve(strict=False).relative_to(
|
||||||
|
MAIN_WORKING_DIRECTORY.resolve(strict=False)
|
||||||
|
).as_posix()
|
||||||
|
except ValueError:
|
||||||
|
return path.as_posix()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_authoring_bytes(raw: bytes, *, source: str = "<authoring>") -> dict[str, Any]:
|
||||||
|
if yaml is None:
|
||||||
|
raise ProjectionError("PYYAML_REQUIRED", "install PyYAML on the offline build host")
|
||||||
|
try:
|
||||||
|
text = raw.decode("utf-8")
|
||||||
|
except UnicodeDecodeError as exc:
|
||||||
|
raise ProjectionError("AUTHORING_UTF8_REQUIRED", f"{source}: {exc}") from exc
|
||||||
|
if text.startswith("\ufeff"):
|
||||||
|
raise ProjectionError("AUTHORING_UTF8_BOM_FORBIDDEN", source)
|
||||||
|
for pattern in PLAINTEXT_SECRET_RES:
|
||||||
|
if pattern.search(text):
|
||||||
|
raise ProjectionError("AUTHORING_PLAINTEXT_SECRET", pattern.pattern)
|
||||||
|
try:
|
||||||
|
loaded = yaml.load(text, Loader=UniqueKeySafeLoader)
|
||||||
|
except ProjectionError:
|
||||||
|
raise
|
||||||
|
except yaml.YAMLError as exc:
|
||||||
|
raise ProjectionError("AUTHORING_YAML_INVALID", f"{source}: {exc}") from exc
|
||||||
|
if not isinstance(loaded, dict):
|
||||||
|
raise ProjectionError("AUTHORING_ROOT_OBJECT_REQUIRED", source)
|
||||||
|
return loaded
|
||||||
|
|
||||||
|
|
||||||
|
def load_authoring(path: Path | None = None) -> tuple[bytes, dict[str, Any]]:
|
||||||
|
path = AUTHORING_PATH if path is None else path
|
||||||
|
if not path.is_file():
|
||||||
|
raise AuthoringMissingError(path)
|
||||||
|
if path.is_symlink():
|
||||||
|
raise ProjectionError("AUTHORING_SYMLINK_FORBIDDEN", path.as_posix())
|
||||||
|
raw = path.read_bytes()
|
||||||
|
return raw, parse_authoring_bytes(raw, source=path.as_posix())
|
||||||
|
|
||||||
|
|
||||||
|
def _require_mapping(value: Any, code: str) -> dict[str, Any]:
|
||||||
|
if not isinstance(value, dict):
|
||||||
|
raise ProjectionError(code, repr(value)[:200])
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _require_list(value: Any, code: str) -> list[Any]:
|
||||||
|
if not isinstance(value, list):
|
||||||
|
raise ProjectionError(code, repr(value)[:200])
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _find_forbidden_model_fields(value: Any, pointer: str = "") -> list[str]:
|
||||||
|
forbidden = {"llm_provider", "llm_model", "llm_reasoning", "llm_verbosity", "prompt"}
|
||||||
|
findings: list[str] = []
|
||||||
|
if isinstance(value, dict):
|
||||||
|
for key, child in value.items():
|
||||||
|
child_pointer = f"{pointer}/{key}"
|
||||||
|
if key in forbidden:
|
||||||
|
findings.append(child_pointer)
|
||||||
|
findings.extend(_find_forbidden_model_fields(child, child_pointer))
|
||||||
|
elif isinstance(value, list):
|
||||||
|
for index, child in enumerate(value):
|
||||||
|
findings.extend(_find_forbidden_model_fields(child, f"{pointer}/{index}"))
|
||||||
|
return findings
|
||||||
|
|
||||||
|
|
||||||
|
def extract_run_code_task(document: Mapping[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
|
||||||
|
agent = _require_mapping(document.get("Agent"), "AGENT_OBJECT_REQUIRED")
|
||||||
|
if agent.get("name") != EXPECTED_AGENT_NAME or agent.get("version") != EXPECTED_AGENT_VERSION:
|
||||||
|
raise ProjectionError(
|
||||||
|
"AGENT_IDENTITY_MISMATCH",
|
||||||
|
f"expected {EXPECTED_AGENT_NAME}/{EXPECTED_AGENT_VERSION}",
|
||||||
|
)
|
||||||
|
forbidden_model_fields = _find_forbidden_model_fields(agent)
|
||||||
|
if forbidden_model_fields:
|
||||||
|
raise ProjectionError("LLM_OR_PROMPT_FIELD_FORBIDDEN", repr(forbidden_model_fields))
|
||||||
|
stages = _require_list(agent.get("Stages"), "STAGES_ARRAY_REQUIRED")
|
||||||
|
if len(stages) != 1:
|
||||||
|
raise ProjectionError("EXACTLY_ONE_STAGE_REQUIRED", str(len(stages)))
|
||||||
|
stage = _require_mapping(stages[0], "STAGE_OBJECT_REQUIRED")
|
||||||
|
if stage.get("name") != "S2_40":
|
||||||
|
raise ProjectionError("S2_40_STAGE_NAME_REQUIRED", repr(stage.get("name")))
|
||||||
|
if stage.get("skip_confirm") is not True:
|
||||||
|
raise ProjectionError(
|
||||||
|
"S2_40_SKIP_CONFIRM_TRUE_REQUIRED",
|
||||||
|
repr(stage.get("skip_confirm")),
|
||||||
|
)
|
||||||
|
for forbidden in ("llm_provider", "llm_model", "llm_reasoning", "llm_verbosity"):
|
||||||
|
if forbidden in stage:
|
||||||
|
raise ProjectionError("MODEL_FIELD_FORBIDDEN", forbidden)
|
||||||
|
|
||||||
|
servers = _require_mapping(
|
||||||
|
_require_mapping(stage.get("tools"), "TOOLS_OBJECT_REQUIRED").get("mcpServers"),
|
||||||
|
"MCP_SERVERS_OBJECT_REQUIRED",
|
||||||
|
)
|
||||||
|
if set(servers) != {"localdocs", "code-executor"}:
|
||||||
|
raise ProjectionError("MCP_SERVER_SET_MISMATCH", repr(sorted(servers)))
|
||||||
|
localdocs = _require_mapping(servers.get("localdocs"), "LOCALDOCS_SERVER_REQUIRED")
|
||||||
|
code_executor = _require_mapping(
|
||||||
|
servers.get("code-executor"), "CODE_EXECUTOR_SERVER_REQUIRED"
|
||||||
|
)
|
||||||
|
if localdocs.get("type") != "streamable-http" or localdocs.get("url") != EXPECTED_LOCALDOCS_URL:
|
||||||
|
raise ProjectionError("LOCALDOCS_SERVER_BINDING_INVALID", repr(localdocs))
|
||||||
|
if (
|
||||||
|
code_executor.get("type") != "streamable-http"
|
||||||
|
or code_executor.get("url") != EXPECTED_CODE_EXECUTOR_URL
|
||||||
|
):
|
||||||
|
raise ProjectionError("CODE_EXECUTOR_SERVER_BINDING_INVALID", repr(code_executor))
|
||||||
|
if "headers" in code_executor:
|
||||||
|
raise ProjectionError(
|
||||||
|
"PLAINTEXT_OR_INLINE_AUTH_HEADER_FORBIDDEN",
|
||||||
|
"authentication must be injected or pre-registered by the backend",
|
||||||
|
)
|
||||||
|
|
||||||
|
tasks = _require_list(stage.get("tasks"), "TASKS_ARRAY_REQUIRED")
|
||||||
|
if len(tasks) != 1:
|
||||||
|
raise ProjectionError("EXACTLY_ONE_TASK_REQUIRED", str(len(tasks)))
|
||||||
|
task = _require_mapping(tasks[0], "TASK_OBJECT_REQUIRED")
|
||||||
|
run_code_tasks = [
|
||||||
|
row
|
||||||
|
for row in tasks
|
||||||
|
if isinstance(row, dict)
|
||||||
|
and row.get("mcp") == "code-executor"
|
||||||
|
and row.get("tool_name") == "run_code"
|
||||||
|
]
|
||||||
|
if len(run_code_tasks) != 1:
|
||||||
|
raise ProjectionError("EXACTLY_ONE_RUN_CODE_REQUIRED", str(len(run_code_tasks)))
|
||||||
|
if task.get("task_name") != EXPECTED_TASK_NAME:
|
||||||
|
raise ProjectionError("TASK_NAME_MISMATCH", repr(task.get("task_name")))
|
||||||
|
parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED")
|
||||||
|
expected_parameter_keys = set(EXPECTED_PARAMETERS) | {"code"}
|
||||||
|
if set(parameters) != expected_parameter_keys:
|
||||||
|
raise ProjectionError(
|
||||||
|
"RUN_CODE_PARAMETER_SET_MISMATCH",
|
||||||
|
repr(sorted(parameters)),
|
||||||
|
)
|
||||||
|
for key, expected in EXPECTED_PARAMETERS.items():
|
||||||
|
if parameters.get(key) != expected:
|
||||||
|
raise ProjectionError(
|
||||||
|
"RUN_CODE_PARAMETER_MISMATCH",
|
||||||
|
f"{key}: expected {expected!r}, observed {parameters.get(key)!r}",
|
||||||
|
)
|
||||||
|
code = parameters.get("code")
|
||||||
|
if not isinstance(code, str) or not code:
|
||||||
|
raise ProjectionError("INLINE_CODE_REQUIRED", repr(code)[:100])
|
||||||
|
if code.endswith(("\n", "\r")):
|
||||||
|
raise ProjectionError(
|
||||||
|
"INLINE_CODE_TRAILING_NEWLINE_FORBIDDEN",
|
||||||
|
"authoring YAML must use code: |- so parser-returned code has no trailing newline",
|
||||||
|
)
|
||||||
|
|
||||||
|
procedure = _require_mapping(stage.get("task_procedure"), "TASK_PROCEDURE_REQUIRED")
|
||||||
|
expected_procedure = {
|
||||||
|
"IN": {"nexts": [EXPECTED_TASK_NAME], "wait_until": []},
|
||||||
|
EXPECTED_TASK_NAME: {"nexts": ["OUT"], "wait_until": ["IN"]},
|
||||||
|
"OUT": {"nexts": [], "wait_until": [EXPECTED_TASK_NAME]},
|
||||||
|
}
|
||||||
|
if procedure != expected_procedure:
|
||||||
|
raise ProjectionError("TASK_PROCEDURE_MISMATCH", repr(procedure)[:500])
|
||||||
|
if stage.get("prevs") != [] or stage.get("nexts") != []:
|
||||||
|
raise ProjectionError("STANDALONE_STAGE_EDGES_MUST_BE_EMPTY", repr(stage))
|
||||||
|
return stage, task
|
||||||
|
|
||||||
|
|
||||||
|
def _import_root(name: str | None) -> str:
|
||||||
|
return (name or "").split(".", 1)[0]
|
||||||
|
|
||||||
|
|
||||||
|
def _attribute_pair(node: ast.Attribute) -> tuple[str, str] | None:
|
||||||
|
if isinstance(node.value, ast.Name):
|
||||||
|
return node.value.id, node.attr
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _is_http_client_receiver(
|
||||||
|
node: ast.expr,
|
||||||
|
derived_client_names: set[str] | None = None,
|
||||||
|
) -> bool:
|
||||||
|
if isinstance(node, ast.Name):
|
||||||
|
return node.id in {"client", "http_client", "httpx"} | (derived_client_names or set())
|
||||||
|
if isinstance(node, ast.Attribute):
|
||||||
|
return (
|
||||||
|
isinstance(node.value, ast.Name)
|
||||||
|
and node.value.id == "self"
|
||||||
|
and node.attr in {"client", "http_client"}
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _is_direct_localdocs_post(call: ast.Call) -> bool:
|
||||||
|
if not isinstance(call.func, ast.Attribute) or call.func.attr != "post":
|
||||||
|
return False
|
||||||
|
receiver = call.func.value
|
||||||
|
if not (
|
||||||
|
isinstance(receiver, ast.Attribute)
|
||||||
|
and isinstance(receiver.value, ast.Name)
|
||||||
|
and receiver.value.id == "self"
|
||||||
|
and receiver.attr == "client"
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
return _is_localdocs_endpoint(_network_endpoint(call) or ast.Constant(value=None))
|
||||||
|
|
||||||
|
|
||||||
|
def _network_endpoint(call: ast.Call) -> ast.expr | None:
|
||||||
|
"""Return a statically identifiable httpx/client endpoint expression."""
|
||||||
|
|
||||||
|
if not isinstance(call.func, ast.Attribute) or call.func.attr not in HTTP_NETWORK_METHODS:
|
||||||
|
return None
|
||||||
|
if not _is_http_client_receiver(call.func.value):
|
||||||
|
return None
|
||||||
|
for keyword in call.keywords:
|
||||||
|
if keyword.arg == "url":
|
||||||
|
return keyword.value
|
||||||
|
index = 1 if call.func.attr == "request" else 0
|
||||||
|
return call.args[index] if len(call.args) > index else ast.Constant(value=None)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_localdocs_endpoint(node: ast.expr) -> bool:
|
||||||
|
return (
|
||||||
|
isinstance(node, ast.Name)
|
||||||
|
and node.id == "LOCALDOCS_URL"
|
||||||
|
or isinstance(node, ast.Constant)
|
||||||
|
and node.value == EXPECTED_LOCALDOCS_URL
|
||||||
|
or isinstance(node, ast.Attribute)
|
||||||
|
and isinstance(node.value, ast.Name)
|
||||||
|
and node.value.id == "self"
|
||||||
|
and node.attr == "endpoint"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_inline_code(code: str) -> dict[str, Any]:
|
||||||
|
code_bytes = code.encode("utf-8")
|
||||||
|
try:
|
||||||
|
compile(code, "<Stage_2_S2_40.parameters.code>", "exec", dont_inherit=True)
|
||||||
|
tree = ast.parse(code, filename="<Stage_2_S2_40.parameters.code>", mode="exec")
|
||||||
|
except (SyntaxError, ValueError, UnicodeError) as exc:
|
||||||
|
raise ProjectionError("INLINE_CODE_COMPILE_FAILED", str(exc)) from exc
|
||||||
|
|
||||||
|
missing_tokens = [token for token in REQUIRED_CODE_TOKENS if token not in code]
|
||||||
|
missing_tokens.extend(
|
||||||
|
"|".join(alternatives)
|
||||||
|
for alternatives in REQUIRED_CODE_TOKEN_ALTERNATIVES
|
||||||
|
if not any(token in code for token in alternatives)
|
||||||
|
)
|
||||||
|
if missing_tokens:
|
||||||
|
raise ProjectionError("INLINE_CODE_REQUIRED_TOKEN_MISSING", ",".join(missing_tokens))
|
||||||
|
if PLACEHOLDER_COMMENT_RE.search(code):
|
||||||
|
raise ProjectionError("INLINE_CODE_PLACEHOLDER_COMMENT", "TODO/FIXME/TBD placeholder")
|
||||||
|
for pattern in PLAINTEXT_SECRET_RES:
|
||||||
|
if pattern.search(code):
|
||||||
|
raise ProjectionError("INLINE_CODE_PLAINTEXT_SECRET", pattern.pattern)
|
||||||
|
|
||||||
|
imports: set[str] = set()
|
||||||
|
forbidden_nodes: list[str] = []
|
||||||
|
external_urls: set[str] = set()
|
||||||
|
forbidden_network_endpoints: set[str] = set()
|
||||||
|
project_source_refs: set[str] = set()
|
||||||
|
import_aliases: dict[str, str] = {}
|
||||||
|
forbidden_callable_aliases: set[str] = set()
|
||||||
|
mcp_client_endpoint_calls: list[ast.expr] = []
|
||||||
|
self_endpoint_assignments: list[ast.expr] = []
|
||||||
|
for candidate in ast.walk(tree):
|
||||||
|
if isinstance(candidate, ast.Import):
|
||||||
|
for alias in candidate.names:
|
||||||
|
import_aliases[alias.asname or _import_root(alias.name)] = _import_root(alias.name)
|
||||||
|
elif isinstance(candidate, ast.ImportFrom) and not candidate.level:
|
||||||
|
root = _import_root(candidate.module)
|
||||||
|
for alias in candidate.names:
|
||||||
|
local_name = alias.asname or alias.name
|
||||||
|
if root == "httpx" and (
|
||||||
|
alias.name in HTTP_NETWORK_METHODS or alias.name in HTTP_CLIENT_FACTORIES
|
||||||
|
):
|
||||||
|
forbidden_callable_aliases.add(local_name)
|
||||||
|
if root == "os" and (
|
||||||
|
alias.name in {name for module, name in FORBIDDEN_ATTRIBUTE_CALLS if module == "os"}
|
||||||
|
or alias.name.startswith("exec")
|
||||||
|
):
|
||||||
|
forbidden_callable_aliases.add(local_name)
|
||||||
|
if root == "builtins" and alias.name in FORBIDDEN_CALL_NAMES:
|
||||||
|
forbidden_callable_aliases.add(local_name)
|
||||||
|
|
||||||
|
derived_client_names: set[str] = set()
|
||||||
|
release_constants: list[str] = []
|
||||||
|
for candidate in ast.walk(tree):
|
||||||
|
if not isinstance(candidate, (ast.Assign, ast.AnnAssign)):
|
||||||
|
continue
|
||||||
|
value = candidate.value
|
||||||
|
targets = candidate.targets if isinstance(candidate, ast.Assign) else [candidate.target]
|
||||||
|
target_names = {target.id for target in targets if isinstance(target, ast.Name)}
|
||||||
|
if (
|
||||||
|
"EXPECTED_STAGE2_RELEASE_SHA256" in target_names
|
||||||
|
and isinstance(value, ast.Constant)
|
||||||
|
and isinstance(value.value, str)
|
||||||
|
):
|
||||||
|
release_constants.append(value.value)
|
||||||
|
if (
|
||||||
|
isinstance(value, ast.Call)
|
||||||
|
and isinstance(value.func, ast.Attribute)
|
||||||
|
and isinstance(value.func.value, ast.Name)
|
||||||
|
and import_aliases.get(value.func.value.id, value.func.value.id) == "httpx"
|
||||||
|
and value.func.attr in HTTP_CLIENT_FACTORIES
|
||||||
|
):
|
||||||
|
derived_client_names.update(target_names)
|
||||||
|
if isinstance(value, ast.Name) and (
|
||||||
|
value.id in FORBIDDEN_CALL_NAMES or value.id in forbidden_callable_aliases
|
||||||
|
):
|
||||||
|
forbidden_callable_aliases.update(target_names)
|
||||||
|
if isinstance(value, ast.Attribute) and isinstance(value.value, ast.Name):
|
||||||
|
module = import_aliases.get(value.value.id, value.value.id)
|
||||||
|
if (module, value.attr) in FORBIDDEN_ATTRIBUTE_CALLS or (
|
||||||
|
module == "os" and value.attr.startswith("exec")
|
||||||
|
):
|
||||||
|
forbidden_callable_aliases.update(target_names)
|
||||||
|
if module == "httpx" and value.attr in HTTP_NETWORK_METHODS:
|
||||||
|
forbidden_callable_aliases.update(target_names)
|
||||||
|
for node in ast.walk(tree):
|
||||||
|
if isinstance(node, ast.Import):
|
||||||
|
imports.update(_import_root(alias.name) for alias in node.names)
|
||||||
|
elif isinstance(node, ast.ImportFrom):
|
||||||
|
if node.level:
|
||||||
|
forbidden_nodes.append(f"relative-import:{node.module or ''}")
|
||||||
|
imports.add(_import_root(node.module))
|
||||||
|
root = _import_root(node.module)
|
||||||
|
for alias in node.names:
|
||||||
|
local_name = alias.asname or alias.name
|
||||||
|
if local_name in forbidden_callable_aliases:
|
||||||
|
forbidden_nodes.append(f"forbidden-import-alias:{root}.{alias.name}")
|
||||||
|
elif isinstance(node, ast.Pass):
|
||||||
|
forbidden_nodes.append("Pass")
|
||||||
|
elif isinstance(node, ast.Expr) and isinstance(node.value, ast.Constant):
|
||||||
|
if node.value.value is Ellipsis:
|
||||||
|
forbidden_nodes.append("Ellipsis-expression")
|
||||||
|
elif isinstance(node, (ast.Assign, ast.AnnAssign)):
|
||||||
|
assignment_targets = node.targets if isinstance(node, ast.Assign) else [node.target]
|
||||||
|
if any(
|
||||||
|
isinstance(target, ast.Attribute)
|
||||||
|
and isinstance(target.value, ast.Name)
|
||||||
|
and target.value.id == "self"
|
||||||
|
and target.attr == "endpoint"
|
||||||
|
for target in assignment_targets
|
||||||
|
):
|
||||||
|
self_endpoint_assignments.append(node.value)
|
||||||
|
if isinstance(node.value, ast.Constant) and node.value.value is Ellipsis:
|
||||||
|
forbidden_nodes.append("Ellipsis-assignment")
|
||||||
|
if (
|
||||||
|
isinstance(node.value, ast.Attribute)
|
||||||
|
and node.value.attr in HTTP_NETWORK_METHODS
|
||||||
|
and _is_http_client_receiver(node.value.value, derived_client_names)
|
||||||
|
):
|
||||||
|
forbidden_nodes.append(f"network-method-alias:{node.value.attr}")
|
||||||
|
elif isinstance(node, ast.Constant):
|
||||||
|
if isinstance(node.value, str):
|
||||||
|
for match in URL_RE.findall(node.value):
|
||||||
|
normalized = match.rstrip(".,);]")
|
||||||
|
if (
|
||||||
|
normalized != EXPECTED_LOCALDOCS_URL
|
||||||
|
and not normalized.startswith(ALLOWED_IDENTIFIER_URL_PREFIXES)
|
||||||
|
):
|
||||||
|
external_urls.add(normalized)
|
||||||
|
if PYTHON_SOURCE_REF_RE.search(node.value.strip()):
|
||||||
|
project_source_refs.add(node.value[:200])
|
||||||
|
if node.value.strip().lower() in {
|
||||||
|
"todo",
|
||||||
|
"tbd",
|
||||||
|
"placeholder",
|
||||||
|
"omitted",
|
||||||
|
"implement me",
|
||||||
|
"...",
|
||||||
|
}:
|
||||||
|
forbidden_nodes.append(f"placeholder-string:{node.value!r}")
|
||||||
|
elif isinstance(node, ast.Call):
|
||||||
|
if isinstance(node.func, ast.Name) and node.func.id == "McpClient":
|
||||||
|
mcp_client_endpoint_calls.append(
|
||||||
|
node.args[0] if node.args else ast.Constant(value=None)
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
isinstance(node.func, ast.Attribute)
|
||||||
|
and node.func.attr in HTTP_NETWORK_METHODS
|
||||||
|
and _is_http_client_receiver(node.func.value, derived_client_names)
|
||||||
|
):
|
||||||
|
if not _is_direct_localdocs_post(node):
|
||||||
|
forbidden_network_endpoints.add(ast.unparse(node.func)[:200])
|
||||||
|
if (
|
||||||
|
isinstance(node.func, ast.Attribute)
|
||||||
|
and node.func.attr in HTTP_NETWORK_METHODS
|
||||||
|
and isinstance(node.func.value, ast.Call)
|
||||||
|
and isinstance(node.func.value.func, ast.Attribute)
|
||||||
|
and isinstance(node.func.value.func.value, ast.Name)
|
||||||
|
and import_aliases.get(
|
||||||
|
node.func.value.func.value.id,
|
||||||
|
node.func.value.func.value.id,
|
||||||
|
) == "httpx"
|
||||||
|
and node.func.value.func.attr in HTTP_CLIENT_FACTORIES
|
||||||
|
):
|
||||||
|
forbidden_network_endpoints.add(ast.unparse(node.func)[:200])
|
||||||
|
if (
|
||||||
|
isinstance(node.func, ast.Name)
|
||||||
|
and node.func.id == "getattr"
|
||||||
|
and len(node.args) >= 2
|
||||||
|
and isinstance(node.args[1], ast.Constant)
|
||||||
|
and node.args[1].value in HTTP_NETWORK_METHODS
|
||||||
|
):
|
||||||
|
forbidden_nodes.append(f"dynamic-network-method:{node.args[1].value}")
|
||||||
|
if isinstance(node.func, ast.Name) and (
|
||||||
|
node.func.id in FORBIDDEN_CALL_NAMES
|
||||||
|
or node.func.id in forbidden_callable_aliases
|
||||||
|
):
|
||||||
|
forbidden_nodes.append(f"call:{node.func.id}")
|
||||||
|
elif isinstance(node.func, ast.Attribute):
|
||||||
|
pair = _attribute_pair(node.func)
|
||||||
|
if pair is not None:
|
||||||
|
module = import_aliases.get(pair[0], pair[0])
|
||||||
|
if (module, pair[1]) in FORBIDDEN_ATTRIBUTE_CALLS or (
|
||||||
|
module == "os" and pair[1].startswith("exec")
|
||||||
|
):
|
||||||
|
forbidden_nodes.append(f"call:{module}.{pair[1]}")
|
||||||
|
elif isinstance(node, ast.Raise):
|
||||||
|
target = node.exc
|
||||||
|
if isinstance(target, ast.Call):
|
||||||
|
target = target.func
|
||||||
|
if isinstance(target, ast.Name) and target.id == "NotImplementedError":
|
||||||
|
forbidden_nodes.append("raise:NotImplementedError")
|
||||||
|
|
||||||
|
imports.discard("")
|
||||||
|
disallowed_imports = sorted(
|
||||||
|
root
|
||||||
|
for root in imports
|
||||||
|
if root in FORBIDDEN_IMPORT_ROOTS
|
||||||
|
or (root not in sys.stdlib_module_names and root not in ALLOWED_NON_STDLIB_IMPORTS)
|
||||||
|
)
|
||||||
|
if disallowed_imports:
|
||||||
|
raise ProjectionError("INLINE_CODE_IMPORT_FORBIDDEN", ",".join(disallowed_imports))
|
||||||
|
if forbidden_nodes:
|
||||||
|
raise ProjectionError("INLINE_CODE_DYNAMIC_OR_PLACEHOLDER_FORBIDDEN", ",".join(forbidden_nodes))
|
||||||
|
if external_urls:
|
||||||
|
raise ProjectionError("INLINE_CODE_EXTERNAL_URL_FORBIDDEN", ",".join(sorted(external_urls)))
|
||||||
|
if forbidden_network_endpoints:
|
||||||
|
raise ProjectionError(
|
||||||
|
"INLINE_CODE_NETWORK_ENDPOINT_FORBIDDEN",
|
||||||
|
",".join(sorted(forbidden_network_endpoints)),
|
||||||
|
)
|
||||||
|
if project_source_refs:
|
||||||
|
raise ProjectionError(
|
||||||
|
"INLINE_CODE_EXTERNAL_PY_SOURCE_REF_FORBIDDEN", ",".join(sorted(project_source_refs))
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
len(self_endpoint_assignments) != 1
|
||||||
|
or not isinstance(self_endpoint_assignments[0], ast.Name)
|
||||||
|
or self_endpoint_assignments[0].id != "endpoint"
|
||||||
|
or not mcp_client_endpoint_calls
|
||||||
|
or any(not _is_localdocs_endpoint(endpoint) for endpoint in mcp_client_endpoint_calls)
|
||||||
|
):
|
||||||
|
raise ProjectionError(
|
||||||
|
"INLINE_CODE_LOCALDOCS_ENDPOINT_FLOW_INVALID",
|
||||||
|
f"assignments={len(self_endpoint_assignments)};constructors={len(mcp_client_endpoint_calls)}",
|
||||||
|
)
|
||||||
|
if len(release_constants) != 1 or not re.fullmatch(r"[a-f0-9]{64}", release_constants[0]):
|
||||||
|
raise ProjectionError(
|
||||||
|
"EXPECTED_PARENT_RELEASE_CONSTANT_EXACT_ONE_REQUIRED",
|
||||||
|
repr(release_constants),
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"code_sha256": sha256_bytes(code_bytes),
|
||||||
|
"code_size_bytes": len(code_bytes),
|
||||||
|
"compile_status": "PASS",
|
||||||
|
"ast_status": "PASS",
|
||||||
|
"imports": sorted(imports),
|
||||||
|
"forbidden_import_count": 0,
|
||||||
|
"forbidden_dynamic_call_count": 0,
|
||||||
|
"external_url_count": 0,
|
||||||
|
"placeholder_count": 0,
|
||||||
|
"plaintext_secret_count": 0,
|
||||||
|
"external_python_source_ref_count": 0,
|
||||||
|
"expected_parent_stage2_release_sha256": release_constants[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _canonical_task_semantics(document: Mapping[str, Any], stage: Mapping[str, Any], task: Mapping[str, Any]) -> dict[str, Any]:
|
||||||
|
agent = _require_mapping(document.get("Agent"), "AGENT_OBJECT_REQUIRED")
|
||||||
|
servers = _require_mapping(
|
||||||
|
_require_mapping(stage.get("tools"), "TOOLS_OBJECT_REQUIRED").get("mcpServers"),
|
||||||
|
"MCP_SERVERS_OBJECT_REQUIRED",
|
||||||
|
)
|
||||||
|
parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED")
|
||||||
|
return {
|
||||||
|
"agent": {"name": agent.get("name"), "version": agent.get("version")},
|
||||||
|
"stage": {
|
||||||
|
"name": stage.get("name"),
|
||||||
|
"prevs": stage.get("prevs"),
|
||||||
|
"nexts": stage.get("nexts"),
|
||||||
|
},
|
||||||
|
"mcp_servers": {
|
||||||
|
name: {"type": value.get("type"), "url": value.get("url")}
|
||||||
|
for name, value in sorted(servers.items())
|
||||||
|
if isinstance(value, dict)
|
||||||
|
},
|
||||||
|
"task": {
|
||||||
|
"task_name": task.get("task_name"),
|
||||||
|
"mcp": task.get("mcp"),
|
||||||
|
"tool_name": task.get("tool_name"),
|
||||||
|
"parameters": {
|
||||||
|
key: parameters.get(key)
|
||||||
|
for key in ("language", "requirements", "network", "timeout")
|
||||||
|
},
|
||||||
|
"code_sha256": sha256_bytes(parameters["code"].encode("utf-8")),
|
||||||
|
},
|
||||||
|
"task_procedure": stage.get("task_procedure"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def expected_outputs(
|
||||||
|
authoring_raw: bytes,
|
||||||
|
document: Mapping[str, Any],
|
||||||
|
) -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
|
stage, task = extract_run_code_task(document)
|
||||||
|
parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED")
|
||||||
|
code = parameters["code"]
|
||||||
|
validation = validate_inline_code(code)
|
||||||
|
code_bytes = code.encode("utf-8")
|
||||||
|
semantics = _canonical_task_semantics(document, stage, task)
|
||||||
|
semantics_sha256 = sha256_bytes(canonical_json_bytes(semantics))
|
||||||
|
|
||||||
|
receipt = {
|
||||||
|
"schema_version": "stage2_s2_40_inline_code_receipt.v1",
|
||||||
|
"workflow_id": "S2_40",
|
||||||
|
"build_kind": "OFFLINE_AUTHORING_PROJECTION",
|
||||||
|
"source_of_truth": _logical_path(AUTHORING_PATH),
|
||||||
|
"authoring_rewritten": False,
|
||||||
|
"authoring": {
|
||||||
|
"path": _logical_path(AUTHORING_PATH),
|
||||||
|
"sha256": sha256_bytes(authoring_raw),
|
||||||
|
"size_bytes": len(authoring_raw),
|
||||||
|
"unique_key_parse": "PASS",
|
||||||
|
},
|
||||||
|
"deployment_projection": {
|
||||||
|
"path": _logical_path(PROJECTION_PATH),
|
||||||
|
"sha256": sha256_bytes(authoring_raw),
|
||||||
|
"size_bytes": len(authoring_raw),
|
||||||
|
"byte_identical_to_authoring": True,
|
||||||
|
"canonical_task_semantics_sha256": semantics_sha256,
|
||||||
|
},
|
||||||
|
"canonical_code": {
|
||||||
|
"yaml_pointer": "/Agent/Stages/0/tasks/0/parameters/code",
|
||||||
|
"encoding": "UTF-8",
|
||||||
|
"extraction_transform": "NONE",
|
||||||
|
**validation,
|
||||||
|
},
|
||||||
|
"full_code_mirrors": [
|
||||||
|
{
|
||||||
|
"path": _logical_path(MIRROR_PY_PATH),
|
||||||
|
"sha256": validation["code_sha256"],
|
||||||
|
"size_bytes": len(code_bytes),
|
||||||
|
"byte_identical_to_canonical_code": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": _logical_path(MIRROR_TXT_PATH),
|
||||||
|
"sha256": validation["code_sha256"],
|
||||||
|
"size_bytes": len(code_bytes),
|
||||||
|
"byte_identical_to_canonical_code": True,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"task_contract": {
|
||||||
|
**semantics,
|
||||||
|
"exactly_one_stage": True,
|
||||||
|
"exactly_one_task": True,
|
||||||
|
"exactly_one_code_executor_run_code": True,
|
||||||
|
"authoring_rewritten": False,
|
||||||
|
"projection_byte_identical_to_authoring": True,
|
||||||
|
"code_mirrors_byte_identical": True,
|
||||||
|
"canonical_task_semantics_sha256": semantics_sha256,
|
||||||
|
},
|
||||||
|
"expected_parent_stage2_release_sha256": validation[
|
||||||
|
"expected_parent_stage2_release_sha256"
|
||||||
|
],
|
||||||
|
"parity_status": "PASS",
|
||||||
|
}
|
||||||
|
outputs = {
|
||||||
|
PROJECTION_PATH: authoring_raw,
|
||||||
|
MIRROR_PY_PATH: code_bytes,
|
||||||
|
MIRROR_TXT_PATH: code_bytes,
|
||||||
|
RECEIPT_PATH: canonical_json_bytes(receipt),
|
||||||
|
}
|
||||||
|
return outputs, receipt
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_output_target(path: Path) -> None:
|
||||||
|
root = DEPLOYMENT_ROOT.resolve(strict=True)
|
||||||
|
resolved = path.resolve(strict=False)
|
||||||
|
try:
|
||||||
|
resolved.relative_to(root)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise ProjectionError("OUTPUT_OUTSIDE_DEPLOYMENT_ROOT", path.as_posix()) from exc
|
||||||
|
if path.exists() and path.is_symlink():
|
||||||
|
raise ProjectionError("OUTPUT_SYMLINK_FORBIDDEN", path.as_posix())
|
||||||
|
|
||||||
|
|
||||||
|
def _atomic_write(path: Path, payload: bytes) -> None:
|
||||||
|
_assert_output_target(path)
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
temporary_name: str | None = None
|
||||||
|
try:
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode="wb",
|
||||||
|
dir=path.parent,
|
||||||
|
prefix=f".{path.name}.",
|
||||||
|
suffix=".tmp",
|
||||||
|
delete=False,
|
||||||
|
) as handle:
|
||||||
|
temporary_name = handle.name
|
||||||
|
handle.write(payload)
|
||||||
|
handle.flush()
|
||||||
|
os.fsync(handle.fileno())
|
||||||
|
os.replace(temporary_name, path)
|
||||||
|
temporary_name = None
|
||||||
|
finally:
|
||||||
|
if temporary_name is not None:
|
||||||
|
try:
|
||||||
|
Path(temporary_name).unlink()
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def compare_outputs(outputs: Mapping[Path, bytes]) -> list[dict[str, Any]]:
|
||||||
|
mismatches: list[dict[str, Any]] = []
|
||||||
|
for path, expected in outputs.items():
|
||||||
|
if not path.is_file():
|
||||||
|
mismatches.append(
|
||||||
|
{"path": _logical_path(path), "status": "MISSING", "expected_sha256": sha256_bytes(expected)}
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
observed = path.read_bytes()
|
||||||
|
if observed != expected:
|
||||||
|
mismatches.append(
|
||||||
|
{
|
||||||
|
"path": _logical_path(path),
|
||||||
|
"status": "BYTE_MISMATCH",
|
||||||
|
"expected_sha256": sha256_bytes(expected),
|
||||||
|
"observed_sha256": sha256_bytes(observed),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return mismatches
|
||||||
|
|
||||||
|
|
||||||
|
def run(*, check: bool) -> tuple[int, dict[str, Any]]:
|
||||||
|
before, document = load_authoring()
|
||||||
|
outputs, receipt = expected_outputs(before, document)
|
||||||
|
if check:
|
||||||
|
mismatches = compare_outputs(outputs)
|
||||||
|
return (
|
||||||
|
0 if not mismatches else 1,
|
||||||
|
{
|
||||||
|
"status": "PARITY_PASS" if not mismatches else "PARITY_DRIFT",
|
||||||
|
"mode": "CHECK_NO_WRITE",
|
||||||
|
"authoring_sha256": sha256_bytes(before),
|
||||||
|
"code_sha256": receipt["canonical_code"]["code_sha256"],
|
||||||
|
"mismatches": mismatches,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
receipt_payload = outputs[RECEIPT_PATH]
|
||||||
|
for path, payload in outputs.items():
|
||||||
|
if path == RECEIPT_PATH:
|
||||||
|
continue
|
||||||
|
_atomic_write(path, payload)
|
||||||
|
after_artifacts = AUTHORING_PATH.read_bytes()
|
||||||
|
if after_artifacts != before:
|
||||||
|
raise ProjectionError(
|
||||||
|
"AUTHORING_CHANGED_DURING_BUILD",
|
||||||
|
f"before={sha256_bytes(before)} after={sha256_bytes(after_artifacts)}",
|
||||||
|
)
|
||||||
|
_atomic_write(RECEIPT_PATH, receipt_payload)
|
||||||
|
after_receipt = AUTHORING_PATH.read_bytes()
|
||||||
|
if after_receipt != before:
|
||||||
|
raise ProjectionError(
|
||||||
|
"AUTHORING_CHANGED_DURING_RECEIPT_WRITE",
|
||||||
|
f"before={sha256_bytes(before)} after={sha256_bytes(after_receipt)}",
|
||||||
|
)
|
||||||
|
mismatches = compare_outputs(outputs)
|
||||||
|
if mismatches:
|
||||||
|
raise ProjectionError("POST_BUILD_PARITY_FAILED", json.dumps(mismatches, sort_keys=True))
|
||||||
|
return 0, {
|
||||||
|
"status": "BUILT_AND_VERIFIED",
|
||||||
|
"mode": "BUILD",
|
||||||
|
"authoring_sha256": sha256_bytes(before),
|
||||||
|
"code_sha256": receipt["canonical_code"]["code_sha256"],
|
||||||
|
"outputs": [_logical_path(path) for path in outputs],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _parser() -> argparse.ArgumentParser:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Build or verify the S2_40 inline Agent projection"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--check",
|
||||||
|
action="store_true",
|
||||||
|
help="perform a no-write byte-parity check against generated outputs",
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: Iterable[str] | None = None) -> int:
|
||||||
|
args = _parser().parse_args(list(argv) if argv is not None else None)
|
||||||
|
try:
|
||||||
|
status, payload = run(check=args.check)
|
||||||
|
except ProjectionError as exc:
|
||||||
|
status = 3 if exc.code == "AUTHORING_YAML_MISSING" else 2
|
||||||
|
payload = {
|
||||||
|
"status": "CONTROLLED_MISSING_AUTHORING" if status == 3 else "BUILD_FAILED",
|
||||||
|
"reason_code": exc.code,
|
||||||
|
"detail": exc.detail,
|
||||||
|
"mode": "CHECK_NO_WRITE" if args.check else "BUILD",
|
||||||
|
}
|
||||||
|
print(json.dumps(payload, ensure_ascii=False, allow_nan=False, sort_keys=True))
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
+894
@@ -0,0 +1,894 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Build the version-free S2_40 Agent projection from its sole authoring YAML.
|
||||||
|
|
||||||
|
This is an offline build tool. It never executes a matter, imports project
|
||||||
|
runtime code, rewrites the authoring YAML, or invokes a subprocess. The YAML
|
||||||
|
parser-returned ``parameters.code`` string is encoded directly as UTF-8; no
|
||||||
|
dedent, newline normalization, or source transformation is permitted.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import ast
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from typing import Any, Iterable, Mapping
|
||||||
|
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except ImportError: # pragma: no cover - exercised only on an incomplete build host
|
||||||
|
yaml = None # type: ignore[assignment]
|
||||||
|
|
||||||
|
|
||||||
|
DEPLOYMENT_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
MAIN_WORKING_DIRECTORY = DEPLOYMENT_ROOT.parent.parent
|
||||||
|
AUTHORING_PATH = MAIN_WORKING_DIRECTORY / "Stage_2_S2_40.yml"
|
||||||
|
PROJECTION_PATH = DEPLOYMENT_ROOT / "agent_scripts" / "Stage_2_S2_40.yml"
|
||||||
|
MIRROR_PY_PATH = DEPLOYMENT_ROOT / "runtime" / "s2_40_commit.py"
|
||||||
|
MIRROR_TXT_PATH = DEPLOYMENT_ROOT / "runtime" / "s2_40_commit.txt"
|
||||||
|
RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_40_inline_code_receipt.json"
|
||||||
|
|
||||||
|
EXPECTED_TASK_NAME = "Task_S2_40_deterministic_finalizer"
|
||||||
|
EXPECTED_AGENT_NAME = "Stage_2_S2_40"
|
||||||
|
EXPECTED_AGENT_VERSION = "1.0.0"
|
||||||
|
EXPECTED_PARAMETERS = {
|
||||||
|
"language": "python",
|
||||||
|
"requirements": "httpx==0.28.1",
|
||||||
|
"network": "agent-network",
|
||||||
|
"timeout": 300,
|
||||||
|
}
|
||||||
|
EXPECTED_LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
||||||
|
EXPECTED_CODE_EXECUTOR_URL = "https://code-executor.mcp.eroomai.com/mcp"
|
||||||
|
REQUIRED_CODE_TOKENS = (
|
||||||
|
EXPECTED_LOCALDOCS_URL,
|
||||||
|
"{{__user_hash__}}",
|
||||||
|
"{{__workspace_hash__}}",
|
||||||
|
"read_binary_doc",
|
||||||
|
"write_binary_file",
|
||||||
|
"stage2_control/s2_40_request.json",
|
||||||
|
"run_status.json",
|
||||||
|
"reduce_and_render",
|
||||||
|
"invariant_results",
|
||||||
|
"candidate_material",
|
||||||
|
"publish_candidate",
|
||||||
|
"validate_review_receipts",
|
||||||
|
"final_status",
|
||||||
|
)
|
||||||
|
REQUIRED_CODE_TOKEN_ALTERNATIVES = (
|
||||||
|
("EXPECTED_STAGE2_RELEASE_SHA256",),
|
||||||
|
)
|
||||||
|
ALLOWED_NON_STDLIB_IMPORTS = frozenset({"httpx"})
|
||||||
|
FORBIDDEN_IMPORT_ROOTS = frozenset(
|
||||||
|
{
|
||||||
|
"ftplib",
|
||||||
|
"http",
|
||||||
|
"importlib",
|
||||||
|
"smtplib",
|
||||||
|
"socket",
|
||||||
|
"subprocess",
|
||||||
|
"telnetlib",
|
||||||
|
"urllib",
|
||||||
|
"xmlrpc",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
FORBIDDEN_CALL_NAMES = frozenset({"__import__", "compile", "eval", "exec", "open"})
|
||||||
|
FORBIDDEN_ATTRIBUTE_CALLS = frozenset(
|
||||||
|
{
|
||||||
|
("os", "popen"),
|
||||||
|
("os", "spawnl"),
|
||||||
|
("os", "spawnle"),
|
||||||
|
("os", "spawnlp"),
|
||||||
|
("os", "spawnlpe"),
|
||||||
|
("os", "spawnv"),
|
||||||
|
("os", "spawnve"),
|
||||||
|
("os", "spawnvp"),
|
||||||
|
("os", "spawnvpe"),
|
||||||
|
("os", "system"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
PLACEHOLDER_COMMENT_RE = re.compile(
|
||||||
|
r"(?im)^\s*#\s*(?:TODO|FIXME|TBD|PLACEHOLDER|OMITTED\s+BODY|IMPLEMENT\s+ME)\b"
|
||||||
|
)
|
||||||
|
PLAINTEXT_SECRET_RES = (
|
||||||
|
re.compile(r"(?i)\bAuthorization\s*:\s*Bearer\s+\S+"),
|
||||||
|
re.compile(r"(?i)\bBearer\s+[A-Za-z0-9+/=_-]{12,}"),
|
||||||
|
re.compile(
|
||||||
|
r"(?i)\b(?:MCP_API_KEY|API_KEY|ACCESS_TOKEN|AUTH_TOKEN|CLIENT_SECRET)\s*=\s*['\"][^'\"]+['\"]"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
URL_RE = re.compile(r"https?://[^\s'\"]+")
|
||||||
|
PYTHON_SOURCE_REF_RE = re.compile(r"(?i)(?:^|[/\\])[^\r\n'\"]+\.py(?:$|[?#])")
|
||||||
|
ALLOWED_IDENTIFIER_URL_PREFIXES = ("https://schemas.liti-agent.local/",)
|
||||||
|
HTTP_NETWORK_METHODS = frozenset({"delete", "get", "head", "options", "patch", "post", "put", "request", "stream"})
|
||||||
|
HTTP_CLIENT_FACTORIES = frozenset({"Client", "AsyncClient"})
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectionError(RuntimeError):
|
||||||
|
"""A controlled build failure with a stable reason code."""
|
||||||
|
|
||||||
|
def __init__(self, code: str, detail: str) -> None:
|
||||||
|
super().__init__(f"{code}: {detail}")
|
||||||
|
self.code = code
|
||||||
|
self.detail = detail
|
||||||
|
|
||||||
|
|
||||||
|
class AuthoringMissingError(ProjectionError):
|
||||||
|
def __init__(self, path: Path) -> None:
|
||||||
|
super().__init__("AUTHORING_YAML_MISSING", path.as_posix())
|
||||||
|
|
||||||
|
|
||||||
|
if yaml is not None:
|
||||||
|
|
||||||
|
class UniqueKeySafeLoader(yaml.SafeLoader):
|
||||||
|
"""SafeLoader variant that rejects duplicate mapping keys recursively."""
|
||||||
|
|
||||||
|
def construct_mapping(self, node: Any, deep: bool = False) -> dict[Any, Any]:
|
||||||
|
if not isinstance(node, yaml.MappingNode):
|
||||||
|
raise ProjectionError("YAML_MAPPING_REQUIRED", repr(node)[:200])
|
||||||
|
self.flatten_mapping(node)
|
||||||
|
result: dict[Any, Any] = {}
|
||||||
|
for key_node, value_node in node.value:
|
||||||
|
key = self.construct_object(key_node, deep=deep)
|
||||||
|
try:
|
||||||
|
duplicate = key in result
|
||||||
|
except TypeError as exc:
|
||||||
|
raise ProjectionError("YAML_UNHASHABLE_KEY", repr(key)[:200]) from exc
|
||||||
|
if duplicate:
|
||||||
|
mark = getattr(key_node, "start_mark", None)
|
||||||
|
location = f" line {mark.line + 1}" if mark is not None else ""
|
||||||
|
raise ProjectionError("YAML_DUPLICATE_KEY", f"{key!r}{location}")
|
||||||
|
result[key] = self.construct_object(value_node, deep=deep)
|
||||||
|
return result
|
||||||
|
|
||||||
|
else: # pragma: no cover - type placeholder for hosts without PyYAML
|
||||||
|
|
||||||
|
class UniqueKeySafeLoader: # type: ignore[no-redef]
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def sha256_bytes(value: bytes) -> str:
|
||||||
|
return hashlib.sha256(value).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def canonical_json_bytes(value: Any) -> bytes:
|
||||||
|
return (
|
||||||
|
json.dumps(
|
||||||
|
value,
|
||||||
|
ensure_ascii=False,
|
||||||
|
allow_nan=False,
|
||||||
|
sort_keys=True,
|
||||||
|
separators=(",", ":"),
|
||||||
|
)
|
||||||
|
+ "\n"
|
||||||
|
).encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _logical_path(path: Path) -> str:
|
||||||
|
try:
|
||||||
|
return path.resolve(strict=False).relative_to(
|
||||||
|
MAIN_WORKING_DIRECTORY.resolve(strict=False)
|
||||||
|
).as_posix()
|
||||||
|
except ValueError:
|
||||||
|
return path.as_posix()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_authoring_bytes(raw: bytes, *, source: str = "<authoring>") -> dict[str, Any]:
|
||||||
|
if yaml is None:
|
||||||
|
raise ProjectionError("PYYAML_REQUIRED", "install PyYAML on the offline build host")
|
||||||
|
try:
|
||||||
|
text = raw.decode("utf-8")
|
||||||
|
except UnicodeDecodeError as exc:
|
||||||
|
raise ProjectionError("AUTHORING_UTF8_REQUIRED", f"{source}: {exc}") from exc
|
||||||
|
if text.startswith("\ufeff"):
|
||||||
|
raise ProjectionError("AUTHORING_UTF8_BOM_FORBIDDEN", source)
|
||||||
|
for pattern in PLAINTEXT_SECRET_RES:
|
||||||
|
if pattern.search(text):
|
||||||
|
raise ProjectionError("AUTHORING_PLAINTEXT_SECRET", pattern.pattern)
|
||||||
|
try:
|
||||||
|
loaded = yaml.load(text, Loader=UniqueKeySafeLoader)
|
||||||
|
except ProjectionError:
|
||||||
|
raise
|
||||||
|
except yaml.YAMLError as exc:
|
||||||
|
raise ProjectionError("AUTHORING_YAML_INVALID", f"{source}: {exc}") from exc
|
||||||
|
if not isinstance(loaded, dict):
|
||||||
|
raise ProjectionError("AUTHORING_ROOT_OBJECT_REQUIRED", source)
|
||||||
|
return loaded
|
||||||
|
|
||||||
|
|
||||||
|
def load_authoring(path: Path | None = None) -> tuple[bytes, dict[str, Any]]:
|
||||||
|
path = AUTHORING_PATH if path is None else path
|
||||||
|
if not path.is_file():
|
||||||
|
raise AuthoringMissingError(path)
|
||||||
|
if path.is_symlink():
|
||||||
|
raise ProjectionError("AUTHORING_SYMLINK_FORBIDDEN", path.as_posix())
|
||||||
|
raw = path.read_bytes()
|
||||||
|
return raw, parse_authoring_bytes(raw, source=path.as_posix())
|
||||||
|
|
||||||
|
|
||||||
|
def _require_mapping(value: Any, code: str) -> dict[str, Any]:
|
||||||
|
if not isinstance(value, dict):
|
||||||
|
raise ProjectionError(code, repr(value)[:200])
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _require_list(value: Any, code: str) -> list[Any]:
|
||||||
|
if not isinstance(value, list):
|
||||||
|
raise ProjectionError(code, repr(value)[:200])
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _find_forbidden_model_fields(value: Any, pointer: str = "") -> list[str]:
|
||||||
|
forbidden = {"llm_provider", "llm_model", "llm_reasoning", "llm_verbosity", "prompt"}
|
||||||
|
findings: list[str] = []
|
||||||
|
if isinstance(value, dict):
|
||||||
|
for key, child in value.items():
|
||||||
|
child_pointer = f"{pointer}/{key}"
|
||||||
|
if key in forbidden:
|
||||||
|
findings.append(child_pointer)
|
||||||
|
findings.extend(_find_forbidden_model_fields(child, child_pointer))
|
||||||
|
elif isinstance(value, list):
|
||||||
|
for index, child in enumerate(value):
|
||||||
|
findings.extend(_find_forbidden_model_fields(child, f"{pointer}/{index}"))
|
||||||
|
return findings
|
||||||
|
|
||||||
|
|
||||||
|
def extract_run_code_task(document: Mapping[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
|
||||||
|
agent = _require_mapping(document.get("Agent"), "AGENT_OBJECT_REQUIRED")
|
||||||
|
if agent.get("name") != EXPECTED_AGENT_NAME or agent.get("version") != EXPECTED_AGENT_VERSION:
|
||||||
|
raise ProjectionError(
|
||||||
|
"AGENT_IDENTITY_MISMATCH",
|
||||||
|
f"expected {EXPECTED_AGENT_NAME}/{EXPECTED_AGENT_VERSION}",
|
||||||
|
)
|
||||||
|
forbidden_model_fields = _find_forbidden_model_fields(agent)
|
||||||
|
if forbidden_model_fields:
|
||||||
|
raise ProjectionError("LLM_OR_PROMPT_FIELD_FORBIDDEN", repr(forbidden_model_fields))
|
||||||
|
stages = _require_list(agent.get("Stages"), "STAGES_ARRAY_REQUIRED")
|
||||||
|
if len(stages) != 1:
|
||||||
|
raise ProjectionError("EXACTLY_ONE_STAGE_REQUIRED", str(len(stages)))
|
||||||
|
stage = _require_mapping(stages[0], "STAGE_OBJECT_REQUIRED")
|
||||||
|
if stage.get("name") != "S2_40":
|
||||||
|
raise ProjectionError("S2_40_STAGE_NAME_REQUIRED", repr(stage.get("name")))
|
||||||
|
if stage.get("skip_confirm") is not True:
|
||||||
|
raise ProjectionError(
|
||||||
|
"S2_40_SKIP_CONFIRM_TRUE_REQUIRED",
|
||||||
|
repr(stage.get("skip_confirm")),
|
||||||
|
)
|
||||||
|
for forbidden in ("llm_provider", "llm_model", "llm_reasoning", "llm_verbosity"):
|
||||||
|
if forbidden in stage:
|
||||||
|
raise ProjectionError("MODEL_FIELD_FORBIDDEN", forbidden)
|
||||||
|
|
||||||
|
servers = _require_mapping(
|
||||||
|
_require_mapping(stage.get("tools"), "TOOLS_OBJECT_REQUIRED").get("mcpServers"),
|
||||||
|
"MCP_SERVERS_OBJECT_REQUIRED",
|
||||||
|
)
|
||||||
|
if set(servers) != {"localdocs", "code-executor"}:
|
||||||
|
raise ProjectionError("MCP_SERVER_SET_MISMATCH", repr(sorted(servers)))
|
||||||
|
localdocs = _require_mapping(servers.get("localdocs"), "LOCALDOCS_SERVER_REQUIRED")
|
||||||
|
code_executor = _require_mapping(
|
||||||
|
servers.get("code-executor"), "CODE_EXECUTOR_SERVER_REQUIRED"
|
||||||
|
)
|
||||||
|
if localdocs.get("type") != "streamable-http" or localdocs.get("url") != EXPECTED_LOCALDOCS_URL:
|
||||||
|
raise ProjectionError("LOCALDOCS_SERVER_BINDING_INVALID", repr(localdocs))
|
||||||
|
if (
|
||||||
|
code_executor.get("type") != "streamable-http"
|
||||||
|
or code_executor.get("url") != EXPECTED_CODE_EXECUTOR_URL
|
||||||
|
):
|
||||||
|
raise ProjectionError("CODE_EXECUTOR_SERVER_BINDING_INVALID", repr(code_executor))
|
||||||
|
if "headers" in code_executor:
|
||||||
|
raise ProjectionError(
|
||||||
|
"PLAINTEXT_OR_INLINE_AUTH_HEADER_FORBIDDEN",
|
||||||
|
"authentication must be injected or pre-registered by the backend",
|
||||||
|
)
|
||||||
|
|
||||||
|
tasks = _require_list(stage.get("tasks"), "TASKS_ARRAY_REQUIRED")
|
||||||
|
if len(tasks) != 1:
|
||||||
|
raise ProjectionError("EXACTLY_ONE_TASK_REQUIRED", str(len(tasks)))
|
||||||
|
task = _require_mapping(tasks[0], "TASK_OBJECT_REQUIRED")
|
||||||
|
run_code_tasks = [
|
||||||
|
row
|
||||||
|
for row in tasks
|
||||||
|
if isinstance(row, dict)
|
||||||
|
and row.get("mcp") == "code-executor"
|
||||||
|
and row.get("tool_name") == "run_code"
|
||||||
|
]
|
||||||
|
if len(run_code_tasks) != 1:
|
||||||
|
raise ProjectionError("EXACTLY_ONE_RUN_CODE_REQUIRED", str(len(run_code_tasks)))
|
||||||
|
if task.get("task_name") != EXPECTED_TASK_NAME:
|
||||||
|
raise ProjectionError("TASK_NAME_MISMATCH", repr(task.get("task_name")))
|
||||||
|
parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED")
|
||||||
|
expected_parameter_keys = set(EXPECTED_PARAMETERS) | {"code"}
|
||||||
|
if set(parameters) != expected_parameter_keys:
|
||||||
|
raise ProjectionError(
|
||||||
|
"RUN_CODE_PARAMETER_SET_MISMATCH",
|
||||||
|
repr(sorted(parameters)),
|
||||||
|
)
|
||||||
|
for key, expected in EXPECTED_PARAMETERS.items():
|
||||||
|
if parameters.get(key) != expected:
|
||||||
|
raise ProjectionError(
|
||||||
|
"RUN_CODE_PARAMETER_MISMATCH",
|
||||||
|
f"{key}: expected {expected!r}, observed {parameters.get(key)!r}",
|
||||||
|
)
|
||||||
|
code = parameters.get("code")
|
||||||
|
if not isinstance(code, str) or not code:
|
||||||
|
raise ProjectionError("INLINE_CODE_REQUIRED", repr(code)[:100])
|
||||||
|
if code.endswith(("\n", "\r")):
|
||||||
|
raise ProjectionError(
|
||||||
|
"INLINE_CODE_TRAILING_NEWLINE_FORBIDDEN",
|
||||||
|
"authoring YAML must use code: |- so parser-returned code has no trailing newline",
|
||||||
|
)
|
||||||
|
|
||||||
|
procedure = _require_mapping(stage.get("task_procedure"), "TASK_PROCEDURE_REQUIRED")
|
||||||
|
expected_procedure = {
|
||||||
|
"IN": {"nexts": [EXPECTED_TASK_NAME], "wait_until": []},
|
||||||
|
EXPECTED_TASK_NAME: {"nexts": ["OUT"], "wait_until": ["IN"]},
|
||||||
|
"OUT": {"nexts": [], "wait_until": [EXPECTED_TASK_NAME]},
|
||||||
|
}
|
||||||
|
if procedure != expected_procedure:
|
||||||
|
raise ProjectionError("TASK_PROCEDURE_MISMATCH", repr(procedure)[:500])
|
||||||
|
if stage.get("prevs") != [] or stage.get("nexts") != []:
|
||||||
|
raise ProjectionError("STANDALONE_STAGE_EDGES_MUST_BE_EMPTY", repr(stage))
|
||||||
|
return stage, task
|
||||||
|
|
||||||
|
|
||||||
|
def _import_root(name: str | None) -> str:
|
||||||
|
return (name or "").split(".", 1)[0]
|
||||||
|
|
||||||
|
|
||||||
|
def _attribute_pair(node: ast.Attribute) -> tuple[str, str] | None:
|
||||||
|
if isinstance(node.value, ast.Name):
|
||||||
|
return node.value.id, node.attr
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _is_http_client_receiver(
|
||||||
|
node: ast.expr,
|
||||||
|
derived_client_names: set[str] | None = None,
|
||||||
|
) -> bool:
|
||||||
|
if isinstance(node, ast.Name):
|
||||||
|
return node.id in {"client", "http_client", "httpx"} | (derived_client_names or set())
|
||||||
|
if isinstance(node, ast.Attribute):
|
||||||
|
return (
|
||||||
|
isinstance(node.value, ast.Name)
|
||||||
|
and node.value.id == "self"
|
||||||
|
and node.attr in {"client", "http_client"}
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _is_direct_localdocs_post(call: ast.Call) -> bool:
|
||||||
|
if not isinstance(call.func, ast.Attribute) or call.func.attr != "post":
|
||||||
|
return False
|
||||||
|
receiver = call.func.value
|
||||||
|
if not (
|
||||||
|
isinstance(receiver, ast.Attribute)
|
||||||
|
and isinstance(receiver.value, ast.Name)
|
||||||
|
and receiver.value.id == "self"
|
||||||
|
and receiver.attr == "client"
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
return _is_localdocs_endpoint(_network_endpoint(call) or ast.Constant(value=None))
|
||||||
|
|
||||||
|
|
||||||
|
def _network_endpoint(call: ast.Call) -> ast.expr | None:
|
||||||
|
"""Return a statically identifiable httpx/client endpoint expression."""
|
||||||
|
|
||||||
|
if not isinstance(call.func, ast.Attribute) or call.func.attr not in HTTP_NETWORK_METHODS:
|
||||||
|
return None
|
||||||
|
if not _is_http_client_receiver(call.func.value):
|
||||||
|
return None
|
||||||
|
for keyword in call.keywords:
|
||||||
|
if keyword.arg == "url":
|
||||||
|
return keyword.value
|
||||||
|
index = 1 if call.func.attr == "request" else 0
|
||||||
|
return call.args[index] if len(call.args) > index else ast.Constant(value=None)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_localdocs_endpoint(node: ast.expr) -> bool:
|
||||||
|
return (
|
||||||
|
isinstance(node, ast.Name)
|
||||||
|
and node.id == "LOCALDOCS_URL"
|
||||||
|
or isinstance(node, ast.Constant)
|
||||||
|
and node.value == EXPECTED_LOCALDOCS_URL
|
||||||
|
or isinstance(node, ast.Attribute)
|
||||||
|
and isinstance(node.value, ast.Name)
|
||||||
|
and node.value.id == "self"
|
||||||
|
and node.attr == "endpoint"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_inline_code(code: str) -> dict[str, Any]:
|
||||||
|
code_bytes = code.encode("utf-8")
|
||||||
|
try:
|
||||||
|
compile(code, "<Stage_2_S2_40.parameters.code>", "exec", dont_inherit=True)
|
||||||
|
tree = ast.parse(code, filename="<Stage_2_S2_40.parameters.code>", mode="exec")
|
||||||
|
except (SyntaxError, ValueError, UnicodeError) as exc:
|
||||||
|
raise ProjectionError("INLINE_CODE_COMPILE_FAILED", str(exc)) from exc
|
||||||
|
|
||||||
|
missing_tokens = [token for token in REQUIRED_CODE_TOKENS if token not in code]
|
||||||
|
missing_tokens.extend(
|
||||||
|
"|".join(alternatives)
|
||||||
|
for alternatives in REQUIRED_CODE_TOKEN_ALTERNATIVES
|
||||||
|
if not any(token in code for token in alternatives)
|
||||||
|
)
|
||||||
|
if missing_tokens:
|
||||||
|
raise ProjectionError("INLINE_CODE_REQUIRED_TOKEN_MISSING", ",".join(missing_tokens))
|
||||||
|
if PLACEHOLDER_COMMENT_RE.search(code):
|
||||||
|
raise ProjectionError("INLINE_CODE_PLACEHOLDER_COMMENT", "TODO/FIXME/TBD placeholder")
|
||||||
|
for pattern in PLAINTEXT_SECRET_RES:
|
||||||
|
if pattern.search(code):
|
||||||
|
raise ProjectionError("INLINE_CODE_PLAINTEXT_SECRET", pattern.pattern)
|
||||||
|
|
||||||
|
imports: set[str] = set()
|
||||||
|
forbidden_nodes: list[str] = []
|
||||||
|
external_urls: set[str] = set()
|
||||||
|
forbidden_network_endpoints: set[str] = set()
|
||||||
|
project_source_refs: set[str] = set()
|
||||||
|
import_aliases: dict[str, str] = {}
|
||||||
|
forbidden_callable_aliases: set[str] = set()
|
||||||
|
mcp_client_endpoint_calls: list[ast.expr] = []
|
||||||
|
self_endpoint_assignments: list[ast.expr] = []
|
||||||
|
for candidate in ast.walk(tree):
|
||||||
|
if isinstance(candidate, ast.Import):
|
||||||
|
for alias in candidate.names:
|
||||||
|
import_aliases[alias.asname or _import_root(alias.name)] = _import_root(alias.name)
|
||||||
|
elif isinstance(candidate, ast.ImportFrom) and not candidate.level:
|
||||||
|
root = _import_root(candidate.module)
|
||||||
|
for alias in candidate.names:
|
||||||
|
local_name = alias.asname or alias.name
|
||||||
|
if root == "httpx" and (
|
||||||
|
alias.name in HTTP_NETWORK_METHODS or alias.name in HTTP_CLIENT_FACTORIES
|
||||||
|
):
|
||||||
|
forbidden_callable_aliases.add(local_name)
|
||||||
|
if root == "os" and (
|
||||||
|
alias.name in {name for module, name in FORBIDDEN_ATTRIBUTE_CALLS if module == "os"}
|
||||||
|
or alias.name.startswith("exec")
|
||||||
|
):
|
||||||
|
forbidden_callable_aliases.add(local_name)
|
||||||
|
if root == "builtins" and alias.name in FORBIDDEN_CALL_NAMES:
|
||||||
|
forbidden_callable_aliases.add(local_name)
|
||||||
|
|
||||||
|
derived_client_names: set[str] = set()
|
||||||
|
release_constants: list[str] = []
|
||||||
|
for candidate in ast.walk(tree):
|
||||||
|
if not isinstance(candidate, (ast.Assign, ast.AnnAssign)):
|
||||||
|
continue
|
||||||
|
value = candidate.value
|
||||||
|
targets = candidate.targets if isinstance(candidate, ast.Assign) else [candidate.target]
|
||||||
|
target_names = {target.id for target in targets if isinstance(target, ast.Name)}
|
||||||
|
if (
|
||||||
|
"EXPECTED_STAGE2_RELEASE_SHA256" in target_names
|
||||||
|
and isinstance(value, ast.Constant)
|
||||||
|
and isinstance(value.value, str)
|
||||||
|
):
|
||||||
|
release_constants.append(value.value)
|
||||||
|
if (
|
||||||
|
isinstance(value, ast.Call)
|
||||||
|
and isinstance(value.func, ast.Attribute)
|
||||||
|
and isinstance(value.func.value, ast.Name)
|
||||||
|
and import_aliases.get(value.func.value.id, value.func.value.id) == "httpx"
|
||||||
|
and value.func.attr in HTTP_CLIENT_FACTORIES
|
||||||
|
):
|
||||||
|
derived_client_names.update(target_names)
|
||||||
|
if isinstance(value, ast.Name) and (
|
||||||
|
value.id in FORBIDDEN_CALL_NAMES or value.id in forbidden_callable_aliases
|
||||||
|
):
|
||||||
|
forbidden_callable_aliases.update(target_names)
|
||||||
|
if isinstance(value, ast.Attribute) and isinstance(value.value, ast.Name):
|
||||||
|
module = import_aliases.get(value.value.id, value.value.id)
|
||||||
|
if (module, value.attr) in FORBIDDEN_ATTRIBUTE_CALLS or (
|
||||||
|
module == "os" and value.attr.startswith("exec")
|
||||||
|
):
|
||||||
|
forbidden_callable_aliases.update(target_names)
|
||||||
|
if module == "httpx" and value.attr in HTTP_NETWORK_METHODS:
|
||||||
|
forbidden_callable_aliases.update(target_names)
|
||||||
|
for node in ast.walk(tree):
|
||||||
|
if isinstance(node, ast.Import):
|
||||||
|
imports.update(_import_root(alias.name) for alias in node.names)
|
||||||
|
elif isinstance(node, ast.ImportFrom):
|
||||||
|
if node.level:
|
||||||
|
forbidden_nodes.append(f"relative-import:{node.module or ''}")
|
||||||
|
imports.add(_import_root(node.module))
|
||||||
|
root = _import_root(node.module)
|
||||||
|
for alias in node.names:
|
||||||
|
local_name = alias.asname or alias.name
|
||||||
|
if local_name in forbidden_callable_aliases:
|
||||||
|
forbidden_nodes.append(f"forbidden-import-alias:{root}.{alias.name}")
|
||||||
|
elif isinstance(node, ast.Pass):
|
||||||
|
forbidden_nodes.append("Pass")
|
||||||
|
elif isinstance(node, ast.Expr) and isinstance(node.value, ast.Constant):
|
||||||
|
if node.value.value is Ellipsis:
|
||||||
|
forbidden_nodes.append("Ellipsis-expression")
|
||||||
|
elif isinstance(node, (ast.Assign, ast.AnnAssign)):
|
||||||
|
assignment_targets = node.targets if isinstance(node, ast.Assign) else [node.target]
|
||||||
|
if any(
|
||||||
|
isinstance(target, ast.Attribute)
|
||||||
|
and isinstance(target.value, ast.Name)
|
||||||
|
and target.value.id == "self"
|
||||||
|
and target.attr == "endpoint"
|
||||||
|
for target in assignment_targets
|
||||||
|
):
|
||||||
|
self_endpoint_assignments.append(node.value)
|
||||||
|
if isinstance(node.value, ast.Constant) and node.value.value is Ellipsis:
|
||||||
|
forbidden_nodes.append("Ellipsis-assignment")
|
||||||
|
if (
|
||||||
|
isinstance(node.value, ast.Attribute)
|
||||||
|
and node.value.attr in HTTP_NETWORK_METHODS
|
||||||
|
and _is_http_client_receiver(node.value.value, derived_client_names)
|
||||||
|
):
|
||||||
|
forbidden_nodes.append(f"network-method-alias:{node.value.attr}")
|
||||||
|
elif isinstance(node, ast.Constant):
|
||||||
|
if isinstance(node.value, str):
|
||||||
|
for match in URL_RE.findall(node.value):
|
||||||
|
normalized = match.rstrip(".,);]")
|
||||||
|
if (
|
||||||
|
normalized != EXPECTED_LOCALDOCS_URL
|
||||||
|
and not normalized.startswith(ALLOWED_IDENTIFIER_URL_PREFIXES)
|
||||||
|
):
|
||||||
|
external_urls.add(normalized)
|
||||||
|
if PYTHON_SOURCE_REF_RE.search(node.value.strip()):
|
||||||
|
project_source_refs.add(node.value[:200])
|
||||||
|
if node.value.strip().lower() in {
|
||||||
|
"todo",
|
||||||
|
"tbd",
|
||||||
|
"placeholder",
|
||||||
|
"omitted",
|
||||||
|
"implement me",
|
||||||
|
"...",
|
||||||
|
}:
|
||||||
|
forbidden_nodes.append(f"placeholder-string:{node.value!r}")
|
||||||
|
elif isinstance(node, ast.Call):
|
||||||
|
if isinstance(node.func, ast.Name) and node.func.id == "McpClient":
|
||||||
|
mcp_client_endpoint_calls.append(
|
||||||
|
node.args[0] if node.args else ast.Constant(value=None)
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
isinstance(node.func, ast.Attribute)
|
||||||
|
and node.func.attr in HTTP_NETWORK_METHODS
|
||||||
|
and _is_http_client_receiver(node.func.value, derived_client_names)
|
||||||
|
):
|
||||||
|
if not _is_direct_localdocs_post(node):
|
||||||
|
forbidden_network_endpoints.add(ast.unparse(node.func)[:200])
|
||||||
|
if (
|
||||||
|
isinstance(node.func, ast.Attribute)
|
||||||
|
and node.func.attr in HTTP_NETWORK_METHODS
|
||||||
|
and isinstance(node.func.value, ast.Call)
|
||||||
|
and isinstance(node.func.value.func, ast.Attribute)
|
||||||
|
and isinstance(node.func.value.func.value, ast.Name)
|
||||||
|
and import_aliases.get(
|
||||||
|
node.func.value.func.value.id,
|
||||||
|
node.func.value.func.value.id,
|
||||||
|
) == "httpx"
|
||||||
|
and node.func.value.func.attr in HTTP_CLIENT_FACTORIES
|
||||||
|
):
|
||||||
|
forbidden_network_endpoints.add(ast.unparse(node.func)[:200])
|
||||||
|
if (
|
||||||
|
isinstance(node.func, ast.Name)
|
||||||
|
and node.func.id == "getattr"
|
||||||
|
and len(node.args) >= 2
|
||||||
|
and isinstance(node.args[1], ast.Constant)
|
||||||
|
and node.args[1].value in HTTP_NETWORK_METHODS
|
||||||
|
):
|
||||||
|
forbidden_nodes.append(f"dynamic-network-method:{node.args[1].value}")
|
||||||
|
if isinstance(node.func, ast.Name) and (
|
||||||
|
node.func.id in FORBIDDEN_CALL_NAMES
|
||||||
|
or node.func.id in forbidden_callable_aliases
|
||||||
|
):
|
||||||
|
forbidden_nodes.append(f"call:{node.func.id}")
|
||||||
|
elif isinstance(node.func, ast.Attribute):
|
||||||
|
pair = _attribute_pair(node.func)
|
||||||
|
if pair is not None:
|
||||||
|
module = import_aliases.get(pair[0], pair[0])
|
||||||
|
if (module, pair[1]) in FORBIDDEN_ATTRIBUTE_CALLS or (
|
||||||
|
module == "os" and pair[1].startswith("exec")
|
||||||
|
):
|
||||||
|
forbidden_nodes.append(f"call:{module}.{pair[1]}")
|
||||||
|
elif isinstance(node, ast.Raise):
|
||||||
|
target = node.exc
|
||||||
|
if isinstance(target, ast.Call):
|
||||||
|
target = target.func
|
||||||
|
if isinstance(target, ast.Name) and target.id == "NotImplementedError":
|
||||||
|
forbidden_nodes.append("raise:NotImplementedError")
|
||||||
|
|
||||||
|
imports.discard("")
|
||||||
|
disallowed_imports = sorted(
|
||||||
|
root
|
||||||
|
for root in imports
|
||||||
|
if root in FORBIDDEN_IMPORT_ROOTS
|
||||||
|
or (root not in sys.stdlib_module_names and root not in ALLOWED_NON_STDLIB_IMPORTS)
|
||||||
|
)
|
||||||
|
if disallowed_imports:
|
||||||
|
raise ProjectionError("INLINE_CODE_IMPORT_FORBIDDEN", ",".join(disallowed_imports))
|
||||||
|
if forbidden_nodes:
|
||||||
|
raise ProjectionError("INLINE_CODE_DYNAMIC_OR_PLACEHOLDER_FORBIDDEN", ",".join(forbidden_nodes))
|
||||||
|
if external_urls:
|
||||||
|
raise ProjectionError("INLINE_CODE_EXTERNAL_URL_FORBIDDEN", ",".join(sorted(external_urls)))
|
||||||
|
if forbidden_network_endpoints:
|
||||||
|
raise ProjectionError(
|
||||||
|
"INLINE_CODE_NETWORK_ENDPOINT_FORBIDDEN",
|
||||||
|
",".join(sorted(forbidden_network_endpoints)),
|
||||||
|
)
|
||||||
|
if project_source_refs:
|
||||||
|
raise ProjectionError(
|
||||||
|
"INLINE_CODE_EXTERNAL_PY_SOURCE_REF_FORBIDDEN", ",".join(sorted(project_source_refs))
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
len(self_endpoint_assignments) != 1
|
||||||
|
or not isinstance(self_endpoint_assignments[0], ast.Name)
|
||||||
|
or self_endpoint_assignments[0].id != "endpoint"
|
||||||
|
or not mcp_client_endpoint_calls
|
||||||
|
or any(not _is_localdocs_endpoint(endpoint) for endpoint in mcp_client_endpoint_calls)
|
||||||
|
):
|
||||||
|
raise ProjectionError(
|
||||||
|
"INLINE_CODE_LOCALDOCS_ENDPOINT_FLOW_INVALID",
|
||||||
|
f"assignments={len(self_endpoint_assignments)};constructors={len(mcp_client_endpoint_calls)}",
|
||||||
|
)
|
||||||
|
if len(release_constants) != 1 or not re.fullmatch(r"[a-f0-9]{64}", release_constants[0]):
|
||||||
|
raise ProjectionError(
|
||||||
|
"EXPECTED_PARENT_RELEASE_CONSTANT_EXACT_ONE_REQUIRED",
|
||||||
|
repr(release_constants),
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"code_sha256": sha256_bytes(code_bytes),
|
||||||
|
"code_size_bytes": len(code_bytes),
|
||||||
|
"compile_status": "PASS",
|
||||||
|
"ast_status": "PASS",
|
||||||
|
"imports": sorted(imports),
|
||||||
|
"forbidden_import_count": 0,
|
||||||
|
"forbidden_dynamic_call_count": 0,
|
||||||
|
"external_url_count": 0,
|
||||||
|
"placeholder_count": 0,
|
||||||
|
"plaintext_secret_count": 0,
|
||||||
|
"external_python_source_ref_count": 0,
|
||||||
|
"expected_parent_stage2_release_sha256": release_constants[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _canonical_task_semantics(document: Mapping[str, Any], stage: Mapping[str, Any], task: Mapping[str, Any]) -> dict[str, Any]:
|
||||||
|
agent = _require_mapping(document.get("Agent"), "AGENT_OBJECT_REQUIRED")
|
||||||
|
servers = _require_mapping(
|
||||||
|
_require_mapping(stage.get("tools"), "TOOLS_OBJECT_REQUIRED").get("mcpServers"),
|
||||||
|
"MCP_SERVERS_OBJECT_REQUIRED",
|
||||||
|
)
|
||||||
|
parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED")
|
||||||
|
return {
|
||||||
|
"agent": {"name": agent.get("name"), "version": agent.get("version")},
|
||||||
|
"stage": {
|
||||||
|
"name": stage.get("name"),
|
||||||
|
"prevs": stage.get("prevs"),
|
||||||
|
"nexts": stage.get("nexts"),
|
||||||
|
},
|
||||||
|
"mcp_servers": {
|
||||||
|
name: {"type": value.get("type"), "url": value.get("url")}
|
||||||
|
for name, value in sorted(servers.items())
|
||||||
|
if isinstance(value, dict)
|
||||||
|
},
|
||||||
|
"task": {
|
||||||
|
"task_name": task.get("task_name"),
|
||||||
|
"mcp": task.get("mcp"),
|
||||||
|
"tool_name": task.get("tool_name"),
|
||||||
|
"parameters": {
|
||||||
|
key: parameters.get(key)
|
||||||
|
for key in ("language", "requirements", "network", "timeout")
|
||||||
|
},
|
||||||
|
"code_sha256": sha256_bytes(parameters["code"].encode("utf-8")),
|
||||||
|
},
|
||||||
|
"task_procedure": stage.get("task_procedure"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def expected_outputs(
|
||||||
|
authoring_raw: bytes,
|
||||||
|
document: Mapping[str, Any],
|
||||||
|
) -> tuple[dict[Path, bytes], dict[str, Any]]:
|
||||||
|
stage, task = extract_run_code_task(document)
|
||||||
|
parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED")
|
||||||
|
code = parameters["code"]
|
||||||
|
validation = validate_inline_code(code)
|
||||||
|
code_bytes = code.encode("utf-8")
|
||||||
|
semantics = _canonical_task_semantics(document, stage, task)
|
||||||
|
semantics_sha256 = sha256_bytes(canonical_json_bytes(semantics))
|
||||||
|
|
||||||
|
receipt = {
|
||||||
|
"schema_version": "stage2_s2_40_inline_code_receipt.v1",
|
||||||
|
"workflow_id": "S2_40",
|
||||||
|
"build_kind": "OFFLINE_AUTHORING_PROJECTION",
|
||||||
|
"source_of_truth": _logical_path(AUTHORING_PATH),
|
||||||
|
"authoring_rewritten": False,
|
||||||
|
"authoring": {
|
||||||
|
"path": _logical_path(AUTHORING_PATH),
|
||||||
|
"sha256": sha256_bytes(authoring_raw),
|
||||||
|
"size_bytes": len(authoring_raw),
|
||||||
|
"unique_key_parse": "PASS",
|
||||||
|
},
|
||||||
|
"deployment_projection": {
|
||||||
|
"path": _logical_path(PROJECTION_PATH),
|
||||||
|
"sha256": sha256_bytes(authoring_raw),
|
||||||
|
"size_bytes": len(authoring_raw),
|
||||||
|
"byte_identical_to_authoring": True,
|
||||||
|
"canonical_task_semantics_sha256": semantics_sha256,
|
||||||
|
},
|
||||||
|
"canonical_code": {
|
||||||
|
"yaml_pointer": "/Agent/Stages/0/tasks/0/parameters/code",
|
||||||
|
"encoding": "UTF-8",
|
||||||
|
"extraction_transform": "NONE",
|
||||||
|
**validation,
|
||||||
|
},
|
||||||
|
"full_code_mirrors": [
|
||||||
|
{
|
||||||
|
"path": _logical_path(MIRROR_PY_PATH),
|
||||||
|
"sha256": validation["code_sha256"],
|
||||||
|
"size_bytes": len(code_bytes),
|
||||||
|
"byte_identical_to_canonical_code": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": _logical_path(MIRROR_TXT_PATH),
|
||||||
|
"sha256": validation["code_sha256"],
|
||||||
|
"size_bytes": len(code_bytes),
|
||||||
|
"byte_identical_to_canonical_code": True,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"task_contract": {
|
||||||
|
**semantics,
|
||||||
|
"exactly_one_stage": True,
|
||||||
|
"exactly_one_task": True,
|
||||||
|
"exactly_one_code_executor_run_code": True,
|
||||||
|
"authoring_rewritten": False,
|
||||||
|
"projection_byte_identical_to_authoring": True,
|
||||||
|
"code_mirrors_byte_identical": True,
|
||||||
|
"canonical_task_semantics_sha256": semantics_sha256,
|
||||||
|
},
|
||||||
|
"expected_parent_stage2_release_sha256": validation[
|
||||||
|
"expected_parent_stage2_release_sha256"
|
||||||
|
],
|
||||||
|
"parity_status": "PASS",
|
||||||
|
}
|
||||||
|
outputs = {
|
||||||
|
PROJECTION_PATH: authoring_raw,
|
||||||
|
MIRROR_PY_PATH: code_bytes,
|
||||||
|
MIRROR_TXT_PATH: code_bytes,
|
||||||
|
RECEIPT_PATH: canonical_json_bytes(receipt),
|
||||||
|
}
|
||||||
|
return outputs, receipt
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_output_target(path: Path) -> None:
|
||||||
|
root = DEPLOYMENT_ROOT.resolve(strict=True)
|
||||||
|
resolved = path.resolve(strict=False)
|
||||||
|
try:
|
||||||
|
resolved.relative_to(root)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise ProjectionError("OUTPUT_OUTSIDE_DEPLOYMENT_ROOT", path.as_posix()) from exc
|
||||||
|
if path.exists() and path.is_symlink():
|
||||||
|
raise ProjectionError("OUTPUT_SYMLINK_FORBIDDEN", path.as_posix())
|
||||||
|
|
||||||
|
|
||||||
|
def _atomic_write(path: Path, payload: bytes) -> None:
|
||||||
|
_assert_output_target(path)
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
temporary_name: str | None = None
|
||||||
|
try:
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode="wb",
|
||||||
|
dir=path.parent,
|
||||||
|
prefix=f".{path.name}.",
|
||||||
|
suffix=".tmp",
|
||||||
|
delete=False,
|
||||||
|
) as handle:
|
||||||
|
temporary_name = handle.name
|
||||||
|
handle.write(payload)
|
||||||
|
handle.flush()
|
||||||
|
os.fsync(handle.fileno())
|
||||||
|
os.replace(temporary_name, path)
|
||||||
|
temporary_name = None
|
||||||
|
finally:
|
||||||
|
if temporary_name is not None:
|
||||||
|
try:
|
||||||
|
Path(temporary_name).unlink()
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def compare_outputs(outputs: Mapping[Path, bytes]) -> list[dict[str, Any]]:
|
||||||
|
mismatches: list[dict[str, Any]] = []
|
||||||
|
for path, expected in outputs.items():
|
||||||
|
if not path.is_file():
|
||||||
|
mismatches.append(
|
||||||
|
{"path": _logical_path(path), "status": "MISSING", "expected_sha256": sha256_bytes(expected)}
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
observed = path.read_bytes()
|
||||||
|
if observed != expected:
|
||||||
|
mismatches.append(
|
||||||
|
{
|
||||||
|
"path": _logical_path(path),
|
||||||
|
"status": "BYTE_MISMATCH",
|
||||||
|
"expected_sha256": sha256_bytes(expected),
|
||||||
|
"observed_sha256": sha256_bytes(observed),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return mismatches
|
||||||
|
|
||||||
|
|
||||||
|
def run(*, check: bool) -> tuple[int, dict[str, Any]]:
|
||||||
|
before, document = load_authoring()
|
||||||
|
outputs, receipt = expected_outputs(before, document)
|
||||||
|
if check:
|
||||||
|
mismatches = compare_outputs(outputs)
|
||||||
|
return (
|
||||||
|
0 if not mismatches else 1,
|
||||||
|
{
|
||||||
|
"status": "PARITY_PASS" if not mismatches else "PARITY_DRIFT",
|
||||||
|
"mode": "CHECK_NO_WRITE",
|
||||||
|
"authoring_sha256": sha256_bytes(before),
|
||||||
|
"code_sha256": receipt["canonical_code"]["code_sha256"],
|
||||||
|
"mismatches": mismatches,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
receipt_payload = outputs[RECEIPT_PATH]
|
||||||
|
for path, payload in outputs.items():
|
||||||
|
if path == RECEIPT_PATH:
|
||||||
|
continue
|
||||||
|
_atomic_write(path, payload)
|
||||||
|
after_artifacts = AUTHORING_PATH.read_bytes()
|
||||||
|
if after_artifacts != before:
|
||||||
|
raise ProjectionError(
|
||||||
|
"AUTHORING_CHANGED_DURING_BUILD",
|
||||||
|
f"before={sha256_bytes(before)} after={sha256_bytes(after_artifacts)}",
|
||||||
|
)
|
||||||
|
_atomic_write(RECEIPT_PATH, receipt_payload)
|
||||||
|
after_receipt = AUTHORING_PATH.read_bytes()
|
||||||
|
if after_receipt != before:
|
||||||
|
raise ProjectionError(
|
||||||
|
"AUTHORING_CHANGED_DURING_RECEIPT_WRITE",
|
||||||
|
f"before={sha256_bytes(before)} after={sha256_bytes(after_receipt)}",
|
||||||
|
)
|
||||||
|
mismatches = compare_outputs(outputs)
|
||||||
|
if mismatches:
|
||||||
|
raise ProjectionError("POST_BUILD_PARITY_FAILED", json.dumps(mismatches, sort_keys=True))
|
||||||
|
return 0, {
|
||||||
|
"status": "BUILT_AND_VERIFIED",
|
||||||
|
"mode": "BUILD",
|
||||||
|
"authoring_sha256": sha256_bytes(before),
|
||||||
|
"code_sha256": receipt["canonical_code"]["code_sha256"],
|
||||||
|
"outputs": [_logical_path(path) for path in outputs],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _parser() -> argparse.ArgumentParser:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Build or verify the S2_40 inline Agent projection"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--check",
|
||||||
|
action="store_true",
|
||||||
|
help="perform a no-write byte-parity check against generated outputs",
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: Iterable[str] | None = None) -> int:
|
||||||
|
args = _parser().parse_args(list(argv) if argv is not None else None)
|
||||||
|
try:
|
||||||
|
status, payload = run(check=args.check)
|
||||||
|
except ProjectionError as exc:
|
||||||
|
status = 3 if exc.code == "AUTHORING_YAML_MISSING" else 2
|
||||||
|
payload = {
|
||||||
|
"status": "CONTROLLED_MISSING_AUTHORING" if status == 3 else "BUILD_FAILED",
|
||||||
|
"reason_code": exc.code,
|
||||||
|
"detail": exc.detail,
|
||||||
|
"mode": "CHECK_NO_WRITE" if args.check else "BUILD",
|
||||||
|
}
|
||||||
|
print(json.dumps(payload, ensure_ascii=False, allow_nan=False, sort_keys=True))
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
+350
-84
@@ -125,29 +125,46 @@ ADVERSE_REQUIRED = frozenset(
|
|||||||
"presentation_authorization_ref",
|
"presentation_authorization_ref",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
SUCCESS_ARTIFACT_FAMILIES = frozenset(
|
PART_FAMILIES = frozenset(
|
||||||
{"DRAFT_PART", "ISSUE_PATCH", "WORKNOTE_PART", "USAGE_PART", "ITEM_RECEIPT"}
|
{"DRAFT_PART", "ISSUE_PATCH", "WORKNOTE_PART", "USAGE_PART"}
|
||||||
)
|
)
|
||||||
SUCCESS_ARTIFACT_FILENAMES = {
|
PART_FILENAMES = {
|
||||||
"DRAFT_PART": "draft_parts",
|
"DRAFT_PART": "draft_parts",
|
||||||
"ISSUE_PATCH": "issue_patches",
|
"ISSUE_PATCH": "issue_patches",
|
||||||
"WORKNOTE_PART": "worknote_parts",
|
"WORKNOTE_PART": "worknote_parts",
|
||||||
"USAGE_PART": "usage_parts",
|
"USAGE_PART": "usage_parts",
|
||||||
"ITEM_RECEIPT": "item_receipts",
|
|
||||||
}
|
}
|
||||||
PUBLISH_ARTIFACT_ROW_FIELDS = frozenset(
|
PART_SCHEMA_REFS = {
|
||||||
|
"DRAFT_PART": "schemas/draft_atoms.schema.json#/$defs/draft_part",
|
||||||
|
"ISSUE_PATCH": "schemas/draft_atoms.schema.json#/$defs/issue_patch_part",
|
||||||
|
"WORKNOTE_PART": "schemas/draft_atoms.schema.json#/$defs/worknote_part",
|
||||||
|
"USAGE_PART": "schemas/draft_atoms.schema.json#/$defs/usage_part",
|
||||||
|
}
|
||||||
|
PART_DEF_NAMES = {
|
||||||
|
"DRAFT_PART": "draft_part",
|
||||||
|
"ISSUE_PATCH": "issue_patch_part",
|
||||||
|
"WORKNOTE_PART": "worknote_part",
|
||||||
|
"USAGE_PART": "usage_part",
|
||||||
|
}
|
||||||
|
PART_SELF_HASH_FIELDS = {family: "part_sha256" for family in PART_FAMILIES}
|
||||||
|
COMMON_PROVENANCE_FIELDS = frozenset(
|
||||||
{
|
{
|
||||||
"claim_group_id",
|
"compile_mode",
|
||||||
"artifact_family",
|
"parent_stage2_release_sha256",
|
||||||
"artifact_path",
|
"s2_30_release_sha256",
|
||||||
"artifact_sha256",
|
"s2_30_agent_sha256",
|
||||||
"read_back_sha256",
|
"s2_30_binding_sha256",
|
||||||
"immutable_write_status",
|
"p00_prompt_sha256",
|
||||||
"read_back_status",
|
"p30_prompt_sha256",
|
||||||
"item_receipt_path",
|
"s2_30_producer_contract_digest",
|
||||||
"item_receipt_sha256",
|
}
|
||||||
"item_receipt_persistence_status",
|
)
|
||||||
"artifact_receipt_sha256",
|
GROUP_PROVENANCE_FIELDS = frozenset(
|
||||||
|
{
|
||||||
|
*COMMON_PROVENANCE_FIELDS,
|
||||||
|
"p31_rule_and_pack_sha256",
|
||||||
|
"p32_common_authority_sha256",
|
||||||
|
"s30_group_slice_sha256",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
REQUIRED_PENDING = {
|
REQUIRED_PENDING = {
|
||||||
@@ -1765,79 +1782,328 @@ def evaluate_retry(
|
|||||||
raise ContractError("ATTEMPT_NO_INVALID", "$/attempt_no", repr(attempt_no))
|
raise ContractError("ATTEMPT_NO_INVALID", "$/attempt_no", repr(attempt_no))
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_handoff_provenance(
|
||||||
|
provenance: Mapping[str, Any],
|
||||||
|
*,
|
||||||
|
common_only: bool,
|
||||||
|
path: str,
|
||||||
|
) -> None:
|
||||||
|
expected = COMMON_PROVENANCE_FIELDS if common_only else GROUP_PROVENANCE_FIELDS
|
||||||
|
if set(provenance) != expected:
|
||||||
|
raise ContractError(
|
||||||
|
"HANDOFF_PROVENANCE_CLOSED_SHAPE",
|
||||||
|
path,
|
||||||
|
repr(sorted(set(provenance) ^ expected)),
|
||||||
|
)
|
||||||
|
if provenance.get("compile_mode") not in ALLOWED_MODES:
|
||||||
|
raise ContractError(
|
||||||
|
"COMPILE_MODE_INVALID", f"{path}/compile_mode", repr(provenance.get("compile_mode"))
|
||||||
|
)
|
||||||
|
for key in expected - {"compile_mode"}:
|
||||||
|
value = provenance.get(key)
|
||||||
|
if not isinstance(value, str) or HEX64.fullmatch(value) is None:
|
||||||
|
raise ContractError("HANDOFF_PROVENANCE_HASH_INVALID", f"{path}/{key}", repr(value))
|
||||||
|
|
||||||
|
|
||||||
|
def _common_provenance(provenance: Mapping[str, Any]) -> dict[str, Any]:
|
||||||
|
return {key: provenance[key] for key in sorted(COMMON_PROVENANCE_FIELDS)}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_persisted_part(
|
||||||
|
part: Mapping[str, Any],
|
||||||
|
family: str,
|
||||||
|
*,
|
||||||
|
expected_group_id: str | None = None,
|
||||||
|
expected_common_provenance: Mapping[str, Any] | None = None,
|
||||||
|
schema: Mapping[str, Any] | None = None,
|
||||||
|
path: str = "$/part",
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Validate one immutable S2_30 physical part without discovering siblings."""
|
||||||
|
|
||||||
|
if family not in PART_FAMILIES:
|
||||||
|
raise ContractError("PART_FAMILY_INVALID", path, repr(family))
|
||||||
|
if schema is not None:
|
||||||
|
validate_instance(part, PART_DEF_NAMES[family], schema)
|
||||||
|
_require_self_hash(part, PART_SELF_HASH_FIELDS[family], path)
|
||||||
|
group_id = part.get("claim_group_id")
|
||||||
|
if expected_group_id is not None and group_id != expected_group_id:
|
||||||
|
raise ContractError(
|
||||||
|
"PART_GROUP_MISMATCH", f"{path}/claim_group_id", repr((group_id, expected_group_id))
|
||||||
|
)
|
||||||
|
provenance = part.get("provenance")
|
||||||
|
if not isinstance(provenance, dict):
|
||||||
|
raise ContractError("HANDOFF_PROVENANCE_REQUIRED", f"{path}/provenance", repr(provenance))
|
||||||
|
_validate_handoff_provenance(provenance, common_only=False, path=f"{path}/provenance")
|
||||||
|
if expected_common_provenance is not None and _common_provenance(provenance) != dict(
|
||||||
|
expected_common_provenance
|
||||||
|
):
|
||||||
|
raise ContractError(
|
||||||
|
"PART_COMMON_PROVENANCE_MISMATCH",
|
||||||
|
f"{path}/provenance",
|
||||||
|
repr((_common_provenance(provenance), dict(expected_common_provenance))),
|
||||||
|
)
|
||||||
|
if family == "DRAFT_PART":
|
||||||
|
atoms = part.get("canonical_atoms")
|
||||||
|
atom_ids = [row.get("atom_id") for row in atoms] if isinstance(atoms, list) else []
|
||||||
|
if len(atom_ids) != len(set(atom_ids)):
|
||||||
|
raise ContractError("DRAFT_PART_ATOM_ID_DUPLICATE", f"{path}/canonical_atoms", repr(atom_ids))
|
||||||
|
if any(isinstance(row, dict) and row.get("claim_group_id") != group_id for row in atoms or []):
|
||||||
|
raise ContractError("DRAFT_PART_ATOM_GROUP_MISMATCH", f"{path}/canonical_atoms", repr(group_id))
|
||||||
|
coverage = part.get("atomic_claim_coverage")
|
||||||
|
coverage_refs = [
|
||||||
|
ref
|
||||||
|
for row in coverage or []
|
||||||
|
if isinstance(row, dict)
|
||||||
|
for key in ("relief_atom_local_refs", "cause_atom_local_refs")
|
||||||
|
for ref in row.get(key, [])
|
||||||
|
]
|
||||||
|
if len(coverage_refs) != len(set(coverage_refs)) or set(coverage_refs) != set(atom_ids) - {
|
||||||
|
row.get("atom_id")
|
||||||
|
for row in atoms or []
|
||||||
|
if isinstance(row, dict) and row.get("output_section") in {"procedural_declaration", "worknote_only"}
|
||||||
|
}:
|
||||||
|
raise ContractError("DRAFT_PART_COVERAGE_PARTITION_MISMATCH", f"{path}/atomic_claim_coverage", repr(coverage_refs))
|
||||||
|
return {"status": "PASS", "claim_group_id": group_id, "part_family": family}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_part_manifest_core(
|
||||||
|
manifest: Mapping[str, Any],
|
||||||
|
part_bytes_by_path: Mapping[str, bytes],
|
||||||
|
schema: Mapping[str, Any] | None = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Validate the receipt-free exact part index using only enumerated byte inputs."""
|
||||||
|
|
||||||
|
if schema is not None:
|
||||||
|
validate_instance(manifest, "part_manifest_core", schema)
|
||||||
|
_require_self_hash(manifest, "part_manifest_core_sha256", "$/artifact_manifest")
|
||||||
|
if any("receipt" in key.lower() for key in manifest):
|
||||||
|
raise ContractError("MANIFEST_RECEIPT_REFERENCE_FORBIDDEN", "$/artifact_manifest", repr(sorted(manifest)))
|
||||||
|
common = manifest.get("provenance")
|
||||||
|
if not isinstance(common, dict):
|
||||||
|
raise ContractError("HANDOFF_PROVENANCE_REQUIRED", "$/artifact_manifest/provenance", repr(common))
|
||||||
|
_validate_handoff_provenance(common, common_only=True, path="$/artifact_manifest/provenance")
|
||||||
|
expected_groups = manifest.get("expected_claim_group_ids")
|
||||||
|
if not isinstance(expected_groups, list) or expected_groups != sorted(expected_groups):
|
||||||
|
raise ContractError("MANIFEST_GROUP_ORDER_INVALID", "$/artifact_manifest/expected_claim_group_ids", repr(expected_groups))
|
||||||
|
rows = manifest.get("part_rows")
|
||||||
|
if not isinstance(rows, list):
|
||||||
|
raise ContractError("MANIFEST_PART_ROWS_REQUIRED", "$/artifact_manifest/part_rows", repr(rows))
|
||||||
|
if any(not isinstance(row, dict) for row in rows):
|
||||||
|
raise ContractError("MANIFEST_PART_ROW_OBJECT_REQUIRED", "$/artifact_manifest/part_rows", repr(rows))
|
||||||
|
sort_key = lambda row: (row.get("claim_group_id"), row.get("part_family"), row.get("path"))
|
||||||
|
if rows != sorted(rows, key=sort_key):
|
||||||
|
raise ContractError("MANIFEST_PART_ROW_ORDER_INVALID", "$/artifact_manifest/part_rows", "not stable sorted")
|
||||||
|
expected_pairs = {(group_id, family) for group_id in expected_groups for family in PART_FAMILIES}
|
||||||
|
observed_pairs: set[tuple[str, str]] = set()
|
||||||
|
declared_paths: list[str] = []
|
||||||
|
group_provenance: dict[str, dict[str, Any]] = {}
|
||||||
|
for index, row in enumerate(rows):
|
||||||
|
if not isinstance(row, dict):
|
||||||
|
raise ContractError("MANIFEST_PART_ROW_OBJECT_REQUIRED", f"$/artifact_manifest/part_rows/{index}", repr(row))
|
||||||
|
group_id = row.get("claim_group_id")
|
||||||
|
family = row.get("part_family")
|
||||||
|
pair = (str(group_id), str(family))
|
||||||
|
if pair not in expected_pairs or pair in observed_pairs:
|
||||||
|
raise ContractError("MANIFEST_PART_PAIR_INVALID", f"$/artifact_manifest/part_rows/{index}", repr(pair))
|
||||||
|
expected_path = f"map_s2_30/{PART_FILENAMES[str(family)]}/{group_id}.json"
|
||||||
|
if row.get("path") != expected_path or row.get("schema_ref") != PART_SCHEMA_REFS[str(family)]:
|
||||||
|
raise ContractError("MANIFEST_PART_BINDING_MISMATCH", f"$/artifact_manifest/part_rows/{index}", repr(row))
|
||||||
|
declared_paths.append(expected_path)
|
||||||
|
raw = part_bytes_by_path.get(expected_path)
|
||||||
|
if not isinstance(raw, bytes):
|
||||||
|
raise ContractError("MANIFEST_PART_BYTES_MISSING", f"$/parts/{expected_path}", repr(type(raw)))
|
||||||
|
if sha256_bytes(raw) != row.get("sha256"):
|
||||||
|
raise ContractError("MANIFEST_PART_RAW_HASH_MISMATCH", f"$/parts/{expected_path}", repr(row.get("sha256")))
|
||||||
|
part = parse_canonical_json_object(raw, f"$/parts/{expected_path}")
|
||||||
|
validate_persisted_part(
|
||||||
|
part,
|
||||||
|
str(family),
|
||||||
|
expected_group_id=str(group_id),
|
||||||
|
expected_common_provenance=common,
|
||||||
|
schema=schema,
|
||||||
|
path=f"$/parts/{expected_path}",
|
||||||
|
)
|
||||||
|
observed_provenance = dict(part["provenance"])
|
||||||
|
previous_provenance = group_provenance.get(str(group_id))
|
||||||
|
if previous_provenance is not None and observed_provenance != previous_provenance:
|
||||||
|
raise ContractError(
|
||||||
|
"PART_GROUP_PROVENANCE_MISMATCH",
|
||||||
|
f"$/parts/{expected_path}/provenance",
|
||||||
|
repr((previous_provenance, observed_provenance)),
|
||||||
|
)
|
||||||
|
group_provenance[str(group_id)] = observed_provenance
|
||||||
|
observed_pairs.add(pair)
|
||||||
|
if observed_pairs != expected_pairs:
|
||||||
|
raise ContractError("MANIFEST_PART_SET_MISMATCH", "$/artifact_manifest/part_rows", repr((observed_pairs, expected_pairs)))
|
||||||
|
if set(part_bytes_by_path) != set(declared_paths):
|
||||||
|
raise ContractError(
|
||||||
|
"UNENUMERATED_PART_INPUT_FORBIDDEN",
|
||||||
|
"$/parts",
|
||||||
|
repr(sorted(set(part_bytes_by_path) ^ set(declared_paths))),
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"status": "PASS",
|
||||||
|
"claim_group_ids": expected_groups,
|
||||||
|
"part_count": len(rows),
|
||||||
|
"part_manifest_core_sha256": manifest["part_manifest_core_sha256"],
|
||||||
|
"group_provenance": group_provenance,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_receipt_object(
|
||||||
|
receipt: Mapping[str, Any],
|
||||||
|
def_name: str,
|
||||||
|
manifest_core_sha256: str,
|
||||||
|
common_provenance: Mapping[str, Any],
|
||||||
|
expected_group_provenance: Mapping[str, Any] | None,
|
||||||
|
schema: Mapping[str, Any] | None,
|
||||||
|
path: str,
|
||||||
|
) -> None:
|
||||||
|
if schema is not None:
|
||||||
|
validate_instance(receipt, def_name, schema)
|
||||||
|
_require_self_hash(receipt, "receipt_sha256", path)
|
||||||
|
if receipt.get("part_manifest_core_sha256") != manifest_core_sha256:
|
||||||
|
raise ContractError(
|
||||||
|
"RECEIPT_MANIFEST_CORE_HASH_MISMATCH",
|
||||||
|
f"{path}/part_manifest_core_sha256",
|
||||||
|
repr(receipt.get("part_manifest_core_sha256")),
|
||||||
|
)
|
||||||
|
provenance = receipt.get("provenance")
|
||||||
|
if not isinstance(provenance, dict):
|
||||||
|
raise ContractError("HANDOFF_PROVENANCE_REQUIRED", f"{path}/provenance", repr(provenance))
|
||||||
|
_validate_handoff_provenance(
|
||||||
|
provenance,
|
||||||
|
common_only=def_name == "cohort_receipt",
|
||||||
|
path=f"{path}/provenance",
|
||||||
|
)
|
||||||
|
if _common_provenance(provenance) != dict(common_provenance):
|
||||||
|
raise ContractError("RECEIPT_COMMON_PROVENANCE_MISMATCH", f"{path}/provenance", repr(provenance))
|
||||||
|
if expected_group_provenance is not None and dict(provenance) != dict(expected_group_provenance):
|
||||||
|
raise ContractError("ITEM_RECEIPT_GROUP_PROVENANCE_MISMATCH", f"{path}/provenance", repr(provenance))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_handoff_chain(
|
||||||
|
publish_status: Mapping[str, Any],
|
||||||
|
manifest: Mapping[str, Any],
|
||||||
|
part_bytes_by_path: Mapping[str, bytes],
|
||||||
|
item_receipt_bytes_by_path: Mapping[str, bytes],
|
||||||
|
cohort_receipt_bytes_by_path: Mapping[str, bytes],
|
||||||
|
schema: Mapping[str, Any] | None = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Validate barrier -> manifest -> parts and ordered receipts without scan/glob."""
|
||||||
|
|
||||||
|
if schema is not None:
|
||||||
|
validate_instance(publish_status, "publish_status", schema)
|
||||||
|
_require_self_hash(publish_status, "status_sha256", "$/publish_status")
|
||||||
|
manifest_report = validate_part_manifest_core(manifest, part_bytes_by_path, schema)
|
||||||
|
manifest_raw = canonical_json_bytes(manifest)
|
||||||
|
if publish_status.get("artifact_manifest_sha256") != sha256_bytes(manifest_raw):
|
||||||
|
raise ContractError("PUBLISH_MANIFEST_RAW_HASH_MISMATCH", "$/publish_status/artifact_manifest_sha256", "raw")
|
||||||
|
if publish_status.get("artifact_manifest_path") != "map_s2_30/artifact_manifest.json":
|
||||||
|
raise ContractError("PUBLISH_MANIFEST_PATH_MISMATCH", "$/publish_status/artifact_manifest_path", repr(publish_status.get("artifact_manifest_path")))
|
||||||
|
common = manifest["provenance"]
|
||||||
|
if publish_status.get("provenance") != common:
|
||||||
|
raise ContractError("PUBLISH_COMMON_PROVENANCE_MISMATCH", "$/publish_status/provenance", "manifest")
|
||||||
|
if publish_status.get("compile_mode") != common.get("compile_mode"):
|
||||||
|
raise ContractError("PUBLISH_COMPILE_MODE_MISMATCH", "$/publish_status/compile_mode", repr(publish_status.get("compile_mode")))
|
||||||
|
if publish_status.get("parent_stage2_release_sha256") != common.get("parent_stage2_release_sha256") or publish_status.get("s2_30_release_sha256") != common.get("s2_30_release_sha256"):
|
||||||
|
raise ContractError("PUBLISH_RELEASE_PROVENANCE_MISMATCH", "$/publish_status", "release")
|
||||||
|
expected_groups = list(manifest["expected_claim_group_ids"])
|
||||||
|
if publish_status.get("expected_claim_group_ids") != expected_groups or publish_status.get("published_claim_group_ids") != expected_groups:
|
||||||
|
raise ContractError("PUBLISH_GROUP_SET_MISMATCH", "$/publish_status", repr(expected_groups))
|
||||||
|
if publish_status.get("terminal_failure_claim_group_ids") != [] or publish_status.get("status") != "S2_30_COMPLETE" or publish_status.get("route") != "TO_S2_40":
|
||||||
|
raise ContractError("PUBLISH_SUCCESS_ROUTE_INVALID", "$/publish_status", repr(publish_status.get("route")))
|
||||||
|
|
||||||
|
item_refs = publish_status.get("ordered_item_receipts")
|
||||||
|
cohort_refs = publish_status.get("ordered_cohort_receipts")
|
||||||
|
if not isinstance(item_refs, list) or any(not isinstance(row, dict) for row in item_refs):
|
||||||
|
raise ContractError("ITEM_RECEIPT_REFS_INVALID", "$/publish_status/ordered_item_receipts", repr(item_refs))
|
||||||
|
if not isinstance(cohort_refs, list) or any(not isinstance(row, dict) for row in cohort_refs):
|
||||||
|
raise ContractError("COHORT_RECEIPT_REFS_INVALID", "$/publish_status/ordered_cohort_receipts", repr(cohort_refs))
|
||||||
|
if item_refs != sorted(item_refs, key=lambda row: (row.get("claim_group_id"), row.get("path"))):
|
||||||
|
raise ContractError("ITEM_RECEIPT_ORDER_INVALID", "$/publish_status/ordered_item_receipts", repr(item_refs))
|
||||||
|
if cohort_refs != sorted(cohort_refs, key=lambda row: row.get("path")):
|
||||||
|
raise ContractError("COHORT_RECEIPT_ORDER_INVALID", "$/publish_status/ordered_cohort_receipts", repr(cohort_refs))
|
||||||
|
if [row.get("claim_group_id") for row in item_refs] != expected_groups:
|
||||||
|
raise ContractError("ITEM_RECEIPT_GROUP_SET_MISMATCH", "$/publish_status/ordered_item_receipts", repr(item_refs))
|
||||||
|
if set(item_receipt_bytes_by_path) != {row.get("path") for row in item_refs}:
|
||||||
|
raise ContractError("ITEM_RECEIPT_INPUT_SET_MISMATCH", "$/item_receipts", repr(sorted(item_receipt_bytes_by_path)))
|
||||||
|
if set(cohort_receipt_bytes_by_path) != {row.get("path") for row in cohort_refs}:
|
||||||
|
raise ContractError("COHORT_RECEIPT_INPUT_SET_MISMATCH", "$/cohort_receipts", repr(sorted(cohort_receipt_bytes_by_path)))
|
||||||
|
for index, ref in enumerate(item_refs):
|
||||||
|
raw = item_receipt_bytes_by_path[ref["path"]]
|
||||||
|
if sha256_bytes(raw) != ref.get("sha256"):
|
||||||
|
raise ContractError("ITEM_RECEIPT_RAW_HASH_MISMATCH", f"$/item_receipts/{index}", ref["path"])
|
||||||
|
receipt = parse_canonical_json_object(raw, f"$/item_receipts/{index}")
|
||||||
|
_validate_receipt_object(
|
||||||
|
receipt,
|
||||||
|
"item_receipt",
|
||||||
|
manifest["part_manifest_core_sha256"],
|
||||||
|
common,
|
||||||
|
manifest_report["group_provenance"].get(str(ref.get("claim_group_id"))),
|
||||||
|
schema,
|
||||||
|
f"$/item_receipts/{index}",
|
||||||
|
)
|
||||||
|
if receipt.get("claim_group_id") != ref.get("claim_group_id"):
|
||||||
|
raise ContractError("ITEM_RECEIPT_GROUP_MISMATCH", f"$/item_receipts/{index}", ref["path"])
|
||||||
|
for index, ref in enumerate(cohort_refs):
|
||||||
|
raw = cohort_receipt_bytes_by_path[ref["path"]]
|
||||||
|
if sha256_bytes(raw) != ref.get("sha256"):
|
||||||
|
raise ContractError("COHORT_RECEIPT_RAW_HASH_MISMATCH", f"$/cohort_receipts/{index}", ref["path"])
|
||||||
|
receipt = parse_canonical_json_object(raw, f"$/cohort_receipts/{index}")
|
||||||
|
_validate_receipt_object(
|
||||||
|
receipt,
|
||||||
|
"cohort_receipt",
|
||||||
|
manifest["part_manifest_core_sha256"],
|
||||||
|
common,
|
||||||
|
None,
|
||||||
|
schema,
|
||||||
|
f"$/cohort_receipts/{index}",
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"status": "PASS",
|
||||||
|
"publish_status_allowed": True,
|
||||||
|
"claim_group_ids": expected_groups,
|
||||||
|
"part_count": manifest_report["part_count"],
|
||||||
|
"item_receipt_count": len(item_refs),
|
||||||
|
"cohort_receipt_count": len(cohort_refs),
|
||||||
|
"physical_atomicity_attested": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def validate_publish_barrier(bundle: Mapping[str, Any]) -> dict[str, Any]:
|
def validate_publish_barrier(bundle: Mapping[str, Any]) -> dict[str, Any]:
|
||||||
|
"""Status-last barrier oracle over explicit objects/bytes; discovery is forbidden."""
|
||||||
|
|
||||||
expected = set(bundle.get("expected_group_ids", []))
|
expected = set(bundle.get("expected_group_ids", []))
|
||||||
succeeded = set(bundle.get("succeeded_group_ids", []))
|
succeeded = set(bundle.get("succeeded_group_ids", []))
|
||||||
failed = set(bundle.get("failed_group_ids", []))
|
failed = set(bundle.get("failed_group_ids", []))
|
||||||
if not expected or succeeded & failed or succeeded | failed != expected:
|
if not expected or succeeded & failed or succeeded | failed != expected:
|
||||||
raise ContractError("PUBLISH_PARTITION_MISMATCH", "$", repr((expected, succeeded, failed)))
|
raise ContractError("PUBLISH_PARTITION_MISMATCH", "$", repr((expected, succeeded, failed)))
|
||||||
artifact_rows = bundle.get("artifact_rows")
|
|
||||||
if not isinstance(artifact_rows, list):
|
|
||||||
raise ContractError("ARTIFACT_ROWS_REQUIRED", "$/artifact_rows", repr(type(artifact_rows)))
|
|
||||||
verified_families: dict[str, set[str]] = {group_id: set() for group_id in expected}
|
|
||||||
for index, row in enumerate(artifact_rows):
|
|
||||||
if not isinstance(row, dict):
|
|
||||||
raise ContractError("ARTIFACT_ROW_OBJECT_REQUIRED", f"$/artifact_rows/{index}", repr(row))
|
|
||||||
if set(row) != PUBLISH_ARTIFACT_ROW_FIELDS:
|
|
||||||
raise ContractError(
|
|
||||||
"ARTIFACT_ROW_CLOSED_SHAPE",
|
|
||||||
f"$/artifact_rows/{index}",
|
|
||||||
repr(sorted(set(row) ^ PUBLISH_ARTIFACT_ROW_FIELDS)),
|
|
||||||
)
|
|
||||||
_require_self_hash(row, "artifact_receipt_sha256", f"$/artifact_rows/{index}")
|
|
||||||
group_id = row.get("claim_group_id")
|
|
||||||
family = row.get("artifact_family")
|
|
||||||
if group_id not in expected or family not in SUCCESS_ARTIFACT_FAMILIES:
|
|
||||||
raise ContractError("ARTIFACT_FAMILY_OR_GROUP_INVALID", f"$/artifact_rows/{index}", repr((group_id, family)))
|
|
||||||
expected_path = f"map_s2_30/{SUCCESS_ARTIFACT_FILENAMES[str(family)]}/{group_id}.json"
|
|
||||||
receipt_path = f"map_s2_30/item_receipts/{group_id}.json"
|
|
||||||
if row.get("artifact_path") != expected_path or row.get("item_receipt_path") != receipt_path:
|
|
||||||
raise ContractError(
|
|
||||||
"ARTIFACT_PATH_BINDING_MISMATCH",
|
|
||||||
f"$/artifact_rows/{index}",
|
|
||||||
repr((row.get("artifact_path"), row.get("item_receipt_path"))),
|
|
||||||
)
|
|
||||||
artifact_sha = row.get("artifact_sha256")
|
|
||||||
read_back_sha = row.get("read_back_sha256")
|
|
||||||
item_receipt_sha = row.get("item_receipt_sha256")
|
|
||||||
if any(
|
|
||||||
not isinstance(value, str) or HEX64.fullmatch(value) is None
|
|
||||||
for value in (artifact_sha, read_back_sha, item_receipt_sha)
|
|
||||||
):
|
|
||||||
raise ContractError("ARTIFACT_HASH_INVALID", f"$/artifact_rows/{index}", repr(row))
|
|
||||||
if artifact_sha != read_back_sha:
|
|
||||||
raise ContractError(
|
|
||||||
"ARTIFACT_READ_BACK_HASH_MISMATCH",
|
|
||||||
f"$/artifact_rows/{index}",
|
|
||||||
repr((artifact_sha, read_back_sha)),
|
|
||||||
)
|
|
||||||
if family == "ITEM_RECEIPT" and artifact_sha != item_receipt_sha:
|
|
||||||
raise ContractError(
|
|
||||||
"ITEM_RECEIPT_HASH_BINDING_MISMATCH",
|
|
||||||
f"$/artifact_rows/{index}",
|
|
||||||
repr((artifact_sha, item_receipt_sha)),
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
row.get("immutable_write_status") in {"CREATED", "IDEMPOTENT_MATCH"}
|
|
||||||
and row.get("read_back_status") == "PASS"
|
|
||||||
and row.get("item_receipt_persistence_status") in {"PUBLISHED", "IDEMPOTENT_BYTE_IDENTICAL"}
|
|
||||||
):
|
|
||||||
if family in verified_families[str(group_id)]:
|
|
||||||
raise ContractError("ARTIFACT_FAMILY_DUPLICATE", f"$/artifact_rows/{index}", str(family))
|
|
||||||
verified_families[str(group_id)].add(str(family))
|
|
||||||
verified = {
|
|
||||||
group_id
|
|
||||||
for group_id, families in verified_families.items()
|
|
||||||
if families == SUCCESS_ARTIFACT_FAMILIES
|
|
||||||
}
|
|
||||||
status_written = bool(bundle.get("publish_status_written"))
|
status_written = bool(bundle.get("publish_status_written"))
|
||||||
if status_written and (failed or verified != expected):
|
if status_written and failed:
|
||||||
raise ContractError("STATUS_LAST_BARRIER_VIOLATION", "$/publish_status_written", repr((verified, expected, failed)))
|
raise ContractError("STATUS_LAST_BARRIER_VIOLATION", "$/publish_status_written", repr(sorted(failed)))
|
||||||
return {
|
required = (
|
||||||
"status": "PASS",
|
"publish_status",
|
||||||
"publish_status_allowed": not failed and verified == expected,
|
"artifact_manifest",
|
||||||
"physical_atomicity_attested": False,
|
"part_bytes_by_path",
|
||||||
}
|
"item_receipt_bytes_by_path",
|
||||||
|
"cohort_receipt_bytes_by_path",
|
||||||
|
)
|
||||||
|
missing = [key for key in required if key not in bundle]
|
||||||
|
if missing:
|
||||||
|
raise ContractError("STATUS_LAST_BARRIER_VIOLATION", "$", repr(missing))
|
||||||
|
report = validate_handoff_chain(
|
||||||
|
bundle["publish_status"],
|
||||||
|
bundle["artifact_manifest"],
|
||||||
|
bundle["part_bytes_by_path"],
|
||||||
|
bundle["item_receipt_bytes_by_path"],
|
||||||
|
bundle["cohort_receipt_bytes_by_path"],
|
||||||
|
bundle.get("schema"),
|
||||||
|
)
|
||||||
|
if set(report["claim_group_ids"]) != expected:
|
||||||
|
raise ContractError("PUBLISH_GROUP_SET_MISMATCH", "$", repr((report["claim_group_ids"], sorted(expected))))
|
||||||
|
report["publish_status_allowed"] = status_written and not failed
|
||||||
|
return report
|
||||||
|
|
||||||
|
|
||||||
def validate_pending_external_receipt(kind: str, receipt: Mapping[str, Any]) -> None:
|
def validate_pending_external_receipt(kind: str, receipt: Mapping[str, Any]) -> None:
|
||||||
@@ -1866,7 +2132,7 @@ def validate_embedded_task_admission(
|
|||||||
if not isinstance(stage_rows, list) or not isinstance(helper_rows, list):
|
if not isinstance(stage_rows, list) or not isinstance(helper_rows, list):
|
||||||
raise ContractError("EXECUTOR_BINDING_ARRAYS_REQUIRED", "$", "stage/helper")
|
raise ContractError("EXECUTOR_BINDING_ARRAYS_REQUIRED", "$", "stage/helper")
|
||||||
stage_ids = [row.get("stage_id") for row in stage_rows if isinstance(row, dict)]
|
stage_ids = [row.get("stage_id") for row in stage_rows if isinstance(row, dict)]
|
||||||
if stage_ids != ["S2_00", "S2_20"] or "S2_30" in stage_ids:
|
if stage_ids != ["S2_00", "S2_20", "S2_40"] or "S2_30" in stage_ids:
|
||||||
raise ContractError("S2_30_STAGE_MISCLASSIFIED", "$/stage_bindings", repr(stage_ids))
|
raise ContractError("S2_30_STAGE_MISCLASSIFIED", "$/stage_bindings", repr(stage_ids))
|
||||||
if len(helper_rows) != 1 or not isinstance(helper_rows[0], dict):
|
if len(helper_rows) != 1 or not isinstance(helper_rows[0], dict):
|
||||||
raise ContractError("S2_30_HELPER_EXACT_ONE_REQUIRED", "$/embedded_task_bindings", repr(helper_rows))
|
raise ContractError("S2_30_HELPER_EXACT_ONE_REQUIRED", "$/embedded_task_bindings", repr(helper_rows))
|
||||||
@@ -1907,7 +2173,7 @@ def validate_embedded_task_admission(
|
|||||||
|
|
||||||
if admission.get("executor_binding_sha256") != binding_sha256:
|
if admission.get("executor_binding_sha256") != binding_sha256:
|
||||||
raise ContractError("ADMISSION_EXECUTOR_HASH_MISMATCH", "$/executor_binding_sha256", repr(admission.get("executor_binding_sha256")))
|
raise ContractError("ADMISSION_EXECUTOR_HASH_MISMATCH", "$/executor_binding_sha256", repr(admission.get("executor_binding_sha256")))
|
||||||
if admission.get("present_deterministic_stage_ids") != ["S2_00", "S2_20"]:
|
if admission.get("present_deterministic_stage_ids") != ["S2_00", "S2_20", "S2_40"]:
|
||||||
raise ContractError("ADMISSION_STAGE_SET_MISMATCH", "$/present_deterministic_stage_ids", repr(admission.get("present_deterministic_stage_ids")))
|
raise ContractError("ADMISSION_STAGE_SET_MISMATCH", "$/present_deterministic_stage_ids", repr(admission.get("present_deterministic_stage_ids")))
|
||||||
embedded = admission.get("embedded_task_admissions")
|
embedded = admission.get("embedded_task_admissions")
|
||||||
if not isinstance(embedded, list) or len(embedded) != 1 or not isinstance(embedded[0], dict):
|
if not isinstance(embedded, list) or len(embedded) != 1 or not isinstance(embedded[0], dict):
|
||||||
|
|||||||
+350
-84
@@ -125,29 +125,46 @@ ADVERSE_REQUIRED = frozenset(
|
|||||||
"presentation_authorization_ref",
|
"presentation_authorization_ref",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
SUCCESS_ARTIFACT_FAMILIES = frozenset(
|
PART_FAMILIES = frozenset(
|
||||||
{"DRAFT_PART", "ISSUE_PATCH", "WORKNOTE_PART", "USAGE_PART", "ITEM_RECEIPT"}
|
{"DRAFT_PART", "ISSUE_PATCH", "WORKNOTE_PART", "USAGE_PART"}
|
||||||
)
|
)
|
||||||
SUCCESS_ARTIFACT_FILENAMES = {
|
PART_FILENAMES = {
|
||||||
"DRAFT_PART": "draft_parts",
|
"DRAFT_PART": "draft_parts",
|
||||||
"ISSUE_PATCH": "issue_patches",
|
"ISSUE_PATCH": "issue_patches",
|
||||||
"WORKNOTE_PART": "worknote_parts",
|
"WORKNOTE_PART": "worknote_parts",
|
||||||
"USAGE_PART": "usage_parts",
|
"USAGE_PART": "usage_parts",
|
||||||
"ITEM_RECEIPT": "item_receipts",
|
|
||||||
}
|
}
|
||||||
PUBLISH_ARTIFACT_ROW_FIELDS = frozenset(
|
PART_SCHEMA_REFS = {
|
||||||
|
"DRAFT_PART": "schemas/draft_atoms.schema.json#/$defs/draft_part",
|
||||||
|
"ISSUE_PATCH": "schemas/draft_atoms.schema.json#/$defs/issue_patch_part",
|
||||||
|
"WORKNOTE_PART": "schemas/draft_atoms.schema.json#/$defs/worknote_part",
|
||||||
|
"USAGE_PART": "schemas/draft_atoms.schema.json#/$defs/usage_part",
|
||||||
|
}
|
||||||
|
PART_DEF_NAMES = {
|
||||||
|
"DRAFT_PART": "draft_part",
|
||||||
|
"ISSUE_PATCH": "issue_patch_part",
|
||||||
|
"WORKNOTE_PART": "worknote_part",
|
||||||
|
"USAGE_PART": "usage_part",
|
||||||
|
}
|
||||||
|
PART_SELF_HASH_FIELDS = {family: "part_sha256" for family in PART_FAMILIES}
|
||||||
|
COMMON_PROVENANCE_FIELDS = frozenset(
|
||||||
{
|
{
|
||||||
"claim_group_id",
|
"compile_mode",
|
||||||
"artifact_family",
|
"parent_stage2_release_sha256",
|
||||||
"artifact_path",
|
"s2_30_release_sha256",
|
||||||
"artifact_sha256",
|
"s2_30_agent_sha256",
|
||||||
"read_back_sha256",
|
"s2_30_binding_sha256",
|
||||||
"immutable_write_status",
|
"p00_prompt_sha256",
|
||||||
"read_back_status",
|
"p30_prompt_sha256",
|
||||||
"item_receipt_path",
|
"s2_30_producer_contract_digest",
|
||||||
"item_receipt_sha256",
|
}
|
||||||
"item_receipt_persistence_status",
|
)
|
||||||
"artifact_receipt_sha256",
|
GROUP_PROVENANCE_FIELDS = frozenset(
|
||||||
|
{
|
||||||
|
*COMMON_PROVENANCE_FIELDS,
|
||||||
|
"p31_rule_and_pack_sha256",
|
||||||
|
"p32_common_authority_sha256",
|
||||||
|
"s30_group_slice_sha256",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
REQUIRED_PENDING = {
|
REQUIRED_PENDING = {
|
||||||
@@ -1765,79 +1782,328 @@ def evaluate_retry(
|
|||||||
raise ContractError("ATTEMPT_NO_INVALID", "$/attempt_no", repr(attempt_no))
|
raise ContractError("ATTEMPT_NO_INVALID", "$/attempt_no", repr(attempt_no))
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_handoff_provenance(
|
||||||
|
provenance: Mapping[str, Any],
|
||||||
|
*,
|
||||||
|
common_only: bool,
|
||||||
|
path: str,
|
||||||
|
) -> None:
|
||||||
|
expected = COMMON_PROVENANCE_FIELDS if common_only else GROUP_PROVENANCE_FIELDS
|
||||||
|
if set(provenance) != expected:
|
||||||
|
raise ContractError(
|
||||||
|
"HANDOFF_PROVENANCE_CLOSED_SHAPE",
|
||||||
|
path,
|
||||||
|
repr(sorted(set(provenance) ^ expected)),
|
||||||
|
)
|
||||||
|
if provenance.get("compile_mode") not in ALLOWED_MODES:
|
||||||
|
raise ContractError(
|
||||||
|
"COMPILE_MODE_INVALID", f"{path}/compile_mode", repr(provenance.get("compile_mode"))
|
||||||
|
)
|
||||||
|
for key in expected - {"compile_mode"}:
|
||||||
|
value = provenance.get(key)
|
||||||
|
if not isinstance(value, str) or HEX64.fullmatch(value) is None:
|
||||||
|
raise ContractError("HANDOFF_PROVENANCE_HASH_INVALID", f"{path}/{key}", repr(value))
|
||||||
|
|
||||||
|
|
||||||
|
def _common_provenance(provenance: Mapping[str, Any]) -> dict[str, Any]:
|
||||||
|
return {key: provenance[key] for key in sorted(COMMON_PROVENANCE_FIELDS)}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_persisted_part(
|
||||||
|
part: Mapping[str, Any],
|
||||||
|
family: str,
|
||||||
|
*,
|
||||||
|
expected_group_id: str | None = None,
|
||||||
|
expected_common_provenance: Mapping[str, Any] | None = None,
|
||||||
|
schema: Mapping[str, Any] | None = None,
|
||||||
|
path: str = "$/part",
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Validate one immutable S2_30 physical part without discovering siblings."""
|
||||||
|
|
||||||
|
if family not in PART_FAMILIES:
|
||||||
|
raise ContractError("PART_FAMILY_INVALID", path, repr(family))
|
||||||
|
if schema is not None:
|
||||||
|
validate_instance(part, PART_DEF_NAMES[family], schema)
|
||||||
|
_require_self_hash(part, PART_SELF_HASH_FIELDS[family], path)
|
||||||
|
group_id = part.get("claim_group_id")
|
||||||
|
if expected_group_id is not None and group_id != expected_group_id:
|
||||||
|
raise ContractError(
|
||||||
|
"PART_GROUP_MISMATCH", f"{path}/claim_group_id", repr((group_id, expected_group_id))
|
||||||
|
)
|
||||||
|
provenance = part.get("provenance")
|
||||||
|
if not isinstance(provenance, dict):
|
||||||
|
raise ContractError("HANDOFF_PROVENANCE_REQUIRED", f"{path}/provenance", repr(provenance))
|
||||||
|
_validate_handoff_provenance(provenance, common_only=False, path=f"{path}/provenance")
|
||||||
|
if expected_common_provenance is not None and _common_provenance(provenance) != dict(
|
||||||
|
expected_common_provenance
|
||||||
|
):
|
||||||
|
raise ContractError(
|
||||||
|
"PART_COMMON_PROVENANCE_MISMATCH",
|
||||||
|
f"{path}/provenance",
|
||||||
|
repr((_common_provenance(provenance), dict(expected_common_provenance))),
|
||||||
|
)
|
||||||
|
if family == "DRAFT_PART":
|
||||||
|
atoms = part.get("canonical_atoms")
|
||||||
|
atom_ids = [row.get("atom_id") for row in atoms] if isinstance(atoms, list) else []
|
||||||
|
if len(atom_ids) != len(set(atom_ids)):
|
||||||
|
raise ContractError("DRAFT_PART_ATOM_ID_DUPLICATE", f"{path}/canonical_atoms", repr(atom_ids))
|
||||||
|
if any(isinstance(row, dict) and row.get("claim_group_id") != group_id for row in atoms or []):
|
||||||
|
raise ContractError("DRAFT_PART_ATOM_GROUP_MISMATCH", f"{path}/canonical_atoms", repr(group_id))
|
||||||
|
coverage = part.get("atomic_claim_coverage")
|
||||||
|
coverage_refs = [
|
||||||
|
ref
|
||||||
|
for row in coverage or []
|
||||||
|
if isinstance(row, dict)
|
||||||
|
for key in ("relief_atom_local_refs", "cause_atom_local_refs")
|
||||||
|
for ref in row.get(key, [])
|
||||||
|
]
|
||||||
|
if len(coverage_refs) != len(set(coverage_refs)) or set(coverage_refs) != set(atom_ids) - {
|
||||||
|
row.get("atom_id")
|
||||||
|
for row in atoms or []
|
||||||
|
if isinstance(row, dict) and row.get("output_section") in {"procedural_declaration", "worknote_only"}
|
||||||
|
}:
|
||||||
|
raise ContractError("DRAFT_PART_COVERAGE_PARTITION_MISMATCH", f"{path}/atomic_claim_coverage", repr(coverage_refs))
|
||||||
|
return {"status": "PASS", "claim_group_id": group_id, "part_family": family}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_part_manifest_core(
|
||||||
|
manifest: Mapping[str, Any],
|
||||||
|
part_bytes_by_path: Mapping[str, bytes],
|
||||||
|
schema: Mapping[str, Any] | None = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Validate the receipt-free exact part index using only enumerated byte inputs."""
|
||||||
|
|
||||||
|
if schema is not None:
|
||||||
|
validate_instance(manifest, "part_manifest_core", schema)
|
||||||
|
_require_self_hash(manifest, "part_manifest_core_sha256", "$/artifact_manifest")
|
||||||
|
if any("receipt" in key.lower() for key in manifest):
|
||||||
|
raise ContractError("MANIFEST_RECEIPT_REFERENCE_FORBIDDEN", "$/artifact_manifest", repr(sorted(manifest)))
|
||||||
|
common = manifest.get("provenance")
|
||||||
|
if not isinstance(common, dict):
|
||||||
|
raise ContractError("HANDOFF_PROVENANCE_REQUIRED", "$/artifact_manifest/provenance", repr(common))
|
||||||
|
_validate_handoff_provenance(common, common_only=True, path="$/artifact_manifest/provenance")
|
||||||
|
expected_groups = manifest.get("expected_claim_group_ids")
|
||||||
|
if not isinstance(expected_groups, list) or expected_groups != sorted(expected_groups):
|
||||||
|
raise ContractError("MANIFEST_GROUP_ORDER_INVALID", "$/artifact_manifest/expected_claim_group_ids", repr(expected_groups))
|
||||||
|
rows = manifest.get("part_rows")
|
||||||
|
if not isinstance(rows, list):
|
||||||
|
raise ContractError("MANIFEST_PART_ROWS_REQUIRED", "$/artifact_manifest/part_rows", repr(rows))
|
||||||
|
if any(not isinstance(row, dict) for row in rows):
|
||||||
|
raise ContractError("MANIFEST_PART_ROW_OBJECT_REQUIRED", "$/artifact_manifest/part_rows", repr(rows))
|
||||||
|
sort_key = lambda row: (row.get("claim_group_id"), row.get("part_family"), row.get("path"))
|
||||||
|
if rows != sorted(rows, key=sort_key):
|
||||||
|
raise ContractError("MANIFEST_PART_ROW_ORDER_INVALID", "$/artifact_manifest/part_rows", "not stable sorted")
|
||||||
|
expected_pairs = {(group_id, family) for group_id in expected_groups for family in PART_FAMILIES}
|
||||||
|
observed_pairs: set[tuple[str, str]] = set()
|
||||||
|
declared_paths: list[str] = []
|
||||||
|
group_provenance: dict[str, dict[str, Any]] = {}
|
||||||
|
for index, row in enumerate(rows):
|
||||||
|
if not isinstance(row, dict):
|
||||||
|
raise ContractError("MANIFEST_PART_ROW_OBJECT_REQUIRED", f"$/artifact_manifest/part_rows/{index}", repr(row))
|
||||||
|
group_id = row.get("claim_group_id")
|
||||||
|
family = row.get("part_family")
|
||||||
|
pair = (str(group_id), str(family))
|
||||||
|
if pair not in expected_pairs or pair in observed_pairs:
|
||||||
|
raise ContractError("MANIFEST_PART_PAIR_INVALID", f"$/artifact_manifest/part_rows/{index}", repr(pair))
|
||||||
|
expected_path = f"map_s2_30/{PART_FILENAMES[str(family)]}/{group_id}.json"
|
||||||
|
if row.get("path") != expected_path or row.get("schema_ref") != PART_SCHEMA_REFS[str(family)]:
|
||||||
|
raise ContractError("MANIFEST_PART_BINDING_MISMATCH", f"$/artifact_manifest/part_rows/{index}", repr(row))
|
||||||
|
declared_paths.append(expected_path)
|
||||||
|
raw = part_bytes_by_path.get(expected_path)
|
||||||
|
if not isinstance(raw, bytes):
|
||||||
|
raise ContractError("MANIFEST_PART_BYTES_MISSING", f"$/parts/{expected_path}", repr(type(raw)))
|
||||||
|
if sha256_bytes(raw) != row.get("sha256"):
|
||||||
|
raise ContractError("MANIFEST_PART_RAW_HASH_MISMATCH", f"$/parts/{expected_path}", repr(row.get("sha256")))
|
||||||
|
part = parse_canonical_json_object(raw, f"$/parts/{expected_path}")
|
||||||
|
validate_persisted_part(
|
||||||
|
part,
|
||||||
|
str(family),
|
||||||
|
expected_group_id=str(group_id),
|
||||||
|
expected_common_provenance=common,
|
||||||
|
schema=schema,
|
||||||
|
path=f"$/parts/{expected_path}",
|
||||||
|
)
|
||||||
|
observed_provenance = dict(part["provenance"])
|
||||||
|
previous_provenance = group_provenance.get(str(group_id))
|
||||||
|
if previous_provenance is not None and observed_provenance != previous_provenance:
|
||||||
|
raise ContractError(
|
||||||
|
"PART_GROUP_PROVENANCE_MISMATCH",
|
||||||
|
f"$/parts/{expected_path}/provenance",
|
||||||
|
repr((previous_provenance, observed_provenance)),
|
||||||
|
)
|
||||||
|
group_provenance[str(group_id)] = observed_provenance
|
||||||
|
observed_pairs.add(pair)
|
||||||
|
if observed_pairs != expected_pairs:
|
||||||
|
raise ContractError("MANIFEST_PART_SET_MISMATCH", "$/artifact_manifest/part_rows", repr((observed_pairs, expected_pairs)))
|
||||||
|
if set(part_bytes_by_path) != set(declared_paths):
|
||||||
|
raise ContractError(
|
||||||
|
"UNENUMERATED_PART_INPUT_FORBIDDEN",
|
||||||
|
"$/parts",
|
||||||
|
repr(sorted(set(part_bytes_by_path) ^ set(declared_paths))),
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"status": "PASS",
|
||||||
|
"claim_group_ids": expected_groups,
|
||||||
|
"part_count": len(rows),
|
||||||
|
"part_manifest_core_sha256": manifest["part_manifest_core_sha256"],
|
||||||
|
"group_provenance": group_provenance,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_receipt_object(
|
||||||
|
receipt: Mapping[str, Any],
|
||||||
|
def_name: str,
|
||||||
|
manifest_core_sha256: str,
|
||||||
|
common_provenance: Mapping[str, Any],
|
||||||
|
expected_group_provenance: Mapping[str, Any] | None,
|
||||||
|
schema: Mapping[str, Any] | None,
|
||||||
|
path: str,
|
||||||
|
) -> None:
|
||||||
|
if schema is not None:
|
||||||
|
validate_instance(receipt, def_name, schema)
|
||||||
|
_require_self_hash(receipt, "receipt_sha256", path)
|
||||||
|
if receipt.get("part_manifest_core_sha256") != manifest_core_sha256:
|
||||||
|
raise ContractError(
|
||||||
|
"RECEIPT_MANIFEST_CORE_HASH_MISMATCH",
|
||||||
|
f"{path}/part_manifest_core_sha256",
|
||||||
|
repr(receipt.get("part_manifest_core_sha256")),
|
||||||
|
)
|
||||||
|
provenance = receipt.get("provenance")
|
||||||
|
if not isinstance(provenance, dict):
|
||||||
|
raise ContractError("HANDOFF_PROVENANCE_REQUIRED", f"{path}/provenance", repr(provenance))
|
||||||
|
_validate_handoff_provenance(
|
||||||
|
provenance,
|
||||||
|
common_only=def_name == "cohort_receipt",
|
||||||
|
path=f"{path}/provenance",
|
||||||
|
)
|
||||||
|
if _common_provenance(provenance) != dict(common_provenance):
|
||||||
|
raise ContractError("RECEIPT_COMMON_PROVENANCE_MISMATCH", f"{path}/provenance", repr(provenance))
|
||||||
|
if expected_group_provenance is not None and dict(provenance) != dict(expected_group_provenance):
|
||||||
|
raise ContractError("ITEM_RECEIPT_GROUP_PROVENANCE_MISMATCH", f"{path}/provenance", repr(provenance))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_handoff_chain(
|
||||||
|
publish_status: Mapping[str, Any],
|
||||||
|
manifest: Mapping[str, Any],
|
||||||
|
part_bytes_by_path: Mapping[str, bytes],
|
||||||
|
item_receipt_bytes_by_path: Mapping[str, bytes],
|
||||||
|
cohort_receipt_bytes_by_path: Mapping[str, bytes],
|
||||||
|
schema: Mapping[str, Any] | None = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Validate barrier -> manifest -> parts and ordered receipts without scan/glob."""
|
||||||
|
|
||||||
|
if schema is not None:
|
||||||
|
validate_instance(publish_status, "publish_status", schema)
|
||||||
|
_require_self_hash(publish_status, "status_sha256", "$/publish_status")
|
||||||
|
manifest_report = validate_part_manifest_core(manifest, part_bytes_by_path, schema)
|
||||||
|
manifest_raw = canonical_json_bytes(manifest)
|
||||||
|
if publish_status.get("artifact_manifest_sha256") != sha256_bytes(manifest_raw):
|
||||||
|
raise ContractError("PUBLISH_MANIFEST_RAW_HASH_MISMATCH", "$/publish_status/artifact_manifest_sha256", "raw")
|
||||||
|
if publish_status.get("artifact_manifest_path") != "map_s2_30/artifact_manifest.json":
|
||||||
|
raise ContractError("PUBLISH_MANIFEST_PATH_MISMATCH", "$/publish_status/artifact_manifest_path", repr(publish_status.get("artifact_manifest_path")))
|
||||||
|
common = manifest["provenance"]
|
||||||
|
if publish_status.get("provenance") != common:
|
||||||
|
raise ContractError("PUBLISH_COMMON_PROVENANCE_MISMATCH", "$/publish_status/provenance", "manifest")
|
||||||
|
if publish_status.get("compile_mode") != common.get("compile_mode"):
|
||||||
|
raise ContractError("PUBLISH_COMPILE_MODE_MISMATCH", "$/publish_status/compile_mode", repr(publish_status.get("compile_mode")))
|
||||||
|
if publish_status.get("parent_stage2_release_sha256") != common.get("parent_stage2_release_sha256") or publish_status.get("s2_30_release_sha256") != common.get("s2_30_release_sha256"):
|
||||||
|
raise ContractError("PUBLISH_RELEASE_PROVENANCE_MISMATCH", "$/publish_status", "release")
|
||||||
|
expected_groups = list(manifest["expected_claim_group_ids"])
|
||||||
|
if publish_status.get("expected_claim_group_ids") != expected_groups or publish_status.get("published_claim_group_ids") != expected_groups:
|
||||||
|
raise ContractError("PUBLISH_GROUP_SET_MISMATCH", "$/publish_status", repr(expected_groups))
|
||||||
|
if publish_status.get("terminal_failure_claim_group_ids") != [] or publish_status.get("status") != "S2_30_COMPLETE" or publish_status.get("route") != "TO_S2_40":
|
||||||
|
raise ContractError("PUBLISH_SUCCESS_ROUTE_INVALID", "$/publish_status", repr(publish_status.get("route")))
|
||||||
|
|
||||||
|
item_refs = publish_status.get("ordered_item_receipts")
|
||||||
|
cohort_refs = publish_status.get("ordered_cohort_receipts")
|
||||||
|
if not isinstance(item_refs, list) or any(not isinstance(row, dict) for row in item_refs):
|
||||||
|
raise ContractError("ITEM_RECEIPT_REFS_INVALID", "$/publish_status/ordered_item_receipts", repr(item_refs))
|
||||||
|
if not isinstance(cohort_refs, list) or any(not isinstance(row, dict) for row in cohort_refs):
|
||||||
|
raise ContractError("COHORT_RECEIPT_REFS_INVALID", "$/publish_status/ordered_cohort_receipts", repr(cohort_refs))
|
||||||
|
if item_refs != sorted(item_refs, key=lambda row: (row.get("claim_group_id"), row.get("path"))):
|
||||||
|
raise ContractError("ITEM_RECEIPT_ORDER_INVALID", "$/publish_status/ordered_item_receipts", repr(item_refs))
|
||||||
|
if cohort_refs != sorted(cohort_refs, key=lambda row: row.get("path")):
|
||||||
|
raise ContractError("COHORT_RECEIPT_ORDER_INVALID", "$/publish_status/ordered_cohort_receipts", repr(cohort_refs))
|
||||||
|
if [row.get("claim_group_id") for row in item_refs] != expected_groups:
|
||||||
|
raise ContractError("ITEM_RECEIPT_GROUP_SET_MISMATCH", "$/publish_status/ordered_item_receipts", repr(item_refs))
|
||||||
|
if set(item_receipt_bytes_by_path) != {row.get("path") for row in item_refs}:
|
||||||
|
raise ContractError("ITEM_RECEIPT_INPUT_SET_MISMATCH", "$/item_receipts", repr(sorted(item_receipt_bytes_by_path)))
|
||||||
|
if set(cohort_receipt_bytes_by_path) != {row.get("path") for row in cohort_refs}:
|
||||||
|
raise ContractError("COHORT_RECEIPT_INPUT_SET_MISMATCH", "$/cohort_receipts", repr(sorted(cohort_receipt_bytes_by_path)))
|
||||||
|
for index, ref in enumerate(item_refs):
|
||||||
|
raw = item_receipt_bytes_by_path[ref["path"]]
|
||||||
|
if sha256_bytes(raw) != ref.get("sha256"):
|
||||||
|
raise ContractError("ITEM_RECEIPT_RAW_HASH_MISMATCH", f"$/item_receipts/{index}", ref["path"])
|
||||||
|
receipt = parse_canonical_json_object(raw, f"$/item_receipts/{index}")
|
||||||
|
_validate_receipt_object(
|
||||||
|
receipt,
|
||||||
|
"item_receipt",
|
||||||
|
manifest["part_manifest_core_sha256"],
|
||||||
|
common,
|
||||||
|
manifest_report["group_provenance"].get(str(ref.get("claim_group_id"))),
|
||||||
|
schema,
|
||||||
|
f"$/item_receipts/{index}",
|
||||||
|
)
|
||||||
|
if receipt.get("claim_group_id") != ref.get("claim_group_id"):
|
||||||
|
raise ContractError("ITEM_RECEIPT_GROUP_MISMATCH", f"$/item_receipts/{index}", ref["path"])
|
||||||
|
for index, ref in enumerate(cohort_refs):
|
||||||
|
raw = cohort_receipt_bytes_by_path[ref["path"]]
|
||||||
|
if sha256_bytes(raw) != ref.get("sha256"):
|
||||||
|
raise ContractError("COHORT_RECEIPT_RAW_HASH_MISMATCH", f"$/cohort_receipts/{index}", ref["path"])
|
||||||
|
receipt = parse_canonical_json_object(raw, f"$/cohort_receipts/{index}")
|
||||||
|
_validate_receipt_object(
|
||||||
|
receipt,
|
||||||
|
"cohort_receipt",
|
||||||
|
manifest["part_manifest_core_sha256"],
|
||||||
|
common,
|
||||||
|
None,
|
||||||
|
schema,
|
||||||
|
f"$/cohort_receipts/{index}",
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"status": "PASS",
|
||||||
|
"publish_status_allowed": True,
|
||||||
|
"claim_group_ids": expected_groups,
|
||||||
|
"part_count": manifest_report["part_count"],
|
||||||
|
"item_receipt_count": len(item_refs),
|
||||||
|
"cohort_receipt_count": len(cohort_refs),
|
||||||
|
"physical_atomicity_attested": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def validate_publish_barrier(bundle: Mapping[str, Any]) -> dict[str, Any]:
|
def validate_publish_barrier(bundle: Mapping[str, Any]) -> dict[str, Any]:
|
||||||
|
"""Status-last barrier oracle over explicit objects/bytes; discovery is forbidden."""
|
||||||
|
|
||||||
expected = set(bundle.get("expected_group_ids", []))
|
expected = set(bundle.get("expected_group_ids", []))
|
||||||
succeeded = set(bundle.get("succeeded_group_ids", []))
|
succeeded = set(bundle.get("succeeded_group_ids", []))
|
||||||
failed = set(bundle.get("failed_group_ids", []))
|
failed = set(bundle.get("failed_group_ids", []))
|
||||||
if not expected or succeeded & failed or succeeded | failed != expected:
|
if not expected or succeeded & failed or succeeded | failed != expected:
|
||||||
raise ContractError("PUBLISH_PARTITION_MISMATCH", "$", repr((expected, succeeded, failed)))
|
raise ContractError("PUBLISH_PARTITION_MISMATCH", "$", repr((expected, succeeded, failed)))
|
||||||
artifact_rows = bundle.get("artifact_rows")
|
|
||||||
if not isinstance(artifact_rows, list):
|
|
||||||
raise ContractError("ARTIFACT_ROWS_REQUIRED", "$/artifact_rows", repr(type(artifact_rows)))
|
|
||||||
verified_families: dict[str, set[str]] = {group_id: set() for group_id in expected}
|
|
||||||
for index, row in enumerate(artifact_rows):
|
|
||||||
if not isinstance(row, dict):
|
|
||||||
raise ContractError("ARTIFACT_ROW_OBJECT_REQUIRED", f"$/artifact_rows/{index}", repr(row))
|
|
||||||
if set(row) != PUBLISH_ARTIFACT_ROW_FIELDS:
|
|
||||||
raise ContractError(
|
|
||||||
"ARTIFACT_ROW_CLOSED_SHAPE",
|
|
||||||
f"$/artifact_rows/{index}",
|
|
||||||
repr(sorted(set(row) ^ PUBLISH_ARTIFACT_ROW_FIELDS)),
|
|
||||||
)
|
|
||||||
_require_self_hash(row, "artifact_receipt_sha256", f"$/artifact_rows/{index}")
|
|
||||||
group_id = row.get("claim_group_id")
|
|
||||||
family = row.get("artifact_family")
|
|
||||||
if group_id not in expected or family not in SUCCESS_ARTIFACT_FAMILIES:
|
|
||||||
raise ContractError("ARTIFACT_FAMILY_OR_GROUP_INVALID", f"$/artifact_rows/{index}", repr((group_id, family)))
|
|
||||||
expected_path = f"map_s2_30/{SUCCESS_ARTIFACT_FILENAMES[str(family)]}/{group_id}.json"
|
|
||||||
receipt_path = f"map_s2_30/item_receipts/{group_id}.json"
|
|
||||||
if row.get("artifact_path") != expected_path or row.get("item_receipt_path") != receipt_path:
|
|
||||||
raise ContractError(
|
|
||||||
"ARTIFACT_PATH_BINDING_MISMATCH",
|
|
||||||
f"$/artifact_rows/{index}",
|
|
||||||
repr((row.get("artifact_path"), row.get("item_receipt_path"))),
|
|
||||||
)
|
|
||||||
artifact_sha = row.get("artifact_sha256")
|
|
||||||
read_back_sha = row.get("read_back_sha256")
|
|
||||||
item_receipt_sha = row.get("item_receipt_sha256")
|
|
||||||
if any(
|
|
||||||
not isinstance(value, str) or HEX64.fullmatch(value) is None
|
|
||||||
for value in (artifact_sha, read_back_sha, item_receipt_sha)
|
|
||||||
):
|
|
||||||
raise ContractError("ARTIFACT_HASH_INVALID", f"$/artifact_rows/{index}", repr(row))
|
|
||||||
if artifact_sha != read_back_sha:
|
|
||||||
raise ContractError(
|
|
||||||
"ARTIFACT_READ_BACK_HASH_MISMATCH",
|
|
||||||
f"$/artifact_rows/{index}",
|
|
||||||
repr((artifact_sha, read_back_sha)),
|
|
||||||
)
|
|
||||||
if family == "ITEM_RECEIPT" and artifact_sha != item_receipt_sha:
|
|
||||||
raise ContractError(
|
|
||||||
"ITEM_RECEIPT_HASH_BINDING_MISMATCH",
|
|
||||||
f"$/artifact_rows/{index}",
|
|
||||||
repr((artifact_sha, item_receipt_sha)),
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
row.get("immutable_write_status") in {"CREATED", "IDEMPOTENT_MATCH"}
|
|
||||||
and row.get("read_back_status") == "PASS"
|
|
||||||
and row.get("item_receipt_persistence_status") in {"PUBLISHED", "IDEMPOTENT_BYTE_IDENTICAL"}
|
|
||||||
):
|
|
||||||
if family in verified_families[str(group_id)]:
|
|
||||||
raise ContractError("ARTIFACT_FAMILY_DUPLICATE", f"$/artifact_rows/{index}", str(family))
|
|
||||||
verified_families[str(group_id)].add(str(family))
|
|
||||||
verified = {
|
|
||||||
group_id
|
|
||||||
for group_id, families in verified_families.items()
|
|
||||||
if families == SUCCESS_ARTIFACT_FAMILIES
|
|
||||||
}
|
|
||||||
status_written = bool(bundle.get("publish_status_written"))
|
status_written = bool(bundle.get("publish_status_written"))
|
||||||
if status_written and (failed or verified != expected):
|
if status_written and failed:
|
||||||
raise ContractError("STATUS_LAST_BARRIER_VIOLATION", "$/publish_status_written", repr((verified, expected, failed)))
|
raise ContractError("STATUS_LAST_BARRIER_VIOLATION", "$/publish_status_written", repr(sorted(failed)))
|
||||||
return {
|
required = (
|
||||||
"status": "PASS",
|
"publish_status",
|
||||||
"publish_status_allowed": not failed and verified == expected,
|
"artifact_manifest",
|
||||||
"physical_atomicity_attested": False,
|
"part_bytes_by_path",
|
||||||
}
|
"item_receipt_bytes_by_path",
|
||||||
|
"cohort_receipt_bytes_by_path",
|
||||||
|
)
|
||||||
|
missing = [key for key in required if key not in bundle]
|
||||||
|
if missing:
|
||||||
|
raise ContractError("STATUS_LAST_BARRIER_VIOLATION", "$", repr(missing))
|
||||||
|
report = validate_handoff_chain(
|
||||||
|
bundle["publish_status"],
|
||||||
|
bundle["artifact_manifest"],
|
||||||
|
bundle["part_bytes_by_path"],
|
||||||
|
bundle["item_receipt_bytes_by_path"],
|
||||||
|
bundle["cohort_receipt_bytes_by_path"],
|
||||||
|
bundle.get("schema"),
|
||||||
|
)
|
||||||
|
if set(report["claim_group_ids"]) != expected:
|
||||||
|
raise ContractError("PUBLISH_GROUP_SET_MISMATCH", "$", repr((report["claim_group_ids"], sorted(expected))))
|
||||||
|
report["publish_status_allowed"] = status_written and not failed
|
||||||
|
return report
|
||||||
|
|
||||||
|
|
||||||
def validate_pending_external_receipt(kind: str, receipt: Mapping[str, Any]) -> None:
|
def validate_pending_external_receipt(kind: str, receipt: Mapping[str, Any]) -> None:
|
||||||
@@ -1866,7 +2132,7 @@ def validate_embedded_task_admission(
|
|||||||
if not isinstance(stage_rows, list) or not isinstance(helper_rows, list):
|
if not isinstance(stage_rows, list) or not isinstance(helper_rows, list):
|
||||||
raise ContractError("EXECUTOR_BINDING_ARRAYS_REQUIRED", "$", "stage/helper")
|
raise ContractError("EXECUTOR_BINDING_ARRAYS_REQUIRED", "$", "stage/helper")
|
||||||
stage_ids = [row.get("stage_id") for row in stage_rows if isinstance(row, dict)]
|
stage_ids = [row.get("stage_id") for row in stage_rows if isinstance(row, dict)]
|
||||||
if stage_ids != ["S2_00", "S2_20"] or "S2_30" in stage_ids:
|
if stage_ids != ["S2_00", "S2_20", "S2_40"] or "S2_30" in stage_ids:
|
||||||
raise ContractError("S2_30_STAGE_MISCLASSIFIED", "$/stage_bindings", repr(stage_ids))
|
raise ContractError("S2_30_STAGE_MISCLASSIFIED", "$/stage_bindings", repr(stage_ids))
|
||||||
if len(helper_rows) != 1 or not isinstance(helper_rows[0], dict):
|
if len(helper_rows) != 1 or not isinstance(helper_rows[0], dict):
|
||||||
raise ContractError("S2_30_HELPER_EXACT_ONE_REQUIRED", "$/embedded_task_bindings", repr(helper_rows))
|
raise ContractError("S2_30_HELPER_EXACT_ONE_REQUIRED", "$/embedded_task_bindings", repr(helper_rows))
|
||||||
@@ -1907,7 +2173,7 @@ def validate_embedded_task_admission(
|
|||||||
|
|
||||||
if admission.get("executor_binding_sha256") != binding_sha256:
|
if admission.get("executor_binding_sha256") != binding_sha256:
|
||||||
raise ContractError("ADMISSION_EXECUTOR_HASH_MISMATCH", "$/executor_binding_sha256", repr(admission.get("executor_binding_sha256")))
|
raise ContractError("ADMISSION_EXECUTOR_HASH_MISMATCH", "$/executor_binding_sha256", repr(admission.get("executor_binding_sha256")))
|
||||||
if admission.get("present_deterministic_stage_ids") != ["S2_00", "S2_20"]:
|
if admission.get("present_deterministic_stage_ids") != ["S2_00", "S2_20", "S2_40"]:
|
||||||
raise ContractError("ADMISSION_STAGE_SET_MISMATCH", "$/present_deterministic_stage_ids", repr(admission.get("present_deterministic_stage_ids")))
|
raise ContractError("ADMISSION_STAGE_SET_MISMATCH", "$/present_deterministic_stage_ids", repr(admission.get("present_deterministic_stage_ids")))
|
||||||
embedded = admission.get("embedded_task_admissions")
|
embedded = admission.get("embedded_task_admissions")
|
||||||
if not isinstance(embedded, list) or len(embedded) != 1 or not isinstance(embedded[0], dict):
|
if not isinstance(embedded, list) or len(embedded) != 1 or not isinstance(embedded[0], dict):
|
||||||
|
|||||||
+71
-22
@@ -5,28 +5,77 @@
|
|||||||
"execution_eligible": false,
|
"execution_eligible": false,
|
||||||
"runtime_legal_source": false,
|
"runtime_legal_source": false,
|
||||||
"required_fixture_case_ids": [
|
"required_fixture_case_ids": [
|
||||||
"S20-F01-SINGLE-OPTION-GROUP",
|
"S20-F01-SINGLE-OPTION-GROUP", "S20-F02-MIXED-PORTFOLIO", "S20-F03-CLIENT-NO-SUE",
|
||||||
"S20-F02-MIXED-PORTFOLIO",
|
"S20-F04-CASE-RULE-EXACTNESS", "S20-F05-ROW-KIND-BRANCH", "S20-F06-PARTY-TITLE",
|
||||||
"S20-F03-CLIENT-NO-SUE",
|
"S20-F07-CE01-INTEREST", "S20-F08-CE03-LIMITATION", "S20-F09-CE08-RESERVED-SHARE",
|
||||||
"S20-F04-CASE-RULE-EXACTNESS",
|
"S20-F10-ACTIO-ROUTE-CAP", "S20-F11-CE13-COURT-VALUE", "S20-F12-RETRIEVAL-BOUNDARY",
|
||||||
"S20-F05-ROW-KIND-BRANCH",
|
"S20-F13-CORPUS-INTEGRITY", "S20-F14-EXHIBIT-LABEL", "S20-F15-OPTION-SILENT-LOSS",
|
||||||
"S20-F06-PARTY-TITLE",
|
"S20-F16-PUBLISH-BARRIER", "S20-F17-LEGACY-PATH"
|
||||||
"S20-F07-CE01-INTEREST",
|
|
||||||
"S20-F08-CE03-LIMITATION",
|
|
||||||
"S20-F09-CE08-RESERVED-SHARE",
|
|
||||||
"S20-F10-ACTIO-ROUTE-CAP",
|
|
||||||
"S20-F11-CE13-COURT-VALUE",
|
|
||||||
"S20-F12-RETRIEVAL-BOUNDARY",
|
|
||||||
"S20-F13-CORPUS-INTEGRITY",
|
|
||||||
"S20-F14-EXHIBIT-LABEL",
|
|
||||||
"S20-F15-OPTION-SILENT-LOSS",
|
|
||||||
"S20-F16-PUBLISH-BARRIER",
|
|
||||||
"S20-F17-LEGACY-PATH"
|
|
||||||
],
|
],
|
||||||
"mapping_rows": [],
|
"mapping_rows": [
|
||||||
"mapped_fixture_case_count": 0,
|
{"source_finding_id":"DR1-STAGE1_TO_STAGE2_FACT_LOSS","source_locator":"discrepancy_report_1.md","invariant_id":"V01","fixture_case_id":"S40-F006","mutation_id":"V01-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"IR1-CRITICAL_FACT_CONSERVATION","source_locator":"improvement_report_1.md","invariant_id":"V02","fixture_case_id":"S40-F006","mutation_id":"V02-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"IR2-DOMAIN_CONFIG_COMPLETENESS","source_locator":"improvement_report_2.md","invariant_id":"V03","fixture_case_id":"S40-F006","mutation_id":"V03-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"IR3-DEFENSE_CHAIN_LOSS","source_locator":"improvement_report_3.md","invariant_id":"V04","fixture_case_id":"S40-F006","mutation_id":"V04-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"IM-CLAIM_GROUP_RELATION","source_locator":"improvement_merged.md","invariant_id":"V05","fixture_case_id":"S40-F006","mutation_id":"V05-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"EVAL-M15-CASE-TYPE-PREDICATE","source_locator":"eval_stage_2_optimal_update_strategy_v.3.md","invariant_id":"V06","fixture_case_id":"S40-F006","mutation_id":"V06-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"DR3-ACTIO-CALCULATION-LOSS","source_locator":"discrepancy_report_3.md","invariant_id":"V07","fixture_case_id":"S40-F007","mutation_id":"V07-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"IM-SAME-RECOVERY-DUPLICATE","source_locator":"improvement_merged.md","invariant_id":"V08","fixture_case_id":"S40-F006","mutation_id":"V08-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"DR4-RELIEF-OBJECT-DATE-MISMATCH","source_locator":"discrepancy_report_4.md","invariant_id":"V09","fixture_case_id":"S40-F005","mutation_id":"V09-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"IM-RELIEF-FORM-NUMBERING","source_locator":"improvement_merged.md","invariant_id":"V10","fixture_case_id":"S40-F004","mutation_id":"V10-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"DR1-RELIEF-CAUSE-CROSSMATCH","source_locator":"discrepancy_report_1.md","invariant_id":"V11","fixture_case_id":"S40-F005","mutation_id":"V11-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"EVAL-M16-CORPUS-NAMESPACE","source_locator":"eval_stage_2_optimal_update_strategy_v.3.md","invariant_id":"V12","fixture_case_id":"S40-F006","mutation_id":"V12-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"EVAL-M13-LAW-VALUE-SEAL","source_locator":"eval_stage_2_optimal_update_strategy_v.3.md","invariant_id":"V13","fixture_case_id":"S40-F007","mutation_id":"V13-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"EVAL-M18-NONCYCLIC-SEAL","source_locator":"eval_stage_2_optimal_update_strategy_v.3.md","invariant_id":"V14","fixture_case_id":"S40-F015","mutation_id":"V14-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"EVAL-C1-CAUSE-ATOM-PROVENANCE","source_locator":"eval_stage_2_optimal_update_strategy_v.3.md","invariant_id":"V15","fixture_case_id":"S40-F008","mutation_id":"V15-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"IR4-OPTION-SILENT-LOSS","source_locator":"improvement_report_4.md","invariant_id":"V16","fixture_case_id":"S40-F003","mutation_id":"V16-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"DR2-OBJECT-EXHIBIT-COMPLETENESS","source_locator":"discrepancy_report_2.md","invariant_id":"V17","fixture_case_id":"S40-F008","mutation_id":"V17-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]},
|
||||||
|
{"source_finding_id":"IM-CLOSED-RENDERER-INDEPENDENCE","source_locator":"improvement_merged.md","invariant_id":"V18","fixture_case_id":"S40-F004","mutation_id":"V18-CONTRADICTION-OR-ABSENCE","expected_statuses":["FAIL","UNEVALUABLE"]}
|
||||||
|
],
|
||||||
|
"mapped_fixture_case_count": 7,
|
||||||
"required_fixture_case_count": 17,
|
"required_fixture_case_count": 17,
|
||||||
"source_finding_families": ["discrepancy_report_1..4", "improvement_report_1..4", "improvement_merged", "eval_stage_2_optimal_update_strategy_v.3"],
|
"s2_40_required_fixture_case_ids": [
|
||||||
"unresolved_reason_codes": ["SOURCE_FINDINGS_NOT_EXTRACTED","INVARIANT_MAPPING_NOT_REVIEWED","FIXTURE_CROSSWALK_NOT_SIGNED"],
|
"S40-F001", "S40-F002", "S40-F003", "S40-F004", "S40-F005",
|
||||||
"guards": {"runtime_routing_use_forbidden":true,"legal_proposition_source_use_forbidden":true,"unmapped_finding_silent_drop_forbidden":true,"pending_map_is_not_coverage_proof":true}
|
"S40-F006", "S40-F007", "S40-F008", "S40-F009", "S40-F010",
|
||||||
|
"S40-F011", "S40-F012", "S40-F013", "S40-F014", "S40-F015"
|
||||||
|
],
|
||||||
|
"s2_40_required_fixture_case_count": 15,
|
||||||
|
"s2_40_v_code_mapping_count": 18,
|
||||||
|
"s2_40_binding_status": "OFFLINE_BYTES_BOUND_LEGAL_AND_LIVE_PENDING",
|
||||||
|
"s2_40_fixture_family_coverage_rows": [
|
||||||
|
{"family_id":"S40-FAMILY-01","fixture_case_ids":["S40-F002"],"coverage":"STATUS_ONLY_EXACT_FIVE"},
|
||||||
|
{"family_id":"S40-FAMILY-02","fixture_case_ids":["S40-F001"],"coverage":"SINGLE_GROUP_LOGICAL_COMMIT"},
|
||||||
|
{"family_id":"S40-FAMILY-03","fixture_case_ids":["S40-F004","S40-F007"],"coverage":"MULTI_GROUP_RELATION_AND_NUMBERING"},
|
||||||
|
{"family_id":"S40-FAMILY-04","fixture_case_ids":["S40-F009","S40-F010"],"coverage":"GAP_PRESERVATION_AND_WAIT"},
|
||||||
|
{"family_id":"S40-FAMILY-05","fixture_case_ids":["S40-F006"],"coverage":"V01_V18_PASS_FAIL_UNEVALUABLE"},
|
||||||
|
{"family_id":"S40-FAMILY-06","fixture_case_ids":["S40-F004"],"coverage":"CASE_RULE_RENDERER_ZERO_MULTI_STALE_FREE_TEXT"},
|
||||||
|
{"family_id":"S40-FAMILY-07","fixture_case_ids":["S40-F007","S40-F008"],"coverage":"COST_PROVISIONAL_ANNEX_EXHIBIT_OBJECT_PARTY"},
|
||||||
|
{"family_id":"S40-FAMILY-08","fixture_case_ids":["S40-F005"],"coverage":"RELIEF_CAUSE_CROSSMATCH"},
|
||||||
|
{"family_id":"S40-FAMILY-09","fixture_case_ids":["S40-F006","S40-F007"],"coverage":"CORPUS_AUTHORITY_LAW_VALUE_CALC_PROVENANCE"},
|
||||||
|
{"family_id":"S40-FAMILY-10","fixture_case_ids":["S40-F003","S40-F005"],"coverage":"OPTION_RECOVERY_ISSUE_CONSERVATION"},
|
||||||
|
{"family_id":"S40-FAMILY-11","fixture_case_ids":["S40-F015"],"coverage":"CANDIDATE_DIGEST_NONCYCLE_TAMPER"},
|
||||||
|
{"family_id":"S40-FAMILY-12","fixture_case_ids":["S40-F010","S40-F011"],"coverage":"RECEIPT_WAIT_RESUME_COMMIT"},
|
||||||
|
{"family_id":"S40-FAMILY-13","fixture_case_ids":["S40-F010"],"coverage":"RECEIPT_TRUST_SCOPE_TIME_REVOCATION"},
|
||||||
|
{"family_id":"S40-FAMILY-14","fixture_case_ids":["S40-F012"],"coverage":"CONTENT_CHANGE_EARLIEST_OWNER"},
|
||||||
|
{"family_id":"S40-FAMILY-15","fixture_case_ids":["S40-F013"],"coverage":"PARTIAL_WRITE_AND_BARRIER_LAST"},
|
||||||
|
{"family_id":"S40-FAMILY-16","fixture_case_ids":["S40-F014"],"coverage":"IDEMPOTENCE_CONFLICT_CONCURRENCY"},
|
||||||
|
{"family_id":"S40-FAMILY-17","fixture_case_ids":["S40-F004"],"coverage":"LEGACY_EXTERNAL_PY_SCAN_SECRET_REJECTION"},
|
||||||
|
{"family_id":"S40-FAMILY-18","fixture_case_ids":["S40-F007"],"coverage":"ACTIO_STRUCTURAL_DEPENDENCIES_LEGAL_PENDING"},
|
||||||
|
{"family_id":"S40-FAMILY-19","fixture_case_ids":["S40-F007"],"coverage":"SPECIAL_CASE_TEMPORAL_STRUCTURES_LEGAL_PENDING"},
|
||||||
|
{"family_id":"S40-FAMILY-20","fixture_case_ids":["S40-F004"],"coverage":"CASE_TYPE_137_STRUCTURAL_COVERAGE_LEGAL_PENDING"}
|
||||||
|
],
|
||||||
|
"source_finding_families": [
|
||||||
|
"discrepancy_report_1..4", "improvement_report_1..4", "improvement_merged",
|
||||||
|
"eval_stage_2_optimal_update_strategy_v.3"
|
||||||
|
],
|
||||||
|
"unresolved_reason_codes": [
|
||||||
|
"SOURCE_FINDING_LOCATORS_NOT_SIGNED",
|
||||||
|
"KOREAN_LAWYER_FIXTURE_CROSSWALK_NOT_SIGNED"
|
||||||
|
],
|
||||||
|
"guards": {
|
||||||
|
"runtime_routing_use_forbidden": true,
|
||||||
|
"legal_proposition_source_use_forbidden": true,
|
||||||
|
"unmapped_finding_silent_drop_forbidden": true,
|
||||||
|
"pending_map_is_not_coverage_proof": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-2
@@ -19,16 +19,57 @@
|
|||||||
],
|
],
|
||||||
"exactly_one_policy_result_required": true
|
"exactly_one_policy_result_required": true
|
||||||
},
|
},
|
||||||
|
"final_review_contract": {
|
||||||
|
"policy_result_enum": [
|
||||||
|
"STANDARD_ATTORNEY_REVIEW",
|
||||||
|
"MANDATORY_SPECIALIST_REVIEW"
|
||||||
|
],
|
||||||
|
"base_required_receipt_types": [
|
||||||
|
"STANDARD_ATTORNEY_REVIEW"
|
||||||
|
],
|
||||||
|
"allowed_review_tags": [
|
||||||
|
"AUTHORITY_TEMPORAL_REVIEW",
|
||||||
|
"CALCULATION_SPECIALIST_REVIEW",
|
||||||
|
"CORPUS_GAP_REVIEW",
|
||||||
|
"PARTY_TITLE_REVIEW",
|
||||||
|
"RENDERER_BRANCH_REVIEW",
|
||||||
|
"SPECIAL_LAW_REVIEW"
|
||||||
|
],
|
||||||
|
"runtime_trigger_sources": [
|
||||||
|
"V01_V18_RESULT",
|
||||||
|
"RENDERER_BINDING",
|
||||||
|
"AUTHORITY_RELEASE",
|
||||||
|
"CORPUS_RELEASE",
|
||||||
|
"CALCULATION_RECEIPT"
|
||||||
|
],
|
||||||
|
"receipt_disposition_enum": [
|
||||||
|
"APPROVE_AS_IS",
|
||||||
|
"CONTENT_CHANGE_REQUIRED"
|
||||||
|
],
|
||||||
|
"external_cryptographic_verification_required": true,
|
||||||
|
"candidate_digest_and_review_subject_digest_required": true,
|
||||||
|
"in_place_candidate_edit_forbidden": true,
|
||||||
|
"missing_or_unapproved_policy_result": "STANDARD_ATTORNEY_REVIEW",
|
||||||
|
"missing_or_invalid_receipt_route": "WAIT_EXTERNAL_REVIEW"
|
||||||
|
},
|
||||||
"policy_rows": [],
|
"policy_rows": [],
|
||||||
"default_unapproved_result": {
|
"default_unapproved_result": {
|
||||||
"drafting_permission": "WORKNOTE_ONLY",
|
"drafting_permission": "WORKNOTE_ONLY",
|
||||||
"issue_code": "REVIEW_POLICY_NOT_APPROVED",
|
"issue_code": "REVIEW_POLICY_NOT_APPROVED",
|
||||||
"ready_eligible": false
|
"ready_eligible": false,
|
||||||
|
"required_receipt_types": [
|
||||||
|
"STANDARD_ATTORNEY_REVIEW"
|
||||||
|
],
|
||||||
|
"legal_readiness_ceiling": "LAWYER_REVIEW_REQUIRED"
|
||||||
},
|
},
|
||||||
"closure": {
|
"closure": {
|
||||||
"ready_requires_all_required_receipts": true,
|
"ready_requires_all_required_receipts": true,
|
||||||
"ready_requires_no_unresolved_blocking_issue": true,
|
"ready_requires_no_unresolved_blocking_issue": true,
|
||||||
"missing_policy_cannot_upgrade_permission": true
|
"missing_policy_cannot_upgrade_permission": true,
|
||||||
|
"ready_requires_production_compile_mode": true,
|
||||||
|
"ready_requires_v01_v18_all_pass": true,
|
||||||
|
"ready_requires_execution_eligible_legal_assets": true,
|
||||||
|
"candidate_receipt_cannot_cure_release_level_legal_gap": true
|
||||||
},
|
},
|
||||||
"review": {
|
"review": {
|
||||||
"required_role": "KOREAN_LAWYER",
|
"required_role": "KOREAN_LAWYER",
|
||||||
|
|||||||
+528
-20
@@ -13,8 +13,14 @@ import copy
|
|||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
from pathlib import Path, PurePosixPath
|
from pathlib import Path, PurePosixPath
|
||||||
|
import re
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except ImportError: # pragma: no cover - incomplete offline validation host
|
||||||
|
yaml = None # type: ignore[assignment]
|
||||||
|
|
||||||
|
|
||||||
MODULE_MANIFEST_SCHEMA = "stage2_module_manifest.v1"
|
MODULE_MANIFEST_SCHEMA = "stage2_module_manifest.v1"
|
||||||
PARENT_RELEASE_SCHEMA = "stage2_release.v2"
|
PARENT_RELEASE_SCHEMA = "stage2_release.v2"
|
||||||
@@ -73,9 +79,118 @@ FORBIDDEN_PARENT_MEMBERS = frozenset(
|
|||||||
"manifest/s2_30_model_benchmark_receipt.json",
|
"manifest/s2_30_model_benchmark_receipt.json",
|
||||||
"manifest/s2_30_legal_review_receipt.json",
|
"manifest/s2_30_legal_review_receipt.json",
|
||||||
"manifest/s2_30_release.json",
|
"manifest/s2_30_release.json",
|
||||||
|
"agent_scripts/Stage_2_S2_40.yml",
|
||||||
|
"runtime/s2_40_commit.py",
|
||||||
|
"runtime/s2_40_commit.txt",
|
||||||
|
"manifest/s2_40_inline_code_receipt.json",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
STAGE_GENERIC_METADATA = {
|
||||||
|
"S2_20": {
|
||||||
|
"asset_version": "s2_20.1",
|
||||||
|
"module_id_prefix": "S2_20-ASSET-",
|
||||||
|
"owner": "Stage_2_S2_20_owner",
|
||||||
|
"schema_version": "stage2_s2_20_generic_asset.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_20",
|
||||||
|
},
|
||||||
|
"S2_30": {
|
||||||
|
"asset_version": "s2_30.1",
|
||||||
|
"module_id_prefix": "S2_30-ASSET-",
|
||||||
|
"owner": "Stage_2_S2_30_owner",
|
||||||
|
"schema_version": "stage2_s2_30_generic_asset.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_30",
|
||||||
|
},
|
||||||
|
"S2_40": {
|
||||||
|
"asset_version": "s2_40.1",
|
||||||
|
"module_id_prefix": "S2_40-ASSET-",
|
||||||
|
"owner": "Stage_2_S2_40_owner",
|
||||||
|
"schema_version": "stage2_s2_40_generic_asset.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_40",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
S2_40_WORKFLOW_PATH = "workflows/S2_40_final_review_render_and_commit.yml"
|
||||||
|
S2_40_TRANSITIONED_STUB_VALUES = frozenset(
|
||||||
|
{
|
||||||
|
"WF-S2_40-STATUS-ONLY",
|
||||||
|
"s2_40.status_only.1",
|
||||||
|
"DRAFT_HANDOFF_STUB_HASH_BOUND",
|
||||||
|
"entrypoint_id == S2_40_STATUS_ONLY",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
S2_40_WORKFLOW_REQUIRED_METADATA: dict[str, Any] = {
|
||||||
|
"asset_version": "s2_40.finalizer.1",
|
||||||
|
"consumed_schema_ids": [
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_00/context.schema.v2.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_00/ingress.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_10/s2_10.schema.v2.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_20/binding_retrieval.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_20/calculation.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_20/relief_plan.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_30/draft_atoms.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_40/package.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/shared/deployment.schema.v3.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/shared/review_status.schema.v1.json",
|
||||||
|
],
|
||||||
|
"entry_routes": ["TO_S2_40", "TO_S2_40_STATUS_ONLY"],
|
||||||
|
"implementation_status": "IMPLEMENTED_OFFLINE_CONTRACT_LIVE_ADMISSION_PENDING",
|
||||||
|
"inputs": [
|
||||||
|
"ingress/ingress_status.json",
|
||||||
|
"ingress/technical_diagnostic.json",
|
||||||
|
"ingress/stage1_input_manifest.json",
|
||||||
|
"ingress/intake_report.json",
|
||||||
|
"review/issue_ledger.base.json",
|
||||||
|
"map_s2_10/s2_10_publish_status.json",
|
||||||
|
"plan/plan_publish_status.json",
|
||||||
|
"plan/canonical_relief_plan.json",
|
||||||
|
"plan/claim_groups.json",
|
||||||
|
"plan/case_type_bindings.json",
|
||||||
|
"map_s2_30/s2_30_publish_status.json",
|
||||||
|
"map_s2_30/artifact_manifest.json",
|
||||||
|
"map_s2_30/{draft_parts,issue_patches,worknote_parts,usage_parts}/<claim_group_id>.json",
|
||||||
|
"review/review_receipts/<request_id>.json",
|
||||||
|
"review/lawyer_judgment_record.json",
|
||||||
|
],
|
||||||
|
"legal_admission_status": "PENDING",
|
||||||
|
"live_admission_status": "PENDING",
|
||||||
|
"module_id": "WF-S2_40",
|
||||||
|
"module_kind": "WORKFLOW",
|
||||||
|
"outputs": [
|
||||||
|
"review/issue_ledger.final.json",
|
||||||
|
"review/assumption_ledger.json",
|
||||||
|
"review/llm_usage.jsonl",
|
||||||
|
"candidates/by-content-digest/<candidate_content_digest>/",
|
||||||
|
"review/review_requests/<request_id>.json",
|
||||||
|
"final/claim_relief.md",
|
||||||
|
"final/claim_cause.md",
|
||||||
|
"final/pleading_draft.md",
|
||||||
|
"final/stage2_package.json",
|
||||||
|
"commit/commit_intent.json",
|
||||||
|
"commit/stage2_commit_result.json",
|
||||||
|
"control/run_status.json",
|
||||||
|
],
|
||||||
|
"owner": "Stage_2_S2_40_owner",
|
||||||
|
"path": S2_40_WORKFLOW_PATH,
|
||||||
|
"produced_schema_ids": [
|
||||||
|
"stage2_s2_40_candidate_manifest.v1",
|
||||||
|
"stage2_s2_40_commit_intent.v1",
|
||||||
|
"stage2_s2_40_commit_result.v1",
|
||||||
|
"stage2_s2_40_package.v1",
|
||||||
|
"stage2_s2_40_run_status.v1",
|
||||||
|
],
|
||||||
|
"schema_version": "stage2_workflow_contract.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_40 and entry_route in {TO_S2_40,TO_S2_40_STATUS_ONLY}",
|
||||||
|
"semantic_review_status": "PENDING_LEGAL_AND_LIVE_ADMISSION",
|
||||||
|
"status_only_exact_inputs": [
|
||||||
|
"ingress/ingress_status.json",
|
||||||
|
"ingress/technical_diagnostic.json",
|
||||||
|
"ingress/stage1_input_manifest.json",
|
||||||
|
"ingress/intake_report.json",
|
||||||
|
"review/issue_ledger.base.json",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ValidationInputError(ValueError):
|
class ValidationInputError(ValueError):
|
||||||
"""Raised when a validation input cannot be parsed deterministically."""
|
"""Raised when a validation input cannot be parsed deterministically."""
|
||||||
@@ -103,6 +218,38 @@ def _load_json(path: Path) -> Any:
|
|||||||
raise ValidationInputError(f"JSON_READ_FAILED:{path.as_posix()}:{exc}") from exc
|
raise ValidationInputError(f"JSON_READ_FAILED:{path.as_posix()}:{exc}") from exc
|
||||||
|
|
||||||
|
|
||||||
|
if yaml is not None:
|
||||||
|
|
||||||
|
class _UniqueKeySafeLoader(yaml.SafeLoader):
|
||||||
|
def construct_mapping(self, node: Any, deep: bool = False) -> dict[Any, Any]:
|
||||||
|
self.flatten_mapping(node)
|
||||||
|
result: dict[Any, Any] = {}
|
||||||
|
for key_node, value_node in node.value:
|
||||||
|
key = self.construct_object(key_node, deep=deep)
|
||||||
|
if key in result:
|
||||||
|
raise ValidationInputError(f"DUPLICATE_YAML_KEY:{key!r}")
|
||||||
|
result[key] = self.construct_object(value_node, deep=deep)
|
||||||
|
return result
|
||||||
|
|
||||||
|
else: # pragma: no cover
|
||||||
|
|
||||||
|
class _UniqueKeySafeLoader: # type: ignore[no-redef]
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _load_yaml(path: Path) -> Any:
|
||||||
|
if yaml is None:
|
||||||
|
raise ValidationInputError("PYYAML_REQUIRED")
|
||||||
|
try:
|
||||||
|
raw = path.read_bytes()
|
||||||
|
text = raw.decode("utf-8")
|
||||||
|
if text.startswith("\ufeff"):
|
||||||
|
raise ValidationInputError(f"YAML_UTF8_BOM_FORBIDDEN:{path.as_posix()}")
|
||||||
|
return yaml.load(text, Loader=_UniqueKeySafeLoader)
|
||||||
|
except (OSError, UnicodeDecodeError, yaml.YAMLError) as exc:
|
||||||
|
raise ValidationInputError(f"YAML_READ_FAILED:{path.as_posix()}:{exc}") from exc
|
||||||
|
|
||||||
|
|
||||||
def _canonical_bytes(document: Any) -> bytes:
|
def _canonical_bytes(document: Any) -> bytes:
|
||||||
return (
|
return (
|
||||||
json.dumps(
|
json.dumps(
|
||||||
@@ -169,6 +316,89 @@ def _physical(root: Path, relative: str) -> Path | None:
|
|||||||
return resolved
|
return resolved
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_root(root: Path) -> Path:
|
||||||
|
"""Resolve a CLI/library root before deriving authoring-relative paths."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
resolved = root.resolve(strict=True)
|
||||||
|
except (FileNotFoundError, OSError) as exc:
|
||||||
|
raise ValidationInputError(f"ROOT_UNAVAILABLE:{root.as_posix()}") from exc
|
||||||
|
if not resolved.is_dir():
|
||||||
|
raise ValidationInputError(f"ROOT_DIRECTORY_REQUIRED:{resolved.as_posix()}")
|
||||||
|
return resolved
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_s2_40_workflow_module_row(
|
||||||
|
rows: Any,
|
||||||
|
) -> list[dict[str, str]]:
|
||||||
|
if not isinstance(rows, list):
|
||||||
|
return [_finding("S2_40_WORKFLOW_MODULE_ROW_REQUIRED", "$/modules", "modules array required")]
|
||||||
|
matches = [
|
||||||
|
row
|
||||||
|
for row in rows
|
||||||
|
if isinstance(row, dict) and row.get("path") == S2_40_WORKFLOW_PATH
|
||||||
|
]
|
||||||
|
s2_40_family_active = bool(matches) or any(
|
||||||
|
isinstance(row, dict)
|
||||||
|
and (
|
||||||
|
row.get("owner") == "Stage_2_S2_40_owner"
|
||||||
|
or row.get("schema_version") == "stage2_s2_40_generic_asset.v1"
|
||||||
|
or (
|
||||||
|
isinstance(row.get("module_id"), str)
|
||||||
|
and row["module_id"].startswith("S2_40-ASSET-")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for row in rows
|
||||||
|
)
|
||||||
|
if not s2_40_family_active:
|
||||||
|
# Pre-S2_40 synthetic/unit manifests remain valid inputs. Once any
|
||||||
|
# S2_40 family member is present, however, the full workflow row is a
|
||||||
|
# mandatory semantic anchor and a status-only substitute is forbidden.
|
||||||
|
return []
|
||||||
|
if len(matches) != 1:
|
||||||
|
return [
|
||||||
|
_finding(
|
||||||
|
"S2_40_WORKFLOW_MODULE_ROW_EXACT_ONE_REQUIRED",
|
||||||
|
"$/modules",
|
||||||
|
f"observed={len(matches)}",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
row = matches[0]
|
||||||
|
observed_stub = sorted(
|
||||||
|
str(value)
|
||||||
|
for value in {
|
||||||
|
row.get("module_id"),
|
||||||
|
row.get("asset_version"),
|
||||||
|
row.get("implementation_status"),
|
||||||
|
row.get("scope_predicate"),
|
||||||
|
}
|
||||||
|
& S2_40_TRANSITIONED_STUB_VALUES
|
||||||
|
)
|
||||||
|
findings: list[dict[str, str]] = []
|
||||||
|
if observed_stub:
|
||||||
|
findings.append(
|
||||||
|
_finding(
|
||||||
|
"S2_40_TRANSITIONED_STUB_METADATA_FORBIDDEN",
|
||||||
|
f"module:{S2_40_WORKFLOW_PATH}",
|
||||||
|
repr(observed_stub),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
drift = {
|
||||||
|
key: {"expected": expected, "observed": row.get(key)}
|
||||||
|
for key, expected in S2_40_WORKFLOW_REQUIRED_METADATA.items()
|
||||||
|
if row.get(key) != expected
|
||||||
|
}
|
||||||
|
if drift:
|
||||||
|
findings.append(
|
||||||
|
_finding(
|
||||||
|
"S2_40_WORKFLOW_MODULE_METADATA_MISMATCH",
|
||||||
|
f"module:{S2_40_WORKFLOW_PATH}",
|
||||||
|
json.dumps(drift, ensure_ascii=False, sort_keys=True),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return findings
|
||||||
|
|
||||||
|
|
||||||
def _validate_module_manifest(
|
def _validate_module_manifest(
|
||||||
root: Path,
|
root: Path,
|
||||||
manifest: dict[str, Any],
|
manifest: dict[str, Any],
|
||||||
@@ -187,6 +417,7 @@ def _validate_module_manifest(
|
|||||||
rows = manifest.get("modules")
|
rows = manifest.get("modules")
|
||||||
if not isinstance(rows, list):
|
if not isinstance(rows, list):
|
||||||
return errors + [_finding("MODULES_REQUIRED", "$/modules", "canonical modules array missing")], pending, {"module_count": 0, "mirror_count": 0}
|
return errors + [_finding("MODULES_REQUIRED", "$/modules", "canonical modules array missing")], pending, {"module_count": 0, "mirror_count": 0}
|
||||||
|
errors.extend(_validate_s2_40_workflow_module_row(rows))
|
||||||
seen_ids: set[str] = set()
|
seen_ids: set[str] = set()
|
||||||
seen_paths: set[str] = set()
|
seen_paths: set[str] = set()
|
||||||
row_by_path: dict[str, dict[str, Any]] = {}
|
row_by_path: dict[str, dict[str, Any]] = {}
|
||||||
@@ -518,6 +749,234 @@ def _validate_137_coverage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _walk_strings(value: Any) -> list[str]:
|
||||||
|
if isinstance(value, str):
|
||||||
|
return [value]
|
||||||
|
if isinstance(value, list):
|
||||||
|
return [item for child in value for item in _walk_strings(child)]
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return [item for child in value.values() for item in _walk_strings(child)]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_s2_40_detached(
|
||||||
|
root: Path,
|
||||||
|
parent_release_raw: bytes,
|
||||||
|
) -> tuple[list[dict[str, str]], list[dict[str, str]], dict[str, int]]:
|
||||||
|
"""Independently validate the detached S2_40 Agent/code/admission lane.
|
||||||
|
|
||||||
|
This check is activated only after the non-empty S2_40 parent allowlist is
|
||||||
|
installed. It never upgrades pending backend or legal evidence to an
|
||||||
|
admitted state.
|
||||||
|
"""
|
||||||
|
|
||||||
|
errors: list[dict[str, str]] = []
|
||||||
|
pending: list[dict[str, str]] = []
|
||||||
|
counts = {"s2_40_stage_binding_count": 0, "s2_40_run_code_task_count": 0}
|
||||||
|
required = {
|
||||||
|
"authoring": root.parent.parent / "Stage_2_S2_40.yml",
|
||||||
|
"projection": root / "agent_scripts/Stage_2_S2_40.yml",
|
||||||
|
"mirror_py": root / "runtime/s2_40_commit.py",
|
||||||
|
"mirror_txt": root / "runtime/s2_40_commit.txt",
|
||||||
|
"receipt": root / "manifest/s2_40_inline_code_receipt.json",
|
||||||
|
"binding": root / "deployment/stage2_code_executor_binding.yml",
|
||||||
|
"admission": root / "manifest/stage2_deterministic_admission_receipt.json",
|
||||||
|
"package_schema": root / "schemas/package.schema.json",
|
||||||
|
"review_schema": root / "schemas/review_status.schema.json",
|
||||||
|
"deployment_schema": root / "schemas/deployment.schema.json",
|
||||||
|
}
|
||||||
|
missing = [name for name, path in required.items() if not path.is_file() or path.is_symlink()]
|
||||||
|
if missing:
|
||||||
|
errors.append(
|
||||||
|
_finding(
|
||||||
|
"S2_40_DETACHED_ASSET_MISSING",
|
||||||
|
"s2_40_detached:$",
|
||||||
|
repr(sorted(missing)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return errors, pending, counts
|
||||||
|
|
||||||
|
authoring_raw = required["authoring"].read_bytes()
|
||||||
|
projection_raw = required["projection"].read_bytes()
|
||||||
|
mirror_py_raw = required["mirror_py"].read_bytes()
|
||||||
|
mirror_txt_raw = required["mirror_txt"].read_bytes()
|
||||||
|
if authoring_raw != projection_raw:
|
||||||
|
errors.append(
|
||||||
|
_finding(
|
||||||
|
"S2_40_AUTHORING_PROJECTION_BYTES_DIFFER",
|
||||||
|
"s2_40_detached:/projection",
|
||||||
|
f"authoring={_sha256(authoring_raw)};projection={_sha256(projection_raw)}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if mirror_py_raw != mirror_txt_raw:
|
||||||
|
errors.append(
|
||||||
|
_finding(
|
||||||
|
"S2_40_CODE_MIRROR_BYTES_DIFFER",
|
||||||
|
"s2_40_detached:/mirror",
|
||||||
|
f"py={_sha256(mirror_py_raw)};txt={_sha256(mirror_txt_raw)}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
agent_doc = _load_yaml(required["authoring"])
|
||||||
|
except ValidationInputError as exc:
|
||||||
|
errors.append(_finding("S2_40_AUTHORING_YAML_INVALID", "s2_40_detached:/authoring", str(exc)))
|
||||||
|
agent_doc = None
|
||||||
|
code_raw = b""
|
||||||
|
if not isinstance(agent_doc, dict):
|
||||||
|
errors.append(_finding("S2_40_AGENT_ROOT_INVALID", "s2_40_detached:/authoring", "object required"))
|
||||||
|
else:
|
||||||
|
agent = agent_doc.get("Agent")
|
||||||
|
if not isinstance(agent, dict):
|
||||||
|
errors.append(_finding("S2_40_AGENT_OBJECT_REQUIRED", "s2_40_detached:/Agent", repr(agent)))
|
||||||
|
else:
|
||||||
|
if agent.get("name") != "Stage_2_S2_40" or agent.get("version") != "1.0.0":
|
||||||
|
errors.append(_finding("S2_40_AGENT_IDENTITY_MISMATCH", "s2_40_detached:/Agent", repr({"name": agent.get("name"), "version": agent.get("version")})))
|
||||||
|
forbidden_model_fields = [
|
||||||
|
string
|
||||||
|
for string in _walk_strings(agent)
|
||||||
|
if string in {"gpt-5.6-sol", "xhigh"}
|
||||||
|
]
|
||||||
|
if forbidden_model_fields or any(
|
||||||
|
key in agent
|
||||||
|
for key in ("llm_provider", "llm_model", "llm_reasoning", "llm_verbosity")
|
||||||
|
):
|
||||||
|
errors.append(_finding("S2_40_LLM_FIELD_FORBIDDEN", "s2_40_detached:/Agent", repr(forbidden_model_fields)))
|
||||||
|
stages = agent.get("Stages")
|
||||||
|
if not isinstance(stages, list) or len(stages) != 1 or not isinstance(stages[0], dict):
|
||||||
|
errors.append(_finding("S2_40_EXACT_ONE_STAGE_REQUIRED", "s2_40_detached:/Agent/Stages", repr(stages)))
|
||||||
|
else:
|
||||||
|
stage = stages[0]
|
||||||
|
tasks = stage.get("tasks")
|
||||||
|
run_code = [
|
||||||
|
row
|
||||||
|
for row in tasks
|
||||||
|
if isinstance(row, dict)
|
||||||
|
and row.get("mcp") == "code-executor"
|
||||||
|
and row.get("tool_name") == "run_code"
|
||||||
|
] if isinstance(tasks, list) else []
|
||||||
|
counts["s2_40_run_code_task_count"] = len(run_code)
|
||||||
|
if stage.get("name") != "S2_40" or not isinstance(tasks, list) or len(tasks) != 1 or len(run_code) != 1:
|
||||||
|
errors.append(_finding("S2_40_EXACT_ONE_RUN_CODE_TASK_REQUIRED", "s2_40_detached:/Agent/Stages/0", f"stage={stage.get('name')!r};tasks={len(tasks) if isinstance(tasks, list) else 'invalid'};run_code={len(run_code)}"))
|
||||||
|
else:
|
||||||
|
task = run_code[0]
|
||||||
|
parameters = task.get("parameters")
|
||||||
|
expected = {
|
||||||
|
"language": "python",
|
||||||
|
"requirements": "httpx==0.28.1",
|
||||||
|
"network": "agent-network",
|
||||||
|
"timeout": 300,
|
||||||
|
}
|
||||||
|
if task.get("task_name") != "Task_S2_40_deterministic_finalizer" or not isinstance(parameters, dict) or any(parameters.get(key) != value for key, value in expected.items()):
|
||||||
|
errors.append(_finding("S2_40_RUN_CODE_SEMANTICS_MISMATCH", "s2_40_detached:/Agent/Stages/0/tasks/0", repr(task)))
|
||||||
|
elif isinstance(parameters.get("code"), str):
|
||||||
|
code_raw = parameters["code"].encode("utf-8")
|
||||||
|
procedure = stage.get("task_procedure")
|
||||||
|
expected_procedure = {
|
||||||
|
"IN": {"nexts": ["Task_S2_40_deterministic_finalizer"], "wait_until": []},
|
||||||
|
"Task_S2_40_deterministic_finalizer": {"nexts": ["OUT"], "wait_until": ["IN"]},
|
||||||
|
"OUT": {"nexts": [], "wait_until": ["Task_S2_40_deterministic_finalizer"]},
|
||||||
|
}
|
||||||
|
if procedure != expected_procedure:
|
||||||
|
errors.append(_finding("S2_40_TASK_PROCEDURE_MISMATCH", "s2_40_detached:/Agent/Stages/0/task_procedure", repr(procedure)))
|
||||||
|
legacy_refs = sorted(
|
||||||
|
value
|
||||||
|
for value in _walk_strings(agent_doc)
|
||||||
|
if re.search(r"(?:^|[/\\])v\.[0-3](?:[/\\]|$)", value)
|
||||||
|
)
|
||||||
|
if legacy_refs:
|
||||||
|
errors.append(_finding("S2_40_LEGACY_STAGE2_DEPENDENCY", "s2_40_detached:/authoring", repr(legacy_refs)))
|
||||||
|
|
||||||
|
if code_raw and code_raw != mirror_py_raw:
|
||||||
|
errors.append(_finding("S2_40_INLINE_CODE_MIRROR_MISMATCH", "s2_40_detached:/canonical_code", f"code={_sha256(code_raw)};mirror={_sha256(mirror_py_raw)}"))
|
||||||
|
|
||||||
|
receipt = _load_json(required["receipt"])
|
||||||
|
if not isinstance(receipt, dict) or receipt.get("schema_version") != "stage2_s2_40_inline_code_receipt.v1" or receipt.get("workflow_id") != "S2_40":
|
||||||
|
errors.append(_finding("S2_40_INLINE_RECEIPT_INVALID", "s2_40_detached:/receipt", repr(receipt)))
|
||||||
|
else:
|
||||||
|
bindings = (
|
||||||
|
("authoring", authoring_raw),
|
||||||
|
("deployment_projection", projection_raw),
|
||||||
|
)
|
||||||
|
for key, raw in bindings:
|
||||||
|
row = receipt.get(key)
|
||||||
|
if not isinstance(row, dict) or row.get("sha256") != _sha256(raw) or row.get("size_bytes") != len(raw):
|
||||||
|
errors.append(_finding("S2_40_INLINE_RECEIPT_HASH_MISMATCH", f"s2_40_detached:/receipt/{key}", repr(row)))
|
||||||
|
canonical_code = receipt.get("canonical_code")
|
||||||
|
if not isinstance(canonical_code, dict) or canonical_code.get("sha256", canonical_code.get("code_sha256")) != _sha256(code_raw):
|
||||||
|
errors.append(_finding("S2_40_INLINE_RECEIPT_CODE_HASH_MISMATCH", "s2_40_detached:/receipt/canonical_code", repr(canonical_code)))
|
||||||
|
expected_parent = _sha256(parent_release_raw)
|
||||||
|
if receipt.get("expected_parent_stage2_release_sha256") != expected_parent:
|
||||||
|
errors.append(_finding("S2_40_PARENT_RELEASE_BINDING_MISMATCH", "s2_40_detached:/receipt/expected_parent_stage2_release_sha256", f"expected={expected_parent};observed={receipt.get('expected_parent_stage2_release_sha256')}"))
|
||||||
|
|
||||||
|
binding = _load_yaml(required["binding"])
|
||||||
|
rows = binding.get("stage_bindings") if isinstance(binding, dict) else None
|
||||||
|
matches = [row for row in rows if isinstance(row, dict) and row.get("stage_id") == "S2_40"] if isinstance(rows, list) else []
|
||||||
|
counts["s2_40_stage_binding_count"] = len(matches)
|
||||||
|
if len(matches) != 1:
|
||||||
|
errors.append(_finding("S2_40_STAGE_BINDING_EXACT_ONE_REQUIRED", "s2_40_detached:/binding/stage_bindings", str(len(matches))))
|
||||||
|
else:
|
||||||
|
row = matches[0]
|
||||||
|
expected = {
|
||||||
|
"workflow_id": "S2_40",
|
||||||
|
"execution_class": "NON-LLM-DETERMINISTIC",
|
||||||
|
"active_runtime_authority": False,
|
||||||
|
"mcp_server_id": "code-executor",
|
||||||
|
"tool_name": "run_code",
|
||||||
|
"language": "python",
|
||||||
|
"network": "agent-network",
|
||||||
|
"timeout_seconds": 300,
|
||||||
|
"external_mcp_contract": None,
|
||||||
|
}
|
||||||
|
drift = {key: {"expected": value, "observed": row.get(key)} for key, value in expected.items() if row.get(key) != value}
|
||||||
|
localdocs = row.get("localdocs_contract")
|
||||||
|
if not isinstance(localdocs, dict) or localdocs.get("endpoint") != "http://mcp-localdocs:8012/mcp" or localdocs.get("fixed_request_path") != "stage2_control/s2_40_request.json" or localdocs.get("tool_allowlist") != ["read_binary_doc", "write_binary_file"]:
|
||||||
|
drift["localdocs_contract"] = {"expected": "S2_40 localdocs binary-only contract", "observed": localdocs}
|
||||||
|
ref_expectations = {
|
||||||
|
"agent_script_ref": ("agent_scripts/Stage_2_S2_40.yml", projection_raw),
|
||||||
|
"inline_code_receipt_ref": ("manifest/s2_40_inline_code_receipt.json", required["receipt"].read_bytes()),
|
||||||
|
"stage2_release_ref": ("manifest/stage2_release.json", parent_release_raw),
|
||||||
|
}
|
||||||
|
for key, (path, raw) in ref_expectations.items():
|
||||||
|
ref = row.get(key)
|
||||||
|
if not isinstance(ref, dict) or ref.get("path") != path or ref.get("sha256") != _sha256(raw):
|
||||||
|
drift[key] = {"expected": {"path": path, "sha256": _sha256(raw)}, "observed": ref}
|
||||||
|
if drift:
|
||||||
|
errors.append(_finding("S2_40_STAGE_BINDING_MISMATCH", "s2_40_detached:/binding/stage_bindings/S2_40", json.dumps(drift, ensure_ascii=False, sort_keys=True)))
|
||||||
|
if row.get("live_admission_status") not in {"PENDING_SECRET_BINDING", "PENDING_LIVE_VERIFICATION", "PENDING"}:
|
||||||
|
errors.append(_finding("S2_40_LIVE_ADMISSION_STATUS_DISHONEST", "s2_40_detached:/binding/stage_bindings/S2_40/live_admission_status", repr(row.get("live_admission_status"))))
|
||||||
|
else:
|
||||||
|
pending.append(_finding("S2_40_LIVE_CODE_EXECUTOR_ADMISSION_PENDING", "s2_40_detached:/binding/stage_bindings/S2_40", str(row.get("live_admission_status"))))
|
||||||
|
|
||||||
|
admission = _load_json(required["admission"])
|
||||||
|
expected_ids = ["S2_00", "S2_20", "S2_40"]
|
||||||
|
if not isinstance(admission, dict) or admission.get("present_deterministic_stage_ids") != expected_ids:
|
||||||
|
errors.append(_finding("S2_40_DETERMINISTIC_ADMISSION_SET_MISMATCH", "s2_40_detached:/admission/present_deterministic_stage_ids", repr(admission.get("present_deterministic_stage_ids") if isinstance(admission, dict) else admission)))
|
||||||
|
elif admission.get("executor_binding_sha256") != _sha256(required["binding"].read_bytes()) or admission.get("parent_release_sha256") != _sha256(parent_release_raw):
|
||||||
|
errors.append(_finding("S2_40_DETERMINISTIC_ADMISSION_HASH_MISMATCH", "s2_40_detached:/admission", repr(admission)))
|
||||||
|
elif admission.get("admission_status") != "PENDING":
|
||||||
|
errors.append(_finding("S2_40_UNSUPPORTED_ADMISSION_CLAIM", "s2_40_detached:/admission/admission_status", repr(admission.get("admission_status"))))
|
||||||
|
else:
|
||||||
|
pending.append(_finding("S2_40_DETERMINISTIC_ADMISSION_PENDING", "s2_40_detached:/admission", "external signed backend evidence pending"))
|
||||||
|
|
||||||
|
deployment_schema = _load_json(required["deployment_schema"])
|
||||||
|
deployment_defs = deployment_schema.get("$defs") if isinstance(deployment_schema, dict) else None
|
||||||
|
required_defs = {
|
||||||
|
"s2_40_request",
|
||||||
|
"s2_40_execution_receipt",
|
||||||
|
"s2_40_inline_code_receipt",
|
||||||
|
"s2_40_commit_intent",
|
||||||
|
"s2_40_commit_result",
|
||||||
|
}
|
||||||
|
if not isinstance(deployment_defs, dict) or not required_defs.issubset(deployment_defs):
|
||||||
|
errors.append(_finding("S2_40_DEPLOYMENT_SCHEMA_DEFS_MISSING", "s2_40_detached:/schemas/deployment/$defs", repr(sorted(required_defs - set(deployment_defs or {})))))
|
||||||
|
for name in ("package_schema", "review_schema"):
|
||||||
|
schema = _load_json(required[name])
|
||||||
|
if not isinstance(schema, dict) or not isinstance(schema.get("$defs"), dict) or not schema.get("$id"):
|
||||||
|
errors.append(_finding("S2_40_SCHEMA_ROOT_INVALID", f"s2_40_detached:/schemas/{name}", repr(schema)))
|
||||||
|
return errors, pending, counts
|
||||||
|
|
||||||
|
|
||||||
def validate_package(
|
def validate_package(
|
||||||
root: Path,
|
root: Path,
|
||||||
manifest_path: Path,
|
manifest_path: Path,
|
||||||
@@ -526,6 +985,15 @@ def validate_package(
|
|||||||
rule_registry_path: Path,
|
rule_registry_path: Path,
|
||||||
coverage_path: Path,
|
coverage_path: Path,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
root = _resolve_root(root)
|
||||||
|
try:
|
||||||
|
manifest_path = manifest_path.resolve(strict=True)
|
||||||
|
release_path = release_path.resolve(strict=True)
|
||||||
|
case_registry_path = case_registry_path.resolve(strict=True)
|
||||||
|
rule_registry_path = rule_registry_path.resolve(strict=True)
|
||||||
|
coverage_path = coverage_path.resolve(strict=True)
|
||||||
|
except (FileNotFoundError, OSError) as exc:
|
||||||
|
raise ValidationInputError(f"VALIDATION_INPUT_UNAVAILABLE:{exc}") from exc
|
||||||
manifest = _load_json(manifest_path)
|
manifest = _load_json(manifest_path)
|
||||||
release = _load_json(release_path)
|
release = _load_json(release_path)
|
||||||
case_registry = _load_json(case_registry_path)
|
case_registry = _load_json(case_registry_path)
|
||||||
@@ -539,6 +1007,17 @@ def validate_package(
|
|||||||
errors.extend(parent_errors)
|
errors.extend(parent_errors)
|
||||||
errors.extend(coverage_errors)
|
errors.extend(coverage_errors)
|
||||||
errors.extend(_validate_parent_member_lists(root, manifest))
|
errors.extend(_validate_parent_member_lists(root, manifest))
|
||||||
|
s2_40_allowlist_path = root / "manifest/s2_40_parent_member_paths.json"
|
||||||
|
if s2_40_allowlist_path.is_file():
|
||||||
|
s2_40_allowlist = _load_json(s2_40_allowlist_path)
|
||||||
|
if isinstance(s2_40_allowlist, list) and s2_40_allowlist:
|
||||||
|
detached_errors, detached_pending, detached_counts = _validate_s2_40_detached(
|
||||||
|
root,
|
||||||
|
release_path.read_bytes(),
|
||||||
|
)
|
||||||
|
errors.extend(detached_errors)
|
||||||
|
pending.extend(detached_pending)
|
||||||
|
counts.update(detached_counts)
|
||||||
pending.extend(parent_pending)
|
pending.extend(parent_pending)
|
||||||
pending.extend(coverage_pending)
|
pending.extend(coverage_pending)
|
||||||
if (
|
if (
|
||||||
@@ -569,28 +1048,29 @@ def validate(root: Path, manifest: dict[str, object]) -> list[str]:
|
|||||||
|
|
||||||
|
|
||||||
def _validate_parent_member_lists(root: Path, manifest: dict[str, Any]) -> list[dict[str, str]]:
|
def _validate_parent_member_lists(root: Path, manifest: dict[str, Any]) -> list[dict[str, str]]:
|
||||||
"""Verify the cumulative S2_20 + S2_30 allowlist without widening it."""
|
"""Verify disjoint cumulative S2_20/S2_30/S2_40 owner allowlists."""
|
||||||
|
|
||||||
paths = (
|
paths = (
|
||||||
root / "manifest" / "s2_20_parent_member_paths.json",
|
("S2_20", root / "manifest" / "s2_20_parent_member_paths.json"),
|
||||||
root / "manifest" / "s2_30_parent_member_paths.json",
|
("S2_30", root / "manifest" / "s2_30_parent_member_paths.json"),
|
||||||
|
("S2_40", root / "manifest" / "s2_40_parent_member_paths.json"),
|
||||||
)
|
)
|
||||||
loaded: list[list[str]] = []
|
loaded: list[tuple[str, list[str]]] = []
|
||||||
for path in paths:
|
for owner_stage, path in paths:
|
||||||
if not path.is_file():
|
if not path.is_file():
|
||||||
return [_finding("PARENT_MEMBER_LIST_MISSING", path.as_posix(), "required cumulative allowlist component")]
|
return [_finding("PARENT_MEMBER_LIST_MISSING", path.as_posix(), "required cumulative allowlist component")]
|
||||||
value = _load_json(path)
|
value = _load_json(path)
|
||||||
if not isinstance(value, list) or not all(isinstance(row, str) for row in value):
|
if not isinstance(value, list) or not all(isinstance(row, str) for row in value):
|
||||||
return [_finding("PARENT_MEMBER_LIST_INVALID", path.as_posix(), "string array required")]
|
return [_finding("PARENT_MEMBER_LIST_INVALID", path.as_posix(), "string array required")]
|
||||||
loaded.append(value)
|
if value != sorted(value) or len(value) != len(set(value)):
|
||||||
overlap = sorted(set(loaded[0]) & set(loaded[1]))
|
return [_finding("PARENT_MEMBER_LIST_NOT_SORTED_UNIQUE", path.as_posix(), "sorted unique string array required")]
|
||||||
|
loaded.append((owner_stage, value))
|
||||||
findings: list[dict[str, str]] = []
|
findings: list[dict[str, str]] = []
|
||||||
if overlap:
|
|
||||||
findings.append(_finding("PARENT_MEMBER_LISTS_NOT_DISJOINT", "manifest", repr(overlap)))
|
|
||||||
rows = manifest.get("modules")
|
rows = manifest.get("modules")
|
||||||
module_paths = {
|
row_by_path = {
|
||||||
row.get("path") for row in rows if isinstance(row, dict)
|
row.get("path"): row for row in rows if isinstance(row, dict) and isinstance(row.get("path"), str)
|
||||||
} if isinstance(rows, list) else set()
|
} if isinstance(rows, list) else {}
|
||||||
|
module_paths = set(row_by_path)
|
||||||
mirrors = manifest.get("documentation_mirrors")
|
mirrors = manifest.get("documentation_mirrors")
|
||||||
if isinstance(mirrors, list):
|
if isinstance(mirrors, list):
|
||||||
module_paths.update(
|
module_paths.update(
|
||||||
@@ -598,14 +1078,42 @@ def _validate_parent_member_lists(root: Path, manifest: dict[str, Any]) -> list[
|
|||||||
for row in mirrors
|
for row in mirrors
|
||||||
if isinstance(row, dict) and isinstance(row.get("mirror_path"), str)
|
if isinstance(row, dict) and isinstance(row.get("mirror_path"), str)
|
||||||
)
|
)
|
||||||
for relative in sorted(set(loaded[0]) | set(loaded[1])):
|
seen: dict[str, str] = {}
|
||||||
normalized = _relative_path(relative)
|
for owner_stage, values in loaded:
|
||||||
if normalized is None:
|
metadata = STAGE_GENERIC_METADATA[owner_stage]
|
||||||
findings.append(_finding("PARENT_MEMBER_PATH_INVALID", "manifest", repr(relative)))
|
for relative in values:
|
||||||
elif normalized in FORBIDDEN_PARENT_MEMBERS:
|
if relative in seen:
|
||||||
findings.append(_finding("NON_CYCLIC_PARENT_VIOLATION", "manifest", normalized))
|
findings.append(_finding("PARENT_MEMBER_LISTS_NOT_DISJOINT", "manifest", f"path={relative};owners={seen[relative]},{owner_stage}"))
|
||||||
elif normalized not in module_paths:
|
else:
|
||||||
findings.append(_finding("PARENT_MEMBER_NOT_IN_MODULE_MANIFEST", "manifest", normalized))
|
seen[relative] = owner_stage
|
||||||
|
normalized = _relative_path(relative)
|
||||||
|
if normalized is None:
|
||||||
|
findings.append(_finding("PARENT_MEMBER_PATH_INVALID", "manifest", repr(relative)))
|
||||||
|
elif normalized in FORBIDDEN_PARENT_MEMBERS:
|
||||||
|
findings.append(_finding("NON_CYCLIC_PARENT_VIOLATION", "manifest", normalized))
|
||||||
|
elif normalized not in module_paths:
|
||||||
|
findings.append(_finding("PARENT_MEMBER_NOT_IN_MODULE_MANIFEST", "manifest", normalized))
|
||||||
|
elif not normalized.endswith(".txt"):
|
||||||
|
row = row_by_path.get(normalized)
|
||||||
|
if isinstance(row, dict) and row.get("schema_version") in {
|
||||||
|
item["schema_version"] for item in STAGE_GENERIC_METADATA.values()
|
||||||
|
}:
|
||||||
|
expected = {
|
||||||
|
"asset_version": metadata["asset_version"],
|
||||||
|
"owner": metadata["owner"],
|
||||||
|
"schema_version": metadata["schema_version"],
|
||||||
|
"scope_predicate": metadata["scope_predicate"],
|
||||||
|
}
|
||||||
|
drift = {
|
||||||
|
key: {"expected": value, "observed": row.get(key)}
|
||||||
|
for key, value in expected.items()
|
||||||
|
if row.get(key) != value
|
||||||
|
}
|
||||||
|
module_id = row.get("module_id")
|
||||||
|
if not isinstance(module_id, str) or not module_id.startswith(metadata["module_id_prefix"]):
|
||||||
|
drift["module_id"] = {"expected_prefix": metadata["module_id_prefix"], "observed": module_id}
|
||||||
|
if drift:
|
||||||
|
findings.append(_finding("PARENT_MEMBER_OWNER_METADATA_MISMATCH", f"manifest:{normalized}", json.dumps(drift, ensure_ascii=False, sort_keys=True)))
|
||||||
return findings
|
return findings
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+528
-20
@@ -13,8 +13,14 @@ import copy
|
|||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
from pathlib import Path, PurePosixPath
|
from pathlib import Path, PurePosixPath
|
||||||
|
import re
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except ImportError: # pragma: no cover - incomplete offline validation host
|
||||||
|
yaml = None # type: ignore[assignment]
|
||||||
|
|
||||||
|
|
||||||
MODULE_MANIFEST_SCHEMA = "stage2_module_manifest.v1"
|
MODULE_MANIFEST_SCHEMA = "stage2_module_manifest.v1"
|
||||||
PARENT_RELEASE_SCHEMA = "stage2_release.v2"
|
PARENT_RELEASE_SCHEMA = "stage2_release.v2"
|
||||||
@@ -73,9 +79,118 @@ FORBIDDEN_PARENT_MEMBERS = frozenset(
|
|||||||
"manifest/s2_30_model_benchmark_receipt.json",
|
"manifest/s2_30_model_benchmark_receipt.json",
|
||||||
"manifest/s2_30_legal_review_receipt.json",
|
"manifest/s2_30_legal_review_receipt.json",
|
||||||
"manifest/s2_30_release.json",
|
"manifest/s2_30_release.json",
|
||||||
|
"agent_scripts/Stage_2_S2_40.yml",
|
||||||
|
"runtime/s2_40_commit.py",
|
||||||
|
"runtime/s2_40_commit.txt",
|
||||||
|
"manifest/s2_40_inline_code_receipt.json",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
STAGE_GENERIC_METADATA = {
|
||||||
|
"S2_20": {
|
||||||
|
"asset_version": "s2_20.1",
|
||||||
|
"module_id_prefix": "S2_20-ASSET-",
|
||||||
|
"owner": "Stage_2_S2_20_owner",
|
||||||
|
"schema_version": "stage2_s2_20_generic_asset.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_20",
|
||||||
|
},
|
||||||
|
"S2_30": {
|
||||||
|
"asset_version": "s2_30.1",
|
||||||
|
"module_id_prefix": "S2_30-ASSET-",
|
||||||
|
"owner": "Stage_2_S2_30_owner",
|
||||||
|
"schema_version": "stage2_s2_30_generic_asset.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_30",
|
||||||
|
},
|
||||||
|
"S2_40": {
|
||||||
|
"asset_version": "s2_40.1",
|
||||||
|
"module_id_prefix": "S2_40-ASSET-",
|
||||||
|
"owner": "Stage_2_S2_40_owner",
|
||||||
|
"schema_version": "stage2_s2_40_generic_asset.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_40",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
S2_40_WORKFLOW_PATH = "workflows/S2_40_final_review_render_and_commit.yml"
|
||||||
|
S2_40_TRANSITIONED_STUB_VALUES = frozenset(
|
||||||
|
{
|
||||||
|
"WF-S2_40-STATUS-ONLY",
|
||||||
|
"s2_40.status_only.1",
|
||||||
|
"DRAFT_HANDOFF_STUB_HASH_BOUND",
|
||||||
|
"entrypoint_id == S2_40_STATUS_ONLY",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
S2_40_WORKFLOW_REQUIRED_METADATA: dict[str, Any] = {
|
||||||
|
"asset_version": "s2_40.finalizer.1",
|
||||||
|
"consumed_schema_ids": [
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_00/context.schema.v2.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_00/ingress.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_10/s2_10.schema.v2.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_20/binding_retrieval.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_20/calculation.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_20/relief_plan.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_30/draft_atoms.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/s2_40/package.schema.v1.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/shared/deployment.schema.v3.json",
|
||||||
|
"https://schemas.liti-agent.local/stage2/shared/review_status.schema.v1.json",
|
||||||
|
],
|
||||||
|
"entry_routes": ["TO_S2_40", "TO_S2_40_STATUS_ONLY"],
|
||||||
|
"implementation_status": "IMPLEMENTED_OFFLINE_CONTRACT_LIVE_ADMISSION_PENDING",
|
||||||
|
"inputs": [
|
||||||
|
"ingress/ingress_status.json",
|
||||||
|
"ingress/technical_diagnostic.json",
|
||||||
|
"ingress/stage1_input_manifest.json",
|
||||||
|
"ingress/intake_report.json",
|
||||||
|
"review/issue_ledger.base.json",
|
||||||
|
"map_s2_10/s2_10_publish_status.json",
|
||||||
|
"plan/plan_publish_status.json",
|
||||||
|
"plan/canonical_relief_plan.json",
|
||||||
|
"plan/claim_groups.json",
|
||||||
|
"plan/case_type_bindings.json",
|
||||||
|
"map_s2_30/s2_30_publish_status.json",
|
||||||
|
"map_s2_30/artifact_manifest.json",
|
||||||
|
"map_s2_30/{draft_parts,issue_patches,worknote_parts,usage_parts}/<claim_group_id>.json",
|
||||||
|
"review/review_receipts/<request_id>.json",
|
||||||
|
"review/lawyer_judgment_record.json",
|
||||||
|
],
|
||||||
|
"legal_admission_status": "PENDING",
|
||||||
|
"live_admission_status": "PENDING",
|
||||||
|
"module_id": "WF-S2_40",
|
||||||
|
"module_kind": "WORKFLOW",
|
||||||
|
"outputs": [
|
||||||
|
"review/issue_ledger.final.json",
|
||||||
|
"review/assumption_ledger.json",
|
||||||
|
"review/llm_usage.jsonl",
|
||||||
|
"candidates/by-content-digest/<candidate_content_digest>/",
|
||||||
|
"review/review_requests/<request_id>.json",
|
||||||
|
"final/claim_relief.md",
|
||||||
|
"final/claim_cause.md",
|
||||||
|
"final/pleading_draft.md",
|
||||||
|
"final/stage2_package.json",
|
||||||
|
"commit/commit_intent.json",
|
||||||
|
"commit/stage2_commit_result.json",
|
||||||
|
"control/run_status.json",
|
||||||
|
],
|
||||||
|
"owner": "Stage_2_S2_40_owner",
|
||||||
|
"path": S2_40_WORKFLOW_PATH,
|
||||||
|
"produced_schema_ids": [
|
||||||
|
"stage2_s2_40_candidate_manifest.v1",
|
||||||
|
"stage2_s2_40_commit_intent.v1",
|
||||||
|
"stage2_s2_40_commit_result.v1",
|
||||||
|
"stage2_s2_40_package.v1",
|
||||||
|
"stage2_s2_40_run_status.v1",
|
||||||
|
],
|
||||||
|
"schema_version": "stage2_workflow_contract.v1",
|
||||||
|
"scope_predicate": "workflow_id == S2_40 and entry_route in {TO_S2_40,TO_S2_40_STATUS_ONLY}",
|
||||||
|
"semantic_review_status": "PENDING_LEGAL_AND_LIVE_ADMISSION",
|
||||||
|
"status_only_exact_inputs": [
|
||||||
|
"ingress/ingress_status.json",
|
||||||
|
"ingress/technical_diagnostic.json",
|
||||||
|
"ingress/stage1_input_manifest.json",
|
||||||
|
"ingress/intake_report.json",
|
||||||
|
"review/issue_ledger.base.json",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ValidationInputError(ValueError):
|
class ValidationInputError(ValueError):
|
||||||
"""Raised when a validation input cannot be parsed deterministically."""
|
"""Raised when a validation input cannot be parsed deterministically."""
|
||||||
@@ -103,6 +218,38 @@ def _load_json(path: Path) -> Any:
|
|||||||
raise ValidationInputError(f"JSON_READ_FAILED:{path.as_posix()}:{exc}") from exc
|
raise ValidationInputError(f"JSON_READ_FAILED:{path.as_posix()}:{exc}") from exc
|
||||||
|
|
||||||
|
|
||||||
|
if yaml is not None:
|
||||||
|
|
||||||
|
class _UniqueKeySafeLoader(yaml.SafeLoader):
|
||||||
|
def construct_mapping(self, node: Any, deep: bool = False) -> dict[Any, Any]:
|
||||||
|
self.flatten_mapping(node)
|
||||||
|
result: dict[Any, Any] = {}
|
||||||
|
for key_node, value_node in node.value:
|
||||||
|
key = self.construct_object(key_node, deep=deep)
|
||||||
|
if key in result:
|
||||||
|
raise ValidationInputError(f"DUPLICATE_YAML_KEY:{key!r}")
|
||||||
|
result[key] = self.construct_object(value_node, deep=deep)
|
||||||
|
return result
|
||||||
|
|
||||||
|
else: # pragma: no cover
|
||||||
|
|
||||||
|
class _UniqueKeySafeLoader: # type: ignore[no-redef]
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _load_yaml(path: Path) -> Any:
|
||||||
|
if yaml is None:
|
||||||
|
raise ValidationInputError("PYYAML_REQUIRED")
|
||||||
|
try:
|
||||||
|
raw = path.read_bytes()
|
||||||
|
text = raw.decode("utf-8")
|
||||||
|
if text.startswith("\ufeff"):
|
||||||
|
raise ValidationInputError(f"YAML_UTF8_BOM_FORBIDDEN:{path.as_posix()}")
|
||||||
|
return yaml.load(text, Loader=_UniqueKeySafeLoader)
|
||||||
|
except (OSError, UnicodeDecodeError, yaml.YAMLError) as exc:
|
||||||
|
raise ValidationInputError(f"YAML_READ_FAILED:{path.as_posix()}:{exc}") from exc
|
||||||
|
|
||||||
|
|
||||||
def _canonical_bytes(document: Any) -> bytes:
|
def _canonical_bytes(document: Any) -> bytes:
|
||||||
return (
|
return (
|
||||||
json.dumps(
|
json.dumps(
|
||||||
@@ -169,6 +316,89 @@ def _physical(root: Path, relative: str) -> Path | None:
|
|||||||
return resolved
|
return resolved
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_root(root: Path) -> Path:
|
||||||
|
"""Resolve a CLI/library root before deriving authoring-relative paths."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
resolved = root.resolve(strict=True)
|
||||||
|
except (FileNotFoundError, OSError) as exc:
|
||||||
|
raise ValidationInputError(f"ROOT_UNAVAILABLE:{root.as_posix()}") from exc
|
||||||
|
if not resolved.is_dir():
|
||||||
|
raise ValidationInputError(f"ROOT_DIRECTORY_REQUIRED:{resolved.as_posix()}")
|
||||||
|
return resolved
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_s2_40_workflow_module_row(
|
||||||
|
rows: Any,
|
||||||
|
) -> list[dict[str, str]]:
|
||||||
|
if not isinstance(rows, list):
|
||||||
|
return [_finding("S2_40_WORKFLOW_MODULE_ROW_REQUIRED", "$/modules", "modules array required")]
|
||||||
|
matches = [
|
||||||
|
row
|
||||||
|
for row in rows
|
||||||
|
if isinstance(row, dict) and row.get("path") == S2_40_WORKFLOW_PATH
|
||||||
|
]
|
||||||
|
s2_40_family_active = bool(matches) or any(
|
||||||
|
isinstance(row, dict)
|
||||||
|
and (
|
||||||
|
row.get("owner") == "Stage_2_S2_40_owner"
|
||||||
|
or row.get("schema_version") == "stage2_s2_40_generic_asset.v1"
|
||||||
|
or (
|
||||||
|
isinstance(row.get("module_id"), str)
|
||||||
|
and row["module_id"].startswith("S2_40-ASSET-")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for row in rows
|
||||||
|
)
|
||||||
|
if not s2_40_family_active:
|
||||||
|
# Pre-S2_40 synthetic/unit manifests remain valid inputs. Once any
|
||||||
|
# S2_40 family member is present, however, the full workflow row is a
|
||||||
|
# mandatory semantic anchor and a status-only substitute is forbidden.
|
||||||
|
return []
|
||||||
|
if len(matches) != 1:
|
||||||
|
return [
|
||||||
|
_finding(
|
||||||
|
"S2_40_WORKFLOW_MODULE_ROW_EXACT_ONE_REQUIRED",
|
||||||
|
"$/modules",
|
||||||
|
f"observed={len(matches)}",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
row = matches[0]
|
||||||
|
observed_stub = sorted(
|
||||||
|
str(value)
|
||||||
|
for value in {
|
||||||
|
row.get("module_id"),
|
||||||
|
row.get("asset_version"),
|
||||||
|
row.get("implementation_status"),
|
||||||
|
row.get("scope_predicate"),
|
||||||
|
}
|
||||||
|
& S2_40_TRANSITIONED_STUB_VALUES
|
||||||
|
)
|
||||||
|
findings: list[dict[str, str]] = []
|
||||||
|
if observed_stub:
|
||||||
|
findings.append(
|
||||||
|
_finding(
|
||||||
|
"S2_40_TRANSITIONED_STUB_METADATA_FORBIDDEN",
|
||||||
|
f"module:{S2_40_WORKFLOW_PATH}",
|
||||||
|
repr(observed_stub),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
drift = {
|
||||||
|
key: {"expected": expected, "observed": row.get(key)}
|
||||||
|
for key, expected in S2_40_WORKFLOW_REQUIRED_METADATA.items()
|
||||||
|
if row.get(key) != expected
|
||||||
|
}
|
||||||
|
if drift:
|
||||||
|
findings.append(
|
||||||
|
_finding(
|
||||||
|
"S2_40_WORKFLOW_MODULE_METADATA_MISMATCH",
|
||||||
|
f"module:{S2_40_WORKFLOW_PATH}",
|
||||||
|
json.dumps(drift, ensure_ascii=False, sort_keys=True),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return findings
|
||||||
|
|
||||||
|
|
||||||
def _validate_module_manifest(
|
def _validate_module_manifest(
|
||||||
root: Path,
|
root: Path,
|
||||||
manifest: dict[str, Any],
|
manifest: dict[str, Any],
|
||||||
@@ -187,6 +417,7 @@ def _validate_module_manifest(
|
|||||||
rows = manifest.get("modules")
|
rows = manifest.get("modules")
|
||||||
if not isinstance(rows, list):
|
if not isinstance(rows, list):
|
||||||
return errors + [_finding("MODULES_REQUIRED", "$/modules", "canonical modules array missing")], pending, {"module_count": 0, "mirror_count": 0}
|
return errors + [_finding("MODULES_REQUIRED", "$/modules", "canonical modules array missing")], pending, {"module_count": 0, "mirror_count": 0}
|
||||||
|
errors.extend(_validate_s2_40_workflow_module_row(rows))
|
||||||
seen_ids: set[str] = set()
|
seen_ids: set[str] = set()
|
||||||
seen_paths: set[str] = set()
|
seen_paths: set[str] = set()
|
||||||
row_by_path: dict[str, dict[str, Any]] = {}
|
row_by_path: dict[str, dict[str, Any]] = {}
|
||||||
@@ -518,6 +749,234 @@ def _validate_137_coverage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _walk_strings(value: Any) -> list[str]:
|
||||||
|
if isinstance(value, str):
|
||||||
|
return [value]
|
||||||
|
if isinstance(value, list):
|
||||||
|
return [item for child in value for item in _walk_strings(child)]
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return [item for child in value.values() for item in _walk_strings(child)]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_s2_40_detached(
|
||||||
|
root: Path,
|
||||||
|
parent_release_raw: bytes,
|
||||||
|
) -> tuple[list[dict[str, str]], list[dict[str, str]], dict[str, int]]:
|
||||||
|
"""Independently validate the detached S2_40 Agent/code/admission lane.
|
||||||
|
|
||||||
|
This check is activated only after the non-empty S2_40 parent allowlist is
|
||||||
|
installed. It never upgrades pending backend or legal evidence to an
|
||||||
|
admitted state.
|
||||||
|
"""
|
||||||
|
|
||||||
|
errors: list[dict[str, str]] = []
|
||||||
|
pending: list[dict[str, str]] = []
|
||||||
|
counts = {"s2_40_stage_binding_count": 0, "s2_40_run_code_task_count": 0}
|
||||||
|
required = {
|
||||||
|
"authoring": root.parent.parent / "Stage_2_S2_40.yml",
|
||||||
|
"projection": root / "agent_scripts/Stage_2_S2_40.yml",
|
||||||
|
"mirror_py": root / "runtime/s2_40_commit.py",
|
||||||
|
"mirror_txt": root / "runtime/s2_40_commit.txt",
|
||||||
|
"receipt": root / "manifest/s2_40_inline_code_receipt.json",
|
||||||
|
"binding": root / "deployment/stage2_code_executor_binding.yml",
|
||||||
|
"admission": root / "manifest/stage2_deterministic_admission_receipt.json",
|
||||||
|
"package_schema": root / "schemas/package.schema.json",
|
||||||
|
"review_schema": root / "schemas/review_status.schema.json",
|
||||||
|
"deployment_schema": root / "schemas/deployment.schema.json",
|
||||||
|
}
|
||||||
|
missing = [name for name, path in required.items() if not path.is_file() or path.is_symlink()]
|
||||||
|
if missing:
|
||||||
|
errors.append(
|
||||||
|
_finding(
|
||||||
|
"S2_40_DETACHED_ASSET_MISSING",
|
||||||
|
"s2_40_detached:$",
|
||||||
|
repr(sorted(missing)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return errors, pending, counts
|
||||||
|
|
||||||
|
authoring_raw = required["authoring"].read_bytes()
|
||||||
|
projection_raw = required["projection"].read_bytes()
|
||||||
|
mirror_py_raw = required["mirror_py"].read_bytes()
|
||||||
|
mirror_txt_raw = required["mirror_txt"].read_bytes()
|
||||||
|
if authoring_raw != projection_raw:
|
||||||
|
errors.append(
|
||||||
|
_finding(
|
||||||
|
"S2_40_AUTHORING_PROJECTION_BYTES_DIFFER",
|
||||||
|
"s2_40_detached:/projection",
|
||||||
|
f"authoring={_sha256(authoring_raw)};projection={_sha256(projection_raw)}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if mirror_py_raw != mirror_txt_raw:
|
||||||
|
errors.append(
|
||||||
|
_finding(
|
||||||
|
"S2_40_CODE_MIRROR_BYTES_DIFFER",
|
||||||
|
"s2_40_detached:/mirror",
|
||||||
|
f"py={_sha256(mirror_py_raw)};txt={_sha256(mirror_txt_raw)}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
agent_doc = _load_yaml(required["authoring"])
|
||||||
|
except ValidationInputError as exc:
|
||||||
|
errors.append(_finding("S2_40_AUTHORING_YAML_INVALID", "s2_40_detached:/authoring", str(exc)))
|
||||||
|
agent_doc = None
|
||||||
|
code_raw = b""
|
||||||
|
if not isinstance(agent_doc, dict):
|
||||||
|
errors.append(_finding("S2_40_AGENT_ROOT_INVALID", "s2_40_detached:/authoring", "object required"))
|
||||||
|
else:
|
||||||
|
agent = agent_doc.get("Agent")
|
||||||
|
if not isinstance(agent, dict):
|
||||||
|
errors.append(_finding("S2_40_AGENT_OBJECT_REQUIRED", "s2_40_detached:/Agent", repr(agent)))
|
||||||
|
else:
|
||||||
|
if agent.get("name") != "Stage_2_S2_40" or agent.get("version") != "1.0.0":
|
||||||
|
errors.append(_finding("S2_40_AGENT_IDENTITY_MISMATCH", "s2_40_detached:/Agent", repr({"name": agent.get("name"), "version": agent.get("version")})))
|
||||||
|
forbidden_model_fields = [
|
||||||
|
string
|
||||||
|
for string in _walk_strings(agent)
|
||||||
|
if string in {"gpt-5.6-sol", "xhigh"}
|
||||||
|
]
|
||||||
|
if forbidden_model_fields or any(
|
||||||
|
key in agent
|
||||||
|
for key in ("llm_provider", "llm_model", "llm_reasoning", "llm_verbosity")
|
||||||
|
):
|
||||||
|
errors.append(_finding("S2_40_LLM_FIELD_FORBIDDEN", "s2_40_detached:/Agent", repr(forbidden_model_fields)))
|
||||||
|
stages = agent.get("Stages")
|
||||||
|
if not isinstance(stages, list) or len(stages) != 1 or not isinstance(stages[0], dict):
|
||||||
|
errors.append(_finding("S2_40_EXACT_ONE_STAGE_REQUIRED", "s2_40_detached:/Agent/Stages", repr(stages)))
|
||||||
|
else:
|
||||||
|
stage = stages[0]
|
||||||
|
tasks = stage.get("tasks")
|
||||||
|
run_code = [
|
||||||
|
row
|
||||||
|
for row in tasks
|
||||||
|
if isinstance(row, dict)
|
||||||
|
and row.get("mcp") == "code-executor"
|
||||||
|
and row.get("tool_name") == "run_code"
|
||||||
|
] if isinstance(tasks, list) else []
|
||||||
|
counts["s2_40_run_code_task_count"] = len(run_code)
|
||||||
|
if stage.get("name") != "S2_40" or not isinstance(tasks, list) or len(tasks) != 1 or len(run_code) != 1:
|
||||||
|
errors.append(_finding("S2_40_EXACT_ONE_RUN_CODE_TASK_REQUIRED", "s2_40_detached:/Agent/Stages/0", f"stage={stage.get('name')!r};tasks={len(tasks) if isinstance(tasks, list) else 'invalid'};run_code={len(run_code)}"))
|
||||||
|
else:
|
||||||
|
task = run_code[0]
|
||||||
|
parameters = task.get("parameters")
|
||||||
|
expected = {
|
||||||
|
"language": "python",
|
||||||
|
"requirements": "httpx==0.28.1",
|
||||||
|
"network": "agent-network",
|
||||||
|
"timeout": 300,
|
||||||
|
}
|
||||||
|
if task.get("task_name") != "Task_S2_40_deterministic_finalizer" or not isinstance(parameters, dict) or any(parameters.get(key) != value for key, value in expected.items()):
|
||||||
|
errors.append(_finding("S2_40_RUN_CODE_SEMANTICS_MISMATCH", "s2_40_detached:/Agent/Stages/0/tasks/0", repr(task)))
|
||||||
|
elif isinstance(parameters.get("code"), str):
|
||||||
|
code_raw = parameters["code"].encode("utf-8")
|
||||||
|
procedure = stage.get("task_procedure")
|
||||||
|
expected_procedure = {
|
||||||
|
"IN": {"nexts": ["Task_S2_40_deterministic_finalizer"], "wait_until": []},
|
||||||
|
"Task_S2_40_deterministic_finalizer": {"nexts": ["OUT"], "wait_until": ["IN"]},
|
||||||
|
"OUT": {"nexts": [], "wait_until": ["Task_S2_40_deterministic_finalizer"]},
|
||||||
|
}
|
||||||
|
if procedure != expected_procedure:
|
||||||
|
errors.append(_finding("S2_40_TASK_PROCEDURE_MISMATCH", "s2_40_detached:/Agent/Stages/0/task_procedure", repr(procedure)))
|
||||||
|
legacy_refs = sorted(
|
||||||
|
value
|
||||||
|
for value in _walk_strings(agent_doc)
|
||||||
|
if re.search(r"(?:^|[/\\])v\.[0-3](?:[/\\]|$)", value)
|
||||||
|
)
|
||||||
|
if legacy_refs:
|
||||||
|
errors.append(_finding("S2_40_LEGACY_STAGE2_DEPENDENCY", "s2_40_detached:/authoring", repr(legacy_refs)))
|
||||||
|
|
||||||
|
if code_raw and code_raw != mirror_py_raw:
|
||||||
|
errors.append(_finding("S2_40_INLINE_CODE_MIRROR_MISMATCH", "s2_40_detached:/canonical_code", f"code={_sha256(code_raw)};mirror={_sha256(mirror_py_raw)}"))
|
||||||
|
|
||||||
|
receipt = _load_json(required["receipt"])
|
||||||
|
if not isinstance(receipt, dict) or receipt.get("schema_version") != "stage2_s2_40_inline_code_receipt.v1" or receipt.get("workflow_id") != "S2_40":
|
||||||
|
errors.append(_finding("S2_40_INLINE_RECEIPT_INVALID", "s2_40_detached:/receipt", repr(receipt)))
|
||||||
|
else:
|
||||||
|
bindings = (
|
||||||
|
("authoring", authoring_raw),
|
||||||
|
("deployment_projection", projection_raw),
|
||||||
|
)
|
||||||
|
for key, raw in bindings:
|
||||||
|
row = receipt.get(key)
|
||||||
|
if not isinstance(row, dict) or row.get("sha256") != _sha256(raw) or row.get("size_bytes") != len(raw):
|
||||||
|
errors.append(_finding("S2_40_INLINE_RECEIPT_HASH_MISMATCH", f"s2_40_detached:/receipt/{key}", repr(row)))
|
||||||
|
canonical_code = receipt.get("canonical_code")
|
||||||
|
if not isinstance(canonical_code, dict) or canonical_code.get("sha256", canonical_code.get("code_sha256")) != _sha256(code_raw):
|
||||||
|
errors.append(_finding("S2_40_INLINE_RECEIPT_CODE_HASH_MISMATCH", "s2_40_detached:/receipt/canonical_code", repr(canonical_code)))
|
||||||
|
expected_parent = _sha256(parent_release_raw)
|
||||||
|
if receipt.get("expected_parent_stage2_release_sha256") != expected_parent:
|
||||||
|
errors.append(_finding("S2_40_PARENT_RELEASE_BINDING_MISMATCH", "s2_40_detached:/receipt/expected_parent_stage2_release_sha256", f"expected={expected_parent};observed={receipt.get('expected_parent_stage2_release_sha256')}"))
|
||||||
|
|
||||||
|
binding = _load_yaml(required["binding"])
|
||||||
|
rows = binding.get("stage_bindings") if isinstance(binding, dict) else None
|
||||||
|
matches = [row for row in rows if isinstance(row, dict) and row.get("stage_id") == "S2_40"] if isinstance(rows, list) else []
|
||||||
|
counts["s2_40_stage_binding_count"] = len(matches)
|
||||||
|
if len(matches) != 1:
|
||||||
|
errors.append(_finding("S2_40_STAGE_BINDING_EXACT_ONE_REQUIRED", "s2_40_detached:/binding/stage_bindings", str(len(matches))))
|
||||||
|
else:
|
||||||
|
row = matches[0]
|
||||||
|
expected = {
|
||||||
|
"workflow_id": "S2_40",
|
||||||
|
"execution_class": "NON-LLM-DETERMINISTIC",
|
||||||
|
"active_runtime_authority": False,
|
||||||
|
"mcp_server_id": "code-executor",
|
||||||
|
"tool_name": "run_code",
|
||||||
|
"language": "python",
|
||||||
|
"network": "agent-network",
|
||||||
|
"timeout_seconds": 300,
|
||||||
|
"external_mcp_contract": None,
|
||||||
|
}
|
||||||
|
drift = {key: {"expected": value, "observed": row.get(key)} for key, value in expected.items() if row.get(key) != value}
|
||||||
|
localdocs = row.get("localdocs_contract")
|
||||||
|
if not isinstance(localdocs, dict) or localdocs.get("endpoint") != "http://mcp-localdocs:8012/mcp" or localdocs.get("fixed_request_path") != "stage2_control/s2_40_request.json" or localdocs.get("tool_allowlist") != ["read_binary_doc", "write_binary_file"]:
|
||||||
|
drift["localdocs_contract"] = {"expected": "S2_40 localdocs binary-only contract", "observed": localdocs}
|
||||||
|
ref_expectations = {
|
||||||
|
"agent_script_ref": ("agent_scripts/Stage_2_S2_40.yml", projection_raw),
|
||||||
|
"inline_code_receipt_ref": ("manifest/s2_40_inline_code_receipt.json", required["receipt"].read_bytes()),
|
||||||
|
"stage2_release_ref": ("manifest/stage2_release.json", parent_release_raw),
|
||||||
|
}
|
||||||
|
for key, (path, raw) in ref_expectations.items():
|
||||||
|
ref = row.get(key)
|
||||||
|
if not isinstance(ref, dict) or ref.get("path") != path or ref.get("sha256") != _sha256(raw):
|
||||||
|
drift[key] = {"expected": {"path": path, "sha256": _sha256(raw)}, "observed": ref}
|
||||||
|
if drift:
|
||||||
|
errors.append(_finding("S2_40_STAGE_BINDING_MISMATCH", "s2_40_detached:/binding/stage_bindings/S2_40", json.dumps(drift, ensure_ascii=False, sort_keys=True)))
|
||||||
|
if row.get("live_admission_status") not in {"PENDING_SECRET_BINDING", "PENDING_LIVE_VERIFICATION", "PENDING"}:
|
||||||
|
errors.append(_finding("S2_40_LIVE_ADMISSION_STATUS_DISHONEST", "s2_40_detached:/binding/stage_bindings/S2_40/live_admission_status", repr(row.get("live_admission_status"))))
|
||||||
|
else:
|
||||||
|
pending.append(_finding("S2_40_LIVE_CODE_EXECUTOR_ADMISSION_PENDING", "s2_40_detached:/binding/stage_bindings/S2_40", str(row.get("live_admission_status"))))
|
||||||
|
|
||||||
|
admission = _load_json(required["admission"])
|
||||||
|
expected_ids = ["S2_00", "S2_20", "S2_40"]
|
||||||
|
if not isinstance(admission, dict) or admission.get("present_deterministic_stage_ids") != expected_ids:
|
||||||
|
errors.append(_finding("S2_40_DETERMINISTIC_ADMISSION_SET_MISMATCH", "s2_40_detached:/admission/present_deterministic_stage_ids", repr(admission.get("present_deterministic_stage_ids") if isinstance(admission, dict) else admission)))
|
||||||
|
elif admission.get("executor_binding_sha256") != _sha256(required["binding"].read_bytes()) or admission.get("parent_release_sha256") != _sha256(parent_release_raw):
|
||||||
|
errors.append(_finding("S2_40_DETERMINISTIC_ADMISSION_HASH_MISMATCH", "s2_40_detached:/admission", repr(admission)))
|
||||||
|
elif admission.get("admission_status") != "PENDING":
|
||||||
|
errors.append(_finding("S2_40_UNSUPPORTED_ADMISSION_CLAIM", "s2_40_detached:/admission/admission_status", repr(admission.get("admission_status"))))
|
||||||
|
else:
|
||||||
|
pending.append(_finding("S2_40_DETERMINISTIC_ADMISSION_PENDING", "s2_40_detached:/admission", "external signed backend evidence pending"))
|
||||||
|
|
||||||
|
deployment_schema = _load_json(required["deployment_schema"])
|
||||||
|
deployment_defs = deployment_schema.get("$defs") if isinstance(deployment_schema, dict) else None
|
||||||
|
required_defs = {
|
||||||
|
"s2_40_request",
|
||||||
|
"s2_40_execution_receipt",
|
||||||
|
"s2_40_inline_code_receipt",
|
||||||
|
"s2_40_commit_intent",
|
||||||
|
"s2_40_commit_result",
|
||||||
|
}
|
||||||
|
if not isinstance(deployment_defs, dict) or not required_defs.issubset(deployment_defs):
|
||||||
|
errors.append(_finding("S2_40_DEPLOYMENT_SCHEMA_DEFS_MISSING", "s2_40_detached:/schemas/deployment/$defs", repr(sorted(required_defs - set(deployment_defs or {})))))
|
||||||
|
for name in ("package_schema", "review_schema"):
|
||||||
|
schema = _load_json(required[name])
|
||||||
|
if not isinstance(schema, dict) or not isinstance(schema.get("$defs"), dict) or not schema.get("$id"):
|
||||||
|
errors.append(_finding("S2_40_SCHEMA_ROOT_INVALID", f"s2_40_detached:/schemas/{name}", repr(schema)))
|
||||||
|
return errors, pending, counts
|
||||||
|
|
||||||
|
|
||||||
def validate_package(
|
def validate_package(
|
||||||
root: Path,
|
root: Path,
|
||||||
manifest_path: Path,
|
manifest_path: Path,
|
||||||
@@ -526,6 +985,15 @@ def validate_package(
|
|||||||
rule_registry_path: Path,
|
rule_registry_path: Path,
|
||||||
coverage_path: Path,
|
coverage_path: Path,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
root = _resolve_root(root)
|
||||||
|
try:
|
||||||
|
manifest_path = manifest_path.resolve(strict=True)
|
||||||
|
release_path = release_path.resolve(strict=True)
|
||||||
|
case_registry_path = case_registry_path.resolve(strict=True)
|
||||||
|
rule_registry_path = rule_registry_path.resolve(strict=True)
|
||||||
|
coverage_path = coverage_path.resolve(strict=True)
|
||||||
|
except (FileNotFoundError, OSError) as exc:
|
||||||
|
raise ValidationInputError(f"VALIDATION_INPUT_UNAVAILABLE:{exc}") from exc
|
||||||
manifest = _load_json(manifest_path)
|
manifest = _load_json(manifest_path)
|
||||||
release = _load_json(release_path)
|
release = _load_json(release_path)
|
||||||
case_registry = _load_json(case_registry_path)
|
case_registry = _load_json(case_registry_path)
|
||||||
@@ -539,6 +1007,17 @@ def validate_package(
|
|||||||
errors.extend(parent_errors)
|
errors.extend(parent_errors)
|
||||||
errors.extend(coverage_errors)
|
errors.extend(coverage_errors)
|
||||||
errors.extend(_validate_parent_member_lists(root, manifest))
|
errors.extend(_validate_parent_member_lists(root, manifest))
|
||||||
|
s2_40_allowlist_path = root / "manifest/s2_40_parent_member_paths.json"
|
||||||
|
if s2_40_allowlist_path.is_file():
|
||||||
|
s2_40_allowlist = _load_json(s2_40_allowlist_path)
|
||||||
|
if isinstance(s2_40_allowlist, list) and s2_40_allowlist:
|
||||||
|
detached_errors, detached_pending, detached_counts = _validate_s2_40_detached(
|
||||||
|
root,
|
||||||
|
release_path.read_bytes(),
|
||||||
|
)
|
||||||
|
errors.extend(detached_errors)
|
||||||
|
pending.extend(detached_pending)
|
||||||
|
counts.update(detached_counts)
|
||||||
pending.extend(parent_pending)
|
pending.extend(parent_pending)
|
||||||
pending.extend(coverage_pending)
|
pending.extend(coverage_pending)
|
||||||
if (
|
if (
|
||||||
@@ -569,28 +1048,29 @@ def validate(root: Path, manifest: dict[str, object]) -> list[str]:
|
|||||||
|
|
||||||
|
|
||||||
def _validate_parent_member_lists(root: Path, manifest: dict[str, Any]) -> list[dict[str, str]]:
|
def _validate_parent_member_lists(root: Path, manifest: dict[str, Any]) -> list[dict[str, str]]:
|
||||||
"""Verify the cumulative S2_20 + S2_30 allowlist without widening it."""
|
"""Verify disjoint cumulative S2_20/S2_30/S2_40 owner allowlists."""
|
||||||
|
|
||||||
paths = (
|
paths = (
|
||||||
root / "manifest" / "s2_20_parent_member_paths.json",
|
("S2_20", root / "manifest" / "s2_20_parent_member_paths.json"),
|
||||||
root / "manifest" / "s2_30_parent_member_paths.json",
|
("S2_30", root / "manifest" / "s2_30_parent_member_paths.json"),
|
||||||
|
("S2_40", root / "manifest" / "s2_40_parent_member_paths.json"),
|
||||||
)
|
)
|
||||||
loaded: list[list[str]] = []
|
loaded: list[tuple[str, list[str]]] = []
|
||||||
for path in paths:
|
for owner_stage, path in paths:
|
||||||
if not path.is_file():
|
if not path.is_file():
|
||||||
return [_finding("PARENT_MEMBER_LIST_MISSING", path.as_posix(), "required cumulative allowlist component")]
|
return [_finding("PARENT_MEMBER_LIST_MISSING", path.as_posix(), "required cumulative allowlist component")]
|
||||||
value = _load_json(path)
|
value = _load_json(path)
|
||||||
if not isinstance(value, list) or not all(isinstance(row, str) for row in value):
|
if not isinstance(value, list) or not all(isinstance(row, str) for row in value):
|
||||||
return [_finding("PARENT_MEMBER_LIST_INVALID", path.as_posix(), "string array required")]
|
return [_finding("PARENT_MEMBER_LIST_INVALID", path.as_posix(), "string array required")]
|
||||||
loaded.append(value)
|
if value != sorted(value) or len(value) != len(set(value)):
|
||||||
overlap = sorted(set(loaded[0]) & set(loaded[1]))
|
return [_finding("PARENT_MEMBER_LIST_NOT_SORTED_UNIQUE", path.as_posix(), "sorted unique string array required")]
|
||||||
|
loaded.append((owner_stage, value))
|
||||||
findings: list[dict[str, str]] = []
|
findings: list[dict[str, str]] = []
|
||||||
if overlap:
|
|
||||||
findings.append(_finding("PARENT_MEMBER_LISTS_NOT_DISJOINT", "manifest", repr(overlap)))
|
|
||||||
rows = manifest.get("modules")
|
rows = manifest.get("modules")
|
||||||
module_paths = {
|
row_by_path = {
|
||||||
row.get("path") for row in rows if isinstance(row, dict)
|
row.get("path"): row for row in rows if isinstance(row, dict) and isinstance(row.get("path"), str)
|
||||||
} if isinstance(rows, list) else set()
|
} if isinstance(rows, list) else {}
|
||||||
|
module_paths = set(row_by_path)
|
||||||
mirrors = manifest.get("documentation_mirrors")
|
mirrors = manifest.get("documentation_mirrors")
|
||||||
if isinstance(mirrors, list):
|
if isinstance(mirrors, list):
|
||||||
module_paths.update(
|
module_paths.update(
|
||||||
@@ -598,14 +1078,42 @@ def _validate_parent_member_lists(root: Path, manifest: dict[str, Any]) -> list[
|
|||||||
for row in mirrors
|
for row in mirrors
|
||||||
if isinstance(row, dict) and isinstance(row.get("mirror_path"), str)
|
if isinstance(row, dict) and isinstance(row.get("mirror_path"), str)
|
||||||
)
|
)
|
||||||
for relative in sorted(set(loaded[0]) | set(loaded[1])):
|
seen: dict[str, str] = {}
|
||||||
normalized = _relative_path(relative)
|
for owner_stage, values in loaded:
|
||||||
if normalized is None:
|
metadata = STAGE_GENERIC_METADATA[owner_stage]
|
||||||
findings.append(_finding("PARENT_MEMBER_PATH_INVALID", "manifest", repr(relative)))
|
for relative in values:
|
||||||
elif normalized in FORBIDDEN_PARENT_MEMBERS:
|
if relative in seen:
|
||||||
findings.append(_finding("NON_CYCLIC_PARENT_VIOLATION", "manifest", normalized))
|
findings.append(_finding("PARENT_MEMBER_LISTS_NOT_DISJOINT", "manifest", f"path={relative};owners={seen[relative]},{owner_stage}"))
|
||||||
elif normalized not in module_paths:
|
else:
|
||||||
findings.append(_finding("PARENT_MEMBER_NOT_IN_MODULE_MANIFEST", "manifest", normalized))
|
seen[relative] = owner_stage
|
||||||
|
normalized = _relative_path(relative)
|
||||||
|
if normalized is None:
|
||||||
|
findings.append(_finding("PARENT_MEMBER_PATH_INVALID", "manifest", repr(relative)))
|
||||||
|
elif normalized in FORBIDDEN_PARENT_MEMBERS:
|
||||||
|
findings.append(_finding("NON_CYCLIC_PARENT_VIOLATION", "manifest", normalized))
|
||||||
|
elif normalized not in module_paths:
|
||||||
|
findings.append(_finding("PARENT_MEMBER_NOT_IN_MODULE_MANIFEST", "manifest", normalized))
|
||||||
|
elif not normalized.endswith(".txt"):
|
||||||
|
row = row_by_path.get(normalized)
|
||||||
|
if isinstance(row, dict) and row.get("schema_version") in {
|
||||||
|
item["schema_version"] for item in STAGE_GENERIC_METADATA.values()
|
||||||
|
}:
|
||||||
|
expected = {
|
||||||
|
"asset_version": metadata["asset_version"],
|
||||||
|
"owner": metadata["owner"],
|
||||||
|
"schema_version": metadata["schema_version"],
|
||||||
|
"scope_predicate": metadata["scope_predicate"],
|
||||||
|
}
|
||||||
|
drift = {
|
||||||
|
key: {"expected": value, "observed": row.get(key)}
|
||||||
|
for key, value in expected.items()
|
||||||
|
if row.get(key) != value
|
||||||
|
}
|
||||||
|
module_id = row.get("module_id")
|
||||||
|
if not isinstance(module_id, str) or not module_id.startswith(metadata["module_id_prefix"]):
|
||||||
|
drift["module_id"] = {"expected_prefix": metadata["module_id_prefix"], "observed": module_id}
|
||||||
|
if drift:
|
||||||
|
findings.append(_finding("PARENT_MEMBER_OWNER_METADATA_MISMATCH", f"manifest:{normalized}", json.dumps(drift, ensure_ascii=False, sort_keys=True)))
|
||||||
return findings
|
return findings
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+17
@@ -13,9 +13,26 @@
|
|||||||
"obligor_ref",
|
"obligor_ref",
|
||||||
"payment_event_ref"
|
"payment_event_ref"
|
||||||
],
|
],
|
||||||
|
"slot_definitions": [
|
||||||
|
{"name": "amount", "type": "MONEY", "cardinality": "EXACTLY_ONE"},
|
||||||
|
{"name": "currency", "type": "STRING", "cardinality": "EXACTLY_ONE"},
|
||||||
|
{"name": "obligor_ref", "type": "PARTY_REF", "cardinality": "EXACTLY_ONE"},
|
||||||
|
{"name": "payment_event_ref", "type": "IDENTIFIER", "cardinality": "EXACTLY_ONE"}
|
||||||
|
],
|
||||||
"legal_branch_may_add_required_slots_only_after_review": true,
|
"legal_branch_may_add_required_slots_only_after_review": true,
|
||||||
"missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING"
|
"missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING"
|
||||||
},
|
},
|
||||||
|
"closed_ast_contract": {
|
||||||
|
"normalization_version": "NFC_LF_UTF8_V1",
|
||||||
|
"allowed_atom_types": ["RELIEF_ATOM", "COST_ATOM", "PROVISIONAL_EXECUTION_ATOM"],
|
||||||
|
"template_representation": "ORDERED_LITERAL_OR_TYPED_SLOT_SEGMENTS",
|
||||||
|
"immutable_literal_source": "APPROVED_BRANCH_ROWS_ONLY",
|
||||||
|
"branch_cardinality": "EXACTLY_ONE",
|
||||||
|
"independent_rerender_required": true,
|
||||||
|
"unknown_slot_forbidden": true,
|
||||||
|
"dangling_token_forbidden": true,
|
||||||
|
"required_exhibit_resolution": "BRANCH_DECLARED_ONLY"
|
||||||
|
},
|
||||||
"branch_rows": [],
|
"branch_rows": [],
|
||||||
"closure": {
|
"closure": {
|
||||||
"exactly_one_approved_branch_required": true,
|
"exactly_one_approved_branch_required": true,
|
||||||
|
|||||||
+16
@@ -12,9 +12,25 @@
|
|||||||
"obligor_ref",
|
"obligor_ref",
|
||||||
"performance_mode"
|
"performance_mode"
|
||||||
],
|
],
|
||||||
|
"slot_definitions": [
|
||||||
|
{"name": "object_ref", "type": "OBJECT_REF", "cardinality": "EXACTLY_ONE"},
|
||||||
|
{"name": "obligor_ref", "type": "PARTY_REF", "cardinality": "EXACTLY_ONE"},
|
||||||
|
{"name": "performance_mode", "type": "IDENTIFIER", "cardinality": "EXACTLY_ONE"}
|
||||||
|
],
|
||||||
"legal_branch_may_add_required_slots_only_after_review": true,
|
"legal_branch_may_add_required_slots_only_after_review": true,
|
||||||
"missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING"
|
"missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING"
|
||||||
},
|
},
|
||||||
|
"closed_ast_contract": {
|
||||||
|
"normalization_version": "NFC_LF_UTF8_V1",
|
||||||
|
"allowed_atom_types": ["RELIEF_ATOM", "COST_ATOM", "PROVISIONAL_EXECUTION_ATOM", "ANNEX_ATOM"],
|
||||||
|
"template_representation": "ORDERED_LITERAL_OR_TYPED_SLOT_SEGMENTS",
|
||||||
|
"immutable_literal_source": "APPROVED_BRANCH_ROWS_ONLY",
|
||||||
|
"branch_cardinality": "EXACTLY_ONE",
|
||||||
|
"independent_rerender_required": true,
|
||||||
|
"unknown_slot_forbidden": true,
|
||||||
|
"dangling_token_forbidden": true,
|
||||||
|
"required_exhibit_resolution": "BRANCH_DECLARED_ONLY"
|
||||||
|
},
|
||||||
"branch_rows": [],
|
"branch_rows": [],
|
||||||
"closure": {
|
"closure": {
|
||||||
"exactly_one_approved_branch_required": true,
|
"exactly_one_approved_branch_required": true,
|
||||||
|
|||||||
+17
@@ -12,9 +12,26 @@
|
|||||||
"registry_action",
|
"registry_action",
|
||||||
"obligor_ref"
|
"obligor_ref"
|
||||||
],
|
],
|
||||||
|
"slot_definitions": [
|
||||||
|
{"name": "registry_object_ref", "type": "OBJECT_REF", "cardinality": "EXACTLY_ONE"},
|
||||||
|
{"name": "registry_action", "type": "IDENTIFIER", "cardinality": "EXACTLY_ONE"},
|
||||||
|
{"name": "obligor_ref", "type": "PARTY_REF", "cardinality": "EXACTLY_ONE"}
|
||||||
|
],
|
||||||
"legal_branch_may_add_required_slots_only_after_review": true,
|
"legal_branch_may_add_required_slots_only_after_review": true,
|
||||||
"missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING"
|
"missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING"
|
||||||
},
|
},
|
||||||
|
"closed_ast_contract": {
|
||||||
|
"normalization_version": "NFC_LF_UTF8_V1",
|
||||||
|
"allowed_atom_types": ["RELIEF_ATOM", "COST_ATOM", "ANNEX_ATOM"],
|
||||||
|
"template_representation": "ORDERED_LITERAL_OR_TYPED_SLOT_SEGMENTS",
|
||||||
|
"immutable_literal_source": "APPROVED_BRANCH_ROWS_ONLY",
|
||||||
|
"branch_cardinality": "EXACTLY_ONE",
|
||||||
|
"independent_rerender_required": true,
|
||||||
|
"unknown_slot_forbidden": true,
|
||||||
|
"dangling_token_forbidden": true,
|
||||||
|
"required_exhibit_resolution": "BRANCH_DECLARED_ONLY",
|
||||||
|
"provisional_execution_forbidden": true
|
||||||
|
},
|
||||||
"branch_rows": [],
|
"branch_rows": [],
|
||||||
"closure": {
|
"closure": {
|
||||||
"exactly_one_approved_branch_required": true,
|
"exactly_one_approved_branch_required": true,
|
||||||
|
|||||||
+17
@@ -12,9 +12,26 @@
|
|||||||
"performance_mode",
|
"performance_mode",
|
||||||
"obligor_ref"
|
"obligor_ref"
|
||||||
],
|
],
|
||||||
|
"slot_definitions": [
|
||||||
|
{"name": "performance_object_ref", "type": "OBJECT_REF", "cardinality": "EXACTLY_ONE"},
|
||||||
|
{"name": "performance_mode", "type": "IDENTIFIER", "cardinality": "EXACTLY_ONE"},
|
||||||
|
{"name": "obligor_ref", "type": "PARTY_REF", "cardinality": "EXACTLY_ONE"}
|
||||||
|
],
|
||||||
"legal_branch_may_add_required_slots_only_after_review": true,
|
"legal_branch_may_add_required_slots_only_after_review": true,
|
||||||
"missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING"
|
"missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING"
|
||||||
},
|
},
|
||||||
|
"closed_ast_contract": {
|
||||||
|
"normalization_version": "NFC_LF_UTF8_V1",
|
||||||
|
"allowed_atom_types": ["RELIEF_ATOM", "COST_ATOM", "PROVISIONAL_EXECUTION_ATOM", "ANNEX_ATOM"],
|
||||||
|
"template_representation": "ORDERED_LITERAL_OR_TYPED_SLOT_SEGMENTS",
|
||||||
|
"immutable_literal_source": "APPROVED_BRANCH_ROWS_ONLY",
|
||||||
|
"branch_cardinality": "EXACTLY_ONE",
|
||||||
|
"independent_rerender_required": true,
|
||||||
|
"unknown_slot_forbidden": true,
|
||||||
|
"dangling_token_forbidden": true,
|
||||||
|
"required_exhibit_resolution": "BRANCH_DECLARED_ONLY",
|
||||||
|
"provisional_execution_requires_branch_authority": true
|
||||||
|
},
|
||||||
"branch_rows": [],
|
"branch_rows": [],
|
||||||
"closure": {
|
"closure": {
|
||||||
"exactly_one_approved_branch_required": true,
|
"exactly_one_approved_branch_required": true,
|
||||||
|
|||||||
+17
@@ -12,9 +12,26 @@
|
|||||||
"declaration_scope",
|
"declaration_scope",
|
||||||
"opposing_party_ref"
|
"opposing_party_ref"
|
||||||
],
|
],
|
||||||
|
"slot_definitions": [
|
||||||
|
{"name": "legal_relation_ref", "type": "IDENTIFIER", "cardinality": "EXACTLY_ONE"},
|
||||||
|
{"name": "declaration_scope", "type": "STRING", "cardinality": "EXACTLY_ONE"},
|
||||||
|
{"name": "opposing_party_ref", "type": "PARTY_REF", "cardinality": "EXACTLY_ONE"}
|
||||||
|
],
|
||||||
"legal_branch_may_add_required_slots_only_after_review": true,
|
"legal_branch_may_add_required_slots_only_after_review": true,
|
||||||
"missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING"
|
"missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING"
|
||||||
},
|
},
|
||||||
|
"closed_ast_contract": {
|
||||||
|
"normalization_version": "NFC_LF_UTF8_V1",
|
||||||
|
"allowed_atom_types": ["RELIEF_ATOM", "COST_ATOM"],
|
||||||
|
"template_representation": "ORDERED_LITERAL_OR_TYPED_SLOT_SEGMENTS",
|
||||||
|
"immutable_literal_source": "APPROVED_BRANCH_ROWS_ONLY",
|
||||||
|
"branch_cardinality": "EXACTLY_ONE",
|
||||||
|
"independent_rerender_required": true,
|
||||||
|
"unknown_slot_forbidden": true,
|
||||||
|
"dangling_token_forbidden": true,
|
||||||
|
"required_exhibit_resolution": "BRANCH_DECLARED_ONLY",
|
||||||
|
"provisional_execution_forbidden": true
|
||||||
|
},
|
||||||
"branch_rows": [],
|
"branch_rows": [],
|
||||||
"closure": {
|
"closure": {
|
||||||
"exactly_one_approved_branch_required": true,
|
"exactly_one_approved_branch_required": true,
|
||||||
|
|||||||
+17
@@ -12,9 +12,26 @@
|
|||||||
"constitutive_method",
|
"constitutive_method",
|
||||||
"opposing_party_ref"
|
"opposing_party_ref"
|
||||||
],
|
],
|
||||||
|
"slot_definitions": [
|
||||||
|
{"name": "legal_effect_ref", "type": "IDENTIFIER", "cardinality": "EXACTLY_ONE"},
|
||||||
|
{"name": "constitutive_method", "type": "IDENTIFIER", "cardinality": "EXACTLY_ONE"},
|
||||||
|
{"name": "opposing_party_ref", "type": "PARTY_REF", "cardinality": "EXACTLY_ONE"}
|
||||||
|
],
|
||||||
"legal_branch_may_add_required_slots_only_after_review": true,
|
"legal_branch_may_add_required_slots_only_after_review": true,
|
||||||
"missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING"
|
"missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING"
|
||||||
},
|
},
|
||||||
|
"closed_ast_contract": {
|
||||||
|
"normalization_version": "NFC_LF_UTF8_V1",
|
||||||
|
"allowed_atom_types": ["RELIEF_ATOM", "COST_ATOM", "ANNEX_ATOM"],
|
||||||
|
"template_representation": "ORDERED_LITERAL_OR_TYPED_SLOT_SEGMENTS",
|
||||||
|
"immutable_literal_source": "APPROVED_BRANCH_ROWS_ONLY",
|
||||||
|
"branch_cardinality": "EXACTLY_ONE",
|
||||||
|
"independent_rerender_required": true,
|
||||||
|
"unknown_slot_forbidden": true,
|
||||||
|
"dangling_token_forbidden": true,
|
||||||
|
"required_exhibit_resolution": "BRANCH_DECLARED_ONLY",
|
||||||
|
"provisional_execution_forbidden": true
|
||||||
|
},
|
||||||
"branch_rows": [],
|
"branch_rows": [],
|
||||||
"closure": {
|
"closure": {
|
||||||
"exactly_one_approved_branch_required": true,
|
"exactly_one_approved_branch_required": true,
|
||||||
|
|||||||
+236
@@ -0,0 +1,236 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""Pure offline C40/C45 oracle for the S2_40 deterministic finalizer."""
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import unicodedata
|
||||||
|
import re
|
||||||
|
from typing import Any, Iterable, Mapping, Sequence
|
||||||
|
|
||||||
|
|
||||||
|
class AssemblyError(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
RELATION_ORDER = {"PRIMARY": 0, "PRELIMINARY": 1, "ALTERNATIVE": 2, "SELECTIVE": 3}
|
||||||
|
CAUSE_SECTION_ORDER = {
|
||||||
|
"PARTY_AND_TITLE": 0, "COMMON_FACT": 1, "GROUP_FACT": 2,
|
||||||
|
"ELEMENT_FACT_EVIDENCE": 3, "DEFENSE_REBUTTAL": 4,
|
||||||
|
"CONCLUSION": 5, "ANNEX_EXHIBIT_REFERENCE": 6,
|
||||||
|
}
|
||||||
|
HEX64 = re.compile(r"^[a-f0-9]{64}$")
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_text(value: str) -> str:
|
||||||
|
if not isinstance(value, str):
|
||||||
|
raise AssemblyError("TEXT_TYPE_INVALID")
|
||||||
|
value = unicodedata.normalize("NFC", value.replace("\r\n", "\n").replace("\r", "\n"))
|
||||||
|
if "\x00" in value:
|
||||||
|
raise AssemblyError("TEXT_NUL_FORBIDDEN")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def canonical_json_bytes(value: Any) -> bytes:
|
||||||
|
return (json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n").encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def sha256_bytes(raw: bytes) -> str:
|
||||||
|
return hashlib.sha256(raw).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def reduce_immutable_rows(parts: Sequence[Mapping[str, Any]], *, id_field: str, row_field: str) -> list[dict[str, Any]]:
|
||||||
|
"""Stable union; only byte-equivalent duplicates are tolerated."""
|
||||||
|
by_id: dict[str, tuple[bytes, dict[str, Any]]] = {}
|
||||||
|
seen_parts: set[str] = set()
|
||||||
|
for part in sorted(parts, key=lambda row: (str(row.get("group_id", "")), str(row.get("part_id", "")))):
|
||||||
|
part_id = part.get("part_id")
|
||||||
|
if not isinstance(part_id, str) or not part_id or part_id in seen_parts:
|
||||||
|
raise AssemblyError("IMMUTABLE_PART_ID_MISSING_OR_DUPLICATE")
|
||||||
|
seen_parts.add(part_id)
|
||||||
|
rows = part.get(row_field)
|
||||||
|
if not isinstance(rows, list):
|
||||||
|
raise AssemblyError("IMMUTABLE_PART_ROWS_INVALID")
|
||||||
|
for row in rows:
|
||||||
|
row_id = row.get(id_field) if isinstance(row, dict) else None
|
||||||
|
if not isinstance(row_id, str) or not row_id:
|
||||||
|
raise AssemblyError("IMMUTABLE_ROW_ID_INVALID")
|
||||||
|
raw = canonical_json_bytes(row)
|
||||||
|
if row_id in by_id and by_id[row_id][0] != raw:
|
||||||
|
raise AssemblyError("IMMUTABLE_ROW_CONFLICT")
|
||||||
|
by_id[row_id] = (raw, dict(row))
|
||||||
|
return [by_id[key][1] for key in sorted(by_id)]
|
||||||
|
|
||||||
|
|
||||||
|
def validate_exact_group_part_set(
|
||||||
|
parts: Sequence[Mapping[str, Any]], *, expected_group_ids: Sequence[str],
|
||||||
|
expected_families: Sequence[str] = ("DRAFT_PART", "ISSUE_PATCH", "WORKNOTE_PART", "USAGE_PART"),
|
||||||
|
) -> None:
|
||||||
|
if len(expected_group_ids) != len(set(expected_group_ids)) or len(expected_families) != len(set(expected_families)):
|
||||||
|
raise AssemblyError("EXPECTED_PART_SET_DUPLICATE")
|
||||||
|
expected = {(group_id, family) for group_id in expected_group_ids for family in expected_families}
|
||||||
|
observed: list[tuple[str, str]] = []
|
||||||
|
part_ids: set[str] = set()
|
||||||
|
for part in parts:
|
||||||
|
part_id, group_id, family = part.get("part_id"), part.get("group_id"), part.get("part_family")
|
||||||
|
if not all(isinstance(value, str) and value for value in (part_id, group_id, family)):
|
||||||
|
raise AssemblyError("PART_SET_ROW_INVALID")
|
||||||
|
if part_id in part_ids:
|
||||||
|
raise AssemblyError("PART_SET_PART_ID_DUPLICATE")
|
||||||
|
part_ids.add(part_id)
|
||||||
|
observed.append((group_id, family))
|
||||||
|
if len(observed) != len(set(observed)) or set(observed) != expected:
|
||||||
|
raise AssemblyError("PART_SET_NOT_EXACT")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_option_partition(option_ids: Iterable[str], partitions: Mapping[str, Sequence[str]]) -> None:
|
||||||
|
required = {"selected", "alternative", "excluded", "deferred"}
|
||||||
|
if set(partitions) != required:
|
||||||
|
raise AssemblyError("OPTION_PARTITION_SHAPE_INVALID")
|
||||||
|
flattened = [item for name in sorted(required) for item in partitions[name]]
|
||||||
|
if len(flattened) != len(set(flattened)):
|
||||||
|
raise AssemblyError("OPTION_PARTITION_OVERLAP")
|
||||||
|
if set(flattened) != set(option_ids):
|
||||||
|
raise AssemblyError("OPTION_PARTITION_NOT_CONSERVATIVE")
|
||||||
|
|
||||||
|
|
||||||
|
def build_usage_projection(
|
||||||
|
usage_parts: Sequence[Mapping[str, Any]], *, run_binding_digest: str,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Reduce immutable S2_30 usage parts into the exact S2_40 projection contract."""
|
||||||
|
if HEX64.fullmatch(run_binding_digest) is None:
|
||||||
|
raise AssemblyError("USAGE_RUN_BINDING_DIGEST_INVALID")
|
||||||
|
source_hashes: list[str] = []
|
||||||
|
by_id: dict[str, tuple[bytes, dict[str, Any]]] = {}
|
||||||
|
observed_row_count = 0
|
||||||
|
for part in sorted(usage_parts, key=lambda value: str(value.get("part_id", ""))):
|
||||||
|
if set(part) != {"part_id", "raw_sha256", "rows"}:
|
||||||
|
raise AssemblyError("USAGE_PART_SHAPE_NOT_EXACT")
|
||||||
|
if not isinstance(part["part_id"], str) or not part["part_id"]:
|
||||||
|
raise AssemblyError("USAGE_PART_ID_INVALID")
|
||||||
|
if not isinstance(part["raw_sha256"], str) or HEX64.fullmatch(part["raw_sha256"]) is None:
|
||||||
|
raise AssemblyError("USAGE_PART_SHA256_INVALID")
|
||||||
|
rows = part["rows"]
|
||||||
|
if not isinstance(rows, list):
|
||||||
|
raise AssemblyError("USAGE_PART_ROWS_INVALID")
|
||||||
|
source_hashes.append(part["raw_sha256"])
|
||||||
|
for row in rows:
|
||||||
|
if not isinstance(row, dict):
|
||||||
|
raise AssemblyError("USAGE_ROW_NOT_OBJECT")
|
||||||
|
usage_id = row.get("usage_id")
|
||||||
|
if not isinstance(usage_id, str) or not usage_id:
|
||||||
|
raise AssemblyError("USAGE_ID_INVALID")
|
||||||
|
observed_row_count += 1
|
||||||
|
raw = canonical_json_bytes(row)
|
||||||
|
if usage_id in by_id and by_id[usage_id][0] != raw:
|
||||||
|
raise AssemblyError("USAGE_ROW_CONFLICT")
|
||||||
|
by_id[usage_id] = (raw, dict(row))
|
||||||
|
if len(source_hashes) != len(set(source_hashes)):
|
||||||
|
raise AssemblyError("USAGE_SOURCE_PART_SHA256_DUPLICATE")
|
||||||
|
rows = [by_id[key][1] for key in sorted(by_id)]
|
||||||
|
if observed_row_count != len(rows):
|
||||||
|
raise AssemblyError("USAGE_CONSERVATION_FAILED")
|
||||||
|
return {
|
||||||
|
"schema_version": "stage2_s2_40_usage_projection.v1",
|
||||||
|
"producer_id": "S2_40",
|
||||||
|
"run_binding_digest": run_binding_digest,
|
||||||
|
"source_usage_part_sha256s": sorted(source_hashes),
|
||||||
|
"rows": rows,
|
||||||
|
"conservation_status": "PASS",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _ordered_relief(rows: Sequence[Mapping[str, Any]]) -> list[Mapping[str, Any]]:
|
||||||
|
claim_ids = [row.get("atomic_claim_id") for row in rows]
|
||||||
|
if any(not isinstance(value, str) or not value for value in claim_ids) or len(claim_ids) != len(set(claim_ids)):
|
||||||
|
raise AssemblyError("RELIEF_ATOMIC_CLAIM_ID_MISSING_OR_DUPLICATE")
|
||||||
|
for row in rows:
|
||||||
|
if row.get("relation") not in RELATION_ORDER:
|
||||||
|
raise AssemblyError("RELIEF_RELATION_UNKNOWN")
|
||||||
|
if not isinstance(row.get("plan_order"), int) or row["plan_order"] < 0:
|
||||||
|
raise AssemblyError("RELIEF_PLAN_ORDER_INVALID")
|
||||||
|
if not isinstance(row.get("rendered_text"), str) or not row["rendered_text"]:
|
||||||
|
raise AssemblyError("RELIEF_RENDERED_TEXT_MISSING")
|
||||||
|
return sorted(rows, key=lambda row: (RELATION_ORDER[row["relation"]], row["plan_order"], row["atomic_claim_id"]))
|
||||||
|
|
||||||
|
|
||||||
|
def _ordered_causes(atoms: Sequence[Mapping[str, Any]]) -> list[Mapping[str, Any]]:
|
||||||
|
atom_ids = [atom.get("atom_id") for atom in atoms]
|
||||||
|
if any(not isinstance(value, str) or not value for value in atom_ids) or len(atom_ids) != len(set(atom_ids)):
|
||||||
|
raise AssemblyError("CAUSE_ATOM_ID_MISSING_OR_DUPLICATE")
|
||||||
|
for atom in atoms:
|
||||||
|
if atom.get("section_kind") not in CAUSE_SECTION_ORDER:
|
||||||
|
raise AssemblyError("CAUSE_SECTION_KIND_UNKNOWN")
|
||||||
|
if not atom.get("source_refs"):
|
||||||
|
raise AssemblyError("CAUSE_ATOM_PROVENANCE_MISSING")
|
||||||
|
if not isinstance(atom.get("text"), str) or not atom["text"]:
|
||||||
|
raise AssemblyError("CAUSE_ATOM_TEXT_MISSING")
|
||||||
|
return sorted(atoms, key=lambda atom: (
|
||||||
|
CAUSE_SECTION_ORDER[atom["section_kind"]], str(atom.get("group_id", "")),
|
||||||
|
int(atom.get("atom_order", 0)), atom["atom_id"],
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def assemble_documents(
|
||||||
|
relief_rows: Sequence[Mapping[str, Any]], cause_atoms: Sequence[Mapping[str, Any]],
|
||||||
|
*, cost_text: str, provisional_rows: Sequence[Mapping[str, Any]],
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
relief, causes = _ordered_relief(relief_rows), _ordered_causes(cause_atoms)
|
||||||
|
cost_text = normalize_text(cost_text)
|
||||||
|
if not cost_text:
|
||||||
|
raise AssemblyError("COST_TEXT_MISSING")
|
||||||
|
relief_lines = [f"{index}. {normalize_text(row['rendered_text'])}" for index, row in enumerate(relief, 1)]
|
||||||
|
relief_lines.append(f"{len(relief_lines) + 1}. {cost_text}")
|
||||||
|
for row in sorted(provisional_rows, key=lambda item: item["atomic_claim_id"]):
|
||||||
|
if row.get("eligible") is not True:
|
||||||
|
raise AssemblyError("PROVISIONAL_EXECUTION_INELIGIBLE_ATOM")
|
||||||
|
relief_lines.append(f"{len(relief_lines) + 1}. {normalize_text(row['rendered_text'])}")
|
||||||
|
relief_text = normalize_text("\n".join(relief_lines) + "\n")
|
||||||
|
cause_sections: list[str] = []
|
||||||
|
current: str | None = None
|
||||||
|
for atom in causes:
|
||||||
|
if atom["section_kind"] != current:
|
||||||
|
current = atom["section_kind"]
|
||||||
|
cause_sections.append(f"## {current}")
|
||||||
|
cause_sections.append(normalize_text(atom["text"]))
|
||||||
|
cause_text = normalize_text("\n\n".join(cause_sections) + "\n")
|
||||||
|
pleading = normalize_text("# CLAIM_RELIEF\n\n" + relief_text + "\n# CLAIM_CAUSE\n\n" + cause_text)
|
||||||
|
result = {
|
||||||
|
"schema_version": "stage2_s2_40_assembled_documents.v1",
|
||||||
|
"claim_relief": {"text": relief_text, "sha256": sha256_bytes(relief_text.encode())},
|
||||||
|
"claim_cause": {"text": cause_text, "sha256": sha256_bytes(cause_text.encode())},
|
||||||
|
"pleading_draft": {"text": pleading, "sha256": sha256_bytes(pleading.encode())},
|
||||||
|
"ordered_atomic_claim_ids": [row["atomic_claim_id"] for row in relief],
|
||||||
|
"ordered_cause_atom_ids": [atom["atom_id"] for atom in causes],
|
||||||
|
}
|
||||||
|
# A second construction from detached copies must be byte-identical. This
|
||||||
|
# catches accidental mutable state, locale ordering, and hidden renderer IO.
|
||||||
|
independent_relief = normalize_text("\n".join(relief_lines) + "\n")
|
||||||
|
independent_cause = normalize_text("\n\n".join(cause_sections) + "\n")
|
||||||
|
independent_pleading = normalize_text("# CLAIM_RELIEF\n\n" + independent_relief + "\n# CLAIM_CAUSE\n\n" + independent_cause)
|
||||||
|
if (independent_relief, independent_cause, independent_pleading) != (relief_text, cause_text, pleading):
|
||||||
|
raise AssemblyError("CLOSED_DOCUMENT_RERENDER_MISMATCH")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def relief_cause_crossmatch(relief_rows: Sequence[Mapping[str, Any]], cause_atoms: Sequence[Mapping[str, Any]]) -> list[str]:
|
||||||
|
cause_claims = {atom.get("atomic_claim_id") for atom in cause_atoms if atom.get("atomic_claim_id")}
|
||||||
|
findings: list[str] = []
|
||||||
|
for row in relief_rows:
|
||||||
|
claim_id = row.get("atomic_claim_id")
|
||||||
|
if claim_id not in cause_claims:
|
||||||
|
findings.append(f"RELIEF_CAUSE_ATOMIC_CLAIM_MISSING::{claim_id}")
|
||||||
|
for field in ("claimant_ref", "defendant_ref"):
|
||||||
|
expected = row.get(field)
|
||||||
|
if expected and not any(atom.get(field) == expected and atom.get("atomic_claim_id") == claim_id for atom in cause_atoms):
|
||||||
|
findings.append(f"RELIEF_CAUSE_{field.upper()}_MISMATCH::{claim_id}")
|
||||||
|
for field in ("amount_ref", "object_ref", "legal_effect_ref"):
|
||||||
|
expected = row.get(field)
|
||||||
|
if expected is not None and not any(
|
||||||
|
atom.get(field) == expected and atom.get("atomic_claim_id") == claim_id for atom in cause_atoms
|
||||||
|
):
|
||||||
|
findings.append(f"RELIEF_CAUSE_{field.upper()}_MISMATCH::{claim_id}")
|
||||||
|
return sorted(findings)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["AssemblyError", "assemble_documents", "build_usage_projection", "canonical_json_bytes", "reduce_immutable_rows", "relief_cause_crossmatch", "sha256_bytes", "validate_exact_group_part_set", "validate_option_partition"]
|
||||||
+236
@@ -0,0 +1,236 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""Pure offline C40/C45 oracle for the S2_40 deterministic finalizer."""
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import unicodedata
|
||||||
|
import re
|
||||||
|
from typing import Any, Iterable, Mapping, Sequence
|
||||||
|
|
||||||
|
|
||||||
|
class AssemblyError(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
RELATION_ORDER = {"PRIMARY": 0, "PRELIMINARY": 1, "ALTERNATIVE": 2, "SELECTIVE": 3}
|
||||||
|
CAUSE_SECTION_ORDER = {
|
||||||
|
"PARTY_AND_TITLE": 0, "COMMON_FACT": 1, "GROUP_FACT": 2,
|
||||||
|
"ELEMENT_FACT_EVIDENCE": 3, "DEFENSE_REBUTTAL": 4,
|
||||||
|
"CONCLUSION": 5, "ANNEX_EXHIBIT_REFERENCE": 6,
|
||||||
|
}
|
||||||
|
HEX64 = re.compile(r"^[a-f0-9]{64}$")
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_text(value: str) -> str:
|
||||||
|
if not isinstance(value, str):
|
||||||
|
raise AssemblyError("TEXT_TYPE_INVALID")
|
||||||
|
value = unicodedata.normalize("NFC", value.replace("\r\n", "\n").replace("\r", "\n"))
|
||||||
|
if "\x00" in value:
|
||||||
|
raise AssemblyError("TEXT_NUL_FORBIDDEN")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def canonical_json_bytes(value: Any) -> bytes:
|
||||||
|
return (json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n").encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def sha256_bytes(raw: bytes) -> str:
|
||||||
|
return hashlib.sha256(raw).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def reduce_immutable_rows(parts: Sequence[Mapping[str, Any]], *, id_field: str, row_field: str) -> list[dict[str, Any]]:
|
||||||
|
"""Stable union; only byte-equivalent duplicates are tolerated."""
|
||||||
|
by_id: dict[str, tuple[bytes, dict[str, Any]]] = {}
|
||||||
|
seen_parts: set[str] = set()
|
||||||
|
for part in sorted(parts, key=lambda row: (str(row.get("group_id", "")), str(row.get("part_id", "")))):
|
||||||
|
part_id = part.get("part_id")
|
||||||
|
if not isinstance(part_id, str) or not part_id or part_id in seen_parts:
|
||||||
|
raise AssemblyError("IMMUTABLE_PART_ID_MISSING_OR_DUPLICATE")
|
||||||
|
seen_parts.add(part_id)
|
||||||
|
rows = part.get(row_field)
|
||||||
|
if not isinstance(rows, list):
|
||||||
|
raise AssemblyError("IMMUTABLE_PART_ROWS_INVALID")
|
||||||
|
for row in rows:
|
||||||
|
row_id = row.get(id_field) if isinstance(row, dict) else None
|
||||||
|
if not isinstance(row_id, str) or not row_id:
|
||||||
|
raise AssemblyError("IMMUTABLE_ROW_ID_INVALID")
|
||||||
|
raw = canonical_json_bytes(row)
|
||||||
|
if row_id in by_id and by_id[row_id][0] != raw:
|
||||||
|
raise AssemblyError("IMMUTABLE_ROW_CONFLICT")
|
||||||
|
by_id[row_id] = (raw, dict(row))
|
||||||
|
return [by_id[key][1] for key in sorted(by_id)]
|
||||||
|
|
||||||
|
|
||||||
|
def validate_exact_group_part_set(
|
||||||
|
parts: Sequence[Mapping[str, Any]], *, expected_group_ids: Sequence[str],
|
||||||
|
expected_families: Sequence[str] = ("DRAFT_PART", "ISSUE_PATCH", "WORKNOTE_PART", "USAGE_PART"),
|
||||||
|
) -> None:
|
||||||
|
if len(expected_group_ids) != len(set(expected_group_ids)) or len(expected_families) != len(set(expected_families)):
|
||||||
|
raise AssemblyError("EXPECTED_PART_SET_DUPLICATE")
|
||||||
|
expected = {(group_id, family) for group_id in expected_group_ids for family in expected_families}
|
||||||
|
observed: list[tuple[str, str]] = []
|
||||||
|
part_ids: set[str] = set()
|
||||||
|
for part in parts:
|
||||||
|
part_id, group_id, family = part.get("part_id"), part.get("group_id"), part.get("part_family")
|
||||||
|
if not all(isinstance(value, str) and value for value in (part_id, group_id, family)):
|
||||||
|
raise AssemblyError("PART_SET_ROW_INVALID")
|
||||||
|
if part_id in part_ids:
|
||||||
|
raise AssemblyError("PART_SET_PART_ID_DUPLICATE")
|
||||||
|
part_ids.add(part_id)
|
||||||
|
observed.append((group_id, family))
|
||||||
|
if len(observed) != len(set(observed)) or set(observed) != expected:
|
||||||
|
raise AssemblyError("PART_SET_NOT_EXACT")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_option_partition(option_ids: Iterable[str], partitions: Mapping[str, Sequence[str]]) -> None:
|
||||||
|
required = {"selected", "alternative", "excluded", "deferred"}
|
||||||
|
if set(partitions) != required:
|
||||||
|
raise AssemblyError("OPTION_PARTITION_SHAPE_INVALID")
|
||||||
|
flattened = [item for name in sorted(required) for item in partitions[name]]
|
||||||
|
if len(flattened) != len(set(flattened)):
|
||||||
|
raise AssemblyError("OPTION_PARTITION_OVERLAP")
|
||||||
|
if set(flattened) != set(option_ids):
|
||||||
|
raise AssemblyError("OPTION_PARTITION_NOT_CONSERVATIVE")
|
||||||
|
|
||||||
|
|
||||||
|
def build_usage_projection(
|
||||||
|
usage_parts: Sequence[Mapping[str, Any]], *, run_binding_digest: str,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Reduce immutable S2_30 usage parts into the exact S2_40 projection contract."""
|
||||||
|
if HEX64.fullmatch(run_binding_digest) is None:
|
||||||
|
raise AssemblyError("USAGE_RUN_BINDING_DIGEST_INVALID")
|
||||||
|
source_hashes: list[str] = []
|
||||||
|
by_id: dict[str, tuple[bytes, dict[str, Any]]] = {}
|
||||||
|
observed_row_count = 0
|
||||||
|
for part in sorted(usage_parts, key=lambda value: str(value.get("part_id", ""))):
|
||||||
|
if set(part) != {"part_id", "raw_sha256", "rows"}:
|
||||||
|
raise AssemblyError("USAGE_PART_SHAPE_NOT_EXACT")
|
||||||
|
if not isinstance(part["part_id"], str) or not part["part_id"]:
|
||||||
|
raise AssemblyError("USAGE_PART_ID_INVALID")
|
||||||
|
if not isinstance(part["raw_sha256"], str) or HEX64.fullmatch(part["raw_sha256"]) is None:
|
||||||
|
raise AssemblyError("USAGE_PART_SHA256_INVALID")
|
||||||
|
rows = part["rows"]
|
||||||
|
if not isinstance(rows, list):
|
||||||
|
raise AssemblyError("USAGE_PART_ROWS_INVALID")
|
||||||
|
source_hashes.append(part["raw_sha256"])
|
||||||
|
for row in rows:
|
||||||
|
if not isinstance(row, dict):
|
||||||
|
raise AssemblyError("USAGE_ROW_NOT_OBJECT")
|
||||||
|
usage_id = row.get("usage_id")
|
||||||
|
if not isinstance(usage_id, str) or not usage_id:
|
||||||
|
raise AssemblyError("USAGE_ID_INVALID")
|
||||||
|
observed_row_count += 1
|
||||||
|
raw = canonical_json_bytes(row)
|
||||||
|
if usage_id in by_id and by_id[usage_id][0] != raw:
|
||||||
|
raise AssemblyError("USAGE_ROW_CONFLICT")
|
||||||
|
by_id[usage_id] = (raw, dict(row))
|
||||||
|
if len(source_hashes) != len(set(source_hashes)):
|
||||||
|
raise AssemblyError("USAGE_SOURCE_PART_SHA256_DUPLICATE")
|
||||||
|
rows = [by_id[key][1] for key in sorted(by_id)]
|
||||||
|
if observed_row_count != len(rows):
|
||||||
|
raise AssemblyError("USAGE_CONSERVATION_FAILED")
|
||||||
|
return {
|
||||||
|
"schema_version": "stage2_s2_40_usage_projection.v1",
|
||||||
|
"producer_id": "S2_40",
|
||||||
|
"run_binding_digest": run_binding_digest,
|
||||||
|
"source_usage_part_sha256s": sorted(source_hashes),
|
||||||
|
"rows": rows,
|
||||||
|
"conservation_status": "PASS",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _ordered_relief(rows: Sequence[Mapping[str, Any]]) -> list[Mapping[str, Any]]:
|
||||||
|
claim_ids = [row.get("atomic_claim_id") for row in rows]
|
||||||
|
if any(not isinstance(value, str) or not value for value in claim_ids) or len(claim_ids) != len(set(claim_ids)):
|
||||||
|
raise AssemblyError("RELIEF_ATOMIC_CLAIM_ID_MISSING_OR_DUPLICATE")
|
||||||
|
for row in rows:
|
||||||
|
if row.get("relation") not in RELATION_ORDER:
|
||||||
|
raise AssemblyError("RELIEF_RELATION_UNKNOWN")
|
||||||
|
if not isinstance(row.get("plan_order"), int) or row["plan_order"] < 0:
|
||||||
|
raise AssemblyError("RELIEF_PLAN_ORDER_INVALID")
|
||||||
|
if not isinstance(row.get("rendered_text"), str) or not row["rendered_text"]:
|
||||||
|
raise AssemblyError("RELIEF_RENDERED_TEXT_MISSING")
|
||||||
|
return sorted(rows, key=lambda row: (RELATION_ORDER[row["relation"]], row["plan_order"], row["atomic_claim_id"]))
|
||||||
|
|
||||||
|
|
||||||
|
def _ordered_causes(atoms: Sequence[Mapping[str, Any]]) -> list[Mapping[str, Any]]:
|
||||||
|
atom_ids = [atom.get("atom_id") for atom in atoms]
|
||||||
|
if any(not isinstance(value, str) or not value for value in atom_ids) or len(atom_ids) != len(set(atom_ids)):
|
||||||
|
raise AssemblyError("CAUSE_ATOM_ID_MISSING_OR_DUPLICATE")
|
||||||
|
for atom in atoms:
|
||||||
|
if atom.get("section_kind") not in CAUSE_SECTION_ORDER:
|
||||||
|
raise AssemblyError("CAUSE_SECTION_KIND_UNKNOWN")
|
||||||
|
if not atom.get("source_refs"):
|
||||||
|
raise AssemblyError("CAUSE_ATOM_PROVENANCE_MISSING")
|
||||||
|
if not isinstance(atom.get("text"), str) or not atom["text"]:
|
||||||
|
raise AssemblyError("CAUSE_ATOM_TEXT_MISSING")
|
||||||
|
return sorted(atoms, key=lambda atom: (
|
||||||
|
CAUSE_SECTION_ORDER[atom["section_kind"]], str(atom.get("group_id", "")),
|
||||||
|
int(atom.get("atom_order", 0)), atom["atom_id"],
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def assemble_documents(
|
||||||
|
relief_rows: Sequence[Mapping[str, Any]], cause_atoms: Sequence[Mapping[str, Any]],
|
||||||
|
*, cost_text: str, provisional_rows: Sequence[Mapping[str, Any]],
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
relief, causes = _ordered_relief(relief_rows), _ordered_causes(cause_atoms)
|
||||||
|
cost_text = normalize_text(cost_text)
|
||||||
|
if not cost_text:
|
||||||
|
raise AssemblyError("COST_TEXT_MISSING")
|
||||||
|
relief_lines = [f"{index}. {normalize_text(row['rendered_text'])}" for index, row in enumerate(relief, 1)]
|
||||||
|
relief_lines.append(f"{len(relief_lines) + 1}. {cost_text}")
|
||||||
|
for row in sorted(provisional_rows, key=lambda item: item["atomic_claim_id"]):
|
||||||
|
if row.get("eligible") is not True:
|
||||||
|
raise AssemblyError("PROVISIONAL_EXECUTION_INELIGIBLE_ATOM")
|
||||||
|
relief_lines.append(f"{len(relief_lines) + 1}. {normalize_text(row['rendered_text'])}")
|
||||||
|
relief_text = normalize_text("\n".join(relief_lines) + "\n")
|
||||||
|
cause_sections: list[str] = []
|
||||||
|
current: str | None = None
|
||||||
|
for atom in causes:
|
||||||
|
if atom["section_kind"] != current:
|
||||||
|
current = atom["section_kind"]
|
||||||
|
cause_sections.append(f"## {current}")
|
||||||
|
cause_sections.append(normalize_text(atom["text"]))
|
||||||
|
cause_text = normalize_text("\n\n".join(cause_sections) + "\n")
|
||||||
|
pleading = normalize_text("# CLAIM_RELIEF\n\n" + relief_text + "\n# CLAIM_CAUSE\n\n" + cause_text)
|
||||||
|
result = {
|
||||||
|
"schema_version": "stage2_s2_40_assembled_documents.v1",
|
||||||
|
"claim_relief": {"text": relief_text, "sha256": sha256_bytes(relief_text.encode())},
|
||||||
|
"claim_cause": {"text": cause_text, "sha256": sha256_bytes(cause_text.encode())},
|
||||||
|
"pleading_draft": {"text": pleading, "sha256": sha256_bytes(pleading.encode())},
|
||||||
|
"ordered_atomic_claim_ids": [row["atomic_claim_id"] for row in relief],
|
||||||
|
"ordered_cause_atom_ids": [atom["atom_id"] for atom in causes],
|
||||||
|
}
|
||||||
|
# A second construction from detached copies must be byte-identical. This
|
||||||
|
# catches accidental mutable state, locale ordering, and hidden renderer IO.
|
||||||
|
independent_relief = normalize_text("\n".join(relief_lines) + "\n")
|
||||||
|
independent_cause = normalize_text("\n\n".join(cause_sections) + "\n")
|
||||||
|
independent_pleading = normalize_text("# CLAIM_RELIEF\n\n" + independent_relief + "\n# CLAIM_CAUSE\n\n" + independent_cause)
|
||||||
|
if (independent_relief, independent_cause, independent_pleading) != (relief_text, cause_text, pleading):
|
||||||
|
raise AssemblyError("CLOSED_DOCUMENT_RERENDER_MISMATCH")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def relief_cause_crossmatch(relief_rows: Sequence[Mapping[str, Any]], cause_atoms: Sequence[Mapping[str, Any]]) -> list[str]:
|
||||||
|
cause_claims = {atom.get("atomic_claim_id") for atom in cause_atoms if atom.get("atomic_claim_id")}
|
||||||
|
findings: list[str] = []
|
||||||
|
for row in relief_rows:
|
||||||
|
claim_id = row.get("atomic_claim_id")
|
||||||
|
if claim_id not in cause_claims:
|
||||||
|
findings.append(f"RELIEF_CAUSE_ATOMIC_CLAIM_MISSING::{claim_id}")
|
||||||
|
for field in ("claimant_ref", "defendant_ref"):
|
||||||
|
expected = row.get(field)
|
||||||
|
if expected and not any(atom.get(field) == expected and atom.get("atomic_claim_id") == claim_id for atom in cause_atoms):
|
||||||
|
findings.append(f"RELIEF_CAUSE_{field.upper()}_MISMATCH::{claim_id}")
|
||||||
|
for field in ("amount_ref", "object_ref", "legal_effect_ref"):
|
||||||
|
expected = row.get(field)
|
||||||
|
if expected is not None and not any(
|
||||||
|
atom.get(field) == expected and atom.get("atomic_claim_id") == claim_id for atom in cause_atoms
|
||||||
|
):
|
||||||
|
findings.append(f"RELIEF_CAUSE_{field.upper()}_MISMATCH::{claim_id}")
|
||||||
|
return sorted(findings)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["AssemblyError", "assemble_documents", "build_usage_projection", "canonical_json_bytes", "reduce_immutable_rows", "relief_cause_crossmatch", "sha256_bytes", "validate_exact_group_part_set", "validate_option_partition"]
|
||||||
+215
@@ -0,0 +1,215 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""Offline oracle for S2_40 closed rendering; contains no legal wording."""
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import unicodedata
|
||||||
|
from typing import Any, Mapping, Sequence
|
||||||
|
|
||||||
|
|
||||||
|
ALLOWED_SLOT_TYPES = {
|
||||||
|
"STRING", "IDENTIFIER", "MONEY", "DATE", "OBJECT_REF", "PARTY_REF", "EXHIBIT_REF"
|
||||||
|
}
|
||||||
|
ALLOWED_PREDICATE_OPS = {"EQ", "IN", "BOOL"}
|
||||||
|
|
||||||
|
|
||||||
|
class ClosedRenderError(ValueError):
|
||||||
|
"""Raised when a closed renderer contract cannot be applied exactly."""
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_text(value: str) -> str:
|
||||||
|
if not isinstance(value, str):
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_TYPE_MISMATCH")
|
||||||
|
normalized = unicodedata.normalize("NFC", value.replace("\r\n", "\n").replace("\r", "\n"))
|
||||||
|
if "\x00" in normalized:
|
||||||
|
raise ClosedRenderError("RENDERER_NUL_FORBIDDEN")
|
||||||
|
return normalized
|
||||||
|
|
||||||
|
|
||||||
|
def canonical_json_bytes(value: Any) -> bytes:
|
||||||
|
return (json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n").encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def sha256_bytes(raw: bytes) -> str:
|
||||||
|
return hashlib.sha256(raw).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def _unique_names(rows: Sequence[Mapping[str, Any]], field: str) -> set[str]:
|
||||||
|
names = [row.get(field) for row in rows]
|
||||||
|
if any(not isinstance(name, str) or not name for name in names):
|
||||||
|
raise ClosedRenderError(f"RENDERER_{field.upper()}_INVALID")
|
||||||
|
if len(names) != len(set(names)):
|
||||||
|
raise ClosedRenderError(f"RENDERER_{field.upper()}_DUPLICATE")
|
||||||
|
return set(names)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_descriptor(descriptor: Mapping[str, Any], *, allow_fixture: bool = False) -> None:
|
||||||
|
required = {
|
||||||
|
"schema_version", "renderer_id", "renderer_kind", "status", "execution_eligible",
|
||||||
|
"stage2_20_role", "stage2_40_role", "typed_slot_contract", "closed_ast_contract",
|
||||||
|
"branch_rows", "closure", "review",
|
||||||
|
}
|
||||||
|
if set(descriptor) != required:
|
||||||
|
raise ClosedRenderError("RENDERER_DESCRIPTOR_SHAPE_INVALID")
|
||||||
|
if not descriptor["execution_eligible"]:
|
||||||
|
raise ClosedRenderError("RENDERER_NOT_EXECUTION_ELIGIBLE")
|
||||||
|
if descriptor["status"] != "APPROVED_LEGAL_CONTENT":
|
||||||
|
if not (allow_fixture and descriptor["status"] == "STRUCTURAL_FIXTURE_ONLY"):
|
||||||
|
raise ClosedRenderError("RENDERER_LEGAL_CONTENT_NOT_APPROVED")
|
||||||
|
slots = descriptor["typed_slot_contract"].get("slot_definitions", [])
|
||||||
|
slot_names = _unique_names(slots, "name")
|
||||||
|
structural_names = descriptor["typed_slot_contract"].get("structural_slot_names")
|
||||||
|
if structural_names is not None and (not isinstance(structural_names, list) or set(structural_names) != slot_names or len(structural_names) != len(slot_names)):
|
||||||
|
raise ClosedRenderError("RENDERER_STRUCTURAL_SLOT_SET_MISMATCH")
|
||||||
|
for slot in slots:
|
||||||
|
if slot.get("type") not in ALLOWED_SLOT_TYPES:
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_TYPE_UNKNOWN")
|
||||||
|
if slot.get("cardinality") not in {"EXACTLY_ONE", "ZERO_OR_ONE", "ONE_OR_MORE"}:
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_CARDINALITY_UNKNOWN")
|
||||||
|
branches = descriptor["branch_rows"]
|
||||||
|
_unique_names(branches, "branch_id")
|
||||||
|
for branch in branches:
|
||||||
|
if branch.get("approval_status") != "APPROVED":
|
||||||
|
raise ClosedRenderError("RENDERER_BRANCH_NOT_APPROVED")
|
||||||
|
predicates = branch.get("predicates", [])
|
||||||
|
if not isinstance(predicates, list):
|
||||||
|
raise ClosedRenderError("RENDERER_PREDICATES_INVALID")
|
||||||
|
predicate_keys: set[tuple[str, str]] = set()
|
||||||
|
for predicate in predicates:
|
||||||
|
if predicate.get("op") not in ALLOWED_PREDICATE_OPS:
|
||||||
|
raise ClosedRenderError("RENDERER_PREDICATE_OP_UNKNOWN")
|
||||||
|
field = predicate.get("field")
|
||||||
|
if not isinstance(field, str) or not field:
|
||||||
|
raise ClosedRenderError("RENDERER_PREDICATE_FIELD_INVALID")
|
||||||
|
key = (field, predicate["op"])
|
||||||
|
if key in predicate_keys:
|
||||||
|
raise ClosedRenderError("RENDERER_PREDICATE_DUPLICATE")
|
||||||
|
predicate_keys.add(key)
|
||||||
|
segments = branch.get("segments")
|
||||||
|
if not isinstance(segments, list) or not segments:
|
||||||
|
raise ClosedRenderError("RENDERER_SEGMENTS_EMPTY")
|
||||||
|
for segment in segments:
|
||||||
|
if segment.get("kind") == "LITERAL":
|
||||||
|
if set(segment) != {"kind", "value"}:
|
||||||
|
raise ClosedRenderError("RENDERER_LITERAL_SEGMENT_INVALID")
|
||||||
|
normalize_text(segment["value"])
|
||||||
|
elif segment.get("kind") == "SLOT":
|
||||||
|
if set(segment) != {"kind", "name", "escape"}:
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_SEGMENT_INVALID")
|
||||||
|
if segment["name"] not in slot_names:
|
||||||
|
raise ClosedRenderError("RENDERER_UNKNOWN_TEMPLATE_SLOT")
|
||||||
|
if segment["escape"] not in {"PLAIN_TEXT", "MARKDOWN_TEXT"}:
|
||||||
|
raise ClosedRenderError("RENDERER_ESCAPE_POLICY_UNKNOWN")
|
||||||
|
else:
|
||||||
|
raise ClosedRenderError("RENDERER_SEGMENT_KIND_UNKNOWN")
|
||||||
|
|
||||||
|
|
||||||
|
def _matches(predicate: Mapping[str, Any], context: Mapping[str, Any]) -> bool:
|
||||||
|
field = predicate.get("field")
|
||||||
|
if not isinstance(field, str) or field not in context:
|
||||||
|
return False
|
||||||
|
observed, op = context[field], predicate.get("op")
|
||||||
|
if op == "EQ":
|
||||||
|
return observed == predicate.get("value")
|
||||||
|
if op == "IN":
|
||||||
|
return isinstance(predicate.get("values"), list) and observed in predicate["values"]
|
||||||
|
if op == "BOOL":
|
||||||
|
return isinstance(observed, bool) and observed is predicate.get("value")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def select_branch(descriptor: Mapping[str, Any], context: Mapping[str, Any], *, allow_fixture: bool = False) -> Mapping[str, Any]:
|
||||||
|
validate_descriptor(descriptor, allow_fixture=allow_fixture)
|
||||||
|
matches = [row for row in descriptor["branch_rows"] if all(_matches(p, context) for p in row.get("predicates", []))]
|
||||||
|
if not matches:
|
||||||
|
raise ClosedRenderError(descriptor["closure"]["zero_match_issue_code"])
|
||||||
|
if len(matches) != 1:
|
||||||
|
raise ClosedRenderError(descriptor["closure"]["multi_match_issue_code"])
|
||||||
|
return matches[0]
|
||||||
|
|
||||||
|
|
||||||
|
def _escape(value: str, policy: str) -> str:
|
||||||
|
value = normalize_text(value)
|
||||||
|
if "{{" in value or "}}" in value:
|
||||||
|
raise ClosedRenderError("RENDERER_DANGLING_TEMPLATE_TOKEN")
|
||||||
|
if policy == "PLAIN_TEXT":
|
||||||
|
return value
|
||||||
|
if policy == "MARKDOWN_TEXT":
|
||||||
|
return re.sub(r"([\\`*_{}\[\]<>#|])", r"\\\1", value)
|
||||||
|
raise ClosedRenderError("RENDERER_ESCAPE_POLICY_UNKNOWN")
|
||||||
|
|
||||||
|
|
||||||
|
def escape_slot_value(value: str, policy: str) -> str:
|
||||||
|
"""Public oracle for the exact inline PLAIN_TEXT/MARKDOWN_TEXT semantics."""
|
||||||
|
return _escape(value, policy)
|
||||||
|
|
||||||
|
|
||||||
|
def _render_frozen(branch: Mapping[str, Any], definitions: Mapping[str, Any], slots: Mapping[str, Any]) -> str:
|
||||||
|
pieces: list[str] = []
|
||||||
|
for segment in branch["segments"]:
|
||||||
|
if segment["kind"] == "LITERAL":
|
||||||
|
pieces.append(normalize_text(segment["value"]))
|
||||||
|
continue
|
||||||
|
definition, value = definitions[segment["name"]], slots.get(segment["name"])
|
||||||
|
if value is None and definition["cardinality"] == "ZERO_OR_ONE":
|
||||||
|
continue
|
||||||
|
if isinstance(value, list):
|
||||||
|
if not value or any(not isinstance(item, str) for item in value):
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_LIST_INVALID")
|
||||||
|
pieces.append(", ".join(_escape(item, segment["escape"]) for item in value))
|
||||||
|
else:
|
||||||
|
if value is not None and not isinstance(value, str):
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_TYPE_MISMATCH")
|
||||||
|
pieces.append(_escape(value, segment["escape"]))
|
||||||
|
return normalize_text("".join(pieces))
|
||||||
|
|
||||||
|
|
||||||
|
def render_closed(
|
||||||
|
descriptor: Mapping[str, Any], branch_context: Mapping[str, Any], slots: Mapping[str, Any],
|
||||||
|
*, atom_type: str, allow_fixture: bool = False,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
branch = select_branch(descriptor, branch_context, allow_fixture=allow_fixture)
|
||||||
|
if atom_type not in set(descriptor["closed_ast_contract"]["allowed_atom_types"]):
|
||||||
|
raise ClosedRenderError("RENDERER_ATOM_TYPE_FORBIDDEN")
|
||||||
|
definitions = {row["name"]: row for row in descriptor["typed_slot_contract"]["slot_definitions"]}
|
||||||
|
if set(slots) - set(definitions):
|
||||||
|
raise ClosedRenderError("RENDERER_UNKNOWN_SLOT")
|
||||||
|
for name, definition in definitions.items():
|
||||||
|
value = slots.get(name)
|
||||||
|
if value is None and definition["cardinality"] in {"EXACTLY_ONE", "ONE_OR_MORE"}:
|
||||||
|
raise ClosedRenderError("RENDERER_REQUIRED_SLOT_MISSING")
|
||||||
|
if isinstance(value, list) != (definition["cardinality"] == "ONE_OR_MORE") and value is not None:
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_CARDINALITY_MISMATCH")
|
||||||
|
output = _render_frozen(branch, definitions, slots)
|
||||||
|
detached = json.loads(canonical_json_bytes({"branch": branch, "definitions": definitions, "slots": slots}))
|
||||||
|
independent = _render_frozen(detached["branch"], detached["definitions"], detached["slots"])
|
||||||
|
if output.encode("utf-8") != independent.encode("utf-8"):
|
||||||
|
raise ClosedRenderError("RENDERER_INDEPENDENT_RERENDER_MISMATCH")
|
||||||
|
if re.search(r"\{\{[^}]*\}\}", output):
|
||||||
|
raise ClosedRenderError("RENDERER_DANGLING_TEMPLATE_TOKEN")
|
||||||
|
return {
|
||||||
|
"schema_version": "stage2_s2_40_closed_render_result.v1",
|
||||||
|
"renderer_id": descriptor["renderer_id"], "branch_id": branch["branch_id"],
|
||||||
|
"atom_type": atom_type, "consumed_slot_names": sorted(slots),
|
||||||
|
"rendered_text": output, "rendered_sha256": sha256_bytes(output.encode("utf-8")),
|
||||||
|
"renderer_branch_sha256": sha256_bytes(canonical_json_bytes(branch)),
|
||||||
|
"slot_binding_sha256": sha256_bytes(canonical_json_bytes(dict(slots))),
|
||||||
|
"independent_rerender_sha256": sha256_bytes(independent.encode("utf-8")),
|
||||||
|
"normalization_version": descriptor["closed_ast_contract"]["normalization_version"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def provisional_execution_eligible(atom: Mapping[str, Any]) -> bool:
|
||||||
|
return bool(
|
||||||
|
atom.get("proprietary_claim") is True and atom.get("performance_atom") is True
|
||||||
|
and atom.get("atomic_branch_provisional_execution_policy") == "ALLOW"
|
||||||
|
and atom.get("approved_authority_ref_is_valid") is True
|
||||||
|
and atom.get("dependent_on_constitutive_judgment") is False
|
||||||
|
and atom.get("renderer_id") not in {"R03", "R05", "R06"}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["ClosedRenderError", "canonical_json_bytes", "escape_slot_value", "normalize_text", "provisional_execution_eligible", "render_closed", "select_branch", "sha256_bytes", "validate_descriptor"]
|
||||||
+215
@@ -0,0 +1,215 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""Offline oracle for S2_40 closed rendering; contains no legal wording."""
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import unicodedata
|
||||||
|
from typing import Any, Mapping, Sequence
|
||||||
|
|
||||||
|
|
||||||
|
ALLOWED_SLOT_TYPES = {
|
||||||
|
"STRING", "IDENTIFIER", "MONEY", "DATE", "OBJECT_REF", "PARTY_REF", "EXHIBIT_REF"
|
||||||
|
}
|
||||||
|
ALLOWED_PREDICATE_OPS = {"EQ", "IN", "BOOL"}
|
||||||
|
|
||||||
|
|
||||||
|
class ClosedRenderError(ValueError):
|
||||||
|
"""Raised when a closed renderer contract cannot be applied exactly."""
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_text(value: str) -> str:
|
||||||
|
if not isinstance(value, str):
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_TYPE_MISMATCH")
|
||||||
|
normalized = unicodedata.normalize("NFC", value.replace("\r\n", "\n").replace("\r", "\n"))
|
||||||
|
if "\x00" in normalized:
|
||||||
|
raise ClosedRenderError("RENDERER_NUL_FORBIDDEN")
|
||||||
|
return normalized
|
||||||
|
|
||||||
|
|
||||||
|
def canonical_json_bytes(value: Any) -> bytes:
|
||||||
|
return (json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n").encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def sha256_bytes(raw: bytes) -> str:
|
||||||
|
return hashlib.sha256(raw).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def _unique_names(rows: Sequence[Mapping[str, Any]], field: str) -> set[str]:
|
||||||
|
names = [row.get(field) for row in rows]
|
||||||
|
if any(not isinstance(name, str) or not name for name in names):
|
||||||
|
raise ClosedRenderError(f"RENDERER_{field.upper()}_INVALID")
|
||||||
|
if len(names) != len(set(names)):
|
||||||
|
raise ClosedRenderError(f"RENDERER_{field.upper()}_DUPLICATE")
|
||||||
|
return set(names)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_descriptor(descriptor: Mapping[str, Any], *, allow_fixture: bool = False) -> None:
|
||||||
|
required = {
|
||||||
|
"schema_version", "renderer_id", "renderer_kind", "status", "execution_eligible",
|
||||||
|
"stage2_20_role", "stage2_40_role", "typed_slot_contract", "closed_ast_contract",
|
||||||
|
"branch_rows", "closure", "review",
|
||||||
|
}
|
||||||
|
if set(descriptor) != required:
|
||||||
|
raise ClosedRenderError("RENDERER_DESCRIPTOR_SHAPE_INVALID")
|
||||||
|
if not descriptor["execution_eligible"]:
|
||||||
|
raise ClosedRenderError("RENDERER_NOT_EXECUTION_ELIGIBLE")
|
||||||
|
if descriptor["status"] != "APPROVED_LEGAL_CONTENT":
|
||||||
|
if not (allow_fixture and descriptor["status"] == "STRUCTURAL_FIXTURE_ONLY"):
|
||||||
|
raise ClosedRenderError("RENDERER_LEGAL_CONTENT_NOT_APPROVED")
|
||||||
|
slots = descriptor["typed_slot_contract"].get("slot_definitions", [])
|
||||||
|
slot_names = _unique_names(slots, "name")
|
||||||
|
structural_names = descriptor["typed_slot_contract"].get("structural_slot_names")
|
||||||
|
if structural_names is not None and (not isinstance(structural_names, list) or set(structural_names) != slot_names or len(structural_names) != len(slot_names)):
|
||||||
|
raise ClosedRenderError("RENDERER_STRUCTURAL_SLOT_SET_MISMATCH")
|
||||||
|
for slot in slots:
|
||||||
|
if slot.get("type") not in ALLOWED_SLOT_TYPES:
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_TYPE_UNKNOWN")
|
||||||
|
if slot.get("cardinality") not in {"EXACTLY_ONE", "ZERO_OR_ONE", "ONE_OR_MORE"}:
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_CARDINALITY_UNKNOWN")
|
||||||
|
branches = descriptor["branch_rows"]
|
||||||
|
_unique_names(branches, "branch_id")
|
||||||
|
for branch in branches:
|
||||||
|
if branch.get("approval_status") != "APPROVED":
|
||||||
|
raise ClosedRenderError("RENDERER_BRANCH_NOT_APPROVED")
|
||||||
|
predicates = branch.get("predicates", [])
|
||||||
|
if not isinstance(predicates, list):
|
||||||
|
raise ClosedRenderError("RENDERER_PREDICATES_INVALID")
|
||||||
|
predicate_keys: set[tuple[str, str]] = set()
|
||||||
|
for predicate in predicates:
|
||||||
|
if predicate.get("op") not in ALLOWED_PREDICATE_OPS:
|
||||||
|
raise ClosedRenderError("RENDERER_PREDICATE_OP_UNKNOWN")
|
||||||
|
field = predicate.get("field")
|
||||||
|
if not isinstance(field, str) or not field:
|
||||||
|
raise ClosedRenderError("RENDERER_PREDICATE_FIELD_INVALID")
|
||||||
|
key = (field, predicate["op"])
|
||||||
|
if key in predicate_keys:
|
||||||
|
raise ClosedRenderError("RENDERER_PREDICATE_DUPLICATE")
|
||||||
|
predicate_keys.add(key)
|
||||||
|
segments = branch.get("segments")
|
||||||
|
if not isinstance(segments, list) or not segments:
|
||||||
|
raise ClosedRenderError("RENDERER_SEGMENTS_EMPTY")
|
||||||
|
for segment in segments:
|
||||||
|
if segment.get("kind") == "LITERAL":
|
||||||
|
if set(segment) != {"kind", "value"}:
|
||||||
|
raise ClosedRenderError("RENDERER_LITERAL_SEGMENT_INVALID")
|
||||||
|
normalize_text(segment["value"])
|
||||||
|
elif segment.get("kind") == "SLOT":
|
||||||
|
if set(segment) != {"kind", "name", "escape"}:
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_SEGMENT_INVALID")
|
||||||
|
if segment["name"] not in slot_names:
|
||||||
|
raise ClosedRenderError("RENDERER_UNKNOWN_TEMPLATE_SLOT")
|
||||||
|
if segment["escape"] not in {"PLAIN_TEXT", "MARKDOWN_TEXT"}:
|
||||||
|
raise ClosedRenderError("RENDERER_ESCAPE_POLICY_UNKNOWN")
|
||||||
|
else:
|
||||||
|
raise ClosedRenderError("RENDERER_SEGMENT_KIND_UNKNOWN")
|
||||||
|
|
||||||
|
|
||||||
|
def _matches(predicate: Mapping[str, Any], context: Mapping[str, Any]) -> bool:
|
||||||
|
field = predicate.get("field")
|
||||||
|
if not isinstance(field, str) or field not in context:
|
||||||
|
return False
|
||||||
|
observed, op = context[field], predicate.get("op")
|
||||||
|
if op == "EQ":
|
||||||
|
return observed == predicate.get("value")
|
||||||
|
if op == "IN":
|
||||||
|
return isinstance(predicate.get("values"), list) and observed in predicate["values"]
|
||||||
|
if op == "BOOL":
|
||||||
|
return isinstance(observed, bool) and observed is predicate.get("value")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def select_branch(descriptor: Mapping[str, Any], context: Mapping[str, Any], *, allow_fixture: bool = False) -> Mapping[str, Any]:
|
||||||
|
validate_descriptor(descriptor, allow_fixture=allow_fixture)
|
||||||
|
matches = [row for row in descriptor["branch_rows"] if all(_matches(p, context) for p in row.get("predicates", []))]
|
||||||
|
if not matches:
|
||||||
|
raise ClosedRenderError(descriptor["closure"]["zero_match_issue_code"])
|
||||||
|
if len(matches) != 1:
|
||||||
|
raise ClosedRenderError(descriptor["closure"]["multi_match_issue_code"])
|
||||||
|
return matches[0]
|
||||||
|
|
||||||
|
|
||||||
|
def _escape(value: str, policy: str) -> str:
|
||||||
|
value = normalize_text(value)
|
||||||
|
if "{{" in value or "}}" in value:
|
||||||
|
raise ClosedRenderError("RENDERER_DANGLING_TEMPLATE_TOKEN")
|
||||||
|
if policy == "PLAIN_TEXT":
|
||||||
|
return value
|
||||||
|
if policy == "MARKDOWN_TEXT":
|
||||||
|
return re.sub(r"([\\`*_{}\[\]<>#|])", r"\\\1", value)
|
||||||
|
raise ClosedRenderError("RENDERER_ESCAPE_POLICY_UNKNOWN")
|
||||||
|
|
||||||
|
|
||||||
|
def escape_slot_value(value: str, policy: str) -> str:
|
||||||
|
"""Public oracle for the exact inline PLAIN_TEXT/MARKDOWN_TEXT semantics."""
|
||||||
|
return _escape(value, policy)
|
||||||
|
|
||||||
|
|
||||||
|
def _render_frozen(branch: Mapping[str, Any], definitions: Mapping[str, Any], slots: Mapping[str, Any]) -> str:
|
||||||
|
pieces: list[str] = []
|
||||||
|
for segment in branch["segments"]:
|
||||||
|
if segment["kind"] == "LITERAL":
|
||||||
|
pieces.append(normalize_text(segment["value"]))
|
||||||
|
continue
|
||||||
|
definition, value = definitions[segment["name"]], slots.get(segment["name"])
|
||||||
|
if value is None and definition["cardinality"] == "ZERO_OR_ONE":
|
||||||
|
continue
|
||||||
|
if isinstance(value, list):
|
||||||
|
if not value or any(not isinstance(item, str) for item in value):
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_LIST_INVALID")
|
||||||
|
pieces.append(", ".join(_escape(item, segment["escape"]) for item in value))
|
||||||
|
else:
|
||||||
|
if value is not None and not isinstance(value, str):
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_TYPE_MISMATCH")
|
||||||
|
pieces.append(_escape(value, segment["escape"]))
|
||||||
|
return normalize_text("".join(pieces))
|
||||||
|
|
||||||
|
|
||||||
|
def render_closed(
|
||||||
|
descriptor: Mapping[str, Any], branch_context: Mapping[str, Any], slots: Mapping[str, Any],
|
||||||
|
*, atom_type: str, allow_fixture: bool = False,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
branch = select_branch(descriptor, branch_context, allow_fixture=allow_fixture)
|
||||||
|
if atom_type not in set(descriptor["closed_ast_contract"]["allowed_atom_types"]):
|
||||||
|
raise ClosedRenderError("RENDERER_ATOM_TYPE_FORBIDDEN")
|
||||||
|
definitions = {row["name"]: row for row in descriptor["typed_slot_contract"]["slot_definitions"]}
|
||||||
|
if set(slots) - set(definitions):
|
||||||
|
raise ClosedRenderError("RENDERER_UNKNOWN_SLOT")
|
||||||
|
for name, definition in definitions.items():
|
||||||
|
value = slots.get(name)
|
||||||
|
if value is None and definition["cardinality"] in {"EXACTLY_ONE", "ONE_OR_MORE"}:
|
||||||
|
raise ClosedRenderError("RENDERER_REQUIRED_SLOT_MISSING")
|
||||||
|
if isinstance(value, list) != (definition["cardinality"] == "ONE_OR_MORE") and value is not None:
|
||||||
|
raise ClosedRenderError("RENDERER_SLOT_CARDINALITY_MISMATCH")
|
||||||
|
output = _render_frozen(branch, definitions, slots)
|
||||||
|
detached = json.loads(canonical_json_bytes({"branch": branch, "definitions": definitions, "slots": slots}))
|
||||||
|
independent = _render_frozen(detached["branch"], detached["definitions"], detached["slots"])
|
||||||
|
if output.encode("utf-8") != independent.encode("utf-8"):
|
||||||
|
raise ClosedRenderError("RENDERER_INDEPENDENT_RERENDER_MISMATCH")
|
||||||
|
if re.search(r"\{\{[^}]*\}\}", output):
|
||||||
|
raise ClosedRenderError("RENDERER_DANGLING_TEMPLATE_TOKEN")
|
||||||
|
return {
|
||||||
|
"schema_version": "stage2_s2_40_closed_render_result.v1",
|
||||||
|
"renderer_id": descriptor["renderer_id"], "branch_id": branch["branch_id"],
|
||||||
|
"atom_type": atom_type, "consumed_slot_names": sorted(slots),
|
||||||
|
"rendered_text": output, "rendered_sha256": sha256_bytes(output.encode("utf-8")),
|
||||||
|
"renderer_branch_sha256": sha256_bytes(canonical_json_bytes(branch)),
|
||||||
|
"slot_binding_sha256": sha256_bytes(canonical_json_bytes(dict(slots))),
|
||||||
|
"independent_rerender_sha256": sha256_bytes(independent.encode("utf-8")),
|
||||||
|
"normalization_version": descriptor["closed_ast_contract"]["normalization_version"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def provisional_execution_eligible(atom: Mapping[str, Any]) -> bool:
|
||||||
|
return bool(
|
||||||
|
atom.get("proprietary_claim") is True and atom.get("performance_atom") is True
|
||||||
|
and atom.get("atomic_branch_provisional_execution_policy") == "ALLOW"
|
||||||
|
and atom.get("approved_authority_ref_is_valid") is True
|
||||||
|
and atom.get("dependent_on_constitutive_judgment") is False
|
||||||
|
and atom.get("renderer_id") not in {"R03", "R05", "R06"}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["ClosedRenderError", "canonical_json_bytes", "escape_slot_value", "normalize_text", "provisional_execution_eligible", "render_closed", "select_branch", "sha256_bytes", "validate_descriptor"]
|
||||||
+1
-1
@@ -149,7 +149,7 @@ _RAW_VALUE_UNSET = object()
|
|||||||
# literal placeholders in the offline parity mirror and its unit tests.
|
# literal placeholders in the offline parity mirror and its unit tests.
|
||||||
INLINE_USER_HASH = "{{__user_hash__}}"
|
INLINE_USER_HASH = "{{__user_hash__}}"
|
||||||
INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}"
|
INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}"
|
||||||
EXPECTED_STAGE2_RELEASE_SHA256 = "579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81"
|
EXPECTED_STAGE2_RELEASE_SHA256 = "9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53"
|
||||||
INLINE_REQUEST_PATH = "stage2_control/s2_00_request.json"
|
INLINE_REQUEST_PATH = "stage2_control/s2_00_request.json"
|
||||||
INLINE_STAGE2_ASSET_ROOT = "Default_Agent/Stage_2_Clean"
|
INLINE_STAGE2_ASSET_ROOT = "Default_Agent/Stage_2_Clean"
|
||||||
INLINE_STAGE2_RELEASE_PATH = (
|
INLINE_STAGE2_RELEASE_PATH = (
|
||||||
|
|||||||
+1
-1
@@ -149,7 +149,7 @@ _RAW_VALUE_UNSET = object()
|
|||||||
# literal placeholders in the offline parity mirror and its unit tests.
|
# literal placeholders in the offline parity mirror and its unit tests.
|
||||||
INLINE_USER_HASH = "{{__user_hash__}}"
|
INLINE_USER_HASH = "{{__user_hash__}}"
|
||||||
INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}"
|
INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}"
|
||||||
EXPECTED_STAGE2_RELEASE_SHA256 = "579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81"
|
EXPECTED_STAGE2_RELEASE_SHA256 = "9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53"
|
||||||
INLINE_REQUEST_PATH = "stage2_control/s2_00_request.json"
|
INLINE_REQUEST_PATH = "stage2_control/s2_00_request.json"
|
||||||
INLINE_STAGE2_ASSET_ROOT = "Default_Agent/Stage_2_Clean"
|
INLINE_STAGE2_ASSET_ROOT = "Default_Agent/Stage_2_Clean"
|
||||||
INLINE_STAGE2_RELEASE_PATH = (
|
INLINE_STAGE2_RELEASE_PATH = (
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ from typing import Any, Iterable, Mapping, Sequence
|
|||||||
|
|
||||||
WORKFLOW_ID = "S2_20"
|
WORKFLOW_ID = "S2_20"
|
||||||
ALGORITHM_VERSION = "s2_20_relief_plan/1.0.0"
|
ALGORITHM_VERSION = "s2_20_relief_plan/1.0.0"
|
||||||
EXPECTED_STAGE2_RELEASE_SHA256 = "579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81"
|
EXPECTED_STAGE2_RELEASE_SHA256 = "9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53"
|
||||||
LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
||||||
WEAVIATE_MCP_URL = "https://weaviate.eroomai.com/mcp"
|
WEAVIATE_MCP_URL = "https://weaviate.eroomai.com/mcp"
|
||||||
MCP_PROTOCOL_VERSION = "2025-03-26"
|
MCP_PROTOCOL_VERSION = "2025-03-26"
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ from typing import Any, Iterable, Mapping, Sequence
|
|||||||
|
|
||||||
WORKFLOW_ID = "S2_20"
|
WORKFLOW_ID = "S2_20"
|
||||||
ALGORITHM_VERSION = "s2_20_relief_plan/1.0.0"
|
ALGORITHM_VERSION = "s2_20_relief_plan/1.0.0"
|
||||||
EXPECTED_STAGE2_RELEASE_SHA256 = "579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81"
|
EXPECTED_STAGE2_RELEASE_SHA256 = "9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53"
|
||||||
LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
||||||
WEAVIATE_MCP_URL = "https://weaviate.eroomai.com/mcp"
|
WEAVIATE_MCP_URL = "https://weaviate.eroomai.com/mcp"
|
||||||
MCP_PROTOCOL_VERSION = "2025-03-26"
|
MCP_PROTOCOL_VERSION = "2025-03-26"
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ import sys
|
|||||||
from typing import Any, Mapping
|
from typing import Any, Mapping
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_STAGE2_RELEASE_SHA256 = "579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81"
|
EXPECTED_STAGE2_RELEASE_SHA256 = "9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53"
|
||||||
LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
||||||
MCP_PROTOCOL_VERSION = "2025-03-26"
|
MCP_PROTOCOL_VERSION = "2025-03-26"
|
||||||
INLINE_USER_HASH = "{{__user_hash__}}"
|
INLINE_USER_HASH = "{{__user_hash__}}"
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ import sys
|
|||||||
from typing import Any, Mapping
|
from typing import Any, Mapping
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_STAGE2_RELEASE_SHA256 = "579e4c896cf16bf2fab1482bc6327319035b1c320528bb818d36d3f9036c1b81"
|
EXPECTED_STAGE2_RELEASE_SHA256 = "9fa85bb94c5f14f675dcdaa0cf94d06745b21675d97797539ffc14015dcc9f53"
|
||||||
LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
|
||||||
MCP_PROTOCOL_VERSION = "2025-03-26"
|
MCP_PROTOCOL_VERSION = "2025-03-26"
|
||||||
INLINE_USER_HASH = "{{__user_hash__}}"
|
INLINE_USER_HASH = "{{__user_hash__}}"
|
||||||
|
|||||||
+3518
File diff suppressed because it is too large
Load Diff
+3518
File diff suppressed because it is too large
Load Diff
+575
@@ -0,0 +1,575 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""Closed S2_40 V01-V18 and review/commit state oracle."""
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from typing import Any, Mapping, Sequence
|
||||||
|
|
||||||
|
|
||||||
|
INVARIANT_IDS = tuple(f"V{index:02d}" for index in range(1, 19))
|
||||||
|
IMPACT_SCOPES = {"OK", "REVIEW_REQUIRED", "INCOMPLETE"}
|
||||||
|
REQUIRED_LEGAL_GATES = {
|
||||||
|
"binding", "authority", "renderer_branch", "rule_sub_rule", "review_policy",
|
||||||
|
"case_type_coverage_137", "corpus", "law_value", "calculator", "required_receipts",
|
||||||
|
}
|
||||||
|
HEX64 = re.compile(r"^[a-f0-9]{64}$")
|
||||||
|
CANDIDATE_MATERIAL_KEYS = {
|
||||||
|
"run_binding_digest", "dependency_snapshot_sha256", "candidate_manifest_core_sha256",
|
||||||
|
"document_sha256s", "attorney_worknotes_core_sha256", "final_issue_ledger_sha256",
|
||||||
|
"assumption_ledger_sha256", "validation_core_sha256",
|
||||||
|
"ordered_source_dependency_snapshot_digest",
|
||||||
|
}
|
||||||
|
DOCUMENT_DIGEST_KEYS = {"claim_relief", "claim_cause", "pleading_draft"}
|
||||||
|
STATUS_ONLY_PATHS = (
|
||||||
|
"ingress/ingress_status.json",
|
||||||
|
"ingress/technical_diagnostic.json",
|
||||||
|
"ingress/stage1_input_manifest.json",
|
||||||
|
"ingress/intake_report.json",
|
||||||
|
"review/issue_ledger.base.json",
|
||||||
|
)
|
||||||
|
REVIEW_RECEIPT_KEYS = {
|
||||||
|
"schema_version", "receipt_id", "request_id", "receipt_type",
|
||||||
|
"candidate_content_digest", "review_subject_digest", "finding_ids", "scope_ids",
|
||||||
|
"reviewer_role", "reviewer_qualification", "issuer_id", "key_id",
|
||||||
|
"signature_algorithm", "signed_material_digest",
|
||||||
|
"external_verification_attestation_sha256", "issued_at", "expires_at",
|
||||||
|
"revocation_status", "cryptographic_verification_status", "review_disposition",
|
||||||
|
"change_scope", "reason_codes",
|
||||||
|
}
|
||||||
|
TRUSTED_REVIEW_ISSUER = "AGENTBACKEND_STAGE2_REVIEW_AUTHORITY"
|
||||||
|
TRUSTED_REVIEW_KEY_IDS = frozenset({"AGENTBACKEND_STAGE2_REVIEW_KEY_V1"})
|
||||||
|
TRUSTED_REVIEW_SIGNATURE_ALGORITHM = "AGENTBACKEND_TRUSTED_ATTESTATION_V1"
|
||||||
|
TRUSTED_REVIEW_ROLE = "KOREAN_LAWYER"
|
||||||
|
TRUSTED_REVIEW_QUALIFICATION = "QUALIFIED_KOREAN_LAWYER"
|
||||||
|
|
||||||
|
|
||||||
|
class InvariantError(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class JsonSchemaValidationError(InvariantError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def canonical_json_bytes(value: Any) -> bytes:
|
||||||
|
return (json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n").encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _require_sha256(value: Any, code: str) -> str:
|
||||||
|
if not isinstance(value, str) or HEX64.fullmatch(value) is None:
|
||||||
|
raise InvariantError(code)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def validate_jsonschema_instance(
|
||||||
|
instance: Any, schema: Mapping[str, Any], *,
|
||||||
|
schema_store: Mapping[str, Mapping[str, Any]] | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Validate the closed JSON-Schema subset used by the S2_40 contracts."""
|
||||||
|
store = dict(schema_store or {})
|
||||||
|
root = schema
|
||||||
|
|
||||||
|
def resolve(reference: str, current_root: Mapping[str, Any]) -> tuple[Mapping[str, Any], Mapping[str, Any]]:
|
||||||
|
if "#" in reference:
|
||||||
|
file_name, fragment = reference.split("#", 1)
|
||||||
|
else:
|
||||||
|
file_name, fragment = reference, ""
|
||||||
|
target_root = current_root if not file_name else store.get(file_name)
|
||||||
|
if target_root is None:
|
||||||
|
raise JsonSchemaValidationError(f"SCHEMA_REF_UNRESOLVED::{reference}")
|
||||||
|
target: Any = target_root
|
||||||
|
if fragment:
|
||||||
|
if not fragment.startswith("/"):
|
||||||
|
raise JsonSchemaValidationError(f"SCHEMA_REF_FRAGMENT_INVALID::{reference}")
|
||||||
|
for token in fragment[1:].split("/"):
|
||||||
|
token = token.replace("~1", "/").replace("~0", "~")
|
||||||
|
if not isinstance(target, Mapping) or token not in target:
|
||||||
|
raise JsonSchemaValidationError(f"SCHEMA_REF_UNRESOLVED::{reference}")
|
||||||
|
target = target[token]
|
||||||
|
if not isinstance(target, Mapping):
|
||||||
|
raise JsonSchemaValidationError(f"SCHEMA_REF_TARGET_INVALID::{reference}")
|
||||||
|
return target, target_root
|
||||||
|
|
||||||
|
def type_matches(value: Any, type_name: str) -> bool:
|
||||||
|
return {
|
||||||
|
"object": isinstance(value, Mapping),
|
||||||
|
"array": isinstance(value, list),
|
||||||
|
"string": isinstance(value, str),
|
||||||
|
"integer": isinstance(value, int) and not isinstance(value, bool),
|
||||||
|
"number": isinstance(value, (int, float)) and not isinstance(value, bool),
|
||||||
|
"boolean": isinstance(value, bool),
|
||||||
|
"null": value is None,
|
||||||
|
}.get(type_name, False)
|
||||||
|
|
||||||
|
def check(value: Any, rule: Mapping[str, Any], current_root: Mapping[str, Any], path: str) -> None:
|
||||||
|
if "$ref" in rule:
|
||||||
|
target, target_root = resolve(str(rule["$ref"]), current_root)
|
||||||
|
check(value, target, target_root, path)
|
||||||
|
return
|
||||||
|
if "allOf" in rule:
|
||||||
|
for index, branch in enumerate(rule["allOf"]):
|
||||||
|
check(value, branch, current_root, f"{path}/allOf/{index}")
|
||||||
|
if "oneOf" in rule:
|
||||||
|
successes = 0
|
||||||
|
for branch in rule["oneOf"]:
|
||||||
|
try:
|
||||||
|
check(value, branch, current_root, path)
|
||||||
|
successes += 1
|
||||||
|
except JsonSchemaValidationError:
|
||||||
|
pass
|
||||||
|
if successes != 1:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:ONE_OF_COUNT::{successes}")
|
||||||
|
if "anyOf" in rule:
|
||||||
|
for branch in rule["anyOf"]:
|
||||||
|
try:
|
||||||
|
check(value, branch, current_root, path)
|
||||||
|
break
|
||||||
|
except JsonSchemaValidationError:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:ANY_OF_NO_MATCH")
|
||||||
|
if "not" in rule:
|
||||||
|
try:
|
||||||
|
check(value, rule["not"], current_root, path)
|
||||||
|
except JsonSchemaValidationError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:NOT_SCHEMA_MATCHED")
|
||||||
|
if "if" in rule:
|
||||||
|
try:
|
||||||
|
check(value, rule["if"], current_root, path)
|
||||||
|
matched = True
|
||||||
|
except JsonSchemaValidationError:
|
||||||
|
matched = False
|
||||||
|
if matched and "then" in rule:
|
||||||
|
check(value, rule["then"], current_root, path)
|
||||||
|
if not matched and "else" in rule:
|
||||||
|
check(value, rule["else"], current_root, path)
|
||||||
|
if "const" in rule and value != rule["const"]:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:CONST_MISMATCH")
|
||||||
|
if "enum" in rule and value not in rule["enum"]:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:ENUM_MISMATCH")
|
||||||
|
declared_type = rule.get("type")
|
||||||
|
if declared_type is not None:
|
||||||
|
allowed = [declared_type] if isinstance(declared_type, str) else list(declared_type)
|
||||||
|
if not any(type_matches(value, item) for item in allowed):
|
||||||
|
raise JsonSchemaValidationError(f"{path}:TYPE_MISMATCH")
|
||||||
|
if isinstance(value, str):
|
||||||
|
if len(value) < int(rule.get("minLength", 0)):
|
||||||
|
raise JsonSchemaValidationError(f"{path}:MIN_LENGTH")
|
||||||
|
if "pattern" in rule and re.search(str(rule["pattern"]), value) is None:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:PATTERN_MISMATCH")
|
||||||
|
if rule.get("format") == "date-time":
|
||||||
|
try:
|
||||||
|
_parse_review_time(value, f"{path}:DATETIME_INVALID")
|
||||||
|
except InvariantError as exc:
|
||||||
|
raise JsonSchemaValidationError(str(exc)) from exc
|
||||||
|
if isinstance(value, Mapping):
|
||||||
|
required = rule.get("required", [])
|
||||||
|
missing = [key for key in required if key not in value]
|
||||||
|
if missing:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:REQUIRED_MISSING::{','.join(missing)}")
|
||||||
|
properties = rule.get("properties", {})
|
||||||
|
if rule.get("additionalProperties") is False:
|
||||||
|
extras = sorted(set(value) - set(properties))
|
||||||
|
if extras:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:ADDITIONAL_PROPERTIES::{','.join(extras)}")
|
||||||
|
for key, child in properties.items():
|
||||||
|
if key in value:
|
||||||
|
check(value[key], child, current_root, f"{path}/{key}")
|
||||||
|
if isinstance(value, list):
|
||||||
|
if len(value) < int(rule.get("minItems", 0)):
|
||||||
|
raise JsonSchemaValidationError(f"{path}:MIN_ITEMS")
|
||||||
|
if "maxItems" in rule and len(value) > int(rule["maxItems"]):
|
||||||
|
raise JsonSchemaValidationError(f"{path}:MAX_ITEMS")
|
||||||
|
if rule.get("uniqueItems") and len({canonical_json_bytes(item) for item in value}) != len(value):
|
||||||
|
raise JsonSchemaValidationError(f"{path}:UNIQUE_ITEMS")
|
||||||
|
if "items" in rule:
|
||||||
|
for index, item in enumerate(value):
|
||||||
|
check(item, rule["items"], current_root, f"{path}/{index}")
|
||||||
|
if isinstance(value, (int, float)) and not isinstance(value, bool) and "minimum" in rule and value < rule["minimum"]:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:MINIMUM")
|
||||||
|
|
||||||
|
check(instance, root, root, "$")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_candidate_material(material: Mapping[str, Any]) -> None:
|
||||||
|
if set(material) != CANDIDATE_MATERIAL_KEYS:
|
||||||
|
raise InvariantError("CANDIDATE_DIGEST_MATERIAL_SHAPE")
|
||||||
|
for key in CANDIDATE_MATERIAL_KEYS - {"document_sha256s"}:
|
||||||
|
_require_sha256(material[key], f"CANDIDATE_{key.upper()}_INVALID")
|
||||||
|
documents = material["document_sha256s"]
|
||||||
|
if not isinstance(documents, Mapping) or set(documents) != DOCUMENT_DIGEST_KEYS:
|
||||||
|
raise InvariantError("CANDIDATE_DOCUMENT_DIGEST_SET_NOT_EXACT")
|
||||||
|
for key, value in documents.items():
|
||||||
|
_require_sha256(value, f"CANDIDATE_DOCUMENT_{key.upper()}_INVALID")
|
||||||
|
|
||||||
|
|
||||||
|
def review_subject_digest(
|
||||||
|
*, candidate_digest: str, review_request_core_sha256: str,
|
||||||
|
lawyer_review_packet_core_sha256: str, validation_envelope_core_sha256: str,
|
||||||
|
finding_ids: Sequence[str], scope_ids: Sequence[str], policy_sha256: str,
|
||||||
|
release_sha256s: Mapping[str, str],
|
||||||
|
) -> str:
|
||||||
|
for value, code in (
|
||||||
|
(candidate_digest, "REVIEW_CANDIDATE_DIGEST_INVALID"),
|
||||||
|
(review_request_core_sha256, "REVIEW_REQUEST_CORE_HASH_INVALID"),
|
||||||
|
(lawyer_review_packet_core_sha256, "REVIEW_PACKET_CORE_HASH_INVALID"),
|
||||||
|
(validation_envelope_core_sha256, "REVIEW_VALIDATION_HASH_INVALID"),
|
||||||
|
(policy_sha256, "REVIEW_POLICY_HASH_INVALID"),
|
||||||
|
):
|
||||||
|
_require_sha256(value, code)
|
||||||
|
expected_release_keys = {"parent", "authority", "corpus", "case_type_coverage"}
|
||||||
|
if set(release_sha256s) != expected_release_keys:
|
||||||
|
raise InvariantError("REVIEW_RELEASE_SNAPSHOT_NOT_EXACT")
|
||||||
|
for value in release_sha256s.values():
|
||||||
|
_require_sha256(value, "REVIEW_RELEASE_HASH_INVALID")
|
||||||
|
if len(finding_ids) != len(set(finding_ids)) or len(scope_ids) != len(set(scope_ids)):
|
||||||
|
raise InvariantError("REVIEW_SUBJECT_SET_DUPLICATE")
|
||||||
|
subject_core = {
|
||||||
|
"schema_version": "stage2_s2_40_review_subject.v1",
|
||||||
|
"domain_separator": "STAGE2_S2_40_REVIEW_SUBJECT_V1",
|
||||||
|
"candidate_content_digest": candidate_digest,
|
||||||
|
"review_request_core_sha256": review_request_core_sha256,
|
||||||
|
"lawyer_review_packet_core_sha256": lawyer_review_packet_core_sha256,
|
||||||
|
"validation_envelope_core_sha256": validation_envelope_core_sha256,
|
||||||
|
"finding_ids": sorted(finding_ids), "scope_ids": sorted(scope_ids),
|
||||||
|
"policy_sha256": policy_sha256,
|
||||||
|
"release_sha256s": {key: release_sha256s[key] for key in sorted(release_sha256s)},
|
||||||
|
}
|
||||||
|
return hashlib.sha256(canonical_json_bytes(subject_core)).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def validate_status_only_paths(paths: Sequence[str]) -> None:
|
||||||
|
if tuple(paths) != STATUS_ONLY_PATHS:
|
||||||
|
raise InvariantError("STATUS_ONLY_INPUT_SET_NOT_EXACT_FIVE")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_reference_closure(required_refs: Sequence[str], available_refs: Sequence[str]) -> None:
|
||||||
|
if len(required_refs) != len(set(required_refs)) or len(available_refs) != len(set(available_refs)):
|
||||||
|
raise InvariantError("REFERENCE_SET_DUPLICATE")
|
||||||
|
missing = sorted(set(required_refs) - set(available_refs))
|
||||||
|
if missing:
|
||||||
|
raise InvariantError("REFERENCE_DANGLING::" + ",".join(missing))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_readback_rows(expected_rows: Sequence[Mapping[str, Any]], observed_rows: Sequence[Mapping[str, Any]]) -> None:
|
||||||
|
def normalize(rows: Sequence[Mapping[str, Any]]) -> list[tuple[str, str, str, int, str | None]]:
|
||||||
|
normalized: list[tuple[str, str, str, int, str | None]] = []
|
||||||
|
for row in rows:
|
||||||
|
if set(row) != {"path", "raw_sha256", "content_type", "size_bytes", "schema_ref"}:
|
||||||
|
raise InvariantError("READBACK_ROW_SHAPE_INVALID")
|
||||||
|
path = row["path"]
|
||||||
|
if not isinstance(path, str) or not path.startswith("final/") or ".." in path.split("/"):
|
||||||
|
raise InvariantError("READBACK_PATH_INVALID")
|
||||||
|
content_type, schema_ref, size = row["content_type"], row["schema_ref"], row["size_bytes"]
|
||||||
|
if not isinstance(content_type, str) or not content_type or not isinstance(size, int) or size < 1:
|
||||||
|
raise InvariantError("READBACK_METADATA_INVALID")
|
||||||
|
if schema_ref is not None and (not isinstance(schema_ref, str) or not schema_ref):
|
||||||
|
raise InvariantError("READBACK_SCHEMA_REF_INVALID")
|
||||||
|
normalized.append((path, _require_sha256(row["raw_sha256"], "READBACK_HASH_INVALID"), content_type, size, schema_ref))
|
||||||
|
if len({row[0] for row in normalized}) != len(normalized):
|
||||||
|
raise InvariantError("READBACK_PATH_DUPLICATE")
|
||||||
|
return sorted(normalized)
|
||||||
|
if normalize(expected_rows) != normalize(observed_rows):
|
||||||
|
raise InvariantError("READBACK_MISMATCH_NO_FINAL_BARRIER")
|
||||||
|
|
||||||
|
|
||||||
|
def run_invariants(checks: Mapping[str, Mapping[str, Any]]) -> list[dict[str, Any]]:
|
||||||
|
if set(checks) != set(INVARIANT_IDS):
|
||||||
|
raise InvariantError("INVARIANT_SET_NOT_EXACT_V01_V18")
|
||||||
|
results: list[dict[str, Any]] = []
|
||||||
|
for invariant_id in INVARIANT_IDS:
|
||||||
|
check = checks[invariant_id]
|
||||||
|
if not isinstance(check, Mapping):
|
||||||
|
raise InvariantError("INVARIANT_CHECK_NOT_OBJECT")
|
||||||
|
required_check_keys = {
|
||||||
|
"evaluable", "passed", "impact_scope", "source_refs", "finding_ids",
|
||||||
|
"expected", "observed", "reason_codes",
|
||||||
|
}
|
||||||
|
if set(check) != required_check_keys:
|
||||||
|
raise InvariantError(f"{invariant_id}_CHECK_SHAPE_NOT_EXACT")
|
||||||
|
evaluable = check["evaluable"]
|
||||||
|
if not isinstance(evaluable, bool):
|
||||||
|
raise InvariantError(f"{invariant_id}_EVALUABLE_NOT_BOOLEAN")
|
||||||
|
if evaluable:
|
||||||
|
if not isinstance(check["passed"], bool):
|
||||||
|
raise InvariantError(f"{invariant_id}_PASSED_NOT_BOOLEAN")
|
||||||
|
if check["observed"] is None or check["passed"] != (check["observed"] == check["expected"]):
|
||||||
|
raise InvariantError(f"{invariant_id}_OBSERVATION_ORACLE_MISMATCH")
|
||||||
|
evaluation = "PASS" if check["passed"] else "FAIL"
|
||||||
|
else:
|
||||||
|
if check["passed"] not in {False, None} or check["observed"] is not None:
|
||||||
|
raise InvariantError(f"{invariant_id}_UNEVALUABLE_OBSERVATION_INVALID")
|
||||||
|
evaluation = "UNEVALUABLE"
|
||||||
|
scope = check.get("impact_scope", "OK" if evaluation == "PASS" else "REVIEW_REQUIRED")
|
||||||
|
if scope not in IMPACT_SCOPES or (evaluation == "PASS" and scope != "OK"):
|
||||||
|
raise InvariantError("INVARIANT_IMPACT_SCOPE_INVALID")
|
||||||
|
reason_values = check["reason_codes"]
|
||||||
|
source_values = check["source_refs"]
|
||||||
|
finding_values = check["finding_ids"]
|
||||||
|
for values in (reason_values, source_values, finding_values):
|
||||||
|
if (not isinstance(values, list) or len(values) != len(set(values))
|
||||||
|
or not all(isinstance(value, str) and value for value in values)):
|
||||||
|
raise InvariantError("INVARIANT_STRING_SET_INVALID")
|
||||||
|
if not source_values:
|
||||||
|
raise InvariantError(f"{invariant_id}_SOURCE_EVIDENCE_REQUIRED")
|
||||||
|
reason_codes = sorted(set(reason_values))
|
||||||
|
if evaluation == "PASS" and (reason_codes or finding_values):
|
||||||
|
raise InvariantError(f"{invariant_id}_PASS_FINDING_FORBIDDEN")
|
||||||
|
if evaluation != "PASS" and (not reason_codes or not finding_values):
|
||||||
|
raise InvariantError(f"{invariant_id}_{evaluation}_FINDING_REQUIRED")
|
||||||
|
results.append({
|
||||||
|
"invariant_id": invariant_id, "evaluation_status": evaluation,
|
||||||
|
"finding_ids": sorted(set(finding_values)), "impact_scope": scope,
|
||||||
|
"source_refs": sorted(set(source_values)),
|
||||||
|
"expected": check["expected"], "observed": check["observed"],
|
||||||
|
"reason_codes": reason_codes, "validator_algorithm_id": f"S2_40::{invariant_id}::V1",
|
||||||
|
})
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def aggregate_status(results: Sequence[Mapping[str, Any]], *, compile_mode: str | None, legal_gates: Mapping[str, bool]) -> dict[str, str]:
|
||||||
|
if [row.get("invariant_id") for row in results] != list(INVARIANT_IDS):
|
||||||
|
raise InvariantError("INVARIANT_RESULT_ORDER_OR_SET_INVALID")
|
||||||
|
for row in results:
|
||||||
|
evaluation, scope = row.get("evaluation_status"), row.get("impact_scope")
|
||||||
|
if evaluation not in {"PASS", "FAIL", "UNEVALUABLE"} or scope not in IMPACT_SCOPES:
|
||||||
|
raise InvariantError("INVARIANT_RESULT_ENUM_INVALID")
|
||||||
|
if evaluation == "PASS" and scope != "OK":
|
||||||
|
raise InvariantError("INVARIANT_PASS_SCOPE_INVALID")
|
||||||
|
scopes = {row.get("impact_scope") for row in results}
|
||||||
|
if "INCOMPLETE" in scopes:
|
||||||
|
run = "TECHNICAL_INCOMPLETE"
|
||||||
|
elif "REVIEW_REQUIRED" in scopes or any(row.get("evaluation_status") == "UNEVALUABLE" for row in results):
|
||||||
|
run = "TECHNICAL_REVIEW_REQUIRED"
|
||||||
|
else:
|
||||||
|
run = "CONSISTENT"
|
||||||
|
if run == "TECHNICAL_INCOMPLETE":
|
||||||
|
artifact, legal = "NOT_PRODUCED", "NOT_ASSESSED"
|
||||||
|
elif run == "TECHNICAL_REVIEW_REQUIRED":
|
||||||
|
artifact, legal = "TECHNICAL_REVIEW_REQUIRED", "LAWYER_REVIEW_REQUIRED"
|
||||||
|
else:
|
||||||
|
artifact = "CONSISTENT"
|
||||||
|
gates = set(legal_gates) == REQUIRED_LEGAL_GATES and all(legal_gates.values())
|
||||||
|
all_pass = all(row.get("evaluation_status") == "PASS" for row in results)
|
||||||
|
legal = "READY_FOR_LAWYER_FILING_DECISION" if compile_mode == "PRODUCTION" and all_pass and gates else "LAWYER_REVIEW_REQUIRED"
|
||||||
|
return {"run_technical_status": run, "artifact_technical_status": artifact, "legal_readiness": legal}
|
||||||
|
|
||||||
|
|
||||||
|
def candidate_content_digest(material: Mapping[str, Any]) -> str:
|
||||||
|
forbidden = {"candidate_content_digest", "review_request", "review_receipt", "commit_intent", "commit_result", "run_status", "artifact_set_digest"}
|
||||||
|
if forbidden & set(material):
|
||||||
|
raise InvariantError("CANDIDATE_DIGEST_MATERIAL_CYCLE")
|
||||||
|
validate_candidate_material(material)
|
||||||
|
digest_core = {
|
||||||
|
"schema_version": "stage2_s2_40_candidate_content_digest.v1",
|
||||||
|
"domain_separator": "STAGE2_S2_40_CANDIDATE_CONTENT_V1",
|
||||||
|
**material,
|
||||||
|
}
|
||||||
|
return hashlib.sha256(canonical_json_bytes(digest_core)).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def artifact_set_digest(rows: Sequence[Mapping[str, Any]]) -> str:
|
||||||
|
ordered = sorted(rows, key=lambda row: row["path"])
|
||||||
|
paths = [row["path"] for row in ordered]
|
||||||
|
if len(paths) != len(set(paths)):
|
||||||
|
raise InvariantError("ARTIFACT_PATH_DUPLICATE")
|
||||||
|
for row in ordered:
|
||||||
|
if set(row) != {"path", "raw_sha256", "content_type", "size_bytes", "schema_ref"}:
|
||||||
|
raise InvariantError("ARTIFACT_ROW_SHAPE_INVALID")
|
||||||
|
_require_sha256(row["raw_sha256"], "ARTIFACT_ROW_HASH_INVALID")
|
||||||
|
if not isinstance(row["content_type"], str) or not row["content_type"]:
|
||||||
|
raise InvariantError("ARTIFACT_ROW_CONTENT_TYPE_INVALID")
|
||||||
|
if not isinstance(row["size_bytes"], int) or row["size_bytes"] < 1:
|
||||||
|
raise InvariantError("ARTIFACT_ROW_SIZE_INVALID")
|
||||||
|
if row["schema_ref"] is not None and (not isinstance(row["schema_ref"], str) or not row["schema_ref"]):
|
||||||
|
raise InvariantError("ARTIFACT_ROW_SCHEMA_REF_INVALID")
|
||||||
|
return hashlib.sha256(canonical_json_bytes(ordered)).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def derive_review_requirements(policy: Mapping[str, Any], active_tags: Sequence[str], runtime_triggers: Sequence[str]) -> list[str]:
|
||||||
|
if policy.get("status") != "APPROVED_LEGAL_CONTENT" or policy.get("execution_eligible") is not True:
|
||||||
|
return ["STANDARD_ATTORNEY_REVIEW"]
|
||||||
|
allowed_tags = set(policy["final_review_contract"]["allowed_review_tags"])
|
||||||
|
if not set(active_tags) <= allowed_tags:
|
||||||
|
raise InvariantError("REVIEW_TAG_UNKNOWN")
|
||||||
|
required = set(policy["final_review_contract"]["base_required_receipt_types"])
|
||||||
|
for row in policy.get("policy_rows", []):
|
||||||
|
if set(row.get("match_tags", [])) <= set(active_tags) and set(row.get("runtime_triggers", [])) <= set(runtime_triggers):
|
||||||
|
required.update(row.get("required_receipt_types", []))
|
||||||
|
return sorted(required)
|
||||||
|
|
||||||
|
|
||||||
|
def review_receipt_signed_material_digest(receipt: Mapping[str, Any]) -> str:
|
||||||
|
"""Hash the exact inline receipt preimage, including detached-adapter evidence."""
|
||||||
|
keys = (
|
||||||
|
"request_id", "candidate_content_digest", "review_subject_digest", "receipt_type",
|
||||||
|
"finding_ids", "scope_ids", "reviewer_role", "reviewer_qualification",
|
||||||
|
"issuer_id", "key_id", "signature_algorithm",
|
||||||
|
"external_verification_attestation_sha256", "issued_at", "expires_at",
|
||||||
|
"revocation_status", "cryptographic_verification_status",
|
||||||
|
"review_disposition", "change_scope", "reason_codes",
|
||||||
|
)
|
||||||
|
if any(key not in receipt for key in keys):
|
||||||
|
raise InvariantError("REVIEW_SIGNED_MATERIAL_FIELD_MISSING")
|
||||||
|
material = ["STAGE2_S2_40_REVIEW_RECEIPT_V1", *[receipt[key] for key in keys]]
|
||||||
|
return hashlib.sha256(canonical_json_bytes(material)).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_review_time(value: Any, code: str) -> datetime:
|
||||||
|
if not isinstance(value, str):
|
||||||
|
raise InvariantError(code)
|
||||||
|
try:
|
||||||
|
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
||||||
|
except ValueError as exc:
|
||||||
|
raise InvariantError(code) from exc
|
||||||
|
if parsed.tzinfo is None:
|
||||||
|
raise InvariantError(code)
|
||||||
|
return parsed.astimezone(timezone.utc)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_review_receipt(
|
||||||
|
receipt: Mapping[str, Any], *, candidate_digest: str,
|
||||||
|
expected_review_subject_digest: str, expected_request_id: str,
|
||||||
|
expected_receipt_type: str, expected_finding_ids: Sequence[str],
|
||||||
|
expected_scope_ids: Sequence[str], now: datetime,
|
||||||
|
) -> list[str]:
|
||||||
|
"""Return closed invalidity codes; an empty list means externally admissible."""
|
||||||
|
reasons: list[str] = []
|
||||||
|
if set(receipt) != REVIEW_RECEIPT_KEYS or receipt.get("schema_version") != "stage2_s2_40_review_receipt.v1":
|
||||||
|
return ["REVIEW_RECEIPT_CLOSED_SHAPE_INVALID"]
|
||||||
|
for key in ("receipt_id", "request_id", "receipt_type", "reviewer_role", "reviewer_qualification", "issuer_id", "key_id", "signature_algorithm"):
|
||||||
|
if not isinstance(receipt.get(key), str) or not receipt[key]:
|
||||||
|
reasons.append(f"REVIEW_RECEIPT_{key.upper()}_INVALID")
|
||||||
|
for key in ("candidate_content_digest", "review_subject_digest", "signed_material_digest", "external_verification_attestation_sha256"):
|
||||||
|
try:
|
||||||
|
_require_sha256(receipt.get(key), f"REVIEW_RECEIPT_{key.upper()}_INVALID")
|
||||||
|
except InvariantError as exc:
|
||||||
|
reasons.append(str(exc))
|
||||||
|
for key, expected in (
|
||||||
|
("candidate_content_digest", candidate_digest),
|
||||||
|
("review_subject_digest", expected_review_subject_digest),
|
||||||
|
("request_id", expected_request_id),
|
||||||
|
("receipt_type", expected_receipt_type),
|
||||||
|
("finding_ids", sorted(expected_finding_ids)),
|
||||||
|
("scope_ids", sorted(expected_scope_ids)),
|
||||||
|
):
|
||||||
|
observed = sorted(receipt[key]) if key in {"finding_ids", "scope_ids"} and isinstance(receipt[key], list) else receipt[key]
|
||||||
|
if observed != expected:
|
||||||
|
reasons.append(f"REVIEW_RECEIPT_{key.upper()}_MISMATCH")
|
||||||
|
for key in ("finding_ids", "scope_ids", "reason_codes"):
|
||||||
|
values = receipt.get(key)
|
||||||
|
if not isinstance(values, list) or values != sorted(set(values)) or not all(isinstance(value, str) and value for value in values):
|
||||||
|
reasons.append(f"REVIEW_RECEIPT_{key.upper()}_NOT_SORTED_SET")
|
||||||
|
if receipt.get("reviewer_role") != TRUSTED_REVIEW_ROLE:
|
||||||
|
reasons.append("REVIEW_RECEIPT_ROLE_UNTRUSTED")
|
||||||
|
if receipt.get("reviewer_qualification") != TRUSTED_REVIEW_QUALIFICATION:
|
||||||
|
reasons.append("REVIEW_RECEIPT_QUALIFICATION_UNTRUSTED")
|
||||||
|
if receipt.get("issuer_id") != TRUSTED_REVIEW_ISSUER:
|
||||||
|
reasons.append("REVIEW_RECEIPT_ISSUER_UNTRUSTED")
|
||||||
|
if receipt.get("key_id") not in TRUSTED_REVIEW_KEY_IDS:
|
||||||
|
reasons.append("REVIEW_RECEIPT_KEY_UNTRUSTED")
|
||||||
|
if receipt.get("signature_algorithm") != TRUSTED_REVIEW_SIGNATURE_ALGORITHM:
|
||||||
|
reasons.append("REVIEW_RECEIPT_SIGNATURE_ALGORITHM_UNTRUSTED")
|
||||||
|
if receipt.get("cryptographic_verification_status") != "VERIFIED_BY_EXTERNAL_ADAPTER":
|
||||||
|
reasons.append("REVIEW_RECEIPT_EXTERNAL_VERIFICATION_NOT_VERIFIED")
|
||||||
|
if receipt.get("revocation_status") != "NOT_REVOKED":
|
||||||
|
reasons.append("REVIEW_RECEIPT_REVOKED_OR_UNKNOWN")
|
||||||
|
try:
|
||||||
|
issued = _parse_review_time(receipt.get("issued_at"), "REVIEW_RECEIPT_ISSUED_AT_INVALID")
|
||||||
|
expires = _parse_review_time(receipt.get("expires_at"), "REVIEW_RECEIPT_EXPIRES_AT_INVALID")
|
||||||
|
current = now.astimezone(timezone.utc)
|
||||||
|
if issued > current or expires <= current or expires <= issued:
|
||||||
|
reasons.append("REVIEW_RECEIPT_TIME_WINDOW_INVALID")
|
||||||
|
except InvariantError as exc:
|
||||||
|
reasons.append(str(exc))
|
||||||
|
disposition, change_scope = receipt.get("review_disposition"), receipt.get("change_scope")
|
||||||
|
if disposition == "APPROVE_AS_IS" and change_scope != "NONE":
|
||||||
|
reasons.append("REVIEW_RECEIPT_APPROVAL_CHANGE_SCOPE_FORBIDDEN")
|
||||||
|
elif disposition == "CONTENT_CHANGE_REQUIRED" and change_scope not in {
|
||||||
|
"STAGE1_CONTEXT", "LEGAL_JUDGMENT", "PLAN_RULE_CALCULATION_BINDING", "DRAFTING_TEXT_ATOM",
|
||||||
|
}:
|
||||||
|
reasons.append("REVIEW_RECEIPT_CHANGE_SCOPE_INVALID")
|
||||||
|
elif disposition not in {"APPROVE_AS_IS", "CONTENT_CHANGE_REQUIRED"}:
|
||||||
|
reasons.append("REVIEW_RECEIPT_DISPOSITION_INVALID")
|
||||||
|
try:
|
||||||
|
expected_signed = review_receipt_signed_material_digest(receipt)
|
||||||
|
if receipt.get("signed_material_digest") != expected_signed:
|
||||||
|
reasons.append("REVIEW_RECEIPT_SIGNED_MATERIAL_MISMATCH")
|
||||||
|
except InvariantError as exc:
|
||||||
|
reasons.append(str(exc))
|
||||||
|
return sorted(set(reasons))
|
||||||
|
|
||||||
|
|
||||||
|
def transition_review_state(
|
||||||
|
*, candidate_digest: str, required_receipt_types: Sequence[str],
|
||||||
|
receipts: Sequence[Mapping[str, Any]], expected_review_subject_digest: str,
|
||||||
|
expected_request_ids: Mapping[str, str], expected_finding_ids: Sequence[str],
|
||||||
|
expected_scope_ids: Sequence[str], now: datetime,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
_require_sha256(candidate_digest, "REVIEW_CANDIDATE_DIGEST_INVALID")
|
||||||
|
if len(required_receipt_types) != len(set(required_receipt_types)):
|
||||||
|
raise InvariantError("REVIEW_REQUIRED_TYPE_DUPLICATE")
|
||||||
|
_require_sha256(expected_review_subject_digest, "REVIEW_SUBJECT_DIGEST_INVALID")
|
||||||
|
if set(expected_request_ids) != set(required_receipt_types) or any(
|
||||||
|
not isinstance(value, str) or not value for value in expected_request_ids.values()
|
||||||
|
):
|
||||||
|
raise InvariantError("REVIEW_REQUEST_ID_MAP_INVALID")
|
||||||
|
valid: dict[str, Mapping[str, Any]] = {}
|
||||||
|
invalid: list[str] = []
|
||||||
|
invalid_reasons: dict[str, list[str]] = {}
|
||||||
|
seen_receipt_ids: set[str] = set()
|
||||||
|
for receipt in receipts:
|
||||||
|
receipt_id = receipt.get("receipt_id")
|
||||||
|
receipt_type = receipt.get("receipt_type")
|
||||||
|
if not isinstance(receipt_id, str) or not receipt_id or receipt_id in seen_receipt_ids:
|
||||||
|
raise InvariantError("REVIEW_RECEIPT_ID_MISSING_OR_DUPLICATE")
|
||||||
|
seen_receipt_ids.add(receipt_id)
|
||||||
|
reasons = validate_review_receipt(
|
||||||
|
receipt, candidate_digest=candidate_digest,
|
||||||
|
expected_review_subject_digest=expected_review_subject_digest,
|
||||||
|
expected_request_id=expected_request_ids.get(str(receipt_type), ""),
|
||||||
|
expected_receipt_type=str(receipt_type),
|
||||||
|
expected_finding_ids=expected_finding_ids, expected_scope_ids=expected_scope_ids,
|
||||||
|
now=now,
|
||||||
|
)
|
||||||
|
if receipt_type not in required_receipt_types:
|
||||||
|
reasons.append("REVIEW_RECEIPT_TYPE_NOT_REQUIRED")
|
||||||
|
if reasons:
|
||||||
|
invalid.append(receipt_id)
|
||||||
|
invalid_reasons[receipt_id] = sorted(set(reasons))
|
||||||
|
else:
|
||||||
|
if receipt_type in valid:
|
||||||
|
raise InvariantError("REVIEW_RECEIPT_TYPE_DUPLICATE")
|
||||||
|
valid[receipt_type] = receipt
|
||||||
|
routes = {
|
||||||
|
"STAGE1_CONTEXT": (0, "RESTART_FROM_S2_00"),
|
||||||
|
"LEGAL_JUDGMENT": (1, "RESTART_FROM_S2_10"),
|
||||||
|
"PLAN_RULE_CALCULATION_BINDING": (2, "RESTART_FROM_S2_20"),
|
||||||
|
"DRAFTING_TEXT_ATOM": (3, "RESTART_FROM_S2_30"),
|
||||||
|
}
|
||||||
|
changed_scopes = [receipt["change_scope"] for receipt in valid.values() if receipt["review_disposition"] == "CONTENT_CHANGE_REQUIRED"]
|
||||||
|
if changed_scopes:
|
||||||
|
earliest = min(changed_scopes, key=lambda scope: routes[scope][0])
|
||||||
|
return {"workflow_phase": "SUPERSEDED", "route": routes[earliest][1], "invalid_receipt_ids": sorted(invalid), "invalid_receipt_reasons": invalid_reasons}
|
||||||
|
missing = sorted(set(required_receipt_types) - set(valid))
|
||||||
|
if missing or invalid:
|
||||||
|
return {"workflow_phase": "AWAITING_EXTERNAL_REVIEW", "route": "WAIT_EXTERNAL_REVIEW", "missing_receipt_types": missing, "invalid_receipt_ids": sorted(invalid), "invalid_receipt_reasons": invalid_reasons}
|
||||||
|
if any(receipt.get("review_disposition") != "APPROVE_AS_IS" for receipt in valid.values()):
|
||||||
|
raise InvariantError("REVIEW_DISPOSITION_INVALID")
|
||||||
|
return {"workflow_phase": "READY_TO_COMMIT", "route": "CONTINUE_TO_COMMIT", "invalid_receipt_ids": [], "invalid_receipt_reasons": {}}
|
||||||
|
|
||||||
|
|
||||||
|
def commit_write_order(final_paths: Sequence[str]) -> list[str]:
|
||||||
|
if len(final_paths) != len(set(final_paths)):
|
||||||
|
raise InvariantError("COMMIT_TARGET_DUPLICATE")
|
||||||
|
if any(not path.startswith("final/") or ".." in path.split("/") for path in final_paths):
|
||||||
|
raise InvariantError("COMMIT_TARGET_OUTSIDE_FINAL_ROOT")
|
||||||
|
return ["commit/commit_intent.json", *sorted(final_paths), "commit/stage2_commit_result.json", "control/run_status.json"]
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["INVARIANT_IDS", "InvariantError", "JsonSchemaValidationError", "aggregate_status", "artifact_set_digest", "candidate_content_digest", "commit_write_order", "derive_review_requirements", "review_receipt_signed_material_digest", "review_subject_digest", "run_invariants", "transition_review_state", "validate_candidate_material", "validate_jsonschema_instance", "validate_readback_rows", "validate_reference_closure", "validate_review_receipt", "validate_status_only_paths"]
|
||||||
+575
@@ -0,0 +1,575 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""Closed S2_40 V01-V18 and review/commit state oracle."""
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from typing import Any, Mapping, Sequence
|
||||||
|
|
||||||
|
|
||||||
|
INVARIANT_IDS = tuple(f"V{index:02d}" for index in range(1, 19))
|
||||||
|
IMPACT_SCOPES = {"OK", "REVIEW_REQUIRED", "INCOMPLETE"}
|
||||||
|
REQUIRED_LEGAL_GATES = {
|
||||||
|
"binding", "authority", "renderer_branch", "rule_sub_rule", "review_policy",
|
||||||
|
"case_type_coverage_137", "corpus", "law_value", "calculator", "required_receipts",
|
||||||
|
}
|
||||||
|
HEX64 = re.compile(r"^[a-f0-9]{64}$")
|
||||||
|
CANDIDATE_MATERIAL_KEYS = {
|
||||||
|
"run_binding_digest", "dependency_snapshot_sha256", "candidate_manifest_core_sha256",
|
||||||
|
"document_sha256s", "attorney_worknotes_core_sha256", "final_issue_ledger_sha256",
|
||||||
|
"assumption_ledger_sha256", "validation_core_sha256",
|
||||||
|
"ordered_source_dependency_snapshot_digest",
|
||||||
|
}
|
||||||
|
DOCUMENT_DIGEST_KEYS = {"claim_relief", "claim_cause", "pleading_draft"}
|
||||||
|
STATUS_ONLY_PATHS = (
|
||||||
|
"ingress/ingress_status.json",
|
||||||
|
"ingress/technical_diagnostic.json",
|
||||||
|
"ingress/stage1_input_manifest.json",
|
||||||
|
"ingress/intake_report.json",
|
||||||
|
"review/issue_ledger.base.json",
|
||||||
|
)
|
||||||
|
REVIEW_RECEIPT_KEYS = {
|
||||||
|
"schema_version", "receipt_id", "request_id", "receipt_type",
|
||||||
|
"candidate_content_digest", "review_subject_digest", "finding_ids", "scope_ids",
|
||||||
|
"reviewer_role", "reviewer_qualification", "issuer_id", "key_id",
|
||||||
|
"signature_algorithm", "signed_material_digest",
|
||||||
|
"external_verification_attestation_sha256", "issued_at", "expires_at",
|
||||||
|
"revocation_status", "cryptographic_verification_status", "review_disposition",
|
||||||
|
"change_scope", "reason_codes",
|
||||||
|
}
|
||||||
|
TRUSTED_REVIEW_ISSUER = "AGENTBACKEND_STAGE2_REVIEW_AUTHORITY"
|
||||||
|
TRUSTED_REVIEW_KEY_IDS = frozenset({"AGENTBACKEND_STAGE2_REVIEW_KEY_V1"})
|
||||||
|
TRUSTED_REVIEW_SIGNATURE_ALGORITHM = "AGENTBACKEND_TRUSTED_ATTESTATION_V1"
|
||||||
|
TRUSTED_REVIEW_ROLE = "KOREAN_LAWYER"
|
||||||
|
TRUSTED_REVIEW_QUALIFICATION = "QUALIFIED_KOREAN_LAWYER"
|
||||||
|
|
||||||
|
|
||||||
|
class InvariantError(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class JsonSchemaValidationError(InvariantError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def canonical_json_bytes(value: Any) -> bytes:
|
||||||
|
return (json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n").encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _require_sha256(value: Any, code: str) -> str:
|
||||||
|
if not isinstance(value, str) or HEX64.fullmatch(value) is None:
|
||||||
|
raise InvariantError(code)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def validate_jsonschema_instance(
|
||||||
|
instance: Any, schema: Mapping[str, Any], *,
|
||||||
|
schema_store: Mapping[str, Mapping[str, Any]] | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Validate the closed JSON-Schema subset used by the S2_40 contracts."""
|
||||||
|
store = dict(schema_store or {})
|
||||||
|
root = schema
|
||||||
|
|
||||||
|
def resolve(reference: str, current_root: Mapping[str, Any]) -> tuple[Mapping[str, Any], Mapping[str, Any]]:
|
||||||
|
if "#" in reference:
|
||||||
|
file_name, fragment = reference.split("#", 1)
|
||||||
|
else:
|
||||||
|
file_name, fragment = reference, ""
|
||||||
|
target_root = current_root if not file_name else store.get(file_name)
|
||||||
|
if target_root is None:
|
||||||
|
raise JsonSchemaValidationError(f"SCHEMA_REF_UNRESOLVED::{reference}")
|
||||||
|
target: Any = target_root
|
||||||
|
if fragment:
|
||||||
|
if not fragment.startswith("/"):
|
||||||
|
raise JsonSchemaValidationError(f"SCHEMA_REF_FRAGMENT_INVALID::{reference}")
|
||||||
|
for token in fragment[1:].split("/"):
|
||||||
|
token = token.replace("~1", "/").replace("~0", "~")
|
||||||
|
if not isinstance(target, Mapping) or token not in target:
|
||||||
|
raise JsonSchemaValidationError(f"SCHEMA_REF_UNRESOLVED::{reference}")
|
||||||
|
target = target[token]
|
||||||
|
if not isinstance(target, Mapping):
|
||||||
|
raise JsonSchemaValidationError(f"SCHEMA_REF_TARGET_INVALID::{reference}")
|
||||||
|
return target, target_root
|
||||||
|
|
||||||
|
def type_matches(value: Any, type_name: str) -> bool:
|
||||||
|
return {
|
||||||
|
"object": isinstance(value, Mapping),
|
||||||
|
"array": isinstance(value, list),
|
||||||
|
"string": isinstance(value, str),
|
||||||
|
"integer": isinstance(value, int) and not isinstance(value, bool),
|
||||||
|
"number": isinstance(value, (int, float)) and not isinstance(value, bool),
|
||||||
|
"boolean": isinstance(value, bool),
|
||||||
|
"null": value is None,
|
||||||
|
}.get(type_name, False)
|
||||||
|
|
||||||
|
def check(value: Any, rule: Mapping[str, Any], current_root: Mapping[str, Any], path: str) -> None:
|
||||||
|
if "$ref" in rule:
|
||||||
|
target, target_root = resolve(str(rule["$ref"]), current_root)
|
||||||
|
check(value, target, target_root, path)
|
||||||
|
return
|
||||||
|
if "allOf" in rule:
|
||||||
|
for index, branch in enumerate(rule["allOf"]):
|
||||||
|
check(value, branch, current_root, f"{path}/allOf/{index}")
|
||||||
|
if "oneOf" in rule:
|
||||||
|
successes = 0
|
||||||
|
for branch in rule["oneOf"]:
|
||||||
|
try:
|
||||||
|
check(value, branch, current_root, path)
|
||||||
|
successes += 1
|
||||||
|
except JsonSchemaValidationError:
|
||||||
|
pass
|
||||||
|
if successes != 1:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:ONE_OF_COUNT::{successes}")
|
||||||
|
if "anyOf" in rule:
|
||||||
|
for branch in rule["anyOf"]:
|
||||||
|
try:
|
||||||
|
check(value, branch, current_root, path)
|
||||||
|
break
|
||||||
|
except JsonSchemaValidationError:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:ANY_OF_NO_MATCH")
|
||||||
|
if "not" in rule:
|
||||||
|
try:
|
||||||
|
check(value, rule["not"], current_root, path)
|
||||||
|
except JsonSchemaValidationError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:NOT_SCHEMA_MATCHED")
|
||||||
|
if "if" in rule:
|
||||||
|
try:
|
||||||
|
check(value, rule["if"], current_root, path)
|
||||||
|
matched = True
|
||||||
|
except JsonSchemaValidationError:
|
||||||
|
matched = False
|
||||||
|
if matched and "then" in rule:
|
||||||
|
check(value, rule["then"], current_root, path)
|
||||||
|
if not matched and "else" in rule:
|
||||||
|
check(value, rule["else"], current_root, path)
|
||||||
|
if "const" in rule and value != rule["const"]:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:CONST_MISMATCH")
|
||||||
|
if "enum" in rule and value not in rule["enum"]:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:ENUM_MISMATCH")
|
||||||
|
declared_type = rule.get("type")
|
||||||
|
if declared_type is not None:
|
||||||
|
allowed = [declared_type] if isinstance(declared_type, str) else list(declared_type)
|
||||||
|
if not any(type_matches(value, item) for item in allowed):
|
||||||
|
raise JsonSchemaValidationError(f"{path}:TYPE_MISMATCH")
|
||||||
|
if isinstance(value, str):
|
||||||
|
if len(value) < int(rule.get("minLength", 0)):
|
||||||
|
raise JsonSchemaValidationError(f"{path}:MIN_LENGTH")
|
||||||
|
if "pattern" in rule and re.search(str(rule["pattern"]), value) is None:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:PATTERN_MISMATCH")
|
||||||
|
if rule.get("format") == "date-time":
|
||||||
|
try:
|
||||||
|
_parse_review_time(value, f"{path}:DATETIME_INVALID")
|
||||||
|
except InvariantError as exc:
|
||||||
|
raise JsonSchemaValidationError(str(exc)) from exc
|
||||||
|
if isinstance(value, Mapping):
|
||||||
|
required = rule.get("required", [])
|
||||||
|
missing = [key for key in required if key not in value]
|
||||||
|
if missing:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:REQUIRED_MISSING::{','.join(missing)}")
|
||||||
|
properties = rule.get("properties", {})
|
||||||
|
if rule.get("additionalProperties") is False:
|
||||||
|
extras = sorted(set(value) - set(properties))
|
||||||
|
if extras:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:ADDITIONAL_PROPERTIES::{','.join(extras)}")
|
||||||
|
for key, child in properties.items():
|
||||||
|
if key in value:
|
||||||
|
check(value[key], child, current_root, f"{path}/{key}")
|
||||||
|
if isinstance(value, list):
|
||||||
|
if len(value) < int(rule.get("minItems", 0)):
|
||||||
|
raise JsonSchemaValidationError(f"{path}:MIN_ITEMS")
|
||||||
|
if "maxItems" in rule and len(value) > int(rule["maxItems"]):
|
||||||
|
raise JsonSchemaValidationError(f"{path}:MAX_ITEMS")
|
||||||
|
if rule.get("uniqueItems") and len({canonical_json_bytes(item) for item in value}) != len(value):
|
||||||
|
raise JsonSchemaValidationError(f"{path}:UNIQUE_ITEMS")
|
||||||
|
if "items" in rule:
|
||||||
|
for index, item in enumerate(value):
|
||||||
|
check(item, rule["items"], current_root, f"{path}/{index}")
|
||||||
|
if isinstance(value, (int, float)) and not isinstance(value, bool) and "minimum" in rule and value < rule["minimum"]:
|
||||||
|
raise JsonSchemaValidationError(f"{path}:MINIMUM")
|
||||||
|
|
||||||
|
check(instance, root, root, "$")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_candidate_material(material: Mapping[str, Any]) -> None:
|
||||||
|
if set(material) != CANDIDATE_MATERIAL_KEYS:
|
||||||
|
raise InvariantError("CANDIDATE_DIGEST_MATERIAL_SHAPE")
|
||||||
|
for key in CANDIDATE_MATERIAL_KEYS - {"document_sha256s"}:
|
||||||
|
_require_sha256(material[key], f"CANDIDATE_{key.upper()}_INVALID")
|
||||||
|
documents = material["document_sha256s"]
|
||||||
|
if not isinstance(documents, Mapping) or set(documents) != DOCUMENT_DIGEST_KEYS:
|
||||||
|
raise InvariantError("CANDIDATE_DOCUMENT_DIGEST_SET_NOT_EXACT")
|
||||||
|
for key, value in documents.items():
|
||||||
|
_require_sha256(value, f"CANDIDATE_DOCUMENT_{key.upper()}_INVALID")
|
||||||
|
|
||||||
|
|
||||||
|
def review_subject_digest(
|
||||||
|
*, candidate_digest: str, review_request_core_sha256: str,
|
||||||
|
lawyer_review_packet_core_sha256: str, validation_envelope_core_sha256: str,
|
||||||
|
finding_ids: Sequence[str], scope_ids: Sequence[str], policy_sha256: str,
|
||||||
|
release_sha256s: Mapping[str, str],
|
||||||
|
) -> str:
|
||||||
|
for value, code in (
|
||||||
|
(candidate_digest, "REVIEW_CANDIDATE_DIGEST_INVALID"),
|
||||||
|
(review_request_core_sha256, "REVIEW_REQUEST_CORE_HASH_INVALID"),
|
||||||
|
(lawyer_review_packet_core_sha256, "REVIEW_PACKET_CORE_HASH_INVALID"),
|
||||||
|
(validation_envelope_core_sha256, "REVIEW_VALIDATION_HASH_INVALID"),
|
||||||
|
(policy_sha256, "REVIEW_POLICY_HASH_INVALID"),
|
||||||
|
):
|
||||||
|
_require_sha256(value, code)
|
||||||
|
expected_release_keys = {"parent", "authority", "corpus", "case_type_coverage"}
|
||||||
|
if set(release_sha256s) != expected_release_keys:
|
||||||
|
raise InvariantError("REVIEW_RELEASE_SNAPSHOT_NOT_EXACT")
|
||||||
|
for value in release_sha256s.values():
|
||||||
|
_require_sha256(value, "REVIEW_RELEASE_HASH_INVALID")
|
||||||
|
if len(finding_ids) != len(set(finding_ids)) or len(scope_ids) != len(set(scope_ids)):
|
||||||
|
raise InvariantError("REVIEW_SUBJECT_SET_DUPLICATE")
|
||||||
|
subject_core = {
|
||||||
|
"schema_version": "stage2_s2_40_review_subject.v1",
|
||||||
|
"domain_separator": "STAGE2_S2_40_REVIEW_SUBJECT_V1",
|
||||||
|
"candidate_content_digest": candidate_digest,
|
||||||
|
"review_request_core_sha256": review_request_core_sha256,
|
||||||
|
"lawyer_review_packet_core_sha256": lawyer_review_packet_core_sha256,
|
||||||
|
"validation_envelope_core_sha256": validation_envelope_core_sha256,
|
||||||
|
"finding_ids": sorted(finding_ids), "scope_ids": sorted(scope_ids),
|
||||||
|
"policy_sha256": policy_sha256,
|
||||||
|
"release_sha256s": {key: release_sha256s[key] for key in sorted(release_sha256s)},
|
||||||
|
}
|
||||||
|
return hashlib.sha256(canonical_json_bytes(subject_core)).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def validate_status_only_paths(paths: Sequence[str]) -> None:
|
||||||
|
if tuple(paths) != STATUS_ONLY_PATHS:
|
||||||
|
raise InvariantError("STATUS_ONLY_INPUT_SET_NOT_EXACT_FIVE")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_reference_closure(required_refs: Sequence[str], available_refs: Sequence[str]) -> None:
|
||||||
|
if len(required_refs) != len(set(required_refs)) or len(available_refs) != len(set(available_refs)):
|
||||||
|
raise InvariantError("REFERENCE_SET_DUPLICATE")
|
||||||
|
missing = sorted(set(required_refs) - set(available_refs))
|
||||||
|
if missing:
|
||||||
|
raise InvariantError("REFERENCE_DANGLING::" + ",".join(missing))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_readback_rows(expected_rows: Sequence[Mapping[str, Any]], observed_rows: Sequence[Mapping[str, Any]]) -> None:
|
||||||
|
def normalize(rows: Sequence[Mapping[str, Any]]) -> list[tuple[str, str, str, int, str | None]]:
|
||||||
|
normalized: list[tuple[str, str, str, int, str | None]] = []
|
||||||
|
for row in rows:
|
||||||
|
if set(row) != {"path", "raw_sha256", "content_type", "size_bytes", "schema_ref"}:
|
||||||
|
raise InvariantError("READBACK_ROW_SHAPE_INVALID")
|
||||||
|
path = row["path"]
|
||||||
|
if not isinstance(path, str) or not path.startswith("final/") or ".." in path.split("/"):
|
||||||
|
raise InvariantError("READBACK_PATH_INVALID")
|
||||||
|
content_type, schema_ref, size = row["content_type"], row["schema_ref"], row["size_bytes"]
|
||||||
|
if not isinstance(content_type, str) or not content_type or not isinstance(size, int) or size < 1:
|
||||||
|
raise InvariantError("READBACK_METADATA_INVALID")
|
||||||
|
if schema_ref is not None and (not isinstance(schema_ref, str) or not schema_ref):
|
||||||
|
raise InvariantError("READBACK_SCHEMA_REF_INVALID")
|
||||||
|
normalized.append((path, _require_sha256(row["raw_sha256"], "READBACK_HASH_INVALID"), content_type, size, schema_ref))
|
||||||
|
if len({row[0] for row in normalized}) != len(normalized):
|
||||||
|
raise InvariantError("READBACK_PATH_DUPLICATE")
|
||||||
|
return sorted(normalized)
|
||||||
|
if normalize(expected_rows) != normalize(observed_rows):
|
||||||
|
raise InvariantError("READBACK_MISMATCH_NO_FINAL_BARRIER")
|
||||||
|
|
||||||
|
|
||||||
|
def run_invariants(checks: Mapping[str, Mapping[str, Any]]) -> list[dict[str, Any]]:
|
||||||
|
if set(checks) != set(INVARIANT_IDS):
|
||||||
|
raise InvariantError("INVARIANT_SET_NOT_EXACT_V01_V18")
|
||||||
|
results: list[dict[str, Any]] = []
|
||||||
|
for invariant_id in INVARIANT_IDS:
|
||||||
|
check = checks[invariant_id]
|
||||||
|
if not isinstance(check, Mapping):
|
||||||
|
raise InvariantError("INVARIANT_CHECK_NOT_OBJECT")
|
||||||
|
required_check_keys = {
|
||||||
|
"evaluable", "passed", "impact_scope", "source_refs", "finding_ids",
|
||||||
|
"expected", "observed", "reason_codes",
|
||||||
|
}
|
||||||
|
if set(check) != required_check_keys:
|
||||||
|
raise InvariantError(f"{invariant_id}_CHECK_SHAPE_NOT_EXACT")
|
||||||
|
evaluable = check["evaluable"]
|
||||||
|
if not isinstance(evaluable, bool):
|
||||||
|
raise InvariantError(f"{invariant_id}_EVALUABLE_NOT_BOOLEAN")
|
||||||
|
if evaluable:
|
||||||
|
if not isinstance(check["passed"], bool):
|
||||||
|
raise InvariantError(f"{invariant_id}_PASSED_NOT_BOOLEAN")
|
||||||
|
if check["observed"] is None or check["passed"] != (check["observed"] == check["expected"]):
|
||||||
|
raise InvariantError(f"{invariant_id}_OBSERVATION_ORACLE_MISMATCH")
|
||||||
|
evaluation = "PASS" if check["passed"] else "FAIL"
|
||||||
|
else:
|
||||||
|
if check["passed"] not in {False, None} or check["observed"] is not None:
|
||||||
|
raise InvariantError(f"{invariant_id}_UNEVALUABLE_OBSERVATION_INVALID")
|
||||||
|
evaluation = "UNEVALUABLE"
|
||||||
|
scope = check.get("impact_scope", "OK" if evaluation == "PASS" else "REVIEW_REQUIRED")
|
||||||
|
if scope not in IMPACT_SCOPES or (evaluation == "PASS" and scope != "OK"):
|
||||||
|
raise InvariantError("INVARIANT_IMPACT_SCOPE_INVALID")
|
||||||
|
reason_values = check["reason_codes"]
|
||||||
|
source_values = check["source_refs"]
|
||||||
|
finding_values = check["finding_ids"]
|
||||||
|
for values in (reason_values, source_values, finding_values):
|
||||||
|
if (not isinstance(values, list) or len(values) != len(set(values))
|
||||||
|
or not all(isinstance(value, str) and value for value in values)):
|
||||||
|
raise InvariantError("INVARIANT_STRING_SET_INVALID")
|
||||||
|
if not source_values:
|
||||||
|
raise InvariantError(f"{invariant_id}_SOURCE_EVIDENCE_REQUIRED")
|
||||||
|
reason_codes = sorted(set(reason_values))
|
||||||
|
if evaluation == "PASS" and (reason_codes or finding_values):
|
||||||
|
raise InvariantError(f"{invariant_id}_PASS_FINDING_FORBIDDEN")
|
||||||
|
if evaluation != "PASS" and (not reason_codes or not finding_values):
|
||||||
|
raise InvariantError(f"{invariant_id}_{evaluation}_FINDING_REQUIRED")
|
||||||
|
results.append({
|
||||||
|
"invariant_id": invariant_id, "evaluation_status": evaluation,
|
||||||
|
"finding_ids": sorted(set(finding_values)), "impact_scope": scope,
|
||||||
|
"source_refs": sorted(set(source_values)),
|
||||||
|
"expected": check["expected"], "observed": check["observed"],
|
||||||
|
"reason_codes": reason_codes, "validator_algorithm_id": f"S2_40::{invariant_id}::V1",
|
||||||
|
})
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def aggregate_status(results: Sequence[Mapping[str, Any]], *, compile_mode: str | None, legal_gates: Mapping[str, bool]) -> dict[str, str]:
|
||||||
|
if [row.get("invariant_id") for row in results] != list(INVARIANT_IDS):
|
||||||
|
raise InvariantError("INVARIANT_RESULT_ORDER_OR_SET_INVALID")
|
||||||
|
for row in results:
|
||||||
|
evaluation, scope = row.get("evaluation_status"), row.get("impact_scope")
|
||||||
|
if evaluation not in {"PASS", "FAIL", "UNEVALUABLE"} or scope not in IMPACT_SCOPES:
|
||||||
|
raise InvariantError("INVARIANT_RESULT_ENUM_INVALID")
|
||||||
|
if evaluation == "PASS" and scope != "OK":
|
||||||
|
raise InvariantError("INVARIANT_PASS_SCOPE_INVALID")
|
||||||
|
scopes = {row.get("impact_scope") for row in results}
|
||||||
|
if "INCOMPLETE" in scopes:
|
||||||
|
run = "TECHNICAL_INCOMPLETE"
|
||||||
|
elif "REVIEW_REQUIRED" in scopes or any(row.get("evaluation_status") == "UNEVALUABLE" for row in results):
|
||||||
|
run = "TECHNICAL_REVIEW_REQUIRED"
|
||||||
|
else:
|
||||||
|
run = "CONSISTENT"
|
||||||
|
if run == "TECHNICAL_INCOMPLETE":
|
||||||
|
artifact, legal = "NOT_PRODUCED", "NOT_ASSESSED"
|
||||||
|
elif run == "TECHNICAL_REVIEW_REQUIRED":
|
||||||
|
artifact, legal = "TECHNICAL_REVIEW_REQUIRED", "LAWYER_REVIEW_REQUIRED"
|
||||||
|
else:
|
||||||
|
artifact = "CONSISTENT"
|
||||||
|
gates = set(legal_gates) == REQUIRED_LEGAL_GATES and all(legal_gates.values())
|
||||||
|
all_pass = all(row.get("evaluation_status") == "PASS" for row in results)
|
||||||
|
legal = "READY_FOR_LAWYER_FILING_DECISION" if compile_mode == "PRODUCTION" and all_pass and gates else "LAWYER_REVIEW_REQUIRED"
|
||||||
|
return {"run_technical_status": run, "artifact_technical_status": artifact, "legal_readiness": legal}
|
||||||
|
|
||||||
|
|
||||||
|
def candidate_content_digest(material: Mapping[str, Any]) -> str:
|
||||||
|
forbidden = {"candidate_content_digest", "review_request", "review_receipt", "commit_intent", "commit_result", "run_status", "artifact_set_digest"}
|
||||||
|
if forbidden & set(material):
|
||||||
|
raise InvariantError("CANDIDATE_DIGEST_MATERIAL_CYCLE")
|
||||||
|
validate_candidate_material(material)
|
||||||
|
digest_core = {
|
||||||
|
"schema_version": "stage2_s2_40_candidate_content_digest.v1",
|
||||||
|
"domain_separator": "STAGE2_S2_40_CANDIDATE_CONTENT_V1",
|
||||||
|
**material,
|
||||||
|
}
|
||||||
|
return hashlib.sha256(canonical_json_bytes(digest_core)).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def artifact_set_digest(rows: Sequence[Mapping[str, Any]]) -> str:
|
||||||
|
ordered = sorted(rows, key=lambda row: row["path"])
|
||||||
|
paths = [row["path"] for row in ordered]
|
||||||
|
if len(paths) != len(set(paths)):
|
||||||
|
raise InvariantError("ARTIFACT_PATH_DUPLICATE")
|
||||||
|
for row in ordered:
|
||||||
|
if set(row) != {"path", "raw_sha256", "content_type", "size_bytes", "schema_ref"}:
|
||||||
|
raise InvariantError("ARTIFACT_ROW_SHAPE_INVALID")
|
||||||
|
_require_sha256(row["raw_sha256"], "ARTIFACT_ROW_HASH_INVALID")
|
||||||
|
if not isinstance(row["content_type"], str) or not row["content_type"]:
|
||||||
|
raise InvariantError("ARTIFACT_ROW_CONTENT_TYPE_INVALID")
|
||||||
|
if not isinstance(row["size_bytes"], int) or row["size_bytes"] < 1:
|
||||||
|
raise InvariantError("ARTIFACT_ROW_SIZE_INVALID")
|
||||||
|
if row["schema_ref"] is not None and (not isinstance(row["schema_ref"], str) or not row["schema_ref"]):
|
||||||
|
raise InvariantError("ARTIFACT_ROW_SCHEMA_REF_INVALID")
|
||||||
|
return hashlib.sha256(canonical_json_bytes(ordered)).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def derive_review_requirements(policy: Mapping[str, Any], active_tags: Sequence[str], runtime_triggers: Sequence[str]) -> list[str]:
|
||||||
|
if policy.get("status") != "APPROVED_LEGAL_CONTENT" or policy.get("execution_eligible") is not True:
|
||||||
|
return ["STANDARD_ATTORNEY_REVIEW"]
|
||||||
|
allowed_tags = set(policy["final_review_contract"]["allowed_review_tags"])
|
||||||
|
if not set(active_tags) <= allowed_tags:
|
||||||
|
raise InvariantError("REVIEW_TAG_UNKNOWN")
|
||||||
|
required = set(policy["final_review_contract"]["base_required_receipt_types"])
|
||||||
|
for row in policy.get("policy_rows", []):
|
||||||
|
if set(row.get("match_tags", [])) <= set(active_tags) and set(row.get("runtime_triggers", [])) <= set(runtime_triggers):
|
||||||
|
required.update(row.get("required_receipt_types", []))
|
||||||
|
return sorted(required)
|
||||||
|
|
||||||
|
|
||||||
|
def review_receipt_signed_material_digest(receipt: Mapping[str, Any]) -> str:
|
||||||
|
"""Hash the exact inline receipt preimage, including detached-adapter evidence."""
|
||||||
|
keys = (
|
||||||
|
"request_id", "candidate_content_digest", "review_subject_digest", "receipt_type",
|
||||||
|
"finding_ids", "scope_ids", "reviewer_role", "reviewer_qualification",
|
||||||
|
"issuer_id", "key_id", "signature_algorithm",
|
||||||
|
"external_verification_attestation_sha256", "issued_at", "expires_at",
|
||||||
|
"revocation_status", "cryptographic_verification_status",
|
||||||
|
"review_disposition", "change_scope", "reason_codes",
|
||||||
|
)
|
||||||
|
if any(key not in receipt for key in keys):
|
||||||
|
raise InvariantError("REVIEW_SIGNED_MATERIAL_FIELD_MISSING")
|
||||||
|
material = ["STAGE2_S2_40_REVIEW_RECEIPT_V1", *[receipt[key] for key in keys]]
|
||||||
|
return hashlib.sha256(canonical_json_bytes(material)).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_review_time(value: Any, code: str) -> datetime:
|
||||||
|
if not isinstance(value, str):
|
||||||
|
raise InvariantError(code)
|
||||||
|
try:
|
||||||
|
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
||||||
|
except ValueError as exc:
|
||||||
|
raise InvariantError(code) from exc
|
||||||
|
if parsed.tzinfo is None:
|
||||||
|
raise InvariantError(code)
|
||||||
|
return parsed.astimezone(timezone.utc)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_review_receipt(
|
||||||
|
receipt: Mapping[str, Any], *, candidate_digest: str,
|
||||||
|
expected_review_subject_digest: str, expected_request_id: str,
|
||||||
|
expected_receipt_type: str, expected_finding_ids: Sequence[str],
|
||||||
|
expected_scope_ids: Sequence[str], now: datetime,
|
||||||
|
) -> list[str]:
|
||||||
|
"""Return closed invalidity codes; an empty list means externally admissible."""
|
||||||
|
reasons: list[str] = []
|
||||||
|
if set(receipt) != REVIEW_RECEIPT_KEYS or receipt.get("schema_version") != "stage2_s2_40_review_receipt.v1":
|
||||||
|
return ["REVIEW_RECEIPT_CLOSED_SHAPE_INVALID"]
|
||||||
|
for key in ("receipt_id", "request_id", "receipt_type", "reviewer_role", "reviewer_qualification", "issuer_id", "key_id", "signature_algorithm"):
|
||||||
|
if not isinstance(receipt.get(key), str) or not receipt[key]:
|
||||||
|
reasons.append(f"REVIEW_RECEIPT_{key.upper()}_INVALID")
|
||||||
|
for key in ("candidate_content_digest", "review_subject_digest", "signed_material_digest", "external_verification_attestation_sha256"):
|
||||||
|
try:
|
||||||
|
_require_sha256(receipt.get(key), f"REVIEW_RECEIPT_{key.upper()}_INVALID")
|
||||||
|
except InvariantError as exc:
|
||||||
|
reasons.append(str(exc))
|
||||||
|
for key, expected in (
|
||||||
|
("candidate_content_digest", candidate_digest),
|
||||||
|
("review_subject_digest", expected_review_subject_digest),
|
||||||
|
("request_id", expected_request_id),
|
||||||
|
("receipt_type", expected_receipt_type),
|
||||||
|
("finding_ids", sorted(expected_finding_ids)),
|
||||||
|
("scope_ids", sorted(expected_scope_ids)),
|
||||||
|
):
|
||||||
|
observed = sorted(receipt[key]) if key in {"finding_ids", "scope_ids"} and isinstance(receipt[key], list) else receipt[key]
|
||||||
|
if observed != expected:
|
||||||
|
reasons.append(f"REVIEW_RECEIPT_{key.upper()}_MISMATCH")
|
||||||
|
for key in ("finding_ids", "scope_ids", "reason_codes"):
|
||||||
|
values = receipt.get(key)
|
||||||
|
if not isinstance(values, list) or values != sorted(set(values)) or not all(isinstance(value, str) and value for value in values):
|
||||||
|
reasons.append(f"REVIEW_RECEIPT_{key.upper()}_NOT_SORTED_SET")
|
||||||
|
if receipt.get("reviewer_role") != TRUSTED_REVIEW_ROLE:
|
||||||
|
reasons.append("REVIEW_RECEIPT_ROLE_UNTRUSTED")
|
||||||
|
if receipt.get("reviewer_qualification") != TRUSTED_REVIEW_QUALIFICATION:
|
||||||
|
reasons.append("REVIEW_RECEIPT_QUALIFICATION_UNTRUSTED")
|
||||||
|
if receipt.get("issuer_id") != TRUSTED_REVIEW_ISSUER:
|
||||||
|
reasons.append("REVIEW_RECEIPT_ISSUER_UNTRUSTED")
|
||||||
|
if receipt.get("key_id") not in TRUSTED_REVIEW_KEY_IDS:
|
||||||
|
reasons.append("REVIEW_RECEIPT_KEY_UNTRUSTED")
|
||||||
|
if receipt.get("signature_algorithm") != TRUSTED_REVIEW_SIGNATURE_ALGORITHM:
|
||||||
|
reasons.append("REVIEW_RECEIPT_SIGNATURE_ALGORITHM_UNTRUSTED")
|
||||||
|
if receipt.get("cryptographic_verification_status") != "VERIFIED_BY_EXTERNAL_ADAPTER":
|
||||||
|
reasons.append("REVIEW_RECEIPT_EXTERNAL_VERIFICATION_NOT_VERIFIED")
|
||||||
|
if receipt.get("revocation_status") != "NOT_REVOKED":
|
||||||
|
reasons.append("REVIEW_RECEIPT_REVOKED_OR_UNKNOWN")
|
||||||
|
try:
|
||||||
|
issued = _parse_review_time(receipt.get("issued_at"), "REVIEW_RECEIPT_ISSUED_AT_INVALID")
|
||||||
|
expires = _parse_review_time(receipt.get("expires_at"), "REVIEW_RECEIPT_EXPIRES_AT_INVALID")
|
||||||
|
current = now.astimezone(timezone.utc)
|
||||||
|
if issued > current or expires <= current or expires <= issued:
|
||||||
|
reasons.append("REVIEW_RECEIPT_TIME_WINDOW_INVALID")
|
||||||
|
except InvariantError as exc:
|
||||||
|
reasons.append(str(exc))
|
||||||
|
disposition, change_scope = receipt.get("review_disposition"), receipt.get("change_scope")
|
||||||
|
if disposition == "APPROVE_AS_IS" and change_scope != "NONE":
|
||||||
|
reasons.append("REVIEW_RECEIPT_APPROVAL_CHANGE_SCOPE_FORBIDDEN")
|
||||||
|
elif disposition == "CONTENT_CHANGE_REQUIRED" and change_scope not in {
|
||||||
|
"STAGE1_CONTEXT", "LEGAL_JUDGMENT", "PLAN_RULE_CALCULATION_BINDING", "DRAFTING_TEXT_ATOM",
|
||||||
|
}:
|
||||||
|
reasons.append("REVIEW_RECEIPT_CHANGE_SCOPE_INVALID")
|
||||||
|
elif disposition not in {"APPROVE_AS_IS", "CONTENT_CHANGE_REQUIRED"}:
|
||||||
|
reasons.append("REVIEW_RECEIPT_DISPOSITION_INVALID")
|
||||||
|
try:
|
||||||
|
expected_signed = review_receipt_signed_material_digest(receipt)
|
||||||
|
if receipt.get("signed_material_digest") != expected_signed:
|
||||||
|
reasons.append("REVIEW_RECEIPT_SIGNED_MATERIAL_MISMATCH")
|
||||||
|
except InvariantError as exc:
|
||||||
|
reasons.append(str(exc))
|
||||||
|
return sorted(set(reasons))
|
||||||
|
|
||||||
|
|
||||||
|
def transition_review_state(
|
||||||
|
*, candidate_digest: str, required_receipt_types: Sequence[str],
|
||||||
|
receipts: Sequence[Mapping[str, Any]], expected_review_subject_digest: str,
|
||||||
|
expected_request_ids: Mapping[str, str], expected_finding_ids: Sequence[str],
|
||||||
|
expected_scope_ids: Sequence[str], now: datetime,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
_require_sha256(candidate_digest, "REVIEW_CANDIDATE_DIGEST_INVALID")
|
||||||
|
if len(required_receipt_types) != len(set(required_receipt_types)):
|
||||||
|
raise InvariantError("REVIEW_REQUIRED_TYPE_DUPLICATE")
|
||||||
|
_require_sha256(expected_review_subject_digest, "REVIEW_SUBJECT_DIGEST_INVALID")
|
||||||
|
if set(expected_request_ids) != set(required_receipt_types) or any(
|
||||||
|
not isinstance(value, str) or not value for value in expected_request_ids.values()
|
||||||
|
):
|
||||||
|
raise InvariantError("REVIEW_REQUEST_ID_MAP_INVALID")
|
||||||
|
valid: dict[str, Mapping[str, Any]] = {}
|
||||||
|
invalid: list[str] = []
|
||||||
|
invalid_reasons: dict[str, list[str]] = {}
|
||||||
|
seen_receipt_ids: set[str] = set()
|
||||||
|
for receipt in receipts:
|
||||||
|
receipt_id = receipt.get("receipt_id")
|
||||||
|
receipt_type = receipt.get("receipt_type")
|
||||||
|
if not isinstance(receipt_id, str) or not receipt_id or receipt_id in seen_receipt_ids:
|
||||||
|
raise InvariantError("REVIEW_RECEIPT_ID_MISSING_OR_DUPLICATE")
|
||||||
|
seen_receipt_ids.add(receipt_id)
|
||||||
|
reasons = validate_review_receipt(
|
||||||
|
receipt, candidate_digest=candidate_digest,
|
||||||
|
expected_review_subject_digest=expected_review_subject_digest,
|
||||||
|
expected_request_id=expected_request_ids.get(str(receipt_type), ""),
|
||||||
|
expected_receipt_type=str(receipt_type),
|
||||||
|
expected_finding_ids=expected_finding_ids, expected_scope_ids=expected_scope_ids,
|
||||||
|
now=now,
|
||||||
|
)
|
||||||
|
if receipt_type not in required_receipt_types:
|
||||||
|
reasons.append("REVIEW_RECEIPT_TYPE_NOT_REQUIRED")
|
||||||
|
if reasons:
|
||||||
|
invalid.append(receipt_id)
|
||||||
|
invalid_reasons[receipt_id] = sorted(set(reasons))
|
||||||
|
else:
|
||||||
|
if receipt_type in valid:
|
||||||
|
raise InvariantError("REVIEW_RECEIPT_TYPE_DUPLICATE")
|
||||||
|
valid[receipt_type] = receipt
|
||||||
|
routes = {
|
||||||
|
"STAGE1_CONTEXT": (0, "RESTART_FROM_S2_00"),
|
||||||
|
"LEGAL_JUDGMENT": (1, "RESTART_FROM_S2_10"),
|
||||||
|
"PLAN_RULE_CALCULATION_BINDING": (2, "RESTART_FROM_S2_20"),
|
||||||
|
"DRAFTING_TEXT_ATOM": (3, "RESTART_FROM_S2_30"),
|
||||||
|
}
|
||||||
|
changed_scopes = [receipt["change_scope"] for receipt in valid.values() if receipt["review_disposition"] == "CONTENT_CHANGE_REQUIRED"]
|
||||||
|
if changed_scopes:
|
||||||
|
earliest = min(changed_scopes, key=lambda scope: routes[scope][0])
|
||||||
|
return {"workflow_phase": "SUPERSEDED", "route": routes[earliest][1], "invalid_receipt_ids": sorted(invalid), "invalid_receipt_reasons": invalid_reasons}
|
||||||
|
missing = sorted(set(required_receipt_types) - set(valid))
|
||||||
|
if missing or invalid:
|
||||||
|
return {"workflow_phase": "AWAITING_EXTERNAL_REVIEW", "route": "WAIT_EXTERNAL_REVIEW", "missing_receipt_types": missing, "invalid_receipt_ids": sorted(invalid), "invalid_receipt_reasons": invalid_reasons}
|
||||||
|
if any(receipt.get("review_disposition") != "APPROVE_AS_IS" for receipt in valid.values()):
|
||||||
|
raise InvariantError("REVIEW_DISPOSITION_INVALID")
|
||||||
|
return {"workflow_phase": "READY_TO_COMMIT", "route": "CONTINUE_TO_COMMIT", "invalid_receipt_ids": [], "invalid_receipt_reasons": {}}
|
||||||
|
|
||||||
|
|
||||||
|
def commit_write_order(final_paths: Sequence[str]) -> list[str]:
|
||||||
|
if len(final_paths) != len(set(final_paths)):
|
||||||
|
raise InvariantError("COMMIT_TARGET_DUPLICATE")
|
||||||
|
if any(not path.startswith("final/") or ".." in path.split("/") for path in final_paths):
|
||||||
|
raise InvariantError("COMMIT_TARGET_OUTSIDE_FINAL_ROOT")
|
||||||
|
return ["commit/commit_intent.json", *sorted(final_paths), "commit/stage2_commit_result.json", "control/run_status.json"]
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["INVARIANT_IDS", "InvariantError", "JsonSchemaValidationError", "aggregate_status", "artifact_set_digest", "candidate_content_digest", "commit_write_order", "derive_review_requirements", "review_receipt_signed_material_digest", "review_subject_digest", "run_invariants", "transition_review_state", "validate_candidate_material", "validate_jsonschema_instance", "validate_readback_rows", "validate_reference_closure", "validate_review_receipt", "validate_status_only_paths"]
|
||||||
+231
-1
@@ -29,6 +29,14 @@
|
|||||||
{"$ref": "#/$defs/s2_20_request"},
|
{"$ref": "#/$defs/s2_20_request"},
|
||||||
{"$ref": "#/$defs/s2_20_execution_receipt"},
|
{"$ref": "#/$defs/s2_20_execution_receipt"},
|
||||||
{"$ref": "#/$defs/s2_20_outer_execution_receipt"},
|
{"$ref": "#/$defs/s2_20_outer_execution_receipt"},
|
||||||
|
{"$ref": "#/$defs/s2_40_request"},
|
||||||
|
{"$ref": "#/$defs/s2_40_host_cas_receipt"},
|
||||||
|
{"$ref": "#/$defs/s2_40_code_executor_binding"},
|
||||||
|
{"$ref": "#/$defs/s2_40_inline_code_receipt"},
|
||||||
|
{"$ref": "#/$defs/s2_40_execution_receipt"},
|
||||||
|
{"$ref": "#/$defs/s2_40_outer_execution_receipt"},
|
||||||
|
{"$ref": "#/$defs/s2_40_commit_intent"},
|
||||||
|
{"$ref": "#/$defs/s2_40_commit_result"},
|
||||||
{"$ref": "#/$defs/stage2_deterministic_admission_receipt"},
|
{"$ref": "#/$defs/stage2_deterministic_admission_receipt"},
|
||||||
{"$ref": "#/$defs/code_executor_binding"},
|
{"$ref": "#/$defs/code_executor_binding"},
|
||||||
{"$ref": "#/$defs/inline_code_receipt"},
|
{"$ref": "#/$defs/inline_code_receipt"},
|
||||||
@@ -469,10 +477,25 @@
|
|||||||
"host_cas_receipt_sha256": {"$ref": "#/$defs/sha256"}
|
"host_cas_receipt_sha256": {"$ref": "#/$defs/sha256"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"s2_40_artifact_digest_row": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["path", "raw_sha256", "content_type", "size_bytes", "schema_ref"],
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^stage2_runs/by-binding/[a-f0-9]{64}/final/(?!.*(?:^|/)\\.\\.(?:/|$))[^\\u0000]+$"
|
||||||
|
},
|
||||||
|
"raw_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"content_type": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"size_bytes": {"type": "integer", "minimum": 1},
|
||||||
|
"schema_ref": {"type": ["string", "null"]}
|
||||||
|
}
|
||||||
|
},
|
||||||
"stage2_deterministic_admission_receipt": {
|
"stage2_deterministic_admission_receipt": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": ["schema_version", "parent_release_sha256", "executor_binding_sha256", "present_deterministic_stage_ids", "stage_receipts", "embedded_task_admissions", "admission_status", "signature", "signature_verification_status", "signed_payload_sha256", "backend_capability_receipt_sha256"],
|
"required": ["schema_version", "parent_release_sha256", "executor_binding_sha256", "present_deterministic_stage_ids", "stage_receipts", "embedded_task_admissions", "admission_status", "external_trust_verified", "signature", "signature_verification_status", "signed_payload_sha256", "backend_capability_receipt_sha256"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"schema_version": {"const": "stage2_deterministic_admission_receipt.v1"},
|
"schema_version": {"const": "stage2_deterministic_admission_receipt.v1"},
|
||||||
"parent_release_sha256": {"$ref": "#/$defs/sha256"},
|
"parent_release_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
@@ -498,6 +521,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"admission_status": {"enum": ["PENDING", "CANARY_ADMITTED", "PRODUCTION_ADMITTED"]},
|
"admission_status": {"enum": ["PENDING", "CANARY_ADMITTED", "PRODUCTION_ADMITTED"]},
|
||||||
|
"external_trust_verified": {"type": "boolean"},
|
||||||
"signature": {"type": "string", "minLength": 1},
|
"signature": {"type": "string", "minLength": 1},
|
||||||
"signature_verification_status": {"enum": ["PENDING_EXTERNAL_SIGNATURE", "BACKEND_VERIFIED"]},
|
"signature_verification_status": {"enum": ["PENDING_EXTERNAL_SIGNATURE", "BACKEND_VERIFIED"]},
|
||||||
"signed_payload_sha256": {"$ref": "#/$defs/bindable_sha256"},
|
"signed_payload_sha256": {"$ref": "#/$defs/bindable_sha256"},
|
||||||
@@ -508,6 +532,7 @@
|
|||||||
"if": {"properties": {"admission_status": {"const": "PENDING"}}, "required": ["admission_status"]},
|
"if": {"properties": {"admission_status": {"const": "PENDING"}}, "required": ["admission_status"]},
|
||||||
"then": {
|
"then": {
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"external_trust_verified": {"const": false},
|
||||||
"signature": {"const": "PENDING_EXTERNAL_SIGNATURE"},
|
"signature": {"const": "PENDING_EXTERNAL_SIGNATURE"},
|
||||||
"signature_verification_status": {"const": "PENDING_EXTERNAL_SIGNATURE"},
|
"signature_verification_status": {"const": "PENDING_EXTERNAL_SIGNATURE"},
|
||||||
"signed_payload_sha256": {"const": "PENDING_SEQUENTIAL_BIND"},
|
"signed_payload_sha256": {"const": "PENDING_SEQUENTIAL_BIND"},
|
||||||
@@ -519,6 +544,7 @@
|
|||||||
"if": {"properties": {"admission_status": {"enum": ["CANARY_ADMITTED", "PRODUCTION_ADMITTED"]}}, "required": ["admission_status"]},
|
"if": {"properties": {"admission_status": {"enum": ["CANARY_ADMITTED", "PRODUCTION_ADMITTED"]}}, "required": ["admission_status"]},
|
||||||
"then": {
|
"then": {
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"external_trust_verified": {"const": true},
|
||||||
"signature": {"type": "string", "minLength": 16, "not": {"pattern": "^PENDING"}},
|
"signature": {"type": "string", "minLength": 16, "not": {"pattern": "^PENDING"}},
|
||||||
"signature_verification_status": {"const": "BACKEND_VERIFIED"},
|
"signature_verification_status": {"const": "BACKEND_VERIFIED"},
|
||||||
"signed_payload_sha256": {"$ref": "#/$defs/sha256"},
|
"signed_payload_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
@@ -1769,6 +1795,210 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"s2_40_request": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"schema_version", "request_id", "candidate_attempt_id", "run_binding_digest",
|
||||||
|
"user_identity_hash", "workspace_identity_hash", "stage2_run_root_ref", "entry_route",
|
||||||
|
"compile_mode", "requested_transition", "expected_previous_run_status_sha256",
|
||||||
|
"expected_candidate_content_digest", "expected_ingress_status_sha256",
|
||||||
|
"expected_s2_10_publish_status_sha256", "expected_plan_publish_status_sha256",
|
||||||
|
"expected_s2_30_publish_status_sha256", "expected_s2_30_artifact_manifest_sha256",
|
||||||
|
"expected_parent_stage2_release_sha256", "expected_s2_40_agent_sha256",
|
||||||
|
"expected_s2_40_executor_binding_sha256", "expected_s2_40_inline_code_receipt_sha256",
|
||||||
|
"host_cas_receipt_ref", "host_cas_receipt_sha256", "detached_admission_receipt_ref",
|
||||||
|
"detached_admission_receipt_sha256", "outer_execution_receipt_target_ref",
|
||||||
|
"review_receipt_refs"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_request.v1"},
|
||||||
|
"request_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"candidate_attempt_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"user_identity_hash": {"$ref": "#/$defs/sha256"},
|
||||||
|
"workspace_identity_hash": {"$ref": "#/$defs/sha256"},
|
||||||
|
"stage2_run_root_ref": {"type": "string", "pattern": "^stage2_runs/by-binding/[a-f0-9]{64}$"},
|
||||||
|
"entry_route": {"enum": ["TO_S2_40", "TO_S2_40_STATUS_ONLY"]},
|
||||||
|
"compile_mode": {"type": ["string", "null"], "enum": [null, "STRUCTURAL_FIXTURE", "SUBSET_CANARY", "PRODUCTION"]},
|
||||||
|
"requested_transition": {"enum": ["FREEZE", "RESUME"]},
|
||||||
|
"expected_previous_run_status_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"expected_candidate_content_digest": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"expected_ingress_status_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"expected_s2_10_publish_status_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"expected_plan_publish_status_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"expected_s2_30_publish_status_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"expected_s2_30_artifact_manifest_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"expected_parent_stage2_release_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"expected_s2_40_agent_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"expected_s2_40_executor_binding_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"expected_s2_40_inline_code_receipt_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"host_cas_receipt_ref": {"type": "string", "pattern": "^stage2_control/host_cas/[a-f0-9]{64}/S2_40/[^/]+/(?:FREEZE|RESUME)\\.json$"},
|
||||||
|
"host_cas_receipt_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"detached_admission_receipt_ref": {"const": "Default_Agent/Stage_2_Clean/manifest/stage2_deterministic_admission_receipt.json"},
|
||||||
|
"detached_admission_receipt_sha256": {"$ref": "#/$defs/bindable_sha256"},
|
||||||
|
"outer_execution_receipt_target_ref": {"type": "string", "pattern": "^stage2_runs/by-binding/[a-f0-9]{64}/control/s2_40_outer_execution_receipt\\.json$"},
|
||||||
|
"review_receipt_refs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string", "pattern": "^stage2_runs/by-binding/[a-f0-9]{64}/review/review_receipts/[^/]+\\.json$"},
|
||||||
|
"maxItems": 64,
|
||||||
|
"uniqueItems": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"if": {"properties": {"entry_route": {"const": "TO_S2_40_STATUS_ONLY"}}, "required": ["entry_route"]},
|
||||||
|
"then": {"properties": {"compile_mode": {"const": null}, "requested_transition": {"const": "FREEZE"}, "expected_previous_run_status_sha256": {"const": null}, "expected_candidate_content_digest": {"const": null}, "expected_s2_10_publish_status_sha256": {"const": null}, "expected_plan_publish_status_sha256": {"const": null}, "expected_s2_30_publish_status_sha256": {"const": null}, "expected_s2_30_artifact_manifest_sha256": {"const": null}, "review_receipt_refs": {"maxItems": 0}}}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": {"properties": {"entry_route": {"const": "TO_S2_40"}, "requested_transition": {"const": "FREEZE"}}, "required": ["entry_route", "requested_transition"]},
|
||||||
|
"then": {"properties": {"compile_mode": {"enum": ["STRUCTURAL_FIXTURE", "SUBSET_CANARY", "PRODUCTION"]}, "expected_candidate_content_digest": {"const": null}, "expected_previous_run_status_sha256": {"const": null}, "expected_s2_10_publish_status_sha256": {"$ref": "#/$defs/sha256"}, "expected_plan_publish_status_sha256": {"$ref": "#/$defs/sha256"}, "expected_s2_30_publish_status_sha256": {"$ref": "#/$defs/sha256"}, "expected_s2_30_artifact_manifest_sha256": {"$ref": "#/$defs/sha256"}, "review_receipt_refs": {"maxItems": 0}}}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": {"properties": {"requested_transition": {"const": "RESUME"}}, "required": ["requested_transition"]},
|
||||||
|
"then": {"properties": {"entry_route": {"const": "TO_S2_40"}, "compile_mode": {"enum": ["STRUCTURAL_FIXTURE", "SUBSET_CANARY", "PRODUCTION"]}, "expected_candidate_content_digest": {"$ref": "#/$defs/sha256"}, "expected_previous_run_status_sha256": {"$ref": "#/$defs/sha256"}, "expected_s2_10_publish_status_sha256": {"$ref": "#/$defs/sha256"}, "expected_plan_publish_status_sha256": {"$ref": "#/$defs/sha256"}, "expected_s2_30_publish_status_sha256": {"$ref": "#/$defs/sha256"}, "expected_s2_30_artifact_manifest_sha256": {"$ref": "#/$defs/sha256"}, "review_receipt_refs": {"minItems": 1}}}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"s2_40_host_cas_receipt": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["schema_version", "run_binding_digest", "stage_id", "candidate_attempt_id", "requested_transition", "expected_previous_run_status_sha256", "lease_id", "lease_status", "issued_at", "expires_at", "signature_attestation"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_host_cas_receipt.v1"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"}, "stage_id": {"const": "S2_40"},
|
||||||
|
"candidate_attempt_id": {"$ref": "#/$defs/nonempty_string"}, "requested_transition": {"enum": ["FREEZE", "RESUME"]},
|
||||||
|
"expected_previous_run_status_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"lease_id": {"$ref": "#/$defs/nonempty_string"}, "lease_status": {"const": "ACQUIRED"},
|
||||||
|
"issued_at": {"type": "string", "format": "date-time"}, "expires_at": {"type": "string", "format": "date-time"},
|
||||||
|
"signature_attestation": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"s2_40_code_executor_binding": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["schema_version", "stage_id", "execution_unit", "agent_path", "agent_sha256", "inline_code_sha256", "workflow_path", "workflow_sha256", "request_path", "timeout_seconds", "requirements", "network", "egress_profile_id", "fallback_allowed", "binding_digest"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_code_executor_binding.v1"}, "stage_id": {"const": "S2_40"},
|
||||||
|
"execution_unit": {"const": "Task_S2_40_deterministic_finalizer"}, "agent_path": {"const": "agent_scripts/Stage_2_S2_40.yml"},
|
||||||
|
"agent_sha256": {"$ref": "#/$defs/bindable_sha256"}, "inline_code_sha256": {"$ref": "#/$defs/bindable_sha256"},
|
||||||
|
"workflow_path": {"const": "workflows/S2_40_final_review_render_and_commit.yml"}, "workflow_sha256": {"$ref": "#/$defs/bindable_sha256"},
|
||||||
|
"request_path": {"const": "stage2_control/s2_40_request.json"}, "timeout_seconds": {"const": 300},
|
||||||
|
"requirements": {"const": "httpx==0.28.1"}, "network": {"const": "agent-network"},
|
||||||
|
"egress_profile_id": {"const": "LOCALDOCS_EXACT_BINARY_ONLY_V1"}, "fallback_allowed": {"const": false},
|
||||||
|
"binding_digest": {"$ref": "#/$defs/bindable_sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"s2_40_inline_code_receipt": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["schema_version", "workflow_id", "build_kind", "source_of_truth", "authoring_rewritten", "authoring", "deployment_projection", "canonical_code", "expected_parent_stage2_release_sha256", "full_code_mirrors", "task_contract", "parity_status"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_inline_code_receipt.v1"},
|
||||||
|
"workflow_id": {"const": "S2_40"},
|
||||||
|
"build_kind": {"const": "OFFLINE_AUTHORING_PROJECTION"},
|
||||||
|
"source_of_truth": {"const": "Stage_2_S2_40.yml"},
|
||||||
|
"authoring_rewritten": {"const": false},
|
||||||
|
"authoring": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["path", "sha256", "size_bytes", "unique_key_parse"],
|
||||||
|
"properties": {"path": {"const": "Stage_2_S2_40.yml"}, "sha256": {"$ref": "#/$defs/sha256"}, "size_bytes": {"type": "integer", "minimum": 1}, "unique_key_parse": {"const": "PASS"}}
|
||||||
|
},
|
||||||
|
"deployment_projection": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["path", "sha256", "size_bytes", "byte_identical_to_authoring", "canonical_task_semantics_sha256"],
|
||||||
|
"properties": {"path": {"const": "Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_40.yml"}, "sha256": {"$ref": "#/$defs/sha256"}, "size_bytes": {"type": "integer", "minimum": 1}, "byte_identical_to_authoring": {"const": true}, "canonical_task_semantics_sha256": {"$ref": "#/$defs/sha256"}}
|
||||||
|
},
|
||||||
|
"canonical_code": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["ast_status", "code_sha256", "code_size_bytes", "compile_status", "encoding", "expected_parent_stage2_release_sha256", "external_python_source_ref_count", "external_url_count", "extraction_transform", "forbidden_dynamic_call_count", "forbidden_import_count", "imports", "placeholder_count", "plaintext_secret_count", "yaml_pointer"],
|
||||||
|
"properties": {
|
||||||
|
"ast_status": {"const": "PASS"}, "code_sha256": {"$ref": "#/$defs/sha256"}, "code_size_bytes": {"type": "integer", "minimum": 1}, "compile_status": {"const": "PASS"}, "encoding": {"const": "UTF-8"},
|
||||||
|
"expected_parent_stage2_release_sha256": {"$ref": "#/$defs/sha256"}, "external_python_source_ref_count": {"const": 0}, "external_url_count": {"const": 0}, "extraction_transform": {"const": "NONE"}, "forbidden_dynamic_call_count": {"const": 0}, "forbidden_import_count": {"const": 0},
|
||||||
|
"imports": {"type": "array", "items": {"$ref": "#/$defs/nonempty_string"}, "uniqueItems": true}, "placeholder_count": {"const": 0}, "plaintext_secret_count": {"const": 0}, "yaml_pointer": {"const": "/Agent/Stages/0/tasks/0/parameters/code"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expected_parent_stage2_release_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"full_code_mirrors": {
|
||||||
|
"type": "array", "minItems": 2, "maxItems": 2,
|
||||||
|
"prefixItems": [
|
||||||
|
{"type": "object", "additionalProperties": false, "required": ["path", "sha256", "size_bytes", "byte_identical_to_canonical_code"], "properties": {"path": {"const": "Default_Agent/Stage_2_Clean/runtime/s2_40_commit.py"}, "sha256": {"$ref": "#/$defs/sha256"}, "size_bytes": {"type": "integer", "minimum": 1}, "byte_identical_to_canonical_code": {"const": true}}},
|
||||||
|
{"type": "object", "additionalProperties": false, "required": ["path", "sha256", "size_bytes", "byte_identical_to_canonical_code"], "properties": {"path": {"const": "Default_Agent/Stage_2_Clean/runtime/s2_40_commit.txt"}, "sha256": {"$ref": "#/$defs/sha256"}, "size_bytes": {"type": "integer", "minimum": 1}, "byte_identical_to_canonical_code": {"const": true}}}
|
||||||
|
], "items": false
|
||||||
|
},
|
||||||
|
"task_contract": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["agent", "authoring_rewritten", "canonical_task_semantics_sha256", "code_mirrors_byte_identical", "exactly_one_code_executor_run_code", "exactly_one_stage", "exactly_one_task", "mcp_servers", "projection_byte_identical_to_authoring", "stage", "task", "task_procedure"],
|
||||||
|
"properties": {
|
||||||
|
"agent": {"type": "object"}, "authoring_rewritten": {"const": false}, "canonical_task_semantics_sha256": {"$ref": "#/$defs/sha256"}, "code_mirrors_byte_identical": {"const": true}, "exactly_one_code_executor_run_code": {"const": true}, "exactly_one_stage": {"const": true}, "exactly_one_task": {"const": true}, "mcp_servers": {"type": "object"}, "projection_byte_identical_to_authoring": {"const": true}, "stage": {"type": "object"}, "task": {"type": "object"}, "task_procedure": {"type": "object"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parity_status": {"const": "PASS"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"s2_40_execution_receipt": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["schema_version", "ok", "workflow_id", "request_id", "run_binding_digest", "candidate_attempt_id", "requested_transition", "workflow_phase", "route", "candidate_content_digest", "run_status_path", "run_status_sha256", "error"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_execution_receipt.v1"}, "ok": {"type": "boolean"}, "workflow_id": {"const": "S2_40"}, "request_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"}, "candidate_attempt_id": {"$ref": "#/$defs/nonempty_string"}, "requested_transition": {"enum": ["FREEZE", "RESUME"]},
|
||||||
|
"workflow_phase": {"enum": ["CANDIDATE_FROZEN", "AWAITING_EXTERNAL_REVIEW", "READY_TO_COMMIT", "COMMITTED", "SUPERSEDED", "DIAGNOSTIC_ONLY"]},
|
||||||
|
"route": {"enum": ["STOP_TECHNICAL_INCOMPLETE", "CHECKPOINT_FROZEN", "WAIT_EXTERNAL_REVIEW", "CONTINUE_TO_COMMIT", "RESTART_FROM_S2_00", "RESTART_FROM_S2_10", "RESTART_FROM_S2_20", "RESTART_FROM_S2_30", "PACKAGE_COMMITTED"]},
|
||||||
|
"candidate_content_digest": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"run_status_path": {"oneOf": [{"type": "string", "pattern": "^stage2_runs/by-binding/[a-f0-9]{64}/control/run_status\\.json$"}, {"type": "null"}]},
|
||||||
|
"run_status_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]}, "error": {"type": ["object", "null"]}
|
||||||
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"if": {"properties": {"ok": {"const": true}}, "required": ["ok"]},
|
||||||
|
"then": {"properties": {"run_status_path": {"type": "string", "pattern": "^stage2_runs/by-binding/[a-f0-9]{64}/control/run_status\\.json$"}, "run_status_sha256": {"$ref": "#/$defs/sha256"}, "error": {"type": "null"}}},
|
||||||
|
"else": {"properties": {"error": {"type": "object"}}}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"oneOf": [
|
||||||
|
{"properties": {"run_status_path": {"type": "null"}, "run_status_sha256": {"type": "null"}}},
|
||||||
|
{"properties": {"run_status_path": {"type": "string", "pattern": "^stage2_runs/by-binding/[a-f0-9]{64}/control/run_status\\.json$"}, "run_status_sha256": {"$ref": "#/$defs/sha256"}}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"s2_40_outer_execution_receipt": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["schema_version", "workflow_id", "request_id", "run_binding_digest", "candidate_attempt_id", "requested_transition", "agent_sha256", "inline_code_sha256", "binding_sha256", "host_cas_receipt_sha256", "detached_admission_receipt_sha256", "workspace_isolation_status", "outer_result_status", "inner_result_sha256", "issued_at", "signature_attestation"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_outer_execution_receipt.v1"}, "workflow_id": {"const": "S2_40"}, "request_id": {"$ref": "#/$defs/nonempty_string"}, "run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"candidate_attempt_id": {"$ref": "#/$defs/nonempty_string"}, "requested_transition": {"enum": ["FREEZE", "RESUME"]},
|
||||||
|
"agent_sha256": {"$ref": "#/$defs/sha256"}, "inline_code_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"binding_sha256": {"$ref": "#/$defs/sha256"}, "host_cas_receipt_sha256": {"$ref": "#/$defs/sha256"}, "detached_admission_receipt_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"workspace_isolation_status": {"enum": ["VERIFIED", "FAILED"]}, "outer_result_status": {"enum": ["SUCCESS", "FAILED"]},
|
||||||
|
"inner_result_sha256": {"$ref": "#/$defs/sha256"}, "issued_at": {"type": "string", "format": "date-time"},
|
||||||
|
"signature_attestation": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"s2_40_commit_intent": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["schema_version", "producer_id", "run_binding_digest", "candidate_content_digest", "candidate_attempt_id", "candidate_manifest_core_sha256", "validation_report_sha256", "review_subject_digest", "review_receipt_sha256s", "stage2_package_sha256", "expected_artifacts", "previous_run_status_sha256", "request_sha256", "host_cas_receipt_sha256", "intent_digest"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_commit_intent.v1"}, "producer_id": {"const": "S2_40"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"}, "candidate_content_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"candidate_attempt_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"candidate_manifest_core_sha256": {"$ref": "#/$defs/sha256"}, "validation_report_sha256": {"$ref": "#/$defs/sha256"}, "review_subject_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"review_receipt_sha256s": {"type": "array", "items": {"$ref": "#/$defs/sha256"}, "uniqueItems": true}, "stage2_package_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"expected_artifacts": {"type": "array", "items": {"$ref": "#/$defs/s2_40_artifact_digest_row"}, "minItems": 8},
|
||||||
|
"previous_run_status_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"request_sha256": {"$ref": "#/$defs/sha256"}, "host_cas_receipt_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"intent_digest": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"s2_40_commit_result": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["schema_version", "producer_id", "run_binding_digest", "candidate_content_digest", "commit_intent_sha256", "artifact_rows", "artifact_set_digest", "readback_status", "conflicting_target_count", "missing_target_count_after_write", "commit_result_digest"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_commit_result.v1"}, "producer_id": {"const": "S2_40"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"}, "candidate_content_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"commit_intent_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"artifact_rows": {"type": "array", "items": {"$ref": "#/$defs/s2_40_artifact_digest_row"}, "minItems": 8},
|
||||||
|
"artifact_set_digest": {"$ref": "#/$defs/sha256"}, "readback_status": {"const": "PASS"}, "conflicting_target_count": {"const": 0}, "missing_target_count_after_write": {"const": 0},
|
||||||
|
"commit_result_digest": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
"loader_receipt": {
|
"loader_receipt": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
+310
-6
@@ -10,6 +10,11 @@
|
|||||||
{"$ref": "#/$defs/materialized_group_item"},
|
{"$ref": "#/$defs/materialized_group_item"},
|
||||||
{"$ref": "#/$defs/raw_joint_draft_output"},
|
{"$ref": "#/$defs/raw_joint_draft_output"},
|
||||||
{"$ref": "#/$defs/canonical_draft_atom"},
|
{"$ref": "#/$defs/canonical_draft_atom"},
|
||||||
|
{"$ref": "#/$defs/draft_part"},
|
||||||
|
{"$ref": "#/$defs/issue_patch_part"},
|
||||||
|
{"$ref": "#/$defs/worknote_part"},
|
||||||
|
{"$ref": "#/$defs/usage_part"},
|
||||||
|
{"$ref": "#/$defs/part_manifest_core"},
|
||||||
{"$ref": "#/$defs/item_receipt"},
|
{"$ref": "#/$defs/item_receipt"},
|
||||||
{"$ref": "#/$defs/canary_authorization"},
|
{"$ref": "#/$defs/canary_authorization"},
|
||||||
{"$ref": "#/$defs/cache_owner_completion_receipt"},
|
{"$ref": "#/$defs/cache_owner_completion_receipt"},
|
||||||
@@ -1398,6 +1403,285 @@
|
|||||||
"canonical_atom_sha256": {"$ref": "#/$defs/sha256"}
|
"canonical_atom_sha256": {"$ref": "#/$defs/sha256"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"execution_provenance": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"compile_mode",
|
||||||
|
"parent_stage2_release_sha256",
|
||||||
|
"s2_30_release_sha256",
|
||||||
|
"s2_30_agent_sha256",
|
||||||
|
"s2_30_binding_sha256",
|
||||||
|
"p00_prompt_sha256",
|
||||||
|
"p30_prompt_sha256",
|
||||||
|
"p31_rule_and_pack_sha256",
|
||||||
|
"p32_common_authority_sha256",
|
||||||
|
"s30_group_slice_sha256",
|
||||||
|
"s2_30_producer_contract_digest"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"compile_mode": {"$ref": "#/$defs/compile_mode"},
|
||||||
|
"parent_stage2_release_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"s2_30_release_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"s2_30_agent_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"s2_30_binding_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"p00_prompt_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"p30_prompt_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"p31_rule_and_pack_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"p32_common_authority_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"s30_group_slice_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"s2_30_producer_contract_digest": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"common_publish_provenance": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"compile_mode",
|
||||||
|
"parent_stage2_release_sha256",
|
||||||
|
"s2_30_release_sha256",
|
||||||
|
"s2_30_agent_sha256",
|
||||||
|
"s2_30_binding_sha256",
|
||||||
|
"p00_prompt_sha256",
|
||||||
|
"p30_prompt_sha256",
|
||||||
|
"s2_30_producer_contract_digest"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"compile_mode": {"$ref": "#/$defs/compile_mode"},
|
||||||
|
"parent_stage2_release_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"s2_30_release_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"s2_30_agent_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"s2_30_binding_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"p00_prompt_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"p30_prompt_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"s2_30_producer_contract_digest": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"draft_part": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"schema_version",
|
||||||
|
"claim_group_id",
|
||||||
|
"provenance",
|
||||||
|
"source_plan_sha256",
|
||||||
|
"drafting_permission",
|
||||||
|
"canonical_atoms",
|
||||||
|
"atomic_claim_coverage",
|
||||||
|
"part_sha256"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_30_draft_part.v1"},
|
||||||
|
"claim_group_id": {"$ref": "#/$defs/claim_group_id"},
|
||||||
|
"provenance": {"$ref": "#/$defs/execution_provenance"},
|
||||||
|
"source_plan_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"drafting_permission": {"$ref": "#/$defs/drafting_permission"},
|
||||||
|
"canonical_atoms": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/$defs/canonical_draft_atom"},
|
||||||
|
"minItems": 1
|
||||||
|
},
|
||||||
|
"atomic_claim_coverage": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/$defs/persisted_atomic_claim_coverage"},
|
||||||
|
"minItems": 1
|
||||||
|
},
|
||||||
|
"part_sha256": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"persisted_atomic_claim_coverage": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"atomic_claim_id",
|
||||||
|
"relief_atom_local_refs",
|
||||||
|
"cause_atom_local_refs",
|
||||||
|
"alignment_signature",
|
||||||
|
"coverage_status",
|
||||||
|
"missing_reason_codes"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"atomic_claim_id": {"$ref": "#/$defs/atomic_claim_id"},
|
||||||
|
"relief_atom_local_refs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/$defs/atom_id"},
|
||||||
|
"uniqueItems": true
|
||||||
|
},
|
||||||
|
"cause_atom_local_refs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/$defs/atom_id"},
|
||||||
|
"uniqueItems": true
|
||||||
|
},
|
||||||
|
"alignment_signature": {"$ref": "#/$defs/sha256"},
|
||||||
|
"coverage_status": {"enum": ["COMPLETE", "INCOMPLETE"]},
|
||||||
|
"missing_reason_codes": {"$ref": "#/$defs/string_set"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"issue_patch_part": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"schema_version",
|
||||||
|
"claim_group_id",
|
||||||
|
"provenance",
|
||||||
|
"source_plan_sha256",
|
||||||
|
"review_flags",
|
||||||
|
"missing_inputs",
|
||||||
|
"adverse_fact_rows",
|
||||||
|
"part_sha256"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_30_issue_patch_part.v1"},
|
||||||
|
"claim_group_id": {"$ref": "#/$defs/claim_group_id"},
|
||||||
|
"provenance": {"$ref": "#/$defs/execution_provenance"},
|
||||||
|
"source_plan_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"review_flags": {"$ref": "#/$defs/string_set"},
|
||||||
|
"missing_inputs": {"$ref": "#/$defs/string_set"},
|
||||||
|
"adverse_fact_rows": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/$defs/adverse_fact_row"}
|
||||||
|
},
|
||||||
|
"part_sha256": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"worknote_part": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"schema_version",
|
||||||
|
"claim_group_id",
|
||||||
|
"provenance",
|
||||||
|
"source_plan_sha256",
|
||||||
|
"worknote_atom_ids",
|
||||||
|
"review_flags",
|
||||||
|
"missing_inputs",
|
||||||
|
"part_sha256"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_30_worknote_part.v1"},
|
||||||
|
"claim_group_id": {"$ref": "#/$defs/claim_group_id"},
|
||||||
|
"provenance": {"$ref": "#/$defs/execution_provenance"},
|
||||||
|
"source_plan_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"worknote_atom_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/$defs/atom_id"},
|
||||||
|
"uniqueItems": true
|
||||||
|
},
|
||||||
|
"review_flags": {"$ref": "#/$defs/string_set"},
|
||||||
|
"missing_inputs": {"$ref": "#/$defs/string_set"},
|
||||||
|
"part_sha256": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"usage_part": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"schema_version",
|
||||||
|
"claim_group_id",
|
||||||
|
"provenance",
|
||||||
|
"request_id",
|
||||||
|
"model",
|
||||||
|
"reasoning_effort",
|
||||||
|
"verbosity",
|
||||||
|
"endpoint",
|
||||||
|
"input_tokens",
|
||||||
|
"cached_input_tokens",
|
||||||
|
"output_tokens",
|
||||||
|
"usage_observed",
|
||||||
|
"part_sha256"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_30_usage_part.v1"},
|
||||||
|
"claim_group_id": {"$ref": "#/$defs/claim_group_id"},
|
||||||
|
"provenance": {"$ref": "#/$defs/execution_provenance"},
|
||||||
|
"request_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"model": {"const": "gpt-5.6-sol"},
|
||||||
|
"reasoning_effort": {"const": "xhigh"},
|
||||||
|
"verbosity": {"const": "medium"},
|
||||||
|
"endpoint": {"const": "responses"},
|
||||||
|
"input_tokens": {"type": "integer", "minimum": 0},
|
||||||
|
"cached_input_tokens": {"type": "integer", "minimum": 0},
|
||||||
|
"output_tokens": {"type": "integer", "minimum": 0},
|
||||||
|
"usage_observed": {"type": "boolean"},
|
||||||
|
"part_sha256": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"part_manifest_row": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"claim_group_id",
|
||||||
|
"part_family",
|
||||||
|
"path",
|
||||||
|
"sha256",
|
||||||
|
"schema_ref"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"claim_group_id": {"$ref": "#/$defs/claim_group_id"},
|
||||||
|
"part_family": {"enum": ["DRAFT_PART", "ISSUE_PATCH", "WORKNOTE_PART", "USAGE_PART"]},
|
||||||
|
"path": {"$ref": "#/$defs/artifact_path"},
|
||||||
|
"sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"schema_ref": {
|
||||||
|
"enum": [
|
||||||
|
"schemas/draft_atoms.schema.json#/$defs/draft_part",
|
||||||
|
"schemas/draft_atoms.schema.json#/$defs/issue_patch_part",
|
||||||
|
"schemas/draft_atoms.schema.json#/$defs/worknote_part",
|
||||||
|
"schemas/draft_atoms.schema.json#/$defs/usage_part"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"part_manifest_core": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"schema_version",
|
||||||
|
"request_id",
|
||||||
|
"run_binding_digest",
|
||||||
|
"provenance",
|
||||||
|
"expected_claim_group_ids",
|
||||||
|
"part_rows",
|
||||||
|
"part_manifest_core_sha256"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_30_part_manifest_core.v1"},
|
||||||
|
"request_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"provenance": {"$ref": "#/$defs/common_publish_provenance"},
|
||||||
|
"expected_claim_group_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/$defs/claim_group_id"},
|
||||||
|
"minItems": 1,
|
||||||
|
"uniqueItems": true
|
||||||
|
},
|
||||||
|
"part_rows": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/$defs/part_manifest_row"},
|
||||||
|
"minItems": 4
|
||||||
|
},
|
||||||
|
"part_manifest_core_sha256": {"$ref": "#/$defs/sha256"}
|
||||||
|
},
|
||||||
|
"$comment": "Receipt-free by construction: item/cohort receipt paths or hashes must not appear in this core."
|
||||||
|
},
|
||||||
|
"receipt_ref": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["path", "sha256"],
|
||||||
|
"properties": {
|
||||||
|
"path": {"$ref": "#/$defs/artifact_path"},
|
||||||
|
"sha256": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"item_receipt_ref": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["claim_group_id", "path", "sha256"],
|
||||||
|
"properties": {
|
||||||
|
"claim_group_id": {"$ref": "#/$defs/claim_group_id"},
|
||||||
|
"path": {"$ref": "#/$defs/artifact_path"},
|
||||||
|
"sha256": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
"item_receipt": {
|
"item_receipt": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
@@ -1408,11 +1692,12 @@
|
|||||||
"cohort_id",
|
"cohort_id",
|
||||||
"attempt_no",
|
"attempt_no",
|
||||||
"claim_group_id",
|
"claim_group_id",
|
||||||
|
"provenance",
|
||||||
"dispatch_sha256",
|
"dispatch_sha256",
|
||||||
"raw_model_output_sha256",
|
"raw_model_output_sha256",
|
||||||
"source_plan_sha256",
|
"source_plan_sha256",
|
||||||
"canonical_atom_ids",
|
"canonical_atom_ids",
|
||||||
"artifact_manifest_sha256",
|
"part_manifest_core_sha256",
|
||||||
"validation_status",
|
"validation_status",
|
||||||
"persistence_status",
|
"persistence_status",
|
||||||
"read_back_verified",
|
"read_back_verified",
|
||||||
@@ -1425,6 +1710,7 @@
|
|||||||
"cohort_id": {"$ref": "#/$defs/nonempty_string"},
|
"cohort_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
"attempt_no": {"type": "integer", "minimum": 1, "maximum": 2},
|
"attempt_no": {"type": "integer", "minimum": 1, "maximum": 2},
|
||||||
"claim_group_id": {"$ref": "#/$defs/claim_group_id"},
|
"claim_group_id": {"$ref": "#/$defs/claim_group_id"},
|
||||||
|
"provenance": {"$ref": "#/$defs/execution_provenance"},
|
||||||
"dispatch_sha256": {"$ref": "#/$defs/sha256"},
|
"dispatch_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
"raw_model_output_sha256": {"$ref": "#/$defs/sha256"},
|
"raw_model_output_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
"source_plan_sha256": {"$ref": "#/$defs/sha256"},
|
"source_plan_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
@@ -1433,7 +1719,7 @@
|
|||||||
"items": {"$ref": "#/$defs/atom_id"},
|
"items": {"$ref": "#/$defs/atom_id"},
|
||||||
"uniqueItems": true
|
"uniqueItems": true
|
||||||
},
|
},
|
||||||
"artifact_manifest_sha256": {"$ref": "#/$defs/sha256"},
|
"part_manifest_core_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
"validation_status": {"enum": ["PASS", "FAIL"]},
|
"validation_status": {"enum": ["PASS", "FAIL"]},
|
||||||
"persistence_status": {"enum": ["PUBLISHED", "IDEMPOTENT_BYTE_IDENTICAL", "NOT_WRITTEN", "CONFLICT"]},
|
"persistence_status": {"enum": ["PUBLISHED", "IDEMPOTENT_BYTE_IDENTICAL", "NOT_WRITTEN", "CONFLICT"]},
|
||||||
"read_back_verified": {"type": "boolean"},
|
"read_back_verified": {"type": "boolean"},
|
||||||
@@ -1450,6 +1736,8 @@
|
|||||||
"cohort_id",
|
"cohort_id",
|
||||||
"dispatch_phase",
|
"dispatch_phase",
|
||||||
"attempt_no",
|
"attempt_no",
|
||||||
|
"provenance",
|
||||||
|
"part_manifest_core_sha256",
|
||||||
"input_claim_group_ids",
|
"input_claim_group_ids",
|
||||||
"valid_claim_group_ids",
|
"valid_claim_group_ids",
|
||||||
"repair_required_claim_group_ids",
|
"repair_required_claim_group_ids",
|
||||||
@@ -1466,6 +1754,8 @@
|
|||||||
"cohort_id": {"$ref": "#/$defs/nonempty_string"},
|
"cohort_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
"dispatch_phase": {"enum": ["CACHE_OWNER_SINGLETON", "FOLLOWER_BATCH"]},
|
"dispatch_phase": {"enum": ["CACHE_OWNER_SINGLETON", "FOLLOWER_BATCH"]},
|
||||||
"attempt_no": {"type": "integer", "minimum": 1, "maximum": 2},
|
"attempt_no": {"type": "integer", "minimum": 1, "maximum": 2},
|
||||||
|
"provenance": {"$ref": "#/$defs/common_publish_provenance"},
|
||||||
|
"part_manifest_core_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
"input_claim_group_ids": {
|
"input_claim_group_ids": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {"$ref": "#/$defs/claim_group_id"},
|
"items": {"$ref": "#/$defs/claim_group_id"},
|
||||||
@@ -1553,11 +1843,16 @@
|
|||||||
"run_binding_digest",
|
"run_binding_digest",
|
||||||
"parent_stage2_release_sha256",
|
"parent_stage2_release_sha256",
|
||||||
"s2_30_release_sha256",
|
"s2_30_release_sha256",
|
||||||
|
"compile_mode",
|
||||||
|
"provenance",
|
||||||
"expected_claim_group_ids",
|
"expected_claim_group_ids",
|
||||||
"published_claim_group_ids",
|
"published_claim_group_ids",
|
||||||
"terminal_failure_claim_group_ids",
|
"terminal_failure_claim_group_ids",
|
||||||
"terminal_cohort_receipt_sha256s",
|
"artifact_manifest_path",
|
||||||
|
"artifact_manifest_schema_ref",
|
||||||
"artifact_manifest_sha256",
|
"artifact_manifest_sha256",
|
||||||
|
"ordered_item_receipts",
|
||||||
|
"ordered_cohort_receipts",
|
||||||
"status",
|
"status",
|
||||||
"route",
|
"route",
|
||||||
"status_written_last",
|
"status_written_last",
|
||||||
@@ -1569,6 +1864,8 @@
|
|||||||
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
"parent_stage2_release_sha256": {"$ref": "#/$defs/sha256"},
|
"parent_stage2_release_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
"s2_30_release_sha256": {"$ref": "#/$defs/sha256"},
|
"s2_30_release_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"compile_mode": {"$ref": "#/$defs/compile_mode"},
|
||||||
|
"provenance": {"$ref": "#/$defs/common_publish_provenance"},
|
||||||
"expected_claim_group_ids": {
|
"expected_claim_group_ids": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {"$ref": "#/$defs/claim_group_id"},
|
"items": {"$ref": "#/$defs/claim_group_id"},
|
||||||
@@ -1585,13 +1882,20 @@
|
|||||||
"items": {"$ref": "#/$defs/claim_group_id"},
|
"items": {"$ref": "#/$defs/claim_group_id"},
|
||||||
"uniqueItems": true
|
"uniqueItems": true
|
||||||
},
|
},
|
||||||
"terminal_cohort_receipt_sha256s": {
|
"artifact_manifest_path": {"const": "map_s2_30/artifact_manifest.json"},
|
||||||
|
"artifact_manifest_schema_ref": {"const": "schemas/draft_atoms.schema.json#/$defs/part_manifest_core"},
|
||||||
|
"artifact_manifest_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"ordered_item_receipts": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {"$ref": "#/$defs/sha256"},
|
"items": {"$ref": "#/$defs/item_receipt_ref"},
|
||||||
|
"uniqueItems": true
|
||||||
|
},
|
||||||
|
"ordered_cohort_receipts": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/$defs/receipt_ref"},
|
||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
"uniqueItems": true
|
"uniqueItems": true
|
||||||
},
|
},
|
||||||
"artifact_manifest_sha256": {"$ref": "#/$defs/sha256"},
|
|
||||||
"status": {"enum": ["S2_30_COMPLETE", "TECHNICAL_INCOMPLETE"]},
|
"status": {"enum": ["S2_30_COMPLETE", "TECHNICAL_INCOMPLETE"]},
|
||||||
"route": {"enum": ["TO_S2_40", "STOP_TECHNICAL_INCOMPLETE"]},
|
"route": {"enum": ["TO_S2_40", "STOP_TECHNICAL_INCOMPLETE"]},
|
||||||
"status_written_last": {"const": true},
|
"status_written_last": {"const": true},
|
||||||
|
|||||||
+80
@@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://schemas.liti-agent.local/stage2/s2_40/migration_regression.schema.v1.json",
|
||||||
|
"title": "S2_40 structural fixture and regression manifest",
|
||||||
|
"schema_version": "stage2_s2_40_migration_regression.v1",
|
||||||
|
"oneOf": [
|
||||||
|
{"$ref": "#/$defs/fixture_payload"},
|
||||||
|
{"$ref": "#/$defs/regression_manifest"}
|
||||||
|
],
|
||||||
|
"$defs": {
|
||||||
|
"sha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
|
||||||
|
"nonempty": {"type": "string", "minLength": 1},
|
||||||
|
"path": {"type": "string", "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},
|
||||||
|
"fixture_header": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["fixture_case_id", "fixture_only", "production_admissible", "compile_mode", "oracle_kind", "source_locators"],
|
||||||
|
"properties": {
|
||||||
|
"fixture_case_id": {"$ref": "#/$defs/nonempty"},
|
||||||
|
"fixture_only": {"const": true},
|
||||||
|
"production_admissible": {"const": false},
|
||||||
|
"compile_mode": {"const": "STRUCTURAL_FIXTURE"},
|
||||||
|
"oracle_kind": {"enum": ["NORMAL", "MUTATION", "STATE", "BARRIER", "DIGEST", "REGRESSION_INDEX"]},
|
||||||
|
"source_locators": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expected_result": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["route", "run_technical_status", "artifact_technical_status", "legal_readiness", "expected_invariant_results", "expected_reason_codes"],
|
||||||
|
"properties": {
|
||||||
|
"route": {"enum": ["PACKAGE_COMMITTED", "WAIT_EXTERNAL_REVIEW", "STOP_TECHNICAL_INCOMPLETE", "RESTART_FROM_S2_00", "RESTART_FROM_S2_10", "RESTART_FROM_S2_20", "RESTART_FROM_S2_30", "CONTRACT_TEST_ONLY"]},
|
||||||
|
"run_technical_status": {"enum": ["CONSISTENT", "TECHNICAL_REVIEW_REQUIRED", "TECHNICAL_INCOMPLETE"]},
|
||||||
|
"artifact_technical_status": {"enum": ["CONSISTENT", "TECHNICAL_REVIEW_REQUIRED", "NOT_PRODUCED"]},
|
||||||
|
"legal_readiness": {"enum": ["READY_FOR_LAWYER_FILING_DECISION", "LAWYER_REVIEW_REQUIRED", "NOT_ASSESSED"]},
|
||||||
|
"expected_invariant_results": {"type": "object", "propertyNames": {"pattern": "^V(?:0[1-9]|1[0-8])$"}, "additionalProperties": {"enum": ["PASS", "FAIL", "UNEVALUABLE"]}},
|
||||||
|
"expected_reason_codes": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fixture_payload": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "header", "input", "mutations", "expected"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_fixture_payload.v1"},
|
||||||
|
"header": {"$ref": "#/$defs/fixture_header"},
|
||||||
|
"input": {"type": "object"},
|
||||||
|
"mutations": {"type": "array", "items": {"type": "object", "required": ["mutation_id", "target", "operation"], "properties": {"mutation_id": {"$ref": "#/$defs/nonempty"}, "target": {"$ref": "#/$defs/nonempty"}, "operation": {"$ref": "#/$defs/nonempty"}}, "additionalProperties": true}},
|
||||||
|
"expected": {"$ref": "#/$defs/expected_result"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"regression_row": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["fixture_case_id", "path", "raw_sha256", "oracle_kind", "expected_route", "expected_invariant_ids"],
|
||||||
|
"properties": {
|
||||||
|
"fixture_case_id": {"$ref": "#/$defs/nonempty"},
|
||||||
|
"path": {"$ref": "#/$defs/path"},
|
||||||
|
"raw_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"oracle_kind": {"$ref": "#/$defs/nonempty"},
|
||||||
|
"expected_route": {"$ref": "#/$defs/nonempty"},
|
||||||
|
"expected_invariant_ids": {"type": "array", "items": {"pattern": "^V(?:0[1-9]|1[0-8])$"}, "uniqueItems": true}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"regression_manifest": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "fixture_only", "production_admissible", "compile_mode", "rows", "test_sources", "manifest_digest"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_regression_manifest.v1"},
|
||||||
|
"fixture_only": {"const": true},
|
||||||
|
"production_admissible": {"const": false},
|
||||||
|
"compile_mode": {"const": "STRUCTURAL_FIXTURE"},
|
||||||
|
"rows": {"type": "array", "items": {"$ref": "#/$defs/regression_row"}, "minItems": 15},
|
||||||
|
"test_sources": {"type": "array", "items": {"type": "object", "required": ["path", "raw_sha256"], "properties": {"path": {"$ref": "#/$defs/path"}, "raw_sha256": {"$ref": "#/$defs/sha256"}}, "additionalProperties": false}, "minItems": 6},
|
||||||
|
"manifest_digest": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+324
@@ -0,0 +1,324 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://schemas.liti-agent.local/stage2/s2_40/package.schema.v1.json",
|
||||||
|
"title": "S2_40 candidate and sealed package contracts",
|
||||||
|
"schema_version": "stage2_s2_40_package.v1",
|
||||||
|
"oneOf": [
|
||||||
|
{"$ref": "#/$defs/candidate_manifest_core"},
|
||||||
|
{"$ref": "#/$defs/candidate_digest_envelope"},
|
||||||
|
{"$ref": "#/$defs/rendered_document_metadata"},
|
||||||
|
{"$ref": "#/$defs/attorney_worknotes_core"},
|
||||||
|
{"$ref": "#/$defs/attorney_worknotes"},
|
||||||
|
{"$ref": "#/$defs/usage_projection"},
|
||||||
|
{"$ref": "#/$defs/render_diagnostic"},
|
||||||
|
{"$ref": "#/$defs/review_policy_core"},
|
||||||
|
{"$ref": "#/$defs/review_request_basis"},
|
||||||
|
{"$ref": "#/$defs/frozen_source_rows"},
|
||||||
|
{"$ref": "#/$defs/legal_gate_snapshot"},
|
||||||
|
{"$ref": "#/$defs/lawyer_review_packet_core"},
|
||||||
|
{"$ref": "#/$defs/lawyer_review_packet"},
|
||||||
|
{"$ref": "#/$defs/validation_core"},
|
||||||
|
{"$ref": "#/$defs/validation_envelope_core"},
|
||||||
|
{"$ref": "#/$defs/validation_envelope"},
|
||||||
|
{"$ref": "#/$defs/review_subject"},
|
||||||
|
{"$ref": "#/$defs/stage2_package"}
|
||||||
|
],
|
||||||
|
"$defs": {
|
||||||
|
"sha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
|
||||||
|
"nonempty": {"type": "string", "minLength": 1},
|
||||||
|
"path": {"type": "string", "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))(?!.*\\x00).+$"},
|
||||||
|
"compile_mode": {"enum": ["STRUCTURAL_FIXTURE", "SUBSET_CANARY", "PRODUCTION"]},
|
||||||
|
"artifact_row": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["path", "raw_sha256", "content_type", "size_bytes", "schema_ref", "producer_id"],
|
||||||
|
"properties": {
|
||||||
|
"path": {"$ref": "#/$defs/path"},
|
||||||
|
"raw_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"content_type": {"$ref": "#/$defs/nonempty"},
|
||||||
|
"size_bytes": {"type": "integer", "minimum": 0},
|
||||||
|
"schema_ref": {"type": ["string", "null"]},
|
||||||
|
"producer_id": {"const": "S2_40"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependency_snapshot": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["parent_release_sha256", "upstream_barrier_sha256s", "ordered_source_digest"],
|
||||||
|
"properties": {
|
||||||
|
"parent_release_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"upstream_barrier_sha256s": {"type": "array", "items": {"$ref": "#/$defs/sha256"}, "minItems": 4, "uniqueItems": true},
|
||||||
|
"ordered_source_digest": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ordered_source_snapshot_preimage": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "ordered_sha256s", "snapshot_digest"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_ordered_source_snapshot_preimage.v1"},
|
||||||
|
"ordered_sha256s": {"type": "array", "items": {"$ref": "#/$defs/sha256"}},
|
||||||
|
"snapshot_digest": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source_hash_row": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["path", "sha256"],
|
||||||
|
"properties": {
|
||||||
|
"path": {"$ref": "#/$defs/path"},
|
||||||
|
"sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"size_bytes": {"type": "integer", "minimum": 0},
|
||||||
|
"ref_family": {"$ref": "#/$defs/nonempty"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"frozen_source_rows": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/$defs/source_hash_row"}
|
||||||
|
},
|
||||||
|
"attorney_worknotes_core": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "producer_id", "run_binding_digest", "rows"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_attorney_worknotes_core.v1"},
|
||||||
|
"producer_id": {"const": "S2_40"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"rows": {"type": "array", "items": {"type": "object", "required": ["worknote_id", "text", "source_refs"], "properties": {"worknote_id": {"$ref": "#/$defs/nonempty"}, "text": {"$ref": "#/$defs/nonempty"}, "source_refs": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "minItems": 1}}, "additionalProperties": false}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"render_diagnostic": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "render_errors", "render_absences", "independent_rerender_equal"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_render_diagnostic.v1"},
|
||||||
|
"render_errors": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true},
|
||||||
|
"render_absences": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true},
|
||||||
|
"independent_rerender_equal": {"type": "boolean"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"review_policy_core": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "policy_registry_sha256", "base_policy", "active_tags", "runtime_triggers", "required_receipt_types", "pre_receipt_ready_eligible"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_review_policy_core.v1"},
|
||||||
|
"policy_registry_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"base_policy": {"enum": ["STANDARD_ATTORNEY_REVIEW", "MANDATORY_SPECIALIST_REVIEW"]},
|
||||||
|
"active_tags": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true},
|
||||||
|
"runtime_triggers": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true},
|
||||||
|
"required_receipt_types": {"type": "array", "items": {"enum": ["STANDARD_ATTORNEY_REVIEW", "MANDATORY_SPECIALIST_REVIEW"]}, "uniqueItems": true},
|
||||||
|
"pre_receipt_ready_eligible": {"type": "boolean"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"review_request_basis": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "required_receipt_types", "scope_ids", "finding_ids", "policy_version", "policy_sha256", "reviewer_role", "reviewer_qualification", "release_snapshot_sha256s"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_review_request_basis.v1"},
|
||||||
|
"required_receipt_types": {"type": "array", "items": {"enum": ["STANDARD_ATTORNEY_REVIEW", "MANDATORY_SPECIALIST_REVIEW"]}, "uniqueItems": true},
|
||||||
|
"scope_ids": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true},
|
||||||
|
"finding_ids": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true},
|
||||||
|
"policy_version": {"$ref": "#/$defs/nonempty"},
|
||||||
|
"policy_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"reviewer_role": {"$ref": "#/$defs/nonempty"},
|
||||||
|
"reviewer_qualification": {"$ref": "#/$defs/nonempty"},
|
||||||
|
"release_snapshot_sha256s": {"type": "object", "additionalProperties": false, "required": ["parent", "authority", "corpus", "case_type_coverage"], "properties": {"parent": {"$ref": "#/$defs/sha256"}, "authority": {"$ref": "#/$defs/sha256"}, "corpus": {"$ref": "#/$defs/sha256"}, "case_type_coverage": {"$ref": "#/$defs/sha256"}}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"legal_gate_snapshot": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["binding", "authority", "renderer_branch", "rule_sub_rule", "review_policy", "case_type_coverage_137", "corpus", "law_value", "calculator", "required_receipts"],
|
||||||
|
"properties": {
|
||||||
|
"binding": {"type": "boolean"}, "authority": {"type": "boolean"},
|
||||||
|
"renderer_branch": {"type": "boolean"}, "rule_sub_rule": {"type": "boolean"},
|
||||||
|
"review_policy": {"type": "boolean"}, "case_type_coverage_137": {"type": "boolean"},
|
||||||
|
"corpus": {"type": "boolean"}, "law_value": {"type": "boolean"},
|
||||||
|
"calculator": {"type": "boolean"}, "required_receipts": {"type": "boolean"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"candidate_manifest_core": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "producer_id", "run_binding_digest", "compile_mode", "candidate_attempt_id", "dependency_snapshot", "artifacts"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_candidate_manifest_core.v1"},
|
||||||
|
"producer_id": {"const": "S2_40"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"compile_mode": {"$ref": "#/$defs/compile_mode"},
|
||||||
|
"candidate_attempt_id": {"$ref": "#/$defs/nonempty"},
|
||||||
|
"dependency_snapshot": {"$ref": "#/$defs/dependency_snapshot"},
|
||||||
|
"artifacts": {"type": "array", "items": {"$ref": "#/$defs/artifact_row"}, "minItems": 6}
|
||||||
|
},
|
||||||
|
"not": {"anyOf": [{"required": ["candidate_content_digest"]}, {"required": ["self_sha256"]}]}
|
||||||
|
},
|
||||||
|
"candidate_digest_envelope": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "domain_separator", "run_binding_digest", "dependency_snapshot_sha256", "candidate_manifest_core_sha256", "document_sha256s", "attorney_worknotes_core_sha256", "final_issue_ledger_sha256", "assumption_ledger_sha256", "validation_core_sha256", "ordered_source_dependency_snapshot_digest", "candidate_content_digest"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_candidate_content_digest.v1"},
|
||||||
|
"domain_separator": {"const": "STAGE2_S2_40_CANDIDATE_CONTENT_V1"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"dependency_snapshot_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"candidate_manifest_core_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"document_sha256s": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["claim_relief", "claim_cause", "pleading_draft"],
|
||||||
|
"properties": {"claim_relief": {"$ref": "#/$defs/sha256"}, "claim_cause": {"$ref": "#/$defs/sha256"}, "pleading_draft": {"$ref": "#/$defs/sha256"}}
|
||||||
|
},
|
||||||
|
"attorney_worknotes_core_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"final_issue_ledger_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"assumption_ledger_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"validation_core_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"ordered_source_dependency_snapshot_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"candidate_content_digest": {"$ref": "#/$defs/sha256"}
|
||||||
|
},
|
||||||
|
"not": {"anyOf": [{"required": ["review_subject_digest"]}, {"required": ["review_receipt_sha256s"]}, {"required": ["commit_result_sha256"]}, {"required": ["run_status_sha256"]}]}
|
||||||
|
},
|
||||||
|
"rendered_document_metadata": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "document_kind", "path", "raw_sha256", "normalization_version", "source_atom_ids", "renderer_branch_refs"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_rendered_document_metadata.v1"},
|
||||||
|
"document_kind": {"enum": ["CLAIM_RELIEF", "CLAIM_CAUSE", "PLEADING_DRAFT"]},
|
||||||
|
"path": {"$ref": "#/$defs/path"},
|
||||||
|
"raw_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"normalization_version": {"const": "NFC_LF_UTF8_V1"},
|
||||||
|
"source_atom_ids": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true},
|
||||||
|
"renderer_branch_refs": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attorney_worknotes": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "producer_id", "run_binding_digest", "candidate_content_digest", "rows"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_attorney_worknotes.v1"},
|
||||||
|
"producer_id": {"const": "S2_40"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"candidate_content_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"rows": {"type": "array", "items": {"type": "object", "required": ["worknote_id", "text", "source_refs"], "properties": {"worknote_id": {"$ref": "#/$defs/nonempty"}, "text": {"$ref": "#/$defs/nonempty"}, "source_refs": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "minItems": 1}}, "additionalProperties": false}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"usage_projection": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["schema_version", "producer_id", "run_binding_digest", "source_usage_part_sha256s", "rows", "conservation_status"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_usage_projection.v1"}, "producer_id": {"const": "S2_40"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"source_usage_part_sha256s": {"type": "array", "items": {"$ref": "#/$defs/sha256"}, "uniqueItems": true},
|
||||||
|
"rows": {"type": "array", "items": {"type": "object"}},
|
||||||
|
"conservation_status": {"const": "PASS"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lawyer_review_packet_core": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "candidate_content_digest", "finding_ids", "scope_ids", "document_refs", "legal_readiness"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_lawyer_review_packet_core.v1"},
|
||||||
|
"candidate_content_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"finding_ids": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true},
|
||||||
|
"scope_ids": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true},
|
||||||
|
"document_refs": {"type": "array", "items": {"$ref": "#/$defs/path"}, "minItems": 1, "uniqueItems": true},
|
||||||
|
"legal_readiness": {"enum": ["LAWYER_REVIEW_REQUIRED", "READY_FOR_LAWYER_FILING_DECISION"]}
|
||||||
|
},
|
||||||
|
"not": {"anyOf": [{"required": ["review_subject_digest"]}, {"required": ["request_id"]}]}
|
||||||
|
},
|
||||||
|
"lawyer_review_packet": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "lawyer_review_packet_core", "lawyer_review_packet_core_sha256", "review_subject_digest"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_lawyer_review_packet.v1"},
|
||||||
|
"lawyer_review_packet_core": {"$ref": "#/$defs/lawyer_review_packet_core"},
|
||||||
|
"lawyer_review_packet_core_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"review_subject_digest": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"validation_core": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "run_binding_digest", "compile_mode", "candidate_manifest_core_sha256", "invariant_results", "run_technical_status", "artifact_technical_status"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_validation_core.v1"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"compile_mode": {"$ref": "#/$defs/compile_mode"},
|
||||||
|
"candidate_manifest_core_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"invariant_results": {"type": "array", "items": {"$ref": "review_status.schema.json#/$defs/evaluation_result"}, "minItems": 18, "maxItems": 18},
|
||||||
|
"run_technical_status": {"$ref": "review_status.schema.json#/$defs/run_technical_status"},
|
||||||
|
"artifact_technical_status": {"$ref": "review_status.schema.json#/$defs/artifact_technical_status"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"validation_envelope_core": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["schema_version", "candidate_content_digest", "validation_core_sha256", "legal_readiness"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_validation_envelope_core.v1"},
|
||||||
|
"candidate_content_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"validation_core_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"legal_readiness": {"$ref": "review_status.schema.json#/$defs/legal_readiness"}
|
||||||
|
},
|
||||||
|
"not": {"anyOf": [{"required": ["review_subject_digest"]}, {"required": ["request_id"]}]}
|
||||||
|
},
|
||||||
|
"validation_envelope": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "validation_envelope_core", "validation_envelope_core_sha256", "review_subject_digest"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_validation_envelope.v1"},
|
||||||
|
"validation_envelope_core": {"$ref": "#/$defs/validation_envelope_core"},
|
||||||
|
"validation_envelope_core_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"review_subject_digest": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"review_request_core_binding": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["receipt_type", "review_request_core_sha256"],
|
||||||
|
"properties": {
|
||||||
|
"receipt_type": {"enum": ["STANDARD_ATTORNEY_REVIEW", "MANDATORY_SPECIALIST_REVIEW"]},
|
||||||
|
"review_request_core_sha256": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"review_subject": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["schema_version", "domain_separator", "candidate_content_digest", "review_request_core_bindings", "lawyer_review_packet_core_sha256", "validation_envelope_core_sha256", "finding_ids", "scope_ids", "review_policy_sha256", "release_sha256s", "review_subject_digest"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_review_subject.v1"}, "domain_separator": {"const": "STAGE2_S2_40_REVIEW_SUBJECT_V1"},
|
||||||
|
"candidate_content_digest": {"$ref": "#/$defs/sha256"}, "review_request_core_bindings": {"type": "array", "items": {"$ref": "#/$defs/review_request_core_binding"}, "uniqueItems": true}, "lawyer_review_packet_core_sha256": {"$ref": "#/$defs/sha256"}, "validation_envelope_core_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"finding_ids": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true}, "scope_ids": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}, "uniqueItems": true}, "review_policy_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"release_sha256s": {"type": "object", "additionalProperties": false, "required": ["parent", "authority", "corpus", "case_type_coverage"], "properties": {"parent": {"$ref": "#/$defs/sha256"}, "authority": {"$ref": "#/$defs/sha256"}, "corpus": {"$ref": "#/$defs/sha256"}, "case_type_coverage": {"$ref": "#/$defs/sha256"}}},
|
||||||
|
"review_subject_digest": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stage2_package": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "producer_id", "run_binding_digest", "candidate_content_digest", "compile_mode", "candidate_manifest_core_sha256", "artifacts", "validation_report_sha256", "review_policy_result_sha256", "review_receipt_sha256s", "filing_decision_receipt_sha256", "run_technical_status", "artifact_technical_status", "legal_readiness", "filing_permission"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_package.v1"},
|
||||||
|
"producer_id": {"const": "S2_40"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"candidate_content_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"compile_mode": {"$ref": "#/$defs/compile_mode"},
|
||||||
|
"candidate_manifest_core_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"artifacts": {"type": "array", "items": {"$ref": "#/$defs/artifact_row"}, "minItems": 8},
|
||||||
|
"validation_report_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"review_policy_result_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"review_receipt_sha256s": {"type": "array", "items": {"$ref": "#/$defs/sha256"}, "uniqueItems": true},
|
||||||
|
"filing_decision_receipt_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"run_technical_status": {"$ref": "review_status.schema.json#/$defs/run_technical_status"},
|
||||||
|
"artifact_technical_status": {"$ref": "review_status.schema.json#/$defs/artifact_technical_status"},
|
||||||
|
"legal_readiness": {"$ref": "review_status.schema.json#/$defs/legal_readiness"},
|
||||||
|
"filing_permission": {"const": "NOT_GRANTED"}
|
||||||
|
},
|
||||||
|
"not": {"anyOf": [{"required": ["artifact_set_digest"]}, {"required": ["commit_result_sha256"]}, {"required": ["run_status_sha256"]}]}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+253
-1
@@ -7,7 +7,16 @@
|
|||||||
{"$ref": "#/$defs/issue_ledger_base"},
|
{"$ref": "#/$defs/issue_ledger_base"},
|
||||||
{"$ref": "relief_plan.schema.json#/$defs/issue_ledger_plan"},
|
{"$ref": "relief_plan.schema.json#/$defs/issue_ledger_plan"},
|
||||||
{"$ref": "#/$defs/review_normalization_receipt"},
|
{"$ref": "#/$defs/review_normalization_receipt"},
|
||||||
{"$ref": "#/$defs/status_only_commit"}
|
{"$ref": "#/$defs/status_only_commit"},
|
||||||
|
{"$ref": "#/$defs/final_issue_ledger"},
|
||||||
|
{"$ref": "#/$defs/assumption_ledger"},
|
||||||
|
{"$ref": "#/$defs/review_policy_result"},
|
||||||
|
{"$ref": "#/$defs/review_request"},
|
||||||
|
{"$ref": "#/$defs/review_receipt"},
|
||||||
|
{"$ref": "#/$defs/lawyer_judgment_record"},
|
||||||
|
{"$ref": "#/$defs/filing_decision_receipt"},
|
||||||
|
{"$ref": "#/$defs/status_event"},
|
||||||
|
{"$ref": "#/$defs/run_status"}
|
||||||
],
|
],
|
||||||
"$defs": {
|
"$defs": {
|
||||||
"sha256": {
|
"sha256": {
|
||||||
@@ -349,6 +358,249 @@
|
|||||||
"issue_refs": {"$ref": "#/$defs/string_set"},
|
"issue_refs": {"$ref": "#/$defs/string_set"},
|
||||||
"commit_status": {"enum": ["STATUS_ONLY_COMMITTED", "STATUS_ONLY_FAILED"]}
|
"commit_status": {"enum": ["STATUS_ONLY_COMMITTED", "STATUS_ONLY_FAILED"]}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"evaluation_result": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["invariant_id", "evaluation_status", "finding_ids", "impact_scope", "source_refs", "expected", "observed", "reason_codes", "validator_algorithm_id"],
|
||||||
|
"properties": {
|
||||||
|
"invariant_id": {"pattern": "^V(?:0[1-9]|1[0-8])$"},
|
||||||
|
"evaluation_status": {"enum": ["PASS", "FAIL", "UNEVALUABLE"]},
|
||||||
|
"finding_ids": {"$ref": "#/$defs/string_set"},
|
||||||
|
"impact_scope": {"enum": ["OK", "REVIEW_REQUIRED", "INCOMPLETE"]},
|
||||||
|
"source_refs": {"$ref": "#/$defs/string_set"},
|
||||||
|
"expected": {},
|
||||||
|
"observed": {},
|
||||||
|
"reason_codes": {"$ref": "#/$defs/string_set"},
|
||||||
|
"validator_algorithm_id": {"$ref": "#/$defs/nonempty_string"}
|
||||||
|
},
|
||||||
|
"allOf": [{"if": {"properties": {"evaluation_status": {"const": "PASS"}}, "required": ["evaluation_status"]}, "then": {"properties": {"impact_scope": {"const": "OK"}}}}]
|
||||||
|
},
|
||||||
|
"final_issue_ledger": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "producer_id", "run_binding_digest", "compile_mode", "source_ledger_sha256s", "source_issue_part_sha256s", "issues", "conservation_status"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_final_issue_ledger.v1"},
|
||||||
|
"producer_id": {"const": "S2_40"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"compile_mode": {"enum": ["STRUCTURAL_FIXTURE", "SUBSET_CANARY", "PRODUCTION"]},
|
||||||
|
"source_ledger_sha256s": {"type": "array", "items": {"$ref": "#/$defs/sha256"}, "minItems": 2, "uniqueItems": true},
|
||||||
|
"source_issue_part_sha256s": {"type": "array", "items": {"$ref": "#/$defs/sha256"}, "uniqueItems": true},
|
||||||
|
"issues": {"type": "array", "items": {"$ref": "#/$defs/issue_row"}},
|
||||||
|
"conservation_status": {"enum": ["PASS", "FAIL"]}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assumption_row": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["assumption_id", "text", "status", "source_refs", "impact_scope"],
|
||||||
|
"properties": {
|
||||||
|
"assumption_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"text": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"status": {"enum": ["OPEN", "SUPPORTED", "REJECTED", "SUPERSEDED"]},
|
||||||
|
"source_refs": {"$ref": "#/$defs/string_set"},
|
||||||
|
"impact_scope": {"enum": ["GLOBAL", "CLAIM_GROUP", "ATOMIC_CLAIM", "DOCUMENT"]}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assumption_ledger": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "producer_id", "run_binding_digest", "compile_mode", "source_assumption_part_sha256s", "rows", "conservation_status"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_assumption_ledger.v1"},
|
||||||
|
"producer_id": {"const": "S2_40"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"compile_mode": {"enum": ["STRUCTURAL_FIXTURE", "SUBSET_CANARY", "PRODUCTION"]},
|
||||||
|
"source_assumption_part_sha256s": {"type": "array", "items": {"$ref": "#/$defs/sha256"}, "uniqueItems": true},
|
||||||
|
"rows": {"type": "array", "items": {"$ref": "#/$defs/assumption_row"}},
|
||||||
|
"conservation_status": {"const": "PASS"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"review_policy_result": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "candidate_content_digest", "policy_registry_sha256", "base_policy", "active_tags", "runtime_triggers", "required_receipt_types", "ready_eligible"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_review_policy_result.v1"},
|
||||||
|
"candidate_content_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"policy_registry_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"base_policy": {"enum": ["STANDARD_ATTORNEY_REVIEW", "MANDATORY_SPECIALIST_REVIEW"]},
|
||||||
|
"active_tags": {"$ref": "#/$defs/string_set"},
|
||||||
|
"runtime_triggers": {"$ref": "#/$defs/string_set"},
|
||||||
|
"required_receipt_types": {"$ref": "#/$defs/string_set"},
|
||||||
|
"ready_eligible": {"type": "boolean"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"review_request_core": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["candidate_content_digest", "receipt_type", "scope_ids", "finding_ids", "policy_version", "policy_sha256", "reviewer_role", "reviewer_qualification", "release_snapshot_sha256s"],
|
||||||
|
"properties": {
|
||||||
|
"candidate_content_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"receipt_type": {"enum": ["STANDARD_ATTORNEY_REVIEW", "MANDATORY_SPECIALIST_REVIEW"]},
|
||||||
|
"scope_ids": {"$ref": "#/$defs/string_set"},
|
||||||
|
"finding_ids": {"$ref": "#/$defs/string_set"},
|
||||||
|
"policy_version": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"policy_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"reviewer_role": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"reviewer_qualification": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"release_snapshot_sha256s": {
|
||||||
|
"type": "object", "additionalProperties": false,
|
||||||
|
"required": ["parent", "authority", "corpus", "case_type_coverage"],
|
||||||
|
"properties": {"parent": {"$ref": "#/$defs/sha256"}, "authority": {"$ref": "#/$defs/sha256"}, "corpus": {"$ref": "#/$defs/sha256"}, "case_type_coverage": {"$ref": "#/$defs/sha256"}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"not": {"anyOf": [{"required": ["request_id"]}, {"required": ["review_subject_digest"]}]}
|
||||||
|
},
|
||||||
|
"review_request": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "request_id", "review_subject_digest", "review_request_core", "review_request_core_sha256"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_review_request.v1"},
|
||||||
|
"request_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"review_subject_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"review_request_core": {"$ref": "#/$defs/review_request_core"},
|
||||||
|
"review_request_core_sha256": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"review_receipt": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "receipt_id", "request_id", "receipt_type", "candidate_content_digest", "review_subject_digest", "finding_ids", "scope_ids", "reviewer_role", "reviewer_qualification", "issuer_id", "key_id", "signature_algorithm", "signed_material_digest", "external_verification_attestation_sha256", "issued_at", "expires_at", "revocation_status", "cryptographic_verification_status", "review_disposition", "change_scope", "reason_codes"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_review_receipt.v1"},
|
||||||
|
"receipt_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"request_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"receipt_type": {"enum": ["STANDARD_ATTORNEY_REVIEW", "MANDATORY_SPECIALIST_REVIEW"]},
|
||||||
|
"candidate_content_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"review_subject_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"finding_ids": {"$ref": "#/$defs/string_set"},
|
||||||
|
"scope_ids": {"$ref": "#/$defs/string_set"},
|
||||||
|
"reviewer_role": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"reviewer_qualification": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"issuer_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"key_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"signature_algorithm": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"signed_material_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"external_verification_attestation_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"issued_at": {"type": "string", "format": "date-time"},
|
||||||
|
"expires_at": {"type": "string", "format": "date-time"},
|
||||||
|
"revocation_status": {"enum": ["NOT_REVOKED", "REVOKED", "UNKNOWN"]},
|
||||||
|
"cryptographic_verification_status": {"enum": ["VERIFIED_BY_EXTERNAL_ADAPTER", "PENDING_PLATFORM_ADMISSION", "INVALID"]},
|
||||||
|
"review_disposition": {"enum": ["APPROVE_AS_IS", "CONTENT_CHANGE_REQUIRED"]},
|
||||||
|
"change_scope": {"enum": ["NONE", "STAGE1_CONTEXT", "LEGAL_JUDGMENT", "PLAN_RULE_CALCULATION_BINDING", "DRAFTING_TEXT_ATOM"]},
|
||||||
|
"reason_codes": {"$ref": "#/$defs/string_set"}
|
||||||
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"if": {"properties": {"review_disposition": {"const": "APPROVE_AS_IS"}}, "required": ["review_disposition"]},
|
||||||
|
"then": {"properties": {"change_scope": {"const": "NONE"}}}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": {"properties": {"review_disposition": {"const": "CONTENT_CHANGE_REQUIRED"}}, "required": ["review_disposition"]},
|
||||||
|
"then": {"not": {"properties": {"change_scope": {"const": "NONE"}}, "required": ["change_scope"]}}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lawyer_judgment_record": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "candidate_content_digest", "review_subject_digest", "review_receipt_ids", "judgment", "writer_role"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_lawyer_judgment_record.v1"},
|
||||||
|
"candidate_content_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"review_subject_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"review_receipt_ids": {"$ref": "#/$defs/string_set"},
|
||||||
|
"judgment": {"enum": ["APPROVE_AS_IS", "CONTENT_CHANGE_REQUIRED"]},
|
||||||
|
"writer_role": {"const": "KOREAN_LAWYER_EXTERNAL_WRITER"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"filing_decision_receipt": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "decision_id", "candidate_content_digest", "stage2_package_sha256", "artifact_set_digest", "committed_run_status_sha256", "filing_scope", "reviewer_identity", "reviewer_qualification", "issuer_id", "key_id", "signature_algorithm", "signed_material_digest", "external_verification_attestation_sha256", "decision", "issued_at", "expires_at", "revocation_status", "signature_attestation"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_filing_decision_receipt.v1"},
|
||||||
|
"decision_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"candidate_content_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"stage2_package_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"artifact_set_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"committed_run_status_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"filing_scope": {"$ref": "#/$defs/string_set"},
|
||||||
|
"reviewer_identity": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"reviewer_qualification": {"const": "QUALIFIED_KOREAN_LAWYER"},
|
||||||
|
"issuer_id": {"$ref": "#/$defs/nonempty_string"}, "key_id": {"$ref": "#/$defs/nonempty_string"}, "signature_algorithm": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"signed_material_digest": {"$ref": "#/$defs/sha256"}, "external_verification_attestation_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"decision": {"enum": ["APPROVE_FOR_FILING", "DO_NOT_FILE"]},
|
||||||
|
"issued_at": {"type": "string", "format": "date-time"},
|
||||||
|
"expires_at": {"type": "string", "format": "date-time"},
|
||||||
|
"revocation_status": {"enum": ["NOT_REVOKED", "REVOKED", "UNKNOWN"]},
|
||||||
|
"signature_attestation": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"workflow_phase": {"enum": ["CANDIDATE_FROZEN", "AWAITING_EXTERNAL_REVIEW", "READY_TO_COMMIT", "COMMITTED", "SUPERSEDED", "DIAGNOSTIC_ONLY"]},
|
||||||
|
"status_event": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "writer_id", "run_binding_digest", "candidate_attempt_id", "workflow_phase", "route", "candidate_content_digest", "previous_run_status_sha256", "request_sha256", "host_cas_receipt_sha256", "event_digest"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_status_event.v1"},
|
||||||
|
"writer_id": {"const": "S2_40"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"candidate_attempt_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"workflow_phase": {"$ref": "#/$defs/workflow_phase"},
|
||||||
|
"route": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"candidate_content_digest": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"previous_run_status_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"request_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"host_cas_receipt_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"event_digest": {"$ref": "#/$defs/sha256"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"run_status": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["schema_version", "writer_id", "run_binding_digest", "candidate_attempt_id", "entry_route", "compile_mode", "workflow_phase", "route", "candidate_content_digest", "run_technical_status", "artifact_technical_status", "legal_readiness", "validation_report_sha256", "review_subject_digest", "review_receipt_sha256s", "stage2_package_sha256", "artifact_set_digest", "commit_intent_sha256", "commit_result_sha256", "request_sha256", "host_cas_receipt_sha256", "outer_execution_receipt_sha256", "previous_run_status_sha256", "status_event_sha256", "barrier_written_last", "readback_verified"],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {"const": "stage2_s2_40_run_status.v1"},
|
||||||
|
"writer_id": {"const": "S2_40"},
|
||||||
|
"run_binding_digest": {"$ref": "#/$defs/sha256"},
|
||||||
|
"candidate_attempt_id": {"$ref": "#/$defs/nonempty_string"},
|
||||||
|
"entry_route": {"enum": ["TO_S2_40", "TO_S2_40_STATUS_ONLY"]},
|
||||||
|
"compile_mode": {"type": ["string", "null"], "enum": [null, "STRUCTURAL_FIXTURE", "SUBSET_CANARY", "PRODUCTION"]},
|
||||||
|
"workflow_phase": {"$ref": "#/$defs/workflow_phase"},
|
||||||
|
"route": {"enum": ["STOP_TECHNICAL_INCOMPLETE", "CHECKPOINT_FROZEN", "WAIT_EXTERNAL_REVIEW", "CONTINUE_TO_COMMIT", "RESTART_FROM_S2_00", "RESTART_FROM_S2_10", "RESTART_FROM_S2_20", "RESTART_FROM_S2_30", "PACKAGE_COMMITTED"]},
|
||||||
|
"candidate_content_digest": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"run_technical_status": {"$ref": "#/$defs/run_technical_status"},
|
||||||
|
"artifact_technical_status": {"$ref": "#/$defs/artifact_technical_status"},
|
||||||
|
"legal_readiness": {"$ref": "#/$defs/legal_readiness"},
|
||||||
|
"validation_report_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"review_subject_digest": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"review_receipt_sha256s": {"type": "array", "items": {"$ref": "#/$defs/sha256"}, "uniqueItems": true},
|
||||||
|
"stage2_package_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"artifact_set_digest": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"commit_intent_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"commit_result_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"request_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"host_cas_receipt_sha256": {"$ref": "#/$defs/sha256"},
|
||||||
|
"outer_execution_receipt_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"previous_run_status_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"status_event_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]},
|
||||||
|
"barrier_written_last": {"const": true},
|
||||||
|
"readback_verified": {"const": true}
|
||||||
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"if": {"properties": {"entry_route": {"const": "TO_S2_40_STATUS_ONLY"}}, "required": ["entry_route"]},
|
||||||
|
"then": {"properties": {"status_event_sha256": {"type": "null"}}}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": {"properties": {"entry_route": {"const": "TO_S2_40"}}, "required": ["entry_route"]},
|
||||||
|
"then": {"properties": {"status_event_sha256": {"$ref": "#/$defs/sha256"}}}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+108
-4
@@ -1236,6 +1236,75 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"s2_40_fixture_binding": {
|
||||||
|
"binding_status": "OFFLINE_BYTES_BOUND_LEGAL_AND_LIVE_PENDING",
|
||||||
|
"fixture_manifest_path": "tests/fixtures/s2_40/regression_manifest.json",
|
||||||
|
"fixture_manifest_sha256": "ac5d68c8d4061d789aa5c773cd1ffb37728c33c7725785a9dd1f4bea63ec88ce",
|
||||||
|
"logical_fixture_count": 15,
|
||||||
|
"payload_file_count": 15,
|
||||||
|
"payloads": [
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/candidate_digest_noncycle.json",
|
||||||
|
"sha256": "74885602909bd19ca1678de781cac26afd34c2f386e98fc0370fd6ff6fb7b8bf"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/cost_provisional_execution_numbering.json",
|
||||||
|
"sha256": "17c380492f5bc56ecf675cf9683784b2e463241c7d0589b7b496e7a381145c9b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/exhibit_object_party_title_completeness.json",
|
||||||
|
"sha256": "40045822f45b2a57805504744e751ee420a6187d0790d5a0f442f529ffedcf7e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/part_set_missing_duplicate_or_cross_group.json",
|
||||||
|
"sha256": "379273497ee23d4a515135771ba4d3bb5ff0e38325ad40eb4e0a0008cd6b88f4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/partial_write_readback_barrier.json",
|
||||||
|
"sha256": "467387c12a4cc6469d243de9f0a7948276adb558a99b3b661ccd74a5d17629cf"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/relief_cause_crossmatch_mutations.json",
|
||||||
|
"sha256": "e8d148fef61920039e36abd0cfb771cc6e2fe3aa1a9cc0b0b0502c38ddf70567"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/renderer_ast_slot_branch_mutations.json",
|
||||||
|
"sha256": "e70ecac4e71de962dfe8bb025a42c73c7c03cfba32c29319523d19f49e8260d0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/review_policy_state_aggregation.json",
|
||||||
|
"sha256": "953fc301703ff353de4df3cf46ed4e90b6bf4ce8268b08705e16151ffc8c3a21"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/review_receipt_approve_resume.json",
|
||||||
|
"sha256": "c5b84e34358761750e8e9e7666de6532e0f22a31f32bf027f24efb256a1af5d5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/review_receipt_content_change_supersede.json",
|
||||||
|
"sha256": "400d4aeb455e5c3ec26c75a552ea91e9af79cb2329998caa1ad72d1be18da8e7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/review_receipt_missing_or_invalid.json",
|
||||||
|
"sha256": "a88645be90a9e0943358d83f7ea105193dd4b4dfebea4519702a17f69615dc65"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/same_binding_idempotence_and_commit_conflict.json",
|
||||||
|
"sha256": "5536793236fa44da601dc8cf65d94dac32b18ebc2e8dc525a6df92106add57ad"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/v01_v18_invariant_matrix.json",
|
||||||
|
"sha256": "e4c0a5476647fa1860cd685a15aad6d465a36b4f6b648c8e2a066c8747780eb8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/valid_full_candidate_and_commit.json",
|
||||||
|
"sha256": "e98e8dc55ab60f8f875ad2f58c1aec35eb1da3882371c22c3e232e0e9ae46c2f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/fixtures/s2_40/valid_status_only_diagnostic.json",
|
||||||
|
"sha256": "5676088f5d79168cbae5e1481da4b7cfd9241d06c935378e06dd2af56aae6285"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"test_sources": [
|
"test_sources": [
|
||||||
{
|
{
|
||||||
"path": "tests/s2_00/test_canonical_ids.py",
|
"path": "tests/s2_00/test_canonical_ids.py",
|
||||||
@@ -1244,7 +1313,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "tests/s2_00/test_cluster_bundle_compile.py",
|
"path": "tests/s2_00/test_cluster_bundle_compile.py",
|
||||||
"sha256": "aa31cc87246a5c8f6b82e98b9f86dd654bc9306dfc7849af19ec16cfd5137a2a",
|
"sha256": "b643d2d4018ebe3349c64304063edf7ba0194fc92fb1fe9337eca3f384ae794a",
|
||||||
"status": "DEV_HASH_BOUND"
|
"status": "DEV_HASH_BOUND"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1254,7 +1323,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "tests/s2_00/test_failure_and_atomic_publish.py",
|
"path": "tests/s2_00/test_failure_and_atomic_publish.py",
|
||||||
"sha256": "128a82b4e413132c0ff4a61ae2dc7f85b6c1c050ea33ccf4e53f23ccc993aba6",
|
"sha256": "613f0c1b32dc26c302365fc62f36c8ede7204e1ced4920e797a1651af8354527",
|
||||||
"status": "DEV_HASH_BOUND"
|
"status": "DEV_HASH_BOUND"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1299,18 +1368,53 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "tests/s2_20/test_plan_publish_and_release.py",
|
"path": "tests/s2_20/test_plan_publish_and_release.py",
|
||||||
"sha256": "eb5a96109d95d4b78473f79129c441f9be9b015bba1157dfa4d4d3616d03525e",
|
"sha256": "ce0dc52cca2c6744be1051346ebecabac8c4ccbd97d697f7645eadd35aebd1e8",
|
||||||
"status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING"
|
"status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "tests/s2_20/test_regression_manifest_closure.py",
|
"path": "tests/s2_20/test_regression_manifest_closure.py",
|
||||||
"sha256": "886ae584a7846acdb5499d52e1ae8ec177e8d1dc34aa1d43e99dbebafa255516",
|
"sha256": "01ac49ab1ba79f158da9da78313407e3a67a95cf539113c617701a74c29b463f",
|
||||||
"status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING"
|
"status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "tests/s2_20/test_retrieval_and_pack.py",
|
"path": "tests/s2_20/test_retrieval_and_pack.py",
|
||||||
"sha256": "c314cf27511b0fe065b8961196084a28b1c7c5cb2fed3dd5c8765172ba40462f",
|
"sha256": "c314cf27511b0fe065b8961196084a28b1c7c5cb2fed3dd5c8765172ba40462f",
|
||||||
"status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING"
|
"status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_agent_and_inline_parity.py",
|
||||||
|
"sha256": "04cc4a14790eceea6ec8a9331212ca6e767ef96994b5b26483392d23145417ae",
|
||||||
|
"status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_c40_reduce_and_conservation.py",
|
||||||
|
"sha256": "f610eced056420277c67294cdfe76d559073d88a6f42d3e8c5dae8f219fa82d9",
|
||||||
|
"status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_c45_closed_render.py",
|
||||||
|
"sha256": "b7cad96735b1a0158644f41703b4f279fdd8b6bc68438f0d7d773b8415ebb460",
|
||||||
|
"status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_commit_barrier_and_idempotence.py",
|
||||||
|
"sha256": "714e2f05397d5dcc31159739363f11c9bbaf5a091de55b465e0b474c21f90ea0",
|
||||||
|
"status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_release_closure.py",
|
||||||
|
"sha256": "89139e0286facd52c3ce91a1c7b382a584dfc3e6bc337fcb55fbc8eed59f5933",
|
||||||
|
"status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_review_state_machine.py",
|
||||||
|
"sha256": "f4dab9bb651b4a3c818c73162b972c8ac3df0f50fbe056e392a12673139b9b4a",
|
||||||
|
"status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_v01_v18.py",
|
||||||
|
"sha256": "72be88be1aa9d495b5c9387e45b75e86f44ac76ffe15c04b5ba33ba21fa8943f",
|
||||||
|
"status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-26
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"schema_version": "stage2_s2_30_partial_write_barrier_fixture.v1",
|
"schema_version": "stage2_s2_30_partial_write_barrier_fixture.v2",
|
||||||
"cases": [
|
"cases": [
|
||||||
{
|
{
|
||||||
"case_id": "partial_write_preserve_existing_rows",
|
"case_id": "partial_write_preserve_existing_rows",
|
||||||
@@ -23,35 +23,28 @@
|
|||||||
"expected": {"failure_code": "READ_BACK_HASH_MISMATCH", "publish_status_written": false, "downstream_allowed": false}
|
"expected": {"failure_code": "READ_BACK_HASH_MISMATCH", "publish_status_written": false, "downstream_allowed": false}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"case_id": "status_last_success",
|
"case_id": "status_last_success_shape",
|
||||||
"expected_claim_group_ids": ["CG-1111111111111111", "CG-2222222222222222"],
|
"manifest": {
|
||||||
"read_back_verified_claim_group_ids": ["CG-1111111111111111", "CG-2222222222222222"],
|
"path": "map_s2_30/artifact_manifest.json",
|
||||||
"terminal_cohort_receipt_sha256s": ["5555555555555555555555555555555555555555555555555555555555555555"],
|
"schema_ref": "schemas/draft_atoms.schema.json#/$defs/part_manifest_core",
|
||||||
"artifact_manifest_sha256": "6666666666666666666666666666666666666666666666666666666666666666",
|
"receipt_free": true,
|
||||||
"publish_status": {
|
"part_manifest_core_sha256": "5555555555555555555555555555555555555555555555555555555555555555",
|
||||||
"schema_version": "stage2_s2_30_publish_status.v1",
|
"raw_sha256": "6666666666666666666666666666666666666666666666666666666666666666"
|
||||||
"request_id": "REQ-S2-30-PUBLISH-001",
|
|
||||||
"run_binding_digest": "7777777777777777777777777777777777777777777777777777777777777777",
|
|
||||||
"parent_stage2_release_sha256": "8888888888888888888888888888888888888888888888888888888888888888",
|
|
||||||
"s2_30_release_sha256": "9999999999999999999999999999999999999999999999999999999999999999",
|
|
||||||
"expected_claim_group_ids": ["CG-1111111111111111", "CG-2222222222222222"],
|
|
||||||
"published_claim_group_ids": ["CG-1111111111111111", "CG-2222222222222222"],
|
|
||||||
"terminal_failure_claim_group_ids": [],
|
|
||||||
"terminal_cohort_receipt_sha256s": ["5555555555555555555555555555555555555555555555555555555555555555"],
|
|
||||||
"artifact_manifest_sha256": "6666666666666666666666666666666666666666666666666666666666666666",
|
|
||||||
"status": "S2_30_COMPLETE",
|
|
||||||
"route": "TO_S2_40",
|
|
||||||
"status_written_last": true,
|
|
||||||
"status_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
||||||
},
|
},
|
||||||
"expected": {"schema_valid": true, "write_order_last": "map_s2_30/s2_30_publish_status.json", "route": "TO_S2_40"}
|
"ordered_item_receipts": [
|
||||||
|
{"claim_group_id": "CG-1111111111111111", "path": "map_s2_30/item_receipts/CG-1111111111111111.json", "sha256": "7777777777777777777777777777777777777777777777777777777777777777"}
|
||||||
|
],
|
||||||
|
"ordered_cohort_receipts": [
|
||||||
|
{"path": "map_s2_30/cohort_receipts/COHORT-001/attempt-1.json", "sha256": "8888888888888888888888888888888888888888888888888888888888888888"}
|
||||||
|
],
|
||||||
|
"expected": {"write_order_last": "map_s2_30/s2_30_publish_status.json", "route": "TO_S2_40", "directory_scan_used": false}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"mutations": [
|
"mutations": [
|
||||||
{"mutation_id": "S30-BARRIER-EARLY", "single_defect": "STATUS_WRITTEN_BEFORE_ALL_READ_BACK", "expected_code": "STATUS_LAST_BARRIER_VIOLATION"},
|
{"mutation_id": "S30-BARRIER-EARLY", "single_defect": "STATUS_WRITTEN_BEFORE_ALL_READ_BACK", "expected_code": "STATUS_LAST_BARRIER_VIOLATION"},
|
||||||
{"mutation_id": "S30-BARRIER-MISSING-GROUP", "single_defect": "EXPECTED_GROUP_NOT_TERMINAL", "expected_code": "GROUP_TERMINAL_SET_MISMATCH"},
|
{"mutation_id": "S30-BARRIER-MISSING-GROUP", "single_defect": "EXPECTED_GROUP_NOT_TERMINAL", "expected_code": "PUBLISH_GROUP_SET_MISMATCH"},
|
||||||
{"mutation_id": "S30-BARRIER-OVERWRITE", "single_defect": "EXISTING_NONIDENTICAL_ARTIFACT_OVERWRITTEN", "expected_code": "IMMUTABLE_OUTPUT_CONFLICT"},
|
{"mutation_id": "S30-BARRIER-UNENUMERATED-PART", "single_defect": "PART_NOT_IN_MANIFEST", "expected_code": "UNENUMERATED_PART_INPUT_FORBIDDEN"},
|
||||||
{"mutation_id": "S30-BARRIER-DELETE-PARTIAL", "single_defect": "SUCCESSFUL_EXISTING_ARTIFACT_DELETED_AFTER_PEER_FAILURE", "expected_code": "PARTIAL_WRITE_PRESERVATION_FAILED"},
|
{"mutation_id": "S30-BARRIER-MANIFEST-RECEIPT-CYCLE", "single_defect": "RECEIPT_HASH_INSERTED_IN_MANIFEST_CORE", "expected_code": "MANIFEST_RECEIPT_REFERENCE_FORBIDDEN"},
|
||||||
{"mutation_id": "S30-BARRIER-TO-S2-40-ON-FAILURE", "single_defect": "DOWNSTREAM_ROUTE_OPEN_WITH_TERMINAL_FAILURE", "expected_code": "DOWNSTREAM_ROUTE_MUST_STOP"}
|
{"mutation_id": "S30-BARRIER-TO-S2-40-ON-FAILURE", "single_defect": "DOWNSTREAM_ROUTE_OPEN_WITH_TERMINAL_FAILURE", "expected_code": "PUBLISH_SUCCESS_ROUTE_INVALID"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage2_s2_30_persisted_handoff_chain_fixture.v1",
|
||||||
|
"contract": {
|
||||||
|
"manifest_path": "map_s2_30/artifact_manifest.json",
|
||||||
|
"manifest_schema_ref": "schemas/draft_atoms.schema.json#/$defs/part_manifest_core",
|
||||||
|
"part_families": ["DRAFT_PART", "ISSUE_PATCH", "USAGE_PART", "WORKNOTE_PART"],
|
||||||
|
"rows_per_claim_group": 4,
|
||||||
|
"manifest_receipt_free": true,
|
||||||
|
"item_and_cohort_receipts_reference": "part_manifest_core_sha256",
|
||||||
|
"publish_status_written_last": true,
|
||||||
|
"runtime_directory_scan_allowed": false,
|
||||||
|
"runtime_glob_allowed": false,
|
||||||
|
"group_provenance_fields": [
|
||||||
|
"compile_mode",
|
||||||
|
"p00_prompt_sha256",
|
||||||
|
"p30_prompt_sha256",
|
||||||
|
"p31_rule_and_pack_sha256",
|
||||||
|
"p32_common_authority_sha256",
|
||||||
|
"parent_stage2_release_sha256",
|
||||||
|
"s2_30_agent_sha256",
|
||||||
|
"s2_30_binding_sha256",
|
||||||
|
"s2_30_producer_contract_digest",
|
||||||
|
"s2_30_release_sha256",
|
||||||
|
"s30_group_slice_sha256"
|
||||||
|
],
|
||||||
|
"ordered_receipt_indexes": ["ordered_item_receipts", "ordered_cohort_receipts"]
|
||||||
|
},
|
||||||
|
"mutations": [
|
||||||
|
{
|
||||||
|
"mutation_id": "S30-HANDOFF-MISSING-PART",
|
||||||
|
"single_defect": "MANIFEST_ROW_HAS_NO_EXPLICIT_PART_BYTES",
|
||||||
|
"expected_code": "MANIFEST_PART_BYTES_MISSING"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mutation_id": "S30-HANDOFF-UNENUMERATED-PART",
|
||||||
|
"single_defect": "PART_BYTES_NOT_LISTED_BY_MANIFEST",
|
||||||
|
"expected_code": "UNENUMERATED_PART_INPUT_FORBIDDEN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mutation_id": "S30-HANDOFF-RECEIPT-CYCLE",
|
||||||
|
"single_defect": "MANIFEST_CORE_CONTAINS_RECEIPT_REFERENCE",
|
||||||
|
"expected_code": "MANIFEST_RECEIPT_REFERENCE_FORBIDDEN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mutation_id": "S30-HANDOFF-MODE-DRIFT",
|
||||||
|
"single_defect": "PUBLISH_COMPILE_MODE_DIFFERS_FROM_MANIFEST",
|
||||||
|
"expected_code": "PUBLISH_COMPILE_MODE_MISMATCH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mutation_id": "S30-HANDOFF-RECEIPT-CORE-DRIFT",
|
||||||
|
"single_defect": "RECEIPT_REFERENCES_DIFFERENT_MANIFEST_CORE_HASH",
|
||||||
|
"expected_code": "RECEIPT_MANIFEST_CORE_HASH_MISMATCH"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+6
-5
@@ -2,7 +2,7 @@
|
|||||||
"schema_version": "stage2_s2_30_regression_manifest.v1",
|
"schema_version": "stage2_s2_30_regression_manifest.v1",
|
||||||
"status": "OFFLINE_STRUCTURAL_ORACLES_BOUND_LIVE_AND_LEGAL_ORACLES_PENDING",
|
"status": "OFFLINE_STRUCTURAL_ORACLES_BOUND_LIVE_AND_LEGAL_ORACLES_PENDING",
|
||||||
"hash_algorithm": "SHA-256",
|
"hash_algorithm": "SHA-256",
|
||||||
"fixture_count_excluding_manifest": 13,
|
"fixture_count_excluding_manifest": 14,
|
||||||
"test_source_count": 5,
|
"test_source_count": 5,
|
||||||
"fixtures": [
|
"fixtures": [
|
||||||
{"filename": "adverse_fact_worknote_policy.json", "sha256": "5b8e9101385f8d65b0332c3be63cb9a22063920675cc86aee51e04da7820aef7", "expected_oracles": ["ADVERSE_FACT_SCHEMA_AND_POLICY_ORACLE"]},
|
{"filename": "adverse_fact_worknote_policy.json", "sha256": "5b8e9101385f8d65b0332c3be63cb9a22063920675cc86aee51e04da7820aef7", "expected_oracles": ["ADVERSE_FACT_SCHEMA_AND_POLICY_ORACLE"]},
|
||||||
@@ -14,16 +14,17 @@
|
|||||||
{"filename": "invalid_preassembled_prompt_item.json", "sha256": "6655402c669e5b40f2e7028a5c0c89571d8e1597dbdef567b4c9d8e2eed15a79", "expected_oracles": ["PARSE_AND_CONTRACT_ORACLE"]},
|
{"filename": "invalid_preassembled_prompt_item.json", "sha256": "6655402c669e5b40f2e7028a5c0c89571d8e1597dbdef567b4c9d8e2eed15a79", "expected_oracles": ["PARSE_AND_CONTRACT_ORACLE"]},
|
||||||
{"filename": "invalid_reference_output.json", "sha256": "eb2982a09f4948baadc27ae49c4f125426074333ea4a626e9c56ddc5c24ce273", "expected_oracles": ["PARSE_AND_CONTRACT_ORACLE"]},
|
{"filename": "invalid_reference_output.json", "sha256": "eb2982a09f4948baadc27ae49c4f125426074333ea4a626e9c56ddc5c24ce273", "expected_oracles": ["PARSE_AND_CONTRACT_ORACLE"]},
|
||||||
{"filename": "owner_singleton_dispatch.json", "sha256": "38f9c364591fcdfe60d0dea9f69eb86b2f3b883929f6d60d06a7c35ff376fca6", "expected_oracles": ["OWNER_DISPATCH_AND_MATERIALIZATION_ORACLE"]},
|
{"filename": "owner_singleton_dispatch.json", "sha256": "38f9c364591fcdfe60d0dea9f69eb86b2f3b883929f6d60d06a7c35ff376fca6", "expected_oracles": ["OWNER_DISPATCH_AND_MATERIALIZATION_ORACLE"]},
|
||||||
{"filename": "partial_write_publish_barrier.json", "sha256": "89f8318c7dbec7bf0095e34194f0a585ea5f4cbce9826af64c30d370612fa1f5", "expected_oracles": ["PARSE_AND_CONTRACT_ORACLE"]},
|
{"filename": "partial_write_publish_barrier.json", "sha256": "0d878b946ebf810e8d0d8da74af33e56f35e164b04076a3867344b95d5f953eb", "expected_oracles": ["STATUS_LAST_AND_PARTIAL_WRITE_ORACLE"]},
|
||||||
|
{"filename": "persisted_handoff_chain.json", "sha256": "54d6d781a60640744cba4b9c2f88a0013a6e58656c9c80a809e39fd4ba7c1097", "expected_oracles": ["RECEIPT_FREE_EXACT_HANDOFF_CHAIN_ORACLE"]},
|
||||||
{"filename": "rule_requirement_admission_gap.json", "sha256": "27dfad63f3508c5d40521270707d84b2bc0c58f035f47d0cde1ae2abc54f9a15", "expected_oracles": ["PARSE_AND_CONTRACT_ORACLE"]},
|
{"filename": "rule_requirement_admission_gap.json", "sha256": "27dfad63f3508c5d40521270707d84b2bc0c58f035f47d0cde1ae2abc54f9a15", "expected_oracles": ["PARSE_AND_CONTRACT_ORACLE"]},
|
||||||
{"filename": "technical_failure.json", "sha256": "bdcc6410e7515edc73d6b154d5b618b3ec34273ddc7269dc3e3ce55a6eb35fa6", "expected_oracles": ["PARSE_AND_CONTRACT_ORACLE"]},
|
{"filename": "technical_failure.json", "sha256": "bdcc6410e7515edc73d6b154d5b618b3ec34273ddc7269dc3e3ce55a6eb35fa6", "expected_oracles": ["PARSE_AND_CONTRACT_ORACLE"]},
|
||||||
{"filename": "valid_joint_draft_output.json", "sha256": "0135616c9375602693af8e12c3c16cf2031eaf3d9b74783bec9b5065846327e8", "expected_oracles": ["PARSE_AND_CONTRACT_ORACLE"]}
|
{"filename": "valid_joint_draft_output.json", "sha256": "0135616c9375602693af8e12c3c16cf2031eaf3d9b74783bec9b5065846327e8", "expected_oracles": ["PARSE_AND_CONTRACT_ORACLE"]}
|
||||||
],
|
],
|
||||||
"test_sources": [
|
"test_sources": [
|
||||||
{"path": "tests/s2_30/test_agent_contract.py", "sha256": "e7d0f069f8c60ff5c5194a9ea9289376e2fd81a50d1b4b18b1b6abf5f2827529"},
|
{"path": "tests/s2_30/test_agent_contract.py", "sha256": "26a37b0b5fa6c4273ed7deff6b0429e3223869533fc2384f4c364fe88d554da5"},
|
||||||
{"path": "tests/s2_30/test_dispatch_materialization.py", "sha256": "9d6ca17f012566b3d7423195ef19908694604a81059bfc10bd0c34dab1c24665"},
|
{"path": "tests/s2_30/test_dispatch_materialization.py", "sha256": "9d6ca17f012566b3d7423195ef19908694604a81059bfc10bd0c34dab1c24665"},
|
||||||
{"path": "tests/s2_30/test_draft_atom_contract.py", "sha256": "cb1ecf7131921c37aa4a6dc31f9053f21526f153407b90d1efffd472b0d68923"},
|
{"path": "tests/s2_30/test_draft_atom_contract.py", "sha256": "cb1ecf7131921c37aa4a6dc31f9053f21526f153407b90d1efffd472b0d68923"},
|
||||||
{"path": "tests/s2_30/test_prompt_cache_and_boundaries.py", "sha256": "9063ca0a247cf06f55e94e586a7ab8ac21facd9f6c607ce4799047aea365e287"},
|
{"path": "tests/s2_30/test_prompt_cache_and_boundaries.py", "sha256": "1b9b562418bc24be6db09dc1d35f71845033adbc693aeb41fbba07be16fa7c9d"},
|
||||||
{"path": "tests/s2_30/test_release_adapter_retry.py", "sha256": "b15505567db55689527056d608a1cb12d3c605e9df860afba607a6e3e276a0ef"}
|
{"path": "tests/s2_30/test_release_adapter_retry.py", "sha256": "e5af75d54f236eb8ba99b194a06aa31f02fddd5c6aa9ff7385401905359b292c"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"schema_version":"stage2_s2_40_fixture_payload.v1","header":{"fixture_case_id":"S40-F015","fixture_only":true,"production_admissible":false,"compile_mode":"STRUCTURAL_FIXTURE","oracle_kind":"DIGEST","source_locators":["fixture://s2_40/noncycle"]},"input":{"ordered_graph":["candidate_manifest_core","candidate_content_digest","review_subject","commit_intent","artifact_set_digest","commit_result","run_status"]},"mutations":[{"mutation_id":"CYCLE","target":"candidate_manifest_core/candidate_content_digest","operation":"ADD"}],"expected":{"route":"CONTRACT_TEST_ONLY","run_technical_status":"TECHNICAL_INCOMPLETE","artifact_technical_status":"NOT_PRODUCED","legal_readiness":"NOT_ASSESSED","expected_invariant_results":{"V14":"FAIL"},"expected_reason_codes":["CANDIDATE_DIGEST_MATERIAL_CYCLE"]}}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage2_s2_40_fixture_payload.v1",
|
||||||
|
"header": {
|
||||||
|
"fixture_case_id": "S40-F007",
|
||||||
|
"fixture_only": true,
|
||||||
|
"production_admissible": false,
|
||||||
|
"compile_mode": "STRUCTURAL_FIXTURE",
|
||||||
|
"oracle_kind": "MUTATION",
|
||||||
|
"source_locators": ["fixture://s2_40/numbering"]
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"renderer_ids": ["R01", "R03", "R05", "R06"],
|
||||||
|
"actio_structural_dependencies": [
|
||||||
|
"route", "recipient", "period_one", "period_two", "three_cap", "provisional_execution"
|
||||||
|
],
|
||||||
|
"special_case_structural_dependencies": [
|
||||||
|
"reserved_share_temporal", "forced_apology_prohibition", "simultaneous_performance",
|
||||||
|
"CE-01", "CE-13", "loan_contract_temporal"
|
||||||
|
],
|
||||||
|
"actio_legal_approval_status": "PENDING_LEGAL_ADMISSION",
|
||||||
|
"special_case_legal_approval_status": "PENDING_LEGAL_ADMISSION"
|
||||||
|
},
|
||||||
|
"mutations": [
|
||||||
|
{"mutation_id":"INELIGIBLE","target":"provisional/R03","operation":"ENABLE"},
|
||||||
|
{"mutation_id":"NUMBER","target":"relief/order","operation":"PERMUTE"},
|
||||||
|
{"mutation_id":"ACTIO_ROUTE","target":"actio.route","operation":"DROP"},
|
||||||
|
{"mutation_id":"ACTIO_RECIPIENT","target":"actio.recipient","operation":"SUBSTITUTE"},
|
||||||
|
{"mutation_id":"ACTIO_PERIODS","target":"actio.periods","operation":"COLLAPSE_TWO_TO_ONE"},
|
||||||
|
{"mutation_id":"ACTIO_THREE_CAP","target":"actio.three_cap","operation":"REMOVE_RECEIPT"},
|
||||||
|
{"mutation_id":"RESERVED_SHARE_TEMPORAL","target":"reserved_share.temporal_branch","operation":"STALE"},
|
||||||
|
{"mutation_id":"FORCED_APOLOGY","target":"relief.forced_apology","operation":"ENABLE"},
|
||||||
|
{"mutation_id":"SIMULTANEOUS_PERFORMANCE","target":"relief.counter_performance","operation":"DROP"},
|
||||||
|
{"mutation_id":"CE01_CE13_LOAN_TEMPORAL","target":"law_value_receipts","operation":"REMOVE_TEMPORAL_SCOPE"}
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"route": "WAIT_EXTERNAL_REVIEW",
|
||||||
|
"run_technical_status": "TECHNICAL_REVIEW_REQUIRED",
|
||||||
|
"artifact_technical_status": "TECHNICAL_REVIEW_REQUIRED",
|
||||||
|
"legal_readiness": "LAWYER_REVIEW_REQUIRED",
|
||||||
|
"expected_invariant_results": {"V10":"FAIL", "V18":"FAIL"},
|
||||||
|
"expected_reason_codes": ["PROVISIONAL_OR_NUMBERING_INVALID"]
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"schema_version":"stage2_s2_40_fixture_payload.v1","header":{"fixture_case_id":"S40-F008","fixture_only":true,"production_admissible":false,"compile_mode":"STRUCTURAL_FIXTURE","oracle_kind":"MUTATION","source_locators":["fixture://s2_40/completeness"]},"input":{"refs":["party:P1","object:O1","exhibit:E1"]},"mutations":[{"mutation_id":"DANGLING","target":"exhibit:E1","operation":"DELETE"}],"expected":{"route":"WAIT_EXTERNAL_REVIEW","run_technical_status":"TECHNICAL_REVIEW_REQUIRED","artifact_technical_status":"TECHNICAL_REVIEW_REQUIRED","legal_readiness":"LAWYER_REVIEW_REQUIRED","expected_invariant_results":{"V15":"FAIL","V17":"FAIL"},"expected_reason_codes":["REFERENCE_DANGLING"]}}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"schema_version":"stage2_s2_40_fixture_payload.v1","header":{"fixture_case_id":"S40-F003","fixture_only":true,"production_admissible":false,"compile_mode":"STRUCTURAL_FIXTURE","oracle_kind":"MUTATION","source_locators":["fixture://s2_40/parts"]},"input":{"expected_groups":["G1","G2"]},"mutations":[{"mutation_id":"MISSING","target":"parts/G2","operation":"DELETE"},{"mutation_id":"DUP","target":"parts/G1","operation":"DUPLICATE"},{"mutation_id":"CROSS","target":"parts/G1/group_id","operation":"REPLACE"}],"expected":{"route":"STOP_TECHNICAL_INCOMPLETE","run_technical_status":"TECHNICAL_INCOMPLETE","artifact_technical_status":"NOT_PRODUCED","legal_readiness":"NOT_ASSESSED","expected_invariant_results":{"V01":"FAIL","V02":"FAIL"},"expected_reason_codes":["PART_SET_NOT_EXACT"]}}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"schema_version":"stage2_s2_40_fixture_payload.v1","header":{"fixture_case_id":"S40-F013","fixture_only":true,"production_admissible":false,"compile_mode":"STRUCTURAL_FIXTURE","oracle_kind":"BARRIER","source_locators":["fixture://s2_40/partial-write"]},"input":{"barrier_last":true},"mutations":[{"mutation_id":"PARTIAL","target":"final/artifact","operation":"TRUNCATE"}],"expected":{"route":"STOP_TECHNICAL_INCOMPLETE","run_technical_status":"TECHNICAL_INCOMPLETE","artifact_technical_status":"NOT_PRODUCED","legal_readiness":"NOT_ASSESSED","expected_invariant_results":{"V14":"FAIL"},"expected_reason_codes":["READBACK_MISMATCH_NO_FINAL_BARRIER"]}}
|
||||||
+213
@@ -0,0 +1,213 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage2_s2_40_regression_manifest.v1",
|
||||||
|
"fixture_only": true,
|
||||||
|
"production_admissible": false,
|
||||||
|
"compile_mode": "STRUCTURAL_FIXTURE",
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F001",
|
||||||
|
"path": "tests/fixtures/s2_40/valid_full_candidate_and_commit.json",
|
||||||
|
"raw_sha256": "e98e8dc55ab60f8f875ad2f58c1aec35eb1da3882371c22c3e232e0e9ae46c2f",
|
||||||
|
"oracle_kind": "NORMAL",
|
||||||
|
"expected_route": "PACKAGE_COMMITTED",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V01",
|
||||||
|
"V18"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F002",
|
||||||
|
"path": "tests/fixtures/s2_40/valid_status_only_diagnostic.json",
|
||||||
|
"raw_sha256": "5676088f5d79168cbae5e1481da4b7cfd9241d06c935378e06dd2af56aae6285",
|
||||||
|
"oracle_kind": "BARRIER",
|
||||||
|
"expected_route": "STOP_TECHNICAL_INCOMPLETE",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V01"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F003",
|
||||||
|
"path": "tests/fixtures/s2_40/part_set_missing_duplicate_or_cross_group.json",
|
||||||
|
"raw_sha256": "379273497ee23d4a515135771ba4d3bb5ff0e38325ad40eb4e0a0008cd6b88f4",
|
||||||
|
"oracle_kind": "MUTATION",
|
||||||
|
"expected_route": "STOP_TECHNICAL_INCOMPLETE",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V01",
|
||||||
|
"V02"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F004",
|
||||||
|
"path": "tests/fixtures/s2_40/renderer_ast_slot_branch_mutations.json",
|
||||||
|
"raw_sha256": "e70ecac4e71de962dfe8bb025a42c73c7c03cfba32c29319523d19f49e8260d0",
|
||||||
|
"oracle_kind": "MUTATION",
|
||||||
|
"expected_route": "STOP_TECHNICAL_INCOMPLETE",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V10",
|
||||||
|
"V18"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F005",
|
||||||
|
"path": "tests/fixtures/s2_40/relief_cause_crossmatch_mutations.json",
|
||||||
|
"raw_sha256": "e8d148fef61920039e36abd0cfb771cc6e2fe3aa1a9cc0b0b0502c38ddf70567",
|
||||||
|
"oracle_kind": "MUTATION",
|
||||||
|
"expected_route": "WAIT_EXTERNAL_REVIEW",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V09",
|
||||||
|
"V11",
|
||||||
|
"V18"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F006",
|
||||||
|
"path": "tests/fixtures/s2_40/v01_v18_invariant_matrix.json",
|
||||||
|
"raw_sha256": "e4c0a5476647fa1860cd685a15aad6d465a36b4f6b648c8e2a066c8747780eb8",
|
||||||
|
"oracle_kind": "MUTATION",
|
||||||
|
"expected_route": "CONTRACT_TEST_ONLY",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V01",
|
||||||
|
"V02",
|
||||||
|
"V03",
|
||||||
|
"V04",
|
||||||
|
"V05",
|
||||||
|
"V06",
|
||||||
|
"V07",
|
||||||
|
"V08",
|
||||||
|
"V09",
|
||||||
|
"V10",
|
||||||
|
"V11",
|
||||||
|
"V12",
|
||||||
|
"V13",
|
||||||
|
"V14",
|
||||||
|
"V15",
|
||||||
|
"V16",
|
||||||
|
"V17",
|
||||||
|
"V18"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F007",
|
||||||
|
"path": "tests/fixtures/s2_40/cost_provisional_execution_numbering.json",
|
||||||
|
"raw_sha256": "17c380492f5bc56ecf675cf9683784b2e463241c7d0589b7b496e7a381145c9b",
|
||||||
|
"oracle_kind": "MUTATION",
|
||||||
|
"expected_route": "WAIT_EXTERNAL_REVIEW",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V10",
|
||||||
|
"V18"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F008",
|
||||||
|
"path": "tests/fixtures/s2_40/exhibit_object_party_title_completeness.json",
|
||||||
|
"raw_sha256": "40045822f45b2a57805504744e751ee420a6187d0790d5a0f442f529ffedcf7e",
|
||||||
|
"oracle_kind": "MUTATION",
|
||||||
|
"expected_route": "WAIT_EXTERNAL_REVIEW",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V15",
|
||||||
|
"V17"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F009",
|
||||||
|
"path": "tests/fixtures/s2_40/review_policy_state_aggregation.json",
|
||||||
|
"raw_sha256": "953fc301703ff353de4df3cf46ed4e90b6bf4ce8268b08705e16151ffc8c3a21",
|
||||||
|
"oracle_kind": "STATE",
|
||||||
|
"expected_route": "WAIT_EXTERNAL_REVIEW",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V18"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F010",
|
||||||
|
"path": "tests/fixtures/s2_40/review_receipt_missing_or_invalid.json",
|
||||||
|
"raw_sha256": "a88645be90a9e0943358d83f7ea105193dd4b4dfebea4519702a17f69615dc65",
|
||||||
|
"oracle_kind": "STATE",
|
||||||
|
"expected_route": "WAIT_EXTERNAL_REVIEW",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V14"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F011",
|
||||||
|
"path": "tests/fixtures/s2_40/review_receipt_approve_resume.json",
|
||||||
|
"raw_sha256": "c5b84e34358761750e8e9e7666de6532e0f22a31f32bf027f24efb256a1af5d5",
|
||||||
|
"oracle_kind": "STATE",
|
||||||
|
"expected_route": "PACKAGE_COMMITTED",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V14"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F012",
|
||||||
|
"path": "tests/fixtures/s2_40/review_receipt_content_change_supersede.json",
|
||||||
|
"raw_sha256": "400d4aeb455e5c3ec26c75a552ea91e9af79cb2329998caa1ad72d1be18da8e7",
|
||||||
|
"oracle_kind": "STATE",
|
||||||
|
"expected_route": "RESTART_FROM_S2_30",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V14"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F013",
|
||||||
|
"path": "tests/fixtures/s2_40/partial_write_readback_barrier.json",
|
||||||
|
"raw_sha256": "467387c12a4cc6469d243de9f0a7948276adb558a99b3b661ccd74a5d17629cf",
|
||||||
|
"oracle_kind": "BARRIER",
|
||||||
|
"expected_route": "STOP_TECHNICAL_INCOMPLETE",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V14"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F014",
|
||||||
|
"path": "tests/fixtures/s2_40/same_binding_idempotence_and_commit_conflict.json",
|
||||||
|
"raw_sha256": "5536793236fa44da601dc8cf65d94dac32b18ebc2e8dc525a6df92106add57ad",
|
||||||
|
"oracle_kind": "BARRIER",
|
||||||
|
"expected_route": "STOP_TECHNICAL_INCOMPLETE",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V14"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_case_id": "S40-F015",
|
||||||
|
"path": "tests/fixtures/s2_40/candidate_digest_noncycle.json",
|
||||||
|
"raw_sha256": "74885602909bd19ca1678de781cac26afd34c2f386e98fc0370fd6ff6fb7b8bf",
|
||||||
|
"oracle_kind": "DIGEST",
|
||||||
|
"expected_route": "CONTRACT_TEST_ONLY",
|
||||||
|
"expected_invariant_ids": [
|
||||||
|
"V14"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"test_sources": [
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_agent_and_inline_parity.py",
|
||||||
|
"raw_sha256": "04cc4a14790eceea6ec8a9331212ca6e767ef96994b5b26483392d23145417ae"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_c40_reduce_and_conservation.py",
|
||||||
|
"raw_sha256": "f610eced056420277c67294cdfe76d559073d88a6f42d3e8c5dae8f219fa82d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_c45_closed_render.py",
|
||||||
|
"raw_sha256": "b7cad96735b1a0158644f41703b4f279fdd8b6bc68438f0d7d773b8415ebb460"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_commit_barrier_and_idempotence.py",
|
||||||
|
"raw_sha256": "714e2f05397d5dcc31159739363f11c9bbaf5a091de55b465e0b474c21f90ea0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_release_closure.py",
|
||||||
|
"raw_sha256": "89139e0286facd52c3ce91a1c7b382a584dfc3e6bc337fcb55fbc8eed59f5933"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_review_state_machine.py",
|
||||||
|
"raw_sha256": "f4dab9bb651b4a3c818c73162b972c8ac3df0f50fbe056e392a12673139b9b4a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tests/s2_40/test_v01_v18.py",
|
||||||
|
"raw_sha256": "72be88be1aa9d495b5c9387e45b75e86f44ac76ffe15c04b5ba33ba21fa8943f"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"manifest_digest": "186e1f1ef33aa20f84004206adcfe0fb3dbfe0c06383411547548d51e20770ab"
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"schema_version":"stage2_s2_40_fixture_payload.v1","header":{"fixture_case_id":"S40-F005","fixture_only":true,"production_admissible":false,"compile_mode":"STRUCTURAL_FIXTURE","oracle_kind":"MUTATION","source_locators":["fixture://s2_40/crossmatch"]},"input":{"atomic_claim_ids":["A1"]},"mutations":[{"mutation_id":"PARTY","target":"cause/party_ref","operation":"REPLACE"},{"mutation_id":"AMOUNT","target":"cause/amount","operation":"REPLACE"}],"expected":{"route":"WAIT_EXTERNAL_REVIEW","run_technical_status":"TECHNICAL_REVIEW_REQUIRED","artifact_technical_status":"TECHNICAL_REVIEW_REQUIRED","legal_readiness":"LAWYER_REVIEW_REQUIRED","expected_invariant_results":{"V09":"FAIL","V11":"FAIL","V18":"FAIL"},"expected_reason_codes":["RELIEF_CAUSE_MISMATCH"]}}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage2_s2_40_fixture_payload.v1",
|
||||||
|
"header": {
|
||||||
|
"fixture_case_id": "S40-F004",
|
||||||
|
"fixture_only": true,
|
||||||
|
"production_admissible": false,
|
||||||
|
"compile_mode": "STRUCTURAL_FIXTURE",
|
||||||
|
"oracle_kind": "MUTATION",
|
||||||
|
"source_locators": ["fixture://s2_40/renderer"]
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"branch_count": 1,
|
||||||
|
"unknown_slots": [],
|
||||||
|
"expected_case_type_catalog_row_count": 137,
|
||||||
|
"case_type_coverage_legal_approval_status": "PENDING_LEGAL_ADMISSION",
|
||||||
|
"renderer_legal_approval_status": "PENDING_LEGAL_CONTENT"
|
||||||
|
},
|
||||||
|
"mutations": [
|
||||||
|
{"mutation_id":"ZERO","target":"branch_rows","operation":"CLEAR"},
|
||||||
|
{"mutation_id":"MULTI","target":"branch_rows","operation":"DUPLICATE"},
|
||||||
|
{"mutation_id":"UNKNOWN","target":"slots","operation":"ADD_UNKNOWN"},
|
||||||
|
{"mutation_id":"STALE_HASH","target":"renderer_branch.raw_sha256","operation":"SUBSTITUTE_HASH"},
|
||||||
|
{"mutation_id":"FREE_TEXT_FALLBACK","target":"render_mode","operation":"SET_FREE_TEXT"},
|
||||||
|
{"mutation_id":"LEGACY_STAGE2_PATH","target":"source_path","operation":"SET_V0_TO_V3_PATH"},
|
||||||
|
{"mutation_id":"EXTERNAL_PY","target":"execution_path","operation":"SET_EXTERNAL_PY"},
|
||||||
|
{"mutation_id":"DIRECTORY_SCAN","target":"input_resolution","operation":"SET_GLOB_SCAN"},
|
||||||
|
{"mutation_id":"SECRET_INJECTION","target":"slot_value","operation":"ADD_SECRET_TOKEN"},
|
||||||
|
{"mutation_id":"COVERAGE_137_ROW_MISSING","target":"case_type_coverage.rows","operation":"DELETE_ONE_ROW"}
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"route": "STOP_TECHNICAL_INCOMPLETE",
|
||||||
|
"run_technical_status": "TECHNICAL_INCOMPLETE",
|
||||||
|
"artifact_technical_status": "NOT_PRODUCED",
|
||||||
|
"legal_readiness": "NOT_ASSESSED",
|
||||||
|
"expected_invariant_results": {"V10":"FAIL", "V18":"FAIL"},
|
||||||
|
"expected_reason_codes": ["CLOSED_RENDERER_REJECTED"]
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"schema_version":"stage2_s2_40_fixture_payload.v1","header":{"fixture_case_id":"S40-F009","fixture_only":true,"production_admissible":false,"compile_mode":"STRUCTURAL_FIXTURE","oracle_kind":"STATE","source_locators":["fixture://s2_40/review-policy"]},"input":{"policy_status":"PENDING_LEGAL_CONTENT"},"mutations":[],"expected":{"route":"WAIT_EXTERNAL_REVIEW","run_technical_status":"CONSISTENT","artifact_technical_status":"CONSISTENT","legal_readiness":"LAWYER_REVIEW_REQUIRED","expected_invariant_results":{"V18":"PASS"},"expected_reason_codes":["REVIEW_POLICY_NOT_APPROVED"]}}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage2_s2_40_fixture_payload.v1",
|
||||||
|
"header": {
|
||||||
|
"fixture_case_id": "S40-F011",
|
||||||
|
"fixture_only": true,
|
||||||
|
"production_admissible": false,
|
||||||
|
"compile_mode": "STRUCTURAL_FIXTURE",
|
||||||
|
"oracle_kind": "STATE",
|
||||||
|
"source_locators": ["fixture://s2_40/receipt-approve"]
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"disposition": "APPROVE_AS_IS",
|
||||||
|
"candidate_bytes_unchanged": true,
|
||||||
|
"rerender_call_count_on_resume": 0,
|
||||||
|
"external_verification_status": "VERIFIED_BY_EXTERNAL_ADAPTER",
|
||||||
|
"phase_sequence": [
|
||||||
|
"FREEZE",
|
||||||
|
"AWAITING_EXTERNAL_REVIEW",
|
||||||
|
"RESUME_VALIDATE",
|
||||||
|
"READY_TO_COMMIT",
|
||||||
|
"COMMITTED"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mutations": [],
|
||||||
|
"expected": {
|
||||||
|
"route": "PACKAGE_COMMITTED",
|
||||||
|
"run_technical_status": "CONSISTENT",
|
||||||
|
"artifact_technical_status": "CONSISTENT",
|
||||||
|
"legal_readiness": "LAWYER_REVIEW_REQUIRED",
|
||||||
|
"expected_invariant_results": {"V14": "PASS"},
|
||||||
|
"expected_reason_codes": ["FIXTURE_COMMIT_NOT_FILING_APPROVAL"]
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"schema_version":"stage2_s2_40_fixture_payload.v1","header":{"fixture_case_id":"S40-F012","fixture_only":true,"production_admissible":false,"compile_mode":"STRUCTURAL_FIXTURE","oracle_kind":"STATE","source_locators":["fixture://s2_40/receipt-change"]},"input":{"disposition":"CONTENT_CHANGE_REQUIRED","change_scope":"DRAFTING_TEXT_ATOM"},"mutations":[],"expected":{"route":"RESTART_FROM_S2_30","run_technical_status":"CONSISTENT","artifact_technical_status":"CONSISTENT","legal_readiness":"LAWYER_REVIEW_REQUIRED","expected_invariant_results":{"V14":"PASS"},"expected_reason_codes":["CANDIDATE_SUPERSEDED_NO_IN_PLACE_EDIT"]}}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"schema_version":"stage2_s2_40_fixture_payload.v1","header":{"fixture_case_id":"S40-F010","fixture_only":true,"production_admissible":false,"compile_mode":"STRUCTURAL_FIXTURE","oracle_kind":"STATE","source_locators":["fixture://s2_40/receipt-invalid"]},"input":{"required_receipts":["STANDARD_ATTORNEY_REVIEW"]},"mutations":[{"mutation_id":"WRONG_DIGEST","target":"receipt/candidate_content_digest","operation":"REPLACE"}],"expected":{"route":"WAIT_EXTERNAL_REVIEW","run_technical_status":"CONSISTENT","artifact_technical_status":"CONSISTENT","legal_readiness":"LAWYER_REVIEW_REQUIRED","expected_invariant_results":{"V14":"PASS"},"expected_reason_codes":["RECEIPT_MISSING_OR_INVALID"]}}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"schema_version":"stage2_s2_40_fixture_payload.v1","header":{"fixture_case_id":"S40-F014","fixture_only":true,"production_admissible":false,"compile_mode":"STRUCTURAL_FIXTURE","oracle_kind":"BARRIER","source_locators":["fixture://s2_40/idempotence"]},"input":{"same_binding_same_bytes":"IDEMPOTENT_SUCCESS"},"mutations":[{"mutation_id":"CONFLICT","target":"final/path","operation":"WRITE_DIFFERENT_BYTES"}],"expected":{"route":"STOP_TECHNICAL_INCOMPLETE","run_technical_status":"TECHNICAL_INCOMPLETE","artifact_technical_status":"NOT_PRODUCED","legal_readiness":"NOT_ASSESSED","expected_invariant_results":{"V14":"FAIL"},"expected_reason_codes":["NO_OVERWRITE_CONFLICT"]}}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage2_s2_40_fixture_payload.v1",
|
||||||
|
"header": {
|
||||||
|
"fixture_case_id": "S40-F006",
|
||||||
|
"fixture_only": true,
|
||||||
|
"production_admissible": false,
|
||||||
|
"compile_mode": "STRUCTURAL_FIXTURE",
|
||||||
|
"oracle_kind": "MUTATION",
|
||||||
|
"source_locators": [
|
||||||
|
"fixture://s2_40/v01-v18",
|
||||||
|
"discrepancy_report_1.md",
|
||||||
|
"discrepancy_report_2.md",
|
||||||
|
"discrepancy_report_3.md",
|
||||||
|
"discrepancy_report_4.md",
|
||||||
|
"improvement_merged.md"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"invariant_ids": [
|
||||||
|
"V01", "V02", "V03", "V04", "V05", "V06", "V07", "V08", "V09",
|
||||||
|
"V10", "V11", "V12", "V13", "V14", "V15", "V16", "V17", "V18"
|
||||||
|
],
|
||||||
|
"normal_oracle": {
|
||||||
|
"evaluable": true,
|
||||||
|
"expected": {"contract_state": "BOUND"},
|
||||||
|
"observed": {"contract_state": "BOUND"},
|
||||||
|
"evaluation_status": "PASS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mutations": [
|
||||||
|
{"mutation_id":"V01-CONTRADICTION-OR-ABSENCE","target":"upstream_allowlist.path_hash_schema_producer","operation":"SUBSTITUTE_HASH_OR_DELETE_ROW","source_finding_ids":["DR1-STAGE1_TO_STAGE2_FACT_LOSS"],"contradiction":{"observed":"HASH_DRIFT","evaluation_status":"FAIL","reason_code":"V01_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V01_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V02-CONTRADICTION-OR-ABSENCE","target":"bo_les_ledger_signal_evidence_review_universe","operation":"DROP_OR_CONTRADICT_REVIEW_KEY","source_finding_ids":["IR1-CRITICAL_FACT_CONSERVATION"],"contradiction":{"observed":"MULTISET_MISMATCH","evaluation_status":"FAIL","reason_code":"V02_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V02_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V03-CONTRADICTION-OR-ABSENCE","target":"active_domain_slot_crosswalk_namespace_owner","operation":"CHANGE_OR_REMOVE_NAMESPACE_OWNER","source_finding_ids":["IR2-DOMAIN_CONFIG_COMPLETENESS"],"contradiction":{"observed":"OWNER_MISMATCH","evaluation_status":"FAIL","reason_code":"V03_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V03_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V04-CONTRADICTION-OR-ABSENCE","target":"defense_opposing_fact_rebuttal_evidence_chain","operation":"REVERSE_POLARITY_OR_REMOVE_REBUTTAL","source_finding_ids":["IR3-DEFENSE_CHAIN_LOSS"],"contradiction":{"observed":"POLARITY_CONFLICT","evaluation_status":"FAIL","reason_code":"V04_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V04_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V05-CONTRADICTION-OR-ABSENCE","target":"cluster_dependency_precondition_incompatibility","operation":"VIOLATE_OR_REMOVE_PRECONDITION","source_finding_ids":["IM-CLAIM_GROUP_RELATION"],"contradiction":{"observed":"DEPENDENCY_VIOLATED","evaluation_status":"FAIL","reason_code":"V05_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V05_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V06-CONTRADICTION-OR-ABSENCE","target":"atomic_claim.case_type_id.sub_rule_id","operation":"DUPLICATE_MATCH_OR_REMOVE_BINDING","source_finding_ids":["EVAL-M15-CASE-TYPE-PREDICATE"],"contradiction":{"observed":"MULTI_MATCH","evaluation_status":"FAIL","reason_code":"V06_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V06_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V07-CONTRADICTION-OR-ABSENCE","target":"calculator_receipt.operand.law_value","operation":"SUBSTITUTE_UNRECEIPTED_VALUE_OR_REMOVE_RECEIPT","source_finding_ids":["DR3-ACTIO-CALCULATION-LOSS"],"contradiction":{"observed":"UNRECEIPTED_VALUE","evaluation_status":"FAIL","reason_code":"V07_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V07_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V08-CONTRADICTION-OR-ABSENCE","target":"same_recovery_relation_partial_claim_disposition","operation":"DUPLICATE_RECOVERY_OR_REMOVE_RELATION","source_finding_ids":["IM-SAME-RECOVERY-DUPLICATE"],"contradiction":{"observed":"DUPLICATE_RECOVERY","evaluation_status":"FAIL","reason_code":"V08_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V08_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V09-CONTRADICTION-OR-ABSENCE","target":"relief_party_object_amount_period","operation":"ALTER_AMOUNT_OR_REMOVE_FROZEN_PLAN","source_finding_ids":["DR4-RELIEF-OBJECT-DATE-MISMATCH"],"contradiction":{"observed":"PLAN_RELIEF_MISMATCH","evaluation_status":"FAIL","reason_code":"V09_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V09_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V10-CONTRADICTION-OR-ABSENCE","target":"cost_provisional_numbering_annex_order_template_scope","operation":"PERMUTE_ORDER_OR_REMOVE_RENDERER_BRANCH","source_finding_ids":["IM-RELIEF-FORM-NUMBERING"],"contradiction":{"observed":"CLOSED_TEMPLATE_DRIFT","evaluation_status":"FAIL","reason_code":"V10_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V10_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V11-CONTRADICTION-OR-ABSENCE","target":"relief_cause_claim_party_amount_interest_counterperformance","operation":"ALTER_PARTY_OR_REMOVE_CAUSE_LINK","source_finding_ids":["DR1-RELIEF-CAUSE-CROSSMATCH"],"contradiction":{"observed":"RELIEF_CAUSE_MISMATCH","evaluation_status":"FAIL","reason_code":"V11_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V11_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V12-CONTRADICTION-OR-ABSENCE","target":"corpus_snapshot_locator_approval_isolation_crosswalk","operation":"SUBSTITUTE_CORPUS_HASH_OR_REMOVE_LOCATOR","source_finding_ids":["EVAL-M16-CORPUS-NAMESPACE"],"contradiction":{"observed":"CORPUS_HASH_DRIFT","evaluation_status":"FAIL","reason_code":"V12_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V12_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V13-CONTRADICTION-OR-ABSENCE","target":"authority_temporal_addenda_negative_treatment_law_value","operation":"EXPIRE_AUTHORITY_OR_REMOVE_TEMPORAL_SCOPE","source_finding_ids":["EVAL-M13-LAW-VALUE-SEAL"],"contradiction":{"observed":"AUTHORITY_TEMPORAL_DRIFT","evaluation_status":"FAIL","reason_code":"V13_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V13_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V14-CONTRADICTION-OR-ABSENCE","target":"candidate_validation_review_status_intent_readback_commit_barrier_graph","operation":"INTRODUCE_BACKLINK_OR_REMOVE_GRAPH_EDGE","source_finding_ids":["EVAL-M18-NONCYCLIC-SEAL"],"contradiction":{"observed":"DIGEST_CYCLE","evaluation_status":"FAIL","reason_code":"V14_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V14_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V15-CONTRADICTION-OR-ABSENCE","target":"atom_provenance_fact_evidence","operation":"INJECT_UNSOURCED_FACT_OR_REMOVE_EVIDENCE_REF","source_finding_ids":["EVAL-C1-CAUSE-ATOM-PROVENANCE"],"contradiction":{"observed":"UNSOURCED_FACT","evaluation_status":"FAIL","reason_code":"V15_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V15_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V16-CONTRADICTION-OR-ABSENCE","target":"claim_option_partition","operation":"OVERLAP_BUCKETS_OR_REMOVE_OPTION","source_finding_ids":["IR4-OPTION-SILENT-LOSS"],"contradiction":{"observed":"PARTITION_OVERLAP","evaluation_status":"FAIL","reason_code":"V16_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V16_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V17-CONTRADICTION-OR-ABSENCE","target":"party_title_liability_object_exhibit_tokens","operation":"CHANGE_PARTY_TITLE_OR_REMOVE_OBJECT_TOKEN","source_finding_ids":["DR2-OBJECT-EXHIBIT-COMPLETENESS"],"contradiction":{"observed":"OBJECT_TOKEN_MISMATCH","evaluation_status":"FAIL","reason_code":"V17_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V17_ABSENT_UNEVALUABLE"}},
|
||||||
|
{"mutation_id":"V18-CONTRADICTION-OR-ABSENCE","target":"closed_ast_independent_rerender_frozen_dependencies","operation":"ALTER_RENDERER_BRANCH_OR_REMOVE_RERENDER_EVIDENCE","source_finding_ids":["IM-CLOSED-RENDERER-INDEPENDENCE"],"contradiction":{"observed":"RERENDER_BYTES_MISMATCH","evaluation_status":"FAIL","reason_code":"V18_CONTRADICTION"},"absence":{"observed":null,"evaluation_status":"UNEVALUABLE","reason_code":"V18_ABSENT_UNEVALUABLE"}}
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"route": "CONTRACT_TEST_ONLY",
|
||||||
|
"run_technical_status": "TECHNICAL_REVIEW_REQUIRED",
|
||||||
|
"artifact_technical_status": "TECHNICAL_REVIEW_REQUIRED",
|
||||||
|
"legal_readiness": "LAWYER_REVIEW_REQUIRED",
|
||||||
|
"expected_invariant_results": {
|
||||||
|
"V01":"PASS", "V02":"PASS", "V03":"PASS", "V04":"PASS", "V05":"PASS", "V06":"PASS",
|
||||||
|
"V07":"PASS", "V08":"PASS", "V09":"PASS", "V10":"PASS", "V11":"PASS", "V12":"PASS",
|
||||||
|
"V13":"PASS", "V14":"PASS", "V15":"PASS", "V16":"PASS", "V17":"PASS", "V18":"PASS"
|
||||||
|
},
|
||||||
|
"expected_reason_codes": ["MATRIX_ORACLE"]
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage2_s2_40_fixture_payload.v1",
|
||||||
|
"header": {
|
||||||
|
"fixture_case_id": "S40-F001",
|
||||||
|
"fixture_only": true,
|
||||||
|
"production_admissible": false,
|
||||||
|
"compile_mode": "STRUCTURAL_FIXTURE",
|
||||||
|
"oracle_kind": "NORMAL",
|
||||||
|
"source_locators": ["fixture://s2_40/valid"]
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"all_v_pass": true,
|
||||||
|
"legal_assets_approved": false,
|
||||||
|
"review_receipt_external_verification_status": "VERIFIED_BY_EXTERNAL_ADAPTER",
|
||||||
|
"phase_sequence": [
|
||||||
|
"FREEZE",
|
||||||
|
"AWAITING_EXTERNAL_REVIEW",
|
||||||
|
"RESUME_VALIDATE",
|
||||||
|
"READY_TO_COMMIT",
|
||||||
|
"COMMITTED"
|
||||||
|
],
|
||||||
|
"candidate_bytes_reused_on_resume": true,
|
||||||
|
"rerender_call_count_on_resume": 0,
|
||||||
|
"logical_commit_order": [
|
||||||
|
"commit/commit_intent.json",
|
||||||
|
"final/*-EXPLICIT-ALLOWLIST-ONLY",
|
||||||
|
"commit/stage2_commit_result.json",
|
||||||
|
"control/run_status.json"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mutations": [],
|
||||||
|
"expected": {
|
||||||
|
"route": "PACKAGE_COMMITTED",
|
||||||
|
"run_technical_status": "CONSISTENT",
|
||||||
|
"artifact_technical_status": "CONSISTENT",
|
||||||
|
"legal_readiness": "LAWYER_REVIEW_REQUIRED",
|
||||||
|
"expected_invariant_results": {"V01": "PASS", "V18": "PASS"},
|
||||||
|
"expected_reason_codes": ["STRUCTURAL_FIXTURE_NOT_PRODUCTION"]
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"schema_version":"stage2_s2_40_fixture_payload.v1","header":{"fixture_case_id":"S40-F002","fixture_only":true,"production_admissible":false,"compile_mode":"STRUCTURAL_FIXTURE","oracle_kind":"BARRIER","source_locators":["fixture://s2_40/status-only"]},"input":{"exact_paths":["ingress/ingress_status.json","ingress/technical_diagnostic.json","ingress/stage1_input_manifest.json","ingress/intake_report.json","review/issue_ledger.base.json"]},"mutations":[],"expected":{"route":"STOP_TECHNICAL_INCOMPLETE","run_technical_status":"TECHNICAL_INCOMPLETE","artifact_technical_status":"NOT_PRODUCED","legal_readiness":"NOT_ASSESSED","expected_invariant_results":{"V01":"PASS"},"expected_reason_codes":["STATUS_ONLY_EXACT_FIVE"]}}
|
||||||
+17
-16
@@ -561,7 +561,7 @@ class ClusterAndBundleTests(unittest.TestCase):
|
|||||||
self.assertIn("producer_emits_field: false", cache_text)
|
self.assertIn("producer_emits_field: false", cache_text)
|
||||||
self.assertIn("S2_10", cache_text)
|
self.assertIn("S2_10", cache_text)
|
||||||
|
|
||||||
def test_s2_40_status_only_is_deterministic_non_llm_stub(self) -> None:
|
def test_s2_40_is_implemented_deterministic_non_llm_finalizer(self) -> None:
|
||||||
workflow = yaml.safe_load(
|
workflow = yaml.safe_load(
|
||||||
(ROOT / "workflows/S2_40_final_review_render_and_commit.yml").read_text(
|
(ROOT / "workflows/S2_40_final_review_render_and_commit.yml").read_text(
|
||||||
encoding="utf-8"
|
encoding="utf-8"
|
||||||
@@ -569,16 +569,18 @@ class ClusterAndBundleTests(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
stage = workflow["Agent"]["Stages"][0]
|
stage = workflow["Agent"]["Stages"][0]
|
||||||
task = stage["tasks"][0]
|
task = stage["tasks"][0]
|
||||||
|
self.assertEqual(
|
||||||
|
workflow["Agent"]["metadata"]["implementation_status"],
|
||||||
|
"IMPLEMENTED_OFFLINE_CONTRACT_LIVE_ADMISSION_PENDING",
|
||||||
|
)
|
||||||
self.assertEqual(task["execution_class"], "NON-LLM-DETERMINISTIC")
|
self.assertEqual(task["execution_class"], "NON-LLM-DETERMINISTIC")
|
||||||
self.assertEqual(task["implementation_status"], "STUB_NOT_IMPLEMENTED")
|
self.assertEqual(task["task_name"], "Task_S2_40_deterministic_finalizer")
|
||||||
self.assertFalse(task["invocation_allowed"])
|
self.assertEqual(task["mcp"], "code-executor")
|
||||||
|
self.assertEqual(task["tool_name"], "run_code")
|
||||||
self.assertNotIn("prompts", task)
|
self.assertNotIn("prompts", task)
|
||||||
self.assertNotIn("tools", stage)
|
self.assertNotIn("tools", stage)
|
||||||
self.assertFalse(stage["prohibitions"]["llm_call_allowed"])
|
self.assertFalse(task["llm_call_allowed"])
|
||||||
self.assertEqual(
|
self.assertFalse(task["shell_or_subprocess_allowed"])
|
||||||
stage["handoff_contract"]["final_status_fields"]["legal_readiness"],
|
|
||||||
["NOT_ASSESSED"],
|
|
||||||
)
|
|
||||||
expected_inputs = [
|
expected_inputs = [
|
||||||
"ingress/ingress_status.json",
|
"ingress/ingress_status.json",
|
||||||
"ingress/technical_diagnostic.json",
|
"ingress/technical_diagnostic.json",
|
||||||
@@ -586,18 +588,17 @@ class ClusterAndBundleTests(unittest.TestCase):
|
|||||||
"ingress/intake_report.json",
|
"ingress/intake_report.json",
|
||||||
"review/issue_ledger.base.json",
|
"review/issue_ledger.base.json",
|
||||||
]
|
]
|
||||||
self.assertEqual(stage["handoff_contract"]["exact_input_count"], 5)
|
entry = stage["entry_contract"]
|
||||||
self.assertEqual(stage["handoff_contract"]["exact_inputs"], expected_inputs)
|
self.assertEqual(entry["accepted_routes"], ["TO_S2_40", "TO_S2_40_STATUS_ONLY"])
|
||||||
self.assertEqual(task["exact_input_files"], expected_inputs)
|
self.assertEqual(entry["status_only_exact_input_count"], 5)
|
||||||
self.assertFalse(stage["handoff_contract"]["additional_input_allowed"])
|
self.assertEqual(entry["status_only_exact_inputs"], expected_inputs)
|
||||||
|
self.assertFalse(entry["status_only_additional_input_allowed"])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
workflow["Agent"]["metadata"]["final_status_single_writer"],
|
workflow["Agent"]["metadata"]["final_status_single_writer"],
|
||||||
"S2_40",
|
"S2_40",
|
||||||
)
|
)
|
||||||
self.assertFalse(
|
self.assertEqual(stage["output_contract"]["final_barrier_path"], "control/run_status.json")
|
||||||
stage["prohibitions"]["final_status_writer_other_than_s2_40_allowed"]
|
self.assertTrue(stage["output_contract"]["barrier_written_last"])
|
||||||
)
|
|
||||||
self.assertEqual(stage["prohibitions"]["output_paths"], ["control/run_status.json"])
|
|
||||||
|
|
||||||
def test_release_oracle_verifies_active_executor_cross_hashes(self) -> None:
|
def test_release_oracle_verifies_active_executor_cross_hashes(self) -> None:
|
||||||
release_path = ROOT / "manifest/stage2_release.json"
|
release_path = ROOT / "manifest/stage2_release.json"
|
||||||
|
|||||||
+17
-16
@@ -561,7 +561,7 @@ class ClusterAndBundleTests(unittest.TestCase):
|
|||||||
self.assertIn("producer_emits_field: false", cache_text)
|
self.assertIn("producer_emits_field: false", cache_text)
|
||||||
self.assertIn("S2_10", cache_text)
|
self.assertIn("S2_10", cache_text)
|
||||||
|
|
||||||
def test_s2_40_status_only_is_deterministic_non_llm_stub(self) -> None:
|
def test_s2_40_is_implemented_deterministic_non_llm_finalizer(self) -> None:
|
||||||
workflow = yaml.safe_load(
|
workflow = yaml.safe_load(
|
||||||
(ROOT / "workflows/S2_40_final_review_render_and_commit.yml").read_text(
|
(ROOT / "workflows/S2_40_final_review_render_and_commit.yml").read_text(
|
||||||
encoding="utf-8"
|
encoding="utf-8"
|
||||||
@@ -569,16 +569,18 @@ class ClusterAndBundleTests(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
stage = workflow["Agent"]["Stages"][0]
|
stage = workflow["Agent"]["Stages"][0]
|
||||||
task = stage["tasks"][0]
|
task = stage["tasks"][0]
|
||||||
|
self.assertEqual(
|
||||||
|
workflow["Agent"]["metadata"]["implementation_status"],
|
||||||
|
"IMPLEMENTED_OFFLINE_CONTRACT_LIVE_ADMISSION_PENDING",
|
||||||
|
)
|
||||||
self.assertEqual(task["execution_class"], "NON-LLM-DETERMINISTIC")
|
self.assertEqual(task["execution_class"], "NON-LLM-DETERMINISTIC")
|
||||||
self.assertEqual(task["implementation_status"], "STUB_NOT_IMPLEMENTED")
|
self.assertEqual(task["task_name"], "Task_S2_40_deterministic_finalizer")
|
||||||
self.assertFalse(task["invocation_allowed"])
|
self.assertEqual(task["mcp"], "code-executor")
|
||||||
|
self.assertEqual(task["tool_name"], "run_code")
|
||||||
self.assertNotIn("prompts", task)
|
self.assertNotIn("prompts", task)
|
||||||
self.assertNotIn("tools", stage)
|
self.assertNotIn("tools", stage)
|
||||||
self.assertFalse(stage["prohibitions"]["llm_call_allowed"])
|
self.assertFalse(task["llm_call_allowed"])
|
||||||
self.assertEqual(
|
self.assertFalse(task["shell_or_subprocess_allowed"])
|
||||||
stage["handoff_contract"]["final_status_fields"]["legal_readiness"],
|
|
||||||
["NOT_ASSESSED"],
|
|
||||||
)
|
|
||||||
expected_inputs = [
|
expected_inputs = [
|
||||||
"ingress/ingress_status.json",
|
"ingress/ingress_status.json",
|
||||||
"ingress/technical_diagnostic.json",
|
"ingress/technical_diagnostic.json",
|
||||||
@@ -586,18 +588,17 @@ class ClusterAndBundleTests(unittest.TestCase):
|
|||||||
"ingress/intake_report.json",
|
"ingress/intake_report.json",
|
||||||
"review/issue_ledger.base.json",
|
"review/issue_ledger.base.json",
|
||||||
]
|
]
|
||||||
self.assertEqual(stage["handoff_contract"]["exact_input_count"], 5)
|
entry = stage["entry_contract"]
|
||||||
self.assertEqual(stage["handoff_contract"]["exact_inputs"], expected_inputs)
|
self.assertEqual(entry["accepted_routes"], ["TO_S2_40", "TO_S2_40_STATUS_ONLY"])
|
||||||
self.assertEqual(task["exact_input_files"], expected_inputs)
|
self.assertEqual(entry["status_only_exact_input_count"], 5)
|
||||||
self.assertFalse(stage["handoff_contract"]["additional_input_allowed"])
|
self.assertEqual(entry["status_only_exact_inputs"], expected_inputs)
|
||||||
|
self.assertFalse(entry["status_only_additional_input_allowed"])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
workflow["Agent"]["metadata"]["final_status_single_writer"],
|
workflow["Agent"]["metadata"]["final_status_single_writer"],
|
||||||
"S2_40",
|
"S2_40",
|
||||||
)
|
)
|
||||||
self.assertFalse(
|
self.assertEqual(stage["output_contract"]["final_barrier_path"], "control/run_status.json")
|
||||||
stage["prohibitions"]["final_status_writer_other_than_s2_40_allowed"]
|
self.assertTrue(stage["output_contract"]["barrier_written_last"])
|
||||||
)
|
|
||||||
self.assertEqual(stage["prohibitions"]["output_paths"], ["control/run_status.json"])
|
|
||||||
|
|
||||||
def test_release_oracle_verifies_active_executor_cross_hashes(self) -> None:
|
def test_release_oracle_verifies_active_executor_cross_hashes(self) -> None:
|
||||||
release_path = ROOT / "manifest/stage2_release.json"
|
release_path = ROOT / "manifest/stage2_release.json"
|
||||||
|
|||||||
+1
-1
@@ -572,7 +572,7 @@ class FailureAndAtomicPublishTests(unittest.TestCase):
|
|||||||
relative = s2._safe_relative_path(row["path"]).as_posix()
|
relative = s2._safe_relative_path(row["path"]).as_posix()
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
PurePosixPath(relative).parent.as_posix(),
|
PurePosixPath(relative).parent.as_posix(),
|
||||||
{"tests/s2_00", "tests/s2_20"},
|
{"tests/s2_00", "tests/s2_20", "tests/s2_40"},
|
||||||
)
|
)
|
||||||
self.assertTrue((ROOT / relative).is_file(), relative)
|
self.assertTrue((ROOT / relative).is_file(), relative)
|
||||||
observed_sha256 = hashlib.sha256((ROOT / relative).read_bytes()).hexdigest()
|
observed_sha256 = hashlib.sha256((ROOT / relative).read_bytes()).hexdigest()
|
||||||
|
|||||||
+1
-1
@@ -572,7 +572,7 @@ class FailureAndAtomicPublishTests(unittest.TestCase):
|
|||||||
relative = s2._safe_relative_path(row["path"]).as_posix()
|
relative = s2._safe_relative_path(row["path"]).as_posix()
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
PurePosixPath(relative).parent.as_posix(),
|
PurePosixPath(relative).parent.as_posix(),
|
||||||
{"tests/s2_00", "tests/s2_20"},
|
{"tests/s2_00", "tests/s2_20", "tests/s2_40"},
|
||||||
)
|
)
|
||||||
self.assertTrue((ROOT / relative).is_file(), relative)
|
self.assertTrue((ROOT / relative).is_file(), relative)
|
||||||
observed_sha256 = hashlib.sha256((ROOT / relative).read_bytes()).hexdigest()
|
observed_sha256 = hashlib.sha256((ROOT / relative).read_bytes()).hexdigest()
|
||||||
|
|||||||
+71
@@ -2,10 +2,13 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import importlib.util
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
@@ -16,6 +19,21 @@ sys.path.insert(0, str(ROOT))
|
|||||||
from offline_build import validate_s2_10_contract as contract # noqa: E402
|
from offline_build import validate_s2_10_contract as contract # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
PROJECTION_BUILDER_PATH = ROOT / "offline_build" / "build_s2_10_agent_projection.py"
|
||||||
|
|
||||||
|
|
||||||
|
def load_module(name: str, path: Path):
|
||||||
|
spec = importlib.util.spec_from_file_location(name, path)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise RuntimeError(path)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
projection_builder = load_module("s2_10_projection_builder", PROJECTION_BUILDER_PATH)
|
||||||
|
|
||||||
|
|
||||||
CAPABILITIES = {
|
CAPABILITIES = {
|
||||||
"HOST_ATOMIC_SINGLE_FLIGHT_CAS_V1",
|
"HOST_ATOMIC_SINGLE_FLIGHT_CAS_V1",
|
||||||
"AGENTBACKEND_MAP_SOURCE_ITEMS_V1",
|
"AGENTBACKEND_MAP_SOURCE_ITEMS_V1",
|
||||||
@@ -34,6 +52,59 @@ def load_json(path: Path) -> dict:
|
|||||||
|
|
||||||
|
|
||||||
class ReleaseAdapterRetryTests(unittest.TestCase):
|
class ReleaseAdapterRetryTests(unittest.TestCase):
|
||||||
|
def test_default_check_uses_cumulative_parent_and_downstream_oracles(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as temporary:
|
||||||
|
root = Path(temporary)
|
||||||
|
prerequisite_path = root / "projection.yml"
|
||||||
|
parent_path = root / "stage2_release.json"
|
||||||
|
child_path = root / "s2_10_release.json"
|
||||||
|
prerequisite_path.write_bytes(b"projection\n")
|
||||||
|
parent_path.write_bytes(b"parent\n")
|
||||||
|
child_path.write_bytes(b"child\n")
|
||||||
|
prerequisite = {prerequisite_path: b"projection\n"}
|
||||||
|
parent = {parent_path: b"parent\n"}
|
||||||
|
child = {child_path: b"child\n"}
|
||||||
|
with (
|
||||||
|
mock.patch.object(
|
||||||
|
projection_builder,
|
||||||
|
"prerequisite_outputs",
|
||||||
|
return_value=(prerequisite, {"phase": "prerequisite"}),
|
||||||
|
),
|
||||||
|
mock.patch.object(
|
||||||
|
projection_builder,
|
||||||
|
"cumulative_parent_outputs",
|
||||||
|
return_value=(parent, {"phase": "cumulative_parent"}),
|
||||||
|
),
|
||||||
|
mock.patch.object(
|
||||||
|
projection_builder,
|
||||||
|
"cumulative_child_outputs",
|
||||||
|
return_value=(child, {"phase": "downstream_child"}),
|
||||||
|
),
|
||||||
|
mock.patch.object(
|
||||||
|
projection_builder,
|
||||||
|
"child_only_outputs",
|
||||||
|
side_effect=AssertionError("observed stale child oracle invoked"),
|
||||||
|
) as observed_child,
|
||||||
|
mock.patch.object(
|
||||||
|
projection_builder,
|
||||||
|
"expected_outputs",
|
||||||
|
side_effect=AssertionError("obsolete S2_10-only parent oracle invoked"),
|
||||||
|
) as obsolete,
|
||||||
|
):
|
||||||
|
result = projection_builder.check()
|
||||||
|
self.assertEqual(result["status"], "S2_10_CUMULATIVE_PACKAGE_CHECK_PASS")
|
||||||
|
self.assertEqual(len(result["checked_paths"]), 3)
|
||||||
|
obsolete.assert_not_called()
|
||||||
|
observed_child.assert_not_called()
|
||||||
|
|
||||||
|
parent_path.write_bytes(b"stale-parent\n")
|
||||||
|
with self.assertRaises(projection_builder.BuildError) as caught:
|
||||||
|
projection_builder.check()
|
||||||
|
self.assertEqual(caught.exception.code, "OFFLINE_PACKAGE_DRIFT")
|
||||||
|
self.assertIn(parent_path.as_posix(), caught.exception.detail)
|
||||||
|
obsolete.assert_not_called()
|
||||||
|
observed_child.assert_not_called()
|
||||||
|
|
||||||
def test_binding_and_all_external_receipts_are_honestly_pending(self) -> None:
|
def test_binding_and_all_external_receipts_are_honestly_pending(self) -> None:
|
||||||
binding = yaml.safe_load((ROOT / "deployment" / "stage2_s2_10_llm_binding.yml").read_text())
|
binding = yaml.safe_load((ROOT / "deployment" / "stage2_s2_10_llm_binding.yml").read_text())
|
||||||
self.assertEqual(binding["binding_status"], "PENDING_EXTERNAL_PLATFORM_BINDING")
|
self.assertEqual(binding["binding_status"], "PENDING_EXTERNAL_PLATFORM_BINDING")
|
||||||
|
|||||||
+71
@@ -2,10 +2,13 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import importlib.util
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
@@ -16,6 +19,21 @@ sys.path.insert(0, str(ROOT))
|
|||||||
from offline_build import validate_s2_10_contract as contract # noqa: E402
|
from offline_build import validate_s2_10_contract as contract # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
PROJECTION_BUILDER_PATH = ROOT / "offline_build" / "build_s2_10_agent_projection.py"
|
||||||
|
|
||||||
|
|
||||||
|
def load_module(name: str, path: Path):
|
||||||
|
spec = importlib.util.spec_from_file_location(name, path)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise RuntimeError(path)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
projection_builder = load_module("s2_10_projection_builder", PROJECTION_BUILDER_PATH)
|
||||||
|
|
||||||
|
|
||||||
CAPABILITIES = {
|
CAPABILITIES = {
|
||||||
"HOST_ATOMIC_SINGLE_FLIGHT_CAS_V1",
|
"HOST_ATOMIC_SINGLE_FLIGHT_CAS_V1",
|
||||||
"AGENTBACKEND_MAP_SOURCE_ITEMS_V1",
|
"AGENTBACKEND_MAP_SOURCE_ITEMS_V1",
|
||||||
@@ -34,6 +52,59 @@ def load_json(path: Path) -> dict:
|
|||||||
|
|
||||||
|
|
||||||
class ReleaseAdapterRetryTests(unittest.TestCase):
|
class ReleaseAdapterRetryTests(unittest.TestCase):
|
||||||
|
def test_default_check_uses_cumulative_parent_and_downstream_oracles(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as temporary:
|
||||||
|
root = Path(temporary)
|
||||||
|
prerequisite_path = root / "projection.yml"
|
||||||
|
parent_path = root / "stage2_release.json"
|
||||||
|
child_path = root / "s2_10_release.json"
|
||||||
|
prerequisite_path.write_bytes(b"projection\n")
|
||||||
|
parent_path.write_bytes(b"parent\n")
|
||||||
|
child_path.write_bytes(b"child\n")
|
||||||
|
prerequisite = {prerequisite_path: b"projection\n"}
|
||||||
|
parent = {parent_path: b"parent\n"}
|
||||||
|
child = {child_path: b"child\n"}
|
||||||
|
with (
|
||||||
|
mock.patch.object(
|
||||||
|
projection_builder,
|
||||||
|
"prerequisite_outputs",
|
||||||
|
return_value=(prerequisite, {"phase": "prerequisite"}),
|
||||||
|
),
|
||||||
|
mock.patch.object(
|
||||||
|
projection_builder,
|
||||||
|
"cumulative_parent_outputs",
|
||||||
|
return_value=(parent, {"phase": "cumulative_parent"}),
|
||||||
|
),
|
||||||
|
mock.patch.object(
|
||||||
|
projection_builder,
|
||||||
|
"cumulative_child_outputs",
|
||||||
|
return_value=(child, {"phase": "downstream_child"}),
|
||||||
|
),
|
||||||
|
mock.patch.object(
|
||||||
|
projection_builder,
|
||||||
|
"child_only_outputs",
|
||||||
|
side_effect=AssertionError("observed stale child oracle invoked"),
|
||||||
|
) as observed_child,
|
||||||
|
mock.patch.object(
|
||||||
|
projection_builder,
|
||||||
|
"expected_outputs",
|
||||||
|
side_effect=AssertionError("obsolete S2_10-only parent oracle invoked"),
|
||||||
|
) as obsolete,
|
||||||
|
):
|
||||||
|
result = projection_builder.check()
|
||||||
|
self.assertEqual(result["status"], "S2_10_CUMULATIVE_PACKAGE_CHECK_PASS")
|
||||||
|
self.assertEqual(len(result["checked_paths"]), 3)
|
||||||
|
obsolete.assert_not_called()
|
||||||
|
observed_child.assert_not_called()
|
||||||
|
|
||||||
|
parent_path.write_bytes(b"stale-parent\n")
|
||||||
|
with self.assertRaises(projection_builder.BuildError) as caught:
|
||||||
|
projection_builder.check()
|
||||||
|
self.assertEqual(caught.exception.code, "OFFLINE_PACKAGE_DRIFT")
|
||||||
|
self.assertIn(parent_path.as_posix(), caught.exception.detail)
|
||||||
|
obsolete.assert_not_called()
|
||||||
|
observed_child.assert_not_called()
|
||||||
|
|
||||||
def test_binding_and_all_external_receipts_are_honestly_pending(self) -> None:
|
def test_binding_and_all_external_receipts_are_honestly_pending(self) -> None:
|
||||||
binding = yaml.safe_load((ROOT / "deployment" / "stage2_s2_10_llm_binding.yml").read_text())
|
binding = yaml.safe_load((ROOT / "deployment" / "stage2_s2_10_llm_binding.yml").read_text())
|
||||||
self.assertEqual(binding["binding_status"], "PENDING_EXTERNAL_PLATFORM_BINDING")
|
self.assertEqual(binding["binding_status"], "PENDING_EXTERNAL_PLATFORM_BINDING")
|
||||||
|
|||||||
+4
-1
@@ -175,6 +175,7 @@ class PublishAndReleaseTest(unittest.TestCase):
|
|||||||
self.assertIn("stage_bindings:", text)
|
self.assertIn("stage_bindings:", text)
|
||||||
self.assertEqual(text.count("stage_id: S2_00"), 1)
|
self.assertEqual(text.count("stage_id: S2_00"), 1)
|
||||||
self.assertEqual(text.count("stage_id: S2_20"), 1)
|
self.assertEqual(text.count("stage_id: S2_20"), 1)
|
||||||
|
has_s2_40 = text.count("stage_id: S2_40") == 1
|
||||||
self.assertIn("embedded_task_bindings:", text)
|
self.assertIn("embedded_task_bindings:", text)
|
||||||
self.assertEqual(text.count("host_stage_id: S2_30"), 1)
|
self.assertEqual(text.count("host_stage_id: S2_30"), 1)
|
||||||
self.assertEqual(text.count("task_name: Task_S2_30_dispatch_planner"), 1)
|
self.assertEqual(text.count("task_name: Task_S2_30_dispatch_planner"), 1)
|
||||||
@@ -183,7 +184,8 @@ class PublishAndReleaseTest(unittest.TestCase):
|
|||||||
encoding="utf-8"
|
encoding="utf-8"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.assertEqual(admission["present_deterministic_stage_ids"], ["S2_00", "S2_20"])
|
expected_stage_ids = ["S2_00", "S2_20"] + (["S2_40"] if has_s2_40 else [])
|
||||||
|
self.assertEqual(admission["present_deterministic_stage_ids"], expected_stage_ids)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
admission["embedded_task_admissions"][0]["execution_unit_id"],
|
admission["embedded_task_admissions"][0]["execution_unit_id"],
|
||||||
"S2_30::Task_S2_30_dispatch_planner",
|
"S2_30::Task_S2_30_dispatch_planner",
|
||||||
@@ -260,6 +262,7 @@ class PublishAndReleaseTest(unittest.TestCase):
|
|||||||
_write_json(root / "manifest/case_type_coverage.json", _coverage())
|
_write_json(root / "manifest/case_type_coverage.json", _coverage())
|
||||||
_write_json(root / "manifest/s2_20_parent_member_paths.json", [])
|
_write_json(root / "manifest/s2_20_parent_member_paths.json", [])
|
||||||
_write_json(root / "manifest/s2_30_parent_member_paths.json", [])
|
_write_json(root / "manifest/s2_30_parent_member_paths.json", [])
|
||||||
|
_write_json(root / "manifest/s2_40_parent_member_paths.json", [])
|
||||||
report = VALIDATOR.validate_package(
|
report = VALIDATOR.validate_package(
|
||||||
root,
|
root,
|
||||||
root / "manifest/module_manifest.json",
|
root / "manifest/module_manifest.json",
|
||||||
|
|||||||
+4
-1
@@ -175,6 +175,7 @@ class PublishAndReleaseTest(unittest.TestCase):
|
|||||||
self.assertIn("stage_bindings:", text)
|
self.assertIn("stage_bindings:", text)
|
||||||
self.assertEqual(text.count("stage_id: S2_00"), 1)
|
self.assertEqual(text.count("stage_id: S2_00"), 1)
|
||||||
self.assertEqual(text.count("stage_id: S2_20"), 1)
|
self.assertEqual(text.count("stage_id: S2_20"), 1)
|
||||||
|
has_s2_40 = text.count("stage_id: S2_40") == 1
|
||||||
self.assertIn("embedded_task_bindings:", text)
|
self.assertIn("embedded_task_bindings:", text)
|
||||||
self.assertEqual(text.count("host_stage_id: S2_30"), 1)
|
self.assertEqual(text.count("host_stage_id: S2_30"), 1)
|
||||||
self.assertEqual(text.count("task_name: Task_S2_30_dispatch_planner"), 1)
|
self.assertEqual(text.count("task_name: Task_S2_30_dispatch_planner"), 1)
|
||||||
@@ -183,7 +184,8 @@ class PublishAndReleaseTest(unittest.TestCase):
|
|||||||
encoding="utf-8"
|
encoding="utf-8"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.assertEqual(admission["present_deterministic_stage_ids"], ["S2_00", "S2_20"])
|
expected_stage_ids = ["S2_00", "S2_20"] + (["S2_40"] if has_s2_40 else [])
|
||||||
|
self.assertEqual(admission["present_deterministic_stage_ids"], expected_stage_ids)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
admission["embedded_task_admissions"][0]["execution_unit_id"],
|
admission["embedded_task_admissions"][0]["execution_unit_id"],
|
||||||
"S2_30::Task_S2_30_dispatch_planner",
|
"S2_30::Task_S2_30_dispatch_planner",
|
||||||
@@ -260,6 +262,7 @@ class PublishAndReleaseTest(unittest.TestCase):
|
|||||||
_write_json(root / "manifest/case_type_coverage.json", _coverage())
|
_write_json(root / "manifest/case_type_coverage.json", _coverage())
|
||||||
_write_json(root / "manifest/s2_20_parent_member_paths.json", [])
|
_write_json(root / "manifest/s2_20_parent_member_paths.json", [])
|
||||||
_write_json(root / "manifest/s2_30_parent_member_paths.json", [])
|
_write_json(root / "manifest/s2_30_parent_member_paths.json", [])
|
||||||
|
_write_json(root / "manifest/s2_40_parent_member_paths.json", [])
|
||||||
report = VALIDATOR.validate_package(
|
report = VALIDATOR.validate_package(
|
||||||
root,
|
root,
|
||||||
root / "manifest/module_manifest.json",
|
root / "manifest/module_manifest.json",
|
||||||
|
|||||||
+21
@@ -14,6 +14,7 @@ ROOT = Path(__file__).resolve().parents[2]
|
|||||||
FIXTURE_ROOT = ROOT / "tests/fixtures/s2_20"
|
FIXTURE_ROOT = ROOT / "tests/fixtures/s2_20"
|
||||||
MANIFEST_PATH = FIXTURE_ROOT / "regression_manifest.json"
|
MANIFEST_PATH = FIXTURE_ROOT / "regression_manifest.json"
|
||||||
GLOBAL_MANIFEST_PATH = ROOT / "tests/fixtures/regression_manifest.json"
|
GLOBAL_MANIFEST_PATH = ROOT / "tests/fixtures/regression_manifest.json"
|
||||||
|
S2_40_MANIFEST_PATH = ROOT / "tests/fixtures/s2_40/regression_manifest.json"
|
||||||
SHA256 = re.compile(r"^[a-f0-9]{64}$")
|
SHA256 = re.compile(r"^[a-f0-9]{64}$")
|
||||||
EXPECTED_FIXTURE_IDS = {
|
EXPECTED_FIXTURE_IDS = {
|
||||||
"S20-F01-SINGLE-OPTION-GROUP",
|
"S20-F01-SINGLE-OPTION-GROUP",
|
||||||
@@ -206,6 +207,26 @@ class RegressionManifestClosureTest(unittest.TestCase):
|
|||||||
self.assertTrue(physical.is_file())
|
self.assertTrue(physical.is_file())
|
||||||
self.assertEqual(row["sha256"], sha256(physical))
|
self.assertEqual(row["sha256"], sha256(physical))
|
||||||
|
|
||||||
|
def test_global_manifest_binds_s2_40_manifest_payloads_and_seven_tests(self) -> None:
|
||||||
|
global_manifest = json.loads(GLOBAL_MANIFEST_PATH.read_text(encoding="utf-8"))
|
||||||
|
binding = global_manifest.get("s2_40_fixture_binding")
|
||||||
|
self.assertIsInstance(binding, dict)
|
||||||
|
self.assertEqual(binding["binding_status"], "OFFLINE_BYTES_BOUND_LEGAL_AND_LIVE_PENDING")
|
||||||
|
self.assertEqual(binding["logical_fixture_count"], 15)
|
||||||
|
self.assertEqual(binding["payload_file_count"], 15)
|
||||||
|
self.assertEqual(ROOT / binding["fixture_manifest_path"], S2_40_MANIFEST_PATH)
|
||||||
|
self.assertEqual(binding["fixture_manifest_sha256"], sha256(S2_40_MANIFEST_PATH))
|
||||||
|
local = json.loads(S2_40_MANIFEST_PATH.read_text(encoding="utf-8"))
|
||||||
|
expected_payloads = {row["path"]: row["raw_sha256"] for row in local["rows"]}
|
||||||
|
self.assertEqual({row["path"]: row["sha256"] for row in binding["payloads"]}, expected_payloads)
|
||||||
|
for path, expected in expected_payloads.items():
|
||||||
|
self.assertEqual(sha256(ROOT / path), expected)
|
||||||
|
s2_40_test_rows = [row for row in global_manifest["test_sources"] if row["path"].startswith("tests/s2_40/")]
|
||||||
|
self.assertEqual(len(s2_40_test_rows), 7)
|
||||||
|
self.assertEqual({row["path"] for row in s2_40_test_rows}, {row["path"] for row in local["test_sources"]})
|
||||||
|
for row in s2_40_test_rows:
|
||||||
|
self.assertEqual(row["sha256"], sha256(ROOT / row["path"]))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
+21
@@ -14,6 +14,7 @@ ROOT = Path(__file__).resolve().parents[2]
|
|||||||
FIXTURE_ROOT = ROOT / "tests/fixtures/s2_20"
|
FIXTURE_ROOT = ROOT / "tests/fixtures/s2_20"
|
||||||
MANIFEST_PATH = FIXTURE_ROOT / "regression_manifest.json"
|
MANIFEST_PATH = FIXTURE_ROOT / "regression_manifest.json"
|
||||||
GLOBAL_MANIFEST_PATH = ROOT / "tests/fixtures/regression_manifest.json"
|
GLOBAL_MANIFEST_PATH = ROOT / "tests/fixtures/regression_manifest.json"
|
||||||
|
S2_40_MANIFEST_PATH = ROOT / "tests/fixtures/s2_40/regression_manifest.json"
|
||||||
SHA256 = re.compile(r"^[a-f0-9]{64}$")
|
SHA256 = re.compile(r"^[a-f0-9]{64}$")
|
||||||
EXPECTED_FIXTURE_IDS = {
|
EXPECTED_FIXTURE_IDS = {
|
||||||
"S20-F01-SINGLE-OPTION-GROUP",
|
"S20-F01-SINGLE-OPTION-GROUP",
|
||||||
@@ -206,6 +207,26 @@ class RegressionManifestClosureTest(unittest.TestCase):
|
|||||||
self.assertTrue(physical.is_file())
|
self.assertTrue(physical.is_file())
|
||||||
self.assertEqual(row["sha256"], sha256(physical))
|
self.assertEqual(row["sha256"], sha256(physical))
|
||||||
|
|
||||||
|
def test_global_manifest_binds_s2_40_manifest_payloads_and_seven_tests(self) -> None:
|
||||||
|
global_manifest = json.loads(GLOBAL_MANIFEST_PATH.read_text(encoding="utf-8"))
|
||||||
|
binding = global_manifest.get("s2_40_fixture_binding")
|
||||||
|
self.assertIsInstance(binding, dict)
|
||||||
|
self.assertEqual(binding["binding_status"], "OFFLINE_BYTES_BOUND_LEGAL_AND_LIVE_PENDING")
|
||||||
|
self.assertEqual(binding["logical_fixture_count"], 15)
|
||||||
|
self.assertEqual(binding["payload_file_count"], 15)
|
||||||
|
self.assertEqual(ROOT / binding["fixture_manifest_path"], S2_40_MANIFEST_PATH)
|
||||||
|
self.assertEqual(binding["fixture_manifest_sha256"], sha256(S2_40_MANIFEST_PATH))
|
||||||
|
local = json.loads(S2_40_MANIFEST_PATH.read_text(encoding="utf-8"))
|
||||||
|
expected_payloads = {row["path"]: row["raw_sha256"] for row in local["rows"]}
|
||||||
|
self.assertEqual({row["path"]: row["sha256"] for row in binding["payloads"]}, expected_payloads)
|
||||||
|
for path, expected in expected_payloads.items():
|
||||||
|
self.assertEqual(sha256(ROOT / path), expected)
|
||||||
|
s2_40_test_rows = [row for row in global_manifest["test_sources"] if row["path"].startswith("tests/s2_40/")]
|
||||||
|
self.assertEqual(len(s2_40_test_rows), 7)
|
||||||
|
self.assertEqual({row["path"] for row in s2_40_test_rows}, {row["path"] for row in local["test_sources"]})
|
||||||
|
for row in s2_40_test_rows:
|
||||||
|
self.assertEqual(row["sha256"], sha256(ROOT / row["path"]))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
+11
-2
@@ -31,6 +31,7 @@ FIXTURE_FILENAMES = (
|
|||||||
"invalid_reference_output.json",
|
"invalid_reference_output.json",
|
||||||
"owner_singleton_dispatch.json",
|
"owner_singleton_dispatch.json",
|
||||||
"partial_write_publish_barrier.json",
|
"partial_write_publish_barrier.json",
|
||||||
|
"persisted_handoff_chain.json",
|
||||||
"rule_requirement_admission_gap.json",
|
"rule_requirement_admission_gap.json",
|
||||||
"technical_failure.json",
|
"technical_failure.json",
|
||||||
"valid_joint_draft_output.json",
|
"valid_joint_draft_output.json",
|
||||||
@@ -60,7 +61,7 @@ class AgentContractTests(unittest.TestCase):
|
|||||||
hashlib.sha256(binding_raw).hexdigest(),
|
hashlib.sha256(binding_raw).hexdigest(),
|
||||||
ROOT,
|
ROOT,
|
||||||
)
|
)
|
||||||
self.assertEqual(report["deterministic_stage_ids"], ["S2_00", "S2_20"])
|
self.assertEqual(report["deterministic_stage_ids"], ["S2_00", "S2_20", "S2_40"])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
report["embedded_execution_unit_id"],
|
report["embedded_execution_unit_id"],
|
||||||
"S2_30::Task_S2_30_dispatch_planner",
|
"S2_30::Task_S2_30_dispatch_planner",
|
||||||
@@ -177,7 +178,7 @@ class AgentContractTests(unittest.TestCase):
|
|||||||
physical_fixtures = sorted(
|
physical_fixtures = sorted(
|
||||||
path for path in fixtures.glob("*.json") if path.name != "regression_manifest.json"
|
path for path in fixtures.glob("*.json") if path.name != "regression_manifest.json"
|
||||||
)
|
)
|
||||||
self.assertEqual(manifest["fixture_count_excluding_manifest"], 13)
|
self.assertEqual(manifest["fixture_count_excluding_manifest"], 14)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[row["filename"] for row in manifest["fixtures"]],
|
[row["filename"] for row in manifest["fixtures"]],
|
||||||
[path.name for path in physical_fixtures],
|
[path.name for path in physical_fixtures],
|
||||||
@@ -209,6 +210,14 @@ class AgentContractTests(unittest.TestCase):
|
|||||||
contract.validate_instance(item["s30_source_refs"], "s30_source_refs", schema)
|
contract.validate_instance(item["s30_source_refs"], "s30_source_refs", schema)
|
||||||
contract.validate_raw_output(valid, item, schema)
|
contract.validate_raw_output(valid, item, schema)
|
||||||
|
|
||||||
|
handoff = documents["persisted_handoff_chain.json"]["contract"]
|
||||||
|
self.assertEqual(handoff["rows_per_claim_group"], len(contract.PART_FAMILIES))
|
||||||
|
self.assertEqual(set(handoff["part_families"]), contract.PART_FAMILIES)
|
||||||
|
self.assertEqual(set(handoff["group_provenance_fields"]), contract.GROUP_PROVENANCE_FIELDS)
|
||||||
|
self.assertTrue(handoff["manifest_receipt_free"])
|
||||||
|
self.assertFalse(handoff["runtime_directory_scan_allowed"])
|
||||||
|
self.assertFalse(handoff["runtime_glob_allowed"])
|
||||||
|
|
||||||
for row in documents["adverse_fact_worknote_policy.json"]["cases"]:
|
for row in documents["adverse_fact_worknote_policy.json"]["cases"]:
|
||||||
contract.validate_instance(row["input"], "adverse_fact_row", schema)
|
contract.validate_instance(row["input"], "adverse_fact_row", schema)
|
||||||
for row in documents["rule_requirement_admission_gap.json"]["cases"]:
|
for row in documents["rule_requirement_admission_gap.json"]["cases"]:
|
||||||
|
|||||||
+11
-2
@@ -31,6 +31,7 @@ FIXTURE_FILENAMES = (
|
|||||||
"invalid_reference_output.json",
|
"invalid_reference_output.json",
|
||||||
"owner_singleton_dispatch.json",
|
"owner_singleton_dispatch.json",
|
||||||
"partial_write_publish_barrier.json",
|
"partial_write_publish_barrier.json",
|
||||||
|
"persisted_handoff_chain.json",
|
||||||
"rule_requirement_admission_gap.json",
|
"rule_requirement_admission_gap.json",
|
||||||
"technical_failure.json",
|
"technical_failure.json",
|
||||||
"valid_joint_draft_output.json",
|
"valid_joint_draft_output.json",
|
||||||
@@ -60,7 +61,7 @@ class AgentContractTests(unittest.TestCase):
|
|||||||
hashlib.sha256(binding_raw).hexdigest(),
|
hashlib.sha256(binding_raw).hexdigest(),
|
||||||
ROOT,
|
ROOT,
|
||||||
)
|
)
|
||||||
self.assertEqual(report["deterministic_stage_ids"], ["S2_00", "S2_20"])
|
self.assertEqual(report["deterministic_stage_ids"], ["S2_00", "S2_20", "S2_40"])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
report["embedded_execution_unit_id"],
|
report["embedded_execution_unit_id"],
|
||||||
"S2_30::Task_S2_30_dispatch_planner",
|
"S2_30::Task_S2_30_dispatch_planner",
|
||||||
@@ -177,7 +178,7 @@ class AgentContractTests(unittest.TestCase):
|
|||||||
physical_fixtures = sorted(
|
physical_fixtures = sorted(
|
||||||
path for path in fixtures.glob("*.json") if path.name != "regression_manifest.json"
|
path for path in fixtures.glob("*.json") if path.name != "regression_manifest.json"
|
||||||
)
|
)
|
||||||
self.assertEqual(manifest["fixture_count_excluding_manifest"], 13)
|
self.assertEqual(manifest["fixture_count_excluding_manifest"], 14)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[row["filename"] for row in manifest["fixtures"]],
|
[row["filename"] for row in manifest["fixtures"]],
|
||||||
[path.name for path in physical_fixtures],
|
[path.name for path in physical_fixtures],
|
||||||
@@ -209,6 +210,14 @@ class AgentContractTests(unittest.TestCase):
|
|||||||
contract.validate_instance(item["s30_source_refs"], "s30_source_refs", schema)
|
contract.validate_instance(item["s30_source_refs"], "s30_source_refs", schema)
|
||||||
contract.validate_raw_output(valid, item, schema)
|
contract.validate_raw_output(valid, item, schema)
|
||||||
|
|
||||||
|
handoff = documents["persisted_handoff_chain.json"]["contract"]
|
||||||
|
self.assertEqual(handoff["rows_per_claim_group"], len(contract.PART_FAMILIES))
|
||||||
|
self.assertEqual(set(handoff["part_families"]), contract.PART_FAMILIES)
|
||||||
|
self.assertEqual(set(handoff["group_provenance_fields"]), contract.GROUP_PROVENANCE_FIELDS)
|
||||||
|
self.assertTrue(handoff["manifest_receipt_free"])
|
||||||
|
self.assertFalse(handoff["runtime_directory_scan_allowed"])
|
||||||
|
self.assertFalse(handoff["runtime_glob_allowed"])
|
||||||
|
|
||||||
for row in documents["adverse_fact_worknote_policy.json"]["cases"]:
|
for row in documents["adverse_fact_worknote_policy.json"]["cases"]:
|
||||||
contract.validate_instance(row["input"], "adverse_fact_row", schema)
|
contract.validate_instance(row["input"], "adverse_fact_row", schema)
|
||||||
for row in documents["rule_requirement_admission_gap.json"]["cases"]:
|
for row in documents["rule_requirement_admission_gap.json"]["cases"]:
|
||||||
|
|||||||
+17
@@ -5,6 +5,7 @@ from pathlib import Path
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[2]
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
@@ -116,6 +117,22 @@ class PromptCacheBoundaryTests(unittest.TestCase):
|
|||||||
self.assertIn("file_write_allowed: false", workflow)
|
self.assertIn("file_write_allowed: false", workflow)
|
||||||
self.assertIn("permanent_id_mint_allowed: false", workflow)
|
self.assertIn("permanent_id_mint_allowed: false", workflow)
|
||||||
|
|
||||||
|
def test_persisted_handoff_echoes_exact_mode_and_producer_provenance(self) -> None:
|
||||||
|
document = yaml.safe_load(WORKFLOW.read_text(encoding="utf-8"))
|
||||||
|
contract = document["output_contract"]["handoff_provenance_contract"]
|
||||||
|
group_fields = contract["group_fields"]
|
||||||
|
common_fields = contract["common_fields"]
|
||||||
|
expected_common = [
|
||||||
|
"compile_mode", "parent_stage2_release_sha256", "s2_30_release_sha256",
|
||||||
|
"s2_30_agent_sha256", "s2_30_binding_sha256", "p00_prompt_sha256",
|
||||||
|
"p30_prompt_sha256", "s2_30_producer_contract_digest",
|
||||||
|
]
|
||||||
|
expected_group_only = ["p31_rule_and_pack_sha256", "p32_common_authority_sha256", "s30_group_slice_sha256"]
|
||||||
|
self.assertEqual(common_fields, expected_common)
|
||||||
|
self.assertEqual(group_fields, expected_common[:-1] + expected_group_only + ["s2_30_producer_contract_digest"])
|
||||||
|
self.assertEqual(contract["exact_echo_chain"], "PART_TO_MANIFEST_TO_RECEIPT_TO_PUBLISH_STATUS_TO_S2_40_REQUEST")
|
||||||
|
self.assertEqual(document["output_contract"]["artifact_manifest_core_contract"]["receipt_free"], True)
|
||||||
|
|
||||||
@unittest.skipUnless(S2_10_AGENT.is_file() and S2_30_AGENT.is_file(), "derived Agent pair not built yet")
|
@unittest.skipUnless(S2_10_AGENT.is_file() and S2_30_AGENT.is_file(), "derived Agent pair not built yet")
|
||||||
def test_s2_10_and_s2_30_common_prefix_raw_scalars_are_byte_equal(self) -> None:
|
def test_s2_10_and_s2_30_common_prefix_raw_scalars_are_byte_equal(self) -> None:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user