Skip to content

feat(harness): add Harness as Code package - #89

Draft
phodal wants to merge 22 commits into
mainfrom
feat/harness-as-code-package
Draft

feat(harness): add Harness as Code package#89
phodal wants to merge 22 commits into
mainfrom
feat/harness-as-code-package

Conversation

@phodal

@phodal phodal commented Aug 16, 2026

Copy link
Copy Markdown
Member

Summary

  • add the versioned Harness-as-Code DSL, IR, resolver, locks, runtime adapters, comparison workflow, UI, and Studio surfaces carried by this feature branch
  • add a Node-only @qoder-ai/harness/session-executor contract and harness-session-executor CLI
  • bind an exact Git base commit to an exact Pi JSONL entry, continue it in an isolated detached worktree, and preserve the one-parent result commit on a namespaced ref
  • persist the validated plan, forked Pi JSONL, and receipt in Git-common-dir metadata without switching the caller branch or staging its worktree

Session checkpoint POC boundaries

  • Pi-only in this first slice; other hosts require an explicit checkpoint adapter
  • a checkpoint is a caller-supplied committed Git tree plus Pi entry, not a reconstruction of dirty files, index state, environment state, or external side effects
  • live tools are limited to contained read, ls, edit, and write; Bash, extensions, skills, prompt templates, delete, rename, and .git access are disabled
  • execution is bounded to 64 tool calls and 15 minutes; adopting the result ref remains a separate user action

Validation

  • npm test -w @qoder-ai/harness — 15 files, 152 tests passed
  • npx vitest run test/plugins/plugin-manifests.test.mjs test/skills-docs/doc-link-graph.test.mjs — 2 files, 9 tests passed
  • npm pack --dry-run --ignore-scripts -w @qoder-ai/harness --json — compiled API, declarations, and CLI included
  • npm run pack:verify — npm and runtime bundles verified

The checkpoint suite uses temporary Git repositories and the real Pi SessionManager branch/fork API, but deliberately does not make a paid/live model request.

phodal and others added 22 commits August 15, 2026 00:00
Add a Langium-based Harness as Code compiler, versioned IR resolver, and Qoder/Pi SDK executors with honest materialization semantics and fail-closed host and runtime handling. Wire the package into the root workspace and cross-platform CI, and harden package verification.

Implements docs/specs/2026-08-14-harness-as-code-v0.1.md. Validated with npm run check, focused documentation-link tests, npm audit, a cold package install, and a native Qoder SDK smoke.

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Add a package-local generation skill, compact language contract, and deterministic compiler/resolver validator, and include them in the @qoder-ai/harness tarball. Cover valid, invalid, missing-composition, and honest degradation behavior.

Implements docs/specs/2026-08-15-harness-dsl-generation-skill.md. Validated with the 42-test package suite, skill validation, agent asset lint, documentation-link checks, npm pack inspection, a cold package install, and an isolated fresh-agent forward test.

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Implements docs/specs/2026-08-15-harness-coding-compare.md with a full-surface DSL fixture and a frozen Qoder README comparison runner. Trials use isolated repositories, bounded tool permissions, deterministic grading, redacted traces, runtime receipts, and durable verdict artifacts.

Validated with npm run check, a native Qoder H0/H1 run where both real README outcomes scored 100, a permission-model regrade, and a cold tarball install/import smoke.

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Add a named qoder-minimal-v1 executor contract and variant-level compare profiles so runtime effects can be measured independently of harness composition. The profile fixes a four-tool surface, isolates optional SDK context, and records non-secret runtime evidence.

Implements docs/specs/2026-08-15-qoder-minimal-runtime-profile.md. Validation passed in an isolated staged checkout with npm run harness:generated, npm run harness:build, npm run harness:test (56 tests), and the documentation link graph (6 checks).

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
…n defects

Make the smallest useful .harness document five lines: a bare
materializes a component as advisory prompt guidance,  mechanism and
strength are optional, one binding can name several hosts, and a composition can
extend another. The README now teaches that minimal form first and routes to the
full surface only when an author needs it.
Replace the v0.1 authored surface (component / binding / plugin /
composition) with host-neutral core semantics: a harness assembles one
workflow, its logical agent roles, and their capability requirements;
skill, tool, and mcp are the three standard capability kinds; runtime
names a concrete host with its adapter and execution style; target is a
deployment statement; binding is the adapter-layer mapping.

The core DSL no longer defines a generic plugin. Host plugin and
extension concepts stay in the host's own namespace as dotted binding
mechanisms (qoder.plugin, pi.extension, deepseek.plugin,
prime.python-skill) behind the runtime's adapter, so DeepSeek's Cordis
plugin runtime and Pi's package/extension split remain isolated
implementation details.

Workflow implementation and capability exposure become independent
dimensions: a workflow is either a declarative graph (edges, event
routes, stop conditions) or a programmatic controller, and a runtime
exposes capabilities through tool calling or programmatic calling in a
host language. Resolution enforces deployability instead of pretending
a Deno workflow can be translated into Prime's Python, and points at a
matching runtime or external ACP driving.

The proven v0.1 machinery is kept: the strength ladder, preferred /
minimum / on-degrade policy, the declared-versus-materialized boundary
with its advisory cap, deny-wins permission merging (now including MCP
transport grants), canonical hashing, and the immutable revision.
Realizations are recorded per agent and capability, and IR_VERSION moves
to 0.2.0.

Validated with npm run build, npm run typecheck, the 78 package tests
covering every new rejection and resolution path, check:generated, the
repository documentation link graph, and the authoring skill validator
against the rewritten examples.

Co-authored-by: QoderAI (Qwen 3.8 Max) <qoder_ai@qoder.com>
Add host-neutral streaming events, an AG-UI SSE adapter, and a React studio for live runs and comparison evidence. Stream Qoder partial messages and bounded tool results into keyboard-expandable cards with honest failed, unavailable, truncated, and interrupted states.

Harden the loopback browser boundary against client-controlled Host trust, return structured 413 responses for oversized bodies, and add a built-app Playwright regression at 390px. Wire the new packages into CI and the protected GitHub Actions publication selector; local workflows only build, test, pack, or dry-run.

Implements docs/specs/2026-08-15-harness-ui-studio.md. Validated with npm run check, the doc-link graph test, workspace pack dry-runs, workflow YAML parsing, and the Playwright interaction test.

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
The execution layer was a one-shot batch surface: HarnessExecutor.execute
ran one prompt to completion, so interactive surfaces could not hold a
conversation against a resolved revision, the executor interface carried no
version of its own, and runtime capability gaps surfaced only as free-form
warning strings.

Introduce harness-adapter-v1: HarnessAdapterV1.doStart binds a resolved
revision to a live host session that accepts multiple sequential prompt
turns, each emitting its own complete run-event sequence under the existing
HarnessRunEmitter invariants. Optional behavior now degrades with a typed
HarnessCapabilityUnsupportedError carrying adapterId and capability instead
of silence, and the framework owns the working directory rather than letting
adapters derive their own paths.

The Qoder and Pi executors become adapters plus a thin runOnce wrapper, so
the batch surface keeps its exact result shape, receipts, metrics, and event
sequences. Each adapter's preamble policy follows its host: the Qoder SDK is
stateless per query, so an ephemeral session re-sends the preamble every
turn while a persisted one sends it once; Pi keeps one createAgentSession
for the whole session and sends it on the first turn only. Pi exposes no
abort surface, so a turn abortSignal is refused as turn-abort rather than
ignored.

The sandbox-ownership precondition of the upstream design is deliberately
not adopted: binding to natively running hosts is this DSL's core scenario.
Turn suspend/continue and AI SDK harness interop are left to later specs.

Implements docs/specs/2026-08-15-harness-executor-session-adapter.md.
Validated with npm run build and 103 package tests, including the 17
pre-existing exec tests unchanged and 10 new adapter tests covering per-turn
event framing, preamble policy, typed capability refusal, graceful-stop
degradation, abort bridging, and host mismatch; harness-ui (20) and
harness-studio (25) build and pass against the new export surface.

Co-authored-by: Claude Code (Claude Agent) <noreply@anthropic.com>
Lock revisions to bundle, source, and adapter facts; fail closed during materialization; keep Qoder turns in one live query; and derive Compare verdicts from paired evidence.

This review-driven change has no external Story and implements docs/specs/2026-08-15-harness-execution-closure-blockers.md. Validated with npm run check, preview health/module smoke, Harness Studio Playwright coverage, and a live two-turn Qoder SDK session.

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Separate the browser-safe Harness core from host adapters and devtools, move adapter facts into a pure registry, and require injected UI executors.

Add trusted-fixture sandbox receipts and bridge persisted harness runs into the Inspector evidence chain with self-contained comparison artifacts.

Implements docs/specs/2026-08-15-harness-as-code-architecture-hardening.md. Validated with the root, Harness, Harness UI, Harness Studio, browser, packaging, cold-install, preview, generated-code, module-graph, and documentation-link checks recorded in the spec.

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
A source-backed skill was locked byte-for-byte, resolved as advisory
guidance, and recorded as delivered while the executor put only its path
into the prompt. The executor now reads the declared SKILL.md under the
source root, bounded at 32 KiB and truncated with an explicit run warning,
and inlines the text; an undeliverable source fails the run before the
host SDK loads, and Pi materialization copies the skill's real files.

Also closes the surrounding honesty and boundary gaps: resolution rejects
a descriptor whose adapterId differs from the runtime's adapter, freezes
only a cloned revision, and counts a permission grant as enforced only for
the exact domain/access pair; compilation rejects stop-free declarative
workflows and agent roles the workflow never references; and the AG-UI
servers refuse a non-loopback bind without --unsafe-allow-remote.

Spec: docs/specs/2026-08-16-harness-review-remediation.md
Test: 143 harness + 26 harness-ui + 29 harness-studio tests pass

Co-authored-by: QoderAI (Qwen 3.8 Max) <qoder_ai@qoder.com>
Add a package-owned plan/apply contract that binds an exact Git commit to an exact Pi JSONL entry, revalidates both inputs, and continues the session in an isolated detached worktree.

Persist the continued session and receipt under Git metadata, create a one-parent result commit on a namespaced ref, and expose a bounded Node-only API and CLI without changing the browser-safe package root.

Validated with the full @qoder-ai/harness suite (15 files, 152 tests), package dry-run contents, manifest checks, doc-link checks, and pack verification.

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Use fileURLToPath for file URL conversion and normalize separators only at assertion boundaries so the Harness package tests exercise the same contracts on Windows, macOS, and Linux.\n\nValidated with: npx vitest run test/compare.test.ts test/module-graph.test.ts\n\nCo-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Assert the redacted file_path value after JSONL parsing so Windows JSON escaping is not mistaken for duplicate path separators.\n\nValidated with: npx vitest run test/compare.test.ts test/module-graph.test.ts\n\nCo-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Convert the built entry file URL with fileURLToPath before walking the Harness UI module graph, avoiding duplicate drive prefixes on Windows.\n\nValidated with: npx vitest run test/module-graph.test.ts\n\nCo-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Thread source locking and delivery through harness-ui, harness-studio, and compare using docs/specs/2026-08-16-harness-ui-studio-compare-source-skill-delivery.md. Resolve skill paths from the harness file directory and validate actual SKILL.md delivery across all three surfaces.

Validated with npm run check.

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Replace the checkout-sensitive harness text rewrites with a direct source-backed compare fixture documented in docs/specs/2026-08-16-harness-ui-studio-compare-source-skill-delivery.md. This keeps Windows CRLF conversion from dropping the skill binding while preserving the delivery behavior under test.

Validated with npm run check (1,325 root tests; 152 harness tests; 29 UI tests; 31 Studio tests; package verification passed).

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Build the expected default source roots with Node's native path resolver so the Harness UI and Studio tests pass on Windows while preserving the explicit override contract. Validated with both package test suites.

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Implements the checkpoint-anchored multi-lane experiment contract, runner, and Studio Workbench described in docs/specs/2026-08-17-harness-studio-checkpoint-compare.md. The change keeps checkpoint identity separate from sandbox creation, streams lane-scoped ACP events, and correlates tool paths without overstating historical provenance.

Validated with 179 harness tests, 39 Studio tests, three browser flows, six doc-link tests, real Better Harness lanes, Canvas preview health checks, and an npm pack dry run. Dirty-state patch replay remains an explicit pending slice in the spec.

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Replace the v0.2 authoring surface with explicit language versions and deployments, honest workflow modes, kind-specific capability realization, and exact tool contracts. Preserve revision, source-lock, and adapter preflight while migrating examples, documentation, the generation skill, and the harness-ui consumer.

Implements docs/specs/2026-08-17-harness-dsl-v0.3-semantic-contraction.md. Validation covered 156 harness tests, 29 harness-ui tests, 1325 root tests, the documentation link graph, and package verification.

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Turn the Studio experiment surface into a two-stage Compare flow. A Builder discovers bounded project history through a server-side checkpoint adapter, resolves one opaque item into a source-neutral checkpoint projection, and locks it into an immutable content-addressed experiment definition; resolve and lock create no worktree, sandbox, or result ref, so isolated copies still appear only on Run. The Workbench then compares one focused Baseline/Candidate pair across Summary, Trace, and Evidence, with Reference/Baseline/Candidate as the single role vocabulary and per-comparison verdicts instead of a global one.

Provider event shapes (ACP, AG-UI, Anthropic) are normalized at the server boundary so React holds no provider branches, and comparability, role selection, pair results, relation counts, resource ledgers, and canonical event folding move into pure tested modules split from the React controller. ADR-0005 records the boundary this rests on: Git/worktree is the first checkpoint source adapter, not the product contract, and discovery, lock, and materialization stay separate phases.

Review of the change also fixed three defects. A run completion now settles only its own run's calls, so parallel trials within one lane can no longer mark each other's in-flight calls result-unavailable. The narrow-viewport browser flow waits for the collapsed context rail instead of measuring before React flushes the media-query state. The Builder treatment assertion expects the real profile ids the manifest carries, and the spec's stale harness and Studio test counts were corrected to the observed runs.

Implements docs/specs/2026-08-17-harness-studio-checkpoint-compare.md slices 4 through 6. Validated with 1325 root tests, 59 Studio tests, 156 harness tests, three browser flows, the documentation link graph, typecheck, and a clean whitespace check. Dirty-state patch replay remains an explicit pending slice, and a third fresh run still lacks its own unfocused role.

Co-authored-by: QoderAI (Qoder Agent) <qoder_ai@qoder.com>
Restructure Harness Inspector Session Detail into numbered Run Cells with a
collapsible Process trace, an evidence-bounded Outcome, and a read-only Session
outline, and restructure the Harness Studio Workbench into a notebook bar,
shared Context, Run Comparison cell, and Compare Result cell with a right-hand
Checkpoints rail. Both follow the spec in
docs/specs/2026-08-18-notebook-session-and-studio.md.

The session projection now keeps one canonical Turn event order, as decided in
docs/adrs/session-notebook-evidence-projection.md: assistant messages and tool
requests stay in observed source order, and an assistant message is promoted to
the terminal response only when it is the final retained event in its Turn. A
Turn whose last observed event is a tool call reports responseStatus
"incomplete" instead of presenting an intermediate message as the result.
Counts now separate intermediate responses, tool calls, and total retained
process events, and the report model carries the new fields with a conservative
fallback for reports written before them. Outcome states that a session-scoped
patch is unavailable rather than deriving a diff from the mutable worktree.
Assistant prose renders through a sanitized Markdown renderer that escapes raw
HTML and drops link targets outside http, https, mailto, anchor, and relative
paths.

The Studio checkpoint rail button now carries call and event counts in its
accessible name, so the Playwright locator for the Trace "Calls" lens is pinned
to an exact name match.

Validated with npx vitest run (95 files, 1327 tests), npm run
harness-studio:test (59 tests), and npm run harness-studio:test:browser
(3 tests).

Co-authored-by: QoderAI (Qoder Agent) <qoder_ai@qoder.com>
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.

1 participant