Repository navigation
chore - fan-out kit: verbs, skill, branch-naming independence, and modus retirement - #178
Merged
Merged
Conversation
Removing modus/package.json, scripts/, tests/, prompts/ and evals/ left the repository in a state where the root test chain did not fail — it recursed. `test:modus` ran `cd modus && npm test`, npm walked up to the root manifest with no modus package.json to stop at, and re-ran the root `test` script. CI would have hung to the job timeout rather than going red. Root package.json drops the modus workspace and the four modus test scripts. package-lock.json drops the stale extraneous entry npm would not prune. validate-plugins now treats package.json as optional: a skills-only plugin has no dependencies to declare and no test script to run, so the file carried nothing the host manifests do not already state. Where one exists it is still checked for name and version agreement. bump-version reads the current version from the Claude manifest when there is no package.json to read it from. modus goes to 0.5.0 rather than 1.0.0. It is a breaking change, but the plugin is pre-1.0 and the replacement guidance has not been compared against a plain agent with no skill loaded, which is not a thing to signal stability about. Prose follows the code. The README, both host manifests and the marketplace entry described a plugin that derives and freezes a definition of done; they now describe one that judges when requested work is finished, and the README says plainly that this is an untested hypothesis and that removing modus remains a legitimate outcome. human-work-item loses three links to the deleted boundary-prose.md and a section premised on a refusal mechanism that no longer exists. plugin-evaluation, refinement-loop and the fetch-depth comment in pr-check say what was removed instead of pointing at it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Files touched: - comitatus/skills/herdr/scripts/fanin.js Reviewer: inspect teardown evidence guards and merge conflict preservation.
Files touched: comitatus/skills/herdr/scripts/fanout.js Reviewer: check recipient-cwd role validation, all-handle preflight, and timeout row preservation.
Files touched: comitatus/skills/herdr/scripts/fanout.js, comitatus/__tests__/fanout.test.js Reviewer: verify failed delivery rows retain worktree and agent state alongside the error.
Files touched: - comitatus/skills/herdr/scripts/fanin.js - comitatus/__tests__/fanin.test.js Reviewer: verify zero remains valid while negative and non-finite values fail at parse time.
two partitions off the two defects the first fan-out run surfaced: up's unconditional git fetch, and completion read off agent status. 21 failing assertions. fanin.js carries typed stubs for parseSettled and settledCmd so the settle tests fail on an assertion rather than a missing export.
criterion 14: a verb in fanout.js/fanin.js that requires herd.js back must work when herd.js is the CLI entry, not only as a library require. the regression test spawns node herd.js so it exercises the path run 1's library-require tests could not see. 22 failing assertions now.
Files touched: comitatus/skills/herdr/scripts/up.js Reviewer: confirm remote detection remains after handle pre-flight and preserves the base passed to worktree create.
Files touched: - comitatus/skills/herdr/scripts/fanin.js - comitatus/skills/herdr/scripts/herd.js - comitatus/scripts/herd-setup.js - comitatus/skills/herdr/SKILL.md Reviewer: verify blocked precedence and the CommonJS export ordering.
fan-in's --wait-handle gate was bypassed deliberately. The gate waits for the architect's handle to reach idle|done, but the orchestrator role is taken by the same agent that wrote the manifest, so the handle to settle is the caller's own - and a caller is 'working' for the whole duration of its own command. Satisfying it with an unrelated settled handle would record a pass that verified nothing, so the merge is a plain sequential git merge in manifest order instead. Sole occupancy of the task worktree was confirmed against herdr agent list: oz only. Filed as .pipeline/runs/orch-selfhost/DEFECT-fanin-selfwait.md.
applied on the task branch after fan-in, because no partition may touch them: the four version manifests plus the lockfile entry (minor - settled is a new verb), and the SKILL.md prose for up's conditional fetch, which both partitions would otherwise have edited for one sentence each. also records .pipeline/runs/orch-selfhost/DEFECT-fanin-selfwait.md: fan-in could not be used for its own run, so the merge was a plain sequential git merge in manifest order with its settle gate bypassed. sole occupancy of the task worktree was confirmed against herdr agent list first. comitatus 294/294; repo 745 tests across 19 suites; validate:plugins passes.
…files the reviewer's only finding was that package-lock.json is modified in the diff and named in neither partition's file list nor shared_files. correct: bump-version.js moves comitatus/package.json, so the lock goes stale the moment it runs, and the architect role's shared_files definition lists lockfiles first. the omission was the manifest's, not the edit's. .gitignore is recorded for the same reason - it gained the runbook's .pipeline/runs/*/diff.patch line after the diff was generated, so it was not in what the reviewer read. no code change: the finding was about an unnamed file, not a wrong one.
first row in the file: the trial had no cumulative log until now. width 2, 38 wall min, 0 conflicts, 0 red-gate fails, 0 scope violations, 0 blocked, 1 reviewer hit, 0 escalations. spend columns are ? because this session cannot read the vendor dashboards, and a zero there would be a false entry in the one column the spend decision reads.
… three-valued rule names where the rule already lives rather than asserting it: probe-skill's exit 2 'could not run' in AGENTS.md, and send's three-valued delivery where accepted means unknown. 0 says the model cost nothing, ? says nobody looked. also records why the single reviewer hit landed on the orchestrator's own file: an implementer sees only its own file list, so a file owned by no partition is invisible from inside one.
.pipeline covered everything inside a run and had no slot for anything that outlived one. runs/<id>/ is retained deliberately, but indexed by run id rather than by subject, so 'has anyone looked at this' meant reading every run directory. backlog.md is that index; one file per item beside it. migrated the three items that had no home: the fan-in self-wait gate, the stale stub headers, and the branch-naming generalization. the other four beads run orch-selfhost filed restated task.md, two partition commits, and a manifest criterion - nothing to migrate. the override is recorded outside the managed markers in both AGENTS.md and CLAUDE.md, because codex implementers read the first and claude agents the second, and it was the managed rule addressing every agent with no role file saying who files that produced three beads for one task at width 2.
one partition, deliberately: fanin.js and fanout.js are disjoint files, but
both halves must agree on the flag names, the default and the composition,
and six independent copies of that logic IS the defect. two partitions would
each write their own and both go green, reproducing the bug at width 2.
23 failing assertions. a throw names nothing, so the not-yet-supported flag
is wrapped to print what came back: {"threw":"unknown flag: --task-branch"}.
the run's own branch is chore/fanout-branch-naming while its run id is
fanout-branch-naming, so the run exercises the decoupling it builds.
Files touched: - comitatus/skills/herdr/scripts/branch-naming.js - comitatus/skills/herdr/scripts/fanin.js - comitatus/skills/herdr/scripts/fanout.js - comitatus/skills/herdr/scripts/herd.js Reviewer focus: - slash separators are refused before branch creation or teardown - --run continues to name only the pipeline artifact directory
version bump across the four manifests AND package-lock.json, which is named in this manifest's shared_files because run orch-selfhost omitted it and the reviewer caught it. SKILL.md's fan-out section opened by asserting '<id> is the task id and the branch name both', which is the assumption this run removed, so the paragraph is rewritten rather than appended to: --run names the artifact directory, --task-branch names the branch, and the / separator is refused with git's own reason. kept out of the partition so usage() and SKILL.md are not two descriptions of one surface written by two agents. backlog item marked done.
rvw found --partition-sep read raw via args[++i] while every other flag went through requiredText, so 'api,--partition-sep,--base' returned '--base' and composed task/r7--baseapi. reproduced independently. the generic validator is not the fix, which is why the hole was there: requiredText rejects /^--/, and '--' is a separator parseSettled must accept. so the separator gets its own rule - punctuation git allows in a ref, nothing else - and '/' keeps its specific git-reason message, so that check runs first. carried to all four parsers, not just fanout: teardown composes the name it hands to git branch -D, which is the destructive path this manifest's risk notes already called the highest in the change. 7 failing assertions. tests only - the fix is the implementer's.
Files touched: - comitatus/skills/herdr/scripts/branch-naming.js - comitatus/skills/herdr/scripts/fanin.js - comitatus/skills/herdr/scripts/fanout.js Reviewer focus: - explicit separators accept only flat -, ., and _ punctuation - slash keeps its git-specific error before the punctuation rule - parsers reject missing and flag-shaped separator values
width 1, 32 wall min, 0 conflicts, 0 red-gate fails, 0 blocked, 1 reviewer hit, 0 escalations, 2 scope violations. both violations were operator-initiated mid-run and neither went through the manifest: 9fbd064 deletes four boundary/ files that exist on main, after the review had already run, and the fan-out skill split was uncommitted when this row was written. the row states what it measures - criteria 1-14 verified at 865e048 - and says plainly that everything after that commit is outside it. path-scoped commit on purpose: another agent is editing this worktree, and a git add -A there would sweep this row into its commit.
operator confirmed 9fbd064 is deliberate and meant to reach main. the row still counts it in scope violations, because that column measures what changed outside the owning partition's file list - a fact about the diff, not a verdict on the change.
- Add the deployable comitatus fan-out skill with packaged pipeline roles - Reference fan-out from the herdr skill and register it in the skill catalog - Bump comitatus to 0.13.0 and record the completed session
A deployed user with no repository-local .pipeline/roles had no path to give `role --roles-dir`: provisionStable copied only skills/herdr, so the orientation had nothing to name. The fan-out roles now land beside the helper under ~/.claude/comitatus and the orientation prints the absolute path. Provisioned under its own try/catch, and only named once the directory exists on disk. A fan-out copy that fails must not cost herdr its stable helper path, and an orientation naming a directory nothing wrote would point `role` at a missing file - the failure that line exists to prevent. provisionInto/provisionStable take a `name` (default herdr), so existing callers and the .herdr.tmp staging convention are unchanged. README: the hook never writes to the Codex home - herdr-orient.js says so and a test pins it. The bullet claiming a ~/.codex/skills/herdr sync was stale prose describing behavior the hook does not have. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
fanout minted impl1/impl2, which is not the comitatus convention: a handle is a bare call-sign from the pool, globally unique, carrying neither model nor part, so a member can be relaunched on another model without renaming the identity everyone addresses it by. The part belongs on the tab label instead, where a human scanning the sidebar can see which agent is playing what. - up.js: `role=` joins model= and effort= as a per-agent selector key. It is label-only - it never reaches the agent's CLI args, and a test pins that. The tab becomes `<handle>-<role> <glyph>`, keeping the glyph's kind signal. - fanout.js: handles come from the pool, read out of reference/names.md rather than copied into JS. That document is what a human claims handles from; a second copy here would drift from it silently. A name in use is skipped; an exhausted pool throws before the first worktree, as a collision used to. - --handle-prefix is removed, not aliased onto --role-tag. It named the handle, and quietly repointing it at a tab label would be the wrong meaning. It throws a message naming its replacement. - A bare --selector is a model id, so the role tag is added after normalising to key=value form; appending to a bare model would make one unparseable id. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The skill described the verbs but not how to run a task with them: the recipes
lived only in .pipeline/runs/orch-selfhost/fan-out-trial.md, a run artifact no
deployed user has. Carried across inline - the four recipes, the topology table
and its one-writer-per-tree rule, the model table, the git state table, and the
operational gotchas. Left behind the trial scaffolding (log.md columns, the
decision table, "do not build yet"), which is about running a trial rather than
doing a fan-out.
Also:
- Which position you hold. The skill is read by more than one agent in a run, so
it no longer asserts who the reader is - a reader identifies itself by whether
it was sent a role file, and the launcher's constraints are third-person so an
orchestrator cannot pick them up by accident.
- The launcher's boundary is now explicit: it sets the run up, then reads,
decides and approves, and writes to no task or partition tree. Previously the
skill said the opposite ("you by hand"), and no role file bounded it.
- Branch names are placeholders. A skill whose subject is --task-branch should
not write task/<id> fifteen times as though it were law; the default is
documented as a default.
- architect.md ends at the red gate and says so. It never mentioned the
orchestrator, so nothing told the one agent that takes both roles to read the
second file - the run stalled there. The launcher verifies the gate and sends
orchestrator.md; self-chaining would have the architect grade its own failing
tests.
- Effort levels follow vendor guidance: xhigh for the opus architect and
orchestrator (Anthropic's level for coding and agentic work, and Claude Code's
own default), medium for the codex implementer (OpenAI's level for routine
development, which names "fixing a failing test"), high for the prober
(OpenAI escalates for difficult debugging, the debug recipe's premise).
- The mechanical tier moves from Haiku to sonnet at low effort: one fewer model
in the run, and 1M context where a directory summary could exceed 200K.
.pipeline/roles/ holds copies rather than symlinks, so both are updated.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Minor: new hook behavior (fan-out roles provisioned to the stable home, a new orientation line) and a new `role=` selector. --handle-prefix is removed, which is breaking, but comitatus is pre-1.0 and this is not a 1.0 declaration. Co-Authored-By: Claude Opus 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.
Consolidates several
.pipelineruns on this branch. 130 files, ~5.3k insertions / ~11.8k deletions — the deletion count is mostly the modus retirement and boundary-artifact cleanup, not removed functionality.What's here
Fan-out verbs (
fanout-comitatus,orch-selfhostruns) —role,fanout,wait-all,state,settled,fan-in,teardownonherd.js.statederives phase from git refs and stores nothing;settledreads completion from commits rather than transient agent status.Branch-naming independence (
fanout-branch-namingrun) — the six hardcodedtask/${run}literals collapse into one resolved value.--task-branchdefaults totask/<run>,--partition-septo-, and a/separator is refused at parse time with git's own reason (refs are files, sorefs/heads/xandrefs/heads/x/apicannot coexist). A run id and a branch name are now independent.comitatus:fan-outskill — extracted fromherdr/SKILL.md, which keeps a pointer rather than a second copy of the runbook. The four recipes, topology, model table, and git state table are now in the skill itself rather than in a run artifact no deployed user has.Deployed-install fixes (this session's work, 4 commits)
provisionStablecopied onlyskills/herdr, so the orientation had no path to hand--roles-dir. Fan-out roles now provision beside the helper and the orientation prints the absolute path.reference/names.md, not copied into JS) instead ofimpl1/impl2. The part moves to the tab label as<handle>-<role> <glyph>.--handle-prefixis removed with an error naming its replacement.architect.mdnever mentionedorchestrator.md, so nothing told the one agent that takes both roles to read the second — the run stalled at the red gate. The launcher now verifies the gate and sends the second role.xhighfor the opus architect/orchestrator,mediumfor the codex implementer,highfor the prober. Mechanical tier moved off Haiku tosonnetatlow.modus retirement — plugin dropped from the npm workspaces array;
modus/package.jsonand its lockfile entry removed accordingly. Bumped 0.4.0 → 0.5.0.Validation
npm test— all suites green (82 / 48 / 23 / 48 / 65 / 338)npm run validate:plugins— passedclaude plugin validate . --strictandclaude plugin validate comitatus --strict— both passed (the fan-out review manifest had recorded these as unrun due toENOTFOUND; they run clean now)quick_validate.py comitatus/skills/fan-out— validReviewer notes
--handle-prefixthrows). Pre-1.0, so it ships as minor rather than declaring 1.0.0 — flagging rather than deciding.fan-in --wait-handlestill defaults toarch, which is never a pool call-sign under the new convention. Every recipe passes it explicitly; the stale default is left for a follow-up.sly-archfor the whole run, including the orchestration phase. Either rename at the handoff or definearchas "the pane that owns the task branch" — undecided.herdr/SKILL.mdstill addresses the reader as being launched under the main-checkout workspace, which is false for every agentfanoutplaces in a partition worktree. Not touched here.🤖 Generated with Claude Code