Skip to content

th-3cabf6: harness conformance contract + th harness doctor - #598

Open
brentrager wants to merge 3 commits into
mainfrom
th-3cabf6-harness-conformance
Open

brentrager wants to merge 3 commits into
mainfrom
th-3cabf6-harness-conformance

Conversation

@brentrager

@brentrager brentrager commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Problem

"Broader harness support" (sibling lanes are adding ~13 harnesses) is only worth something if a user can tell which harnesses actually work on their machine, and a regression is caught. Today neither is possible short of launching one and watching.

Solution

1. Conformance contract (crates/smooth-flow): every manifest in harness::BUILTIN (+ reference manifests in tests/conformance/manifests/) runs resolve · launch · working · idle · steer · permission · resume · kill against smooth-flow-fake-agent, a stand-in CLI that derives its argv parsing (match_argv, the inverse of render_argv) and its hook events (Claude table / inverted event_map / scraped screens) from the manifest itself. Private engine per manifest: scratch HOME/flow.db/worktree, own tmux socket, in-process /api/flow/hooks listener with the permission long-poll, binary resolution pinned to the scratch HOME with a cmux shim decoy first on PATH. No real CLIs, network or credentials. A new harness enrolls by being listed in BUILTIN; scraped ones add screens captured from the real CLI, checked against their own regexes.

2. th harness doctor [name] [--json] [-v] (crates/smooth-cli/src/harness_doctor.rs): read-only verdict works / degraded / not installed + the one fix per degraded row — binary actually launched vs cmux shim, --version, resolution under the SmoothFlow app's launchd PATH (incl. #!/usr/bin/env node scripts), hooks installed and trusted (Codex [hooks.state]), stale smooth-agent plugin without the flow hook, OpenCode plugin without the generic event hook, daemon reachability, sign-in (env / files / keychain item presence).

3. CI: new Harness conformance job (Linux, tmux, SMOOTH_E2E_STRICT=1).

4. Engine bug the suite found on its first run: a permission ask under a harness's own event_map name carried no request_id, so th flow approve / the apps could not approve it. Now gets hook-<uuid> → falls through to the approval keystroke. Plus Engine::set_resolve_env test seam.

Picker surfacing is not small (needs doctor core in the daemon + 4 pickers per the mobile parity rule) → filed th-51bf88.

Verification

  • cargo test -p smooai-smooth-flow: 159 lib tests + conformance suite green (~23 s): claude, opencode, codex, th-code, conformance-mapped, conformance-scrape all 8/8 (th-code permission n/a).
  • cargo test -p smooai-smooth-cli: 846 + 3 green (incl. 11 doctor tests + the doctor clap parse test); cargo clippy -p smooai-smooth-cli adds no warnings (one pre-existing in harness.rs add). Real th harness doctor run from the built binary matches below.
  • Doctor on this machine: claude degraded (smooth-agent 0.41.4 predates the flow hook → th harness enable claude-code; which claude is a cmux shim, correctly skipped), opencode degraded (linked plugin posts no flow events → th harness enable opencode), codex works (7 flow hooks, all trusted), th-code works.

Pearl th-3cabf6 (epic th-faa590).

🤖 Generated with Claude Code

brentrager and others added 2 commits September 14, 2026 17:01
…ent speaking its mechanism

A generic fake agent (smooth-flow-fake-agent) derives argv parsing and hook
events from the manifest itself, so a new harness enrolls by being listed in
BUILTIN (scrape ones add captured screens). The rig runs resolve · launch ·
working · idle · steer · permission · resume · kill on a private engine with
an in-process hook listener and a cmux-shim decoy first on PATH.

It caught a real engine bug: a permission ask under a harness's own event
name (event_map → needs_you) carried no request_id, so flow.approve / th flow
approve could not address it. Mapped permission asks now get a hook-<uuid>
id that falls through to the approval keystroke.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s machine

Read-only per-harness verdict (works / degraded / not installed) with the one
command that fixes each degraded row: the binary SmoothFlow runs and whether
`which` returns a cmux shim, --version, resolution under the SmoothFlow app's
launchd PATH (including #!/usr/bin/env scripts whose interpreter the app
cannot see), hooks installed AND trusted (Codex's Hooks-need-review state,
stale smooth-agent plugins without the flow hook, OpenCode plugins without
the generic event hook), daemon reachability and sign-in. --json for the app.

Plus the Harness conformance CI job and the 'Supporting a harness' contract
docs. Picker surfacing is filed as th-51bf88 (needs the doctor core in the
daemon and all four pickers — not small).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cc9337e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@smooai/smooth Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@brentrager
brentrager enabled auto-merge (squash) September 14, 2026 21:02
brentrager added a commit that referenced this pull request Sep 14, 2026
Both lanes fixed un-approvable hook asks. Adopt #598's rule (id assigned in
the pure mapper, permission asks only; a question gets none) so the two merge
cleanly. The delta kept here: the permission label is derived from the event
name / notification_type, and only Claude-protocol harnesses hold
PermissionRequest open.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nks; oxfmt the contract doc

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.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