From 1c964184eead8648def0dd3feab45b00d2686d21 Mon Sep 17 00:00:00 2001 From: agent-a6e6ec231c5f7801 Date: Sun, 19 Apr 2026 20:56:29 +0200 Subject: [PATCH] build(agent): new-agents#a6e6ec iteration --- README.md | 3 +++ core/contract_registry.py | 22 ++++++++++++++++++++++ tests/test_contract_registry.py | 14 ++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/README.md b/README.md index a8687b9..77a6d93 100644 --- a/README.md +++ b/README.md @@ -43,3 +43,6 @@ How to contribute - All changes should be backed by tests. This repo ships a production-like MVP rather than a toy example. Expect incremental exposure of domain adapters and more rigorous conformance tests in future milestones. + +## Migration and Evolution +- Contract migration: The contract registry now supports migrating a contract from an older version to a newer version. This enables adapters to evolve data schemas over time while marking the legacy version as migrated. The old version will have a migrated_to field pointing to the new version, and the new version will register its updated schema. This helps maintain backward-compatibility during MVP-driven evolution of the canonical IR. diff --git a/core/contract_registry.py b/core/contract_registry.py index 060989e..fc7a2c9 100644 --- a/core/contract_registry.py +++ b/core/contract_registry.py @@ -40,3 +40,25 @@ class ContractRegistry: if not isinstance(required, (list, tuple)): return False return all(field in adapter_data for field in required) + + def migrate_contract(self, name: str, old_version: str, new_version: str, new_schema: Dict[str, Any]) -> None: + """Migrate a contract from an old version to a new version. + + - Mark the old_version as migrated_to new_version (if present). + - Register the new_version with the provided new_schema. + This enables simple, MVP-aligned contract evolution for adapters during the MVP lifecycle. + """ + # Ensure parent namespace exists + self._store.setdefault(name, {}) + # Mark old version as migrated to new version if it exists + if old_version in self._store.get(name, {}): + self._store[name][old_version]["migrated_to"] = new_version + # Register the new version with its schema + self._store[name][new_version] = dict(new_schema) + + def get_migration_target(self, name: str, version: str) -> Optional[str]: + """Return the version that this version migrated to, if any.""" + contract = self.get_contract(name, version) + if contract is None: + return None + return contract.get("migrated_to") diff --git a/tests/test_contract_registry.py b/tests/test_contract_registry.py index d90b2d2..d771868 100644 --- a/tests/test_contract_registry.py +++ b/tests/test_contract_registry.py @@ -32,3 +32,17 @@ class TestContractRegistry(unittest.TestCase): reg.register_contract("LocalProblem", "1.0.0", {"required_fields": ["id", "objective"]}) adapter_data = {"id": "lp-1"} self.assertFalse(reg.conformance_check("LocalProblem", "1.0.0", adapter_data)) + + def test_contract_migration(self): + reg = ContractRegistry() + reg.register_contract("LocalProblem", "1.0.0", {"required_fields": ["id"]}) + # Migrate to a new version with a richer schema + reg.migrate_contract("LocalProblem", "1.0.0", "1.1.0", {"required_fields": ["id", "objective"]}) + + # New version should exist + self.assertEqual(reg.get_contract("LocalProblem", "1.1.0"), {"required_fields": ["id", "objective"]}) + + # Old version should be marked as migrated to the new version + old = reg.get_contract("LocalProblem", "1.0.0") + self.assertIsNotNone(old) + self.assertEqual(old.get("migrated_to"), "1.1.0")