#1111 Declare which harnesses sync targets - #1221
Merged
Merged
Conversation
`codeassembly.yaml` now takes a `harnesses` block with `use` and `drop` lists, alongside the existing artifact-type blocks. A name that does not match a known harness fails the run, naming the file and the entry that carries it. Nothing consumes the block yet; a later change resolves it into the harnesses a sync run targets.
`sync` deploys a project's skills, subagents, and ambient rulebooks into every harness installed on the machine, whatever directories the repository itself holds. Previously it looked for `.claude` and `.rovodev` inside the repository, so a project that committed neither — most of them — received nothing for the missing harness and reported success having written no file. A project pins its own set with the `harnesses` block in `.agents/codeassembly.yaml`, which takes `use` and `drop` lists and resolves across the user-global and project tiers. `--harness` still overrides both. `sync --global` honors the same declaration. Because the block states which harnesses a developer runs, a project tier cannot discard what the user-global tier declared: `root: true` clears only its own tier pair. A `drop` still reaches across, so `.agents/codeassembly.local.yaml` can withdraw a harness for one checkout. Every run now names the harnesses it targeted and what decided them, and a run that fell back to detection names the key that would pin the set.
The `codeassembly init` scaffold describes where an ambient rulebook actually lands: the ambient region of every targeted harness's machine-local project guidance file, `CLAUDE.local.md` or `AGENTS.local.md`. It had gone on naming `PROJECT.md`, which stopped receiving ambient content several releases ago. Both the project and user-global scaffolds also carry a commented `harnesses` block, so the key that pins harness targeting is visible in the file that hosts it.
The project-declaration reference covers the `harnesses` key: its shape, the order a run resolves its targets in, and how a project's targeting composes with an artifact's own `supported-harnesses` frontmatter. It also names which commands honor the declaration and which are answered by detection alone. Two rules readers would otherwise have to infer are now stated: `harnesses` resolves across the user-global and project tiers, alone among the keys, and `root: true` clears only the contributions of the tier pair that declares it. The scopes reference no longer claims without qualification that the domains never cross. Passages describing where `sync` deploys say "targeted harness" rather than "detected harness", matching what decides the set.
Adds the case proving `sync --global` narrows to the harnesses its own tier declares, delivering to that harness's guidance file alone. The behavior shipped with the declaration itself; only its coverage was missing.
`sync --global` now points its pin advice at `~/.agents/codeassembly.yaml`. It had named `.agents/codeassembly.yaml`, which a global run never reads, so following the advice left the next run reporting the same fallback. The `init` scaffolds seed `harnesses.use` with a harness id rather than an empty list. An empty list is honored as "target nothing", so a reader who uncommented the previous example verbatim disabled every deployment for that scope, having just read a comment promising the fallback instead. The `init --global` scaffold no longer claims that only a project's gitignored `codeassembly.local.yaml` can withdraw a harness. Either project-tier file can, `drop` being unscoped by domain; only `root` is confined to its own tier pair.
The `init` scaffold cases assert the wording each template ships. Two of them guarded a deleted phrase with `not.toContain`, which encodes history rather than contract: it can fail only on a verbatim revert, so a differently-worded but equally wrong claim passes it. The global template's withdrawal claim, previously unasserted, now has a positive case. The assertion unwraps the comment before matching, so it reads the sentence rather than the column the template happens to wrap at.
Dependency auditProduction dependency audit passed. |
williamthorsen
marked this pull request as ready for review
August 6, 2026 22:03
Harness targeting resolves entirely from the user's home directory: both the declaration it reads and the installed harnesses it falls back to live there. The README, the scaffolds `init` writes, and the code comments now describe that scope as the user's rather than the machine's, matching the `user-global` tier name used elsewhere. On a shared machine, each user carries a separate declaration and a separate set of installed harnesses.
williamthorsen
added a commit
that referenced
this pull request
Aug 7, 2026
…ssembly-mcp-v0.2.4 codeassembly-v0.6.0 codeassembly-v0.6.0 - #794 feat: Guide kb-add note placement with the store's declared taxonomy (#1223) - #1111 feat: Declare which harnesses sync targets (#1221) - #1214 feat: Allow an artifact to belong to more than one collection (#1219) - #1186 drop!: Dissolve `common-mistakes` into its consumers' guidance (#1217) - deps: Upgrade all deps to latest version - #1203 fix: Bar acceptance-criteria revision prompts to genuine conflicts (#1212) - #1196 feat: Add the .kb/taxonomy.yaml format with drift reporting and back-fill (#1210) - #1165 feat: Make a vetted subset of the library declarable (#1207) - #1197 feat: Report the retired `harnesses:` frontmatter key from `validate` (#1204) - #963 feat!: Rename the harness id to rovo and qualify the frontmatter key (#1199) - #1188 drop!: Retire the bundled Bitbucket inline-comment script (#1198) - #1187 feat: Gate the boolean prefix on a test and add the tail rule it rests on (#1195) factory-v0.2.4 - deps: Upgrade all deps to latest version - deps: Upgrade all deps to latest version - #1209 refactor: Clear factory's remaining lint violations and retire both deferral lists (#1216) - #1208 refactor: Clear the visualizations lint violations and retire six rule deferrals (#1213) kb-v0.4.0 - #794 feat: Guide kb-add note placement with the store's declared taxonomy (#1223) - #1196 feat: Add the .kb/taxonomy.yaml format with drift reporting and back-fill (#1210) codeassembly-lifecycle-v0.3.0 - #963 feat!: Rename the harness id to rovo and qualify the frontmatter key (#1199) codeassembly-mcp-v0.2.4 - #1206 refactor: Retire fleet's lint deferrals (#1211) - #1200 tooling: Retire mcp's lint deferrals and prune the dead root entries (#1205)
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.
What
Guidance artifacts declared by a project (skills, subagents, rulebooks, and their dependencies) are now automatically deployed into every installed harness, and no longer depend on detection of a previous deployment when identifying which harnesses to target. Target harnesses can be declared for the user, for the project, or for a single copy of it, with command-line options available as overrides. The deployment report names the harnesses it targeted and how they were decided.
Why
Project-domain
syncdecided its targets by probing the repository for.claudeand.rovodev. Nothing establishes that precondition: those directories hold generated output, so a repository has no reason to commit one. Claude was targeted incidentally wherever a repository committed harness config; Rovo Dev generally was not targeted at all. A repository with neither directory received no project-domain artifacts of any kind, and the run reported zero targeted harnesses rather than naming what it had skipped --williamthorsen/operationsdeclared an ambient rulebook and received nothing.Which harnesses a developer runs is a fact about the user, not about a checkout, so no repository probe can answer it. The declaration puts the answer where it belongs and leaves detection as the fallback for anyone who never states it.
Details
🎉 Features
codeassembly.yamlaccepts aharnesses: {use, drop}block in the same shape as every other declaration key. An unknown harness id fails the run, naming the file and the offending entry.--harnessflag, then the declaration, then the harnesses installed under the home directory. A declaration resolving to an empty set is honored -- the run targets nothing and says so -- which is distinct from an absent declaration, which falls back.harnessesresolves on a chain (the user-global tier pair, then the project tier pair), making it the one key that crosses the two declaration domains. Artifact keys continue to resolve on the project tier alone, so a user-globalcollections: use: [all]cannot deploy the whole catalog into every repository.root: trueclears only its own domain's contributions. For artifact keys this is indistinguishable from clearing the whole chain; forharnessesit is what stops a committed project file from discarding the user's global declaration. Adropstill crosses the boundary, from either project-tier file.sync --globalhonors the declaration on the same terms.install,uninstall,status, andconfigure-hooksdeploy into the harness homes and keep detection.🐛 Bug fixes
init's project scaffold namedPROJECT.mdas the ambient destination, which stopped receiving ambient content in Deliver project ambient rulebooks per harness #1088. It now names each harness's instance-local project guidance file (CLAUDE.local.md,AGENTS.local.md).🧪 Tests
root: trueleaves user-global harnesses standing, while a user-global-tier one clears them.syncCommandcall in the suite injects a temp home directory, so no test reads the developer's own declaration or installed set.📚 Documentation
Harness targetingsection in the agents README covers the key, the resolution order, the two-chain rule, the domain-scopedrootrule, and how targeting composes with artifact-levelsupported-harnesses:-- targeting selects the harness set, artifact narrowing filters within it.Scopessection claimed without qualification that the declaration domains never cross. It now scopes that claim to artifact keys and namesharnessesas the deliberate exception.initscaffolds surface theharnesseskey, with an example that targets a harness rather than none.Closes #1111