From b7453fb848f15b4ebec65a7258b29038120cf3bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BD=A9=E8=BE=95=E5=8D=81=E5=9B=9B?= Date: Tue, 4 Aug 2026 17:29:21 +0800 Subject: [PATCH 1/6] refactor: adopt contract-first repository layout --- .../v1 => Contract/v1/Fixtures}/app-info.json | 0 .../v1/Fixtures}/handshake-request.json | 0 .../v1/Fixtures}/handshake-response.json | 0 .../v1/Fixtures}/hierarchy.json | 0 .../Fixtures}/invalid-handshake-request.json | 0 .../v1/Fixtures}/node-detail.json | 0 PROTOCOL.md => Contract/v1/PROTOCOL.md | 0 .../v1/Schemas}/app-info.schema.json | 0 .../v1/Schemas}/handshake.schema.json | 0 .../v1/Schemas}/hierarchy.schema.json | 0 .../v1/Schemas}/node-detail.schema.json | 0 .../attribute-identifier-not-namespaced.json | 0 .../invalid/coordinate-rect-missing-unit.json | 0 .../invalid/descending-protocol-range.json | 0 .../invalid/display-size-missing-unit.json | 0 .../invalid/extension-key-not-namespaced.json | 0 .../failure-response-contains-payload.json | 0 .../invalid/inconsistent-visibility.json | 0 .../Fixtures}/invalid/node-id-is-number.json | 0 .../invalid/patch-pattern-not-namespaced.json | 0 .../invalid/request-missing-request-id.json | 0 .../v2/Fixtures}/invalid/request-uses-v1.json | 0 .../success-response-contains-error.json | 0 .../invalid/unknown-response-status.json | 0 .../unsafe-integer-attribute-value.json | 0 .../valid/application-info-request.json | 0 .../valid/application-info-response.json | 0 .../valid/apply-attribute-patch-request.json | 0 .../valid/apply-attribute-patch-response.json | 0 .../v2/Fixtures}/valid/cancel-request.json | 0 .../v2/Fixtures}/valid/cancel-response.json | 0 .../clear-attribute-patches-request.json | 0 .../clear-attribute-patches-response.json | 0 .../v2/Fixtures}/valid/handshake-request.json | 0 .../Fixtures}/valid/handshake-response.json | 0 .../valid/hierarchy-snapshot-request.json | 0 .../valid/hierarchy-snapshot-response.json | 0 .../valid/list-attribute-patches-request.json | 0 .../list-attribute-patches-response.json | 0 .../valid/node-detail-failure-response.json | 0 .../Fixtures}/valid/node-detail-request.json | 0 .../Fixtures}/valid/node-detail-response.json | 0 .../valid/patchable-attributes-request.json | 0 .../valid/patchable-attributes-response.json | 0 .../valid/revert-attribute-patch-request.json | 0 .../revert-attribute-patch-response.json | 0 .../unknown-method-failure-response.json | 0 .../valid/unknown-method-request.json | 0 .../valid/vector-attribute-value.json | 0 PROTOCOL-2.0.md => Contract/v2/PROTOCOL.md | 4 +- .../v2/Schemas}/common.schema.json | 0 .../Schemas}/conformance-manifest.schema.json | 2 +- .../v2/Schemas}/inspection.schema.json | 0 .../v2/Schemas}/patching.schema.json | 0 .../v2/Schemas}/request.schema.json | 0 .../v2/Schemas}/response.schema.json | 0 .../v2/Schemas}/session.schema.json | 0 Contract/v2/manifest.json | 116 +++++++ Fixtures/v2/manifest.json | 116 ------- .../Kotlin}/build.gradle.kts | 2 +- .../astrolabe/protocol/core/RuntimeColor.kt | 0 .../protocol/core/RuntimeGeometry.kt | 0 .../protocol/core/RuntimeIdentifier.kt | 0 .../protocol/framing/RuntimeFrameCodec.kt | 0 .../protocol/inspection/RuntimeApplication.kt | 0 .../inspection/RuntimeAttributeValue.kt | 0 .../inspection/RuntimeAttributedTextRun.kt | 0 .../protocol/inspection/RuntimeHierarchy.kt | 0 .../inspection/RuntimeLayoutRelation.kt | 0 .../protocol/inspection/RuntimeNodeDetail.kt | 0 .../protocol/messaging/RuntimeCancellation.kt | 0 .../messaging/RuntimeJsonDocumentValidator.kt | 0 .../protocol/messaging/RuntimeMessage.kt | 0 .../protocol/messaging/RuntimeMessageCodec.kt | 0 .../protocol/negotiation/RuntimeCapability.kt | 0 .../protocol/negotiation/RuntimeHandshake.kt | 0 .../protocol/negotiation/RuntimeMethod.kt | 0 .../negotiation/RuntimeProtocolVersion.kt | 0 .../patching/RuntimeAttributePatch.kt | 0 .../patching/RuntimePatchableAttribute.kt | 0 .../ProtocolFixtureConformanceTest.kt | 8 +- .../protocol/framing/RuntimeFrameCodecTest.kt | 0 .../inspection/RuntimeApplicationModelTest.kt | 6 +- .../inspection/RuntimeHierarchyModelTest.kt | 8 +- .../inspection/RuntimeNodeDetailModelTest.kt | 8 +- .../messaging/RuntimeCancellationModelTest.kt | 4 +- .../messaging/RuntimeMessageCodecTest.kt | 18 +- .../negotiation/RuntimeSessionModelTest.kt | 16 +- .../patching/RuntimePatchingModelTest.kt | 10 +- .../AstrolabeProtocol/Core/RuntimeColor.swift | 0 .../Core/RuntimeGeometry.swift | 0 .../Core/RuntimeIdentifier.swift | 0 .../Core/RuntimeJSONValue.swift | 0 .../Core/RuntimeProtocolMetadata.swift | 0 .../Core/RuntimeRequiredNullable.swift | 0 .../Framing/RuntimeFrameCodec.swift | 0 .../Framing/RuntimeMessageCodec.swift | 0 .../Inspection/RuntimeApplication.swift | 0 .../Inspection/RuntimeAttributeValue.swift | 0 .../Inspection/RuntimeAttributedTextRun.swift | 0 .../Inspection/RuntimeHierarchy.swift | 0 .../Inspection/RuntimeLayoutRelation.swift | 0 .../Inspection/RuntimeNodeDetail.swift | 0 .../Messaging/RuntimeError.swift | 0 .../Messaging/RuntimeRequest.swift | 0 .../Messaging/RuntimeResponse.swift | 0 .../Negotiation/RuntimeCapability.swift | 0 .../Negotiation/RuntimeHandshake.swift | 0 .../Negotiation/RuntimeMethod.swift | 0 .../Negotiation/RuntimeProtocolVersion.swift | 0 .../Patching/RuntimeAttributePatch.swift | 0 .../Patching/RuntimePatchableAttribute.swift | 0 .../Contract/ProtocolFixtureTests.swift | 2 +- .../Contract/ProtocolV2ContractTests.swift | 8 +- .../Framing/RuntimeFrameCodecTests.swift | 0 .../Inspection/RuntimeModelTests.swift | 0 .../Messaging/RuntimeMessageCodecTests.swift | 0 Package.swift | 8 +- README.md | 23 +- README.zh-CN.md | 21 +- .../ContractValidation}/validate-contract.mjs | 8 +- .../Release}/release-prepare.mjs | 2 +- {scripts => Tooling/Release}/version-sync.mjs | 2 +- {scripts => Tooling/Release}/versioning.mjs | 2 +- {test => Tooling/Tests}/ci-workflow.test.mjs | 0 .../Tests}/release-prepare.test.mjs | 9 +- {test => Tooling/Tests}/versioning.test.mjs | 2 +- ...8-04-contract-first-directory-migration.md | 283 ++++++++++++++++++ package.json | 10 +- settings.gradle.kts | 1 + 130 files changed, 507 insertions(+), 192 deletions(-) rename {Fixtures/v1 => Contract/v1/Fixtures}/app-info.json (100%) rename {Fixtures/v1 => Contract/v1/Fixtures}/handshake-request.json (100%) rename {Fixtures/v1 => Contract/v1/Fixtures}/handshake-response.json (100%) rename {Fixtures/v1 => Contract/v1/Fixtures}/hierarchy.json (100%) rename {Fixtures/v1 => Contract/v1/Fixtures}/invalid-handshake-request.json (100%) rename {Fixtures/v1 => Contract/v1/Fixtures}/node-detail.json (100%) rename PROTOCOL.md => Contract/v1/PROTOCOL.md (100%) rename {Schemas/v1 => Contract/v1/Schemas}/app-info.schema.json (100%) rename {Schemas/v1 => Contract/v1/Schemas}/handshake.schema.json (100%) rename {Schemas/v1 => Contract/v1/Schemas}/hierarchy.schema.json (100%) rename {Schemas/v1 => Contract/v1/Schemas}/node-detail.schema.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/attribute-identifier-not-namespaced.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/coordinate-rect-missing-unit.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/descending-protocol-range.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/display-size-missing-unit.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/extension-key-not-namespaced.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/failure-response-contains-payload.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/inconsistent-visibility.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/node-id-is-number.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/patch-pattern-not-namespaced.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/request-missing-request-id.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/request-uses-v1.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/success-response-contains-error.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/unknown-response-status.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/invalid/unsafe-integer-attribute-value.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/application-info-request.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/application-info-response.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/apply-attribute-patch-request.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/apply-attribute-patch-response.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/cancel-request.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/cancel-response.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/clear-attribute-patches-request.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/clear-attribute-patches-response.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/handshake-request.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/handshake-response.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/hierarchy-snapshot-request.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/hierarchy-snapshot-response.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/list-attribute-patches-request.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/list-attribute-patches-response.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/node-detail-failure-response.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/node-detail-request.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/node-detail-response.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/patchable-attributes-request.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/patchable-attributes-response.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/revert-attribute-patch-request.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/revert-attribute-patch-response.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/unknown-method-failure-response.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/unknown-method-request.json (100%) rename {Fixtures/v2 => Contract/v2/Fixtures}/valid/vector-attribute-value.json (100%) rename PROTOCOL-2.0.md => Contract/v2/PROTOCOL.md (99%) rename {Schemas/v2 => Contract/v2/Schemas}/common.schema.json (100%) rename {Schemas/v2 => Contract/v2/Schemas}/conformance-manifest.schema.json (95%) rename {Schemas/v2 => Contract/v2/Schemas}/inspection.schema.json (100%) rename {Schemas/v2 => Contract/v2/Schemas}/patching.schema.json (100%) rename {Schemas/v2 => Contract/v2/Schemas}/request.schema.json (100%) rename {Schemas/v2 => Contract/v2/Schemas}/response.schema.json (100%) rename {Schemas/v2 => Contract/v2/Schemas}/session.schema.json (100%) create mode 100644 Contract/v2/manifest.json delete mode 100644 Fixtures/v2/manifest.json rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/build.gradle.kts (99%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeColor.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeGeometry.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeIdentifier.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/framing/RuntimeFrameCodec.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeApplication.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributeValue.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributedTextRun.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchy.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeLayoutRelation.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetail.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeCancellation.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeJsonDocumentValidator.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessage.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodec.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeCapability.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeHandshake.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeMethod.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeProtocolVersion.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/patching/RuntimeAttributePatch.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/main/kotlin/dev/astrolabe/protocol/patching/RuntimePatchableAttribute.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/test/kotlin/dev/astrolabe/protocol/contract/ProtocolFixtureConformanceTest.kt (96%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/test/kotlin/dev/astrolabe/protocol/framing/RuntimeFrameCodecTest.kt (100%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeApplicationModelTest.kt (81%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchyModelTest.kt (78%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetailModelTest.kt (93%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeCancellationModelTest.kt (81%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodecTest.kt (84%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/test/kotlin/dev/astrolabe/protocol/negotiation/RuntimeSessionModelTest.kt (86%) rename {AstrolabeProtocolKotlin => Implementations/Kotlin}/src/test/kotlin/dev/astrolabe/protocol/patching/RuntimePatchingModelTest.kt (85%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Core/RuntimeColor.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Core/RuntimeGeometry.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Core/RuntimeIdentifier.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Core/RuntimeJSONValue.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Core/RuntimeRequiredNullable.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Framing/RuntimeFrameCodec.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Framing/RuntimeMessageCodec.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Inspection/RuntimeApplication.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Inspection/RuntimeAttributeValue.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Inspection/RuntimeAttributedTextRun.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Inspection/RuntimeHierarchy.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Inspection/RuntimeLayoutRelation.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Inspection/RuntimeNodeDetail.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Messaging/RuntimeError.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Messaging/RuntimeRequest.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Messaging/RuntimeResponse.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Negotiation/RuntimeCapability.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Negotiation/RuntimeHandshake.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Negotiation/RuntimeMethod.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Negotiation/RuntimeProtocolVersion.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Patching/RuntimeAttributePatch.swift (100%) rename {Sources => Implementations/Swift/Sources}/AstrolabeProtocol/Patching/RuntimePatchableAttribute.swift (100%) rename {Tests => Implementations/Swift/Tests}/AstrolabeProtocolTests/Contract/ProtocolFixtureTests.swift (99%) rename {Tests => Implementations/Swift/Tests}/AstrolabeProtocolTests/Contract/ProtocolV2ContractTests.swift (93%) rename {Tests => Implementations/Swift/Tests}/AstrolabeProtocolTests/Framing/RuntimeFrameCodecTests.swift (100%) rename {Tests => Implementations/Swift/Tests}/AstrolabeProtocolTests/Inspection/RuntimeModelTests.swift (100%) rename {Tests => Implementations/Swift/Tests}/AstrolabeProtocolTests/Messaging/RuntimeMessageCodecTests.swift (100%) rename {scripts => Tooling/ContractValidation}/validate-contract.mjs (95%) rename {scripts => Tooling/Release}/release-prepare.mjs (98%) rename {scripts => Tooling/Release}/version-sync.mjs (95%) rename {scripts => Tooling/Release}/versioning.mjs (98%) rename {test => Tooling/Tests}/ci-workflow.test.mjs (100%) rename {test => Tooling/Tests}/release-prepare.test.mjs (92%) rename {test => Tooling/Tests}/versioning.test.mjs (96%) create mode 100644 docs/superpowers/plans/2026-08-04-contract-first-directory-migration.md diff --git a/Fixtures/v1/app-info.json b/Contract/v1/Fixtures/app-info.json similarity index 100% rename from Fixtures/v1/app-info.json rename to Contract/v1/Fixtures/app-info.json diff --git a/Fixtures/v1/handshake-request.json b/Contract/v1/Fixtures/handshake-request.json similarity index 100% rename from Fixtures/v1/handshake-request.json rename to Contract/v1/Fixtures/handshake-request.json diff --git a/Fixtures/v1/handshake-response.json b/Contract/v1/Fixtures/handshake-response.json similarity index 100% rename from Fixtures/v1/handshake-response.json rename to Contract/v1/Fixtures/handshake-response.json diff --git a/Fixtures/v1/hierarchy.json b/Contract/v1/Fixtures/hierarchy.json similarity index 100% rename from Fixtures/v1/hierarchy.json rename to Contract/v1/Fixtures/hierarchy.json diff --git a/Fixtures/v1/invalid-handshake-request.json b/Contract/v1/Fixtures/invalid-handshake-request.json similarity index 100% rename from Fixtures/v1/invalid-handshake-request.json rename to Contract/v1/Fixtures/invalid-handshake-request.json diff --git a/Fixtures/v1/node-detail.json b/Contract/v1/Fixtures/node-detail.json similarity index 100% rename from Fixtures/v1/node-detail.json rename to Contract/v1/Fixtures/node-detail.json diff --git a/PROTOCOL.md b/Contract/v1/PROTOCOL.md similarity index 100% rename from PROTOCOL.md rename to Contract/v1/PROTOCOL.md diff --git a/Schemas/v1/app-info.schema.json b/Contract/v1/Schemas/app-info.schema.json similarity index 100% rename from Schemas/v1/app-info.schema.json rename to Contract/v1/Schemas/app-info.schema.json diff --git a/Schemas/v1/handshake.schema.json b/Contract/v1/Schemas/handshake.schema.json similarity index 100% rename from Schemas/v1/handshake.schema.json rename to Contract/v1/Schemas/handshake.schema.json diff --git a/Schemas/v1/hierarchy.schema.json b/Contract/v1/Schemas/hierarchy.schema.json similarity index 100% rename from Schemas/v1/hierarchy.schema.json rename to Contract/v1/Schemas/hierarchy.schema.json diff --git a/Schemas/v1/node-detail.schema.json b/Contract/v1/Schemas/node-detail.schema.json similarity index 100% rename from Schemas/v1/node-detail.schema.json rename to Contract/v1/Schemas/node-detail.schema.json diff --git a/Fixtures/v2/invalid/attribute-identifier-not-namespaced.json b/Contract/v2/Fixtures/invalid/attribute-identifier-not-namespaced.json similarity index 100% rename from Fixtures/v2/invalid/attribute-identifier-not-namespaced.json rename to Contract/v2/Fixtures/invalid/attribute-identifier-not-namespaced.json diff --git a/Fixtures/v2/invalid/coordinate-rect-missing-unit.json b/Contract/v2/Fixtures/invalid/coordinate-rect-missing-unit.json similarity index 100% rename from Fixtures/v2/invalid/coordinate-rect-missing-unit.json rename to Contract/v2/Fixtures/invalid/coordinate-rect-missing-unit.json diff --git a/Fixtures/v2/invalid/descending-protocol-range.json b/Contract/v2/Fixtures/invalid/descending-protocol-range.json similarity index 100% rename from Fixtures/v2/invalid/descending-protocol-range.json rename to Contract/v2/Fixtures/invalid/descending-protocol-range.json diff --git a/Fixtures/v2/invalid/display-size-missing-unit.json b/Contract/v2/Fixtures/invalid/display-size-missing-unit.json similarity index 100% rename from Fixtures/v2/invalid/display-size-missing-unit.json rename to Contract/v2/Fixtures/invalid/display-size-missing-unit.json diff --git a/Fixtures/v2/invalid/extension-key-not-namespaced.json b/Contract/v2/Fixtures/invalid/extension-key-not-namespaced.json similarity index 100% rename from Fixtures/v2/invalid/extension-key-not-namespaced.json rename to Contract/v2/Fixtures/invalid/extension-key-not-namespaced.json diff --git a/Fixtures/v2/invalid/failure-response-contains-payload.json b/Contract/v2/Fixtures/invalid/failure-response-contains-payload.json similarity index 100% rename from Fixtures/v2/invalid/failure-response-contains-payload.json rename to Contract/v2/Fixtures/invalid/failure-response-contains-payload.json diff --git a/Fixtures/v2/invalid/inconsistent-visibility.json b/Contract/v2/Fixtures/invalid/inconsistent-visibility.json similarity index 100% rename from Fixtures/v2/invalid/inconsistent-visibility.json rename to Contract/v2/Fixtures/invalid/inconsistent-visibility.json diff --git a/Fixtures/v2/invalid/node-id-is-number.json b/Contract/v2/Fixtures/invalid/node-id-is-number.json similarity index 100% rename from Fixtures/v2/invalid/node-id-is-number.json rename to Contract/v2/Fixtures/invalid/node-id-is-number.json diff --git a/Fixtures/v2/invalid/patch-pattern-not-namespaced.json b/Contract/v2/Fixtures/invalid/patch-pattern-not-namespaced.json similarity index 100% rename from Fixtures/v2/invalid/patch-pattern-not-namespaced.json rename to Contract/v2/Fixtures/invalid/patch-pattern-not-namespaced.json diff --git a/Fixtures/v2/invalid/request-missing-request-id.json b/Contract/v2/Fixtures/invalid/request-missing-request-id.json similarity index 100% rename from Fixtures/v2/invalid/request-missing-request-id.json rename to Contract/v2/Fixtures/invalid/request-missing-request-id.json diff --git a/Fixtures/v2/invalid/request-uses-v1.json b/Contract/v2/Fixtures/invalid/request-uses-v1.json similarity index 100% rename from Fixtures/v2/invalid/request-uses-v1.json rename to Contract/v2/Fixtures/invalid/request-uses-v1.json diff --git a/Fixtures/v2/invalid/success-response-contains-error.json b/Contract/v2/Fixtures/invalid/success-response-contains-error.json similarity index 100% rename from Fixtures/v2/invalid/success-response-contains-error.json rename to Contract/v2/Fixtures/invalid/success-response-contains-error.json diff --git a/Fixtures/v2/invalid/unknown-response-status.json b/Contract/v2/Fixtures/invalid/unknown-response-status.json similarity index 100% rename from Fixtures/v2/invalid/unknown-response-status.json rename to Contract/v2/Fixtures/invalid/unknown-response-status.json diff --git a/Fixtures/v2/invalid/unsafe-integer-attribute-value.json b/Contract/v2/Fixtures/invalid/unsafe-integer-attribute-value.json similarity index 100% rename from Fixtures/v2/invalid/unsafe-integer-attribute-value.json rename to Contract/v2/Fixtures/invalid/unsafe-integer-attribute-value.json diff --git a/Fixtures/v2/valid/application-info-request.json b/Contract/v2/Fixtures/valid/application-info-request.json similarity index 100% rename from Fixtures/v2/valid/application-info-request.json rename to Contract/v2/Fixtures/valid/application-info-request.json diff --git a/Fixtures/v2/valid/application-info-response.json b/Contract/v2/Fixtures/valid/application-info-response.json similarity index 100% rename from Fixtures/v2/valid/application-info-response.json rename to Contract/v2/Fixtures/valid/application-info-response.json diff --git a/Fixtures/v2/valid/apply-attribute-patch-request.json b/Contract/v2/Fixtures/valid/apply-attribute-patch-request.json similarity index 100% rename from Fixtures/v2/valid/apply-attribute-patch-request.json rename to Contract/v2/Fixtures/valid/apply-attribute-patch-request.json diff --git a/Fixtures/v2/valid/apply-attribute-patch-response.json b/Contract/v2/Fixtures/valid/apply-attribute-patch-response.json similarity index 100% rename from Fixtures/v2/valid/apply-attribute-patch-response.json rename to Contract/v2/Fixtures/valid/apply-attribute-patch-response.json diff --git a/Fixtures/v2/valid/cancel-request.json b/Contract/v2/Fixtures/valid/cancel-request.json similarity index 100% rename from Fixtures/v2/valid/cancel-request.json rename to Contract/v2/Fixtures/valid/cancel-request.json diff --git a/Fixtures/v2/valid/cancel-response.json b/Contract/v2/Fixtures/valid/cancel-response.json similarity index 100% rename from Fixtures/v2/valid/cancel-response.json rename to Contract/v2/Fixtures/valid/cancel-response.json diff --git a/Fixtures/v2/valid/clear-attribute-patches-request.json b/Contract/v2/Fixtures/valid/clear-attribute-patches-request.json similarity index 100% rename from Fixtures/v2/valid/clear-attribute-patches-request.json rename to Contract/v2/Fixtures/valid/clear-attribute-patches-request.json diff --git a/Fixtures/v2/valid/clear-attribute-patches-response.json b/Contract/v2/Fixtures/valid/clear-attribute-patches-response.json similarity index 100% rename from Fixtures/v2/valid/clear-attribute-patches-response.json rename to Contract/v2/Fixtures/valid/clear-attribute-patches-response.json diff --git a/Fixtures/v2/valid/handshake-request.json b/Contract/v2/Fixtures/valid/handshake-request.json similarity index 100% rename from Fixtures/v2/valid/handshake-request.json rename to Contract/v2/Fixtures/valid/handshake-request.json diff --git a/Fixtures/v2/valid/handshake-response.json b/Contract/v2/Fixtures/valid/handshake-response.json similarity index 100% rename from Fixtures/v2/valid/handshake-response.json rename to Contract/v2/Fixtures/valid/handshake-response.json diff --git a/Fixtures/v2/valid/hierarchy-snapshot-request.json b/Contract/v2/Fixtures/valid/hierarchy-snapshot-request.json similarity index 100% rename from Fixtures/v2/valid/hierarchy-snapshot-request.json rename to Contract/v2/Fixtures/valid/hierarchy-snapshot-request.json diff --git a/Fixtures/v2/valid/hierarchy-snapshot-response.json b/Contract/v2/Fixtures/valid/hierarchy-snapshot-response.json similarity index 100% rename from Fixtures/v2/valid/hierarchy-snapshot-response.json rename to Contract/v2/Fixtures/valid/hierarchy-snapshot-response.json diff --git a/Fixtures/v2/valid/list-attribute-patches-request.json b/Contract/v2/Fixtures/valid/list-attribute-patches-request.json similarity index 100% rename from Fixtures/v2/valid/list-attribute-patches-request.json rename to Contract/v2/Fixtures/valid/list-attribute-patches-request.json diff --git a/Fixtures/v2/valid/list-attribute-patches-response.json b/Contract/v2/Fixtures/valid/list-attribute-patches-response.json similarity index 100% rename from Fixtures/v2/valid/list-attribute-patches-response.json rename to Contract/v2/Fixtures/valid/list-attribute-patches-response.json diff --git a/Fixtures/v2/valid/node-detail-failure-response.json b/Contract/v2/Fixtures/valid/node-detail-failure-response.json similarity index 100% rename from Fixtures/v2/valid/node-detail-failure-response.json rename to Contract/v2/Fixtures/valid/node-detail-failure-response.json diff --git a/Fixtures/v2/valid/node-detail-request.json b/Contract/v2/Fixtures/valid/node-detail-request.json similarity index 100% rename from Fixtures/v2/valid/node-detail-request.json rename to Contract/v2/Fixtures/valid/node-detail-request.json diff --git a/Fixtures/v2/valid/node-detail-response.json b/Contract/v2/Fixtures/valid/node-detail-response.json similarity index 100% rename from Fixtures/v2/valid/node-detail-response.json rename to Contract/v2/Fixtures/valid/node-detail-response.json diff --git a/Fixtures/v2/valid/patchable-attributes-request.json b/Contract/v2/Fixtures/valid/patchable-attributes-request.json similarity index 100% rename from Fixtures/v2/valid/patchable-attributes-request.json rename to Contract/v2/Fixtures/valid/patchable-attributes-request.json diff --git a/Fixtures/v2/valid/patchable-attributes-response.json b/Contract/v2/Fixtures/valid/patchable-attributes-response.json similarity index 100% rename from Fixtures/v2/valid/patchable-attributes-response.json rename to Contract/v2/Fixtures/valid/patchable-attributes-response.json diff --git a/Fixtures/v2/valid/revert-attribute-patch-request.json b/Contract/v2/Fixtures/valid/revert-attribute-patch-request.json similarity index 100% rename from Fixtures/v2/valid/revert-attribute-patch-request.json rename to Contract/v2/Fixtures/valid/revert-attribute-patch-request.json diff --git a/Fixtures/v2/valid/revert-attribute-patch-response.json b/Contract/v2/Fixtures/valid/revert-attribute-patch-response.json similarity index 100% rename from Fixtures/v2/valid/revert-attribute-patch-response.json rename to Contract/v2/Fixtures/valid/revert-attribute-patch-response.json diff --git a/Fixtures/v2/valid/unknown-method-failure-response.json b/Contract/v2/Fixtures/valid/unknown-method-failure-response.json similarity index 100% rename from Fixtures/v2/valid/unknown-method-failure-response.json rename to Contract/v2/Fixtures/valid/unknown-method-failure-response.json diff --git a/Fixtures/v2/valid/unknown-method-request.json b/Contract/v2/Fixtures/valid/unknown-method-request.json similarity index 100% rename from Fixtures/v2/valid/unknown-method-request.json rename to Contract/v2/Fixtures/valid/unknown-method-request.json diff --git a/Fixtures/v2/valid/vector-attribute-value.json b/Contract/v2/Fixtures/valid/vector-attribute-value.json similarity index 100% rename from Fixtures/v2/valid/vector-attribute-value.json rename to Contract/v2/Fixtures/valid/vector-attribute-value.json diff --git a/PROTOCOL-2.0.md b/Contract/v2/PROTOCOL.md similarity index 99% rename from PROTOCOL-2.0.md rename to Contract/v2/PROTOCOL.md index e1b5665..a85549d 100644 --- a/PROTOCOL-2.0.md +++ b/Contract/v2/PROTOCOL.md @@ -12,7 +12,7 @@ ADB, screenshots, CLI commands, MCP tools, UIKit, Auto Layout, Android View, or Compose implementation details. A transport only needs to provide a reliable, ordered byte stream. -`PROTOCOL-2.0.md`, `Schemas/v2`, and `Fixtures/v2/manifest.json` are normative. +`Contract/v2/PROTOCOL.md`, `Contract/v2/Schemas`, and `Contract/v2/manifest.json` are normative. Swift and Kotlin source code are implementations of this contract, not its definition. @@ -439,7 +439,7 @@ not reproduce Swift type names or Swift coding behavior. ## 15. Conformance assets -Schemas use JSON Schema Draft 2020-12. `Fixtures/v2/manifest.json` lists every +Schemas use JSON Schema Draft 2020-12. `Contract/v2/manifest.json` lists every method, Schema reference, valid Fixture, invalid Fixture, expected outcome, and rejection reason. A conforming implementation must: diff --git a/Schemas/v2/common.schema.json b/Contract/v2/Schemas/common.schema.json similarity index 100% rename from Schemas/v2/common.schema.json rename to Contract/v2/Schemas/common.schema.json diff --git a/Schemas/v2/conformance-manifest.schema.json b/Contract/v2/Schemas/conformance-manifest.schema.json similarity index 95% rename from Schemas/v2/conformance-manifest.schema.json rename to Contract/v2/Schemas/conformance-manifest.schema.json index 86225b7..0bbf98a 100644 --- a/Schemas/v2/conformance-manifest.schema.json +++ b/Contract/v2/Schemas/conformance-manifest.schema.json @@ -32,7 +32,7 @@ "required": ["name", "fixture", "schema", "expectation"], "properties": { "name": { "type": "string", "minLength": 1 }, - "fixture": { "type": "string", "pattern": "^Fixtures/v2/(valid|invalid)/.+\\.json$" }, + "fixture": { "type": "string", "pattern": "^Contract/v2/Fixtures/(valid|invalid)/.+\\.json$" }, "schema": { "type": "string", "format": "uri-reference" }, "expectation": { "enum": ["valid", "invalid"] }, "semanticRule": { "enum": ["ascendingProtocolRange", "derivedVisibility"] }, diff --git a/Schemas/v2/inspection.schema.json b/Contract/v2/Schemas/inspection.schema.json similarity index 100% rename from Schemas/v2/inspection.schema.json rename to Contract/v2/Schemas/inspection.schema.json diff --git a/Schemas/v2/patching.schema.json b/Contract/v2/Schemas/patching.schema.json similarity index 100% rename from Schemas/v2/patching.schema.json rename to Contract/v2/Schemas/patching.schema.json diff --git a/Schemas/v2/request.schema.json b/Contract/v2/Schemas/request.schema.json similarity index 100% rename from Schemas/v2/request.schema.json rename to Contract/v2/Schemas/request.schema.json diff --git a/Schemas/v2/response.schema.json b/Contract/v2/Schemas/response.schema.json similarity index 100% rename from Schemas/v2/response.schema.json rename to Contract/v2/Schemas/response.schema.json diff --git a/Schemas/v2/session.schema.json b/Contract/v2/Schemas/session.schema.json similarity index 100% rename from Schemas/v2/session.schema.json rename to Contract/v2/Schemas/session.schema.json diff --git a/Contract/v2/manifest.json b/Contract/v2/manifest.json new file mode 100644 index 0000000..afb7f65 --- /dev/null +++ b/Contract/v2/manifest.json @@ -0,0 +1,116 @@ +{ + "schemaVersion": 1, + "wireProtocolVersion": { "major": 2, "minor": 0 }, + "methods": [ + { + "name": "handshake", + "capability": null, + "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/handshakeRequest", + "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/handshakeSuccessResponse", + "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/handshakeFailureResponse" + }, + { + "name": "applicationInfo", + "capability": "applicationInfo", + "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applicationInfoRequest", + "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/applicationInfoSuccessResponse", + "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/applicationInfoFailureResponse" + }, + { + "name": "hierarchySnapshot", + "capability": "hierarchySnapshot", + "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/hierarchySnapshotRequest", + "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/hierarchySnapshotSuccessResponse", + "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/hierarchySnapshotFailureResponse" + }, + { + "name": "nodeDetail", + "capability": "nodeDetail", + "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/nodeDetailRequest", + "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/nodeDetailSuccessResponse", + "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/nodeDetailFailureResponse" + }, + { + "name": "patchableAttributes", + "capability": "attributePatchDiscovery", + "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/patchableAttributesRequest", + "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/patchableAttributesSuccessResponse", + "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/patchableAttributesFailureResponse" + }, + { + "name": "applyAttributePatch", + "capability": "attributePatching", + "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applyAttributePatchRequest", + "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/applyAttributePatchSuccessResponse", + "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/applyAttributePatchFailureResponse" + }, + { + "name": "listAttributePatches", + "capability": "attributePatching", + "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/listAttributePatchesRequest", + "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/listAttributePatchesSuccessResponse", + "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/listAttributePatchesFailureResponse" + }, + { + "name": "revertAttributePatch", + "capability": "attributePatching", + "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/revertAttributePatchRequest", + "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/revertAttributePatchSuccessResponse", + "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/revertAttributePatchFailureResponse" + }, + { + "name": "clearAttributePatches", + "capability": "attributePatching", + "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/clearAttributePatchesRequest", + "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/clearAttributePatchesSuccessResponse", + "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/clearAttributePatchesFailureResponse" + }, + { + "name": "cancelRequest", + "capability": "requestCancellation", + "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/cancelRequestRequest", + "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/cancelRequestSuccessResponse", + "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/cancelRequestFailureResponse" + } + ], + "cases": [ + { "name": "handshake request", "fixture": "Contract/v2/Fixtures/valid/handshake-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/handshakeRequest", "expectation": "valid" }, + { "name": "handshake response", "fixture": "Contract/v2/Fixtures/valid/handshake-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/handshakeSuccessResponse", "expectation": "valid" }, + { "name": "application info request", "fixture": "Contract/v2/Fixtures/valid/application-info-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applicationInfoRequest", "expectation": "valid" }, + { "name": "application info response", "fixture": "Contract/v2/Fixtures/valid/application-info-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/applicationInfoSuccessResponse", "expectation": "valid" }, + { "name": "hierarchy request", "fixture": "Contract/v2/Fixtures/valid/hierarchy-snapshot-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/hierarchySnapshotRequest", "expectation": "valid" }, + { "name": "hierarchy response", "fixture": "Contract/v2/Fixtures/valid/hierarchy-snapshot-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/hierarchySnapshotSuccessResponse", "expectation": "valid" }, + { "name": "node detail request", "fixture": "Contract/v2/Fixtures/valid/node-detail-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/nodeDetailRequest", "expectation": "valid" }, + { "name": "node detail response", "fixture": "Contract/v2/Fixtures/valid/node-detail-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/nodeDetailSuccessResponse", "expectation": "valid" }, + { "name": "patch catalog request", "fixture": "Contract/v2/Fixtures/valid/patchable-attributes-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/patchableAttributesRequest", "expectation": "valid" }, + { "name": "patch catalog response", "fixture": "Contract/v2/Fixtures/valid/patchable-attributes-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/patchableAttributesSuccessResponse", "expectation": "valid" }, + { "name": "apply patch request", "fixture": "Contract/v2/Fixtures/valid/apply-attribute-patch-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applyAttributePatchRequest", "expectation": "valid" }, + { "name": "apply patch response", "fixture": "Contract/v2/Fixtures/valid/apply-attribute-patch-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/applyAttributePatchSuccessResponse", "expectation": "valid" }, + { "name": "list patches request", "fixture": "Contract/v2/Fixtures/valid/list-attribute-patches-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/listAttributePatchesRequest", "expectation": "valid" }, + { "name": "list patches response", "fixture": "Contract/v2/Fixtures/valid/list-attribute-patches-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/listAttributePatchesSuccessResponse", "expectation": "valid" }, + { "name": "revert patch request", "fixture": "Contract/v2/Fixtures/valid/revert-attribute-patch-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/revertAttributePatchRequest", "expectation": "valid" }, + { "name": "revert patch response", "fixture": "Contract/v2/Fixtures/valid/revert-attribute-patch-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/revertAttributePatchSuccessResponse", "expectation": "valid" }, + { "name": "clear patches request", "fixture": "Contract/v2/Fixtures/valid/clear-attribute-patches-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/clearAttributePatchesRequest", "expectation": "valid" }, + { "name": "clear patches response", "fixture": "Contract/v2/Fixtures/valid/clear-attribute-patches-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/clearAttributePatchesSuccessResponse", "expectation": "valid" }, + { "name": "cancel request", "fixture": "Contract/v2/Fixtures/valid/cancel-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/cancelRequestRequest", "expectation": "valid" }, + { "name": "cancel response", "fixture": "Contract/v2/Fixtures/valid/cancel-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/cancelRequestSuccessResponse", "expectation": "valid" }, + { "name": "failure response", "fixture": "Contract/v2/Fixtures/valid/node-detail-failure-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/nodeDetailFailureResponse", "expectation": "valid" }, + { "name": "unknown method request envelope", "fixture": "Contract/v2/Fixtures/valid/unknown-method-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/unknownMethodRequest", "expectation": "valid" }, + { "name": "unknown method failure envelope", "fixture": "Contract/v2/Fixtures/valid/unknown-method-failure-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/unknownMethodFailureResponse", "expectation": "valid" }, + { "name": "signed vector attribute value", "fixture": "Contract/v2/Fixtures/valid/vector-attribute-value.json", "schema": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/attributeValue", "expectation": "valid" }, + { "name": "missing request id", "fixture": "Contract/v2/Fixtures/invalid/request-missing-request-id.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applicationInfoRequest", "expectation": "invalid", "reason": "requestID is required" }, + { "name": "v1 message on v2 contract", "fixture": "Contract/v2/Fixtures/invalid/request-uses-v1.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applicationInfoRequest", "expectation": "invalid", "reason": "V2 does not accept V1 aliases or envelopes" }, + { "name": "numeric node id", "fixture": "Contract/v2/Fixtures/invalid/node-id-is-number.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/nodeDetailRequest", "expectation": "invalid", "reason": "node IDs are opaque strings" }, + { "name": "success with error", "fixture": "Contract/v2/Fixtures/invalid/success-response-contains-error.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/cancelRequestSuccessResponse", "expectation": "invalid", "reason": "success responses cannot contain error" }, + { "name": "failure with payload", "fixture": "Contract/v2/Fixtures/invalid/failure-response-contains-payload.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/nodeDetailFailureResponse", "expectation": "invalid", "reason": "failure responses cannot contain payload" }, + { "name": "unnamespaced attribute", "fixture": "Contract/v2/Fixtures/invalid/attribute-identifier-not-namespaced.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applyAttributePatchRequest", "expectation": "invalid", "reason": "attribute identifiers require a namespace" }, + { "name": "unnamespaced patch pattern", "fixture": "Contract/v2/Fixtures/invalid/patch-pattern-not-namespaced.json", "schema": "https://astrolabe.dev/schemas/v2/patching.schema.json#/$defs/patchableAttribute", "expectation": "invalid", "reason": "patch catalog patterns require a namespace" }, + { "name": "unsafe integer", "fixture": "Contract/v2/Fixtures/invalid/unsafe-integer-attribute-value.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applyAttributePatchRequest", "expectation": "invalid", "reason": "integer values must remain JSON safe" }, + { "name": "coordinate without unit", "fixture": "Contract/v2/Fixtures/invalid/coordinate-rect-missing-unit.json", "schema": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/coordinateRect", "expectation": "invalid", "reason": "every coordinate declares its unit" }, + { "name": "display size without unit", "fixture": "Contract/v2/Fixtures/invalid/display-size-missing-unit.json", "schema": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/displayInfo", "expectation": "invalid", "reason": "display dimensions declare logical or pixel units" }, + { "name": "unnamespaced extension", "fixture": "Contract/v2/Fixtures/invalid/extension-key-not-namespaced.json", "schema": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/extensionMap", "expectation": "invalid", "reason": "extension keys require a namespace" }, + { "name": "unknown response status", "fixture": "Contract/v2/Fixtures/invalid/unknown-response-status.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json", "expectation": "invalid", "reason": "response status is a closed discriminator" }, + { "name": "descending protocol range", "fixture": "Contract/v2/Fixtures/invalid/descending-protocol-range.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/handshakeRequest", "expectation": "invalid", "semanticRule": "ascendingProtocolRange", "reason": "minimum protocol version cannot exceed maximum" }, + { "name": "inconsistent visibility", "fixture": "Contract/v2/Fixtures/invalid/inconsistent-visibility.json", "schema": "https://astrolabe.dev/schemas/v2/inspection.schema.json#/$defs/visibility", "expectation": "invalid", "semanticRule": "derivedVisibility", "reason": "a hidden node cannot be onscreen" } + ] +} diff --git a/Fixtures/v2/manifest.json b/Fixtures/v2/manifest.json deleted file mode 100644 index 64d6901..0000000 --- a/Fixtures/v2/manifest.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "schemaVersion": 1, - "wireProtocolVersion": { "major": 2, "minor": 0 }, - "methods": [ - { - "name": "handshake", - "capability": null, - "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/handshakeRequest", - "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/handshakeSuccessResponse", - "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/handshakeFailureResponse" - }, - { - "name": "applicationInfo", - "capability": "applicationInfo", - "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applicationInfoRequest", - "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/applicationInfoSuccessResponse", - "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/applicationInfoFailureResponse" - }, - { - "name": "hierarchySnapshot", - "capability": "hierarchySnapshot", - "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/hierarchySnapshotRequest", - "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/hierarchySnapshotSuccessResponse", - "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/hierarchySnapshotFailureResponse" - }, - { - "name": "nodeDetail", - "capability": "nodeDetail", - "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/nodeDetailRequest", - "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/nodeDetailSuccessResponse", - "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/nodeDetailFailureResponse" - }, - { - "name": "patchableAttributes", - "capability": "attributePatchDiscovery", - "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/patchableAttributesRequest", - "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/patchableAttributesSuccessResponse", - "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/patchableAttributesFailureResponse" - }, - { - "name": "applyAttributePatch", - "capability": "attributePatching", - "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applyAttributePatchRequest", - "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/applyAttributePatchSuccessResponse", - "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/applyAttributePatchFailureResponse" - }, - { - "name": "listAttributePatches", - "capability": "attributePatching", - "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/listAttributePatchesRequest", - "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/listAttributePatchesSuccessResponse", - "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/listAttributePatchesFailureResponse" - }, - { - "name": "revertAttributePatch", - "capability": "attributePatching", - "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/revertAttributePatchRequest", - "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/revertAttributePatchSuccessResponse", - "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/revertAttributePatchFailureResponse" - }, - { - "name": "clearAttributePatches", - "capability": "attributePatching", - "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/clearAttributePatchesRequest", - "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/clearAttributePatchesSuccessResponse", - "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/clearAttributePatchesFailureResponse" - }, - { - "name": "cancelRequest", - "capability": "requestCancellation", - "requestSchema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/cancelRequestRequest", - "successResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/cancelRequestSuccessResponse", - "failureResponseSchema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/cancelRequestFailureResponse" - } - ], - "cases": [ - { "name": "handshake request", "fixture": "Fixtures/v2/valid/handshake-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/handshakeRequest", "expectation": "valid" }, - { "name": "handshake response", "fixture": "Fixtures/v2/valid/handshake-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/handshakeSuccessResponse", "expectation": "valid" }, - { "name": "application info request", "fixture": "Fixtures/v2/valid/application-info-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applicationInfoRequest", "expectation": "valid" }, - { "name": "application info response", "fixture": "Fixtures/v2/valid/application-info-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/applicationInfoSuccessResponse", "expectation": "valid" }, - { "name": "hierarchy request", "fixture": "Fixtures/v2/valid/hierarchy-snapshot-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/hierarchySnapshotRequest", "expectation": "valid" }, - { "name": "hierarchy response", "fixture": "Fixtures/v2/valid/hierarchy-snapshot-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/hierarchySnapshotSuccessResponse", "expectation": "valid" }, - { "name": "node detail request", "fixture": "Fixtures/v2/valid/node-detail-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/nodeDetailRequest", "expectation": "valid" }, - { "name": "node detail response", "fixture": "Fixtures/v2/valid/node-detail-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/nodeDetailSuccessResponse", "expectation": "valid" }, - { "name": "patch catalog request", "fixture": "Fixtures/v2/valid/patchable-attributes-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/patchableAttributesRequest", "expectation": "valid" }, - { "name": "patch catalog response", "fixture": "Fixtures/v2/valid/patchable-attributes-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/patchableAttributesSuccessResponse", "expectation": "valid" }, - { "name": "apply patch request", "fixture": "Fixtures/v2/valid/apply-attribute-patch-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applyAttributePatchRequest", "expectation": "valid" }, - { "name": "apply patch response", "fixture": "Fixtures/v2/valid/apply-attribute-patch-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/applyAttributePatchSuccessResponse", "expectation": "valid" }, - { "name": "list patches request", "fixture": "Fixtures/v2/valid/list-attribute-patches-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/listAttributePatchesRequest", "expectation": "valid" }, - { "name": "list patches response", "fixture": "Fixtures/v2/valid/list-attribute-patches-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/listAttributePatchesSuccessResponse", "expectation": "valid" }, - { "name": "revert patch request", "fixture": "Fixtures/v2/valid/revert-attribute-patch-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/revertAttributePatchRequest", "expectation": "valid" }, - { "name": "revert patch response", "fixture": "Fixtures/v2/valid/revert-attribute-patch-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/revertAttributePatchSuccessResponse", "expectation": "valid" }, - { "name": "clear patches request", "fixture": "Fixtures/v2/valid/clear-attribute-patches-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/clearAttributePatchesRequest", "expectation": "valid" }, - { "name": "clear patches response", "fixture": "Fixtures/v2/valid/clear-attribute-patches-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/clearAttributePatchesSuccessResponse", "expectation": "valid" }, - { "name": "cancel request", "fixture": "Fixtures/v2/valid/cancel-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/cancelRequestRequest", "expectation": "valid" }, - { "name": "cancel response", "fixture": "Fixtures/v2/valid/cancel-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/cancelRequestSuccessResponse", "expectation": "valid" }, - { "name": "failure response", "fixture": "Fixtures/v2/valid/node-detail-failure-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/nodeDetailFailureResponse", "expectation": "valid" }, - { "name": "unknown method request envelope", "fixture": "Fixtures/v2/valid/unknown-method-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/unknownMethodRequest", "expectation": "valid" }, - { "name": "unknown method failure envelope", "fixture": "Fixtures/v2/valid/unknown-method-failure-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/unknownMethodFailureResponse", "expectation": "valid" }, - { "name": "signed vector attribute value", "fixture": "Fixtures/v2/valid/vector-attribute-value.json", "schema": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/attributeValue", "expectation": "valid" }, - { "name": "missing request id", "fixture": "Fixtures/v2/invalid/request-missing-request-id.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applicationInfoRequest", "expectation": "invalid", "reason": "requestID is required" }, - { "name": "v1 message on v2 contract", "fixture": "Fixtures/v2/invalid/request-uses-v1.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applicationInfoRequest", "expectation": "invalid", "reason": "V2 does not accept V1 aliases or envelopes" }, - { "name": "numeric node id", "fixture": "Fixtures/v2/invalid/node-id-is-number.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/nodeDetailRequest", "expectation": "invalid", "reason": "node IDs are opaque strings" }, - { "name": "success with error", "fixture": "Fixtures/v2/invalid/success-response-contains-error.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/cancelRequestSuccessResponse", "expectation": "invalid", "reason": "success responses cannot contain error" }, - { "name": "failure with payload", "fixture": "Fixtures/v2/invalid/failure-response-contains-payload.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/nodeDetailFailureResponse", "expectation": "invalid", "reason": "failure responses cannot contain payload" }, - { "name": "unnamespaced attribute", "fixture": "Fixtures/v2/invalid/attribute-identifier-not-namespaced.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applyAttributePatchRequest", "expectation": "invalid", "reason": "attribute identifiers require a namespace" }, - { "name": "unnamespaced patch pattern", "fixture": "Fixtures/v2/invalid/patch-pattern-not-namespaced.json", "schema": "https://astrolabe.dev/schemas/v2/patching.schema.json#/$defs/patchableAttribute", "expectation": "invalid", "reason": "patch catalog patterns require a namespace" }, - { "name": "unsafe integer", "fixture": "Fixtures/v2/invalid/unsafe-integer-attribute-value.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applyAttributePatchRequest", "expectation": "invalid", "reason": "integer values must remain JSON safe" }, - { "name": "coordinate without unit", "fixture": "Fixtures/v2/invalid/coordinate-rect-missing-unit.json", "schema": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/coordinateRect", "expectation": "invalid", "reason": "every coordinate declares its unit" }, - { "name": "display size without unit", "fixture": "Fixtures/v2/invalid/display-size-missing-unit.json", "schema": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/displayInfo", "expectation": "invalid", "reason": "display dimensions declare logical or pixel units" }, - { "name": "unnamespaced extension", "fixture": "Fixtures/v2/invalid/extension-key-not-namespaced.json", "schema": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/extensionMap", "expectation": "invalid", "reason": "extension keys require a namespace" }, - { "name": "unknown response status", "fixture": "Fixtures/v2/invalid/unknown-response-status.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json", "expectation": "invalid", "reason": "response status is a closed discriminator" }, - { "name": "descending protocol range", "fixture": "Fixtures/v2/invalid/descending-protocol-range.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/handshakeRequest", "expectation": "invalid", "semanticRule": "ascendingProtocolRange", "reason": "minimum protocol version cannot exceed maximum" }, - { "name": "inconsistent visibility", "fixture": "Fixtures/v2/invalid/inconsistent-visibility.json", "schema": "https://astrolabe.dev/schemas/v2/inspection.schema.json#/$defs/visibility", "expectation": "invalid", "semanticRule": "derivedVisibility", "reason": "a hidden node cannot be onscreen" } - ] -} diff --git a/AstrolabeProtocolKotlin/build.gradle.kts b/Implementations/Kotlin/build.gradle.kts similarity index 99% rename from AstrolabeProtocolKotlin/build.gradle.kts rename to Implementations/Kotlin/build.gradle.kts index 6b752de..2a85313 100644 --- a/AstrolabeProtocolKotlin/build.gradle.kts +++ b/Implementations/Kotlin/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { sourceSets { test { - resources.srcDir(rootProject.layout.projectDirectory.dir("Fixtures")) + resources.srcDir(rootProject.layout.projectDirectory.dir("Contract")) } } diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeColor.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeColor.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeColor.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeColor.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeGeometry.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeGeometry.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeGeometry.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeGeometry.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeIdentifier.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeIdentifier.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeIdentifier.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/core/RuntimeIdentifier.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/framing/RuntimeFrameCodec.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/framing/RuntimeFrameCodec.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/framing/RuntimeFrameCodec.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/framing/RuntimeFrameCodec.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeApplication.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeApplication.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeApplication.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeApplication.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributeValue.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributeValue.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributeValue.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributeValue.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributedTextRun.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributedTextRun.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributedTextRun.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributedTextRun.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchy.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchy.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchy.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchy.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeLayoutRelation.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeLayoutRelation.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeLayoutRelation.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeLayoutRelation.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetail.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetail.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetail.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetail.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeCancellation.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeCancellation.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeCancellation.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeCancellation.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeJsonDocumentValidator.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeJsonDocumentValidator.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeJsonDocumentValidator.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeJsonDocumentValidator.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessage.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessage.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessage.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessage.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodec.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodec.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodec.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodec.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeCapability.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeCapability.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeCapability.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeCapability.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeHandshake.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeHandshake.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeHandshake.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeHandshake.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeMethod.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeMethod.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeMethod.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeMethod.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeProtocolVersion.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeProtocolVersion.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeProtocolVersion.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeProtocolVersion.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/patching/RuntimeAttributePatch.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/patching/RuntimeAttributePatch.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/patching/RuntimeAttributePatch.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/patching/RuntimeAttributePatch.kt diff --git a/AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/patching/RuntimePatchableAttribute.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/patching/RuntimePatchableAttribute.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/main/kotlin/dev/astrolabe/protocol/patching/RuntimePatchableAttribute.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/patching/RuntimePatchableAttribute.kt diff --git a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/contract/ProtocolFixtureConformanceTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/contract/ProtocolFixtureConformanceTest.kt similarity index 96% rename from AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/contract/ProtocolFixtureConformanceTest.kt rename to Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/contract/ProtocolFixtureConformanceTest.kt index e45160c..9580c39 100644 --- a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/contract/ProtocolFixtureConformanceTest.kt +++ b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/contract/ProtocolFixtureConformanceTest.kt @@ -44,8 +44,8 @@ class ProtocolFixtureConformanceTest { "vector-attribute-value.json" to { codec.decodeValue(it, RuntimeAttributeValue.serializer()) } ) - assertEquals(fixtureNames("v2/valid"), cases.keys) - cases.forEach { (name, operation) -> operation(fixture("v2/valid/$name")) } + assertEquals(fixtureNames("v2/Fixtures/valid"), cases.keys) + cases.forEach { (name, operation) -> operation(fixture("v2/Fixtures/valid/$name")) } } @Test @@ -85,10 +85,10 @@ class ProtocolFixtureConformanceTest { } ) - assertEquals(fixtureNames("v2/invalid"), cases.keys) + assertEquals(fixtureNames("v2/Fixtures/invalid"), cases.keys) cases.forEach { (name, operation) -> assertFailsWith { - operation(fixture("v2/invalid/$name")) + operation(fixture("v2/Fixtures/invalid/$name")) } } } diff --git a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/framing/RuntimeFrameCodecTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/framing/RuntimeFrameCodecTest.kt similarity index 100% rename from AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/framing/RuntimeFrameCodecTest.kt rename to Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/framing/RuntimeFrameCodecTest.kt diff --git a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeApplicationModelTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeApplicationModelTest.kt similarity index 81% rename from AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeApplicationModelTest.kt rename to Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeApplicationModelTest.kt index c4306e6..6242f9b 100644 --- a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeApplicationModelTest.kt +++ b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeApplicationModelTest.kt @@ -16,8 +16,8 @@ class RuntimeApplicationModelTest { @Test fun `application info fixtures decode through typed contracts`() { - val request = codec.decodeRequest(fixture("v2/valid/application-info-request.json")) - val response = codec.decodeResponse(fixture("v2/valid/application-info-response.json")) + val request = codec.decodeRequest(fixture("v2/Fixtures/valid/application-info-request.json")) + val response = codec.decodeResponse(fixture("v2/Fixtures/valid/application-info-response.json")) codec.decodeRequestParameters(request, RuntimeApplicationInfoParameters.contract) val payload = codec.decodeSuccessPayload(response, RuntimeApplicationInfoPayload.contract) @@ -31,7 +31,7 @@ class RuntimeApplicationModelTest { fun `display facts require units`() { assertFailsWith { codec.decodeValue( - fixture("v2/invalid/display-size-missing-unit.json"), + fixture("v2/Fixtures/invalid/display-size-missing-unit.json"), RuntimeDisplayInfo.serializer() ) } diff --git a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchyModelTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchyModelTest.kt similarity index 78% rename from AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchyModelTest.kt rename to Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchyModelTest.kt index eafa4ff..c865831 100644 --- a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchyModelTest.kt +++ b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchyModelTest.kt @@ -16,8 +16,8 @@ class RuntimeHierarchyModelTest { @Test fun `hierarchy fixtures decode through typed contracts`() { - val request = codec.decodeRequest(fixture("v2/valid/hierarchy-snapshot-request.json")) - val response = codec.decodeResponse(fixture("v2/valid/hierarchy-snapshot-response.json")) + val request = codec.decodeRequest(fixture("v2/Fixtures/valid/hierarchy-snapshot-request.json")) + val response = codec.decodeResponse(fixture("v2/Fixtures/valid/hierarchy-snapshot-response.json")) codec.decodeRequestParameters(request, RuntimeHierarchySnapshotParameters.contract) val payload = codec.decodeSuccessPayload(response, RuntimeHierarchySnapshotPayload.contract) @@ -30,13 +30,13 @@ class RuntimeHierarchyModelTest { fun `hierarchy values reject missing units and inconsistent visibility`() { assertFailsWith { codec.decodeValue( - fixture("v2/invalid/coordinate-rect-missing-unit.json"), + fixture("v2/Fixtures/invalid/coordinate-rect-missing-unit.json"), RuntimeCoordinateRect.serializer() ) } assertFailsWith { codec.decodeValue( - fixture("v2/invalid/inconsistent-visibility.json"), + fixture("v2/Fixtures/invalid/inconsistent-visibility.json"), RuntimeNodeVisibility.serializer() ) } diff --git a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetailModelTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetailModelTest.kt similarity index 93% rename from AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetailModelTest.kt rename to Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetailModelTest.kt index 76f45fe..824091a 100644 --- a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetailModelTest.kt +++ b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetailModelTest.kt @@ -20,8 +20,8 @@ class RuntimeNodeDetailModelTest { @Test fun `node detail fixtures decode through typed contracts`() { - val request = codec.decodeRequest(fixture("v2/valid/node-detail-request.json")) - val response = codec.decodeResponse(fixture("v2/valid/node-detail-response.json")) + val request = codec.decodeRequest(fixture("v2/Fixtures/valid/node-detail-request.json")) + val response = codec.decodeResponse(fixture("v2/Fixtures/valid/node-detail-response.json")) val parameters = codec.decodeRequestParameters(request, RuntimeNodeDetailParameters.contract) val payload = codec.decodeSuccessPayload(response, RuntimeNodeDetailPayload.contract) @@ -33,7 +33,7 @@ class RuntimeNodeDetailModelTest { @Test fun `node identifiers and attribute identifiers retain wire constraints`() { assertFailsWith { - val request = codec.decodeRequest(fixture("v2/invalid/node-id-is-number.json")) + val request = codec.decodeRequest(fixture("v2/Fixtures/invalid/node-id-is-number.json")) codec.decodeRequestParameters(request, RuntimeNodeDetailParameters.contract) } assertFailsWith { @@ -44,7 +44,7 @@ class RuntimeNodeDetailModelTest { @Test fun `standalone vector attribute values decode with signed components`() { val value = codec.decodeValue( - fixture("v2/valid/vector-attribute-value.json"), + fixture("v2/Fixtures/valid/vector-attribute-value.json"), RuntimeAttributeValue.serializer() ) diff --git a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeCancellationModelTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeCancellationModelTest.kt similarity index 81% rename from AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeCancellationModelTest.kt rename to Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeCancellationModelTest.kt index 08c5bf5..57d6e81 100644 --- a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeCancellationModelTest.kt +++ b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeCancellationModelTest.kt @@ -15,8 +15,8 @@ class RuntimeCancellationModelTest { @Test fun `cancellation fixtures decode through typed contracts`() { - val request = codec.decodeRequest(fixture("v2/valid/cancel-request.json")) - val response = codec.decodeResponse(fixture("v2/valid/cancel-response.json")) + val request = codec.decodeRequest(fixture("v2/Fixtures/valid/cancel-request.json")) + val response = codec.decodeResponse(fixture("v2/Fixtures/valid/cancel-response.json")) val parameters = codec.decodeRequestParameters(request, RuntimeCancelRequestParameters.contract) val payload = codec.decodeSuccessPayload(response, RuntimeCancelRequestPayload.contract) diff --git a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodecTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodecTest.kt similarity index 84% rename from AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodecTest.kt rename to Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodecTest.kt index f2b3f1f..22871be 100644 --- a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodecTest.kt +++ b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodecTest.kt @@ -20,7 +20,7 @@ class RuntimeMessageCodecTest { @Test fun `request envelope preserves method parameters and extensions`() { - val source = fixture("v2/valid/unknown-method-request.json") + val source = fixture("v2/Fixtures/valid/unknown-method-request.json") val request = codec.decodeRequest(source) val encoded = codec.encodeRequest(request) @@ -33,23 +33,23 @@ class RuntimeMessageCodecTest { @Test fun `response envelope enforces status member exclusivity`() { - val success = codec.decodeResponse(fixture("v2/valid/handshake-response.json")) - val failure = codec.decodeResponse(fixture("v2/valid/node-detail-failure-response.json")) + val success = codec.decodeResponse(fixture("v2/Fixtures/valid/handshake-response.json")) + val failure = codec.decodeResponse(fixture("v2/Fixtures/valid/node-detail-failure-response.json")) assertIs(success.outcome) assertIs(failure.outcome) assertFailsWith { - codec.decodeResponse(fixture("v2/invalid/success-response-contains-error.json")) + codec.decodeResponse(fixture("v2/Fixtures/invalid/success-response-contains-error.json")) } assertFailsWith { - codec.decodeResponse(fixture("v2/invalid/failure-response-contains-payload.json")) + codec.decodeResponse(fixture("v2/Fixtures/invalid/failure-response-contains-payload.json")) } } @Test fun `codec rejects unsupported protocol versions and scalar roots`() { assertFailsWith { - codec.decodeRequest(fixture("v2/invalid/request-uses-v1.json")) + codec.decodeRequest(fixture("v2/Fixtures/invalid/request-uses-v1.json")) } assertFailsWith { codec.decodeRequest("[]".encodeToByteArray()) @@ -105,7 +105,7 @@ class RuntimeMessageCodecTest { validFixtureNames() .filterNot { it == "vector-attribute-value.json" } .forEach { name -> - val source = fixture("v2/valid/$name") + val source = fixture("v2/Fixtures/valid/$name") val document = codec.decodeDocument(source) val objectValue = assertIs(document) when { @@ -118,13 +118,13 @@ class RuntimeMessageCodecTest { @Test fun `codec decodes standalone valid attribute values`() { - val document = codec.decodeDocument(fixture("v2/valid/vector-attribute-value.json")) + val document = codec.decodeDocument(fixture("v2/Fixtures/valid/vector-attribute-value.json")) assertIs(document) } private fun validFixtureNames(): List = checkNotNull( - javaClass.classLoader.getResource("v2/valid") + javaClass.classLoader.getResource("v2/Fixtures/valid") ).toURI().let(::File).listFiles() ?.filter { it.extension == "json" } ?.map { it.name } diff --git a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/negotiation/RuntimeSessionModelTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/negotiation/RuntimeSessionModelTest.kt similarity index 86% rename from AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/negotiation/RuntimeSessionModelTest.kt rename to Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/negotiation/RuntimeSessionModelTest.kt index a610be5..f15016a 100644 --- a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/negotiation/RuntimeSessionModelTest.kt +++ b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/negotiation/RuntimeSessionModelTest.kt @@ -18,7 +18,7 @@ class RuntimeSessionModelTest { @Test fun `handshake request decodes typed parameters`() { - val request = codec.decodeRequest(fixture("v2/valid/handshake-request.json")) + val request = codec.decodeRequest(fixture("v2/Fixtures/valid/handshake-request.json")) val parameters = codec.decodeRequestParameters( request, @@ -31,7 +31,7 @@ class RuntimeSessionModelTest { @Test fun `handshake response decodes typed payload`() { - val response = codec.decodeResponse(fixture("v2/valid/handshake-response.json")) + val response = codec.decodeResponse(fixture("v2/Fixtures/valid/handshake-response.json")) val payload = codec.decodeSuccessPayload( response, @@ -45,7 +45,7 @@ class RuntimeSessionModelTest { @Test fun `typed handshake values round trip without exposing JSON assembly`() { - val sourceRequest = codec.decodeRequest(fixture("v2/valid/handshake-request.json")) + val sourceRequest = codec.decodeRequest(fixture("v2/Fixtures/valid/handshake-request.json")) val parameters = codec.decodeRequestParameters(sourceRequest, RuntimeHandshakeParameters.contract) val encodedRequest = codec.encodeRequest( requestID = sourceRequest.requestID, @@ -53,7 +53,7 @@ class RuntimeSessionModelTest { parameters = parameters ) - val sourceResponse = codec.decodeResponse(fixture("v2/valid/handshake-response.json")) + val sourceResponse = codec.decodeResponse(fixture("v2/Fixtures/valid/handshake-response.json")) val payload = codec.decodeSuccessPayload(sourceResponse, RuntimeHandshakePayload.contract) val encodedResponse = codec.encodeSuccessResponse( requestID = sourceResponse.requestID, @@ -82,7 +82,7 @@ class RuntimeSessionModelTest { ) } assertFailsWith { - codec.decodeRequest(fixture("v2/invalid/descending-protocol-range.json")).let { request -> + codec.decodeRequest(fixture("v2/Fixtures/invalid/descending-protocol-range.json")).let { request -> codec.decodeRequestParameters(request, RuntimeHandshakeParameters.contract) } } @@ -90,8 +90,8 @@ class RuntimeSessionModelTest { @Test fun `typed decoding rejects a mismatched method before reading payload`() { - val request = codec.decodeRequest(fixture("v2/valid/application-info-request.json")) - val response = codec.decodeResponse(fixture("v2/valid/application-info-response.json")) + val request = codec.decodeRequest(fixture("v2/Fixtures/valid/application-info-request.json")) + val response = codec.decodeResponse(fixture("v2/Fixtures/valid/application-info-response.json")) assertFailsWith { codec.decodeRequestParameters(request, RuntimeHandshakeParameters.contract) @@ -119,7 +119,7 @@ class RuntimeSessionModelTest { } assertFailsWith { codec.decodeValue( - fixture("v2/invalid/extension-key-not-namespaced.json"), + fixture("v2/Fixtures/invalid/extension-key-not-namespaced.json"), RuntimeExtensionMap.serializer() ) } diff --git a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/patching/RuntimePatchingModelTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/patching/RuntimePatchingModelTest.kt similarity index 85% rename from AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/patching/RuntimePatchingModelTest.kt rename to Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/patching/RuntimePatchingModelTest.kt index de47e2b..f9591ec 100644 --- a/AstrolabeProtocolKotlin/src/test/kotlin/dev/astrolabe/protocol/patching/RuntimePatchingModelTest.kt +++ b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/patching/RuntimePatchingModelTest.kt @@ -45,20 +45,20 @@ class RuntimePatchingModelTest { @Test fun `patch identifiers and patterns reject ambiguous values`() { - val request = codec.decodeRequest(fixture("v2/invalid/attribute-identifier-not-namespaced.json")) + val request = codec.decodeRequest(fixture("v2/Fixtures/invalid/attribute-identifier-not-namespaced.json")) assertFailsWith { codec.decodeRequestParameters(request, RuntimeApplyAttributePatchParameters.contract) } assertFailsWith { codec.decodeValue( - fixture("v2/invalid/patch-pattern-not-namespaced.json"), + fixture("v2/Fixtures/invalid/patch-pattern-not-namespaced.json"), RuntimePatchableAttribute.serializer() ) } assertFailsWith { val unsafeRequest = codec.decodeRequest( - fixture("v2/invalid/unsafe-integer-attribute-value.json") + fixture("v2/Fixtures/invalid/unsafe-integer-attribute-value.json") ) codec.decodeRequestParameters(unsafeRequest, RuntimeApplyAttributePatchParameters.contract) } @@ -76,8 +76,8 @@ class RuntimePatchingModelTest { requestContract: RuntimeMethodContract, payloadContract: RuntimeMethodContract ) { - val request = codec.decodeRequest(fixture("v2/valid/$fixturePrefix-request.json")) - val response = codec.decodeResponse(fixture("v2/valid/$fixturePrefix-response.json")) + val request = codec.decodeRequest(fixture("v2/Fixtures/valid/$fixturePrefix-request.json")) + val response = codec.decodeResponse(fixture("v2/Fixtures/valid/$fixturePrefix-response.json")) codec.decodeRequestParameters(request, requestContract) codec.decodeSuccessPayload(response, payloadContract) diff --git a/Sources/AstrolabeProtocol/Core/RuntimeColor.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeColor.swift similarity index 100% rename from Sources/AstrolabeProtocol/Core/RuntimeColor.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeColor.swift diff --git a/Sources/AstrolabeProtocol/Core/RuntimeGeometry.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeGeometry.swift similarity index 100% rename from Sources/AstrolabeProtocol/Core/RuntimeGeometry.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeGeometry.swift diff --git a/Sources/AstrolabeProtocol/Core/RuntimeIdentifier.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeIdentifier.swift similarity index 100% rename from Sources/AstrolabeProtocol/Core/RuntimeIdentifier.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeIdentifier.swift diff --git a/Sources/AstrolabeProtocol/Core/RuntimeJSONValue.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeJSONValue.swift similarity index 100% rename from Sources/AstrolabeProtocol/Core/RuntimeJSONValue.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeJSONValue.swift diff --git a/Sources/AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift similarity index 100% rename from Sources/AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift diff --git a/Sources/AstrolabeProtocol/Core/RuntimeRequiredNullable.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeRequiredNullable.swift similarity index 100% rename from Sources/AstrolabeProtocol/Core/RuntimeRequiredNullable.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeRequiredNullable.swift diff --git a/Sources/AstrolabeProtocol/Framing/RuntimeFrameCodec.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Framing/RuntimeFrameCodec.swift similarity index 100% rename from Sources/AstrolabeProtocol/Framing/RuntimeFrameCodec.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Framing/RuntimeFrameCodec.swift diff --git a/Sources/AstrolabeProtocol/Framing/RuntimeMessageCodec.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Framing/RuntimeMessageCodec.swift similarity index 100% rename from Sources/AstrolabeProtocol/Framing/RuntimeMessageCodec.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Framing/RuntimeMessageCodec.swift diff --git a/Sources/AstrolabeProtocol/Inspection/RuntimeApplication.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeApplication.swift similarity index 100% rename from Sources/AstrolabeProtocol/Inspection/RuntimeApplication.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeApplication.swift diff --git a/Sources/AstrolabeProtocol/Inspection/RuntimeAttributeValue.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributeValue.swift similarity index 100% rename from Sources/AstrolabeProtocol/Inspection/RuntimeAttributeValue.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributeValue.swift diff --git a/Sources/AstrolabeProtocol/Inspection/RuntimeAttributedTextRun.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributedTextRun.swift similarity index 100% rename from Sources/AstrolabeProtocol/Inspection/RuntimeAttributedTextRun.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributedTextRun.swift diff --git a/Sources/AstrolabeProtocol/Inspection/RuntimeHierarchy.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeHierarchy.swift similarity index 100% rename from Sources/AstrolabeProtocol/Inspection/RuntimeHierarchy.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeHierarchy.swift diff --git a/Sources/AstrolabeProtocol/Inspection/RuntimeLayoutRelation.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeLayoutRelation.swift similarity index 100% rename from Sources/AstrolabeProtocol/Inspection/RuntimeLayoutRelation.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeLayoutRelation.swift diff --git a/Sources/AstrolabeProtocol/Inspection/RuntimeNodeDetail.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeNodeDetail.swift similarity index 100% rename from Sources/AstrolabeProtocol/Inspection/RuntimeNodeDetail.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeNodeDetail.swift diff --git a/Sources/AstrolabeProtocol/Messaging/RuntimeError.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Messaging/RuntimeError.swift similarity index 100% rename from Sources/AstrolabeProtocol/Messaging/RuntimeError.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Messaging/RuntimeError.swift diff --git a/Sources/AstrolabeProtocol/Messaging/RuntimeRequest.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Messaging/RuntimeRequest.swift similarity index 100% rename from Sources/AstrolabeProtocol/Messaging/RuntimeRequest.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Messaging/RuntimeRequest.swift diff --git a/Sources/AstrolabeProtocol/Messaging/RuntimeResponse.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Messaging/RuntimeResponse.swift similarity index 100% rename from Sources/AstrolabeProtocol/Messaging/RuntimeResponse.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Messaging/RuntimeResponse.swift diff --git a/Sources/AstrolabeProtocol/Negotiation/RuntimeCapability.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Negotiation/RuntimeCapability.swift similarity index 100% rename from Sources/AstrolabeProtocol/Negotiation/RuntimeCapability.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Negotiation/RuntimeCapability.swift diff --git a/Sources/AstrolabeProtocol/Negotiation/RuntimeHandshake.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Negotiation/RuntimeHandshake.swift similarity index 100% rename from Sources/AstrolabeProtocol/Negotiation/RuntimeHandshake.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Negotiation/RuntimeHandshake.swift diff --git a/Sources/AstrolabeProtocol/Negotiation/RuntimeMethod.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Negotiation/RuntimeMethod.swift similarity index 100% rename from Sources/AstrolabeProtocol/Negotiation/RuntimeMethod.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Negotiation/RuntimeMethod.swift diff --git a/Sources/AstrolabeProtocol/Negotiation/RuntimeProtocolVersion.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Negotiation/RuntimeProtocolVersion.swift similarity index 100% rename from Sources/AstrolabeProtocol/Negotiation/RuntimeProtocolVersion.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Negotiation/RuntimeProtocolVersion.swift diff --git a/Sources/AstrolabeProtocol/Patching/RuntimeAttributePatch.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Patching/RuntimeAttributePatch.swift similarity index 100% rename from Sources/AstrolabeProtocol/Patching/RuntimeAttributePatch.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Patching/RuntimeAttributePatch.swift diff --git a/Sources/AstrolabeProtocol/Patching/RuntimePatchableAttribute.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Patching/RuntimePatchableAttribute.swift similarity index 100% rename from Sources/AstrolabeProtocol/Patching/RuntimePatchableAttribute.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Patching/RuntimePatchableAttribute.swift diff --git a/Tests/AstrolabeProtocolTests/Contract/ProtocolFixtureTests.swift b/Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolFixtureTests.swift similarity index 99% rename from Tests/AstrolabeProtocolTests/Contract/ProtocolFixtureTests.swift rename to Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolFixtureTests.swift index dbfa76d..e423b4b 100644 --- a/Tests/AstrolabeProtocolTests/Contract/ProtocolFixtureTests.swift +++ b/Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolFixtureTests.swift @@ -137,7 +137,7 @@ final class ProtocolFixtureTests: XCTestCase { private func manifestCases(expectation: String) throws -> [ManifestCase] { let data = try Data(contentsOf: repositoryRootURL() - .appendingPathComponent("Fixtures/v2/manifest.json")) + .appendingPathComponent("Contract/v2/manifest.json")) return try JSONDecoder().decode(Manifest.self, from: data).cases.filter { $0.expectation == expectation } diff --git a/Tests/AstrolabeProtocolTests/Contract/ProtocolV2ContractTests.swift b/Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolV2ContractTests.swift similarity index 93% rename from Tests/AstrolabeProtocolTests/Contract/ProtocolV2ContractTests.swift rename to Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolV2ContractTests.swift index e6876c5..eee88c9 100644 --- a/Tests/AstrolabeProtocolTests/Contract/ProtocolV2ContractTests.swift +++ b/Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolV2ContractTests.swift @@ -12,7 +12,7 @@ final class ProtocolV2ContractTests: XCTestCase { func testV2ContractAssetsCoverEveryMethod() throws { let repositoryURL = try repositoryRootURL() let manifest = try jsonObject( - at: repositoryURL.appendingPathComponent("Fixtures/v2/manifest.json") + at: repositoryURL.appendingPathComponent("Contract/v2/manifest.json") ) let methods = try XCTUnwrap(manifest["methods"] as? [[String: Any]]) let methodNames = Set(try methods.map { method in @@ -23,7 +23,7 @@ final class ProtocolV2ContractTests: XCTestCase { XCTAssertEqual(methodNames, Set(Self.expectedMethods)) XCTAssertTrue( FileManager.default.fileExists( - atPath: repositoryURL.appendingPathComponent("PROTOCOL-2.0.md").path + atPath: repositoryURL.appendingPathComponent("Contract/v2/PROTOCOL.md").path ) ) @@ -38,7 +38,7 @@ final class ProtocolV2ContractTests: XCTestCase { func testV2ConformanceCasesReferenceExistingFixturesAndSchemas() throws { let repositoryURL = try repositoryRootURL() let manifest = try jsonObject( - at: repositoryURL.appendingPathComponent("Fixtures/v2/manifest.json") + at: repositoryURL.appendingPathComponent("Contract/v2/manifest.json") ) let cases = try XCTUnwrap(manifest["cases"] as? [[String: Any]]) let expectations = Set(cases.compactMap { $0["expectation"] as? String }) @@ -70,7 +70,7 @@ final class ProtocolV2ContractTests: XCTestCase { XCTAssertTrue( FileManager.default.fileExists( atPath: repositoryURL - .appendingPathComponent("Schemas/v2") + .appendingPathComponent("Contract/v2/Schemas") .appendingPathComponent(fileName) .path ), diff --git a/Tests/AstrolabeProtocolTests/Framing/RuntimeFrameCodecTests.swift b/Implementations/Swift/Tests/AstrolabeProtocolTests/Framing/RuntimeFrameCodecTests.swift similarity index 100% rename from Tests/AstrolabeProtocolTests/Framing/RuntimeFrameCodecTests.swift rename to Implementations/Swift/Tests/AstrolabeProtocolTests/Framing/RuntimeFrameCodecTests.swift diff --git a/Tests/AstrolabeProtocolTests/Inspection/RuntimeModelTests.swift b/Implementations/Swift/Tests/AstrolabeProtocolTests/Inspection/RuntimeModelTests.swift similarity index 100% rename from Tests/AstrolabeProtocolTests/Inspection/RuntimeModelTests.swift rename to Implementations/Swift/Tests/AstrolabeProtocolTests/Inspection/RuntimeModelTests.swift diff --git a/Tests/AstrolabeProtocolTests/Messaging/RuntimeMessageCodecTests.swift b/Implementations/Swift/Tests/AstrolabeProtocolTests/Messaging/RuntimeMessageCodecTests.swift similarity index 100% rename from Tests/AstrolabeProtocolTests/Messaging/RuntimeMessageCodecTests.swift rename to Implementations/Swift/Tests/AstrolabeProtocolTests/Messaging/RuntimeMessageCodecTests.swift diff --git a/Package.swift b/Package.swift index 25cdbaf..a8293a6 100644 --- a/Package.swift +++ b/Package.swift @@ -15,10 +15,14 @@ let package = Package( ) ], targets: [ - .target(name: "AstrolabeProtocol"), + .target( + name: "AstrolabeProtocol", + path: "Implementations/Swift/Sources/AstrolabeProtocol" + ), .testTarget( name: "AstrolabeProtocolTests", - dependencies: ["AstrolabeProtocol"] + dependencies: ["AstrolabeProtocol"], + path: "Implementations/Swift/Tests/AstrolabeProtocolTests" ) ] ) diff --git a/README.md b/README.md index edf6c5f..ccf0dcf 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,15 @@ Astrolabe Host and platform Runtime SDKs. ## Contents -- Swift DTOs and typed protocol models in the `AstrolabeProtocol` product. +- The normative, versioned wire contract under `Contract/`. +- Equal Swift and Kotlin implementations under `Implementations/`. - Request and response envelopes, errors, version negotiation, and frame codecs. -- Versioned JSON Schemas under `Schemas/`. -- Valid and invalid cross-language examples under `Fixtures/`. +- Versioned JSON Schemas and cross-language Fixtures under each Contract version. - The Wire Protocol 2.0 specification in - [PROTOCOL-2.0.md](PROTOCOL-2.0.md). -- Archived Wire Protocol 1.0 documentation in [PROTOCOL.md](PROTOCOL.md). + [Contract/v2/PROTOCOL.md](Contract/v2/PROTOCOL.md). +- Archived Wire Protocol 1.0 documentation in + [Contract/v1/PROTOCOL.md](Contract/v1/PROTOCOL.md). UIKit, Android View, transport listeners, device discovery, screenshots, CLI commands, and MCP tools are outside this repository. @@ -45,6 +46,18 @@ Swift types are one implementation of the contract. Other implementations use the specification, Schemas, Fixtures, and documented wire behavior as their compatibility source of truth. +## Repository Layout + +```text +Contract/ Normative protocol documents, Schemas, and Fixtures +Implementations/ Equal Swift and Kotlin protocol implementations +Tooling/ Contract validation, release automation, and tests +``` + +Root SwiftPM, Gradle, and npm manifests are ecosystem entrypoints. They map to +the implementation and Tooling directories without making one language the +repository's primary implementation. + ## Development Install the contract validator and run all checks: diff --git a/README.zh-CN.md b/README.zh-CN.md index e3f9c23..8470297 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -6,12 +6,12 @@ Astrolabe Protocol 定义 Astrolabe Host 与各平台 Runtime SDK 共享的平 ## 内容 -- `AstrolabeProtocol` Product 中的 Swift DTO 和强类型协议模型。 +- `Contract/` 下版本化的规范 Wire Contract。 +- `Implementations/` 下地位对等的 Swift 与 Kotlin 实现。 - 请求与响应封装、错误、版本协商和帧编解码器。 -- `Schemas/` 下带版本的 JSON Schema。 -- `Fixtures/` 下用于跨语言验证的有效和无效示例。 -- [PROTOCOL-2.0.md](PROTOCOL-2.0.md) 中的 Wire Protocol 2.0 规范。 -- [PROTOCOL.md](PROTOCOL.md) 中归档的 Wire Protocol 1.0 文档。 +- 每个 Contract 版本下的 JSON Schema 与跨语言 Fixture。 +- [Contract/v2/PROTOCOL.md](Contract/v2/PROTOCOL.md) 中的 Wire Protocol 2.0 规范。 +- [Contract/v1/PROTOCOL.md](Contract/v1/PROTOCOL.md) 中归档的 Wire Protocol 1.0 文档。 UIKit、Android View、Transport Listener、设备发现、截图、CLI 命令和 MCP Tools 均不属于本仓库。 @@ -40,6 +40,17 @@ Wire Protocol 版本为 `2.0`。 Swift 类型只是该协议的一种实现。其他语言的实现应以规范、Schema、Fixture 和文档约定的 Wire 行为作为兼容性事实源。 +## 仓库目录 + +```text +Contract/ 规范协议文档、Schema 和 Fixture +Implementations/ 地位对等的 Swift 与 Kotlin 协议实现 +Tooling/ Contract 校验、发布自动化和测试 +``` + +根目录的 SwiftPM、Gradle 和 npm 清单只是各生态的标准入口,通过显式路径映射到实现与 Tooling, +不表示任一语言是仓库的主实现。 + ## 开发 安装协议校验器并运行全部检查: diff --git a/scripts/validate-contract.mjs b/Tooling/ContractValidation/validate-contract.mjs similarity index 95% rename from scripts/validate-contract.mjs rename to Tooling/ContractValidation/validate-contract.mjs index 54feb27..89832f9 100644 --- a/scripts/validate-contract.mjs +++ b/Tooling/ContractValidation/validate-contract.mjs @@ -5,10 +5,10 @@ import { fileURLToPath } from "node:url"; import Ajv2020 from "ajv/dist/2020.js"; import addFormats from "ajv-formats"; -const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); -const schemaDirectory = join(repositoryRoot, "Schemas/v2"); -const fixtureDirectory = join(repositoryRoot, "Fixtures/v2"); -const manifest = readJSON(join(fixtureDirectory, "manifest.json")); +const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); +const schemaDirectory = join(repositoryRoot, "Contract/v2/Schemas"); +const fixtureDirectory = join(repositoryRoot, "Contract/v2/Fixtures"); +const manifest = readJSON(join(repositoryRoot, "Contract/v2/manifest.json")); const ajv = new Ajv2020({ allErrors: true, strict: true, allowUnionTypes: true }); addFormats(ajv); diff --git a/scripts/release-prepare.mjs b/Tooling/Release/release-prepare.mjs similarity index 98% rename from scripts/release-prepare.mjs rename to Tooling/Release/release-prepare.mjs index 3b0a300..7b5a04d 100644 --- a/scripts/release-prepare.mjs +++ b/Tooling/Release/release-prepare.mjs @@ -12,7 +12,7 @@ import { } from "./versioning.mjs"; const scriptPath = fileURLToPath(import.meta.url); -const defaultProjectRoot = resolve(dirname(scriptPath), ".."); +const defaultProjectRoot = resolve(dirname(scriptPath), "../.."); export function parseReleaseArgs(args) { if (args.length !== 1) { diff --git a/scripts/version-sync.mjs b/Tooling/Release/version-sync.mjs similarity index 95% rename from scripts/version-sync.mjs rename to Tooling/Release/version-sync.mjs index ed98b6b..65d954f 100644 --- a/scripts/version-sync.mjs +++ b/Tooling/Release/version-sync.mjs @@ -10,7 +10,7 @@ import { } from "./versioning.mjs"; const scriptPath = fileURLToPath(import.meta.url); -const projectRoot = resolve(dirname(scriptPath), ".."); +const projectRoot = resolve(dirname(scriptPath), "../.."); export function parseVersionSyncArgs(args) { if (args.length === 1 && args[0] === "--check") return { mode: "check" }; diff --git a/scripts/versioning.mjs b/Tooling/Release/versioning.mjs similarity index 98% rename from scripts/versioning.mjs rename to Tooling/Release/versioning.mjs index 2cf8031..94edc8a 100644 --- a/scripts/versioning.mjs +++ b/Tooling/Release/versioning.mjs @@ -1,7 +1,7 @@ import { readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; -const metadataPath = "Sources/AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift"; +const metadataPath = "Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift"; const gradlePropertiesPath = "gradle.properties"; export const versionedPaths = Object.freeze([ diff --git a/test/ci-workflow.test.mjs b/Tooling/Tests/ci-workflow.test.mjs similarity index 100% rename from test/ci-workflow.test.mjs rename to Tooling/Tests/ci-workflow.test.mjs diff --git a/test/release-prepare.test.mjs b/Tooling/Tests/release-prepare.test.mjs similarity index 92% rename from test/release-prepare.test.mjs rename to Tooling/Tests/release-prepare.test.mjs index 759a356..0ab81db 100644 --- a/test/release-prepare.test.mjs +++ b/Tooling/Tests/release-prepare.test.mjs @@ -4,8 +4,8 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import test from "node:test"; -import { parseReleaseArgs, prepareRelease } from "../scripts/release-prepare.mjs"; -import { versionedPaths } from "../scripts/versioning.mjs"; +import { parseReleaseArgs, prepareRelease } from "../Release/release-prepare.mjs"; +import { versionedPaths } from "../Release/versioning.mjs"; test("release preparation accepts one prerelease SemVer", () => { assert.deepEqual(parseReleaseArgs(["2.0.0-rc.2"]), { version: "2.0.0-rc.2" }); @@ -37,7 +37,10 @@ test("release preparation synchronizes, verifies, commits, and tags a prerelease '.package(url: "example", exact: "2.0.0-rc.1")\n' ); writeFileSync(join(projectRoot, "gradle.properties"), "astrolabeVersion=2.0.0-rc.1\n"); - const sourceDirectory = join(projectRoot, "Sources/AstrolabeProtocol/Core"); + const sourceDirectory = join( + projectRoot, + "Implementations/Swift/Sources/AstrolabeProtocol/Core" + ); mkdirSync(sourceDirectory, { recursive: true }); writeFileSync( join(sourceDirectory, "RuntimeProtocolMetadata.swift"), diff --git a/test/versioning.test.mjs b/Tooling/Tests/versioning.test.mjs similarity index 96% rename from test/versioning.test.mjs rename to Tooling/Tests/versioning.test.mjs index 0d45559..665377f 100644 --- a/test/versioning.test.mjs +++ b/Tooling/Tests/versioning.test.mjs @@ -4,7 +4,7 @@ import test from "node:test"; import { assertReleaseVersion, compareReleaseVersions -} from "../scripts/versioning.mjs"; +} from "../Release/versioning.mjs"; test("accepts stable and prerelease SemVer values", () => { assert.equal(assertReleaseVersion("2.0.0"), "2.0.0"); diff --git a/docs/superpowers/plans/2026-08-04-contract-first-directory-migration.md b/docs/superpowers/plans/2026-08-04-contract-first-directory-migration.md new file mode 100644 index 0000000..9b52a76 --- /dev/null +++ b/docs/superpowers/plans/2026-08-04-contract-first-directory-migration.md @@ -0,0 +1,283 @@ +# Contract-first Directory Migration Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking. + +**Goal:** Move `astrolabe-protocol` to the approved Contract-first top-level layout without changing public APIs, wire semantics, artifact coordinates, or release behavior. + +**Architecture:** `Contract/` becomes the normative location for protocol documents, schemas, fixtures, and the V2 manifest. Swift and Kotlin move under symmetric `Implementations/` directories, while validation, release, and test scripts move under `Tooling/`; root build manifests explicitly map these physical paths. + +**Tech Stack:** Swift 5.9 / SwiftPM, Kotlin/JVM / Gradle, Node.js 22, JSON Schema Draft 2020-12, GitHub Actions. + +## Global Constraints + +- Do not add compatibility directories, symlinks, duplicated schemas, or forwarding files. +- Keep Swift module/product `AstrolabeProtocol` unchanged. +- Keep Kotlin package `dev.astrolabe.protocol`, Gradle project `:AstrolabeProtocolKotlin`, Maven coordinates, and protocol version unchanged. +- Keep every Schema `$id`, message type, JSON field, and Fixture validity expectation unchanged. +- This plan performs the mechanical top-level migration only; it does not split Schema domains, reorganize implementation source domains, or add UI Graph models. +- Never commit automatically; leave all changes uncommitted for user review. +- Run `release-prepare.mjs` tests, but do not invoke the release command because it commits and tags by design. + +--- + +### Task 1: Capture the pre-migration regression baseline + +**Files:** +- Verify only; no files changed. + +**Interfaces:** +- Consumes: current root paths and build manifests. +- Produces: passing baseline for comparison after the move. + +- [x] **Step 1: Run the aggregate contract and Tooling tests** + +Run: `npm test` + +Expected: version check, schema/fixture validation, Swift contract tests, and eight Node tests pass. + +- [x] **Step 2: Run the full Swift suite and release build** + +Run: `swift test --parallel && swift build -c release` + +Expected: 20 Swift tests pass and the release product builds. + +- [x] **Step 3: Run Kotlin tests and local publication build** + +Run: `./gradlew :AstrolabeProtocolKotlin:test :AstrolabeProtocolKotlin:publishToMavenLocal` + +Expected: both Gradle tasks complete successfully. + +### Task 2: Move normative Contract assets + +**Files:** +- Move: `PROTOCOL.md` → `Contract/v1/PROTOCOL.md` +- Move: `PROTOCOL-2.0.md` → `Contract/v2/PROTOCOL.md` +- Move: `Schemas/v1/` → `Contract/v1/Schemas/` +- Move: `Schemas/v2/` → `Contract/v2/Schemas/` +- Move: `Fixtures/v1/` → `Contract/v1/Fixtures/` +- Move: `Fixtures/v2/manifest.json` → `Contract/v2/manifest.json` +- Move: `Fixtures/v2/valid/` → `Contract/v2/Fixtures/valid/` +- Move: `Fixtures/v2/invalid/` → `Contract/v2/Fixtures/invalid/` +- Modify: `Contract/v2/manifest.json` +- Modify: `Contract/v2/Schemas/conformance-manifest.schema.json` +- Modify: `Contract/v2/PROTOCOL.md` + +**Interfaces:** +- Consumes: existing Schema IDs and fixture case registration. +- Produces: `Contract/v1` and `Contract/v2` as the only normative asset roots. + +- [x] **Step 1: Move assets with history-preserving Git moves** + +```bash +mkdir -p Contract/v1 Contract/v2/Fixtures +git mv PROTOCOL.md Contract/v1/PROTOCOL.md +git mv PROTOCOL-2.0.md Contract/v2/PROTOCOL.md +git mv Schemas/v1 Contract/v1/Schemas +git mv Schemas/v2 Contract/v2/Schemas +git mv Fixtures/v1 Contract/v1/Fixtures +git mv Fixtures/v2/manifest.json Contract/v2/manifest.json +git mv Fixtures/v2/valid Contract/v2/Fixtures/valid +git mv Fixtures/v2/invalid Contract/v2/Fixtures/invalid +``` + +- [x] **Step 2: Rewrite only repository-relative fixture paths in the manifest** + +Replace every `Fixtures/v2/valid/` and `Fixtures/v2/invalid/` prefix with `Contract/v2/Fixtures/valid/` and `Contract/v2/Fixtures/invalid/`. Leave Schema URLs and fixture JSON content unchanged. + +Update the conformance manifest Schema path pattern and V2 protocol document's normative asset paths to the same Contract-first locations. + +- [x] **Step 3: Verify contract inventory** + +Run: `find Contract -type f | sort` + +Expected: both protocol versions exist under `Contract/`, with V2 manifest, schemas, valid fixtures, and invalid fixtures present exactly once. + +### Task 3: Move symmetric language implementations + +**Files:** +- Move: `Sources/` → `Implementations/Swift/Sources/` +- Move: `Tests/` → `Implementations/Swift/Tests/` +- Move: `AstrolabeProtocolKotlin/` → `Implementations/Kotlin/` +- Modify: `Package.swift` +- Modify: `settings.gradle.kts` +- Modify: `Implementations/Kotlin/build.gradle.kts` +- Modify: `Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolFixtureTests.swift` +- Modify: `Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolV2ContractTests.swift` +- Modify: `Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/contract/ProtocolFixtureConformanceTest.kt` +- Modify: all Kotlin tests that load `v2/valid` or `v2/invalid` classpath resources + +**Interfaces:** +- Consumes: Contract paths produced by Task 2. +- Produces: unchanged Swift and Kotlin public APIs from symmetric physical roots. + +- [x] **Step 1: Move both implementations** + +```bash +mkdir -p Implementations/Swift Implementations +git mv Sources Implementations/Swift/Sources +git mv Tests Implementations/Swift/Tests +git mv AstrolabeProtocolKotlin Implementations/Kotlin +``` + +- [x] **Step 2: Add explicit SwiftPM target paths** + +```swift +.target( + name: "AstrolabeProtocol", + path: "Implementations/Swift/Sources/AstrolabeProtocol" +), +.testTarget( + name: "AstrolabeProtocolTests", + dependencies: ["AstrolabeProtocol"], + path: "Implementations/Swift/Tests/AstrolabeProtocolTests" +) +``` + +- [x] **Step 3: Map the existing Gradle project to its new directory** + +```kotlin +include(":AstrolabeProtocolKotlin") +project(":AstrolabeProtocolKotlin").projectDir = file("Implementations/Kotlin") +``` + +- [x] **Step 4: Point Kotlin test resources at Contract** + +```kotlin +sourceSets { + test { + resources.srcDir(rootProject.layout.projectDirectory.dir("Contract")) + } +} +``` + +Update every Kotlin fixture lookup from `v2/valid` and `v2/invalid` to `v2/Fixtures/valid` and `v2/Fixtures/invalid`. + +- [x] **Step 5: Update Swift Contract test paths** + +Use `Contract/v2/manifest.json`, `Contract/v2/PROTOCOL.md`, and `Contract/v2/Schemas` while keeping repository-root discovery based on `Package.swift`. + +- [x] **Step 6: Run language-specific regressions** + +Run: `swift test --parallel && ./gradlew :AstrolabeProtocolKotlin:test` + +Expected: the same 20 Swift tests and all Kotlin tests pass from the new roots. + +### Task 4: Move Tooling and update root entrypoints + +**Files:** +- Move: `scripts/validate-contract.mjs` → `Tooling/ContractValidation/validate-contract.mjs` +- Move: `scripts/versioning.mjs` → `Tooling/Release/versioning.mjs` +- Move: `scripts/version-sync.mjs` → `Tooling/Release/version-sync.mjs` +- Move: `scripts/release-prepare.mjs` → `Tooling/Release/release-prepare.mjs` +- Move: `test/*.test.mjs` → `Tooling/Tests/` +- Modify: `package.json` +- Modify: `Tooling/ContractValidation/validate-contract.mjs` +- Modify: `Tooling/Release/versioning.mjs` +- Modify: `Tooling/Release/version-sync.mjs` +- Modify: `Tooling/Release/release-prepare.mjs` +- Modify: `Tooling/Tests/release-prepare.test.mjs` +- Modify: `Tooling/Tests/versioning.test.mjs` + +**Interfaces:** +- Consumes: Contract and implementation paths from Tasks 2 and 3. +- Produces: root npm commands with unchanged names and release behavior. + +- [x] **Step 1: Move Tooling by responsibility** + +```bash +mkdir -p Tooling/ContractValidation Tooling/Release Tooling/Tests +git mv scripts/validate-contract.mjs Tooling/ContractValidation/validate-contract.mjs +git mv scripts/versioning.mjs Tooling/Release/versioning.mjs +git mv scripts/version-sync.mjs Tooling/Release/version-sync.mjs +git mv scripts/release-prepare.mjs Tooling/Release/release-prepare.mjs +git mv test/*.test.mjs Tooling/Tests/ +``` + +- [x] **Step 2: Update Tooling repository-root and asset paths** + +Scripts under `Tooling/*` resolve the repository root with `../..`. Use: + +```javascript +const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); +const schemaDirectory = join(repositoryRoot, "Contract/v2/Schemas"); +const fixtureDirectory = join(repositoryRoot, "Contract/v2/Fixtures"); +const manifest = readJSON(join(repositoryRoot, "Contract/v2/manifest.json")); +``` + +Change the Swift metadata path in `versioning.mjs` to `Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift`. + +- [x] **Step 3: Update Tooling test imports and temporary Swift metadata path** + +```javascript +import { parseReleaseArgs, prepareRelease } from "../Release/release-prepare.mjs"; +import { versionedPaths } from "../Release/versioning.mjs"; +``` + +Create temporary metadata under `Implementations/Swift/Sources/AstrolabeProtocol/Core` in the release test. + +- [x] **Step 4: Update npm scripts without changing command names** + +```json +"test:schema": "node Tooling/ContractValidation/validate-contract.mjs", +"version:check": "node Tooling/Release/version-sync.mjs --check", +"version:set": "node Tooling/Release/version-sync.mjs --set", +"release:prepare": "node Tooling/Release/release-prepare.mjs", +"test": "npm run version:check && npm run test:schema && npm run test:swift-contract && node --test Tooling/Tests/*.test.mjs" +``` + +- [x] **Step 5: Run aggregate Tooling regression** + +Run: `npm test` + +Expected: schema/fixture validation, Swift Contract tests, version tests, release tests, and CI workflow tests pass. + +### Task 5: Update documentation and run final gates + +**Files:** +- Modify: `README.md` +- Modify: `README.zh-CN.md` +- Verify: `.github/workflows/ci.yml` +- Verify: all repository files and Git diff. + +**Interfaces:** +- Consumes: completed Contract-first filesystem. +- Produces: reviewable, documented, fully regressed migration. + +- [x] **Step 1: Document the Contract-first layout** + +Update both READMEs to link to `Contract/v2/PROTOCOL.md` and `Contract/v1/PROTOCOL.md`, and describe: + +```text +Contract/ normative protocol documents, schemas, and fixtures +Implementations/ equal Swift and Kotlin implementations +Tooling/ contract validation, release automation, and tests +``` + +- [x] **Step 2: Confirm CI needs no task-name changes** + +The workflow continues to call `npm test`, SwiftPM root commands, and `:AstrolabeProtocolKotlin:*`; explicit root mappings absorb the physical move. + +- [x] **Step 3: Run all final gates** + +```bash +npm test +swift test --parallel +swift build -c release +./gradlew :AstrolabeProtocolKotlin:test +./gradlew :AstrolabeProtocolKotlin:publishToMavenLocal +git diff --check +``` + +Expected: all commands exit zero. + +- [x] **Step 4: Audit invariants and scope** + +Run path searches for `Fixtures/`, `Schemas/`, `PROTOCOL-2.0.md`, `Sources/AstrolabeProtocol`, `Tests/AstrolabeProtocolTests`, `AstrolabeProtocolKotlin/`, `scripts/`, and `test/`. + +Expected: references either point to the new Contract-first paths or intentionally retain the Gradle project name; no old physical directory remains. + +- [x] **Step 5: Leave the change uncommitted** + +Run: `git status --short && git diff --stat` + +Expected: only the planned directory migration, path updates, README changes, and this plan are present; no commit or tag is created. diff --git a/package.json b/package.json index 3594f09..7a08b03 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,12 @@ "private": true, "type": "module", "scripts": { - "test:schema": "node scripts/validate-contract.mjs", + "test:schema": "node Tooling/ContractValidation/validate-contract.mjs", "test:swift-contract": "swift test --filter ProtocolFixtureTests", - "version:check": "node scripts/version-sync.mjs --check", - "version:set": "node scripts/version-sync.mjs --set", - "release:prepare": "node scripts/release-prepare.mjs", - "test": "npm run version:check && npm run test:schema && npm run test:swift-contract && node --test test/*.test.mjs" + "version:check": "node Tooling/Release/version-sync.mjs --check", + "version:set": "node Tooling/Release/version-sync.mjs --set", + "release:prepare": "node Tooling/Release/release-prepare.mjs", + "test": "npm run version:check && npm run test:schema && npm run test:swift-contract && node --test Tooling/Tests/*.test.mjs" }, "devDependencies": { "ajv": "8.20.0", diff --git a/settings.gradle.kts b/settings.gradle.kts index d6f5cd0..55fbfdd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,3 +14,4 @@ dependencyResolutionManagement { rootProject.name = "astrolabe-protocol" include(":AstrolabeProtocolKotlin") +project(":AstrolabeProtocolKotlin").projectDir = file("Implementations/Kotlin") From 3403e2f2d7d83609f40e5156b4d46f2fe1e0a551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BD=A9=E8=BE=95=E5=8D=81=E5=9B=9B?= Date: Tue, 4 Aug 2026 17:49:13 +0800 Subject: [PATCH 2/6] refactor: align implementation domain layout --- .../RuntimeAttributeValue.kt | 0 .../RuntimeAttributedTextRun.kt | 0 .../RuntimeLayoutRelation.kt | 0 .../attributes/RuntimeAttributeValueTest.kt | 112 ++++++++++ .../inspection/RuntimeNodeDetailModelTest.kt | 92 -------- .../RuntimeAttributeValue.swift | 0 .../RuntimeAttributedTextRun.swift | 0 .../RuntimeLayoutRelation.swift | 0 .../RuntimeApplication.swift | 0 .../{ => Hierarchy}/RuntimeHierarchy.swift | 0 .../{ => NodeDetail}/RuntimeNodeDetail.swift | 0 .../RuntimeMessageCodec.swift | 0 .../RuntimeAttributeValueTests.swift} | 29 +-- .../Core/RuntimeIdentifierTests.swift | 21 ++ .../RuntimeProtocolRangeTests.swift | 26 +++ README.md | 7 + README.zh-CN.md | 5 + ...rotocol-implementation-domain-alignment.md | 201 ++++++++++++++++++ 18 files changed, 374 insertions(+), 119 deletions(-) rename Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/{inspection => attributes}/RuntimeAttributeValue.kt (100%) rename Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/{inspection => attributes}/RuntimeAttributedTextRun.kt (100%) rename Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/{inspection => attributes}/RuntimeLayoutRelation.kt (100%) create mode 100644 Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributeValueTest.kt rename Implementations/Swift/Sources/AstrolabeProtocol/{Inspection => Attributes}/RuntimeAttributeValue.swift (100%) rename Implementations/Swift/Sources/AstrolabeProtocol/{Inspection => Attributes}/RuntimeAttributedTextRun.swift (100%) rename Implementations/Swift/Sources/AstrolabeProtocol/{Inspection => Attributes}/RuntimeLayoutRelation.swift (100%) rename Implementations/Swift/Sources/AstrolabeProtocol/Inspection/{ => Application}/RuntimeApplication.swift (100%) rename Implementations/Swift/Sources/AstrolabeProtocol/Inspection/{ => Hierarchy}/RuntimeHierarchy.swift (100%) rename Implementations/Swift/Sources/AstrolabeProtocol/Inspection/{ => NodeDetail}/RuntimeNodeDetail.swift (100%) rename Implementations/Swift/Sources/AstrolabeProtocol/{Framing => Messaging}/RuntimeMessageCodec.swift (100%) rename Implementations/Swift/Tests/AstrolabeProtocolTests/{Inspection/RuntimeModelTests.swift => Attributes/RuntimeAttributeValueTests.swift} (60%) create mode 100644 Implementations/Swift/Tests/AstrolabeProtocolTests/Core/RuntimeIdentifierTests.swift create mode 100644 Implementations/Swift/Tests/AstrolabeProtocolTests/Negotiation/RuntimeProtocolRangeTests.swift create mode 100644 docs/superpowers/plans/2026-08-04-protocol-implementation-domain-alignment.md diff --git a/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributeValue.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributeValue.kt similarity index 100% rename from Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributeValue.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributeValue.kt diff --git a/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributedTextRun.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributedTextRun.kt similarity index 100% rename from Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributedTextRun.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributedTextRun.kt diff --git a/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeLayoutRelation.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeLayoutRelation.kt similarity index 100% rename from Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeLayoutRelation.kt rename to Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeLayoutRelation.kt diff --git a/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributeValueTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributeValueTest.kt new file mode 100644 index 0000000..bd85d14 --- /dev/null +++ b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributeValueTest.kt @@ -0,0 +1,112 @@ +// +// RuntimeAttributeValueTest.kt +// astrolabe-protocol +// +// Created by 轩辕十四 on 2026/7/20. +// + +package dev.astrolabe.protocol + +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertIs + +class RuntimeAttributeValueTest { + private val codec = RuntimeMessageCodec() + + @Test + fun `standalone vector attribute values decode with signed components`() { + val value = codec.decodeValue( + fixture("v2/Fixtures/valid/vector-attribute-value.json"), + RuntimeAttributeValue.serializer() + ) + + assertEquals(RuntimeAttributeValue.Vector(RuntimeVector(-2.0, 4.0, RuntimeMeasurementUnit.logical)), value) + } + + @Test + fun `attribute value discriminator must be a string`() { + val error = assertFailsWith { + codec.decodeValue( + """{"type":1,"value":"opaque"}""".encodeToByteArray(), + RuntimeAttributeValue.serializer() + ) + } + + assertIs(error.cause) + } + + @Test + fun `integer attribute value must contain a JSON number`() { + assertEquals( + RuntimeAttributeValue.Integer(42), + codec.decodeValue( + """{"type":"integer","value":42.0}""".encodeToByteArray(), + RuntimeAttributeValue.serializer() + ) + ) + assertFailsWith { + codec.decodeValue( + """{"type":"integer","value":"42"}""".encodeToByteArray(), + RuntimeAttributeValue.serializer() + ) + } + assertFailsWith { + codec.decodeValue( + """{"type":"integer","value":42.000000000000001}""".encodeToByteArray(), + RuntimeAttributeValue.serializer() + ) + } + } + + @Test + fun `common and extension attribute values round trip symmetrically`() { + val values = listOf( + RuntimeAttributeValue.Null, + RuntimeAttributeValue.BooleanValue(true), + RuntimeAttributeValue.Integer(42), + RuntimeAttributeValue.Number(1.5), + RuntimeAttributeValue.StringValue("value"), + RuntimeAttributeValue.StringList(listOf("a", "b")), + RuntimeAttributeValue.Measurement(RuntimeMeasurement(12.0, RuntimeMeasurementUnit.logical)), + RuntimeAttributeValue.Point( + RuntimeCoordinatePoint(1.0, 2.0, RuntimeCoordinateSpace.screen, RuntimeMeasurementUnit.logical) + ), + RuntimeAttributeValue.Size(RuntimeMeasuredSize(3.0, 4.0, RuntimeMeasurementUnit.pixel)), + RuntimeAttributeValue.Vector(RuntimeVector(-2.0, 4.0, RuntimeMeasurementUnit.logical)), + RuntimeAttributeValue.Rect( + RuntimeCoordinateRect( + 0.0, + 0.0, + 10.0, + 20.0, + RuntimeCoordinateSpace.local, + RuntimeMeasurementUnit.logical + ) + ), + RuntimeAttributeValue.Insets( + RuntimeInsets(1.0, 2.0, 3.0, 4.0, RuntimeMeasurementUnit.logical) + ), + RuntimeAttributeValue.Color(RuntimeColor("srgb", 0.1, 0.2, 0.3, 1.0)), + RuntimeAttributeValue.ArrayValue(JsonArray(listOf(JsonPrimitive("item")))), + RuntimeAttributeValue.ObjectValue(JsonObject(mapOf("key" to JsonPrimitive(true)))), + RuntimeAttributeValue.Extension( + RuntimeNamespacedIdentifier("vendor.custom.value"), + JsonPrimitive("opaque") + ) + ) + + values.forEach { value -> + val encoded = codec.encodeValue(value, RuntimeAttributeValue.serializer()) + assertEquals(value, codec.decodeValue(encoded, RuntimeAttributeValue.serializer())) + } + } + + private fun fixture(path: String): ByteArray = checkNotNull( + javaClass.classLoader.getResource(path) + ).readBytes() +} diff --git a/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetailModelTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetailModelTest.kt index 824091a..8ecac6c 100644 --- a/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetailModelTest.kt +++ b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetailModelTest.kt @@ -7,13 +7,9 @@ package dev.astrolabe.protocol -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonPrimitive import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith -import kotlin.test.assertIs class RuntimeNodeDetailModelTest { private val codec = RuntimeMessageCodec() @@ -41,94 +37,6 @@ class RuntimeNodeDetailModelTest { } } - @Test - fun `standalone vector attribute values decode with signed components`() { - val value = codec.decodeValue( - fixture("v2/Fixtures/valid/vector-attribute-value.json"), - RuntimeAttributeValue.serializer() - ) - - assertEquals(RuntimeAttributeValue.Vector(RuntimeVector(-2.0, 4.0, RuntimeMeasurementUnit.logical)), value) - } - - @Test - fun `attribute value discriminator must be a string`() { - val error = assertFailsWith { - codec.decodeValue( - """{"type":1,"value":"opaque"}""".encodeToByteArray(), - RuntimeAttributeValue.serializer() - ) - } - - assertIs(error.cause) - } - - @Test - fun `integer attribute value must contain a JSON number`() { - assertEquals( - RuntimeAttributeValue.Integer(42), - codec.decodeValue( - """{"type":"integer","value":42.0}""".encodeToByteArray(), - RuntimeAttributeValue.serializer() - ) - ) - assertFailsWith { - codec.decodeValue( - """{"type":"integer","value":"42"}""".encodeToByteArray(), - RuntimeAttributeValue.serializer() - ) - } - assertFailsWith { - codec.decodeValue( - """{"type":"integer","value":42.000000000000001}""".encodeToByteArray(), - RuntimeAttributeValue.serializer() - ) - } - } - - @Test - fun `common and extension attribute values round trip symmetrically`() { - val values = listOf( - RuntimeAttributeValue.Null, - RuntimeAttributeValue.BooleanValue(true), - RuntimeAttributeValue.Integer(42), - RuntimeAttributeValue.Number(1.5), - RuntimeAttributeValue.StringValue("value"), - RuntimeAttributeValue.StringList(listOf("a", "b")), - RuntimeAttributeValue.Measurement(RuntimeMeasurement(12.0, RuntimeMeasurementUnit.logical)), - RuntimeAttributeValue.Point( - RuntimeCoordinatePoint(1.0, 2.0, RuntimeCoordinateSpace.screen, RuntimeMeasurementUnit.logical) - ), - RuntimeAttributeValue.Size(RuntimeMeasuredSize(3.0, 4.0, RuntimeMeasurementUnit.pixel)), - RuntimeAttributeValue.Vector(RuntimeVector(-2.0, 4.0, RuntimeMeasurementUnit.logical)), - RuntimeAttributeValue.Rect( - RuntimeCoordinateRect( - 0.0, - 0.0, - 10.0, - 20.0, - RuntimeCoordinateSpace.local, - RuntimeMeasurementUnit.logical - ) - ), - RuntimeAttributeValue.Insets( - RuntimeInsets(1.0, 2.0, 3.0, 4.0, RuntimeMeasurementUnit.logical) - ), - RuntimeAttributeValue.Color(RuntimeColor("srgb", 0.1, 0.2, 0.3, 1.0)), - RuntimeAttributeValue.ArrayValue(JsonArray(listOf(JsonPrimitive("item")))), - RuntimeAttributeValue.ObjectValue(JsonObject(mapOf("key" to JsonPrimitive(true)))), - RuntimeAttributeValue.Extension( - RuntimeNamespacedIdentifier("vendor.custom.value"), - JsonPrimitive("opaque") - ) - ) - - values.forEach { value -> - val encoded = codec.encodeValue(value, RuntimeAttributeValue.serializer()) - assertEquals(value, codec.decodeValue(encoded, RuntimeAttributeValue.serializer())) - } - } - private fun fixture(path: String): ByteArray = checkNotNull( javaClass.classLoader.getResource(path) ).readBytes() diff --git a/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributeValue.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeAttributeValue.swift similarity index 100% rename from Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributeValue.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeAttributeValue.swift diff --git a/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributedTextRun.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeAttributedTextRun.swift similarity index 100% rename from Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributedTextRun.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeAttributedTextRun.swift diff --git a/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeLayoutRelation.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeLayoutRelation.swift similarity index 100% rename from Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeLayoutRelation.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeLayoutRelation.swift diff --git a/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeApplication.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Application/RuntimeApplication.swift similarity index 100% rename from Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeApplication.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Application/RuntimeApplication.swift diff --git a/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeHierarchy.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeHierarchy.swift similarity index 100% rename from Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeHierarchy.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeHierarchy.swift diff --git a/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeNodeDetail.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/NodeDetail/RuntimeNodeDetail.swift similarity index 100% rename from Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeNodeDetail.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Inspection/NodeDetail/RuntimeNodeDetail.swift diff --git a/Implementations/Swift/Sources/AstrolabeProtocol/Framing/RuntimeMessageCodec.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Messaging/RuntimeMessageCodec.swift similarity index 100% rename from Implementations/Swift/Sources/AstrolabeProtocol/Framing/RuntimeMessageCodec.swift rename to Implementations/Swift/Sources/AstrolabeProtocol/Messaging/RuntimeMessageCodec.swift diff --git a/Implementations/Swift/Tests/AstrolabeProtocolTests/Inspection/RuntimeModelTests.swift b/Implementations/Swift/Tests/AstrolabeProtocolTests/Attributes/RuntimeAttributeValueTests.swift similarity index 60% rename from Implementations/Swift/Tests/AstrolabeProtocolTests/Inspection/RuntimeModelTests.swift rename to Implementations/Swift/Tests/AstrolabeProtocolTests/Attributes/RuntimeAttributeValueTests.swift index 9abc056..8de5211 100644 --- a/Implementations/Swift/Tests/AstrolabeProtocolTests/Inspection/RuntimeModelTests.swift +++ b/Implementations/Swift/Tests/AstrolabeProtocolTests/Attributes/RuntimeAttributeValueTests.swift @@ -1,15 +1,14 @@ // -// RuntimeModelTests.swift +// RuntimeAttributeValueTests.swift // astrolabe-protocol // // Created by 轩辕十四 on 2026/7/16. // import AstrolabeProtocol -import Foundation import XCTest -final class RuntimeModelTests: XCTestCase { +final class RuntimeAttributeValueTests: XCTestCase { func testTypedAttributeValuesRoundTrip() throws { let values: [RuntimeAttributeValue] = [ .null, @@ -41,28 +40,4 @@ final class RuntimeModelTests: XCTestCase { ) } } - - func testProtocolRangeRejectsDescendingAndCrossMajorRanges() { - XCTAssertThrowsError( - try RuntimeProtocolRange( - minimum: RuntimeProtocolVersion(major: 2, minor: 1), - maximum: RuntimeProtocolVersion(major: 2, minor: 0) - ) - ) - XCTAssertThrowsError( - try RuntimeProtocolRange( - minimum: RuntimeProtocolVersion(major: 1, minor: 9), - maximum: RuntimeProtocolVersion(major: 2, minor: 0) - ) - ) - } - - func testOpaqueIdentifiersEncodeAsStrings() throws { - let identifier = try RuntimeOpaqueIdentifier(rawValue: "node:label:1") - let payload = try RuntimeMessageCodec().encode( - RuntimeJSONObject(values: ["nodeID": .string(identifier.rawValue)]) - ) - - XCTAssertTrue(String(decoding: payload, as: UTF8.self).contains(#""node:label:1""#)) - } } diff --git a/Implementations/Swift/Tests/AstrolabeProtocolTests/Core/RuntimeIdentifierTests.swift b/Implementations/Swift/Tests/AstrolabeProtocolTests/Core/RuntimeIdentifierTests.swift new file mode 100644 index 0000000..a60adb1 --- /dev/null +++ b/Implementations/Swift/Tests/AstrolabeProtocolTests/Core/RuntimeIdentifierTests.swift @@ -0,0 +1,21 @@ +// +// RuntimeIdentifierTests.swift +// astrolabe-protocol +// +// Created by 轩辕十四 on 2026/7/16. +// + +import AstrolabeProtocol +import Foundation +import XCTest + +final class RuntimeIdentifierTests: XCTestCase { + func testOpaqueIdentifiersEncodeAsStrings() throws { + let identifier = try RuntimeOpaqueIdentifier(rawValue: "node:label:1") + let payload = try RuntimeMessageCodec().encode( + RuntimeJSONObject(values: ["nodeID": .string(identifier.rawValue)]) + ) + + XCTAssertTrue(String(decoding: payload, as: UTF8.self).contains(#""node:label:1""#)) + } +} diff --git a/Implementations/Swift/Tests/AstrolabeProtocolTests/Negotiation/RuntimeProtocolRangeTests.swift b/Implementations/Swift/Tests/AstrolabeProtocolTests/Negotiation/RuntimeProtocolRangeTests.swift new file mode 100644 index 0000000..90615fb --- /dev/null +++ b/Implementations/Swift/Tests/AstrolabeProtocolTests/Negotiation/RuntimeProtocolRangeTests.swift @@ -0,0 +1,26 @@ +// +// RuntimeProtocolRangeTests.swift +// astrolabe-protocol +// +// Created by 轩辕十四 on 2026/7/16. +// + +import AstrolabeProtocol +import XCTest + +final class RuntimeProtocolRangeTests: XCTestCase { + func testProtocolRangeRejectsDescendingAndCrossMajorRanges() { + XCTAssertThrowsError( + try RuntimeProtocolRange( + minimum: RuntimeProtocolVersion(major: 2, minor: 1), + maximum: RuntimeProtocolVersion(major: 2, minor: 0) + ) + ) + XCTAssertThrowsError( + try RuntimeProtocolRange( + minimum: RuntimeProtocolVersion(major: 1, minor: 9), + maximum: RuntimeProtocolVersion(major: 2, minor: 0) + ) + ) + } +} diff --git a/README.md b/README.md index ccf0dcf..0083fbf 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,13 @@ Root SwiftPM, Gradle, and npm manifests are ecosystem entrypoints. They map to the implementation and Tooling directories without making one language the repository's primary implementation. +Both language implementations use the same conceptual domains: `Core`, +`Attributes`, `Framing`, `Messaging`, `Negotiation`, `Inspection`, and +`Patching`. Swift uses nested `Application`, `Hierarchy`, and `NodeDetail` +directories inside Inspection; Kotlin keeps the flat public package +`dev.astrolabe.protocol` and does not manufacture package boundaries for +physical directory symmetry. + ## Development Install the contract validator and run all checks: diff --git a/README.zh-CN.md b/README.zh-CN.md index 8470297..d4070fe 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -51,6 +51,11 @@ Tooling/ Contract 校验、发布自动化和测试 根目录的 SwiftPM、Gradle 和 npm 清单只是各生态的标准入口,通过显式路径映射到实现与 Tooling, 不表示任一语言是仓库的主实现。 +双端实现使用相同的概念分域:`Core`、`Attributes`、`Framing`、`Messaging`、`Negotiation`、 +`Inspection` 和 `Patching`。Swift 在 Inspection 内继续按 `Application`、`Hierarchy` 和 +`NodeDetail` 建立子目录;Kotlin 保持公开 package `dev.astrolabe.protocol` 扁平,不为追求物理目录 +对称而制造新的 package 边界。 + ## 开发 安装协议校验器并运行全部检查: diff --git a/docs/superpowers/plans/2026-08-04-protocol-implementation-domain-alignment.md b/docs/superpowers/plans/2026-08-04-protocol-implementation-domain-alignment.md new file mode 100644 index 0000000..858d99e --- /dev/null +++ b/docs/superpowers/plans/2026-08-04-protocol-implementation-domain-alignment.md @@ -0,0 +1,201 @@ +# Protocol Implementation Domain Alignment Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking. + +**Goal:** Align Swift and Kotlin implementation files with the approved protocol domains without changing declarations, packages, modules, wire behavior, schemas, fixtures, or published coordinates. + +**Architecture:** Attribute values and their supporting types form an `Attributes` domain consumed by Inspection and Patching. Framing contains only byte framing, Messaging owns JSON message encoding, and Swift Inspection uses Application, Hierarchy, and NodeDetail subdirectories while Kotlin retains a flat `inspection` package directory because its public package remains `dev.astrolabe.protocol`. + +**Tech Stack:** Swift 5.9 / SwiftPM, Kotlin/JVM / Gradle, Kotlin Serialization, XCTest, Kotlin Test. + +## Global Constraints + +- Do not change any public type, initializer, property, method, Codable shape, serializer, package, module, product, artifact, Schema, Fixture, or Wire Version. +- Do not create compatibility directories, symlinks, forwarding types, or duplicate source files. +- Keep one Swift module named `AstrolabeProtocol` and one Kotlin package named `dev.astrolabe.protocol`. +- Keep `RuntimePatchableAttribute` and `RuntimeAttributePatch` in Patching because they define patch discovery and lifecycle, not general attribute values. +- Put `RuntimeLayoutRelation` in Attributes because `RuntimeAttributeValue` directly owns the `layoutRelations` case and should not depend conceptually on Inspection. +- Do not split Kotlin `inspection` into package-like subdirectories while declarations retain a flat package. +- Never commit automatically; leave the completed change for user review. + +--- + +### Task 1: Record the clean baseline + +**Files:** +- Verify only. + +**Interfaces:** +- Consumes: commit `b7453fb` on `develop`. +- Produces: pre-move Swift and Kotlin regression evidence. + +- [x] **Step 1: Run Swift tests** + +Run: `swift test --parallel` + +Expected: 20 tests pass. + +- [x] **Step 2: Run Kotlin tests** + +Run: `./gradlew :AstrolabeProtocolKotlin:test` + +Expected: Gradle exits zero. + +### Task 2: Align Swift source domains + +**Files:** +- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Framing/RuntimeMessageCodec.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Messaging/RuntimeMessageCodec.swift` +- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributeValue.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeAttributeValue.swift` +- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributedTextRun.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeAttributedTextRun.swift` +- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeLayoutRelation.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeLayoutRelation.swift` +- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeApplication.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Application/RuntimeApplication.swift` +- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeHierarchy.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeHierarchy.swift` +- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeNodeDetail.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/NodeDetail/RuntimeNodeDetail.swift` + +**Interfaces:** +- Consumes: SwiftPM recursive source discovery under the explicit target path. +- Produces: `Core`, `Attributes`, `Framing`, `Messaging`, `Negotiation`, `Inspection`, and `Patching` source ownership with unchanged declarations. + +- [x] **Step 1: Create domain directories and move files with Git** + +```bash +mkdir -p Implementations/Swift/Sources/AstrolabeProtocol/Attributes +mkdir -p Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Application +mkdir -p Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy +mkdir -p Implementations/Swift/Sources/AstrolabeProtocol/Inspection/NodeDetail +git mv Implementations/Swift/Sources/AstrolabeProtocol/Framing/RuntimeMessageCodec.swift Implementations/Swift/Sources/AstrolabeProtocol/Messaging/RuntimeMessageCodec.swift +git mv Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributeValue.swift Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeAttributeValue.swift +git mv Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributedTextRun.swift Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeAttributedTextRun.swift +git mv Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeLayoutRelation.swift Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeLayoutRelation.swift +git mv Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeApplication.swift Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Application/RuntimeApplication.swift +git mv Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeHierarchy.swift Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeHierarchy.swift +git mv Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeNodeDetail.swift Implementations/Swift/Sources/AstrolabeProtocol/Inspection/NodeDetail/RuntimeNodeDetail.swift +``` + +- [x] **Step 2: Verify the move is declaration-preserving** + +Run: `git diff --staged --summary && git diff --staged --numstat` + +Expected: all seven files are 100% renames with zero added or deleted source lines. + +- [x] **Step 3: Run Swift tests** + +Run: `swift test --parallel` + +Expected: 20 tests still pass. + +### Task 3: Mirror Swift tests by domain + +**Files:** +- Delete after extraction: `Implementations/Swift/Tests/AstrolabeProtocolTests/Inspection/RuntimeModelTests.swift` +- Create: `Implementations/Swift/Tests/AstrolabeProtocolTests/Attributes/RuntimeAttributeValueTests.swift` +- Create: `Implementations/Swift/Tests/AstrolabeProtocolTests/Core/RuntimeIdentifierTests.swift` +- Create: `Implementations/Swift/Tests/AstrolabeProtocolTests/Negotiation/RuntimeProtocolRangeTests.swift` + +**Interfaces:** +- Consumes: the three existing `RuntimeModelTests` methods unchanged. +- Produces: one test class per owning source domain. + +- [x] **Step 1: Move the attribute round-trip test** + +Create `RuntimeAttributeValueTests` containing only `testTypedAttributeValuesRoundTrip`, preserving its existing value list, codec calls, and assertions byte-for-byte. + +- [x] **Step 2: Move the identifier test** + +Create `RuntimeIdentifierTests` containing only `testOpaqueIdentifiersEncodeAsStrings`, preserving its payload and assertion. + +- [x] **Step 3: Move the protocol range test** + +Create `RuntimeProtocolRangeTests` containing only `testProtocolRangeRejectsDescendingAndCrossMajorRanges`, preserving both invalid ranges. + +- [x] **Step 4: Remove the mixed-domain test file and verify test count** + +Run: `swift test --parallel` + +Expected: the same 20 tests pass under the three domain-specific test classes. + +### Task 4: Align Kotlin source and test domains + +**Files:** +- Move: `Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributeValue.kt` → `Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributeValue.kt` +- Move: `Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributedTextRun.kt` → `Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributedTextRun.kt` +- Move: `Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeLayoutRelation.kt` → `Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeLayoutRelation.kt` +- Modify: `Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetailModelTest.kt` +- Create: `Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributeValueTest.kt` + +**Interfaces:** +- Consumes: flat Kotlin package declarations and recursive Gradle source discovery. +- Produces: a physical `attributes` domain without changing `package dev.astrolabe.protocol`. + +- [x] **Step 1: Move Kotlin attribute source files** + +```bash +mkdir -p Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes +git mv Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributeValue.kt Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributeValue.kt +git mv Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributedTextRun.kt Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributedTextRun.kt +git mv Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeLayoutRelation.kt Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeLayoutRelation.kt +``` + +- [x] **Step 2: Extract attribute-value tests from node detail** + +Move these four methods unchanged into `RuntimeAttributeValueTest`: + +```text +standalone vector attribute values decode with signed components +attribute value discriminator must be a string +integer attribute value must contain a JSON number +common and extension attribute values round trip symmetrically +``` + +Keep `node detail fixtures decode through typed contracts` and `node identifiers and attribute identifiers retain wire constraints` in `RuntimeNodeDetailModelTest`. + +- [x] **Step 3: Run Kotlin tests** + +Run: `./gradlew :AstrolabeProtocolKotlin:test` + +Expected: Gradle exits zero with the same test behavior. + +### Task 5: Document and verify the domain boundary + +**Files:** +- Modify: `README.md` +- Modify: `README.zh-CN.md` + +**Interfaces:** +- Consumes: final implementation directory tree. +- Produces: a discoverable ownership rule for future UI Graph work. + +- [x] **Step 1: Add the implementation-domain list to both READMEs** + +Document the seven shared domains: + +```text +Core / Attributes / Framing / Messaging / Negotiation / Inspection / Patching +``` + +State that Swift may use nested Inspection directories while Kotlin keeps the flat public package. + +- [x] **Step 2: Run all regression gates** + +```bash +npm test +swift test --parallel +swift build -c release +./gradlew :AstrolabeProtocolKotlin:test +./gradlew :AstrolabeProtocolKotlin:publishToMavenLocal +git diff --check +``` + +Expected: every command exits zero. + +- [x] **Step 3: Audit scope** + +Run current-tree searches for attribute files under Inspection and `RuntimeMessageCodec.swift` under Framing. + +Expected: no misplaced file remains, no public declaration changes appear, and no compatibility path exists. + +- [x] **Step 4: Leave all changes uncommitted** + +Run: `git status --short --branch` + +Expected: only planned domain moves, test splits, README updates, and this plan are present. From ea96dee9f1ae816752db1df9bb7ce9b72fa83b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BD=A9=E8=BE=95=E5=8D=81=E5=9B=9B?= Date: Tue, 4 Aug 2026 18:59:36 +0800 Subject: [PATCH 3/6] chore: ignore superpowers artifacts --- .gitignore | 1 + ...8-04-contract-first-directory-migration.md | 283 ------------------ ...rotocol-implementation-domain-alignment.md | 201 ------------- 3 files changed, 1 insertion(+), 484 deletions(-) delete mode 100644 docs/superpowers/plans/2026-08-04-contract-first-directory-migration.md delete mode 100644 docs/superpowers/plans/2026-08-04-protocol-implementation-domain-alignment.md diff --git a/.gitignore b/.gitignore index 4e4c1bd..14e83ec 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ node_modules/ **/build/ *.xcuserstate .DS_Store +docs/superpowers diff --git a/docs/superpowers/plans/2026-08-04-contract-first-directory-migration.md b/docs/superpowers/plans/2026-08-04-contract-first-directory-migration.md deleted file mode 100644 index 9b52a76..0000000 --- a/docs/superpowers/plans/2026-08-04-contract-first-directory-migration.md +++ /dev/null @@ -1,283 +0,0 @@ -# Contract-first Directory Migration Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking. - -**Goal:** Move `astrolabe-protocol` to the approved Contract-first top-level layout without changing public APIs, wire semantics, artifact coordinates, or release behavior. - -**Architecture:** `Contract/` becomes the normative location for protocol documents, schemas, fixtures, and the V2 manifest. Swift and Kotlin move under symmetric `Implementations/` directories, while validation, release, and test scripts move under `Tooling/`; root build manifests explicitly map these physical paths. - -**Tech Stack:** Swift 5.9 / SwiftPM, Kotlin/JVM / Gradle, Node.js 22, JSON Schema Draft 2020-12, GitHub Actions. - -## Global Constraints - -- Do not add compatibility directories, symlinks, duplicated schemas, or forwarding files. -- Keep Swift module/product `AstrolabeProtocol` unchanged. -- Keep Kotlin package `dev.astrolabe.protocol`, Gradle project `:AstrolabeProtocolKotlin`, Maven coordinates, and protocol version unchanged. -- Keep every Schema `$id`, message type, JSON field, and Fixture validity expectation unchanged. -- This plan performs the mechanical top-level migration only; it does not split Schema domains, reorganize implementation source domains, or add UI Graph models. -- Never commit automatically; leave all changes uncommitted for user review. -- Run `release-prepare.mjs` tests, but do not invoke the release command because it commits and tags by design. - ---- - -### Task 1: Capture the pre-migration regression baseline - -**Files:** -- Verify only; no files changed. - -**Interfaces:** -- Consumes: current root paths and build manifests. -- Produces: passing baseline for comparison after the move. - -- [x] **Step 1: Run the aggregate contract and Tooling tests** - -Run: `npm test` - -Expected: version check, schema/fixture validation, Swift contract tests, and eight Node tests pass. - -- [x] **Step 2: Run the full Swift suite and release build** - -Run: `swift test --parallel && swift build -c release` - -Expected: 20 Swift tests pass and the release product builds. - -- [x] **Step 3: Run Kotlin tests and local publication build** - -Run: `./gradlew :AstrolabeProtocolKotlin:test :AstrolabeProtocolKotlin:publishToMavenLocal` - -Expected: both Gradle tasks complete successfully. - -### Task 2: Move normative Contract assets - -**Files:** -- Move: `PROTOCOL.md` → `Contract/v1/PROTOCOL.md` -- Move: `PROTOCOL-2.0.md` → `Contract/v2/PROTOCOL.md` -- Move: `Schemas/v1/` → `Contract/v1/Schemas/` -- Move: `Schemas/v2/` → `Contract/v2/Schemas/` -- Move: `Fixtures/v1/` → `Contract/v1/Fixtures/` -- Move: `Fixtures/v2/manifest.json` → `Contract/v2/manifest.json` -- Move: `Fixtures/v2/valid/` → `Contract/v2/Fixtures/valid/` -- Move: `Fixtures/v2/invalid/` → `Contract/v2/Fixtures/invalid/` -- Modify: `Contract/v2/manifest.json` -- Modify: `Contract/v2/Schemas/conformance-manifest.schema.json` -- Modify: `Contract/v2/PROTOCOL.md` - -**Interfaces:** -- Consumes: existing Schema IDs and fixture case registration. -- Produces: `Contract/v1` and `Contract/v2` as the only normative asset roots. - -- [x] **Step 1: Move assets with history-preserving Git moves** - -```bash -mkdir -p Contract/v1 Contract/v2/Fixtures -git mv PROTOCOL.md Contract/v1/PROTOCOL.md -git mv PROTOCOL-2.0.md Contract/v2/PROTOCOL.md -git mv Schemas/v1 Contract/v1/Schemas -git mv Schemas/v2 Contract/v2/Schemas -git mv Fixtures/v1 Contract/v1/Fixtures -git mv Fixtures/v2/manifest.json Contract/v2/manifest.json -git mv Fixtures/v2/valid Contract/v2/Fixtures/valid -git mv Fixtures/v2/invalid Contract/v2/Fixtures/invalid -``` - -- [x] **Step 2: Rewrite only repository-relative fixture paths in the manifest** - -Replace every `Fixtures/v2/valid/` and `Fixtures/v2/invalid/` prefix with `Contract/v2/Fixtures/valid/` and `Contract/v2/Fixtures/invalid/`. Leave Schema URLs and fixture JSON content unchanged. - -Update the conformance manifest Schema path pattern and V2 protocol document's normative asset paths to the same Contract-first locations. - -- [x] **Step 3: Verify contract inventory** - -Run: `find Contract -type f | sort` - -Expected: both protocol versions exist under `Contract/`, with V2 manifest, schemas, valid fixtures, and invalid fixtures present exactly once. - -### Task 3: Move symmetric language implementations - -**Files:** -- Move: `Sources/` → `Implementations/Swift/Sources/` -- Move: `Tests/` → `Implementations/Swift/Tests/` -- Move: `AstrolabeProtocolKotlin/` → `Implementations/Kotlin/` -- Modify: `Package.swift` -- Modify: `settings.gradle.kts` -- Modify: `Implementations/Kotlin/build.gradle.kts` -- Modify: `Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolFixtureTests.swift` -- Modify: `Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolV2ContractTests.swift` -- Modify: `Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/contract/ProtocolFixtureConformanceTest.kt` -- Modify: all Kotlin tests that load `v2/valid` or `v2/invalid` classpath resources - -**Interfaces:** -- Consumes: Contract paths produced by Task 2. -- Produces: unchanged Swift and Kotlin public APIs from symmetric physical roots. - -- [x] **Step 1: Move both implementations** - -```bash -mkdir -p Implementations/Swift Implementations -git mv Sources Implementations/Swift/Sources -git mv Tests Implementations/Swift/Tests -git mv AstrolabeProtocolKotlin Implementations/Kotlin -``` - -- [x] **Step 2: Add explicit SwiftPM target paths** - -```swift -.target( - name: "AstrolabeProtocol", - path: "Implementations/Swift/Sources/AstrolabeProtocol" -), -.testTarget( - name: "AstrolabeProtocolTests", - dependencies: ["AstrolabeProtocol"], - path: "Implementations/Swift/Tests/AstrolabeProtocolTests" -) -``` - -- [x] **Step 3: Map the existing Gradle project to its new directory** - -```kotlin -include(":AstrolabeProtocolKotlin") -project(":AstrolabeProtocolKotlin").projectDir = file("Implementations/Kotlin") -``` - -- [x] **Step 4: Point Kotlin test resources at Contract** - -```kotlin -sourceSets { - test { - resources.srcDir(rootProject.layout.projectDirectory.dir("Contract")) - } -} -``` - -Update every Kotlin fixture lookup from `v2/valid` and `v2/invalid` to `v2/Fixtures/valid` and `v2/Fixtures/invalid`. - -- [x] **Step 5: Update Swift Contract test paths** - -Use `Contract/v2/manifest.json`, `Contract/v2/PROTOCOL.md`, and `Contract/v2/Schemas` while keeping repository-root discovery based on `Package.swift`. - -- [x] **Step 6: Run language-specific regressions** - -Run: `swift test --parallel && ./gradlew :AstrolabeProtocolKotlin:test` - -Expected: the same 20 Swift tests and all Kotlin tests pass from the new roots. - -### Task 4: Move Tooling and update root entrypoints - -**Files:** -- Move: `scripts/validate-contract.mjs` → `Tooling/ContractValidation/validate-contract.mjs` -- Move: `scripts/versioning.mjs` → `Tooling/Release/versioning.mjs` -- Move: `scripts/version-sync.mjs` → `Tooling/Release/version-sync.mjs` -- Move: `scripts/release-prepare.mjs` → `Tooling/Release/release-prepare.mjs` -- Move: `test/*.test.mjs` → `Tooling/Tests/` -- Modify: `package.json` -- Modify: `Tooling/ContractValidation/validate-contract.mjs` -- Modify: `Tooling/Release/versioning.mjs` -- Modify: `Tooling/Release/version-sync.mjs` -- Modify: `Tooling/Release/release-prepare.mjs` -- Modify: `Tooling/Tests/release-prepare.test.mjs` -- Modify: `Tooling/Tests/versioning.test.mjs` - -**Interfaces:** -- Consumes: Contract and implementation paths from Tasks 2 and 3. -- Produces: root npm commands with unchanged names and release behavior. - -- [x] **Step 1: Move Tooling by responsibility** - -```bash -mkdir -p Tooling/ContractValidation Tooling/Release Tooling/Tests -git mv scripts/validate-contract.mjs Tooling/ContractValidation/validate-contract.mjs -git mv scripts/versioning.mjs Tooling/Release/versioning.mjs -git mv scripts/version-sync.mjs Tooling/Release/version-sync.mjs -git mv scripts/release-prepare.mjs Tooling/Release/release-prepare.mjs -git mv test/*.test.mjs Tooling/Tests/ -``` - -- [x] **Step 2: Update Tooling repository-root and asset paths** - -Scripts under `Tooling/*` resolve the repository root with `../..`. Use: - -```javascript -const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); -const schemaDirectory = join(repositoryRoot, "Contract/v2/Schemas"); -const fixtureDirectory = join(repositoryRoot, "Contract/v2/Fixtures"); -const manifest = readJSON(join(repositoryRoot, "Contract/v2/manifest.json")); -``` - -Change the Swift metadata path in `versioning.mjs` to `Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift`. - -- [x] **Step 3: Update Tooling test imports and temporary Swift metadata path** - -```javascript -import { parseReleaseArgs, prepareRelease } from "../Release/release-prepare.mjs"; -import { versionedPaths } from "../Release/versioning.mjs"; -``` - -Create temporary metadata under `Implementations/Swift/Sources/AstrolabeProtocol/Core` in the release test. - -- [x] **Step 4: Update npm scripts without changing command names** - -```json -"test:schema": "node Tooling/ContractValidation/validate-contract.mjs", -"version:check": "node Tooling/Release/version-sync.mjs --check", -"version:set": "node Tooling/Release/version-sync.mjs --set", -"release:prepare": "node Tooling/Release/release-prepare.mjs", -"test": "npm run version:check && npm run test:schema && npm run test:swift-contract && node --test Tooling/Tests/*.test.mjs" -``` - -- [x] **Step 5: Run aggregate Tooling regression** - -Run: `npm test` - -Expected: schema/fixture validation, Swift Contract tests, version tests, release tests, and CI workflow tests pass. - -### Task 5: Update documentation and run final gates - -**Files:** -- Modify: `README.md` -- Modify: `README.zh-CN.md` -- Verify: `.github/workflows/ci.yml` -- Verify: all repository files and Git diff. - -**Interfaces:** -- Consumes: completed Contract-first filesystem. -- Produces: reviewable, documented, fully regressed migration. - -- [x] **Step 1: Document the Contract-first layout** - -Update both READMEs to link to `Contract/v2/PROTOCOL.md` and `Contract/v1/PROTOCOL.md`, and describe: - -```text -Contract/ normative protocol documents, schemas, and fixtures -Implementations/ equal Swift and Kotlin implementations -Tooling/ contract validation, release automation, and tests -``` - -- [x] **Step 2: Confirm CI needs no task-name changes** - -The workflow continues to call `npm test`, SwiftPM root commands, and `:AstrolabeProtocolKotlin:*`; explicit root mappings absorb the physical move. - -- [x] **Step 3: Run all final gates** - -```bash -npm test -swift test --parallel -swift build -c release -./gradlew :AstrolabeProtocolKotlin:test -./gradlew :AstrolabeProtocolKotlin:publishToMavenLocal -git diff --check -``` - -Expected: all commands exit zero. - -- [x] **Step 4: Audit invariants and scope** - -Run path searches for `Fixtures/`, `Schemas/`, `PROTOCOL-2.0.md`, `Sources/AstrolabeProtocol`, `Tests/AstrolabeProtocolTests`, `AstrolabeProtocolKotlin/`, `scripts/`, and `test/`. - -Expected: references either point to the new Contract-first paths or intentionally retain the Gradle project name; no old physical directory remains. - -- [x] **Step 5: Leave the change uncommitted** - -Run: `git status --short && git diff --stat` - -Expected: only the planned directory migration, path updates, README changes, and this plan are present; no commit or tag is created. diff --git a/docs/superpowers/plans/2026-08-04-protocol-implementation-domain-alignment.md b/docs/superpowers/plans/2026-08-04-protocol-implementation-domain-alignment.md deleted file mode 100644 index 858d99e..0000000 --- a/docs/superpowers/plans/2026-08-04-protocol-implementation-domain-alignment.md +++ /dev/null @@ -1,201 +0,0 @@ -# Protocol Implementation Domain Alignment Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking. - -**Goal:** Align Swift and Kotlin implementation files with the approved protocol domains without changing declarations, packages, modules, wire behavior, schemas, fixtures, or published coordinates. - -**Architecture:** Attribute values and their supporting types form an `Attributes` domain consumed by Inspection and Patching. Framing contains only byte framing, Messaging owns JSON message encoding, and Swift Inspection uses Application, Hierarchy, and NodeDetail subdirectories while Kotlin retains a flat `inspection` package directory because its public package remains `dev.astrolabe.protocol`. - -**Tech Stack:** Swift 5.9 / SwiftPM, Kotlin/JVM / Gradle, Kotlin Serialization, XCTest, Kotlin Test. - -## Global Constraints - -- Do not change any public type, initializer, property, method, Codable shape, serializer, package, module, product, artifact, Schema, Fixture, or Wire Version. -- Do not create compatibility directories, symlinks, forwarding types, or duplicate source files. -- Keep one Swift module named `AstrolabeProtocol` and one Kotlin package named `dev.astrolabe.protocol`. -- Keep `RuntimePatchableAttribute` and `RuntimeAttributePatch` in Patching because they define patch discovery and lifecycle, not general attribute values. -- Put `RuntimeLayoutRelation` in Attributes because `RuntimeAttributeValue` directly owns the `layoutRelations` case and should not depend conceptually on Inspection. -- Do not split Kotlin `inspection` into package-like subdirectories while declarations retain a flat package. -- Never commit automatically; leave the completed change for user review. - ---- - -### Task 1: Record the clean baseline - -**Files:** -- Verify only. - -**Interfaces:** -- Consumes: commit `b7453fb` on `develop`. -- Produces: pre-move Swift and Kotlin regression evidence. - -- [x] **Step 1: Run Swift tests** - -Run: `swift test --parallel` - -Expected: 20 tests pass. - -- [x] **Step 2: Run Kotlin tests** - -Run: `./gradlew :AstrolabeProtocolKotlin:test` - -Expected: Gradle exits zero. - -### Task 2: Align Swift source domains - -**Files:** -- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Framing/RuntimeMessageCodec.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Messaging/RuntimeMessageCodec.swift` -- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributeValue.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeAttributeValue.swift` -- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributedTextRun.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeAttributedTextRun.swift` -- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeLayoutRelation.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeLayoutRelation.swift` -- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeApplication.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Application/RuntimeApplication.swift` -- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeHierarchy.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeHierarchy.swift` -- Move: `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeNodeDetail.swift` → `Implementations/Swift/Sources/AstrolabeProtocol/Inspection/NodeDetail/RuntimeNodeDetail.swift` - -**Interfaces:** -- Consumes: SwiftPM recursive source discovery under the explicit target path. -- Produces: `Core`, `Attributes`, `Framing`, `Messaging`, `Negotiation`, `Inspection`, and `Patching` source ownership with unchanged declarations. - -- [x] **Step 1: Create domain directories and move files with Git** - -```bash -mkdir -p Implementations/Swift/Sources/AstrolabeProtocol/Attributes -mkdir -p Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Application -mkdir -p Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy -mkdir -p Implementations/Swift/Sources/AstrolabeProtocol/Inspection/NodeDetail -git mv Implementations/Swift/Sources/AstrolabeProtocol/Framing/RuntimeMessageCodec.swift Implementations/Swift/Sources/AstrolabeProtocol/Messaging/RuntimeMessageCodec.swift -git mv Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributeValue.swift Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeAttributeValue.swift -git mv Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeAttributedTextRun.swift Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeAttributedTextRun.swift -git mv Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeLayoutRelation.swift Implementations/Swift/Sources/AstrolabeProtocol/Attributes/RuntimeLayoutRelation.swift -git mv Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeApplication.swift Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Application/RuntimeApplication.swift -git mv Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeHierarchy.swift Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeHierarchy.swift -git mv Implementations/Swift/Sources/AstrolabeProtocol/Inspection/RuntimeNodeDetail.swift Implementations/Swift/Sources/AstrolabeProtocol/Inspection/NodeDetail/RuntimeNodeDetail.swift -``` - -- [x] **Step 2: Verify the move is declaration-preserving** - -Run: `git diff --staged --summary && git diff --staged --numstat` - -Expected: all seven files are 100% renames with zero added or deleted source lines. - -- [x] **Step 3: Run Swift tests** - -Run: `swift test --parallel` - -Expected: 20 tests still pass. - -### Task 3: Mirror Swift tests by domain - -**Files:** -- Delete after extraction: `Implementations/Swift/Tests/AstrolabeProtocolTests/Inspection/RuntimeModelTests.swift` -- Create: `Implementations/Swift/Tests/AstrolabeProtocolTests/Attributes/RuntimeAttributeValueTests.swift` -- Create: `Implementations/Swift/Tests/AstrolabeProtocolTests/Core/RuntimeIdentifierTests.swift` -- Create: `Implementations/Swift/Tests/AstrolabeProtocolTests/Negotiation/RuntimeProtocolRangeTests.swift` - -**Interfaces:** -- Consumes: the three existing `RuntimeModelTests` methods unchanged. -- Produces: one test class per owning source domain. - -- [x] **Step 1: Move the attribute round-trip test** - -Create `RuntimeAttributeValueTests` containing only `testTypedAttributeValuesRoundTrip`, preserving its existing value list, codec calls, and assertions byte-for-byte. - -- [x] **Step 2: Move the identifier test** - -Create `RuntimeIdentifierTests` containing only `testOpaqueIdentifiersEncodeAsStrings`, preserving its payload and assertion. - -- [x] **Step 3: Move the protocol range test** - -Create `RuntimeProtocolRangeTests` containing only `testProtocolRangeRejectsDescendingAndCrossMajorRanges`, preserving both invalid ranges. - -- [x] **Step 4: Remove the mixed-domain test file and verify test count** - -Run: `swift test --parallel` - -Expected: the same 20 tests pass under the three domain-specific test classes. - -### Task 4: Align Kotlin source and test domains - -**Files:** -- Move: `Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributeValue.kt` → `Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributeValue.kt` -- Move: `Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributedTextRun.kt` → `Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributedTextRun.kt` -- Move: `Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeLayoutRelation.kt` → `Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeLayoutRelation.kt` -- Modify: `Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeDetailModelTest.kt` -- Create: `Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributeValueTest.kt` - -**Interfaces:** -- Consumes: flat Kotlin package declarations and recursive Gradle source discovery. -- Produces: a physical `attributes` domain without changing `package dev.astrolabe.protocol`. - -- [x] **Step 1: Move Kotlin attribute source files** - -```bash -mkdir -p Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes -git mv Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributeValue.kt Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributeValue.kt -git mv Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeAttributedTextRun.kt Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeAttributedTextRun.kt -git mv Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeLayoutRelation.kt Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/attributes/RuntimeLayoutRelation.kt -``` - -- [x] **Step 2: Extract attribute-value tests from node detail** - -Move these four methods unchanged into `RuntimeAttributeValueTest`: - -```text -standalone vector attribute values decode with signed components -attribute value discriminator must be a string -integer attribute value must contain a JSON number -common and extension attribute values round trip symmetrically -``` - -Keep `node detail fixtures decode through typed contracts` and `node identifiers and attribute identifiers retain wire constraints` in `RuntimeNodeDetailModelTest`. - -- [x] **Step 3: Run Kotlin tests** - -Run: `./gradlew :AstrolabeProtocolKotlin:test` - -Expected: Gradle exits zero with the same test behavior. - -### Task 5: Document and verify the domain boundary - -**Files:** -- Modify: `README.md` -- Modify: `README.zh-CN.md` - -**Interfaces:** -- Consumes: final implementation directory tree. -- Produces: a discoverable ownership rule for future UI Graph work. - -- [x] **Step 1: Add the implementation-domain list to both READMEs** - -Document the seven shared domains: - -```text -Core / Attributes / Framing / Messaging / Negotiation / Inspection / Patching -``` - -State that Swift may use nested Inspection directories while Kotlin keeps the flat public package. - -- [x] **Step 2: Run all regression gates** - -```bash -npm test -swift test --parallel -swift build -c release -./gradlew :AstrolabeProtocolKotlin:test -./gradlew :AstrolabeProtocolKotlin:publishToMavenLocal -git diff --check -``` - -Expected: every command exits zero. - -- [x] **Step 3: Audit scope** - -Run current-tree searches for attribute files under Inspection and `RuntimeMessageCodec.swift` under Framing. - -Expected: no misplaced file remains, no public declaration changes appear, and no compatibility path exists. - -- [x] **Step 4: Leave all changes uncommitted** - -Run: `git status --short --branch` - -Expected: only planned domain moves, test splits, README updates, and this plan are present. From 1629c1d87e2e109173e1dc58199b508f48594129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BD=A9=E8=BE=95=E5=8D=81=E5=9B=9B?= Date: Tue, 4 Aug 2026 19:00:01 +0800 Subject: [PATCH 4/6] feat: add manifest-driven fixture discovery --- .../Schemas/conformance-manifest.schema.json | 15 ++- Contract/v2/manifest.json | 6 +- README.md | 4 + README.zh-CN.md | 3 + .../ContractValidation/fixture-discovery.mjs | 76 +++++++++++ .../ContractValidation/validate-contract.mjs | 21 ++-- Tooling/Tests/fixture-discovery.test.mjs | 118 ++++++++++++++++++ 7 files changed, 232 insertions(+), 11 deletions(-) create mode 100644 Tooling/ContractValidation/fixture-discovery.mjs create mode 100644 Tooling/Tests/fixture-discovery.test.mjs diff --git a/Contract/v2/Schemas/conformance-manifest.schema.json b/Contract/v2/Schemas/conformance-manifest.schema.json index 0bbf98a..e8d1745 100644 --- a/Contract/v2/Schemas/conformance-manifest.schema.json +++ b/Contract/v2/Schemas/conformance-manifest.schema.json @@ -3,12 +3,21 @@ "$id": "https://astrolabe.dev/schemas/v2/conformance-manifest.schema.json", "title": "Astrolabe Protocol 2.0 Conformance Manifest", "type": "object", - "required": ["schemaVersion", "wireProtocolVersion", "methods", "cases"], + "required": ["schemaVersion", "wireProtocolVersion", "fixtureRoots", "methods", "cases"], "properties": { - "schemaVersion": { "const": 1 }, + "schemaVersion": { "const": 2 }, "wireProtocolVersion": { "$ref": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/protocolVersion" }, + "fixtureRoots": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "pattern": "^(?!.*\\/\\.\\.?($|\\/))Contract/v2/Fixtures/(valid|invalid)(?:/[^/]+)*$" + } + }, "methods": { "type": "array", "minItems": 1, @@ -32,7 +41,7 @@ "required": ["name", "fixture", "schema", "expectation"], "properties": { "name": { "type": "string", "minLength": 1 }, - "fixture": { "type": "string", "pattern": "^Contract/v2/Fixtures/(valid|invalid)/.+\\.json$" }, + "fixture": { "type": "string", "pattern": "^Contract/v2/Fixtures/(valid|invalid)/.+\\.json$" }, "schema": { "type": "string", "format": "uri-reference" }, "expectation": { "enum": ["valid", "invalid"] }, "semanticRule": { "enum": ["ascendingProtocolRange", "derivedVisibility"] }, diff --git a/Contract/v2/manifest.json b/Contract/v2/manifest.json index afb7f65..e6f39b6 100644 --- a/Contract/v2/manifest.json +++ b/Contract/v2/manifest.json @@ -1,6 +1,10 @@ { - "schemaVersion": 1, + "schemaVersion": 2, "wireProtocolVersion": { "major": 2, "minor": 0 }, + "fixtureRoots": [ + "Contract/v2/Fixtures/valid", + "Contract/v2/Fixtures/invalid" + ], "methods": [ { "name": "handshake", diff --git a/README.md b/README.md index 0083fbf..ca86220 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,10 @@ Validation compiles every Draft 2020-12 Schema, checks valid and invalid Fixtures, applies semantic rules that JSON Schema cannot express, and verifies that the Swift DTOs accept and reject the same payloads. +`Contract/v2/manifest.json` declares recursive Fixture roots through +`fixtureRoots`. Every JSON Fixture below those roots must have exactly one +registration in `cases`. + ## License Astrolabe Protocol is available under the [Apache License 2.0](LICENSE). diff --git a/README.zh-CN.md b/README.zh-CN.md index d4070fe..cb3aaeb 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -70,6 +70,9 @@ swift build -c release 校验流程会编译全部 Draft 2020-12 Schema,检查有效和无效 Fixture,执行 JSON Schema 无法表达的 语义规则,并验证 Swift DTO 对相同 Payload 的接受和拒绝行为保持一致。 +`Contract/v2/manifest.json` 通过 `fixtureRoots` 声明递归 Fixture 根目录。根目录下的每个 JSON +Fixture 都必须在 `cases` 中且仅有一条注册记录。 + ## 许可证 Astrolabe Protocol 使用 [Apache License 2.0](LICENSE) 许可。 diff --git a/Tooling/ContractValidation/fixture-discovery.mjs b/Tooling/ContractValidation/fixture-discovery.mjs new file mode 100644 index 0000000..bb383cd --- /dev/null +++ b/Tooling/ContractValidation/fixture-discovery.mjs @@ -0,0 +1,76 @@ +import { lstatSync, readdirSync } from "node:fs"; +import { isAbsolute, join, relative, resolve, sep } from "node:path"; + +export function discoverFixturePaths(repositoryRoot, fixtureRoots) { + const resolvedRepositoryRoot = resolve(repositoryRoot); + const fixturePaths = []; + const discoveredPaths = new Set(); + + for (const fixtureRoot of fixtureRoots) { + const resolvedFixtureRoot = resolve(resolvedRepositoryRoot, fixtureRoot); + assertWithinRepository(resolvedRepositoryRoot, resolvedFixtureRoot, fixtureRoot); + const rootStatus = fixtureRootStatus(resolvedFixtureRoot, fixtureRoot); + if (rootStatus.isSymbolicLink()) { + throw new Error(`Fixture discovery does not allow symbolic links: ${fixtureRoot}`); + } + if (!rootStatus.isDirectory()) { + throw new Error(`Fixture root is not a directory: ${fixtureRoot}`); + } + visitDirectory( + resolvedFixtureRoot, + resolvedRepositoryRoot, + fixturePaths, + discoveredPaths + ); + } + + return fixturePaths.sort(); +} + +function visitDirectory(directory, repositoryRoot, fixturePaths, discoveredPaths) { + const entries = readdirSync(directory, { withFileTypes: true }); + for (const entry of entries) { + const path = join(directory, entry.name); + if (entry.isSymbolicLink()) { + throw new Error( + `Fixture discovery does not allow symbolic links: ${repositoryRelativePath(repositoryRoot, path)}` + ); + } + if (entry.isDirectory()) { + visitDirectory(path, repositoryRoot, fixturePaths, discoveredPaths); + } else if (entry.isFile() && entry.name.endsWith(".json")) { + const fixturePath = repositoryRelativePath(repositoryRoot, path); + if (discoveredPaths.has(fixturePath)) { + throw new Error(`Fixture discovered through multiple roots: ${fixturePath}`); + } + discoveredPaths.add(fixturePath); + fixturePaths.push(fixturePath); + } + } +} + +function fixtureRootStatus(path, displayPath) { + try { + return lstatSync(path); + } catch (error) { + if (error?.code === "ENOENT") { + throw new Error(`Fixture root does not exist: ${displayPath}`); + } + throw error; + } +} + +function assertWithinRepository(repositoryRoot, path, displayPath) { + const repositoryRelative = relative(repositoryRoot, path); + if ( + repositoryRelative === ".." || + repositoryRelative.startsWith(`..${sep}`) || + isAbsolute(repositoryRelative) + ) { + throw new Error(`Fixture root resolves outside repository: ${displayPath}`); + } +} + +function repositoryRelativePath(repositoryRoot, path) { + return relative(repositoryRoot, path).split(sep).join("/"); +} diff --git a/Tooling/ContractValidation/validate-contract.mjs b/Tooling/ContractValidation/validate-contract.mjs index 89832f9..1458eeb 100644 --- a/Tooling/ContractValidation/validate-contract.mjs +++ b/Tooling/ContractValidation/validate-contract.mjs @@ -1,13 +1,14 @@ import { readFileSync, readdirSync } from "node:fs"; -import { dirname, join, relative, resolve } from "node:path"; +import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import Ajv2020 from "ajv/dist/2020.js"; import addFormats from "ajv-formats"; +import { discoverFixturePaths } from "./fixture-discovery.mjs"; + const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); const schemaDirectory = join(repositoryRoot, "Contract/v2/Schemas"); -const fixtureDirectory = join(repositoryRoot, "Contract/v2/Fixtures"); const manifest = readJSON(join(repositoryRoot, "Contract/v2/manifest.json")); const ajv = new Ajv2020({ allErrors: true, strict: true, allowUnionTypes: true }); addFormats(ajv); @@ -92,16 +93,22 @@ for (const method of manifest.methods) { } } -const fixtureFiles = ["valid", "invalid"].flatMap((directory) => - readdirSync(join(fixtureDirectory, directory)) - .filter((name) => name.endsWith(".json")) - .map((name) => relative(repositoryRoot, join(fixtureDirectory, directory, name))) -); +const fixtureFiles = discoverFixturePaths(repositoryRoot, manifest.fixtureRoots); +const discoveredFixtures = new Set(fixtureFiles); const unmanifestedFixtures = fixtureFiles.filter((path) => !manifestedFixtures.has(path)); if (unmanifestedFixtures.length > 0) { throw new Error(`Unregistered V2 fixtures found: ${unmanifestedFixtures.join(", ")}`); } +const undiscoveredFixtures = [...manifestedFixtures] + .filter((path) => !discoveredFixtures.has(path)) + .sort(); +if (undiscoveredFixtures.length > 0) { + throw new Error( + `Registered V2 fixtures are outside fixtureRoots: ${undiscoveredFixtures.join(", ")}` + ); +} + function assertSchemaExists(reference) { const validate = ajv.getSchema(reference); if (!validate) { diff --git a/Tooling/Tests/fixture-discovery.test.mjs b/Tooling/Tests/fixture-discovery.test.mjs new file mode 100644 index 0000000..6145997 --- /dev/null +++ b/Tooling/Tests/fixture-discovery.test.mjs @@ -0,0 +1,118 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { + mkdirSync, + mkdtempSync, + rmSync, + symlinkSync, + writeFileSync +} from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import test from "node:test"; + +import { discoverFixturePaths } from "../ContractValidation/fixture-discovery.mjs"; + +const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); +const validatorPath = join( + repositoryRoot, + "Tooling/ContractValidation/validate-contract.mjs" +); + +test("contract validation discovers nested unregistered fixtures", () => { + const fixtureDirectory = mkdtempSync( + join(repositoryRoot, "Contract/v2/Fixtures/valid/fixture-discovery-test-") + ); + const fixturePath = join(fixtureDirectory, "unregistered.json"); + + try { + mkdirSync(fixtureDirectory, { recursive: true }); + writeFileSync(fixturePath, "{}\n"); + + const result = spawnSync(process.execPath, [validatorPath], { + cwd: repositoryRoot, + encoding: "utf8" + }); + + assert.notEqual(result.status, 0); + assert.match( + `${result.stdout}\n${result.stderr}`, + /Unregistered V2 fixtures found: .*fixture-discovery-test-[^/]+\/unregistered\.json/ + ); + } finally { + rmSync(fixtureDirectory, { recursive: true, force: true }); + } +}); + +test("fixture discovery returns nested JSON paths in deterministic order", (context) => { + const root = temporaryRepository(context); + mkdirSync(join(root, "fixtures/nested"), { recursive: true }); + writeFileSync(join(root, "fixtures/z.json"), "{}\n"); + writeFileSync(join(root, "fixtures/nested/a.json"), "{}\n"); + writeFileSync(join(root, "fixtures/nested/ignored.txt"), "ignored\n"); + + assert.deepEqual(discoverFixturePaths(root, ["fixtures"]), [ + "fixtures/nested/a.json", + "fixtures/z.json" + ]); +}); + +test("fixture discovery rejects symbolic links", (context) => { + const root = temporaryRepository(context); + mkdirSync(join(root, "fixtures"), { recursive: true }); + writeFileSync(join(root, "target.json"), "{}\n"); + symlinkSync(join(root, "target.json"), join(root, "fixtures/linked.json")); + + assert.throws( + () => discoverFixturePaths(root, ["fixtures"]), + /Fixture discovery does not allow symbolic links: fixtures\/linked\.json/ + ); +}); + +test("fixture discovery rejects overlapping roots", (context) => { + const root = temporaryRepository(context); + mkdirSync(join(root, "fixtures/nested"), { recursive: true }); + writeFileSync(join(root, "fixtures/nested/value.json"), "{}\n"); + + assert.throws( + () => discoverFixturePaths(root, ["fixtures", "fixtures/nested"]), + /Fixture discovered through multiple roots: fixtures\/nested\/value\.json/ + ); +}); + +test("fixture discovery rejects roots outside the repository", (context) => { + const root = temporaryRepository(context); + const outsideRoot = temporaryRepository(context); + mkdirSync(join(outsideRoot, "fixtures"), { recursive: true }); + + assert.throws( + () => discoverFixturePaths(root, [join(outsideRoot, "fixtures")]), + /Fixture root resolves outside repository/ + ); +}); + +test("fixture discovery reports missing roots", (context) => { + const root = temporaryRepository(context); + + assert.throws( + () => discoverFixturePaths(root, ["missing"]), + /Fixture root does not exist: missing/ + ); +}); + +test("fixture discovery rejects non-directory roots", (context) => { + const root = temporaryRepository(context); + writeFileSync(join(root, "fixture.json"), "{}\n"); + + assert.throws( + () => discoverFixturePaths(root, ["fixture.json"]), + /Fixture root is not a directory: fixture\.json/ + ); +}); + +function temporaryRepository(context) { + const root = mkdtempSync(join(tmpdir(), "astrolabe-fixture-discovery-")); + context.after(() => rmSync(root, { recursive: true, force: true })); + return root; +} From 51b503ff385543a04ba4a5258296e9cf5f30deb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BD=A9=E8=BE=95=E5=8D=81=E5=9B=9B?= Date: Tue, 4 Aug 2026 20:15:55 +0800 Subject: [PATCH 5/6] feat: add UI graph relation contract --- .../invalid/relation-type-not-namespaced.json | 6 ++++ .../v2/Fixtures/valid/handshake-response.json | 1 + .../valid/hierarchy-snapshot-response.json | 8 +++++ Contract/v2/Fixtures/valid/node-relation.json | 6 ++++ Contract/v2/PROTOCOL.md | 13 ++++++++ Contract/v2/Schemas/inspection.schema.json | 22 +++++++++++++ Contract/v2/manifest.json | 4 ++- .../protocol/inspection/RuntimeHierarchy.kt | 2 ++ .../inspection/RuntimeNodeRelation.kt | 23 +++++++++++++ .../protocol/negotiation/RuntimeCapability.kt | 2 ++ .../ProtocolFixtureConformanceTest.kt | 4 +++ .../inspection/RuntimeNodeRelationTest.kt | 29 ++++++++++++++++ .../messaging/RuntimeMessageCodecTest.kt | 2 +- .../Hierarchy/RuntimeHierarchy.swift | 5 +++ .../Hierarchy/RuntimeNodeRelation.swift | 33 +++++++++++++++++++ .../Negotiation/RuntimeCapability.swift | 1 + .../Contract/ProtocolFixtureTests.swift | 2 ++ .../Inspection/RuntimeNodeRelationTests.swift | 22 +++++++++++++ README.md | 7 ++++ README.zh-CN.md | 6 ++++ 20 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 Contract/v2/Fixtures/invalid/relation-type-not-namespaced.json create mode 100644 Contract/v2/Fixtures/valid/node-relation.json create mode 100644 Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeRelation.kt create mode 100644 Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeRelationTest.kt create mode 100644 Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeNodeRelation.swift create mode 100644 Implementations/Swift/Tests/AstrolabeProtocolTests/Inspection/RuntimeNodeRelationTests.swift diff --git a/Contract/v2/Fixtures/invalid/relation-type-not-namespaced.json b/Contract/v2/Fixtures/invalid/relation-type-not-namespaced.json new file mode 100644 index 0000000..c254cc2 --- /dev/null +++ b/Contract/v2/Fixtures/invalid/relation-type-not-namespaced.json @@ -0,0 +1,6 @@ +{ + "type": "backingLayer", + "sourceNodeID": "node:view:1", + "targetNodeID": "node:layer:1", + "extensions": { "vendor.graph.confidence": 1 } +} diff --git a/Contract/v2/Fixtures/valid/handshake-response.json b/Contract/v2/Fixtures/valid/handshake-response.json index b502f97..28862af 100644 --- a/Contract/v2/Fixtures/valid/handshake-response.json +++ b/Contract/v2/Fixtures/valid/handshake-response.json @@ -18,6 +18,7 @@ "attributePatchDiscovery", "attributePatching", "requestCancellation", + "uiGraphRelations", "vendor.experimentalInspection" ] } diff --git a/Contract/v2/Fixtures/valid/hierarchy-snapshot-response.json b/Contract/v2/Fixtures/valid/hierarchy-snapshot-response.json index 5b65b1c..8894fff 100644 --- a/Contract/v2/Fixtures/valid/hierarchy-snapshot-response.json +++ b/Contract/v2/Fixtures/valid/hierarchy-snapshot-response.json @@ -92,6 +92,14 @@ } ] } + ], + "relations": [ + { + "type": "vendor.graph.related", + "sourceNodeID": "node:window:0", + "targetNodeID": "node:label:1", + "extensions": {} + } ] } } diff --git a/Contract/v2/Fixtures/valid/node-relation.json b/Contract/v2/Fixtures/valid/node-relation.json new file mode 100644 index 0000000..f3ae58e --- /dev/null +++ b/Contract/v2/Fixtures/valid/node-relation.json @@ -0,0 +1,6 @@ +{ + "type": "vendor.graph.related", + "sourceNodeID": "node:view:1", + "targetNodeID": "node:layer:1", + "extensions": { "vendor.graph.confidence": 1 } +} diff --git a/Contract/v2/PROTOCOL.md b/Contract/v2/PROTOCOL.md index a85549d..25b3d59 100644 --- a/Contract/v2/PROTOCOL.md +++ b/Contract/v2/PROTOCOL.md @@ -201,6 +201,19 @@ Visibility keeps independent causes instead of overloading one Boolean: ancestor, has effective opacity greater than `0.01`, intersects the viewport, and is not fully clipped by an ancestor. +### 8.3 UI graph relations + +The `uiGraphRelations` capability advertises that hierarchy snapshots may +contain an optional `relations` member. Each directed relation contains an open +namespaced `type`, a `sourceNodeID`, a `targetNodeID`, and namespaced +`extensions`. Relation types remain producer-owned vocabulary rather than a +closed Protocol enum. + +The Runtime reports observed relations between captured nodes. It does not +duplicate hierarchy parent/child edges in `relations`. When either the +`uiGraphRelations` capability or the optional member is absent, the Host must +not infer that any cross-tree relation exists. + ## 9. Node details and attributes Node detail is grouped into sections. Section categories and attribute diff --git a/Contract/v2/Schemas/inspection.schema.json b/Contract/v2/Schemas/inspection.schema.json index f867891..9cceb44 100644 --- a/Contract/v2/Schemas/inspection.schema.json +++ b/Contract/v2/Schemas/inspection.schema.json @@ -222,6 +222,24 @@ } } }, + "nodeRelation": { + "type": "object", + "required": ["type", "sourceNodeID", "targetNodeID", "extensions"], + "properties": { + "type": { + "$ref": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/namespacedIdentifier" + }, + "sourceNodeID": { + "$ref": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/opaqueIdentifier" + }, + "targetNodeID": { + "$ref": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/opaqueIdentifier" + }, + "extensions": { + "$ref": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/extensionMap" + } + } + }, "hierarchySnapshotPayload": { "type": "object", "required": [ @@ -255,6 +273,10 @@ "type": "array", "items": { "$ref": "#/$defs/node" } }, + "relations": { + "type": "array", + "items": { "$ref": "#/$defs/nodeRelation" } + }, "extensions": { "$ref": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/extensionMap" } diff --git a/Contract/v2/manifest.json b/Contract/v2/manifest.json index e6f39b6..9798474 100644 --- a/Contract/v2/manifest.json +++ b/Contract/v2/manifest.json @@ -102,6 +102,7 @@ { "name": "unknown method request envelope", "fixture": "Contract/v2/Fixtures/valid/unknown-method-request.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/unknownMethodRequest", "expectation": "valid" }, { "name": "unknown method failure envelope", "fixture": "Contract/v2/Fixtures/valid/unknown-method-failure-response.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json#/$defs/unknownMethodFailureResponse", "expectation": "valid" }, { "name": "signed vector attribute value", "fixture": "Contract/v2/Fixtures/valid/vector-attribute-value.json", "schema": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/attributeValue", "expectation": "valid" }, + { "name": "namespaced node relation", "fixture": "Contract/v2/Fixtures/valid/node-relation.json", "schema": "https://astrolabe.dev/schemas/v2/inspection.schema.json#/$defs/nodeRelation", "expectation": "valid" }, { "name": "missing request id", "fixture": "Contract/v2/Fixtures/invalid/request-missing-request-id.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applicationInfoRequest", "expectation": "invalid", "reason": "requestID is required" }, { "name": "v1 message on v2 contract", "fixture": "Contract/v2/Fixtures/invalid/request-uses-v1.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/applicationInfoRequest", "expectation": "invalid", "reason": "V2 does not accept V1 aliases or envelopes" }, { "name": "numeric node id", "fixture": "Contract/v2/Fixtures/invalid/node-id-is-number.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/nodeDetailRequest", "expectation": "invalid", "reason": "node IDs are opaque strings" }, @@ -115,6 +116,7 @@ { "name": "unnamespaced extension", "fixture": "Contract/v2/Fixtures/invalid/extension-key-not-namespaced.json", "schema": "https://astrolabe.dev/schemas/v2/common.schema.json#/$defs/extensionMap", "expectation": "invalid", "reason": "extension keys require a namespace" }, { "name": "unknown response status", "fixture": "Contract/v2/Fixtures/invalid/unknown-response-status.json", "schema": "https://astrolabe.dev/schemas/v2/response.schema.json", "expectation": "invalid", "reason": "response status is a closed discriminator" }, { "name": "descending protocol range", "fixture": "Contract/v2/Fixtures/invalid/descending-protocol-range.json", "schema": "https://astrolabe.dev/schemas/v2/request.schema.json#/$defs/handshakeRequest", "expectation": "invalid", "semanticRule": "ascendingProtocolRange", "reason": "minimum protocol version cannot exceed maximum" }, - { "name": "inconsistent visibility", "fixture": "Contract/v2/Fixtures/invalid/inconsistent-visibility.json", "schema": "https://astrolabe.dev/schemas/v2/inspection.schema.json#/$defs/visibility", "expectation": "invalid", "semanticRule": "derivedVisibility", "reason": "a hidden node cannot be onscreen" } + { "name": "inconsistent visibility", "fixture": "Contract/v2/Fixtures/invalid/inconsistent-visibility.json", "schema": "https://astrolabe.dev/schemas/v2/inspection.schema.json#/$defs/visibility", "expectation": "invalid", "semanticRule": "derivedVisibility", "reason": "a hidden node cannot be onscreen" }, + { "name": "relation type without namespace", "fixture": "Contract/v2/Fixtures/invalid/relation-type-not-namespaced.json", "schema": "https://astrolabe.dev/schemas/v2/inspection.schema.json#/$defs/nodeRelation", "expectation": "invalid", "reason": "relation types require a namespace" } ] } diff --git a/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchy.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchy.kt index 7bbec1c..97afc45 100644 --- a/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchy.kt +++ b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeHierarchy.kt @@ -186,6 +186,8 @@ public data class RuntimeHierarchySnapshotPayload( public val viewport: RuntimeCoordinateRect, /** Ordered hierarchy roots. */ public val roots: List, + /** Directed cross-tree relations captured with this hierarchy. */ + public val relations: List? = null, /** Optional namespaced snapshot facts. */ public val extensions: RuntimeExtensionMap? = null ) { diff --git a/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeRelation.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeRelation.kt new file mode 100644 index 0000000..48594f6 --- /dev/null +++ b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeRelation.kt @@ -0,0 +1,23 @@ +// +// RuntimeNodeRelation.kt +// astrolabe-protocol +// +// Created by 轩辕十四 on 2026/8/4. +// + +package dev.astrolabe.protocol + +import kotlinx.serialization.Serializable + +/** Directed platform-neutral relation between two captured Runtime nodes. */ +@Serializable +public data class RuntimeNodeRelation( + /** Open namespaced relation type owned by its producer. */ + public val type: RuntimeNamespacedIdentifier, + /** Source node identifier for this directed relation. */ + public val sourceNodeID: RuntimeOpaqueIdentifier, + /** Target node identifier for this directed relation. */ + public val targetNodeID: RuntimeOpaqueIdentifier, + /** Namespaced producer-specific relation facts. */ + public val extensions: RuntimeExtensionMap +) diff --git a/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeCapability.kt b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeCapability.kt index 8c1523e..3d8ea39 100644 --- a/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeCapability.kt +++ b/Implementations/Kotlin/src/main/kotlin/dev/astrolabe/protocol/negotiation/RuntimeCapability.kt @@ -35,6 +35,8 @@ public value class RuntimeCapability( public val attributePatching: RuntimeCapability = RuntimeCapability("attributePatching") /** Runtime supports request cancellation. */ public val requestCancellation: RuntimeCapability = RuntimeCapability("requestCancellation") + /** Runtime can report directed cross-tree node relations. */ + public val uiGraphRelations: RuntimeCapability = RuntimeCapability("uiGraphRelations") } } diff --git a/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/contract/ProtocolFixtureConformanceTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/contract/ProtocolFixtureConformanceTest.kt index 9580c39..4d43107 100644 --- a/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/contract/ProtocolFixtureConformanceTest.kt +++ b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/contract/ProtocolFixtureConformanceTest.kt @@ -32,6 +32,7 @@ class ProtocolFixtureConformanceTest { "hierarchy-snapshot-response.json" to { decodeResponse(RuntimeHierarchySnapshotPayload.contract, it) }, "list-attribute-patches-request.json" to { decodeRequest(RuntimeListAttributePatchesParameters.contract, it) }, "list-attribute-patches-response.json" to { decodeResponse(RuntimeAttributePatchListPayload.contract, it) }, + "node-relation.json" to { codec.decodeValue(it, RuntimeNodeRelation.serializer()) }, "node-detail-failure-response.json" to { codec.decodeResponse(it) }, "node-detail-request.json" to { decodeRequest(RuntimeNodeDetailParameters.contract, it) }, "node-detail-response.json" to { decodeResponse(RuntimeNodeDetailPayload.contract, it) }, @@ -78,6 +79,9 @@ class ProtocolFixtureConformanceTest { }, "request-missing-request-id.json" to { source -> codec.decodeRequest(source) }, "request-uses-v1.json" to { source -> codec.decodeRequest(source) }, + "relation-type-not-namespaced.json" to { source -> + codec.decodeValue(source, RuntimeNodeRelation.serializer()) + }, "success-response-contains-error.json" to { source -> codec.decodeResponse(source) }, "unknown-response-status.json" to { source -> codec.decodeResponse(source) }, "unsafe-integer-attribute-value.json" to { source -> diff --git a/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeRelationTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeRelationTest.kt new file mode 100644 index 0000000..9e9ec7b --- /dev/null +++ b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/inspection/RuntimeNodeRelationTest.kt @@ -0,0 +1,29 @@ +package dev.astrolabe.protocol + +import kotlinx.serialization.json.JsonPrimitive +import kotlin.test.Test +import kotlin.test.assertEquals + +class RuntimeNodeRelationTest { + private val codec = RuntimeMessageCodec() + + @Test + fun `namespaced node relation round trips`() { + val relation = RuntimeNodeRelation( + type = RuntimeNamespacedIdentifier("vendor.graph.related"), + sourceNodeID = RuntimeOpaqueIdentifier("node:view:1"), + targetNodeID = RuntimeOpaqueIdentifier("node:layer:1"), + extensions = RuntimeExtensionMap( + mapOf("vendor.graph.confidence" to JsonPrimitive(1)) + ) + ) + + val encoded = codec.encodeValue(relation, RuntimeNodeRelation.serializer()) + + assertEquals( + relation, + codec.decodeValue(encoded, RuntimeNodeRelation.serializer()) + ) + assertEquals("uiGraphRelations", RuntimeCapability.uiGraphRelations.rawValue) + } +} diff --git a/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodecTest.kt b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodecTest.kt index 22871be..edd2926 100644 --- a/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodecTest.kt +++ b/Implementations/Kotlin/src/test/kotlin/dev/astrolabe/protocol/messaging/RuntimeMessageCodecTest.kt @@ -103,7 +103,7 @@ class RuntimeMessageCodecTest { @Test fun `codec decodes every valid request and response fixture`() { validFixtureNames() - .filterNot { it == "vector-attribute-value.json" } + .filterNot { it in setOf("node-relation.json", "vector-attribute-value.json") } .forEach { name -> val source = fixture("v2/Fixtures/valid/$name") val document = codec.decodeDocument(source) diff --git a/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeHierarchy.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeHierarchy.swift index 032e750..1cb18c6 100644 --- a/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeHierarchy.swift +++ b/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeHierarchy.swift @@ -268,6 +268,9 @@ public struct RuntimeHierarchySnapshotPayload: Codable, Equatable, Sendable, Run /// Ordered hierarchy roots. public let roots: [RuntimeNode] + /// Directed cross-tree relations captured with this hierarchy. + public let relations: [RuntimeNodeRelation]? + /// Optional namespaced snapshot facts. public let extensions: RuntimeExtensionMap? @@ -279,6 +282,7 @@ public struct RuntimeHierarchySnapshotPayload: Codable, Equatable, Sendable, Run display: RuntimeDisplayInfo, viewport: RuntimeCoordinateRect, roots: [RuntimeNode], + relations: [RuntimeNodeRelation]? = nil, extensions: RuntimeExtensionMap? = nil ) { self.snapshotID = snapshotID @@ -288,6 +292,7 @@ public struct RuntimeHierarchySnapshotPayload: Codable, Equatable, Sendable, Run self.display = display self.viewport = viewport self.roots = roots + self.relations = relations self.extensions = extensions } diff --git a/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeNodeRelation.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeNodeRelation.swift new file mode 100644 index 0000000..004a795 --- /dev/null +++ b/Implementations/Swift/Sources/AstrolabeProtocol/Inspection/Hierarchy/RuntimeNodeRelation.swift @@ -0,0 +1,33 @@ +// +// RuntimeNodeRelation.swift +// astrolabe-protocol +// +// Created by 轩辕十四 on 2026/8/4. +// + +/// Directed platform-neutral relation between two captured Runtime nodes. +public struct RuntimeNodeRelation: Codable, Equatable, Sendable { + /// Open namespaced relation type owned by its producer. + public let type: RuntimeNamespacedIdentifier + + /// Source node identifier for this directed relation. + public let sourceNodeID: RuntimeOpaqueIdentifier + + /// Target node identifier for this directed relation. + public let targetNodeID: RuntimeOpaqueIdentifier + + /// Namespaced producer-specific relation facts. + public let extensions: RuntimeExtensionMap + + public init( + type: RuntimeNamespacedIdentifier, + sourceNodeID: RuntimeOpaqueIdentifier, + targetNodeID: RuntimeOpaqueIdentifier, + extensions: RuntimeExtensionMap + ) { + self.type = type + self.sourceNodeID = sourceNodeID + self.targetNodeID = targetNodeID + self.extensions = extensions + } +} diff --git a/Implementations/Swift/Sources/AstrolabeProtocol/Negotiation/RuntimeCapability.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Negotiation/RuntimeCapability.swift index 52c1c3f..0e3295c 100644 --- a/Implementations/Swift/Sources/AstrolabeProtocol/Negotiation/RuntimeCapability.swift +++ b/Implementations/Swift/Sources/AstrolabeProtocol/Negotiation/RuntimeCapability.swift @@ -32,6 +32,7 @@ public struct RuntimeCapability: Codable, Equatable, Hashable, Sendable { public static let attributePatchDiscovery = RuntimeCapability(uncheckedRawValue: "attributePatchDiscovery") public static let attributePatching = RuntimeCapability(uncheckedRawValue: "attributePatching") public static let requestCancellation = RuntimeCapability(uncheckedRawValue: "requestCancellation") + public static let uiGraphRelations = RuntimeCapability(uncheckedRawValue: "uiGraphRelations") private init(uncheckedRawValue: String) { rawValue = uncheckedRawValue diff --git a/Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolFixtureTests.swift b/Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolFixtureTests.swift index e423b4b..dc563c1 100644 --- a/Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolFixtureTests.swift +++ b/Implementations/Swift/Tests/AstrolabeProtocolTests/Contract/ProtocolFixtureTests.swift @@ -112,6 +112,8 @@ final class ProtocolFixtureTests: XCTestCase { return try wrap(RuntimeExtensionMap.self, data, codec) case "visibility": return try wrap(RuntimeNodeVisibility.self, data, codec) + case "nodeRelation": + return try wrap(RuntimeNodeRelation.self, data, codec) case "response.schema.json": return try wrap(RuntimeResponse.self, data, codec) default: diff --git a/Implementations/Swift/Tests/AstrolabeProtocolTests/Inspection/RuntimeNodeRelationTests.swift b/Implementations/Swift/Tests/AstrolabeProtocolTests/Inspection/RuntimeNodeRelationTests.swift new file mode 100644 index 0000000..526fcf5 --- /dev/null +++ b/Implementations/Swift/Tests/AstrolabeProtocolTests/Inspection/RuntimeNodeRelationTests.swift @@ -0,0 +1,22 @@ +import AstrolabeProtocol +import XCTest + +final class RuntimeNodeRelationTests: XCTestCase { + func testNamespacedNodeRelationRoundTrips() throws { + let relation = RuntimeNodeRelation( + type: try RuntimeNamespacedIdentifier(rawValue: "vendor.graph.related"), + sourceNodeID: try RuntimeOpaqueIdentifier(rawValue: "node:view:1"), + targetNodeID: try RuntimeOpaqueIdentifier(rawValue: "node:layer:1"), + extensions: try RuntimeExtensionMap( + values: ["vendor.graph.confidence": .integer(1)] + ) + ) + let codec = RuntimeMessageCodec() + + XCTAssertEqual( + relation, + try codec.decode(RuntimeNodeRelation.self, from: codec.encode(relation)) + ) + XCTAssertEqual(RuntimeCapability.uiGraphRelations.rawValue, "uiGraphRelations") + } +} diff --git a/README.md b/README.md index ca86220..7af2ff9 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,13 @@ Swift types are one implementation of the contract. Other implementations use the specification, Schemas, Fixtures, and documented wire behavior as their compatibility source of truth. +## UI Graph Relations + +Runtimes advertising `uiGraphRelations` may include an optional `relations` +array in hierarchy snapshots. Each directed relation uses an open namespaced +type plus source and target node IDs. Hierarchy parent/child edges remain in the +authoritative trees and are not duplicated in this array. + ## Repository Layout ```text diff --git a/README.zh-CN.md b/README.zh-CN.md index cb3aaeb..dbbffb5 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -40,6 +40,12 @@ Wire Protocol 版本为 `2.0`。 Swift 类型只是该协议的一种实现。其他语言的实现应以规范、Schema、Fixture 和文档约定的 Wire 行为作为兼容性事实源。 +## UI Graph 关系 + +声明 `uiGraphRelations` capability 的 Runtime 可以在 hierarchy snapshot 中返回可选的 +`relations` 数组。每条有向关系使用开放的 namespaced type 以及源、目标 node ID。层级父子边仍由 +权威树承载,不在该数组中重复存储。 + ## 仓库目录 ```text From 368e8bce331199e349f5cd39aa1a1025f647fd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BD=A9=E8=BE=95=E5=8D=81=E5=9B=9B?= Date: Sun, 9 Aug 2026 20:21:18 +0800 Subject: [PATCH 6/6] chore: release 2.1.0 --- .../AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift | 2 +- README.md | 2 +- README.zh-CN.md | 2 +- gradle.properties | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift b/Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift index 42bfb57..deed87f 100644 --- a/Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift +++ b/Implementations/Swift/Sources/AstrolabeProtocol/Core/RuntimeProtocolMetadata.swift @@ -7,7 +7,7 @@ public enum RuntimeProtocolMetadata { /// AstrolabeProtocol package release version. - public static let packageVersion = "2.0.0" + public static let packageVersion = "2.1.0" /// Wire protocol version implemented by this package. public static let wireVersion = RuntimeProtocolVersion.v2 diff --git a/README.md b/README.md index 7af2ff9..b76d2c0 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Add the package through Swift Package Manager: ```swift .package( url: "https://github.com/regulusleow/astrolabe-protocol.git", - exact: "2.0.0" + exact: "2.1.0" ) ``` diff --git a/README.zh-CN.md b/README.zh-CN.md index dbbffb5..04acdb7 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -22,7 +22,7 @@ UIKit、Android View、Transport Listener、设备发现、截图、CLI 命令 ```swift .package( url: "https://github.com/regulusleow/astrolabe-protocol.git", - exact: "2.0.0" + exact: "2.1.0" ) ``` diff --git a/gradle.properties b/gradle.properties index 2de6cde..c51a78d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -astrolabeVersion=2.0.0 +astrolabeVersion=2.1.0 org.gradle.configuration-cache=true org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 kotlin.code.style=official diff --git a/package-lock.json b/package-lock.json index c20b648..f9d4f04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "astrolabe-protocol-contract", - "version": "2.0.0", + "version": "2.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "astrolabe-protocol-contract", - "version": "2.0.0", + "version": "2.1.0", "devDependencies": { "ajv": "8.20.0", "ajv-formats": "3.0.1" diff --git a/package.json b/package.json index 7a08b03..a037463 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "astrolabe-protocol-contract", - "version": "2.0.0", + "version": "2.1.0", "private": true, "type": "module", "scripts": {