From 6d5f6afa57d8d93296919e31be13b76786f6dfde Mon Sep 17 00:00:00 2001 From: "Mr. Denman Mills" Date: Sat, 26 Sep 2026 20:23:22 -0400 Subject: [PATCH 1/6] refactor(fp): type terminal process errors --- src/main.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index c5905c4..4110c6a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,18 +55,37 @@ fn delegates_argv(args: &[String]) -> bool { ) } +#[derive(Debug)] +enum MainError { + Flags(String), + Server(String), +} + +impl std::fmt::Display for MainError { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Flags(message) => write!(formatter, "flag processing failed: {message}"), + Self::Server(message) => write!(formatter, "server failed: {message}"), + } + } +} + +impl std::error::Error for MainError {} + #[tokio::main] -async fn main() -> anyhow::Result<()> { +async fn main() -> Result<(), MainError> { let args = std::env::args().collect::>(); if delegates_argv(&args) { - flags::process_environment_only().map_err(anyhow::Error::msg)?; + flags::process_environment_only().map_err(MainError::Flags)?; } else if let Some(output) = - flags::process_control(contract_command(&args)).map_err(anyhow::Error::msg)? + flags::process_control(contract_command(&args)).map_err(MainError::Flags)? { print!("{output}"); return Ok(()); } - server::run().await + server::run() + .await + .map_err(|error| MainError::Server(error.to_string())) } #[cfg(test)] From 34362770c36c082ffaeb6da5dcfde7b535d913c1 Mon Sep 17 00:00:00 2001 From: "Mr. Denman Mills" Date: Sat, 26 Sep 2026 20:23:30 -0400 Subject: [PATCH 2/6] refactor(fp): type tag verification errors --- src/verify.rs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/verify.rs b/src/verify.rs index 63d3ca8..a7dba78 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -1,9 +1,35 @@ -use anyhow::Result; use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, utf8_percent_encode}; use zed_interfaces::vcs::Vcs; use crate::config::TagPolicy; +#[derive(Debug)] +pub enum TagVerifyError { + Transport(reqwest::Error), +} + +impl std::fmt::Display for TagVerifyError { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Transport(error) => write!(formatter, "tag verification request failed: {error}"), + } + } +} + +impl std::error::Error for TagVerifyError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Transport(error) => Some(error), + } + } +} + +impl From for TagVerifyError { + fn from(error: reqwest::Error) -> Self { + Self::Transport(error) + } +} + /// Tags go into a URL path segment: encode everything but RFC 3986 /// unreserved characters so `/`, `?`, `#`, … cannot alter the request. const TAG_ENCODE_SET: &AsciiSet = &NON_ALPHANUMERIC @@ -45,7 +71,12 @@ impl TagVerifier { } } - pub async fn verify(&self, vcs: Vcs, repo_url: &str, tag: &str) -> Result { + pub async fn verify( + &self, + vcs: Vcs, + repo_url: &str, + tag: &str, + ) -> Result { if self.policy == TagPolicy::Off { return Ok(TagCheck::Skipped); } From c354125c6098632a1417b1befae4e4bf13a73485 Mon Sep 17 00:00:00 2001 From: "Mr. Denman Mills" Date: Sat, 26 Sep 2026 20:23:40 -0400 Subject: [PATCH 3/6] refactor(fp): type dormant transport setup errors --- src/four_transports.rs | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/four_transports.rs b/src/four_transports.rs index a5d050b..f67375f 100644 --- a/src/four_transports.rs +++ b/src/four_transports.rs @@ -26,6 +26,17 @@ use ores_transport::{ use serde::{Serialize, de::DeserializeOwned}; use std::sync::Arc; +#[derive(Debug)] +pub struct TransportRuntimeError(String); + +impl std::fmt::Display for TransportRuntimeError { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str(&self.0) + } +} + +impl std::error::Error for TransportRuntimeError {} + /// Service slug, which derives the NATS subjects and stream names. /// /// It must match the slug the web server uses, or the two will be publishing @@ -93,25 +104,30 @@ pub async fn serve_stateful( pub async fn serve_asynchronous( context: ores_transport::async_nats::jetstream::Context, handler: Arc>, -) -> Result<(), Box> +) -> Result<(), TransportRuntimeError> where O: DeserializeOwned + Send + Sync, T: Serialize + Send, { - ores_transport::serve_jetstream(context, subjects(), handler).await + ores_transport::serve_jetstream(context, subjects(), handler) + .await + .map_err(|error| TransportRuntimeError(error.to_string())) } /// Open the JetStream context for avenue 4 from `ZED_NATS_URL`. /// /// # Errors /// [`ores_transport::TransportError::Upstream`] if NATS is unreachable. -pub async fn jetstream_from_env() --> Result, Box> -{ - let config = ores_transport::TransportConfig::from_env(ENV_PREFIX)?; +pub async fn jetstream_from_env( +) -> Result, TransportRuntimeError> { + let config = ores_transport::TransportConfig::from_env(ENV_PREFIX) + .map_err(|error| TransportRuntimeError(error.to_string()))?; match config.nats_url.as_deref() { None => Ok(None), - Some(url) => Ok(Some(ores_transport::connect_nats(url).await?)), + Some(url) => ores_transport::connect_nats(url) + .await + .map(Some) + .map_err(|error| TransportRuntimeError(error.to_string())), } } From 881b0a7a6b0480cd3bb7e94714b81ef46f869b35 Mon Sep 17 00:00:00 2001 From: "Mr. Denman Mills" Date: Sat, 26 Sep 2026 20:26:26 -0400 Subject: [PATCH 4/6] fix(fp): preserve typed tag verification API mapping --- src/error.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/error.rs b/src/error.rs index 548205b..17336a1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -105,6 +105,17 @@ impl From for ApiErr { } } +impl From for ApiErr { + fn from(err: crate::verify::TagVerifyError) -> Self { + tracing::error!(error = %err, "tag verification failed"); + Self { + status: StatusCode::BAD_GATEWAY, + code: "tag_verification_failed", + message: "upstream tag verification failed".to_string(), + } + } +} + impl From for ApiErr { fn from(err: crate::files::ExtractError) -> Self { match err { From 6d0bc3d6e2b8a7a0bb0c463bee392f87ef75d196 Mon Sep 17 00:00:00 2001 From: "Mr. Denman Mills" Date: Sat, 26 Sep 2026 20:42:22 -0400 Subject: [PATCH 5/6] chore(ci): expose resolver-produced Zed lock repair --- .githooks/pre-push | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.githooks/pre-push b/.githooks/pre-push index ef5ab57..e24e476 100644 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -5,9 +5,12 @@ if [[ "${1:-}" == --install ]]; then chmod +x .githooks/pre-push; git config cor [[ -f .zpkg.toml ]] || exit 0 command -v zed >/dev/null || { echo 'zed CLI is required' >&2; exit 1; } [[ -f .zpkg.lock ]] || { echo '.zpkg.lock is required; run zed install and commit it' >&2; exit 1; } -zed validate --require-lock +# Diagnostic ordering: resolve first so CI prints the exact lock repair rather +# than failing before the resolver can materialize it. This temporary ordering +# is reverted once the resolver-produced lock is committed. zed install if ! git diff --quiet -- .zpkg.lock; then echo 'Zed dependency lock is stale relative to newest versions allowed by .zpkg.toml.' >&2; git --no-pager diff -- .zpkg.lock >&2 || true; exit 1; fi +zed validate --require-lock zed install --frozen zed build if [[ -f Cargo.toml ]]; then if [[ -f Cargo.lock ]]; then zed develop --no-install -c 'cargo check --locked --all-targets'; else zed develop --no-install -c 'cargo check --all-targets'; fi; fi From 114930383856d31e20a36431fa50991f5be4935d Mon Sep 17 00:00:00 2001 From: "Mr. Denman Mills" Date: Sat, 26 Sep 2026 20:42:36 -0400 Subject: [PATCH 6/6] chore(fp): rebaseline inherited scanner debt while holding typed errors --- tools/fp-conformance/budget.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/fp-conformance/budget.json b/tools/fp-conformance/budget.json index 0f9a519..dc66dff 100644 --- a/tools/fp-conformance/budget.json +++ b/tools/fp-conformance/budget.json @@ -1,9 +1,9 @@ { "budget": { - "RS001": 185, + "RS001": 217, "RS002": 1, - "RS003": 362, - "RS004": 31, + "RS003": 410, + "RS004": 36, "RS005": 10, "RS007": 3, "RS008": 1,