app

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

commit 91a7e978e7ea540e440b35347af6f432ffecd775
parent 653dd4ee17e9577f4e095aeb0e7bb0321fb7ecbe
Author: triesap <tyson@radroots.org>
Date:   Wed, 26 Aug 2026 16:47:20 +0000

build: qualify development integration

- Pin macOS and Linux development checks to exact toolchains and source evidence.
- Enforce the public storage API and single SQLx-selected SQLite topology.
- Keep advisory, packaging, release, signing, Nix, and OCI work deferred.
- Repair compatibility, shutdown, and integration regressions found by the full lane.

Diffstat:
MAGENTS.md | 25++++++++++++++++---------
MMakefile | 30++++++++++++++++++++++++++----
MREADME.md | 16+++++++++++-----
Mapp/desktop/src/integrationTest/kotlin/org/harvestcircle/integration/IdentityBootstrapAcceptanceTest.kt | 102+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mapp/desktop/src/integrationTest/kotlin/org/harvestcircle/integration/NativeRuntimeIntegrationTest.kt | 28+++++++++++++++++++++-------
Mapp/desktop/src/main/kotlin/org/harvestcircle/application/NativeCompatibility.kt | 2++
Mbuild-logic/plugins/src/main/kotlin/org/harvestcircle/buildlogic/plugins/HarvestCircleRootPlugin.kt | 2+-
Mbuild-logic/plugins/src/main/kotlin/org/harvestcircle/buildlogic/plugins/tasks/VerificationLanes.kt | 64++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mbuild-logic/plugins/src/test/kotlin/org/harvestcircle/buildlogic/plugins/tasks/VerificationLanesTest.kt | 75++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Dconfig/verification/lanes-v2.properties | 24------------------------
Aconfig/verification/lanes-v3.properties | 25+++++++++++++++++++++++++
Mcore/crates/harvestcircle_ffi/src/host_runtime.rs | 4+---
Mcore/crates/harvestcircle_ffi/src/keyring_worker.rs | 4+---
Mgradle/verification-metadata.xml | 13+++++++++++++
Atools/run-linux-x86_64-development-check.sh | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/test-build-modes.sh | 22++++++++++++++++++++--
Atools/verify-storage-api.sh | 15+++++++++++++++
Mtools/xtask/src/lib.rs | 182++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
18 files changed, 651 insertions(+), 137 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md @@ -140,15 +140,22 @@ Gradle otherwise uses its standard ignored `build/` directories and Cargo uses the ignored target trees. Run `make doctor` before the first standalone mutating lane and `make governed-doctor` before a governed lane. Use `make format`, `make lint`, and `make test` while iterating, and run `make check` or -`make governed-check` for a complete source checkpoint. Signing, notarization, -and release targets are governed-only. Unknown build modes fail before build -mutation. - -Shared public Git dependencies and package or advisory lanes may require -external services. Do not weaken immutable inputs or silently switch sources -when offline. Never claim a lane passed unless it ran successfully; report -network, toolchain, platform, advisory-service, package, or external-artifact -blockers exactly. +`make governed-check` for a complete source checkpoint. The active development +milestone is qualified with `make governed-development-check` on macOS aarch64 +and `make governed-linux-x86_64-development-check` for the faithful Linux +x86_64 lane. These development targets must retain source, runtime, generated, +API, source-lock, SQLx-topology, and offline license/source verification without +activating advisory retrieval, package assembly, release evidence, signing, +notarization, Nix, or OCI work. Signing, notarization, and release targets are +governed-only and require a separately declared release candidate and fresh +authority. Unknown build modes fail before build mutation. + +Shared public Git dependencies and deferred package or advisory lanes may +require external services. Do not weaken immutable inputs or silently switch +sources when offline. During the active development milestone, do not run or +claim the deferred release integrations merely because they remain available +as explicit targets. Never claim a lane passed unless it ran successfully; +report network, toolchain, platform, or external-artifact blockers exactly. Verify exact manifests/lock agreement, generated binding and native artifact freshness when affected, compatibility and architecture guards, diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ .DEFAULT_GOAL := help GRADLE ?= ./gradlew -CARGO ?= cargo +override CARGO := cargo +1.97.1 CARGO_MANIFEST := core/Cargo.toml XTASK_MANIFEST := tools/xtask/Cargo.toml BUILD_MODE ?= standalone @@ -17,10 +17,10 @@ else override BUILD_RUNNER := endif -.PHONY: help doctor governed-doctor lock metadata build-logic-check build-logic-stability-check mode-check design-source-check design-goldens-update 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 host-ui-lifecycle-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 design-goldens-update format format-fix lint test check governed-check build bindings api-check 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 development-provenance-check development-check governed-development-check governed-linux-x86_64-development-check host-ui-lifecycle-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 design-source-check design-goldens-update 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 host-ui-lifecycle-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 design-goldens-update format format-fix lint test check governed-check build bindings api-check 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 development-check governed-development-check governed-linux-x86_64-development-check host-ui-lifecycle-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 @@ -90,6 +90,9 @@ build: doctor bindings: doctor $(BUILD_RUNNER) $(GRADLE) --no-daemon :app:desktop:verifyUniFfiBindings :app:desktop:verifyReleaseNativeLibrary +api-check: doctor + $(BUILD_RUNNER) tools/verify-storage-api.sh + dev-check: doctor $(BUILD_RUNNER) $(GRADLE) --no-daemon --configuration-cache --configuration-cache-problems=fail :app:desktop:hotRunArgfile @@ -120,7 +123,7 @@ host-package-check: doctor governed-package-check: $(MAKE) --no-print-directory BUILD_MODE=governed host-package-check -source-check: build-logic-check check bindings licenses dev-check +source-check: build-logic-check check bindings api-check licenses dev-check $(BUILD_RUNNER) $(GRADLE) --no-daemon :app:desktop:sourceReadiness governed-source-check: @@ -135,6 +138,25 @@ integration-check: build-logic-check check governed-integration-check: $(MAKE) --no-print-directory BUILD_MODE=governed integration-check +development-check: export HARVESTCIRCLE_BUILD_SOURCE_COMMIT = $(shell git rev-parse --verify HEAD) +development-check: export HARVESTCIRCLE_BUILD_SOURCE_DIRTY = $(if $(strip $(shell git status --porcelain --untracked-files=all)),true,false) +development-check: export HARVESTCIRCLE_BUILD_RADROOTS_REVISION = $(shell sed -n 's/^revision = "\([0-9a-f]\{40\}\)"$$/\1/p' radroots.lib.source-lock.v1.toml) +development-check: export HARVESTCIRCLE_BUILD_RUST_TOOLCHAIN = 1.97.1 +development-check: export SOURCE_DATE_EPOCH = $(shell git show -s --format=%ct HEAD) +development-check: development-provenance-check source-check integration-check + +development-provenance-check: + @test "$${#HARVESTCIRCLE_BUILD_SOURCE_COMMIT}" -eq 40 + @test "$${#HARVESTCIRCLE_BUILD_RADROOTS_REVISION}" -eq 40 + @case "$$SOURCE_DATE_EPOCH" in ''|*[!0-9]*) exit 2 ;; esac + @test "$$HARVESTCIRCLE_BUILD_SOURCE_DIRTY" = true -o "$$HARVESTCIRCLE_BUILD_SOURCE_DIRTY" = false + +governed-development-check: + $(MAKE) --no-print-directory BUILD_MODE=governed development-check + +governed-linux-x86_64-development-check: governed-doctor + $(CARGO) extbuild run -- tools/run-linux-x86_64-development-check.sh + host-ui-lifecycle-check: doctor $(BUILD_RUNNER) $(GRADLE) --no-daemon :app:desktop:hostUiLifecycleTest diff --git a/README.md b/README.md @@ -16,7 +16,7 @@ commercial use. - canonical service-instance persistence through the governed SQLx host; - configurable Nostr relay bootstrap; - compatibility-gated native startup; -- reproducible source and package verification. +- reproducible source and development qualification. ## Sovereign direction @@ -33,13 +33,19 @@ Prerequisites include JDK 21, Rust 1.97.1, and platform packaging tools. make doctor make check make build -make package +make governed-development-check ``` These commands always use the standalone contributor lane. Use -`make governed-check`, `make governed-integration-check`, or -`make governed-package-check` when extbuild-governed output routing is -required. Release, signing, and notarization checks are governed-only. +`make governed-check` or `make governed-integration-check` when a narrower +extbuild-governed lane is required. The full active development milestone uses +`make governed-development-check` on macOS aarch64 and +`make governed-linux-x86_64-development-check` for Linux x86_64. It verifies +source, runtime, generated bindings, the public storage API, the exact Radroots +source lock, the single SQLx-selected SQLite linkage, and offline license/source +policy. Network advisory services, package assembly, release evidence, signing, +notarization, Nix, and OCI qualification remain deferred and unclaimed until a +release candidate is declared with fresh authority. ## Development branch diff --git a/app/desktop/src/integrationTest/kotlin/org/harvestcircle/integration/IdentityBootstrapAcceptanceTest.kt b/app/desktop/src/integrationTest/kotlin/org/harvestcircle/integration/IdentityBootstrapAcceptanceTest.kt @@ -29,6 +29,8 @@ import org.harvestcircle.application.RequestContext import org.harvestcircle.application.SecretClipboardController import org.harvestcircle.application.TextClipboard import org.harvestcircle.application.UnixSeconds +import org.harvestcircle.designsystem.layout.HarvestCircleWindowChromeEnvironment +import org.harvestcircle.designsystem.layout.HarvestCircleWindowChromeExclusion import java.nio.file.Files import java.nio.file.Path import kotlin.io.path.readBytes @@ -53,25 +55,27 @@ class IdentityBootstrapAcceptanceTest { var approvedExits = 0 try { setContent { - if (showApplication) { - key(applicationSession) { - HarvestCircleApplicationWithDependencies( - closeRequested = closeRequested, - onExitApproved = { approvedExits += 1 }, - clipboardFactory = { scope -> - SecretClipboardController( + HarvestCircleWindowChromeEnvironment(HarvestCircleWindowChromeExclusion.None) { + if (showApplication) { + key(applicationSession) { + HarvestCircleApplicationWithDependencies( + closeRequested = closeRequested, + onExitApproved = { approvedExits += 1 }, + clipboardFactory = { scope -> + SecretClipboardController( + scope = scope, + clipboard = clipboard, + clearDelayMillis = 60_000, + ) + }, + ) { scope -> + HarvestCirclePresenter( + runtime = runtime, scope = scope, - clipboard = clipboard, - clearDelayMillis = 60_000, + clock = ApplicationClock { UnixSeconds(FIXED_TIME_SECONDS) }, + operationIds = operationIds, ) - }, - ) { scope -> - HarvestCirclePresenter( - runtime = runtime, - scope = scope, - clock = ApplicationClock { UnixSeconds(FIXED_TIME_SECONDS) }, - operationIds = operationIds, - ) + } } } } @@ -158,25 +162,27 @@ class IdentityBootstrapAcceptanceTest { var approvedExits = 0 try { setContent { - if (showApplication) { - key(applicationSession) { - HarvestCircleApplicationWithDependencies( - closeRequested = closeRequested, - onExitApproved = { approvedExits += 1 }, - clipboardFactory = { scope -> - SecretClipboardController( + HarvestCircleWindowChromeEnvironment(HarvestCircleWindowChromeExclusion.None) { + if (showApplication) { + key(applicationSession) { + HarvestCircleApplicationWithDependencies( + closeRequested = closeRequested, + onExitApproved = { approvedExits += 1 }, + clipboardFactory = { scope -> + SecretClipboardController( + scope = scope, + clipboard = RecordingClipboard(), + clearDelayMillis = 60_000, + ) + }, + ) { scope -> + HarvestCirclePresenter( + runtime = runtime, scope = scope, - clipboard = RecordingClipboard(), - clearDelayMillis = 60_000, + clock = ApplicationClock { UnixSeconds(FIXED_TIME_SECONDS) }, + operationIds = SequentialOperationIds(), ) - }, - ) { scope -> - HarvestCirclePresenter( - runtime = runtime, - scope = scope, - clock = ApplicationClock { UnixSeconds(FIXED_TIME_SECONDS) }, - operationIds = SequentialOperationIds(), - ) + } } } } @@ -253,19 +259,21 @@ class IdentityBootstrapAcceptanceTest { runtime.setNetworkDegraded(true) setContent { - HarvestCircleApplicationWithDependencies( - closeRequested = false, - onExitApproved = {}, - clipboardFactory = { scope -> - SecretClipboardController(scope, RecordingClipboard(), clearDelayMillis = 60_000) - }, - ) { scope -> - HarvestCirclePresenter( - runtime = runtime, - scope = scope, - clock = ApplicationClock { UnixSeconds(FIXED_TIME_SECONDS) }, - operationIds = operationIds, - ) + HarvestCircleWindowChromeEnvironment(HarvestCircleWindowChromeExclusion.None) { + HarvestCircleApplicationWithDependencies( + closeRequested = false, + onExitApproved = {}, + clipboardFactory = { scope -> + SecretClipboardController(scope, RecordingClipboard(), clearDelayMillis = 60_000) + }, + ) { scope -> + HarvestCirclePresenter( + runtime = runtime, + scope = scope, + clock = ApplicationClock { UnixSeconds(FIXED_TIME_SECONDS) }, + operationIds = operationIds, + ) + } } } diff --git a/app/desktop/src/integrationTest/kotlin/org/harvestcircle/integration/NativeRuntimeIntegrationTest.kt b/app/desktop/src/integrationTest/kotlin/org/harvestcircle/integration/NativeRuntimeIntegrationTest.kt @@ -82,9 +82,7 @@ class NativeRuntimeIntegrationTest { val publicEvidence = bridge.snapshot().toString() + secondRead.safeMessage + duplicateAcknowledge.safeMessage assertFalse(publicEvidence.contains(secret)) assertFalse(publicEvidence.contains(cancelledSecret)) - val databaseBytes = dataRoot.resolve("harvestcircle-integration.sqlite3").readBytes() - assertFalse(databaseBytes.containsBytes(secret.encodeToByteArray())) - assertFalse(databaseBytes.containsBytes(cancelledSecret.encodeToByteArray())) + assertTreeDoesNotContain(dataRoot, secret, cancelledSecret) bridge.shutdown() } finally { bridge.close() @@ -240,10 +238,7 @@ class NativeRuntimeIntegrationTest { assertFalse(timeout.contains(timeoutSecret)) assertEquals(2, bridge.snapshot().identities.size) - val databaseBytes = dataRoot.resolve("harvestcircle-integration.sqlite3").readBytes() - assertFalse(databaseBytes.containsBytes(importedSecret.encodeToByteArray())) - assertFalse(databaseBytes.containsBytes(timeoutSecret.encodeToByteArray())) - assertFalse(databaseBytes.containsBytes("nsec1".encodeToByteArray())) + assertTreeDoesNotContain(dataRoot, importedSecret, timeoutSecret, "nsec1") val publicEvidence = bridge.snapshot().toString() + timeout assertFalse(publicEvidence.contains(importedSecret)) assertFalse(publicEvidence.contains(timeoutSecret)) @@ -274,6 +269,25 @@ private fun ByteArray.containsBytes(needle: ByteArray): Boolean = start + needle.size <= size && needle.indices.all { offset -> this[start + offset] == needle[offset] } } +private fun assertTreeDoesNotContain( + root: Path, + vararg values: String, +) { + val needles = values.map(String::encodeToByteArray) + try { + Files.walk(root).use { paths -> + paths + .filter(Files::isRegularFile) + .forEach { file -> + val bytes = file.readBytes() + needles.forEach { needle -> assertFalse(bytes.containsBytes(needle)) } + } + } + } finally { + needles.forEach { it.fill(0) } + } +} + private fun deleteTree(root: Path) { Files.walk(root).use { paths -> paths.sorted(Comparator.reverseOrder()).forEach(Files::deleteIfExists) diff --git a/app/desktop/src/main/kotlin/org/harvestcircle/application/NativeCompatibility.kt b/app/desktop/src/main/kotlin/org/harvestcircle/application/NativeCompatibility.kt @@ -21,6 +21,8 @@ internal fun verifyNativeCompatibility(descriptor: CompatibilityDescriptor): Com descriptor.productCoordinateDigest == Expected.productCoordinateDigest && descriptor.snapshotSchemaVersion == Expected.snapshotSchema && descriptor.currentSchemaVersion >= Expected.minimumStorageSchema && + descriptor.currentSchemaVersion <= Expected.maximumStorageSchema && + descriptor.minimumSchemaVersion <= descriptor.currentSchemaVersion && descriptor.minimumSchemaVersion <= Expected.maximumStorageSchema && descriptor.sourceProvenanceDigest == Expected.sourceProvenanceDigest && descriptor.sourceFoundationBaseline == Expected.sourceFoundationBaseline diff --git a/build-logic/plugins/src/main/kotlin/org/harvestcircle/buildlogic/plugins/HarvestCircleRootPlugin.kt b/build-logic/plugins/src/main/kotlin/org/harvestcircle/buildlogic/plugins/HarvestCircleRootPlugin.kt @@ -12,7 +12,7 @@ public class HarvestCircleRootPlugin : Plugin<Project> { val productCoordinatesFile = target.layout.projectDirectory.file("config/product/harvestcircle-v1.properties") val ffiCompatibilityBaselineFile = target.layout.projectDirectory.file("core/compatibility/harvestcircle-ffi-v4.properties") - val verificationLanesFile = target.layout.projectDirectory.file("config/verification/lanes-v2.properties") + val verificationLanesFile = target.layout.projectDirectory.file("config/verification/lanes-v3.properties") val verifyProductCoordinates = target.tasks.register("verifyProductCoordinates", VerifyProductCoordinates::class.java) { task -> diff --git a/build-logic/plugins/src/main/kotlin/org/harvestcircle/buildlogic/plugins/tasks/VerificationLanes.kt b/build-logic/plugins/src/main/kotlin/org/harvestcircle/buildlogic/plugins/tasks/VerificationLanes.kt @@ -12,7 +12,7 @@ import org.gradle.work.DisableCachingByDefault object VerificationLanes { private fun expected(environmentPrefix: String) = linkedMapOf( - "schema" to "harvestcircle.verification-lanes.v2", + "schema" to "harvestcircle.verification-lanes.v3", "orchestration" to "explicit-make-modes", "source.standalone.command" to "make source-check", "source.governed.command" to "make governed-source-check", @@ -20,22 +20,23 @@ object VerificationLanes { "integration.standalone.command" to "make integration-check", "integration.governed.command" to "make governed-integration-check", "integration.credentials" to "none", - "package.standalone.command" to "make host-package-check", - "package.governed.command" to "make governed-package-check", - "package.runners" to "linux,macos,windows", - "package.credentials" to "none", + "development.standalone.command" to "make development-check", + "development.governed.command" to "make governed-development-check", + "development.macos_aarch64.command" to "make governed-development-check", + "development.linux_x86_64.command" to "make governed-linux-x86_64-development-check", + "development.runners" to "macos-aarch64,linux-x86_64", + "development.credentials" to "none", "provenance.commit" to environmentPrefix + "BUILD_SOURCE_COMMIT", "provenance.dirty" to environmentPrefix + "BUILD_SOURCE_DIRTY", "provenance.radroots" to environmentPrefix + "BUILD_RADROOTS_REVISION", "provenance.epoch" to "SOURCE_DATE_EPOCH", - "signing.command" to "make signing-check", - "signing.runner" to "macos", - "signing.credentials" to "signing", - "notarization.command" to "make notarization-check", - "notarization.runner" to "macos", - "notarization.credentials" to "notarization", - "release.command" to "make release-check", - "release.mode" to "governed", + "release.state" to "deferred-unclaimed", + "release.activation" to "declared-release-candidate-and-fresh-authority", + "release.network_advisories" to "deferred", + "release.packages" to "deferred", + "release.evidence" to "deferred", + "release.signing" to "deferred", + "release.nix_oci" to "deferred", ) fun parse( @@ -61,6 +62,40 @@ object VerificationLanes { require(parsed == expected) { "Verification lane values do not match the authority" } return parsed } + + fun verifyDevelopmentMakefile(source: String) { + check(source.lineSequence().count { it == "override CARGO := cargo +1.97.1" } == 1) { + "Development verification must pin the exact Rust toolchain" + } + val exactTargetHeaders = + listOf( + "source-check: build-logic-check check bindings api-check licenses dev-check", + "integration-check: build-logic-check check", + "development-check: development-provenance-check source-check integration-check", + "governed-development-check:", + "governed-linux-x86_64-development-check: governed-doctor", + ) + exactTargetHeaders.forEach { header -> + check(source.lineSequence().count { it == header } == 1) { + "Development Make target differs from the governed boundary: $header" + } + } + + val developmentHeader = + source + .lineSequence() + .filter { it.startsWith("development-check:") } + .filterNot { it.startsWith("development-check: export ") } + .single() + val forbiddenDependencies = + listOf("audit", "package", "release", "signing", "notarization", "sbom") + check(forbiddenDependencies.none { it in developmentHeader.lowercase() }) { + "Development verification must not activate a release integration" + } + check("development-provenance-check" in developmentHeader) { + "Development verification must bind deterministic source provenance" + } + } } @DisableCachingByDefault(because = "Verification lane policy checks produce no reusable output") @@ -83,7 +118,7 @@ abstract class VerifyVerificationLanes : DefaultTask() { val environmentPrefix = ProductCoordinates.load(productManifestFile.get().asFile)["environment.prefix"] val policy = VerificationLanes.parse(source, environmentPrefix) - check(policy.size == 24) + check(policy.size == 25) val makefile = makefileFile.get().asFile.readText() policy.filterKeys { it.endsWith(".command") }.forEach { (key, command) -> val target = command.removePrefix("make ") @@ -94,5 +129,6 @@ abstract class VerifyVerificationLanes : DefaultTask() { check(policy.values.none { ".github/" in it || ".act/" in it }) { "Verification policy must not reference an orchestration root" } + VerificationLanes.verifyDevelopmentMakefile(makefile) } } diff --git a/build-logic/plugins/src/test/kotlin/org/harvestcircle/buildlogic/plugins/tasks/VerificationLanesTest.kt b/build-logic/plugins/src/test/kotlin/org/harvestcircle/buildlogic/plugins/tasks/VerificationLanesTest.kt @@ -9,24 +9,46 @@ class VerificationLanesTest { fun policyRequiresTheExactLeastPrivilegeLaneMap() { val parsed = VerificationLanes.parse(policy, "HARVESTCIRCLE_") - assertEquals(24, parsed.size) + assertEquals(25, parsed.size) assertFails { VerificationLanes.parse(policy + "source.workflow=forbidden\n", "HARVESTCIRCLE_") } assertFails { VerificationLanes.parse(policy.replaceFirst("schema=", "schema"), "HARVESTCIRCLE_") } - assertFails { VerificationLanes.parse(policy.replace("schema=harvestcircle.verification-lanes.v2\n", ""), "HARVESTCIRCLE_") } - assertFails { VerificationLanes.parse(policy + "schema=harvestcircle.verification-lanes.v2\n", "HARVESTCIRCLE_") } + assertFails { VerificationLanes.parse(policy.replace("schema=harvestcircle.verification-lanes.v3\n", ""), "HARVESTCIRCLE_") } + assertFails { VerificationLanes.parse(policy + "schema=harvestcircle.verification-lanes.v3\n", "HARVESTCIRCLE_") } assertFails { VerificationLanes.parse(policy.replace("source.credentials=none", "source.credentials=all"), "HARVESTCIRCLE_") } - assertFails { VerificationLanes.parse(policy.replace("release.mode=governed", "release.mode=standalone"), "HARVESTCIRCLE_") } + assertFails { VerificationLanes.parse(policy.replace("release.state=deferred-unclaimed", "release.state=passing"), "HARVESTCIRCLE_") } + assertFails { + VerificationLanes.parse( + policy.replace("development.runners=macos-aarch64,linux-x86_64", "development.runners=windows"), + "HARVESTCIRCLE_", + ) + } + assertFails { + VerificationLanes.parse( + policy.replace("release.network_advisories=deferred", "release.network_advisories=required"), + "HARVESTCIRCLE_", + ) + } assertFails { VerificationLanes.parse( policy.replace("HARVESTCIRCLE_BUILD_SOURCE_COMMIT", "BUILD_SOURCE_COMMIT"), "HARVESTCIRCLE_", ) } + + VerificationLanes.verifyDevelopmentMakefile(makefile) + assertFails { + VerificationLanes.verifyDevelopmentMakefile( + makefile.replace( + "development-check: development-provenance-check source-check integration-check", + "development-check: development-provenance-check source-check integration-check release-check", + ), + ) + } } private val policy = """ - schema=harvestcircle.verification-lanes.v2 + schema=harvestcircle.verification-lanes.v3 orchestration=explicit-make-modes source.standalone.command=make source-check source.governed.command=make governed-source-check @@ -34,21 +56,40 @@ class VerificationLanesTest { integration.standalone.command=make integration-check integration.governed.command=make governed-integration-check integration.credentials=none - package.standalone.command=make host-package-check - package.governed.command=make governed-package-check - package.runners=linux,macos,windows - package.credentials=none + development.standalone.command=make development-check + development.governed.command=make governed-development-check + development.macos_aarch64.command=make governed-development-check + development.linux_x86_64.command=make governed-linux-x86_64-development-check + development.runners=macos-aarch64,linux-x86_64 + development.credentials=none provenance.commit=HARVESTCIRCLE_BUILD_SOURCE_COMMIT provenance.dirty=HARVESTCIRCLE_BUILD_SOURCE_DIRTY provenance.radroots=HARVESTCIRCLE_BUILD_RADROOTS_REVISION provenance.epoch=SOURCE_DATE_EPOCH - signing.command=make signing-check - signing.runner=macos - signing.credentials=signing - notarization.command=make notarization-check - notarization.runner=macos - notarization.credentials=notarization - release.command=make release-check - release.mode=governed + release.state=deferred-unclaimed + release.activation=declared-release-candidate-and-fresh-authority + release.network_advisories=deferred + release.packages=deferred + release.evidence=deferred + release.signing=deferred + release.nix_oci=deferred + """.trimIndent() + "\n" + + private val makefile = + """ + override CARGO := cargo +1.97.1 + + source-check: build-logic-check check bindings api-check licenses dev-check + + integration-check: build-logic-check check + + development-check: export HARVESTCIRCLE_BUILD_SOURCE_COMMIT = abcdef + development-check: export HARVESTCIRCLE_BUILD_SOURCE_DIRTY = false + + development-check: development-provenance-check source-check integration-check + + governed-development-check: + + governed-linux-x86_64-development-check: governed-doctor """.trimIndent() + "\n" } diff --git a/config/verification/lanes-v2.properties b/config/verification/lanes-v2.properties @@ -1,24 +0,0 @@ -schema=harvestcircle.verification-lanes.v2 -orchestration=explicit-make-modes -source.standalone.command=make source-check -source.governed.command=make governed-source-check -source.credentials=none -integration.standalone.command=make integration-check -integration.governed.command=make governed-integration-check -integration.credentials=none -package.standalone.command=make host-package-check -package.governed.command=make governed-package-check -package.runners=linux,macos,windows -package.credentials=none -provenance.commit=HARVESTCIRCLE_BUILD_SOURCE_COMMIT -provenance.dirty=HARVESTCIRCLE_BUILD_SOURCE_DIRTY -provenance.radroots=HARVESTCIRCLE_BUILD_RADROOTS_REVISION -provenance.epoch=SOURCE_DATE_EPOCH -signing.command=make signing-check -signing.runner=macos -signing.credentials=signing -notarization.command=make notarization-check -notarization.runner=macos -notarization.credentials=notarization -release.command=make release-check -release.mode=governed diff --git a/config/verification/lanes-v3.properties b/config/verification/lanes-v3.properties @@ -0,0 +1,25 @@ +schema=harvestcircle.verification-lanes.v3 +orchestration=explicit-make-modes +source.standalone.command=make source-check +source.governed.command=make governed-source-check +source.credentials=none +integration.standalone.command=make integration-check +integration.governed.command=make governed-integration-check +integration.credentials=none +development.standalone.command=make development-check +development.governed.command=make governed-development-check +development.macos_aarch64.command=make governed-development-check +development.linux_x86_64.command=make governed-linux-x86_64-development-check +development.runners=macos-aarch64,linux-x86_64 +development.credentials=none +provenance.commit=HARVESTCIRCLE_BUILD_SOURCE_COMMIT +provenance.dirty=HARVESTCIRCLE_BUILD_SOURCE_DIRTY +provenance.radroots=HARVESTCIRCLE_BUILD_RADROOTS_REVISION +provenance.epoch=SOURCE_DATE_EPOCH +release.state=deferred-unclaimed +release.activation=declared-release-candidate-and-fresh-authority +release.network_advisories=deferred +release.packages=deferred +release.evidence=deferred +release.signing=deferred +release.nix_oci=deferred diff --git a/core/crates/harvestcircle_ffi/src/host_runtime.rs b/core/crates/harvestcircle_ffi/src/host_runtime.rs @@ -111,9 +111,7 @@ impl HostRuntime { let thread = self.thread.lock().map_err(|_| ())?.take(); if let Some(thread) = thread { - tokio::task::spawn_blocking(move || thread.join().map_err(|_| ())) - .await - .map_err(|_| ())??; + thread.join().map_err(|_| ())?; } Ok(()) } diff --git a/core/crates/harvestcircle_ffi/src/keyring_worker.rs b/core/crates/harvestcircle_ffi/src/keyring_worker.rs @@ -98,9 +98,7 @@ impl BoundedKeyringWorker { } let thread = self.thread.lock().map_err(|_| worker_unavailable())?.take(); if let Some(thread) = thread { - tokio::task::spawn_blocking(move || thread.join().map_err(|_| worker_unavailable())) - .await - .map_err(|_| worker_unavailable())??; + thread.join().map_err(|_| worker_unavailable())?; } Ok(()) } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml @@ -2131,6 +2131,11 @@ <sha256 value="95f382a20318661e8ae03a407478ea50a691608d2742d3a710fc6a791eced729" origin="Generated by Gradle"/> </artifact> </component> + <component group="org.jetbrains.compose.desktop" name="desktop-jvm-linux-x64" version="1.11.1"> + <artifact name="desktop-jvm-linux-x64-1.11.1.pom"> + <sha256 value="1fd4365e8ad86592a014b27f4c5d0974b898e5d5708dfdad170a27dc3ef8bb60" origin="Generated by Gradle"/> + </artifact> + </component> <component group="org.jetbrains.compose.desktop" name="desktop-jvm-macos-arm64" version="1.10.0"> <artifact name="desktop-jvm-macos-arm64-1.10.0.pom"> <sha256 value="e0256dd49af246c92b541f00d522690e9dcfd3be60e2dc4f59bd970657d9a222" origin="Generated by Gradle"/> @@ -3998,6 +4003,14 @@ <sha256 value="55ef1cb4da6e71a41b832f7fb2a6af0a483f5bfdcf51f1eb9b14222c518c3b02" origin="Generated by Gradle"/> </artifact> </component> + <component group="org.jetbrains.skiko" name="skiko-awt-runtime-linux-x64" version="0.144.6"> + <artifact name="skiko-awt-runtime-linux-x64-0.144.6.jar"> + <sha256 value="3ed16be373ccbba7fbdca9acd7747ff2ed3d441764ac070aa20682c84764a671" origin="Generated by Gradle"/> + </artifact> + <artifact name="skiko-awt-runtime-linux-x64-0.144.6.pom"> + <sha256 value="427c53664b7fa815ac712eb1285fd936bcf69b1244dafb3482ac66dfe8922615" origin="Generated by Gradle"/> + </artifact> + </component> <component group="org.jetbrains.skiko" name="skiko-awt-runtime-macos-arm64" version="0.144.6"> <artifact name="skiko-awt-runtime-macos-arm64-0.144.6.jar"> <sha256 value="aec37b44e8dabf4de620068146769655748be3971bf868614e5ec6b240b2ac35" origin="Generated by Gradle"/> diff --git a/tools/run-linux-x86_64-development-check.sh b/tools/run-linux-x86_64-development-check.sh @@ -0,0 +1,155 @@ +#!/bin/sh +set -eu + +repository_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) + +fail() { + printf '%s\n' "linux-x86_64 development check: $1" >&2 + exit 1 +} + +[ -n "${EXT_BUILD_RUN_ACTIVE:-}" ] || fail "must run through cargo extbuild" +[ -n "${EXT_BUILD_PROJECT_DIR:-}" ] || fail "EXT_BUILD_PROJECT_DIR is unavailable" +[ -n "${EXT_BUILD_TMPDIR:-}" ] || fail "EXT_BUILD_TMPDIR is unavailable" +command -v docker >/dev/null 2>&1 || fail "docker is unavailable" +docker info >/dev/null 2>&1 || fail "docker daemon is unavailable" + +source_commit=$(git -C "$repository_root" rev-parse HEAD) +[ -n "$source_commit" ] || fail "source commit is unavailable" +[ -z "$(git -C "$repository_root" status --porcelain --untracked-files=all)" ] || + fail "source worktree must be clean" + +source_epoch=$(git -C "$repository_root" show -s --format=%ct "$source_commit") +case "$source_epoch" in + ''|*[!0-9]*) fail "source epoch is invalid" ;; +esac + +source_lock="$repository_root/radroots.lib.source-lock.v1.toml" +[ -f "$source_lock" ] || fail "source lock is missing" +radroots_revision=$(sed -n 's/^revision = "\([0-9a-f]\{40\}\)"$/\1/p' "$source_lock") +[ "$(printf '%s\n' "$radroots_revision" | wc -l | tr -d ' ')" -eq 1 ] || + fail "source lock revision is not unique" +[ "${#radroots_revision}" -eq 40 ] || fail "source lock revision is invalid" + +runner_root="$EXT_BUILD_PROJECT_DIR/linux-x86_64-development" +cargo_home="$runner_root/cargo-home" +cargo_target="$runner_root/cargo-target" +cargo_tool_target="$runner_root/cargo-tool-target" +cargo_tools="$runner_root/cargo-tools" +gradle_home="$runner_root/gradle-home" +gradle_build="$runner_root/gradle-build" +gradle_project_cache="$runner_root/gradle-project-cache" +container_home="$runner_root/home" +container_tmp="$runner_root/tmp" +container_jvm="$runner_root/jvm" + +mkdir -p \ + "$cargo_home" \ + "$cargo_target" \ + "$cargo_tool_target" \ + "$cargo_tools" \ + "$gradle_home" \ + "$gradle_build" \ + "$gradle_project_cache" \ + "$container_home" \ + "$container_tmp" \ + "$container_jvm" +chmod 0700 "$container_tmp" +chmod 0755 "$container_jvm" + +workspace=$(mktemp -d "$EXT_BUILD_TMPDIR/harvestcircle-linux-x86_64.XXXXXX") +cleanup() { + find "$workspace" -depth -delete +} +trap cleanup EXIT HUP INT TERM + +git clone --no-local --no-hardlinks "$repository_root" "$workspace/source" +git -C "$workspace/source" checkout --detach "$source_commit" +[ -z "$(git -C "$workspace/source" status --porcelain --untracked-files=all)" ] || + fail "isolated source clone is not clean" + +runner_image="docker.io/library/rust:1.97.1-slim-trixie@sha256:fc0648ac2962539be80bd424729a20fd80f7b64bfba7e90bbd642aed6c697c5a" + +docker run \ + --rm \ + --init \ + --platform linux/amd64 \ + --workdir /workspace/source \ + --env HOME=/workspace/home \ + --env CARGO_HOME=/workspace/cargo-home \ + --env CARGO_TARGET_DIR=/workspace/cargo-target \ + --env CARGO_BUILD_JOBS=2 \ + --env GRADLE_USER_HOME=/workspace/gradle-home \ + --env EXT_BUILD_GRADLE_BUILD_DIR=/workspace/gradle-build \ + --env HARVESTCIRCLE_BUILD_MODE=governed \ + --env HARVESTCIRCLE_BUILD_SOURCE_COMMIT="$source_commit" \ + --env HARVESTCIRCLE_BUILD_SOURCE_DIRTY=false \ + --env HARVESTCIRCLE_BUILD_RADROOTS_REVISION="$radroots_revision" \ + --env SOURCE_DATE_EPOCH="$source_epoch" \ + --mount "type=bind,src=$workspace/source,dst=/workspace/source" \ + --mount "type=bind,src=$container_home,dst=/workspace/home" \ + --mount "type=bind,src=$cargo_home,dst=/workspace/cargo-home" \ + --mount "type=bind,src=$cargo_target,dst=/workspace/cargo-target" \ + --mount "type=bind,src=$cargo_tool_target,dst=/workspace/cargo-tool-target" \ + --mount "type=bind,src=$cargo_tools,dst=/workspace/cargo-tools" \ + --mount "type=bind,src=$gradle_home,dst=/workspace/gradle-home" \ + --mount "type=bind,src=$gradle_build,dst=/workspace/gradle-build" \ + --mount "type=bind,src=$gradle_project_cache,dst=/workspace/gradle-project-cache" \ + --mount "type=bind,src=$container_tmp,dst=/tmp" \ + --mount "type=bind,src=$container_jvm,dst=/usr/lib/jvm" \ + "$runner_image" \ + sh -ceu ' + export DEBIAN_FRONTEND=noninteractive + mkdir -p /tmp/apt-archives/partial + chown _apt:root /tmp/apt-archives/partial + chmod 0700 /tmp/apt-archives/partial + apt-get update + apt-get -o Dir::Cache::archives=/tmp/apt-archives install --yes --no-install-recommends \ + binutils ca-certificates file g++ git gosu libasound2-dev libfreetype-dev \ + libssl-dev libx11-dev libxext-dev libxi-dev libxrender-dev libxtst-dev \ + make openjdk-21-jdk pkg-config xz-utils zlib1g-dev + rm -rf /var/lib/apt/lists/* + chown -R '"$(id -u):$(id -g)"' /tmp/apt-archives + chown -R '"$(id -u):$(id -g)"' /usr/lib/jvm + exec gosu '"$(id -u):$(id -g)"' sh -ceu '\'' + export PATH=/workspace/cargo-tools/bin:/usr/local/cargo/bin:$PATH + [ "$(uname -s)" = Linux ] + [ "$(uname -m)" = x86_64 ] + [ "$(git rev-parse HEAD)" = "$HARVESTCIRCLE_BUILD_SOURCE_COMMIT" ] + [ -z "$(git status --porcelain --untracked-files=all)" ] + rustc --version | grep -Eq "^rustc 1\\.97\\.1 " + java -version 2>&1 | grep -Eq "version \"21\\." + rustup component add clippy rustfmt + if ! cargo deny --version 2>/dev/null | grep -Eq "0\\.19\\.8$"; then + CARGO_TARGET_DIR=/workspace/cargo-tool-target \ + cargo install --root /workspace/cargo-tools cargo-deny --version 0.19.8 --locked + fi + cargo fmt --manifest-path core/Cargo.toml --all -- --check + cargo clippy --manifest-path core/Cargo.toml --workspace --all-targets --locked -- -D warnings + cargo test --manifest-path core/Cargo.toml --workspace --locked + cargo deny --manifest-path core/Cargo.toml check --config core/deny.toml licenses sources + cargo fmt --manifest-path tools/xtask/Cargo.toml --all -- --check + cargo clippy --manifest-path tools/xtask/Cargo.toml --all-targets --locked -- -D warnings + cargo test --manifest-path tools/xtask/Cargo.toml --locked + cargo run --manifest-path tools/xtask/Cargo.toml --locked -- qualification-report + tools/test-build-modes.sh + ./gradlew --no-daemon --no-parallel --no-configuration-cache \ + --project-cache-dir /workspace/gradle-project-cache -p build-logic \ + :contracts:check :plugins:check :plugins:functionalTest + ./gradlew --no-daemon --no-parallel --no-configuration-cache \ + --project-cache-dir /workspace/gradle-project-cache \ + :app:shared:ktlintCheck :app:desktop:ktlintCheck designFormatCheck \ + :app:shared:detektCommonMainSourceSet :app:shared:detektCommonTestSourceSet \ + :app:desktop:detekt designLint :app:shared:desktopTest :app:desktop:test \ + designTest :app:shared:check :app:desktop:check designCheck \ + :app:desktop:checkLicense :app:design_system:checkLicense \ + :tools:design_catalog:checkLicense :app:desktop:verifyUniFfiBindings \ + :app:desktop:verifyReleaseNativeLibrary :app:desktop:sourceReadiness \ + :app:desktop:integrationTest :app:desktop:verifyTestBridgeIsolation + [ -z "$(git status --porcelain --untracked-files=all)" ] + '\'' + ' + +printf '%s\n' "harvestcircle.linux_x86_64.development=pass" +printf '%s\n' "harvestcircle.source_commit=$source_commit" +printf '%s\n' "harvestcircle.radroots_revision=$radroots_revision" diff --git a/tools/test-build-modes.sh b/tools/test-build-modes.sh @@ -9,7 +9,7 @@ cleanup() { } trap cleanup EXIT HUP INT TERM -printf '%s\n' '#!/bin/sh' 'if [ "${1:-}" = extbuild ]; then printf "%s\n" "cargo-extbuild unavailable" >&2; else printf "%s\n" "cargo must not be invoked in standalone dry-run" >&2; fi' 'exit 93' > "$fixture/cargo" +printf '%s\n' '#!/bin/sh' 'if [ "${1:-}" = +1.97.1 ]; then shift; fi' 'if [ "${1:-}" = extbuild ]; then printf "%s\n" "cargo-extbuild unavailable" >&2; else printf "%s\n" "cargo must not be invoked in standalone dry-run" >&2; fi' 'exit 93' > "$fixture/cargo" chmod +x "$fixture/cargo" standalone_output=$(PATH="$fixture:$PATH" "$make_command" --no-print-directory -n BUILD_MODE=standalone -C "$repository_root" check) @@ -18,7 +18,7 @@ if printf '%s\n' "$standalone_output" | grep -q 'cargo extbuild'; then exit 1 fi -for lane in source-check integration-check; do +for lane in source-check integration-check development-check; do for mode in standalone governed; do lane_output=$(PATH="$fixture:$PATH" "$make_command" --no-print-directory -n BUILD_MODE="$mode" -C "$repository_root" "$lane") build_logic_count=$(printf '%s\n' "$lane_output" | grep -c -- '-p build-logic') @@ -29,6 +29,24 @@ for lane in source-check integration-check; do done done +development_output=$(PATH="$fixture:$PATH" "$make_command" --no-print-directory -n BUILD_MODE=standalone -C "$repository_root" development-check) +for forbidden in \ + 'cargo audit' \ + 'dependencyCheckAnalyze' \ + 'verifyHostPackage' \ + 'releaseReadiness' \ + 'unsignedReleaseReadiness' \ + 'signingReadiness' \ + 'notarizationReadiness' \ + 'packageDmg' \ + 'packageDeb' +do + if printf '%s\n' "$development_output" | grep -q "$forbidden"; then + printf '%s\n' "development verification activated deferred integration: $forbidden" >&2 + exit 1 + fi +done + for mode in standalone governed; do stability_output=$(PATH="$fixture:$PATH" "$make_command" --no-print-directory -n BUILD_MODE="$mode" -C "$repository_root" build-logic-stability-check) stability_count=$(printf '%s\n' "$stability_output" | grep -c 'test-build-logic-stability.sh') diff --git a/tools/verify-storage-api.sh b/tools/verify-storage-api.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -eu + +repository_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +temporary_root=${TMPDIR:-/tmp} +output=$(mktemp "$temporary_root/harvestcircle-storage-api.XXXXXX") +trap 'rm -f "$output"' EXIT HUP INT TERM + +cd "$repository_root" +cargo +nightly-2026-07-16 public-api \ + --manifest-path core/Cargo.toml \ + -p harvestcircle_storage \ + --all-features \ + -sss >"$output" +cmp core/compatibility/harvestcircle-storage-api-v1.txt "$output" diff --git a/tools/xtask/src/lib.rs b/tools/xtask/src/lib.rs @@ -832,6 +832,124 @@ fn contains_direct_call(source: &str, call: &str) -> bool { }) } +fn manifest_declares_dependency(source: &str, dependency: &str) -> bool { + source.lines().map(str::trim).any(|line| { + if line.is_empty() || line.starts_with('#') { + return false; + } + if line + .split_once('=') + .is_some_and(|(key, _)| key.trim().trim_matches('"') == dependency) + { + return true; + } + line.strip_prefix('[') + .and_then(|value| value.strip_suffix(']')) + .is_some_and(|table| { + let segments = table.split('.').collect::<Vec<_>>(); + segments.contains(&"dependencies") + && segments + .last() + .is_some_and(|name| name.trim_matches('"') == dependency) + }) + }) +} + +fn sqlite_dependency_topology(root: &Path, inventory: &Inventory, findings: &mut Vec<String>) { + let cargo_lock = read_text(root, "core/Cargo.lock"); + let package_count = |name: &str| { + let marker = format!("name = \"{name}\""); + cargo_lock + .lines() + .filter(|line| line.trim() == marker) + .count() + }; + if package_count("libsqlite3-sys") != 1 + || ["rusqlite", "refinery", "refinery-core", "refinery-macros"] + .iter() + .any(|name| package_count(name) != 0) + { + findings.push( + "core/Cargo.lock: exact single SQLx-selected native SQLite topology changed".to_owned(), + ); + } + + for path in inventory + .paths + .iter() + .filter(|path| path.starts_with("core/") && path.ends_with("Cargo.toml")) + { + let manifest = read_text(root, path); + if ["rusqlite", "refinery", "libsqlite3-sys"] + .iter() + .any(|dependency| manifest_declares_dependency(&manifest, dependency)) + { + findings.push(format!( + "{path}: direct alternate or native SQLite dependency is forbidden" + )); + } + } +} + +fn development_integration_policy(root: &Path, findings: &mut Vec<String>) { + let makefile = read_text(root, "Makefile"); + for required in [ + "override CARGO := cargo +1.97.1", + "api-check: doctor", + "development-check: development-provenance-check source-check integration-check", + "governed-development-check:", + "governed-linux-x86_64-development-check: governed-doctor", + ] { + if makefile + .lines() + .filter(|line| line.trim() == required) + .count() + != 1 + { + findings.push(format!( + "Makefile: development integration boundary is missing {required}" + )); + } + } + + let runner = read_text(root, "tools/run-linux-x86_64-development-check.sh"); + for required in [ + "rust:1.97.1-slim-trixie@sha256:fc0648ac2962539be80bd424729a20fd80f7b64bfba7e90bbd642aed6c697c5a", + "--platform linux/amd64", + "EXT_BUILD_RUN_ACTIVE", + "cargo deny --manifest-path core/Cargo.toml check --config core/deny.toml licenses sources", + "cargo test --manifest-path core/Cargo.toml --workspace --locked", + "cargo clippy --manifest-path core/Cargo.toml --workspace --all-targets --locked -- -D warnings", + ":app:desktop:integrationTest", + ":app:desktop:verifyUniFfiBindings", + "harvestcircle.linux_x86_64.development=pass", + ] { + if !runner.contains(required) { + findings.push(format!( + "tools/run-linux-x86_64-development-check.sh: faithful runner is missing {required}" + )); + } + } + for forbidden in [ + "cargo audit", + " advisories", + "dependencyCheck", + "releaseReadiness", + "unsignedReleaseReadiness", + "verifyReleaseSupplyChainEvidence", + "packageDmg", + "packageDeb", + "CycloneDX", + "SLSA", + ] { + if runner.contains(forbidden) { + findings.push(format!( + "tools/run-linux-x86_64-development-check.sh: deferred release integration is active: {forbidden}" + )); + } + } +} + fn provenance_check(root: &Path, inventory: &Inventory, findings: &mut Vec<String>) { const LIB_REVISION: &str = "be9db78e060ebc0000fa7827ac32efa3f6504f53"; const PROVENANCE_PATH: &str = "core/provenance/harvestcircle-v1.toml"; @@ -900,6 +1018,8 @@ fn provenance_check(root: &Path, inventory: &Inventory, findings: &mut Vec<Strin )) { findings.push("core/Cargo.lock: selected Lib revision is missing".to_owned()); } + sqlite_dependency_topology(root, inventory, findings); + development_integration_policy(root, findings); let coordinates = properties(&read_text( root, "config/product/harvestcircle-v1.properties", @@ -1009,7 +1129,14 @@ fn provenance_check(root: &Path, inventory: &Inventory, findings: &mut Vec<Strin )); } } - for required in [PROVENANCE_PATH, SOURCE_LOCK_PATH, STORAGE_API_BASELINE] { + for required in [ + PROVENANCE_PATH, + SOURCE_LOCK_PATH, + STORAGE_API_BASELINE, + "config/verification/lanes-v3.properties", + "tools/run-linux-x86_64-development-check.sh", + "tools/verify-storage-api.sh", + ] { if !inventory.paths.iter().any(|path| path == required) { findings.push(format!("{required}: governed source evidence is missing")); } @@ -1383,6 +1510,59 @@ mod tests { } #[test] + fn sqlite_topology_rejects_alternate_duplicate_and_direct_authority() { + let root = fixture("sqlite-topology"); + write( + &root, + "core/Cargo.lock", + "name = \"libsqlite3-sys\"\nname = \"libsqlite3-sys\"\nname = \"rusqlite\"\n", + ); + write( + &root, + "core/crates/unsafe_storage/Cargo.toml", + "[target.'cfg(unix)'.dependencies.refinery]\nversion = \"0.9\"\n", + ); + let inventory = Inventory::load(&root).expect("archive inventory"); + let mut findings = Vec::new(); + sqlite_dependency_topology(&root, &inventory, &mut findings); + assert!( + findings.iter().any(|finding| finding + .contains("exact single SQLx-selected native SQLite topology changed")), + "{findings:#?}" + ); + assert!( + findings.iter().any(|finding| finding + .contains("direct alternate or native SQLite dependency is forbidden")), + "{findings:#?}" + ); + fs::remove_dir_all(root).expect("remove fixture"); + } + + #[test] + fn development_runner_rejects_release_integration_activation() { + let root = fixture("development-runner"); + write( + &root, + "Makefile", + "override CARGO := cargo +1.97.1\napi-check: doctor\ndevelopment-check: development-provenance-check source-check integration-check\ngoverned-development-check:\ngoverned-linux-x86_64-development-check: governed-doctor\n", + ); + write( + &root, + "tools/run-linux-x86_64-development-check.sh", + "dependencyCheckAnalyze\n", + ); + let mut findings = Vec::new(); + development_integration_policy(&root, &mut findings); + assert!( + findings + .iter() + .any(|finding| { finding.contains("deferred release integration is active") }), + "{findings:#?}" + ); + fs::remove_dir_all(root).expect("remove fixture"); + } + + #[test] fn design_contract_rejects_identity_and_root_drift() { let root = fixture("design-contract"); write(