diff --git a/README.md b/README.md index 540e62d..c9e4372 100644 --- a/README.md +++ b/README.md @@ -21,24 +21,30 @@ AAE consumes versioned REMORA artifacts. It never installs from `remora.policy`, `remora.enforcement`, `remora.governance`, or `servers.*`. This repository pins a hash-identified REMORA artifact set — -release [`core-candidate-2026.08.05`](https://github.com/darklordVirtual/REMORA-research/releases/tag/core-candidate-2026.08.05), -built from a clean checkout of commit `f3e58db`: +release [`core-candidate-2026.08.05.2`](https://github.com/darklordVirtual/REMORA-research/releases/tag/core-candidate-2026.08.05.2), +built from a clean checkout of commit `716f6bd`: | Artifact | Pinned by | |---|---| | `remora-0.10.0-py3-none-any.whl` | SHA-256 | | `openapi.json` | SHA-256 | -| `public_api_v1.json` (28 SDK symbols) | SHA-256 | +| `public_api_v1.json` (35 SDK symbols) | SHA-256 | | `execution_lifecycle_v1.yaml` | SHA-256 | +| `tool_spec_v1.yaml` | SHA-256 | +| `postcondition_contract_v1.yaml` | SHA-256 | + +The two frozen contracts are pinned alongside the code on purpose: +agreeing on the wheel while disagreeing about what a ToolSpec is, or what +an effect status means, is exactly the drift a pin exists to prevent. `product/core-artifact-lock.json` holds the pin; `scripts/verify_core_pin.py` downloads the release and **refuses on any hash mismatch** — a pin nobody verifies is a comment, not a control. The release is marked **prerelease deliberately**: it is pinnable, not -blessed. REMORA's Gate B is unfinished (see blockers below) and no -external review has run against this build. The signed control-plane -image, SBOM and provenance do not exist yet. +blessed. No external review has run against this build, Gate B is not +closed, and the signed control-plane image, SBOM and provenance do not +exist yet. ## Bootstrap validation @@ -69,12 +75,20 @@ Closed in REMORA core on 2026-08-05 and available in the pinned release: crash matrix executed as tests and reconciliation of stranded dispatches. - ~~evidence export~~ — `export_evidence` returns a hashed manifest. +- ~~`verify_effect` / effect verification~~ — FT-04 landed. A postcondition + is declared, verified against **the declared delta only**, and the record + handed back with `record_effect`. Verification runs in this product's + process because the reader holds the credentials; REMORA stores the + result as an attestation by a named verifier, not as a proof of its own. +- ~~signed ToolSpec (FT-03)~~ — runtime enforcement is in the pinned core, + and `ToolSpecIdentity` tells this product which spec authorized an action + and whether specs are enforced at all. + Still open, and the reason this is not a release candidate: -- **`EffectVerification` / `verify_effect`** — depend on FT-04 postcondition - verification, which does not exist in REMORA core. Asserted absent by a - compatibility test so the gap cannot drift into place unnoticed. -- **Signed ToolSpec (FT-03)** runtime enforcement is not a consumable artifact. +- Effect verification covers tools that **declare a postcondition reader**. + A tool without one reports `EFFECT_UNSUPPORTED`, recorded so the absence + is visible — that is not a verified effect. - **No AAE control plane, worker, ToolPack, console or migrations exist yet** — this repository is a verified pin plus its compatibility gate, nothing more. - OIDC, worker isolation, backup/restore and external review remain open. diff --git a/product/core-artifact-lock.json b/product/core-artifact-lock.json index e1352eb..3d67cb5 100644 --- a/product/core-artifact-lock.json +++ b/product/core-artifact-lock.json @@ -1,25 +1,27 @@ { "_comment": "The exact REMORA core artifacts this product build consumes. AAE never installs from REMORA master and never imports internal REMORA modules; it pins this set and verifies every hash before use.", "built_from_clean_worktree": true, - "execution_lifecycle_schema_sha256": "a62ec3ebb6e07998c84115d575455c6b2c966e5a9104797538c41e2e8311b999", + "execution_lifecycle_schema_sha256": "b39e41d89dd8cb1c1504c7c20254e654550700663053a19b3b81be440d807458", "forbidden_namespaces": [ "remora.policy", "remora.enforcement", "remora.governance", "servers" ], - "openapi_sha256": "9ea2612c54a238367c0af44d88aa53cf6ff63e0f9b7f9696eb83a2f9ddc870c9", + "openapi_sha256": "c06dc53b89fd53efcd2262ab06f0adb7dbdb8aac71e625d0c8b050436a289d24", + "postcondition_contract_schema_sha256": "d0070e3f29ff533359e51f6f5cbf357812343ddc60629a3b172e747cbfd45565", "release_repo": "darklordVirtual/REMORA-research", "release_status": "prerelease", - "release_status_reason": "Gate B is not finished in REMORA core (FT-03 signed ToolSpec and FT-04 postcondition verification are open) and no external review has run against this build. Pinnable, not blessed.", - "release_tag": "core-candidate-2026.08.05", - "remora_core_commit": "f3e58dbb6f47c61767f42210d9d939df3ae9b645", + "release_status_reason": "FT-03 signed ToolSpec and FT-04 effect verification are now in the pinned core, so the §12 surface is complete. Still a prerelease: no external review has run against this build and Gate B is not closed. Pinnable, not blessed.", + "release_tag": "core-candidate-2026.08.05.2", + "remora_core_commit": "716f6bd477396f697fa4c4bf2584def710ee3318", "remora_core_version": "0.10.0", - "sdk_public_api_sha256": "2d26f773819664db2e2364116a9044096f1fa2919bcebf8e97d5e400139e7c79", + "sdk_public_api_sha256": "bcd004d05dcc2cb1175d4a67c4826be65085ca050aba3169aba97d5a5b6b7744", "stable_namespace": "remora.sdk", - "supported_sdk_symbols": 28, + "supported_sdk_symbols": 35, + "tool_spec_schema_sha256": "be5a437079e37868f1797a3fccfb2a4502321a04ba5dd316c6f0979154d6892e", "wheel": { "filename": "remora-0.10.0-py3-none-any.whl", - "sha256": "2d966454faa2ce39c5ae387763220577e87528caa803c18c91a10d428f745304" + "sha256": "71018cf8ec110db1bfefac66a33e6c92a41b55421dbf69ee7a970749140b3184" } } diff --git a/scripts/verify_core_pin.py b/scripts/verify_core_pin.py index c2a5ac5..c809dd8 100644 --- a/scripts/verify_core_pin.py +++ b/scripts/verify_core_pin.py @@ -33,6 +33,11 @@ "openapi.json": "openapi_sha256", "public_api_v1.json": "sdk_public_api_sha256", "execution_lifecycle_v1.yaml": "execution_lifecycle_schema_sha256", + # The two frozen contracts. Verifying the wheel but not these would + # let the product agree with core about the CODE and still disagree + # about what a ToolSpec is or what an effect status means. + "tool_spec_v1.yaml": "tool_spec_schema_sha256", + "postcondition_contract_v1.yaml": "postcondition_contract_schema_sha256", } diff --git a/tests/compatibility/test_sdk_contract.py b/tests/compatibility/test_sdk_contract.py index b02f040..a24460d 100644 --- a/tests/compatibility/test_sdk_contract.py +++ b/tests/compatibility/test_sdk_contract.py @@ -52,6 +52,10 @@ def test_pinned_symbol_count_matches_the_installed_surface() -> None: "InvalidRequestError", "ConflictError", "ApprovalExpiredError", "BindingRefusedError", "ReplayRefusedError", "UnknownExecutionStateError", + # FT-03/FT-04, present since core-candidate-2026.08.05.2. + "ToolSpecIdentity", + "PostconditionSpec", "EffectStatus", "EffectVerificationView", + "build_postcondition", "verify_effect", "content_digest", ]) def test_required_symbol_is_importable(symbol: str) -> None: assert hasattr(sdk, symbol), f"required SDK symbol missing: {symbol}" @@ -60,6 +64,7 @@ def test_required_symbol_is_importable(symbol: str) -> None: @pytest.mark.parametrize("operation", [ "assess", "approve", "reject", "execute", "execute_accepted", "get_proposal", "get_lifecycle", "export_evidence", "verify_audit_chain", + "record_effect", ]) def test_required_operation_exists_on_both_clients(operation: str) -> None: """Sync and async must not drift: an operation missing from one of them @@ -70,19 +75,52 @@ def test_required_operation_exists_on_both_clients(operation: str) -> None: ) -def test_effect_verification_is_honestly_absent() -> None: - """AAE §12 also names verify_effect/EffectVerification. They depend on - FT-04 postcondition verification, which is NOT in the pinned release. +def test_effect_statuses_keep_unknown_apart_from_wrong() -> None: + """The distinction this product's incident handling depends on. - Asserting the absence keeps the gap visible: when FT-04 lands and a - newer core is pinned, this test fails and forces the surface list - above to be updated deliberately rather than drifting into place. + ``EFFECT_UNOBSERVABLE`` and ``EFFECT_VERIFIER_FAILED`` mean *we could + not look*; only ``EFFECT_MISMATCH`` means *we looked and it was + wrong*. If a future core made the unknowns terminal, this product + would start closing incidents that are still open — so the property + is asserted here rather than trusted. """ - assert not hasattr(sdk, "EffectVerification"), ( - "EffectVerification is now available — update the required-symbol " - "list and the known-limitations doc" + assert {s.value for s in sdk.EffectStatus} == { + "EFFECT_VERIFIED", "EFFECT_MISMATCH", "EFFECT_UNOBSERVABLE", + "EFFECT_VERIFIER_FAILED", "EFFECT_UNSUPPORTED", + } + assert sdk.EffectStatus.UNOBSERVABLE.is_terminal is False + assert sdk.EffectStatus.VERIFIER_FAILED.is_terminal is False + assert sdk.EffectStatus.MISMATCH.is_terminal is True + + +def test_verification_compares_only_the_declared_delta() -> None: + """A system of record has other legitimate writers. If core ever began + reporting undeclared fields, every concurrent update would surface + here as a mismatch and the signal would become noise.""" + spec = sdk.build_postcondition( + tool_id="create_ticket", target_selector={"system": "ops"}, + expected_fields={"title": "approved title"}, ) - assert not hasattr(sdk.RemoraClient, "verify_effect") + result = sdk.verify_effect( + spec, {"title": "approved title", "updated_by_someone_else": True}, + proposal_id="p-1", execution_id="e-1", toolspec_hash="0" * 64, + verifier_identity="aae.contract_test/v1", + ) + assert result.status is sdk.EffectStatus.VERIFIED + + +def test_an_unreadable_object_is_never_reported_as_a_mismatch() -> None: + """Failing to READ a result is not evidence the wrong thing happened, + and this product must never compensate on that basis.""" + spec = sdk.build_postcondition( + tool_id="create_ticket", target_selector={}, expected_fields={"a": 1}, + ) + result = sdk.verify_effect( + spec, None, proposal_id="p-1", execution_id="e-1", + toolspec_hash="0" * 64, verifier_identity="aae.contract_test/v1", + ) + assert result.status is sdk.EffectStatus.UNOBSERVABLE + assert result.status.is_terminal is False _INTERNAL = re.compile(