Skip to content

feat(config): add HarnessPosture data model for per-model behavior profiles (#2693)#2741

Closed
idling11 wants to merge 1 commit into
Hmbown:mainfrom
idling11:feat/harness-posture
Closed

feat(config): add HarnessPosture data model for per-model behavior profiles (#2693)#2741
idling11 wants to merge 1 commit into
Hmbown:mainfrom
idling11:feat/harness-posture

Conversation

@idling11

@idling11 idling11 commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

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 #2693

Testing

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features
  • cargo test --workspace --all-features

Checklist

  • Updated docs or comments as needed
  • Added or updated tests where relevant
  • Verified TUI behavior manually if UI changes

Greptile Summary

Introduces the HarnessPosture and HarnessProfile data model for per-model behavior profiles, wiring it into ConfigToml.harness_profiles and covering it with 5 unit tests. No runtime wiring is included in this PR.

  • HarnessPostureKind is an enum with four variants (Standard, CacheHeavy, Lean, Custom); #[serde(other)] on Custom silently absorbs any unknown or misspelled string instead of returning a deserialization error.
  • HarnessPosture carries 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.
  • The manual Default impl for HarnessPosture duplicates the string literals already defined in the default_* 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 on Custom means a misspelled kind in 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 HarnessPostureKind enum's #[serde(other)] attribute and the duplicated default string literals in the Default impl.

Important Files Changed

Filename Overview
crates/config/src/lib.rs Adds HarnessPosture/HarnessProfile data model with serde support and 5 unit tests; #[serde(other)] on Custom silently swallows unknown/misspelled kind values, and default string values are duplicated between the manual Default impl 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" HarnessPostureKind
Loading

Fix All in Codex Fix All in Claude Code Fix All in Cursor

Reviews (1): Last reviewed commit: "feat(config): add HarnessPosture data mo..." | Re-trigger Greptile

Greptile also left 4 inline comments on this PR.

…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
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

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 .github/APPROVED_CONTRIBUTORS will be closed automatically.

Please read CONTRIBUTING.md for the expected contribution shape. A maintainer can grant PR access by commenting /lgtm on a pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread crates/config/src/lib.rs
Comment on lines +335 to +407
/// 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(),
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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,
        }
    }
}

Comment thread crates/config/src/lib.rs
Comment on lines +5210 to +5240
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");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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);
    }

Comment thread crates/config/src/lib.rs
Comment on lines +5258 to +5280
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");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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);
    }

Comment thread crates/config/src/lib.rs
Comment on lines +331 to +332
#[serde(other)]
Custom,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Fix in Codex Fix in Claude Code Fix in Cursor

Comment thread crates/config/src/lib.rs
Comment on lines +368 to +379
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(),
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Suggested change
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!

Fix in Codex Fix in Claude Code Fix in Cursor

Comment thread crates/config/src/lib.rs
Comment on lines +335 to +337
/// A concrete harness posture with policy knobs.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HarnessPosture {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Suggested change
/// 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!

Fix in Codex Fix in Claude Code Fix in Cursor

Comment thread crates/config/src/lib.rs
Comment on lines +409 to +411
/// A harness profile binds a posture to a provider route + model pattern.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct HarnessProfile {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 HarnessProfile is also missing PartialEq. Since HarnessPosture is a field of HarnessProfile, both should derive it together to allow whole-profile equality comparisons.

Suggested change
/// 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!

Fix in Codex Fix in Claude Code Fix in Cursor

@Hmbown

Hmbown commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Thanks @idling11 — the HarnessPosture data model was harvested into the public v0.9.0 integration branch (codex/v0.9.0-stewardship) as 586640a43.

The landed slice adds typed HarnessPostureKind, compaction/tool/safety enums, HarnessPosture, HarnessProfile, and ConfigToml.harness_profiles, with review fixes folded in: no silent unknown-kind fallback, unknown profile keys are rejected, and whole-struct equality is derived for future reload/runtime checks.

Verification evidence includes harness_posture_default_is_standard, harness_posture_factories_are_typed, harness_profile_serde_round_trips_as_a_whole_struct, config_toml_accepts_harness_profiles, harness_posture_kind_rejects_unknown_values, and harness_posture_rejects_unknown_policy_keys. The harvest commit includes Harvested from PR #2741 by @idling11 for #2693 and your GitHub-mappable co-author trailer.

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.

@Hmbown Hmbown closed this Jun 5, 2026
Hmbown added a commit that referenced this pull request Jun 5, 2026
Update the execution map after closing harvested or superseded PRs #2733, #2734, #2736, #2737, #2740, and #2741, and refresh the live PR count.
timothybrush pushed a commit to timothybrush/DeepSeek-TUI that referenced this pull request Jun 8, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v0.9.0 HarnessPosture: model-specific context and subagent policy

2 participants