feat(agent-interface): AgentProfile.harness — typed, executor-overridable harness preference#25
feat(agent-interface): AgentProfile.harness — typed, executor-overridable harness preference#25drewstone wants to merge 1 commit into
Conversation
…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
left a comment
There was a problem hiding this comment.
✅ 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
✅ No Blockers —
|
| 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 viaset(which is typed as AgentProfile) but cannot remove/clear it viaremove. This contradicts the PR's stated goal of making harness a first-class lever for improvement loops. Fix: addharness: 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
harnessfield 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,harnesswould 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)
mergeAgentProfileswhen base has harness and overlay doesn't, or vice versa; (b)applyAgentProfileDiffwith harness inset; (c)changedAgentProfileAxesdetecting 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
left a comment
There was a problem hiding this comment.
✅ 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
left a comment
There was a problem hiding this comment.
🟠 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?: HarnessTypeto theAgentProfileinterface (packages/agent-interface/src/agent-profile.ts:275-287), wires it throughagentProfileSchemawithharnessTypeSchema.optional()(packages/agent-interface/src/profile-schema.ts:167), and updates theharness.tsdesign note to clarify it is an executor-overridable preference, not profile identity. Includes 4 tests validating valid - Goals it achieves: 1) Formalize the
harnessfield 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
harnessinto the diff contract that already exists in the same package.modelis an analogous preference/hint axis and is fully supported: addharnesstoAgentProfileDiffAxis(packages/agent-interface/src/profile-diff.ts:8-22), addharness?: truetoAgentProfileDiffRemoval(packages/agent-interface/src/profile-diff.ts:41-57) andagentProfileDiffRemovalSchema(`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
AgentProfileinterface (agent-profile.ts:287), is validated byagentProfileSchema(profile-schema.ts:167), and is exported via index.ts:808-812. It reuses the EXISTINGHarnessType/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 existingcanonicalizeHarnessat 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
harnessentry inAgentProfileDiffAxis(profile-diff.ts:8-22),AgentProfileDiffRemoval(41-57),changedAgentProfileAxes(244-283), orpruneAgentProfileDiff(305-323). BecauseAgentProfileDiff.setIS a fullAgentProfile, a loop CAN set harness viaset.harness(it merges throughmergeAgentProfiles'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) andAgentProfileDiffRemoval(packages/agent-interface/src/profile-diff.ts:41-57) have noharnessentry;agentProfileDiffRemovalSchema(packages/agent-interface/src/profile-schema.ts:137-153) lacks it; andapplyRemoval/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.
What
Adds an optional, typed
harness?: HarnessTypetoAgentProfile.Why
profile.harnessis already read by runtimes — e.g. agent-runtimesupervisor-agent.tsdoesconst harness = profile.harness ?? nullandruntime.tscastsspec.harness as BackendType. It's a real field, just untyped and undeclared. This formalizes it: typed with the existingHarnessType, validated byharnessTypeSchema.The design decision (respecting the existing invariant)
harness.tsdeliberately documented harness as "NOT part of the portable profile identity — the same profile runs on any harness." That invariant is preserved:harnessis an optional preference, not identity —harness × modelaxis still sweeps one profile across every harness (executor overrides the preference);harnessbehaves exactly as before.The
harness.tsdesign 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.