feat(harness): add forge and cursor to the canonical harness enum - #105
Conversation
Both ship full provider adapters downstream (sdk-provider-forge, sdk-provider-cursor) but were absent from HarnessType, which forced agent-dev-container and agent-app to keep divergent copies of the harness taxonomy and made agent-app's documented "superset of HarnessType" claim unenforceable. They are multi-provider CLI runners with no vendor lock, so they need no entry in the capability tables and resolve as router-backed. harness.test.ts pins harnessTypeSchema.options so downstream packages can derive from it.
tangletools
left a comment
There was a problem hiding this comment.
✅ Auto-approved drewstone PR — 802d0946
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-31T17:28:22Z
tangletools
left a comment
There was a problem hiding this comment.
🟢 Value Audit — sound
| Verdict | sound |
| Concerns | 0 (none) |
| Heuristic | 0.0s |
| Duplication | 0.0s |
| Interrogation | 145.4s (2 bridge agents) |
| Total | 145.4s |
💰 Value — sound
Adds forge and cursor to the canonical HarnessType enum — two missing multi-provider CLI harnesses whose provider adapters already exist downstream. Minimal, in-grain, tight.
- What it does: Adds
forgeandcursoras string literal members to theHarnessTypeunion type and theharnessTypeSchemaZod enum inpackages/agent-interface/src/harness.ts(the canonical harness definition). Also adds a newharness.test.tsthat pins the full set of 15 canonical harness IDs and tests they validate, plus a capability test confirming both are router-backed with no vendor lock. - Goals it achieves: Eliminates the divergence between the canonical harness enum (in this repo) and the downstream provider adapters that already ship for forge and cursor (in other repos). Once merged, agent-dev-container's
BACKEND_TYPEand agent-app'sKNOWN_HARNESSEScan derive from this single source of truth instead of maintaining divergent copies. The compile-timeMutuallyAssignabledrift guard keeps the T - Assessment: Sound, minimal change that follows the codebase's existing patterns exactly. Both forge and cursor are multi-provider CLI runners with no vendor lock, so they need no entries in any of the
Partial<Record<HarnessType, ...>>capability tables — they correctly fall through to router-backed defaults (harness-capabilities.ts:64-66). The new test file mirrors the same validation approach the codebase - Better / existing approach: none — this is the right approach. The canonical enum is the authoritative list by design. Adding two IDs to it is the only coherent action when downstream packages already ship adapters for those IDs. No existing mechanism in this repo (grep'd for BACKEND_TYPE, KNOWN_HARNESSES) auto-derives or consolidates harness values — the canonical enum is the mechanism.
- Model: opencode/deepseek/deepseek-v4-pro
- Bridge attempts: 3
- Bridge warning: opencode/kimi-for-coding/k2p7: Bridge returned 503: {"error":{"message":"cli-bridge admission timed out after 30000ms","type":"admission_rejected","reason":"queue_timeout","admission":{"active":20,"queued":0,"maxActive":20,"maxQueue":48}}}; opencode/zai-coding-plan/glm-5.2: Bridge returned 503: {"error":{"message":"cli-bridge admission timed out after 30000ms","type":"admission_rejected","reason":
🎯 Usefulness — error
usefulness agent produced no parseable value-audit JSON.
- Model: opencode/deepseek/deepseek-v4-pro
- Bridge attempts: 3
- Bridge error: opencode/zai-coding-plan/glm-5.2: Bridge returned 503: {"error":{"message":"cli-bridge admission timed out after 30000ms","type":"admission_rejected","reason":"queue_timeout","admission":{"active":20,"queued":1,"maxActive":20,"maxQueue":48}}}; opencode/kimi-for-coding/k2p7: Bridge returned 503: {"error":{"message":"cli-bridge admission timed out after 30000ms","type":"admission_rejected","reason":
No concerns — sound change, no better or existing approach found. ✅
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.
✅ No Blockers —
|
| glm | deepseek | deepseek-flash | aggregate | |
|---|---|---|---|---|
| Readiness | 89 | 92 | 89 | 89 |
| Confidence | 70 | 70 | 70 | 70 |
| Correctness | 89 | 92 | 89 | 89 |
| Security | 89 | 92 | 89 | 89 |
| Testing | 89 | 92 | 89 | 89 |
| Architecture | 89 | 92 | 89 | 89 |
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 4 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 2/2 planned shots over 4 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 2/2 planned shots over 4 changed files. Global verifier still owns final merge decision.
🟡 LOW No explicit reasoning-effort test for new router-backed harnesses — packages/agent-interface/src/harness-capabilities.test.ts
The test verifies providers, model support, snapping, and selectors for forge/cursor, but not harnessReasoningEfforts. Since forge/cursor have no entry in harnessReasoningEffortsOverride or harnessReasoningCeiling, they default to the full reasoningLadder (harness-capabilities.ts:193). This is correct — same behavior as opencode — but no test proves it. Add
expect(harnessReasoningEfforts('forge')).toEqual(reasoningLadder)to lock in the contract and prevent a future capability-table entry from silently changing the output.
🟡 LOW forge/cursor selector+effort capabilities are un-audited defaults, not adapter facts — packages/agent-interface/src/harness-capabilities.test.ts
The new test pins harnessHonorsSelectors('forge')/('cursor') === true and (via no override/ceiling entry) the full reasoning ladder. But in harness-capabilities.ts these are DEFAULT-BY-ABSENCE behaviors: harnessIgnoresModel/harnessIgnoresEffort/harnessReasoningEffortsOverride/harnessReasoningCeiling classify every other harness from a documented 'cli-bridge adapter audit' (see comment at harness-capabilities.ts:227-237), while forge/cursor get no entry and silently inherit 'honors everything'. The changeset asserts downstream already relies on router-backed behavior, but nothing in this repo validates that the real sdk-provider-forge / sdk-provider-cursor adapters honor per-turn model/effort selectors — if they drop them (as many CLI runners do), the picker would silently ignore the user's
🟡 LOW forge/cursor selector-honoring is default-inherited, not adapter-verified — packages/agent-interface/src/harness-capabilities.test.ts
The new test asserts
harnessHonorsSelectors('forge'|'cursor')istrue, and it passes — but only because both harnesses are absent fromharnessIgnoresModel/harnessIgnoresEffortin harness-capabilities.ts:239,250. Every other entry in those sets is documented as 'grounded in the cli-bridge adapter audit, NOT a guess' (harness-capabilities.ts:227-228). forge (tailcallhq/forgecode) and cursor (cursor-agent) get the honor-by-default path with no adapter evidence cited in this repo. The test therefore confirms the table default, not the real CLI behavior — it would stay green even if a downstream adapter actually dropped the per-turn model/effort. Impact is low: both are explicitly designated router-backed (caller's selections are respected by construction), so the claim is defensible; b
🟡 LOW Third harness.test.ts test is redundant with the exact-set pin — packages/agent-interface/src/harness.test.ts
The 'carries the CLI runners...' test (lines 43-49) asserts only that harnessTypeSchema.options contains 'forge' and 'cursor' — both already enforced by the first test's exact-set equality (CANONICAL_HARNESSES includes forge/cursor at lines 23-24). It adds no failing signal beyond the pin and doubles the maintenance surface when the list next changes. Either fold the rationale into the CANONICAL_HARNESSES comment and delete the third test, or keep it only as documented history. Harmless; does not affect
🟡 LOW harness.test.ts downstream-derivation claim is not enforceable from this repo — packages/agent-interface/src/harness.test.ts
The header comment states this list is what 'agent-dev-container's
BACKEND_TYPEand agent-app'sKNOWN_HARNESSES' derive from. Those packages live in other repos and are not checked here, so the comment is a statement of intent rather than something the test guards. The test itself only pins the in-package canonical set againstharnessTypeSchema.options— which is genuinely useful as a regression guard against accidental add/remove within this repo. No functional defect; just flagging that a future reader shouldn't read the comment as evidence of cross-repo coverage. Optional: soften wording to 'is intended to be the canonical set downstream packages derive from' or add a TODO linking the downstream sync check.
tangletools · 2026-07-31T17:37:45Z · trace
tangletools
left a comment
There was a problem hiding this comment.
✅ Approved — 5 non-blocking findings — 802d0946
Full multi-shot audit completed 2/2 planned shots over 4 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 2/2 planned shots over 4 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 2/2 planned shots over 4 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-31T17:37:45Z · immutable trace
The separate forge/cursor membership test asserted only what the exact-set pin already enforces, so it added no failing signal and doubled the maintenance surface when the list next changes.
Problem
Four enums describe "which coding agent runs this profile", and they disagree:
HarnessType/harnessTypeSchema(canonical, has a drift guard)packages/agent-interface/src/harness.tsBACKEND_TYPE/backendTypeSchemapackages/runtime-contracts/src/backend-types.tsKNOWN_HARNESSES/Harnesssrc/harness/index.tsMATERIALIZER_HARNESSES/HarnessIdpackages/agent-profile-materialize/src/index.tsforgeandcursorship full provider adapters downstream (packages/sdk-provider-forge,packages/sdk-provider-cursor— both with areadonly provider = "…"adapter class) but were absent from the canonical enum. That absence is what forced the other two packages to keep divergent copies, and it made agent-app's documented claim that its taxonomy is "a superset of agent-interface'sHarnessType" unenforceable — threeas HarnessTypecasts rest on it.Change
Add
forgeandcursortoHarnessTypeandharnessTypeSchema. The existing compile-time drift guard keeps the TS union and the Zod enum in lockstep.Both are multi-provider CLI runners with no vendor lock, so they need no entry in the capability tables —
harnessProviderLock,harnessPreferredModelPatterns,harnessReasoningEffortsOverride,harnessReasoningCeiling,harnessIgnoresModel,harnessIgnoresEffortare allPartial<Record<HarnessType, …>>and default to router-backed / full ladder / honors-both-selectors. That is exactly the behavior agent-app already documented and relied on, so this is additive with no behavior change for existing ids.harness.test.tspinsharnessTypeSchema.options, so downstream packages can derive their enums from it and an accidental add/remove fails here rather than silently three repos away.Verification
Full gate on a clean worktree off
origin/main, before and after:Baseline note:
pnpm testwithout a priorpnpm buildfails 3 agent-core test files withFailed to resolve entry for package "@tangle-network/agent-interface"— an unbuilt-distartifact, not a defect. Building first is green both before and after this change.Follow-ups (separate PRs, not merged here)
BACKEND_TYPEfromharnessTypeSchema.options, addsatisfies readonly HarnessType[]toMATERIALIZER_HARNESSES, drop the batch-request enum intersection.KNOWN_HARNESSESfrom the canonical options and fix the false superset claim.Those consume the published enum, so they land against the currently-published
agent-interfaceand pickforge/cursorup from an explicit named extras list until this ships.