Repository navigation
feat(coding-agent): add extensible harness versioning - #93
Conversation
- Models complete harness revisions with immutable artifact inventories and atomic global selection - Publishes the lockstep state package and carries it through release metadata
- Loads the global selected revision for CLI and SDK sessions with safe fallback diagnostics - Switches validated snapshots live while preserving conversation history and request isolation
- Presents current lineage, deterministic search, previews, and unavailable revision diagnostics - Applies validated live switches without exposing terminal control sequences
- Allows cancellation to interrupt queued children before provider startup - Keeps the regression focused on cancelled coordination lifecycle and final state
- Registers the repository-resolved tsx loader for source subprocesses - Lets local tests resolve workspace packages without requiring generated build output
|
Issue: The cancellation regression waited for the child provider call even though cancellation can validly interrupt the child while it is still queued. Resolved by commit sha: Issue: Source-based CLI subprocess tests could not resolve the new harness-state workspace package unless generated distribution output already existed. Resolved by commit sha: This comment is AI-generated by Codex. |
- Prevents live catalog capability expansion from breaking unrelated reasoning-model coverage - Preserves the stable requirement that GLM 5.2 accepts text input
Issacboy123
left a comment
There was a problem hiding this comment.
Architecturally, the features added and changed in this PR looks good: content-addressed immutable revisions, explicit lineage, atomic global selection for evolution, strict validation before activation, conflict-aware advancement, and live session switching provide a sound foundation for harness evolution. The separation between persistence, runtime activation, and TUI selection is appropriate and should support future artifact types without coupling them directly to session history.
There are just a couple of additional issues I think are worth fixing, mentioned in inline comments. Not breaking, mostly just edge cases.
| signal: options.modelRuntimeSignal, | ||
| })); | ||
| const settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir); | ||
| const harnessState = await loadGlobalHarnessState(agentDir); |
There was a problem hiding this comment.
This reloads the global harness pointer for every service creation, including subagent services. A parent retains its in-memory revision, so a concurrent global switch can cause a newly spawned child to use a different revision. If the selected bundle is corrupt, the child silently falls back because the subagent path does not surface these diagnostics. The parent's activated store should be bundled into child session creation. Another option is for the child's independent activation to be explicitly surfaced and documented.
| if (!Check(HarnessStateSchema, state)) throw new Error("Invalid harness state to save"); | ||
| if (!Check(HarnessStateSaveOptionsSchema, options)) throw new Error("Invalid harness state save options"); | ||
| if (!Check(Type.Union([Type.Null(), HarnessVersionSchema]), base)) throw new Error("Invalid harness base version"); | ||
| const memoryBytes = UTF8_ENCODER.encode(state.memory); |
There was a problem hiding this comment.
HarnessStateSchema accepts lone UTF-16 surrogates, but TextEncoder replaces them with U+FFFD. The returned bundle therefore retains a different string from the bytes that were hashed and written. Reloading changes the state, and saving the same input to the same destination fails the idempotence check. Non-well-formed strings should either be rejected, or canonicalize the value before constructing and returning the bundle, with a regression test for repeated saves of a lone surrogate.
Summary
Adds immutable, globally selected harness revisions whose initial artifact inventory contains
EVOLVE.md.The active revision can be switched live through
/version, and subsequent model requests receive non-empty evolution memory without changing conversation history.Reasoning
Harness evolution needs a complete, content-addressed snapshot rather than a mutable memory file.
Revision hashes include the parent and sorted artifact descriptors, which preserves lineage and allows identical content to form a distinct successor while keeping artifact hashes independent.
Activation validates the entire bundle and falls back to the built-in harness when selected state is invalid, so future artifact kinds cannot be partially activated by older runtimes.
Changes
.../diffex-harness-statewith strict manifests, immutable version writes, cross-process locking, atomic pointers, conflict-aware advancement, and per-revision diagnostics.AgentSession./versionselector with lineage ordering, unavailable revision diagnostics, safe terminal rendering, and current-version no-op behavior.Testing
npm run check./test.shnode ../../node_modules/vitest/dist/cli.js --run test/harness-state.test.tsnode ../../node_modules/vitest/dist/cli.js --run test/harness-state-runtime.test.ts test/system-prompt.test.ts test/suite/agent-session-harness-version.test.tsnode ../../node_modules/vitest/dist/cli.js --run test/slash-commands.test.ts test/harness-version-selector.test.ts test/interactive-mode-harness-version.test.ts/versionsearch, rendering, and cancellation