feat(config): add HarnessPosture data model for per-model behavior profiles (#2693)#2741
feat(config): add HarnessPosture data model for per-model behavior profiles (#2693)#2741idling11 wants to merge 1 commit into
Conversation
…ofiles (Hmbown#2693) Introduce the data model for per-model behavior profiles (harness postures). A posture encodes model-specific idioms and failure modes as a falsifiable contract that configures prompt layering, tool surface, sub-agent fan-out, compaction strategy, and safety posture. Changes: - `crates/config/src/lib.rs`: - `HarnessPostureKind` enum: Standard, CacheHeavy, Lean, Custom - `HarnessPosture` struct: kind + max_subagents, prefer_codebase_search, compaction_strategy, tool_surface, safety_posture - `HarnessPosture::cache_heavy()` — tuned for DeepSeek V4 / MiMo - `HarnessPosture::lean()` — tuned for small-context / weak models - `HarnessProfile` struct: binds posture to provider route + model pattern - `ConfigToml.harness_profiles: Vec<HarnessProfile>` — TOML integration - 5 new unit tests covering defaults, factory constructors, serde round-trip, and TOML deserialisation Non-goals for this PR: - Wiring posture into the runtime (will be in follow-up PRs) - /config CLI integration - Sidebar/tray posture display Closes Hmbown#2693
|
Thanks @idling11 for taking the time to contribute. This repository is currently observing a maintainer-managed contribution gate in dry-run mode, so this pull request is staying open. When enforcement is enabled, pull requests from contributors who are not listed in Please read |
There was a problem hiding this comment.
Code Review
This pull request introduces per-model behavior profiles (Harness Postures) to configure prompt layering, tool surface, sub-agent fan-out, compaction strategy, and safety posture. It adds the HarnessPostureKind, HarnessPosture, and HarnessProfile structs, integrates them into ConfigToml, and includes comprehensive unit tests. The review feedback suggests improving type safety by replacing raw String fields in HarnessPosture (such as compaction_strategy, tool_surface, and safety_posture) with strongly-typed enums, and provides the necessary updates for both the structs and their corresponding unit tests.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| /// A concrete harness posture with policy knobs. | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct HarnessPosture { | ||
| /// Named posture kind. | ||
| #[serde(default)] | ||
| pub kind: HarnessPostureKind, | ||
| /// Maximum number of concurrent sub-agents (0 = default). | ||
| #[serde(default)] | ||
| pub max_subagents: usize, | ||
| /// Prefer search-based context over always-on documentation. | ||
| #[serde(default)] | ||
| pub prefer_codebase_search: bool, | ||
| /// Compaction strategy: "prefix-cache" | "aggressive" | "default". | ||
| #[serde(default = "default_compaction_strategy")] | ||
| pub compaction_strategy: String, | ||
| /// Tool surface mode: "full" | "read-only" | "auto". | ||
| #[serde(default = "default_tool_surface")] | ||
| pub tool_surface: String, | ||
| /// Safety posture: "standard" | "strict" | "permissive". | ||
| #[serde(default = "default_safety_posture")] | ||
| pub safety_posture: String, | ||
| } | ||
|
|
||
| fn default_compaction_strategy() -> String { | ||
| "default".into() | ||
| } | ||
| fn default_tool_surface() -> String { | ||
| "full".into() | ||
| } | ||
| fn default_safety_posture() -> String { | ||
| "standard".into() | ||
| } | ||
|
|
||
| impl Default for HarnessPosture { | ||
| fn default() -> Self { | ||
| Self { | ||
| kind: HarnessPostureKind::Standard, | ||
| max_subagents: 0, | ||
| prefer_codebase_search: false, | ||
| compaction_strategy: "default".into(), | ||
| tool_surface: "full".into(), | ||
| safety_posture: "standard".into(), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl HarnessPosture { | ||
| /// A cache-heavy posture tuned for DeepSeek V4 / MiMo models. | ||
| #[must_use] | ||
| pub fn cache_heavy() -> Self { | ||
| Self { | ||
| kind: HarnessPostureKind::CacheHeavy, | ||
| max_subagents: 10, | ||
| prefer_codebase_search: false, | ||
| compaction_strategy: "prefix-cache".into(), | ||
| tool_surface: "full".into(), | ||
| safety_posture: "standard".into(), | ||
| } | ||
| } | ||
|
|
||
| /// A lean posture for small-context / weak tool-use models. | ||
| #[must_use] | ||
| pub fn lean() -> Self { | ||
| Self { | ||
| kind: HarnessPostureKind::Lean, | ||
| max_subagents: 20, | ||
| prefer_codebase_search: true, | ||
| compaction_strategy: "aggressive".into(), | ||
| tool_surface: "full".into(), | ||
| safety_posture: "standard".into(), | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Using String for policy knobs like compaction_strategy, tool_surface, and safety_posture reduces type safety and requires manual string matching/validation. Defining these as strongly-typed enums with #[derive(Serialize, Deserialize)] and #[serde(rename_all = "kebab-case")] leverages Rust's type system to enforce valid values at compile and deserialization time. This also eliminates the need for helper functions like default_compaction_strategy.
/// Compaction strategy options.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "kebab-case")]
pub enum CompactionStrategy {
#[default]
Default,
PrefixCache,
Aggressive,
}
/// Tool surface modes.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "kebab-case")]
pub enum ToolSurface {
#[default]
Full,
ReadOnly,
Auto,
}
/// Safety posture options.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "kebab-case")]
pub enum SafetyPosture {
#[default]
Standard,
Strict,
Permissive,
}
/// A concrete harness posture with policy knobs.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HarnessPosture {
/// Named posture kind.
#[serde(default)]
pub kind: HarnessPostureKind,
/// Maximum number of concurrent sub-agents (0 = default).
#[serde(default)]
pub max_subagents: usize,
/// Prefer search-based context over always-on documentation.
#[serde(default)]
pub prefer_codebase_search: bool,
/// Compaction strategy.
#[serde(default)]
pub compaction_strategy: CompactionStrategy,
/// Tool surface mode.
#[serde(default)]
pub tool_surface: ToolSurface,
/// Safety posture.
#[serde(default)]
pub safety_posture: SafetyPosture,
}
impl Default for HarnessPosture {
fn default() -> Self {
Self {
kind: HarnessPostureKind::Standard,
max_subagents: 0,
prefer_codebase_search: false,
compaction_strategy: CompactionStrategy::Default,
tool_surface: ToolSurface::Full,
safety_posture: SafetyPosture::Standard,
}
}
}
impl HarnessPosture {
/// A cache-heavy posture tuned for DeepSeek V4 / MiMo models.
#[must_use]
pub fn cache_heavy() -> Self {
Self {
kind: HarnessPostureKind::CacheHeavy,
max_subagents: 10,
prefer_codebase_search: false,
compaction_strategy: CompactionStrategy::PrefixCache,
tool_surface: ToolSurface::Full,
safety_posture: SafetyPosture::Standard,
}
}
/// A lean posture for small-context / weak tool-use models.
#[must_use]
pub fn lean() -> Self {
Self {
kind: HarnessPostureKind::Lean,
max_subagents: 20,
prefer_codebase_search: true,
compaction_strategy: CompactionStrategy::Aggressive,
tool_surface: ToolSurface::Full,
safety_posture: SafetyPosture::Standard,
}
}
}| fn harness_posture_default_is_standard() { | ||
| let posture = HarnessPosture::default(); | ||
| assert_eq!(posture.kind, HarnessPostureKind::Standard); | ||
| assert_eq!(posture.max_subagents, 0); | ||
| assert!(!posture.prefer_codebase_search); | ||
| assert_eq!(posture.compaction_strategy, "default"); | ||
| assert_eq!(posture.tool_surface, "full"); | ||
| assert_eq!(posture.safety_posture, "standard"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn harness_posture_cache_heavy_is_correct() { | ||
| let posture = HarnessPosture::cache_heavy(); | ||
| assert_eq!(posture.kind, HarnessPostureKind::CacheHeavy); | ||
| assert_eq!(posture.max_subagents, 10); | ||
| assert!(!posture.prefer_codebase_search); | ||
| assert_eq!(posture.compaction_strategy, "prefix-cache"); | ||
| assert_eq!(posture.tool_surface, "full"); | ||
| assert_eq!(posture.safety_posture, "standard"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn harness_posture_lean_is_correct() { | ||
| let posture = HarnessPosture::lean(); | ||
| assert_eq!(posture.kind, HarnessPostureKind::Lean); | ||
| assert_eq!(posture.max_subagents, 20); | ||
| assert!(posture.prefer_codebase_search); | ||
| assert_eq!(posture.compaction_strategy, "aggressive"); | ||
| assert_eq!(posture.tool_surface, "full"); | ||
| assert_eq!(posture.safety_posture, "standard"); | ||
| } |
There was a problem hiding this comment.
Update the tests to assert against the new strongly-typed enums instead of raw string literals.
#[test]
fn harness_posture_default_is_standard() {
let posture = HarnessPosture::default();
assert_eq!(posture.kind, HarnessPostureKind::Standard);
assert_eq!(posture.max_subagents, 0);
assert!(!posture.prefer_codebase_search);
assert_eq!(posture.compaction_strategy, CompactionStrategy::Default);
assert_eq!(posture.tool_surface, ToolSurface::Full);
assert_eq!(posture.safety_posture, SafetyPosture::Standard);
}
#[test]
fn harness_posture_cache_heavy_is_correct() {
let posture = HarnessPosture::cache_heavy();
assert_eq!(posture.kind, HarnessPostureKind::CacheHeavy);
assert_eq!(posture.max_subagents, 10);
assert!(!posture.prefer_codebase_search);
assert_eq!(posture.compaction_strategy, CompactionStrategy::PrefixCache);
assert_eq!(posture.tool_surface, ToolSurface::Full);
assert_eq!(posture.safety_posture, SafetyPosture::Standard);
}
#[test]
fn harness_posture_lean_is_correct() {
let posture = HarnessPosture::lean();
assert_eq!(posture.kind, HarnessPostureKind::Lean);
assert_eq!(posture.max_subagents, 20);
assert!(posture.prefer_codebase_search);
assert_eq!(posture.compaction_strategy, CompactionStrategy::Aggressive);
assert_eq!(posture.tool_surface, ToolSurface::Full);
assert_eq!(posture.safety_posture, SafetyPosture::Standard);
}| fn config_toml_accepts_harness_profiles() { | ||
| let toml_str = r#" | ||
| provider = "deepseek" | ||
| model = "deepseek-v4-pro" | ||
|
|
||
| [[harness_profiles]] | ||
| provider_route = "deepseek" | ||
| model_pattern = "deepseek-v4.*" | ||
|
|
||
| [harness_profiles.posture] | ||
| kind = "cache-heavy" | ||
| max_subagents = 10 | ||
| compaction_strategy = "prefix-cache" | ||
| "#; | ||
| let config: ConfigToml = toml::from_str(toml_str).unwrap(); | ||
| assert_eq!(config.harness_profiles.len(), 1); | ||
| let profile = &config.harness_profiles[0]; | ||
| assert_eq!(profile.provider_route, "deepseek"); | ||
| assert_eq!(profile.model_pattern, "deepseek-v4.*"); | ||
| assert_eq!(profile.posture.kind, HarnessPostureKind::CacheHeavy); | ||
| assert_eq!(profile.posture.max_subagents, 10); | ||
| assert_eq!(profile.posture.compaction_strategy, "prefix-cache"); | ||
| } |
There was a problem hiding this comment.
Update the deserialization test to assert against the strongly-typed CompactionStrategy enum.
#[test]
fn config_toml_accepts_harness_profiles() {
let toml_str = r#"
provider = "deepseek"
model = "deepseek-v4-pro"
[[harness_profiles]]
provider_route = "deepseek"
model_pattern = "deepseek-v4.*"
[harness_profiles.posture]
kind = "cache-heavy"
max_subagents = 10
compaction_strategy = "prefix-cache"
"#;
let config: ConfigToml = toml::from_str(toml_str).unwrap();
assert_eq!(config.harness_profiles.len(), 1);
let profile = &config.harness_profiles[0];
assert_eq!(profile.provider_route, "deepseek");
assert_eq!(profile.model_pattern, "deepseek-v4.*");
assert_eq!(profile.posture.kind, HarnessPostureKind::CacheHeavy);
assert_eq!(profile.posture.max_subagents, 10);
assert_eq!(profile.posture.compaction_strategy, CompactionStrategy::PrefixCache);
}| #[serde(other)] | ||
| Custom, |
There was a problem hiding this comment.
Silent catch-all hides typos in
kind values
#[serde(other)] on Custom means any unrecognized string — including typos like "cahce-heavy" or "standard " (trailing space) — silently deserializes as Custom instead of returning a DeserializeError. A user who misspells their posture kind will get silently wrong behavior, with no indication that their config was misread.
| impl Default for HarnessPosture { | ||
| fn default() -> Self { | ||
| Self { | ||
| kind: HarnessPostureKind::Standard, | ||
| max_subagents: 0, | ||
| prefer_codebase_search: false, | ||
| compaction_strategy: "default".into(), | ||
| tool_surface: "full".into(), | ||
| safety_posture: "standard".into(), | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Default value duplication risk —
Default::default() repeats the same string literals already defined in default_compaction_strategy, default_tool_surface, and default_safety_posture. If either side is updated without changing the other the two sources of truth diverge. Call the default functions directly to keep a single source of truth.
| impl Default for HarnessPosture { | |
| fn default() -> Self { | |
| Self { | |
| kind: HarnessPostureKind::Standard, | |
| max_subagents: 0, | |
| prefer_codebase_search: false, | |
| compaction_strategy: "default".into(), | |
| tool_surface: "full".into(), | |
| safety_posture: "standard".into(), | |
| } | |
| } | |
| } | |
| impl Default for HarnessPosture { | |
| fn default() -> Self { | |
| Self { | |
| kind: HarnessPostureKind::Standard, | |
| max_subagents: 0, | |
| prefer_codebase_search: false, | |
| compaction_strategy: default_compaction_strategy(), | |
| tool_surface: default_tool_surface(), | |
| safety_posture: default_safety_posture(), | |
| } | |
| } | |
| } |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| /// A concrete harness posture with policy knobs. | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct HarnessPosture { |
There was a problem hiding this comment.
HarnessPosture and HarnessProfile are missing PartialEq derives, even though HarnessPostureKind already has it. The round-trip test manually checks individual fields because whole-struct comparison is unavailable. Adding PartialEq makes future tests and runtime equality checks (e.g. detecting whether a reload changed the active profile) straightforward.
| /// A concrete harness posture with policy knobs. | |
| #[derive(Debug, Clone, Serialize, Deserialize)] | |
| pub struct HarnessPosture { | |
| /// A concrete harness posture with policy knobs. | |
| #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] | |
| pub struct HarnessPosture { |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| /// A harness profile binds a posture to a provider route + model pattern. | ||
| #[derive(Debug, Clone, Serialize, Deserialize, Default)] | ||
| pub struct HarnessProfile { |
There was a problem hiding this comment.
HarnessProfile is also missing PartialEq. Since HarnessPosture is a field of HarnessProfile, both should derive it together to allow whole-profile equality comparisons.
| /// A harness profile binds a posture to a provider route + model pattern. | |
| #[derive(Debug, Clone, Serialize, Deserialize, Default)] | |
| pub struct HarnessProfile { | |
| /// A harness profile binds a posture to a provider route + model pattern. | |
| #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)] | |
| pub struct HarnessProfile { |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
Thanks @idling11 — the HarnessPosture data model was harvested into the public v0.9.0 integration branch ( The landed slice adds typed Verification evidence includes Closing this PR as harvested into the integration branch. Issue #2693 remains open for the runtime provider/model posture wiring, telemetry, and docs that were intentionally left as follow-up scope. |
Harvested from PR Hmbown#2741 by @idling11 for Hmbown#2693, with review fixes folded in: typed compaction/tool/safety enums, no silent unknown-kind fallback, unknown profile keys rejected, and whole-struct equality for future reload/runtime checks. Co-authored-by: idling11 <8055620+idling11@users.noreply.github.com>
Introduce the data model for per-model behavior profiles (harness
postures). A posture encodes model-specific idioms and failure modes as
a falsifiable contract that configures prompt layering, tool surface,
sub-agent fan-out, compaction strategy, and safety posture.
Changes:
crates/config/src/lib.rs:HarnessPostureKindenum: Standard, CacheHeavy, Lean, CustomHarnessPosturestruct: kind + max_subagents,prefer_codebase_search, compaction_strategy, tool_surface,
safety_posture
HarnessPosture::cache_heavy()— tuned for DeepSeek V4 / MiMoHarnessPosture::lean()— tuned for small-context / weak modelsHarnessProfilestruct: binds posture to provider route +model pattern
ConfigToml.harness_profiles: Vec<HarnessProfile>— TOMLintegration
serde round-trip, and TOML deserialisation
Non-goals for this PR:
Closes #2693
Testing
cargo fmt --all -- --checkcargo clippy --workspace --all-targets --all-featurescargo test --workspace --all-featuresChecklist
Greptile Summary
Introduces the
HarnessPostureandHarnessProfiledata model for per-model behavior profiles, wiring it intoConfigToml.harness_profilesand covering it with 5 unit tests. No runtime wiring is included in this PR.HarnessPostureKindis an enum with four variants (Standard,CacheHeavy,Lean,Custom);#[serde(other)]onCustomsilently absorbs any unknown or misspelled string instead of returning a deserialization error.HarnessPosturecarries six policy knobs (max_subagents,prefer_codebase_search,compaction_strategy,tool_surface,safety_posture); three string fields accept arbitrary values with no enum-level validation.Defaultimpl forHarnessPostureduplicates the string literals already defined in thedefault_*serde helper functions, creating two sources of truth that can drift.Confidence Score: 3/5
Safe to merge as a data-model-only change, but the silent catch-all on unknown posture kinds should be addressed before the runtime wiring lands.
The
#[serde(other)]catch-all onCustommeans a misspelledkindin a user's config file will silently apply the wrong posture instead of surfacing an error. Because this is still an unreferenced data model with no runtime wiring, the blast radius is currently zero — but the defect will be live the moment the follow-up PR consumes these profiles.crates/config/src/lib.rs — the
HarnessPostureKindenum's#[serde(other)]attribute and the duplicated default string literals in theDefaultimpl.Important Files Changed
#[serde(other)]onCustomsilently swallows unknown/misspelled kind values, and default string values are duplicated between the manualDefaultimpl and the serde default functions.Class Diagram
%%{init: {'theme': 'neutral'}}%% classDiagram class ConfigToml { +harness_profiles: Vec~HarnessProfile~ } class HarnessProfile { +provider_route: String +model_pattern: String +posture: HarnessPosture } class HarnessPosture { +kind: HarnessPostureKind +max_subagents: usize +prefer_codebase_search: bool +compaction_strategy: String +tool_surface: String +safety_posture: String +cache_heavy()$ HarnessPosture +lean()$ HarnessPosture +default()$ HarnessPosture } class HarnessPostureKind { <<enumeration>> Standard CacheHeavy Lean Custom } ConfigToml "1" o-- "0..*" HarnessProfile HarnessProfile "1" *-- "1" HarnessPosture HarnessPosture "1" *-- "1" HarnessPostureKindReviews (1): Last reviewed commit: "feat(config): add HarnessPosture data mo..." | Re-trigger Greptile