refactor(harness): fork_history + shell-tool capability axes; derive gating (PR 1.8) - #3648
Conversation
…gating (PR 1.8) Final Phase-1 PR of the modular native-harness registry refactor: move registry-parallel enumerations onto HarnessCapabilities. - Add a fork_history axis (ForkHistory enum: none/rebuild/preamble) to HarnessCapabilities, declared per harness in _BUILTIN_CAPABILITIES. Derive the server's two fork-history gating frozensets in _sessions/common.py from it instead of hand-listing. The derivation emits each canonical id plus its reversed native-<key> spelling, because native-claude/native-codex/native-cursor are valid ids canonicalize_harness passes through unchanged and the read sites match on the canonicalized id (guarded by the existing reversed-spelling fork test) — so the derived sets are a superset of the prior literals. - Add optional shell_tool_name / shell_tool_prompt fields carrying the harness bench's shell-tool provocation; delete the bench's hardcoded _NATIVE_TOOL_PROVOCATION table and read the fields off capabilities in native_vendor() (byte-identical (tool_name, prompt) per harness). - Delete the dead _HARNESS_MODULES literal in runtime/harnesses/__init__.py (~120 lines, overwritten unconditionally by harness_modules() next line). - Extend the drift-guard tests in test_harness_capabilities.py. Scope kept tight to the doc's mandate: sets that would need new NativeCodingAgent identity fields (_ANTIGRAVITY_FAMILY_HARNESSES, _PROVIDER_RESOLUTION_HARNESS, *_NATIVE_TERMINAL_ROLE) are left as-is; noted as follow-ups. Co-authored-by: Isaac Signed-off-by: Pat Sukprasert <pattara.sk127@gmail.com>
a9ba65d to
3c3c0ea
Compare
|
/review |
|
|
🏷️ Doc impact: Internal refactor deriving fork-history/shell-tool capability axes from the harness registry (no new harness, no user-facing config or documented default changed), so no doc update is needed. Auto-classified on merge. Set the label manually before merging to override. · run |
…ospective (#3658) Phase 1 of the modular native-harness registry refactor landed (10 PRs, 2026-07-28 → 07-31). Bring the design doc in line with what actually shipped: - Status header, Phase 1 subtotal, effort summary, and bottom line updated from forward-looking ('1.1–1.3 in review') to Phase 1 complete / Phase 2 next. - Ledger: 1.8 (#3648) landed; 1.4 marked descoped (with rationale); the 1.7 opencode-e2e follow-up (#3656) recorded; per-PR merge dates added. - Calibration rewritten as a Phase 1 retrospective: estimate (~20–29 eng-days) vs. actual (10 PRs / 4 calendar days), the real cost centers (test-shape churn + review-caught behavior bugs, enumerated per PR), the correct runner re-scope, the two intentional behavior deltas (qwen label, antigravity relay), and the recurring uv.lock / full-suite-only-flake operational friction. Doc-only; no code change. Co-authored-by: Isaac Signed-off-by: Pat Sukprasert <pattara.sk127@gmail.com>
Related issue
N/A — final Phase-1 PR of the modular native-harness registry workstream (
designs/harness-modular-registry-proposal.md).Summary
PR 1.8 derives registry-parallel enumerations from
HarnessCapabilitiesinstead ofhand-maintained literals, and adds the two capability axes the workstream reserved for this
step. Disjoint hub (depends on 1.1).
fork_historycapability axis (ForkHistoryenum:none/rebuild/preamble) onHarnessCapabilities, declared per harness in_BUILTIN_CAPABILITIES. The server's twofork-history gating frozensets in
_sessions/common.py(
_FORK_HISTORY_NATIVE_HARNESSES,_CURSOR_FORK_HISTORY_HARNESSES) are now derived fromit instead of hand-listed.
shell_tool_name/shell_tool_promptcapability fields (optional) carrying theharness bench's shell-tool provocation. The bench's hardcoded
_NATIVE_TOOL_PROVOCATIONtable is deleted;
native_vendor()reads the fields off capabilities (it already readharness_capabilities())._HARNESS_MODULESliteral inruntime/harnesses/__init__.py(~120lines, overwritten unconditionally by
harness_modules()on the next line — confirmed noread in between).
test_harness_capabilities.pypattern.ELI5
"Which harnesses carry chat history across a fork?" and "what shell command does the bench
poke each harness with?" were answered by copy-pasted lists that drift when a harness is added.
This moves both answers onto each harness's capability record and computes the lists from it,
so a new harness declares its behavior in one place.
Test Plan
membership (rebuild: claude/codex/hermes/pi/qwen; preamble: cursor/opencode), and the
bench
native_vendor()yields the same(tool_name, tool_prompt)per harness as thedeleted table (spot-checked all 9 + cursor=None).
test_fork_history_matches_derived_gating_sets,test_fork_history_derivation_preserves_prior_membership,test_registered_native_aliases_still_classify_fork_history,test_native_tui_harnesses_declare_shell_tool_provocation; updatedtest_optional_bench_capabilities_default_to_unknownfor the newas_dict()keys.Demo
N/A — internal capability-model refactor, no user-facing or UI change.
Type of change
Test coverage
Coverage notes
Behavior-preserving derivation. One subtlety worth flagging for reviewers:
Reversed
native-<x>spellings must be preserved in the derived sets.native-claude/native-codex/native-cursorare valid harness ids thatcanonicalize_harnesspasses through unchanged (they are not registered aliases thatnormalize to
*-native). The read sites match oncanonicalize_harness(harness_kind) in SET,so the sets must contain those literal reversed spellings or an identically-behaving agent
silently loses fork history — there is an explicit test for exactly this
(
test_sessions_fork.py::test_fork_reversed_native_spelling_carry_gating). The derivationtherefore emits both the canonical id and its
native-<key>spelling per fork-history harness,so the derived sets are a superset of the prior literals (extra reversed spellings that
canonicalize into the set are harmless at the read site). Pinned by
test_reversed_native_spellings_classify_fork_history+test_fork_history_derivation_preserves_prior_membership.Scope was kept tight to the doc's mandate. Other registry-parallel sets that would need
new
NativeCodingAgentidentity fields (_ANTIGRAVITY_FAMILY_HARNESSES,_PROVIDER_RESOLUTION_HARNESS, the*_NATIVE_TERMINAL_ROLEconstants) are left as-is — theyfunction and converting them touches model routing / runner hot paths. Noted as follow-up
candidates.
Workstream progress (
designs/harness-modular-registry-proposal.md, ~14 PRs):Phase 1 — 1.1 (#3239), 1.2 (#3244), 1.3 (#3314), 1.5a (#3495), 1.5b-i (#3500), 1.5b-ii
(#3501), 1.5c (#3543), 1.6 (#3568) landed; 1.7 (#3599) in review; 1.8 is this PR — the last
Phase-1 item. Next: Phase 2 (2.1 validator flip → 2.2
/v1/harnessesnative rows → 2.3 weboff endpoint → 2.4 docs + example plugin).