app

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

commit 76d8a2b9bcc36db05670389930c1df70a841cffd
parent 23b2cddadac96bba15992fe6332bbc4c73865ec5
Author: triesap <tyson@radroots.org>
Date:   Tue, 11 Aug 2026 17:11:31 +0000

test: qualify the HarvestCircle product shell

- Require the complete shell source set and locked foundation copy in qualification.
- Bound legacy and placeholder exceptions to exact approved paths and values.
- Reject hard-coded shell colors and fake commercial product-data markers.
- Verify source readiness, audits, bindings, licences, formatting, and all tests.

Diffstat:
Mapp/shared/src/desktopTest/kotlin/org/harvestcircle/ui/shell/MainPanelTemplatesTest.kt | 2+-
Mtools/xtask/src/lib.rs | 177++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 177 insertions(+), 2 deletions(-)

diff --git a/app/shared/src/desktopTest/kotlin/org/harvestcircle/ui/shell/MainPanelTemplatesTest.kt b/app/shared/src/desktopTest/kotlin/org/harvestcircle/ui/shell/MainPanelTemplatesTest.kt @@ -23,7 +23,7 @@ class MainPanelTemplatesTest { } @Test - fun tabbedAndStudioTemplatesExposeOnlyStructuralSlots() = + fun tabbedAndWorkbenchTemplatesExposeOnlyStructuralSlots() = runComposeUiTest { val tab = TemplateTab(TemplateSelectionKey("overview"), "Overview") setContent { diff --git a/tools/xtask/src/lib.rs b/tools/xtask/src/lib.rs @@ -46,6 +46,7 @@ pub fn run(root: &Path, command: Command) -> Result<String, Vec<String>> { repo_audit(root, &inventory, &mut findings); namespace_audit(root, &inventory, &mut findings); provenance_check(root, &inventory, &mut findings); + product_shell_audit(root, &inventory, &mut findings); } } findings.sort(); @@ -283,6 +284,9 @@ fn namespace_audit(root: &Path, inventory: &Inventory, findings: &mut Vec<String ) .replace(&provenance_path, ""); } + for exact in approved_legacy_product_fragments(path, &legacy, &legacy_repository) { + inspected = inspected.replace(&exact, ""); + } if inspected.to_ascii_lowercase().contains(&legacy) { findings.push(format!( "{path}: legacy product name outside the exact provenance allowlist" @@ -334,12 +338,136 @@ fn namespace_audit(root: &Path, inventory: &Inventory, findings: &mut Vec<String findings.push(format!("{path}: inherited non-product preference {token}")); } } - if production_kotlin && lowercase.contains(&["nsec", "1"].concat()) { + let secret_marker = ["nsec", "1"].concat(); + let inspected_secret = lowercase.replace(&format!("{secret_marker}…"), ""); + if production_kotlin && inspected_secret.contains(&secret_marker) { findings.push(format!("{path}: secret key literal in production Kotlin")); } } } +fn approved_legacy_product_fragments( + path: &str, + legacy: &str, + legacy_repository: &str, +) -> Vec<String> { + match path { + "app/shared/src/commonMain/kotlin/org/harvestcircle/product/SurfaceRegistry.kt" => vec![ + format!("round_{legacy}_screen"), + format!("Round{}", title_case(legacy)), + ], + "app/shared/src/commonTest/kotlin/org/harvestcircle/product/SurfaceRegistryTest.kt" => { + vec![format!("round_{legacy}_screen")] + } + "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/MainPanelTemplates.kt" + | "app/shared/src/desktopTest/kotlin/org/harvestcircle/ui/shell/MainPanelTemplatesTest.kt" => + { + vec![format!("{}Template", title_case(legacy))] + } + "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/FoundationSettingsScreen.kt" => + { + vec![legacy_repository.to_owned()] + } + _ => Vec::new(), + } +} + +fn product_shell_audit(root: &Path, inventory: &Inventory, findings: &mut Vec<String>) { + let required = [ + "app/shared/src/commonMain/kotlin/org/harvestcircle/product/SurfaceRegistry.kt", + "app/shared/src/commonMain/kotlin/org/harvestcircle/navigation/Navigation.kt", + "app/shared/src/commonMain/kotlin/org/harvestcircle/design/HarvestCircleDesign.kt", + "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/HarvestCircleShell.kt", + "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/FoundationTodayScreen.kt", + "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/FoundationNetworkScreen.kt", + "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/FoundationSettingsScreen.kt", + "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/ShellAccessibility.kt", + ]; + for path in required { + if !inventory.paths.iter().any(|candidate| candidate == path) { + findings.push(format!("{path}: required product-shell source is missing")); + } + } + let locked_copy = [ + ( + "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/HarvestCircleShell.kt", + &[ + "Coordinate local food with clear, signed terms.", + "You do not need a HarvestCircle account.", + "Open source · Nostr-based · No managed service required", + ][..], + ), + ( + "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/FoundationTodayScreen.kt", + &[ + "No active commitments", + "Explore nearby buying circles or open a shared Nostr reference.", + "Not available in this build.", + ][..], + ), + ( + "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/FoundationNetworkScreen.kt", + &[ + "Overview", + "Identity", + "Public relays", + "Runtime", + "No managed HarvestCircle service is configured.", + ][..], + ), + ( + "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/FoundationSettingsScreen.kt", + &[ + "Appearance", + "Project", + "Theme", + "Text size", + "Motion", + "FFI contract", + "Storage schema", + ][..], + ), + ]; + for (path, expected) in locked_copy { + let source = read_text(root, path); + for text in expected { + if !source.contains(text) { + findings.push(format!( + "{path}: locked product-shell copy is missing: {text}" + )); + } + } + } + for path in &inventory.paths { + if !path.starts_with("app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/") + || !path.ends_with(".kt") + { + continue; + } + let source = read_text(root, path); + if path != "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/GlobalTopBar.kt" + && source.contains("Color(0x") + { + findings.push(format!( + "{path}: hard-coded shell color outside the token adapter" + )); + } + let lowercase = source.to_ascii_lowercase(); + for marker in [ + "sample farm", + "sample commitment", + "pricecents", + "commitmentid", + ] { + if lowercase.contains(marker) { + findings.push(format!( + "{path}: fake commercial product data marker {marker}" + )); + } + } + } +} + fn provenance_check(root: &Path, inventory: &Inventory, findings: &mut Vec<String>) { let legacy = ["stu", "dio"].concat(); let legacy_repository = format!("https://github.com/radrootslabs/{legacy}_app"); @@ -718,6 +846,53 @@ mod tests { fs::remove_dir_all(root).expect("remove fixture"); } + #[test] + fn namespace_policy_allows_only_exact_locked_legacy_contracts_and_placeholder() { + let root = fixture("namespace-allowlist"); + let legacy = ["stu", "dio"].concat(); + let repository = format!("https://github.com/radrootslabs/{legacy}_app"); + let registry = + "app/shared/src/commonMain/kotlin/org/harvestcircle/product/SurfaceRegistry.kt"; + let entry = + "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/BootstrapIdentityEntry.kt"; + write( + &root, + registry, + &format!( + "val key = \"round_{legacy}_screen\"\nclass Round{}\n", + title_case(&legacy) + ), + ); + write(&root, entry, "val placeholder = \"nsec1…\"\n"); + write( + &root, + "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/FoundationSettingsScreen.kt", + &format!( + "val source = \"{repository}\"\nval licence = \"{repository}/blob/dev/LICENSE\"\n" + ), + ); + let inventory = Inventory::load(&root).expect("allowlist inventory"); + let mut findings = Vec::new(); + namespace_audit(&root, &inventory, &mut findings); + assert!(findings.is_empty(), "{findings:?}"); + fs::remove_dir_all(root).expect("remove fixture"); + } + + #[test] + fn product_shell_audit_requires_the_complete_source_contract() { + let root = fixture("product-shell"); + write(&root, "README.md", "safe\n"); + let inventory = Inventory::load(&root).expect("product shell inventory"); + let mut findings = Vec::new(); + product_shell_audit(&root, &inventory, &mut findings); + assert!( + findings + .iter() + .any(|finding| finding.contains("required product-shell source is missing")) + ); + fs::remove_dir_all(root).expect("remove fixture"); + } + #[cfg(unix)] #[test] fn repository_policy_rejects_symbolic_links() {