Repository navigation
refactor(harness): declare tool layout in one adapter table - #51
Merged
Merged
Conversation
replygirl
force-pushed
the
worktree-harness-adapter-table
branch
from
September 29, 2026 08:26
d8179be to
112fd4e
Compare
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
First implementation commit for harness-adapter-table (R8): committed raw golden files (Buffer-compared, regenerated only under COSPEC_GOLDEN_WRITE=1) for renderHarnessFiles across claude/codex/opencode/agents alone and together, plus a wiring characterization of init/update/doctor's current behavior (receipts, --harness validation, both detection systems, removal containment, doctor findings). No src file is touched, so later commits in this branch have a provable byte-identity baseline to diff against. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add HarnessAdapter and the four-row HARNESS_TABLE beside the harness.yaml harnesses block (additive; render still reads the yaml). HarnessName, HARNESS_NAMES and isHarnessName now derive from the table with the same ids in the same order. Adds the flat body dialect, path helpers, and the two-pass scan-root and removal-root derivations, with table invariants, yaml path parity and pinned AI_TOOLS field parity tests. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
renderHarnessFiles now takes every path, frontmatter builder, argument injection and rules file from the tool rows, with a RenderOptions.adapters test seam and a scope field on RenderedFile. The harnesses: block leaves canon/workflows/harness.yaml, which now carries workflow identity only, and the opencode body dialect becomes flat. Rendered bytes are unchanged: the render goldens and existing snapshots show no diff. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
render.ts dispatches on the row's commands.serializer: markdown as before, toml as upstream Gemini's description/prompt layout with both escapers ported, emitted with no frontmatter and no content hash. Fixture rows through RenderOptions.adapters cover toml, .prompt, .prompt.md, a split commands root, namespaced and flat filenames, the @ prefix and home scope. Goldens and generated output are byte-identical. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Build the compiled binary and run init --harness all in a fresh temp repo before T3 touches init.ts, per tasks.md's timing requirement. Records the file count and a sha256 digest over the sorted per-file sha256sum output plus the stdout digest, for task 5.2's post-T3 re-take to compare against in verification 3.8. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Run the full unit/integration/contract suites and generate:check against the table-based render (groups 2 and 3), confirm the render and snapshot goldens are untouched, and record observed evidence for verification rows 1.1-1.4, 2.1-2.9 and 4.1-4.3. Row 1.4 defers its after-task-5.5 half until T3 lands; every other row in this checkpoint passed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
harness-integration.md now names HARNESS_TABLE in harness/adapters.ts as the single place a tool's skills/commands/rules layout is declared, notes that harness.yaml carries workflow identity only, and rewrites the shared-root, legacy-migration and restart-line bullets around the table's actual fields (skillsDir, bodyDialect, legacySkillsDirs, rulesPath, setupNote, requiresIdeRestart). No apps/docs change: no user-facing behavior changed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The invocation-prefix test compared the real opencode row (already `/`) against its own live render, so a regression in the flat respelling moved both sides. Relocate the fixture row to `.x/` and require every file to be Buffer-equal to the committed pre-change opencode golden; repoint verification 2.6 and task 3.2 at it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Rebased onto main d25c5c0 (unknown-option-contract, upstream-spellings, passthrough-json-and-doctor merged). Blocked by now lists all three as archived; sync-blockers reports the change fully unblocked. Verification 4.4 and 5.1 observed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
COSPEC_GOLDEN_WRITE=1 over harness-wiring.test.ts on main d25c5c0 with apps/cli/src/commands/ unmodified rewrote every golden byte-identically, so no golden file changes here. Records the built-binary init --harness all baseline (127 files, file-list digest equal to task 1.3's; the normalized stdout digest task 5.6 compares against). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
init.ts builds the --harness value list from HARNESS_NAMES, detects from each row's detectionPaths, walks the opsx sweep over scanRoots(), and prints each selected row's setupNote plus upstream's single IDE restart line (ideRestartLine; none of the four rows sets requiresIdeRestart). DETECT_PATHS and RESTART_LINES are gone. adapters.ts exports primaryRoot and ideRestartLine. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…E (5.4) update.ts reads each harness's skills root, legacy skills roots and rulesPath marker from its row, takes MANAGED_REMOVAL_ROOTS from removalRoots(), routes manifest tracking on frontmatter === null, and refuses a home-scoped rendered file before any write. GenerateOptions gains the adapters test seam; the write-mode receipt prints the requiresIdeRestart line after a write (never, for the four rows). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
doctor.ts resolves a dangling reference through the owning row's skills root and commandPath, walks scanRoots() for harness markdown and sidecars, and attributes a file to the row whose primaryRoot prefixes it. The SKILL_BASE and COMMAND_LOC copies are gone; WORKFLOW_SKILL's comment now says it mirrors workflow identity, not tool layout. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
.agents/shared.md (synced to CLAUDE.md and AGENTS.md) now says managed harness files compose from canon plus HARNESS_TABLE in harness/adapters.ts, which render, init, update and doctor all read. docs/harness-integration.md names what each command reads from the table, the manifest tracking of TOML commands, the home-scope refusal and update's restart line. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every verification row observed after T3: wiring goldens unchanged since the task 5.2 re-take (4625056), render goldens unchanged since task 1.1, generate:check no drift, test:pack green, and the built binary's init --harness all file list and normalized stdout identical to task 5.2's. Tasks 5.3 to 5.6 and 7.1 ticked; mise run check green at 77db34a. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
update's orphan sweep filtered command dirs on a literal `.md`, so a row with a `.prompt` extension never had a retired command removed and `update --check` reported no drift. Match each command dir against the extensions of the markdown rows that render into it; a TOML row's dir is left to the manifest. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
doctor matched references only as `/cospec[:-]`, so an `@`-prefix row's dangling `@cospec-<id>` passed silently, and it attributed a file only by its row's primary root, so a split-root row's skills tree was skipped. Fall back to the row whose skills root, commands dir or rules dir covers the file, and match the owning row's invocation prefix. Record the review fixes in the design, tasks and verification ledger. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
docs/harness-integration.md and .agents/shared.md claimed no command branches on a tool name and that a later tool needs only a new row. init's settings merge and claude default, its codex/agents receipt line and doctor's `.md`-only scan still sit outside the table; say so, and list what update's sweep and doctor's attribution now read from it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Doctor's stale-harness, mixed-versions and dangling-ref checks collected only `*.md`, so a `.prompt` command row got none of them. The scan now reads each markdown row's command extension under its commands dir and the skill file's extension under its skills roots, from HARNESS_TABLE; TOML commands stay with the manifest. The four rows scan the same files. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The init receipt's "skills ... share the .agents/skills root" line was keyed on the ids codex and agents, so a third row on that root was left out. It is now one line per skills root that two or more rows resolve to, naming every row on it in table order. The four rows print the same bytes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Init's opsx leftover scan still filtered on a literal `.md`, so a `.prompt` leftover in a row's commands dir was never found; it now reads files through isHarnessDocument like doctor. The skill filename comes from SKILL_FILE in adapters.ts instead of four `SKILL.md` literals. The four rows render and scan the same files. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Design records init's Claude-only settings merge, `claude` default and `/cospec:propose` hint, and openspec's shared opsx skills root, as deliberate. docs/harness-integration.md and shared.md stop listing the receipt line and doctor's `.md`-only scan as gaps now the table drives both; CLAUDE.md and AGENTS.md re-synced. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A commands dir under another row's primary root (Antigravity's .agents/workflows) went to the earlier row, and a legacy skills root no primary root covers went to no row, so its references were never checked. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The scan reads every .md under a skills root's top-level dir, not only skill files, and the legacy-skills migration covers only .codex/skills. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
design.md framed the receipt's always-Claude /cospec:propose hint and doctor's every-.md scan breadth as deliberate Non-Goals; both are known defects on main, routed by ruling to harness-receipt-and-doctor-scope. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
replygirl
force-pushed
the
worktree-harness-adapter-table
branch
from
October 5, 2026 05:45
815ca40 to
5d9a842
Compare
Second rebase onto main (v0.8.3 + reset-yes-pipe-flake, 67f20c5): 0 conflicts, render/wiring goldens untouched, and a sandboxed built-binary init --harness all run is now byte-identical to main including the version stamp (both trees are cospec@0.8.3). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
mise run check green on the rebased tree (HEAD 9ce9a8e): unit 1891, contract 2403, integration 184, bench 339, release-test 14, all 0 fail. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
cospec archive, no flags: validates, moves the change to openspec/changes/archive/2026-10-05-harness-adapter-table/, and reports blocking-changes fully synced. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
replygirl
marked this pull request as ready for review
October 5, 2026 06:11
replygirl
added a commit
that referenced
this pull request
Oct 5, 2026
… files * fix(harness): plan harness-receipt-and-doctor-scope change Proposal, blocking-changes, design, harness-workflows delta, tasks and verification for the two defects #51 surfaced: the init receipt hint always spelled for Claude, and doctor's harness checks reading every .md under a harness dir. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(harness): confirm baseline for harness-receipt-and-doctor-scope Task 1.1: the branch sits on main at 16598da (#51 merged) and cospec apply exits 0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(harness): pin the receipt hint per selected harness Lifts the receipt's two closing hint lines into receiptHintLines with today's output, and adds verification 1.6's rows: claude, all and none keep /cospec:propose; opencode, codex, agents, an explicit opencode,claude list and a synthetic @ row land as test.failing until the hint follows the first selected row. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(harness): spell the init receipt hint for the selected harness cospec init ended every receipt with /cospec:propose, a command only Claude Code registers. The two closing hint lines now go through the first selected row's body dialect and invocation prefix with transformBody, the respelling its generated bodies get; init and render.ts read the canon workflow manifest through one shared loader. Release note: the `cospec init` receipt for `--harness opencode`, `--harness codex` and `--harness agents` (and for any list or detection whose first row is one of them) now ends with the corrected hint: `/cospec-propose` for OpenCode, and `$cospec-propose (Codex) or /cospec-propose (other agents)` for Codex and the shared .agents skills. The claude, all, default and none receipts are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(harness): pin doctor's harness checks to files cospec writes Adds verification 2.1-2.4 and 3.2's rows. A user's .claude/notes.md, a nested worktree's copy under .claude/worktrees/, a SKILL.md deeper than one directory, and other markdown under a scan root land as test.failing until isHarnessDocument narrows. A dangling ref in a cospec-written command, the legacy .codex/skills root, the table's command paths, and upstream's legacy opsx command paths in the leftover scan pass before and after. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(harness): check only harness files cospec writes in doctor isHarnessDocument accepted every .md under a top-level harness dir, so doctor's stale-harness, mixed-versions and dangling-ref checks read a user's own .claude/notes.md and a nested worktree's checkout under .claude/worktrees/. It now accepts exactly <skills-root>/<skill>/SKILL.md (project and legacy skills roots) and each markdown row's command paths, matched on the full root. The opsx leftover scan keeps today's breadth: it moves into init.ts (isLeftoverCandidate, leftoverScanFiles), and findOpsxFiles and doctor's checkOpsx both read it. Release note: - The `cospec init` receipt for `--harness opencode`, `--harness codex` and `--harness agents` (and for any list or detection whose first row is one of them) ends with the corrected hint lines. The claude, all, default and none receipts do not change. - `cospec doctor` (text and `--json`) no longer reports stale-harness, mixed-versions or dangling-ref findings for markdown outside <skills-root>/<skill>/SKILL.md and the table's command paths. A repo that failed doctor only because of such a file, such as a user's own notes or a nested worktree's copy, now passes, and its findings and summary counts shrink. Findings on files cospec writes are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs(harness): document the receipt hint and doctor's scan boundary harness-setup.md states that init's Try: hint uses the first selected harness's spelling and what --harness none prints. The commands.md doctor row names the files the harness checks read and that user markdown and nested worktree copies are no longer checked. docs/harness-integration.md drops its two known-defect passages and describes the corrected behaviour. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs(agents): describe the corrected receipt hint and doctor scan Replaces shared.md's known-defects sentence with the behaviour this change ships, then syncs CLAUDE.md and AGENTS.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chore(harness): tick the release-note task for the doctor fix Task 6.1: e21c226's body relays the proposal's BREAKING list (receipt hint for opencode/codex/agents; doctor's narrowed harness checks) with no ! and no BREAKING CHANGE footer. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chore(harness): record the full check in the verification ledger Task 7.1 and rows 5.1-5.2: mise run check exits 0 and validate --strict is clean. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chore(harness): record PR #63 and close out the verification ledger Tasks 1.2, 6.2 and 7.2: the PR number is recorded in proposal.md and the ledger header; row 4.4 observed the BREAKING list in both fix-commit bodies and the PR body; validate --strict is clean and every verification row is [x]. Task 7.3 (archive) stays open for the merge stage. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore(harness): tick the archive task row Task 7.3 (cospec archive) is performed by the next commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(harness): archive harness-receipt-and-doctor-scope cospec archive: moved the change to openspec/changes/archive/2026-10-05-harness-receipt-and-doctor-scope/ and merged both new requirements (init receipt hint follows the selected harness; doctor checks only the harness files cospec writes) into openspec/specs/harness-workflows/spec.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The harness layer's tool shape was spread across five places that had to agree by hand:
harness.yaml'sharnesses:block, name branches inrender.ts,DETECT_PATHS/RESTART_LINESininit.ts, and separate copies of the roots inupdate.ts/doctor.ts. None of them could express the per-tool shapes the pinned OpenSpec dist declares (a split commands root, per-extension filename templates, TOML serialization, IDE-restart flags, setup notes). This change puts all of it in one typedHARNESS_TABLEinapps/cli/src/harness/adapters.ts.render.ts,init,updateanddoctorall read the table, and none of them keeps its own copy.HarnessName/HARNESS_NAMES/isHarnessNameare still exported, now derived from the table, and keep the same name, shape and order.What each consumer reads from the table
injectArguments,rulesPath, and themarkdown/tomlserializer. The table is injectable throughRenderOptions.adapters.--harnessvalue list and error text fromHARNESS_NAMESand detects from each row'sdetectionPaths. The opsx leftover sweep and doctor's harness scan both read a row's shape throughisHarnessDocument/findOpsxFiles. The receipt prints each selected row'ssetupNote, then upstream's singlerequiresIdeRestartline.rulesPathmarker from the row, andMANAGED_REMOVAL_ROOTSisremovalRoots(). Manifest routing is keyed onfrontmatter === null, which is how TOML commands get tracked. Ascope: 'home'file is refused before any write.scanRoots().None of the four shipped rows sets
requiresIdeRestart, so no receipt gains a line.Byte-identical output
git diff 70b32f9e HEAD -- apps/cli/test/unit/__golden__/harness-render/is empty.--harnesserror, both detection systems, removal containment, doctor human and--json): re-taken on the rebased, unmodified tree at2f5a9de7.git diff 2f5a9de7 HEAD -- apps/cli/test/integration/__golden__/harness-wiring/is empty.cospec init --harness all --yesin a fresh repo, run once from this branch and once frommain: both write 127 files to the identical file-tree digest (sha256:599c19a0…0902) and identical normalized stdout (sha256:aa758dca…eb62) — full byte identity, including the version stamp, since the branch rebased ontomain's v0.8.3 release before the comparison.generate:checkshows no drift,test:packis green, andapps/cli/test/contract/andparity-pending.yamlhave no changes againstmain.Review rounds (table-fitness gaps, no behavior change today)
Three post-implementation review rounds found table-fitness gaps — "no behavior change today, breaks when a row is added" — each fixed on this branch before merge:
update.ts's orphan sweep matched commands by a literal.mdfilter instead of each row's extension;doctor.tsattributed a file by primary root first instead of the longest matching surface, and matched references only on/instead of the owning row's invocation prefix.doctor.ts's harness-file scan and init's opsx leftover scan both read a literal.mdfilter instead of each row's shape (isHarnessDocument); the init receipt's shared-skills-root line was keyed on the literal idscodex/agentsinstead of equal resolved skills roots.doctor.tsattributed a file by primary root on a tie instead of the longest row surface, missing legacy skills roots as surfaces.Each fix added a synthetic fixture row to the relevant test file proving the gap, rather than relying on the four shipped rows (which can't exercise it).
Routed by ruling, not fixed here
Two further findings are genuine, user-visible defects on
maintoday — not table-fitness gaps this change introduces — and fixing them here would break the byte-identical scope above. Per ruling, they are routed to a new follow-on PR,harness-receipt-and-doctor-scope, sequenced directly after this one merges:Try: /cospec:propose …hint always prints in Claude's spelling, regardless of which row was selected..mdfile under a row's skills or legacy skills root, rather than narrowing toSKILL.mdand the table's command paths.design.md's Non-Goals no longer frame either one as a deliberate choice this change preserves — both are now named as known defects, out of scope here by ruling.Gates
Rebased onto
maintwice: first ontod25c5c0onceunknown-option-contract,upstream-spellingsandpassthrough-json-and-doctorhad merged (all three now recorded archived under## Blocked by), then a second time onto67f20c5d(the v0.8.3 release plusreset-yes-pipe-flake) — 0 conflicts both times.cospec validate harness-adapter-table --strictpasses andcospec applyexits 0.mise run checkis green on the final tree: lint, format, typecheck, unit 1891, contract 2403, integration 184, bench 339, release-test 14, all 0 fail;generate:checkno drift;agents:checkin sync;cospec-validate-all0 errors.Archive
cospec archive harness-adapter-table, no flags — validated, moved the change toopenspec/changes/archive/2026-10-05-harness-adapter-table/, blocking-changes fully synced. The archive commit touches onlyopenspec/paths:🤖 Generated with Claude Code