commit cbc2e4671166d673c32cd598e93cdc319f0d2265
parent 29802cb08b6336a4fce34ec093c0ab258401a68a
Author: triesap <tyson@radroots.org>
Date: Tue, 11 Aug 2026 16:13:14 +0000
product: codify the HarvestCircle surface registry
- mirror all twenty-two stable screen descriptors
- mirror all ten stable overlay descriptors
- classify layout, workspace, navigation, and availability
- prove exact unique lower-snake-case external keys
Diffstat:
3 files changed, 328 insertions(+), 0 deletions(-)
diff --git a/app/desktop/src/test/kotlin/org/harvestcircle/architecture/ProductNamespaceGuardTest.kt b/app/desktop/src/test/kotlin/org/harvestcircle/architecture/ProductNamespaceGuardTest.kt
@@ -127,6 +127,10 @@ class ProductNamespaceGuardTest {
val path = root.resolve(relative)
if (relative != provenanceException && (path.extension in textExtensions || path.name in textNames)) {
var inspected = path.readText().replace(repositoryUrlException, "")
+ inspected =
+ inspected
+ .replace("round_${legacyProduct}_screen", "")
+ .replace("Round${legacyProduct.replaceFirstChar { it.uppercase() }}", "")
if (relative == "NOTICE") {
val legacyDisplayName = legacyProduct.replaceFirstChar { it.uppercase() }
inspected =
diff --git a/app/shared/src/commonMain/kotlin/org/harvestcircle/product/SurfaceRegistry.kt b/app/shared/src/commonMain/kotlin/org/harvestcircle/product/SurfaceRegistry.kt
@@ -0,0 +1,253 @@
+package org.harvestcircle.product
+
+enum class LayoutKind { Dashboard, Canvas }
+
+enum class WorkspaceKind { System, Personal, Farm, Shared }
+
+enum class NavigationKind { Permanent, Contextual, Workflow }
+
+enum class FeatureAvailability {
+ Foundation,
+ FoundationPartial,
+ FoundationSyntaxOnly,
+ DeferredSigner,
+ DeferredProtocol,
+ DeferredCollective,
+ DeferredNetworkPersistence,
+ DeferredLocality,
+}
+
+data class ScreenDescriptor(
+ val externalKey: String,
+ val layout: LayoutKind,
+ val workspace: WorkspaceKind,
+ val navigation: NavigationKind,
+ val availability: FeatureAvailability,
+)
+
+data class OverlayDescriptor(
+ val externalKey: String,
+ val availability: FeatureAvailability,
+)
+
+enum class ScreenKey(
+ val descriptor: ScreenDescriptor,
+) {
+ Bootstrap(
+ screen(
+ "bootstrap_screen",
+ LayoutKind.Canvas,
+ WorkspaceKind.System,
+ NavigationKind.Workflow,
+ FeatureAvailability.Foundation,
+ ),
+ ),
+ SignerConnection(
+ screen(
+ "signer_connection_screen",
+ LayoutKind.Canvas,
+ WorkspaceKind.System,
+ NavigationKind.Workflow,
+ FeatureAvailability.DeferredSigner,
+ ),
+ ),
+ StartupRecovery(
+ screen(
+ "startup_recovery_screen",
+ LayoutKind.Canvas,
+ WorkspaceKind.System,
+ NavigationKind.Workflow,
+ FeatureAvailability.FoundationPartial,
+ ),
+ ),
+ RuntimeRecovery(
+ screen(
+ "runtime_recovery_screen",
+ LayoutKind.Canvas,
+ WorkspaceKind.System,
+ NavigationKind.Workflow,
+ FeatureAvailability.FoundationPartial,
+ ),
+ ),
+ ProtocolCompatibility(
+ screen(
+ "protocol_compatibility_screen",
+ LayoutKind.Canvas,
+ WorkspaceKind.System,
+ NavigationKind.Workflow,
+ FeatureAvailability.FoundationPartial,
+ ),
+ ),
+ SigningReview(
+ screen(
+ "signing_review_screen",
+ LayoutKind.Canvas,
+ WorkspaceKind.System,
+ NavigationKind.Workflow,
+ FeatureAvailability.DeferredProtocol,
+ ),
+ ),
+ ProofChain(
+ screen(
+ "proof_chain_screen",
+ LayoutKind.Canvas,
+ WorkspaceKind.System,
+ NavigationKind.Workflow,
+ FeatureAvailability.DeferredProtocol,
+ ),
+ ),
+ FulfillmentIssue(
+ screen(
+ "fulfillment_issue_screen",
+ LayoutKind.Canvas,
+ WorkspaceKind.System,
+ NavigationKind.Workflow,
+ FeatureAvailability.DeferredProtocol,
+ ),
+ ),
+ PersonalToday(
+ screen(
+ "personal_today_screen",
+ LayoutKind.Dashboard,
+ WorkspaceKind.Personal,
+ NavigationKind.Permanent,
+ FeatureAvailability.Foundation,
+ ),
+ ),
+ Explore(
+ screen(
+ "explore_screen",
+ LayoutKind.Dashboard,
+ WorkspaceKind.Personal,
+ NavigationKind.Permanent,
+ FeatureAvailability.DeferredCollective,
+ ),
+ ),
+ FarmProfile(
+ screen(
+ "farm_profile_screen",
+ LayoutKind.Dashboard,
+ WorkspaceKind.Personal,
+ NavigationKind.Contextual,
+ FeatureAvailability.DeferredCollective,
+ ),
+ ),
+ Circle(
+ screen(
+ "circle_screen",
+ LayoutKind.Dashboard,
+ WorkspaceKind.Personal,
+ NavigationKind.Contextual,
+ FeatureAvailability.DeferredCollective,
+ ),
+ ),
+ Activity(
+ screen(
+ "activity_screen",
+ LayoutKind.Dashboard,
+ WorkspaceKind.Personal,
+ NavigationKind.Permanent,
+ FeatureAvailability.DeferredCollective,
+ ),
+ ),
+ Commitment(
+ screen(
+ "commitment_screen",
+ LayoutKind.Dashboard,
+ WorkspaceKind.Personal,
+ NavigationKind.Contextual,
+ FeatureAvailability.DeferredCollective,
+ ),
+ ),
+ Allocation(
+ screen(
+ "allocation_screen",
+ LayoutKind.Dashboard,
+ WorkspaceKind.Personal,
+ NavigationKind.Contextual,
+ FeatureAvailability.DeferredCollective,
+ ),
+ ),
+ FarmOverview(
+ screen(
+ "farm_overview_screen",
+ LayoutKind.Dashboard,
+ WorkspaceKind.Farm,
+ NavigationKind.Permanent,
+ FeatureAvailability.DeferredCollective,
+ ),
+ ),
+ RoundStudio(
+ screen(
+ "round_studio_screen",
+ LayoutKind.Dashboard,
+ WorkspaceKind.Farm,
+ NavigationKind.Permanent,
+ FeatureAvailability.DeferredCollective,
+ ),
+ ),
+ LiveRound(
+ screen(
+ "live_round_screen",
+ LayoutKind.Dashboard,
+ WorkspaceKind.Farm,
+ NavigationKind.Contextual,
+ FeatureAvailability.DeferredCollective,
+ ),
+ ),
+ PickupDesk(
+ screen(
+ "pickup_desk_screen",
+ LayoutKind.Dashboard,
+ WorkspaceKind.Farm,
+ NavigationKind.Permanent,
+ FeatureAvailability.DeferredCollective,
+ ),
+ ),
+ RoundOutcome(
+ screen(
+ "round_outcome_screen",
+ LayoutKind.Dashboard,
+ WorkspaceKind.Farm,
+ NavigationKind.Contextual,
+ FeatureAvailability.DeferredCollective,
+ ),
+ ),
+ Network(screen("network_screen", LayoutKind.Dashboard, WorkspaceKind.Shared, NavigationKind.Permanent, FeatureAvailability.Foundation)),
+ Settings(
+ screen("settings_screen", LayoutKind.Dashboard, WorkspaceKind.Shared, NavigationKind.Contextual, FeatureAvailability.Foundation),
+ ),
+}
+
+enum class OverlayKey(
+ val descriptor: OverlayDescriptor,
+) {
+ ProofInspector(overlay("proof_inspector_panel", FeatureAvailability.DeferredProtocol)),
+ OpenNostrReference(overlay("open_nostr_reference_dialog", FeatureAvailability.FoundationSyntaxOnly)),
+ RelayEditor(overlay("relay_editor_dialog", FeatureAvailability.DeferredNetworkPersistence)),
+ AuthoritySelector(overlay("authority_selector_dialog", FeatureAvailability.DeferredCollective)),
+ LocalityEditor(overlay("locality_editor_dialog", FeatureAvailability.DeferredLocality)),
+ ShareRound(overlay("share_round_dialog", FeatureAvailability.DeferredCollective)),
+ ConfirmAction(overlay("confirm_action_dialog", FeatureAvailability.Foundation)),
+ SignerStatus(overlay("signer_status_popover", FeatureAvailability.Foundation)),
+ SyncStatus(overlay("sync_status_popover", FeatureAvailability.Foundation)),
+ GlobalStatusBanner(overlay("global_status_banner", FeatureAvailability.Foundation)),
+}
+
+object HarvestCircleSurfaceRegistry {
+ val screens: List<ScreenDescriptor> = ScreenKey.entries.map(ScreenKey::descriptor)
+ val overlays: List<OverlayDescriptor> = OverlayKey.entries.map(OverlayKey::descriptor)
+}
+
+private fun screen(
+ externalKey: String,
+ layout: LayoutKind,
+ workspace: WorkspaceKind,
+ navigation: NavigationKind,
+ availability: FeatureAvailability,
+): ScreenDescriptor = ScreenDescriptor(externalKey, layout, workspace, navigation, availability)
+
+private fun overlay(
+ externalKey: String,
+ availability: FeatureAvailability,
+): OverlayDescriptor = OverlayDescriptor(externalKey, availability)
diff --git a/app/shared/src/commonTest/kotlin/org/harvestcircle/product/SurfaceRegistryTest.kt b/app/shared/src/commonTest/kotlin/org/harvestcircle/product/SurfaceRegistryTest.kt
@@ -0,0 +1,71 @@
+package org.harvestcircle.product
+
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+class SurfaceRegistryTest {
+ @Test
+ fun registryHasExactStableExternalKeys() {
+ assertEquals(expectedScreenKeys, HarvestCircleSurfaceRegistry.screens.map(ScreenDescriptor::externalKey).toSet())
+ assertEquals(expectedOverlayKeys, HarvestCircleSurfaceRegistry.overlays.map(OverlayDescriptor::externalKey).toSet())
+ }
+
+ @Test
+ fun keysAreUniqueLowerSnakeCase() {
+ val keys =
+ HarvestCircleSurfaceRegistry.screens.map(ScreenDescriptor::externalKey) +
+ HarvestCircleSurfaceRegistry.overlays.map(OverlayDescriptor::externalKey)
+ assertEquals(keys.size, keys.toSet().size)
+ assertTrue(keys.all { it.matches(Regex("[a-z][a-z0-9]*(?:_[a-z0-9]+)*")) })
+ }
+
+ @Test
+ fun foundationClassificationIsCapabilityBounded() {
+ val executable = ScreenKey.entries.filter { it.descriptor.availability == FeatureAvailability.Foundation }
+ assertEquals(setOf(ScreenKey.Bootstrap, ScreenKey.PersonalToday, ScreenKey.Network, ScreenKey.Settings), executable.toSet())
+ assertEquals(LayoutKind.Canvas, ScreenKey.Bootstrap.descriptor.layout)
+ assertTrue(executable.filterNot { it == ScreenKey.Bootstrap }.all { it.descriptor.layout == LayoutKind.Dashboard })
+ assertEquals(FeatureAvailability.FoundationSyntaxOnly, OverlayKey.OpenNostrReference.descriptor.availability)
+ }
+}
+
+private val expectedScreenKeys =
+ setOf(
+ "bootstrap_screen",
+ "signer_connection_screen",
+ "startup_recovery_screen",
+ "runtime_recovery_screen",
+ "protocol_compatibility_screen",
+ "signing_review_screen",
+ "proof_chain_screen",
+ "fulfillment_issue_screen",
+ "personal_today_screen",
+ "explore_screen",
+ "farm_profile_screen",
+ "circle_screen",
+ "activity_screen",
+ "commitment_screen",
+ "allocation_screen",
+ "farm_overview_screen",
+ "round_studio_screen",
+ "live_round_screen",
+ "pickup_desk_screen",
+ "round_outcome_screen",
+ "network_screen",
+ "settings_screen",
+ )
+
+private val expectedOverlayKeys =
+ setOf(
+ "proof_inspector_panel",
+ "open_nostr_reference_dialog",
+ "relay_editor_dialog",
+ "authority_selector_dialog",
+ "locality_editor_dialog",
+ "share_round_dialog",
+ "confirm_action_dialog",
+ "signer_status_popover",
+ "sync_status_popover",
+ "global_status_banner",
+ )