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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
Known gap: MCP-provided tools are not inspected and may still perform their own HTTP
egress. This is a best-effort tool/command-identity block, not a sandbox-level
guarantee — see `specs/069-threat-model/spec.md` INVARIANT-5.

- `zeph-config`/`zeph-memory`/`zeph-experiments`/`zeph-common`/`zeph-index`: 9 `Display`
impls (`ProviderKind`, `MemoryTier`, `ContentFidelity`, `EntityType`, `SourceKind`,
`ParameterKind`, `ExperimentSource`, `EdgeType`, `Lang`) used `Formatter::write_str`,
which silently ignores width/fill/align flags from the caller's format spec — only
`Formatter::pad` respects them. Switched all 9 to `f.pad(...)`, closing off the same
latent width-spec bug already fixed for `SessionKind`/`SessionStatus`/`SessionChannel`
in #6060 (#6066).

- `zeph-skills`/`src/acp.rs`/`src/serve/`: `SkillOrchestra`'s RL routing head lost learned
updates under concurrent ACP/`/sessions` agents (#5974). `#5921` wired `RoutingHead`
persistence into `spawn_acp_agent` and `build_agent_factory`, but each session independently
Expand Down
19 changes: 17 additions & 2 deletions crates/zeph-common/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ impl EdgeType {

impl fmt::Display for EdgeType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
f.pad(self.as_str())
}
}

Expand Down Expand Up @@ -761,7 +761,22 @@ pub trait ContextMemoryBackend: Send + Sync {

#[cfg(test)]
mod tests {
use super::MemoryRoute;
use super::{EdgeType, MemoryRoute};

/// Locks in the `f.pad` fix (#6066): `f.write_str` ignores width/fill/align flags.
/// `f.pad` must reproduce the same padding a plain `&str` would get under an
/// identical width specifier.
#[test]
fn edge_type_display_respects_width() {
assert_eq!(
format!("{:<10}", EdgeType::Causal),
format!("{:<10}", "causal")
);
assert_eq!(
format!("{:>10}", EdgeType::Semantic),
format!("{:>10}", "semantic")
);
}

#[test]
fn memory_route_serde_roundtrip() {
Expand Down
2 changes: 1 addition & 1 deletion crates/zeph-config/src/providers/llm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ impl ProviderKind {

impl std::fmt::Display for ProviderKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
f.pad(self.as_str())
}
}

Expand Down
15 changes: 15 additions & 0 deletions crates/zeph-config/src/providers/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -982,3 +982,18 @@ fn validate_cocoon_pricing_valid_passes() {
});
assert!(e.validate().is_ok());
}

/// Locks in the `f.pad` fix (#6066): `f.write_str` ignores width/fill/align flags, so
/// width-specifier `format!` calls used to render unpadded text. `f.pad` must reproduce
/// the same padding a plain `&str` would get under an identical width specifier.
#[test]
fn provider_kind_display_respects_width() {
assert_eq!(
format!("{:<12}", ProviderKind::Claude),
format!("{:<12}", "claude")
);
assert_eq!(
format!("{:>12}", ProviderKind::Compatible),
format!("{:>12}", "compatible")
);
}
31 changes: 29 additions & 2 deletions crates/zeph-experiments/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ impl ParameterKind {

impl std::fmt::Display for ParameterKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
f.pad(self.as_str())
}
}

Expand Down Expand Up @@ -283,7 +283,7 @@ impl ExperimentSource {

impl std::fmt::Display for ExperimentSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
f.pad(self.as_str())
}
}

Expand Down Expand Up @@ -383,6 +383,33 @@ mod tests {
assert_eq!(ExperimentSource::Scheduled.to_string(), "scheduled");
}

/// Locks in the `f.pad` fix (#6066): `f.write_str` ignores width/fill/align flags.
/// `f.pad` must reproduce the same padding a plain `&str` would get under an
/// identical width specifier.
#[test]
fn parameter_kind_display_respects_width() {
assert_eq!(
format!("{:<20}", ParameterKind::TopK),
format!("{:<20}", "top_k")
);
assert_eq!(
format!("{:>20}", ParameterKind::SimilarityThreshold),
format!("{:>20}", "similarity_threshold")
);
}

#[test]
fn experiment_source_display_respects_width() {
assert_eq!(
format!("{:<12}", ExperimentSource::Manual),
format!("{:<12}", "manual")
);
assert_eq!(
format!("{:>12}", ExperimentSource::Scheduled),
format!("{:>12}", "scheduled")
);
}

#[test]
fn variation_value_int_display() {
let v = VariationValue::Int(42);
Expand Down
14 changes: 13 additions & 1 deletion crates/zeph-index/src/languages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ impl Lang {

impl std::fmt::Display for Lang {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.id())
f.pad(self.id())
}
}

Expand Down Expand Up @@ -455,4 +455,16 @@ mod tests {
assert_eq!(lang.to_string(), lang.id());
}
}

/// Locks in the `f.pad` fix (#6066): `f.write_str` ignores width/fill/align flags.
/// `f.pad` must reproduce the same padding a plain `&str` would get under an
/// identical width specifier.
#[test]
fn lang_display_respects_width() {
assert_eq!(format!("{:<12}", Lang::Rust), format!("{:<12}", "rust"));
assert_eq!(
format!("{:>12}", Lang::TypeScript),
format!("{:>12}", "typescript")
);
}
}
17 changes: 16 additions & 1 deletion crates/zeph-memory/src/graph/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl EntityType {

impl fmt::Display for EntityType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
f.pad(self.as_str())
}
}

Expand Down Expand Up @@ -625,6 +625,21 @@ mod tests {
}
}

/// Locks in the `f.pad` fix (#6066): `f.write_str` ignores width/fill/align flags.
/// `f.pad` must reproduce the same padding a plain `&str` would get under an
/// identical width specifier.
#[test]
fn entity_type_display_respects_width() {
assert_eq!(
format!("{:<15}", EntityType::Person),
format!("{:<15}", "person")
);
assert_eq!(
format!("{:>15}", EntityType::Organization),
format!("{:>15}", "organization")
);
}

#[test]
fn graph_fact_composite_score() {
let fact = GraphFact {
Expand Down
17 changes: 16 additions & 1 deletion crates/zeph-memory/src/optical_forgetting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl ContentFidelity {

impl std::fmt::Display for ContentFidelity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
f.pad(self.as_str())
}
}

Expand Down Expand Up @@ -413,6 +413,21 @@ mod tests {
assert!("unknown".parse::<ContentFidelity>().is_err());
}

/// Locks in the `f.pad` fix (#6066): `f.write_str` ignores width/fill/align flags.
/// `f.pad` must reproduce the same padding a plain `&str` would get under an
/// identical width specifier.
#[test]
fn content_fidelity_display_respects_width() {
assert_eq!(
format!("{:<15}", ContentFidelity::Full),
format!("{:<15}", "Full")
);
assert_eq!(
format!("{:>15}", ContentFidelity::SummaryOnly),
format!("{:>15}", "SummaryOnly")
);
}

#[test]
fn optical_forgetting_config_defaults() {
let cfg = OpticalForgettingConfig::default();
Expand Down
17 changes: 16 additions & 1 deletion crates/zeph-memory/src/store/trust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl SourceKind {

impl std::fmt::Display for SourceKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
f.pad(self.as_str())
}
}

Expand Down Expand Up @@ -600,6 +600,21 @@ mod tests {
assert_eq!(SourceKind::File.to_string(), "file");
}

/// Locks in the `f.pad` fix (#6066): `f.write_str` ignores width/fill/align flags.
/// `f.pad` must reproduce the same padding a plain `&str` would get under an
/// identical width specifier.
#[test]
fn source_kind_display_respects_width() {
assert_eq!(
format!("{:<10}", SourceKind::Local),
format!("{:<10}", "local")
);
assert_eq!(
format!("{:>10}", SourceKind::Bundled),
format!("{:>10}", "bundled")
);
}

#[test]
fn source_kind_from_str_local() {
let kind: SourceKind = "local".parse().unwrap();
Expand Down
17 changes: 16 additions & 1 deletion crates/zeph-memory/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl MemoryTier {

impl std::fmt::Display for MemoryTier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
f.pad(self.as_str())
}
}

Expand Down Expand Up @@ -224,6 +224,21 @@ mod tests {
assert!("unknown".parse::<MemoryTier>().is_err());
}

/// Locks in the `f.pad` fix (#6066): `f.write_str` ignores width/fill/align flags.
/// `f.pad` must reproduce the same padding a plain `&str` would get under an
/// identical width specifier.
#[test]
fn memory_tier_display_respects_width() {
assert_eq!(
format!("{:<10}", MemoryTier::Working),
format!("{:<10}", "working")
);
assert_eq!(
format!("{:>10}", MemoryTier::Semantic),
format!("{:>10}", "semantic")
);
}

#[test]
fn memory_tier_serde_round_trip() {
let json = serde_json::to_string(&MemoryTier::Semantic).unwrap();
Expand Down