Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve

### Fixed

- **Rust Holmes validation gate review fixes**: Law evidence validation now
continues artifact checks when structure validation emits warning-only
diagnostics, rejects duplicate artifact roles after workspace-relative path
normalization, and validates artifact `sha256` digests with artifact-specific
diagnostics instead of allowing malformed digest anchors into later
traceability gates.
- **Rust Holmes assurance review fixes**: The new Holmes artifact locator now
returns stable Holmes diagnostics for invalid and escaping paths, rejects
platform-specific backslash and drive-path input before normalization, the
Expand Down Expand Up @@ -40,6 +46,14 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve

### Added

- **Rust Holmes law evidence validation gate**: Extended the unpublished
`wesley-holmes` crate with collected law evidence validation results,
required-versus-optional bundle artifact validation, canonical provenance
hash/source checks, deprecated schema-version warnings, artifact-local
version checks for law diff/coverage/capability/manifest evidence, and an
application-layer validator that loads artifacts through deterministic ports
while reporting unavailable, unreadable, and oversized artifacts without
panics.
- **Rust Holmes assurance foundation**: Added the unpublished
`crates/wesley-holmes` workspace crate with a hexagonal module shell, domain
dependency-boundary tests, deterministic port traits and fakes, a structured
Expand Down
10 changes: 5 additions & 5 deletions crates/wesley-holmes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

`wesley-holmes` is the Rust foundation for Holmes law assurance work inside
Wesley. It consumes Wesley-published law evidence, policy, witness, MCP, and
GitHub payload artifacts; validates their envelope shape and version posture;
and prepares deterministic diagnostics and reporting surfaces for later CLI,
API, and MCP interfaces.
GitHub payload artifacts; validates their envelope shape, provenance, artifact
availability, and version posture; and prepares deterministic diagnostics and
reporting surfaces for later CLI, API, and MCP interfaces.

This crate is intentionally not published yet. It is a workspace implementation
crate for the Holmes redesign described in the Wesley design packet:
Expand All @@ -27,5 +27,5 @@ The crate follows the planned hexagonal boundary:
surfaces.
- `reporting`: future renderer-facing DTOs and report assembly helpers.

The current slice establishes the foundation only. No public Holmes CLI command
is exposed from Wesley yet.
The current implementation includes the first local law evidence validation
gate. No public Holmes CLI command is exposed from Wesley yet.
122 changes: 122 additions & 0 deletions crates/wesley-holmes/src/application/evidence_validation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//! Evidence-bundle validation application service.

use std::collections::BTreeMap;

use crate::application::WeslawArtifactLocator;
use crate::domain::{
ArtifactRef, HolmesDiagnostic, HolmesDiagnosticCode, HolmesLawEvidenceBundle, HolmesSeverity,
LawEvidenceValidationResult, LoadedArtifactMetadata, VersionRegistry,
};
use crate::ports::ArtifactLoadPort;

/// Validates Holmes law evidence bundles without performing assurance judgment.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LawEvidenceValidator {
locator: WeslawArtifactLocator,
max_bytes: usize,
}

impl LawEvidenceValidator {
/// Create a validator with a workspace artifact locator.
pub fn new(locator: WeslawArtifactLocator) -> Self {
Self {
locator,
max_bytes: 8 * 1024 * 1024,
}
}

/// Return a validator with a deterministic artifact byte limit.
pub fn with_max_bytes(mut self, max_bytes: usize) -> Self {
self.max_bytes = max_bytes;
self
}

/// Validate structure, provenance, and referenced artifact availability.
pub fn validate(
&self,
bundle: &HolmesLawEvidenceBundle,
artifact_loader: &impl ArtifactLoadPort,
version_registry: &VersionRegistry,
) -> LawEvidenceValidationResult {
let structural = bundle.validate_structure(version_registry);
if structural
.diagnostics
.iter()
.any(|diagnostic| diagnostic.severity == HolmesSeverity::Error)
{
return structural;
}

let mut diagnostics = structural.diagnostics;
let mut loaded_artifacts = Vec::new();
let mut normalized_paths = BTreeMap::new();

for bundle_artifact in bundle.artifact_refs() {
let artifact = bundle_artifact.artifact;
let resolved = match self.locator.resolve(&artifact.path) {
Ok(resolved) => resolved,
Err(diagnostic) => {
diagnostics.push(diagnostic.at_field(bundle_artifact.field_path));
continue;
}
};

if let Some(first_field_path) = normalized_paths.insert(
resolved.workspace_relative.clone(),
bundle_artifact.field_path,
) {
diagnostics.push(
HolmesDiagnostic::new(
HolmesDiagnosticCode::HlawEvidenceBundleInvalid,
HolmesSeverity::Error,
format!(
"artifact path normalizes to the same path as {first_field_path}; each artifact role must point at distinct evidence"
),
)
.for_family(bundle_artifact.family.id())
.at_field(bundle_artifact.field_path),
);
continue;
}

let normalized = ArtifactRef {
path: resolved.workspace_relative.clone(),
schema_version: artifact.schema_version.clone(),
sha256: artifact.sha256.clone(),
};

match artifact_loader.read_artifact(&normalized) {
Ok(bytes) if bytes.len() <= self.max_bytes => {
loaded_artifacts.push(LoadedArtifactMetadata {
field_path: bundle_artifact.field_path.to_owned(),
artifact_family: bundle_artifact.family.id().to_owned(),
path: normalized.path,
byte_len: bytes.len(),
});
}
Ok(bytes) => diagnostics.push(
HolmesDiagnostic::new(
HolmesDiagnosticCode::HlawArtifactOversized,
HolmesSeverity::Error,
format!(
"artifact {:?} is {} bytes, above the configured {} byte limit",
normalized.path,
bytes.len(),
self.max_bytes
),
)
.for_family(bundle_artifact.family.id())
.at_field(bundle_artifact.field_path),
),
Err(diagnostic) => diagnostics.push(
diagnostic
.for_family(bundle_artifact.family.id())
.at_field(bundle_artifact.field_path),
),
}
}

LawEvidenceValidationResult::from_diagnostics(diagnostics)
.with_loaded_artifacts(loaded_artifacts)
}
}
2 changes: 2 additions & 0 deletions crates/wesley-holmes/src/application/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Application services for deterministic Holmes law-assurance orchestration.

mod artifact_locator;
mod evidence_validation;

pub use artifact_locator::{ResolvedArtifactPath, WeslawArtifactLocator};
pub use evidence_validation::LawEvidenceValidator;
16 changes: 16 additions & 0 deletions crates/wesley-holmes/src/domain/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub enum HolmesDiagnosticCode {
HlawSchemaVersionMissing,
/// A `schemaVersion` field was not valid semantic version syntax.
HlawSchemaVersionMalformed,
/// A `schemaVersion` is accepted but deprecated.
HlawSchemaVersionDeprecated,
/// A `schemaVersion` major version is not supported by this Holmes build.
HlawSchemaVersionUnsupportedMajor,
/// A `schemaVersion` minor version is newer than this Holmes build accepts.
Expand All @@ -25,8 +27,22 @@ pub enum HolmesDiagnosticCode {
HlawArtifactPathInvalid,
/// A law evidence bundle was missing a required artifact reference.
HlawEvidenceBundleInvalid,
/// An artifact digest field was absent or blank.
HlawArtifactHashMissing,
/// An artifact digest field did not use canonical `sha256:<64 lowercase hex>` syntax.
HlawArtifactHashMalformed,
/// A provenance hash was absent or blank.
HlawProvenanceHashMissing,
/// A provenance hash did not use canonical `sha256:<64 lowercase hex>` syntax.
HlawProvenanceHashMalformed,
/// A provenance source identity was absent or blank.
HlawProvenanceSourceMissing,
/// A requested artifact was unavailable through its port.
HlawArtifactUnavailable,
/// A requested artifact was present but unreadable through its port.
HlawArtifactUnreadable,
/// A requested artifact exceeded the configured byte limit.
HlawArtifactOversized,
}

/// Severity attached to a Holmes diagnostic.
Expand Down
Loading
Loading