diff --git a/a2a.yaml b/a2a.yaml index 36e91a2..a58e543 100644 --- a/a2a.yaml +++ b/a2a.yaml @@ -1,5 +1,5 @@ name: contract-clock-studio-v1 -version: 0.1.6 +version: 0.1.7 entrypoint: agent:ContractClockStudioV1 expose: public: false diff --git a/agent.py b/agent.py index ddb8762..b1078e5 100644 --- a/agent.py +++ b/agent.py @@ -108,7 +108,7 @@ class ContractClockStudioV1(A2AAgent[ContractClockStudioV1Config, PlatformUserAu "from contracts, persists user-scoped timelines, and serves a packed " "one-page React product at /app." ) - version = "0.1.6" + version = "0.1.7" config_model = ContractClockStudioV1Config auth_model = PlatformUserAuth diff --git a/db/migrations/002_reconcile_execution_receipts.sql b/db/migrations/002_reconcile_execution_receipts.sql new file mode 100644 index 0000000..a697e34 --- /dev/null +++ b/db/migrations/002_reconcile_execution_receipts.sql @@ -0,0 +1,36 @@ +-- Earlier generated revisions created execution_receipts with `skill_name` +-- and without the hashes/task metadata now written by ContractClock. Keep the +-- migration additive so existing receipts survive and fresh databases (whose +-- 001 migration already has these columns) remain idempotent. +ALTER TABLE execution_receipts + ADD COLUMN IF NOT EXISTS tool_name TEXT, + ADD COLUMN IF NOT EXISTS task_id TEXT, + ADD COLUMN IF NOT EXISTS input_hash TEXT, + ADD COLUMN IF NOT EXISTS result_hash TEXT; + +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = current_schema() + AND table_name = 'execution_receipts' + AND column_name = 'skill_name' + ) THEN + EXECUTE 'UPDATE execution_receipts + SET tool_name = COALESCE(tool_name, skill_name)'; + END IF; +END +$$; + +UPDATE execution_receipts +SET tool_name = COALESCE(tool_name, 'legacy'), + task_id = COALESCE(task_id, ''), + input_hash = COALESCE(input_hash, 'legacy'), + result_hash = COALESCE(result_hash, 'legacy'); + +ALTER TABLE execution_receipts + ALTER COLUMN tool_name SET NOT NULL, + ALTER COLUMN task_id SET NOT NULL, + ALTER COLUMN input_hash SET NOT NULL, + ALTER COLUMN result_hash SET NOT NULL; diff --git a/tests/test_full_stack_contract.py b/tests/test_full_stack_contract.py index e12f834..847addd 100644 --- a/tests/test_full_stack_contract.py +++ b/tests/test_full_stack_contract.py @@ -26,7 +26,7 @@ def test_full_stack_product_contract_card_and_manifest(): frontend = (ROOT / "frontend" / "src" / "App.jsx").read_text(encoding="utf-8") a2a_client = (ROOT / "frontend" / "src" / "a2a.js").read_text(encoding="utf-8") - assert "version: 0.1.6" in manifest + assert "version: 0.1.7" in manifest assert "public: false" in manifest assert "frontend:" in manifest and "mount: /app" in manifest assert "resources:" in manifest and "databases:" in manifest @@ -44,7 +44,7 @@ def test_full_stack_product_contract_card_and_manifest(): assert migrations and all(path.read_text(encoding="utf-8").strip() for path in migrations) card = load_local_project(ROOT).agent_cls().card() - assert card.version == "0.1.6" + assert card.version == "0.1.7" skill_names = {skill.name for skill in card.skills} assert {"analyze_contract", "get_contract_deadlines", "analyze_contract_upload_base64", "analyze_contract_file"} <= skill_names analyze_schema = next(skill.input_schema for skill in card.skills if skill.name == "analyze_contract") @@ -58,6 +58,13 @@ def test_full_stack_product_contract_card_and_manifest(): assert databases[0].migrations.path == "db/migrations" assert card.mcp_endpoint == "/mcp" + reconciliation = (ROOT / "db" / "migrations" / "002_reconcile_execution_receipts.sql").read_text( + encoding="utf-8" + ) + assert "ADD COLUMN IF NOT EXISTS tool_name" in reconciliation + assert "COALESCE(tool_name, skill_name)" in reconciliation + assert "ALTER COLUMN input_hash SET NOT NULL" in reconciliation + def test_explicit_date_only_success_and_failure(): module = load_agent_module()