Skip to content

feat(agent-interface): AgentProfile.harness — typed, executor-overridable harness preference#25

Open
drewstone wants to merge 1 commit into
mainfrom
feat/agentprofile-harness-preference
Open

feat(agent-interface): AgentProfile.harness — typed, executor-overridable harness preference#25
drewstone wants to merge 1 commit into
mainfrom
feat/agentprofile-harness-preference

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

What

Adds an optional, typed harness?: HarnessType to AgentProfile.

Why

profile.harness is already read by runtimes — e.g. agent-runtime supervisor-agent.ts does const harness = profile.harness ?? null and runtime.ts casts spec.harness as BackendType. It's a real field, just untyped and undeclared. This formalizes it: typed with the existing HarnessType, validated by harnessTypeSchema.

The design decision (respecting the existing invariant)

harness.ts deliberately documented harness as "NOT part of the portable profile identity — the same profile runs on any harness." That invariant is preserved: harness is an optional preference, not identity —

  • the leaderboard's harness × model axis still sweeps one profile across every harness (executor overrides the preference);
  • a supervisor spawning a worker may pick a harness per subtask (heterogeneous orchestration);
  • a profile with no harness behaves exactly as before.

The harness.ts design note is nuanced to say this explicitly. The upside: making it a typed first-class field lets a self-improvement loop optimize harness routing as a lever, and gives supervisors a clean typed way to request a harness per worker (instead of an untyped cast).

Tests

4 new (profile-harness.test.ts): accepts a valid HarnessType + round-trips; optional (identity stays harness-agnostic); rejects unknown; the same profile is valid with any harness swapped in. 37/37 package tests, types clean. Changeset: minor.

…able harness preference

Adds an optional typed `harness?: HarnessType` to AgentProfile. Runtimes
already read `profile.harness` untyped (agent-runtime supervisor-agent.ts casts
it `as BackendType`); this formalizes it as a first-class, typed field.

Deliberately a PREFERENCE, not identity: the same profile still runs on any
harness, and the executor may override it — the leaderboard's harness x model
axis sweeps one profile across every harness, and a supervisor may pick a
harness per spawned worker (heterogeneous orchestration). The harness.ts design
note is nuanced accordingly. Making it typed also lets an improvement loop
optimize harness routing as a lever. Zod schema + 4 tests (valid/optional/
rejects-unknown/identity-preserved); 37/37 package tests.

@tangletools tangletools left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Auto-approved drewstone PR — f36c0fdb

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

tangletools · auto-approval · reason: drewstone_author · 2026-07-07T18:24:11Z

@tangletools

Copy link
Copy Markdown

✅ No Blockers — f36c0fdb

Review health 100/100 · Reviewer score 69/100 · Confidence 70/100 · 3 findings (1 medium, 2 low)

deepseek kimi-code aggregate
Readiness 69 85 69
Confidence 70 70 70
Correctness 69 85 69
Security 69 85 69
Testing 69 85 69
Architecture 69 85 69

Reviewer score is advisory once the run is complete and the verdict has no blockers.

Full multi-shot audit completed 2/2 planned shots over 5 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 2/2 planned shots over 5 changed files. Global verifier still owns final merge decision.

🟠 MEDIUM harness missing from profile-diff removal schema — packages/agent-interface/src/profile-schema.ts

agentProfileDiffRemovalSchema enumerates removable axes (identity, tags, prompt, model, permissions, tools, mcp, connections, subagents, resources, hooks, modes, confidential, metadata, extensions) but does not include harness. Since AgentProfile.harness is now a first-class optional field, a profile diff can set it via set (which is typed as AgentProfile) but cannot remove/clear it via remove. This contradicts the PR's stated goal of making harness a first-class lever for improvement loops. Fix: add harness: z.literal(true).optional() to agentProfileDiffRemovalSchema and the corresponding AgentProfileDiffRemoval interface/applyRemoval/changedAgentProfileAxes/pruneAgentProfileDiff logic in profile-diff.ts.

🟡 LOW mergeAgentProfiles handles harness only via implicit spread — no explicit merge logic — packages/agent-interface/src/agent-profile.ts

The harness field survives the merge only through the catch-all ...(base ?? {}), ...(overlay ?? {}) on lines 442–444. Every other top-level field (prompt, permissions, tools, mcp, connections, subagents, resources, hooks, modes, metadata, extensions) has explicit merge logic or is intentionally not covered (tags). While the spread behavior is correct today (overlay wins), it is brittle: if someone refactors the merge function to use explicit field-by-field assignment, harness would silently be dropped. This is low-risk because the spread pattern has been stable, but it's inconsistent with the rest of the function. F

🟡 LOW No test for harness in merge/diff operations — packages/agent-interface/src/profile-harness.test.ts

The test file covers schema parsing (accept, optional, reject, identity-preservation) but does not test: (a) mergeAgentProfiles when base has harness and overlay doesn't, or vice versa; (b) applyAgentProfileDiff with harness in set; (c) changedAgentProfileAxes detecting harness changes (currently won't pass since harness is missing from the axis list — see medium finding above). These are the paths most likely to be exercised in the 'improvement loop' use case from the docstring. Fix: add merge and diff tests once harness is added to the diff axis machinery.


tangletools · 2026-07-07T18:35:47Z · trace

@tangletools tangletools left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Approved — 3 non-blocking findings — f36c0fdb

Full multi-shot audit completed 2/2 planned shots over 5 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 2/2 planned shots over 5 changed files. Global verifier still owns final merge decision.

Full immutable report for this review: trace

Summary comment for this run: full summary


tangletools · 2026-07-07T18:35:47Z · immutable trace

@tangletools tangletools left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 Value Audit — better-approach-exists

Verdict better-approach-exists
Concerns 2 (1 medium-concern, 1 weak-concern)
Heuristic 0.0s
Duplication 0.0s
Interrogation 205.6s (2 bridge agents)
Total 205.6s

💰 Value — better-approach-exists

Adds a typed, optional harness preference to AgentProfile, which is coherent and well-tested, but it stops short of integrating the new lever into the package's existing profile-diff/improvement-loop contract.

  • What it does: Adds harness?: HarnessType to the AgentProfile interface (packages/agent-interface/src/agent-profile.ts:275-287), wires it through agentProfileSchema with harnessTypeSchema.optional() (packages/agent-interface/src/profile-schema.ts:167), and updates the harness.ts design note to clarify it is an executor-overridable preference, not profile identity. Includes 4 tests validating valid
  • Goals it achieves: 1) Formalize the harness field that runtimes already consume as an untyped cast, giving it a single shared type and schema. 2) Make harness routing a typed, first-class lever that self-improvement loops can set and that supervisors can use to request a harness per worker. 3) Preserve the existing invariant that profile identity is harness-agnostic, so executors can still override it per run.
  • Assessment: The change is coherent and follows the package's grain: it reuses the existing HarnessType/harnessTypeSchema (packages/agent-interface/src/harness.ts:21-57), respects the schema/interface drift guard, and keeps the field optional so backward compatibility is intact. Tests and typecheck pass (pnpm --filter ./packages/agent-interface test -- --no-file-parallelism → 37/37; `pnpm --filter ./pa
  • Better / existing approach: Integrate harness into the diff contract that already exists in the same package. model is an analogous preference/hint axis and is fully supported: add harness to AgentProfileDiffAxis (packages/agent-interface/src/profile-diff.ts:8-22), add harness?: true to AgentProfileDiffRemoval (packages/agent-interface/src/profile-diff.ts:41-57) and agentProfileDiffRemovalSchema (`packages/
  • Model: opencode/kimi-for-coding/k2p7
  • Bridge attempts: 1

🎯 Usefulness — sound-with-nits

A minimal, in-grain formalization: types and validates the optional harness field runtimes already read untyped, preserving the identity-vs-execution invariant.

  • Integration: Reachable and consistent. The field lands on the public AgentProfile interface (agent-profile.ts:287), is validated by agentProfileSchema (profile-schema.ts:167), and is exported via index.ts:808-812. It reuses the EXISTING HarnessType/harnessTypeSchema (harness.ts:21-57) — no new enum is introduced, and the existing compile-time drift guards (harness.ts:73-82, profile-schema.ts:218-227) n
  • Fit with existing patterns: Excellent. Mirrors the established optional-preference pattern exactly — model?: AgentProfileModelHints (agent-profile.ts:274) is the direct precedent: an optional preference the executor may override, sitting right above the new field. The harness.ts doc-comment nuance (harness.ts:6-12) preserves the documented invariant ('NOT part of profile identity') while admitting the optional preference.
  • Real-world viability: Solid on the happy and edge paths. Optional (existing profiles parse unchanged), validated (unknown harnesses rejected — covered by the new test at profile-harness.test.ts:17), alias-tolerant (a profile with harness: "claude" parses; consumers canonicalize via the existing canonicalizeHarness at harness.ts:67, matching how every other layer treats harness input). The drift guard prevents type/
  • Model: opencode/zai-coding-plan/glm-5.2
  • Bridge attempts: 1

🎯 Usefulness Audit

🟡 Diff-axis machinery doesn't include harness, so the cited 'improvement loop optimizes harness routing' can SET it but not report/prune/remove it [integration] ``

The PR body states the typed field 'lets an improvement loop optimize harness routing as a first-class lever.' profile-diff.ts — the artifact an improvement loop emits — has no harness entry in AgentProfileDiffAxis (profile-diff.ts:8-22), AgentProfileDiffRemoval (41-57), changedAgentProfileAxes (244-283), or pruneAgentProfileDiff (305-323). Because AgentProfileDiff.set IS a full AgentProfile, a loop CAN set harness via set.harness (it merges through mergeAgentProfiles's spread

💰 Value Audit

🟠 New harness field is not integrated with the AgentProfileDiff contract [better-architecture] ``

The PR explicitly says harness becomes a 'first-class lever an improvement loop can optimize,' but the diff system in the same package does not treat it as an axis. AgentProfileDiffAxis (packages/agent-interface/src/profile-diff.ts:8-22) and AgentProfileDiffRemoval (packages/agent-interface/src/profile-diff.ts:41-57) have no harness entry; agentProfileDiffRemovalSchema (packages/agent-interface/src/profile-schema.ts:137-153) lacks it; and applyRemoval/changedAgentProfileAxes/`p


What this audit checks

It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.

Pass What it asks
Heuristic Vague title? Whitespace-only or cruft-bearing diff? (content signals only)
Duplication Do added function/class names already exist elsewhere in the repo?
Value Audit What does it do? What goal does it achieve? Is it good? Better architecture or already-exists?
Usefulness Audit Does it integrate and fit? Will it hold up in real use and actually get used?

Findings are concerns, not blocks — the human reviewer decides what to do with them.

value-audit · 20260707T183623Z

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.

2 participants