| ... | @@ -683,7 +683,7 @@ describe("EnvironmentRegistry", () => { | ... | @@ -683,7 +683,7 @@ describe("EnvironmentRegistry", () => { |
| 683 | | 683 | |
| 684 | it.effect("only a fresh health check for the rejected environment unlocks it", () => | 684 | it.effect("only a fresh health check for the rejected environment unlocks it", () => |
| 685 | Effect.gen(function* () { | 685 | Effect.gen(function* () { |
| 686 | const harness = yield* makeHarness([RELAY_TARGET], [], [], { | 686 | const harness = yield* makeHarness([RELAY_TARGET, SECOND_RELAY_TARGET], [], [], { |
| 687 | initialDisabled: [RELAY_TARGET.environmentId], | 687 | initialDisabled: [RELAY_TARGET.environmentId], |
| 688 | }); | 688 | }); |
| 689 | const descriptor = (environmentId: EnvironmentId): ExecutionEnvironmentDescriptor => ({ | 689 | const descriptor = (environmentId: EnvironmentId): ExecutionEnvironmentDescriptor => ({ |
| ... | @@ -778,8 +778,8 @@ describe("EnvironmentRegistry", () => { | ... | @@ -778,8 +778,8 @@ describe("EnvironmentRegistry", () => { |
| 778 | yield* SubscriptionRef.update(discoveryState, (state) => ({ | 778 | yield* SubscriptionRef.update(discoveryState, (state) => ({ |
| 779 | ...state, | 779 | ...state, |
| 780 | environments: new Map(state.environments).set( | 780 | environments: new Map(state.environments).set( |
| 781 | SECOND_TARGET.environmentId, | 781 | SECOND_RELAY_TARGET.environmentId, |
| 782 | discovered(descriptor(SECOND_TARGET.environmentId)), | 782 | discovered(descriptor(SECOND_RELAY_TARGET.environmentId)), |
| 783 | ), | 783 | ), |
| 784 | })); | 784 | })); |
| 785 | yield* Deferred.await(unrelated); | 785 | yield* Deferred.await(unrelated); |
| ... | @@ -798,8 +798,8 @@ describe("EnvironmentRegistry", () => { | ... | @@ -798,8 +798,8 @@ describe("EnvironmentRegistry", () => { |
| 798 | environments: new Map([ | 798 | environments: new Map([ |
| 799 | [RELAY_TARGET.environmentId, discovered(descriptor(RELAY_TARGET.environmentId))], | 799 | [RELAY_TARGET.environmentId, discovered(descriptor(RELAY_TARGET.environmentId))], |
| 800 | [ | 800 | [ |
| 801 | SECOND_TARGET.environmentId, | 801 | SECOND_RELAY_TARGET.environmentId, |
| 802 | discovered(descriptor(SECOND_TARGET.environmentId), "2026-09-15T00:01:00Z"), | 802 | discovered(descriptor(SECOND_RELAY_TARGET.environmentId), "2026-09-15T00:01:00Z"), |
| 803 | ], | 803 | ], |
| 804 | ]), | 804 | ]), |
| 805 | })); | 805 | })); |
| ... | @@ -827,6 +827,92 @@ describe("EnvironmentRegistry", () => { | ... | @@ -827,6 +827,92 @@ describe("EnvironmentRegistry", () => { |
| 827 | }), | 827 | }), |
| 828 | ); | 828 | ); |
| 829 | | 829 | |
| | 830 | it.effect("discovery leaves direct connections that share an environment id alone", () => |
| | 831 | Effect.gen(function* () { |
| | 832 | const harness = yield* makeHarness([TARGET, RELAY_TARGET]); |
| | 833 | const discovered = (environmentId: EnvironmentId) => { |
| | 834 | const endpoint = { |
| | 835 | httpBaseUrl: "https://relay.example.test", |
| | 836 | wsBaseUrl: "wss://relay.example.test", |
| | 837 | providerKind: "manual" as const, |
| | 838 | }; |
| | 839 | return { |
| | 840 | environment: { |
| | 841 | environmentId, |
| | 842 | label: "Preview server", |
| | 843 | endpoint, |
| | 844 | linkedAt: "2026-09-25T00:00:00Z", |
| | 845 | }, |
| | 846 | availability: "online" as const, |
| | 847 | status: Option.some<RelayEnvironmentStatusResponse>({ |
| | 848 | environmentId, |
| | 849 | endpoint, |
| | 850 | status: "online", |
| | 851 | checkedAt: "2026-09-25T00:00:00Z", |
| | 852 | descriptor: { |
| | 853 | environmentId, |
| | 854 | label: "Preview server", |
| | 855 | platform: { os: "darwin", arch: "arm64" }, |
| | 856 | serverVersion: "2.0.0", |
| | 857 | orchestrationProtocolVersion: ORCHESTRATION_PROTOCOL_VERSION + 1, |
| | 858 | capabilities: { repositoryIdentity: true }, |
| | 859 | }, |
| | 860 | }), |
| | 861 | error: Option.none(), |
| | 862 | }; |
| | 863 | }; |
| | 864 | yield* Effect.gen(function* () { |
| | 865 | const registry = yield* EnvironmentRegistry.EnvironmentRegistry; |
| | 866 | yield* registry.start; |
| | 867 | yield* awaitConnectionState( |
| | 868 | registry, |
| | 869 | TARGET.environmentId, |
| | 870 | (state) => state.phase === "connected", |
| | 871 | ); |
| | 872 | const relayChecked = yield* Deferred.make<void>(); |
| | 873 | yield* watchDiscoveredCompatibility().pipe( |
| | 874 | Effect.provideService(EnvironmentRegistry.EnvironmentRegistry, { |
| | 875 | ...registry, |
| | 876 | setCompatibility: (environmentId, error) => |
| | 877 | registry |
| | 878 | .setCompatibility(environmentId, error) |
| | 879 | .pipe( |
| | 880 | Effect.andThen( |
| | 881 | environmentId === RELAY_TARGET.environmentId |
| | 882 | ? Deferred.succeed(relayChecked, undefined) |
| | 883 | : Effect.void, |
| | 884 | ), |
| | 885 | ), |
| | 886 | }), |
| | 887 | Effect.provideService( |
| | 888 | RelayEnvironmentDiscovery.RelayEnvironmentDiscovery, |
| | 889 | RelayEnvironmentDiscovery.RelayEnvironmentDiscovery.of({ |
| | 890 | state: |
| | 891 | yield* SubscriptionRef.make<RelayEnvironmentDiscovery.RelayEnvironmentDiscoveryState>( |
| | 892 | { |
| | 893 | ...RelayEnvironmentDiscovery.EMPTY_RELAY_ENVIRONMENT_DISCOVERY_STATE, |
| | 894 | environments: new Map([ |
| | 895 | [TARGET.environmentId, discovered(TARGET.environmentId)], |
| | 896 | [RELAY_TARGET.environmentId, discovered(RELAY_TARGET.environmentId)], |
| | 897 | ]), |
| | 898 | }, |
| | 899 | ), |
| | 900 | refresh: Effect.void, |
| | 901 | }), |
| | 902 | ), |
| | 903 | Effect.forkScoped, |
| | 904 | ); |
| | 905 | yield* Deferred.await(relayChecked); |
| | 906 | |
| | 907 | const entries = yield* SubscriptionRef.get(registry.entries); |
| | 908 | expect(entries.get(RELAY_TARGET.environmentId)).toMatchObject({ enabled: false }); |
| | 909 | expect(entries.get(TARGET.environmentId)).toMatchObject({ enabled: true }); |
| | 910 | expect(entries.get(TARGET.environmentId)?.unsupportedReason).toBeUndefined(); |
| | 911 | expect((yield* registry.state(TARGET.environmentId)).phase).toBe("connected"); |
| | 912 | }).pipe(Effect.provide(harness.layer), Effect.scoped); |
| | 913 | }), |
| | 914 | ); |
| | 915 | |
| 830 | it.effect("discovery keeps unsupported environments off until compatibility changes", () => | 916 | it.effect("discovery keeps unsupported environments off until compatibility changes", () => |
| 831 | Effect.gen(function* () { | 917 | Effect.gen(function* () { |
| 832 | const harness = yield* makeHarness([RELAY_TARGET], [], [], { | 918 | const harness = yield* makeHarness([RELAY_TARGET], [], [], { |