commit f7de844fc6c3833faa31e42ac7abf287d18fe32b
parent 1187de3e2407ead7565bb52237f9ffbb8f2d53cb
Author: triesap <tyson@radroots.org>
Date: Thu, 13 Aug 2026 15:11:08 +0000
policy: admit HarvestCircle design provenance
- freeze the governed visual snapshot identity and source state
- map every approved source cohort to its owned destination
- reject copy product text and unsupported web platform inputs
- add a standalone design-source audit to qualification
Diffstat:
4 files changed, 329 insertions(+), 6 deletions(-)
diff --git a/Makefile b/Makefile
@@ -17,10 +17,13 @@ else
override BUILD_RUNNER :=
endif
-.PHONY: help doctor governed-doctor lock metadata build-logic-check build-logic-stability-check mode-check format format-fix lint test check governed-check build bindings dev-check dev run audit licenses foundation-check package host-package-check governed-package-check source-check governed-source-check package-check integration-check governed-integration-check acceptance-check signing-check _signing-check notarization-check _notarization-check release-check _release-check clean
+.PHONY: help doctor governed-doctor lock metadata build-logic-check build-logic-stability-check mode-check design-source-check format format-fix lint test check governed-check build bindings dev-check dev run audit licenses foundation-check package host-package-check governed-package-check source-check governed-source-check package-check integration-check governed-integration-check acceptance-check signing-check _signing-check notarization-check _notarization-check release-check _release-check clean
help:
- @printf '%s\n' doctor governed-doctor lock metadata build-logic-check build-logic-stability-check mode-check format format-fix lint test check governed-check build bindings dev-check dev run audit licenses foundation-check package host-package-check governed-package-check source-check governed-source-check package-check integration-check governed-integration-check acceptance-check signing-check notarization-check release-check clean
+ @printf '%s\n' doctor governed-doctor lock metadata build-logic-check build-logic-stability-check mode-check design-source-check format format-fix lint test check governed-check build bindings dev-check dev run audit licenses foundation-check package host-package-check governed-package-check source-check governed-source-check package-check integration-check governed-integration-check acceptance-check signing-check notarization-check release-check clean
+
+design-source-check: doctor
+ HARVESTCIRCLE_BUILD_MODE=$(BUILD_MODE) $(BUILD_RUNNER) $(CARGO) run --manifest-path $(XTASK_MANIFEST) --locked -- design-source-audit
doctor:
@printf '%s\n' "harvestcircle.build.mode=$(BUILD_MODE)"
@@ -102,7 +105,7 @@ licenses: doctor
$(BUILD_RUNNER) $(CARGO) deny --manifest-path $(CARGO_MANIFEST) check --config core/deny.toml licenses sources
$(BUILD_RUNNER) $(GRADLE) --no-daemon --no-parallel --no-configuration-cache :app:desktop:checkLicense
-foundation-check: doctor
+foundation-check: design-source-check
HARVESTCIRCLE_BUILD_MODE=$(BUILD_MODE) $(BUILD_RUNNER) $(CARGO) run --manifest-path $(XTASK_MANIFEST) --locked -- qualification-report
package: check
diff --git a/config/design/source_baseline_v1.toml b/config/design/source_baseline_v1.toml
@@ -0,0 +1,66 @@
+schema = "harvestcircle.design_source_baseline.v1"
+source_product = "Studio"
+source_repository = "https://github.com/radrootslabs/studio_app"
+source_head = "768279b16a1256a4432a692b4f74582ab22eb2f4"
+source_state = "clean"
+snapshot_sha256 = "914c5d3b81c95bded19e36044cece8cff93e74cce0d4089324bb2effa4eebb93"
+snapshot_file_count = 91
+source_license = "GPL-3.0-only"
+golden_host = "macos-aarch64"
+golden_status = "pending"
+golden_source_snapshot_sha256 = "914c5d3b81c95bded19e36044cece8cff93e74cce0d4089324bb2effa4eebb93"
+
+[[mapping]]
+source = "core/designsystem/src/commonMain"
+destination = "app/design_system/src/commonMain"
+disposition = "owned-port"
+
+[[mapping]]
+source = "core/designsystem/src/commonTest"
+destination = "app/design_system/src/commonTest"
+disposition = "owned-port"
+
+[[mapping]]
+source = "core/designsystem/src/jvmMain"
+destination = "app/design_system/src/desktopMain"
+disposition = "owned-port"
+
+[[mapping]]
+source = "tools/designcatalog/src/commonMain"
+destination = "tools/design_catalog/src/commonMain"
+disposition = "owned-port"
+
+[[mapping]]
+source = "tools/designcatalog/src/jvmMain"
+destination = "tools/design_catalog/src/desktopMain"
+disposition = "owned-port"
+
+[[mapping]]
+source = "shared/src/commonMain/kotlin/com/radroots/studio/ui/shell"
+destination = "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell"
+disposition = "visual-reference"
+
+[[mapping]]
+source = "shared/src/commonTest/kotlin/com/radroots/studio/ui/shell"
+destination = "app/shared/src/commonTest/kotlin/org/harvestcircle/ui/shell"
+disposition = "test-reference"
+
+[[mapping]]
+source = "shared/src/jvmMain/kotlin/com/radroots/studio/ui/shell"
+destination = "app/shared/src/desktopMain/kotlin/org/harvestcircle/ui/shell"
+disposition = "visual-reference"
+
+[[mapping]]
+source = "shared/src/commonMain/kotlin/com/radroots/studio/ui/dashboard"
+destination = "audit-only/dashboard-visual-inputs"
+disposition = "audit-only"
+
+[[mapping]]
+source = "shared/src/commonMain/composeResources/values/strings.xml"
+destination = "audit-only/product-copy"
+disposition = "reject-product-copy"
+
+[[mapping]]
+source = "shared/src/webMain/kotlin/com/radroots/studio/ui/shell"
+destination = "audit-only/web-target"
+disposition = "reject-platform-target"
diff --git a/tools/xtask/src/lib.rs b/tools/xtask/src/lib.rs
@@ -6,6 +6,7 @@ use std::str::FromStr;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Command {
+ DesignSourceAudit,
RepoAudit,
NamespaceAudit,
ProvenanceCheck,
@@ -17,6 +18,7 @@ impl FromStr for Command {
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
+ "design-source-audit" => Ok(Self::DesignSourceAudit),
"repo-audit" => Ok(Self::RepoAudit),
"namespace-audit" => Ok(Self::NamespaceAudit),
"provenance-check" => Ok(Self::ProvenanceCheck),
@@ -39,6 +41,7 @@ pub fn run(root: &Path, command: Command) -> Result<String, Vec<String>> {
let inventory = Inventory::load(root).map_err(|finding| vec![finding])?;
let mut findings = Vec::new();
match command {
+ Command::DesignSourceAudit => design_source_audit(root, &inventory, &mut findings),
Command::RepoAudit => repo_audit(root, &inventory, &mut findings),
Command::NamespaceAudit => namespace_audit(root, &inventory, &mut findings),
Command::ProvenanceCheck => provenance_check(root, &inventory, &mut findings),
@@ -46,6 +49,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);
+ design_source_audit(root, &inventory, &mut findings);
product_shell_audit(root, &inventory, &mut findings);
}
}
@@ -58,6 +62,7 @@ pub fn run(root: &Path, command: Command) -> Result<String, Vec<String>> {
"archive"
};
let command_name = match command {
+ Command::DesignSourceAudit => "design-source-audit",
Command::RepoAudit => "repo-audit",
Command::NamespaceAudit => "namespace-audit",
Command::ProvenanceCheck => "provenance-check",
@@ -229,6 +234,8 @@ fn repo_audit(root: &Path, inventory: &Inventory, findings: &mut Vec<String>) {
fn namespace_audit(root: &Path, inventory: &Inventory, findings: &mut Vec<String>) {
let legacy = ["stu", "dio"].concat();
let provenance_path = format!("core/provenance/{legacy}-import-v1.toml");
+ let design_provenance_path = "config/design/source_baseline_v1.toml";
+ let design_audit_path = "tools/xtask/src/lib.rs";
let legacy_repository = format!("https://github.com/radrootslabs/{legacy}_app");
let temporary_namespace = ["org", "radroots", "harvestcircle"].join(".");
let inherited_preferences = [
@@ -243,7 +250,10 @@ fn namespace_audit(root: &Path, inventory: &Inventory, findings: &mut Vec<String
];
for path in &inventory.paths {
let normalized = path.to_ascii_lowercase();
- if path != &provenance_path && normalized.contains(&legacy) {
+ if path != &provenance_path
+ && path != design_provenance_path
+ && normalized.contains(&legacy)
+ {
findings.push(format!("{path}: legacy product name in source path"));
}
if normalized.starts_with("app/")
@@ -264,7 +274,7 @@ fn namespace_audit(root: &Path, inventory: &Inventory, findings: &mut Vec<String
continue;
}
let source = read_text(root, path);
- if path != &provenance_path {
+ if path != &provenance_path && path != design_provenance_path {
let mut inspected = source.replace(
if path == "core/Cargo.toml" {
&legacy_repository
@@ -284,6 +294,43 @@ fn namespace_audit(root: &Path, inventory: &Inventory, findings: &mut Vec<String
for exact in approved_legacy_product_fragments(path, &legacy, &legacy_repository) {
inspected = inspected.replace(&exact, "");
}
+ if path == design_audit_path {
+ inspected = inspected
+ .replace("design-source-audit", "")
+ .replace("design_source_audit", "")
+ .replace("DesignSourceAudit", "")
+ .replace("design_source_mappings", "")
+ .replace("push_design_mapping", "")
+ .replace("current_design_source_baseline_is_exact", "")
+ .replace(
+ "design_source_baseline_rejects_snapshot_and_mapping_drift",
+ "",
+ )
+ .replace(design_provenance_path, "")
+ .replace("harvestcircle.design_source_baseline.v1", "")
+ .replace(&format!("source_product = \"{}\"", title_case(&legacy)), "")
+ .replace(&legacy_repository, "")
+ .replace(
+ "shared/src/commonMain/kotlin/com/radroots/studio/ui/shell",
+ "",
+ )
+ .replace(
+ "shared/src/commonTest/kotlin/com/radroots/studio/ui/shell",
+ "",
+ )
+ .replace("shared/src/jvmMain/kotlin/com/radroots/studio/ui/shell", "")
+ .replace(
+ "shared/src/commonMain/kotlin/com/radroots/studio/ui/dashboard",
+ "",
+ )
+ .replace("shared/src/webMain/kotlin/com/radroots/studio/ui/shell", "")
+ .replace("audit-only/dashboard-visual-inputs", "")
+ .replace("audit-only/product-copy", "")
+ .replace("audit-only/web-target", "")
+ .replace("fixture(\"design-source\")", "")
+ .replace(r#"source_product = \"Studio\""#, "")
+ .replace(&format!("\"{}\"", title_case(&legacy)), "");
+ }
if inspected.to_ascii_lowercase().contains(&legacy) {
findings.push(format!(
"{path}: legacy product name outside the exact provenance allowlist"
@@ -802,6 +849,172 @@ fn provenance_check(root: &Path, inventory: &Inventory, findings: &mut Vec<Strin
}
}
+fn design_source_audit(root: &Path, inventory: &Inventory, findings: &mut Vec<String>) {
+ const PATH: &str = "config/design/source_baseline_v1.toml";
+ if !inventory.paths.iter().any(|path| path == PATH) {
+ findings.push(format!("{PATH}: design source baseline is missing"));
+ return;
+ }
+ let source = read_text(root, PATH);
+ let expected_snapshot = "914c5d3b81c95bded19e36044cece8cff93e74cce0d4089324bb2effa4eebb93";
+ let required_scalars = [
+ "schema = \"harvestcircle.design_source_baseline.v1\"",
+ "source_product = \"Studio\"",
+ "source_repository = \"https://github.com/radrootslabs/studio_app\"",
+ "source_head = \"768279b16a1256a4432a692b4f74582ab22eb2f4\"",
+ "source_state = \"clean\"",
+ "snapshot_file_count = 91",
+ "source_license = \"GPL-3.0-only\"",
+ "golden_host = \"macos-aarch64\"",
+ "golden_status = \"pending\"",
+ ];
+ for scalar in required_scalars {
+ if source.lines().filter(|line| line.trim() == scalar).count() != 1 {
+ findings.push(format!("{PATH}: missing or duplicate authority: {scalar}"));
+ }
+ }
+ for key in ["snapshot_sha256", "golden_source_snapshot_sha256"] {
+ let expected = format!("{key} = \"{expected_snapshot}\"");
+ if source
+ .lines()
+ .filter(|line| line.trim() == expected)
+ .count()
+ != 1
+ {
+ findings.push(format!("{PATH}: {key} must match the governed snapshot"));
+ }
+ }
+ let mappings = design_source_mappings(&source, PATH, findings);
+ let required = [
+ (
+ "core/designsystem/src/commonMain",
+ "app/design_system/src/commonMain",
+ "owned-port",
+ ),
+ (
+ "core/designsystem/src/commonTest",
+ "app/design_system/src/commonTest",
+ "owned-port",
+ ),
+ (
+ "core/designsystem/src/jvmMain",
+ "app/design_system/src/desktopMain",
+ "owned-port",
+ ),
+ (
+ "tools/designcatalog/src/commonMain",
+ "tools/design_catalog/src/commonMain",
+ "owned-port",
+ ),
+ (
+ "tools/designcatalog/src/jvmMain",
+ "tools/design_catalog/src/desktopMain",
+ "owned-port",
+ ),
+ (
+ "shared/src/commonMain/kotlin/com/radroots/studio/ui/shell",
+ "app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell",
+ "visual-reference",
+ ),
+ (
+ "shared/src/commonTest/kotlin/com/radroots/studio/ui/shell",
+ "app/shared/src/commonTest/kotlin/org/harvestcircle/ui/shell",
+ "test-reference",
+ ),
+ (
+ "shared/src/jvmMain/kotlin/com/radroots/studio/ui/shell",
+ "app/shared/src/desktopMain/kotlin/org/harvestcircle/ui/shell",
+ "visual-reference",
+ ),
+ (
+ "shared/src/commonMain/kotlin/com/radroots/studio/ui/dashboard",
+ "audit-only/dashboard-visual-inputs",
+ "audit-only",
+ ),
+ (
+ "shared/src/commonMain/composeResources/values/strings.xml",
+ "audit-only/product-copy",
+ "reject-product-copy",
+ ),
+ (
+ "shared/src/webMain/kotlin/com/radroots/studio/ui/shell",
+ "audit-only/web-target",
+ "reject-platform-target",
+ ),
+ ];
+ let expected = required
+ .iter()
+ .map(|(source, destination, disposition)| {
+ (
+ (*source).to_owned(),
+ (*destination).to_owned(),
+ (*disposition).to_owned(),
+ )
+ })
+ .collect::<BTreeSet<_>>();
+ if mappings != expected {
+ findings.push(format!(
+ "{PATH}: source-to-owned mapping differs from the approved migration"
+ ));
+ }
+}
+
+fn design_source_mappings(
+ source: &str,
+ path: &str,
+ findings: &mut Vec<String>,
+) -> BTreeSet<(String, String, String)> {
+ let mut mappings = BTreeSet::new();
+ let mut current = Vec::new();
+ for line in source.lines().map(str::trim) {
+ if line == "[[mapping]]" {
+ if !current.is_empty() {
+ push_design_mapping(&mut mappings, ¤t, path, findings);
+ current.clear();
+ }
+ } else if !line.is_empty()
+ && line.contains(" = ")
+ && (!current.is_empty() || line.starts_with("source = "))
+ {
+ current.push(line.to_owned());
+ }
+ }
+ if !current.is_empty() {
+ push_design_mapping(&mut mappings, ¤t, path, findings);
+ }
+ mappings
+}
+
+fn push_design_mapping(
+ mappings: &mut BTreeSet<(String, String, String)>,
+ lines: &[String],
+ path: &str,
+ findings: &mut Vec<String>,
+) {
+ let value = |key: &str| {
+ lines
+ .iter()
+ .find_map(|line| {
+ line.strip_prefix(&format!("{key} = \""))
+ .and_then(|value| value.strip_suffix('"'))
+ })
+ .unwrap_or_default()
+ .to_owned()
+ };
+ let mapping = (value("source"), value("destination"), value("disposition"));
+ if mapping.0.is_empty()
+ || mapping.1.is_empty()
+ || mapping.2.is_empty()
+ || mapping.0.starts_with('/')
+ || mapping.1.starts_with('/')
+ || mapping.0.split('/').any(|part| part == "..")
+ || mapping.1.split('/').any(|part| part == "..")
+ || !mappings.insert(mapping)
+ {
+ findings.push(format!("{path}: invalid or duplicate source mapping"));
+ }
+}
+
fn git_source_policy(root: &Path, findings: &mut Vec<String>) {
let deny = read_text(root, "core/deny.toml");
if !deny
@@ -997,6 +1210,10 @@ mod tests {
#[test]
fn commands_are_exact_and_unknown_values_fail_closed() {
+ assert_eq!(
+ "design-source-audit".parse(),
+ Ok(Command::DesignSourceAudit)
+ );
assert_eq!("repo-audit".parse(), Ok(Command::RepoAudit));
assert_eq!("namespace-audit".parse(), Ok(Command::NamespaceAudit));
assert_eq!("provenance-check".parse(), Ok(Command::ProvenanceCheck));
@@ -1008,6 +1225,43 @@ mod tests {
}
#[test]
+ fn current_design_source_baseline_is_exact() {
+ let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
+ .parent()
+ .and_then(Path::parent)
+ .expect("repository root")
+ .to_path_buf();
+ let inventory = Inventory::load(&root).expect("source inventory");
+ let mut findings = Vec::new();
+ design_source_audit(&root, &inventory, &mut findings);
+ assert!(findings.is_empty(), "{findings:#?}");
+ }
+
+ #[test]
+ fn design_source_baseline_rejects_snapshot_and_mapping_drift() {
+ let root = fixture("design-source");
+ write(
+ &root,
+ "config/design/source_baseline_v1.toml",
+ "schema = \"harvestcircle.design_source_baseline.v1\"\n[[mapping]]\nsource = \"../escape\"\ndestination = \"/tmp\"\ndisposition = \"owned-port\"\n",
+ );
+ let inventory = Inventory::load(&root).expect("archive inventory");
+ let mut findings = Vec::new();
+ design_source_audit(&root, &inventory, &mut findings);
+ assert!(
+ findings
+ .iter()
+ .any(|finding| finding.contains("governed snapshot"))
+ );
+ assert!(
+ findings
+ .iter()
+ .any(|finding| finding.contains("invalid or duplicate source mapping"))
+ );
+ fs::remove_dir_all(root).expect("remove fixture");
+ }
+
+ #[test]
fn archive_inventory_excludes_outputs_and_includes_source() {
let root = fixture("archive");
write(&root, "src/main.rs", "fn main() {}\n");
diff --git a/tools/xtask/src/main.rs b/tools/xtask/src/main.rs
@@ -6,7 +6,7 @@ fn main() -> ExitCode {
let mut arguments = env::args().skip(1);
let Some(command) = arguments.next() else {
eprintln!(
- "usage: cargo run --manifest-path tools/xtask/Cargo.toml -- <repo-audit|namespace-audit|provenance-check|qualification-report>"
+ "usage: cargo run --manifest-path tools/xtask/Cargo.toml -- <design-source-audit|repo-audit|namespace-audit|provenance-check|qualification-report>"
);
return ExitCode::FAILURE;
};