app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

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:
Mapp/desktop/src/test/kotlin/org/harvestcircle/architecture/ProductNamespaceGuardTest.kt | 4++++
Aapp/shared/src/commonMain/kotlin/org/harvestcircle/product/SurfaceRegistry.kt | 253+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/shared/src/commonTest/kotlin/org/harvestcircle/product/SurfaceRegistryTest.kt | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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", + )