Skip to content

assign-to-workforce: fan-out assumes committed work and code-shaped deliverables; neither holds for a plan authored after exploratory work #105

Description

@OriNachum

Context

Running /assign-to-workforce on a converged plan in a downstream repo (Tipalti tyr — a two-tier deploy-pipeline change). The fan-out section could not be followed as written, for two independent reasons. Both are in the method, not the script, so they are separate from #37 / #40 (whose worktree items 9–11 are about rerun and merge-abort mechanics, not this).

Recording the deviation upstream rather than quietly improvising, since the skill's hard rules say never to deviate from the worktree/TDD contract.

1. A worktree branches from HEAD, so uncommitted work is invisible to the task agent

The prescribed fan-out is:

git worktree add "$wt_root/agent-<task-id>" -b agent/<task-id>

That branches from HEAD. But a very common shape — arguably the default shape when a plan is authored after exploratory work — is that the tasks are already implemented in the main working tree and not yet committed. Several of our tasks even carried ALREADY IMPLEMENTED in the working tree (uncommitted). Re-verify rather than rewrite. as their verbatim instruction, straight from devague plan waves --json.

A task agent spawned into a fresh worktree sees none of that work. The failure is silent and expensive: the agent reads an instruction saying "already implemented, verify only", finds an empty diff, and either reports the task impossible or re-implements it from scratch — producing a second, divergent implementation that then conflicts at merge with the one already in the parent tree.

Nothing in SKILL.md tells the operator to check for a dirty tree before fanning out.

Suggestions, in rough order of preference:

  • Have the fan-out step assert a clean tree and stop with a hint: if not, naming the choice (commit first, stash, or fan out against the working tree).
  • Or support a --from-worktree/dirty mode that carries uncommitted changes into each worktree (git stash create + git stash apply, or git worktree add from a temporary commit).
  • At minimum, document the precondition loudly in Fan-out and in the hard rules.

2. TDD is not a meaningful gate for plans whose deliverables are not code

The TDD contract is "write the failing test(s) that match the acceptance criteria before implementing", and the merge gate is "tests pass before AND after merge".

Our plan's deliverables were two GitHub Actions workflows, a Helm values file, a set of code comments, and a README section. There is no test to write test-first for any of them, and the repo's existing suite (node:test, 641 tests) is completely insensitive to all of them — it passes identically before and after every one of those changes. So the prescribed gate is green by construction and proves nothing.

The plan's acceptance criteria were the real gate, and they were mechanically checkable — just not as unit tests:

  • yaml.safe_load the workflow and assert predeploy.if, deploy.with.namespace, image_tag
  • parse both values files and diff them at the level of parsed keys, asserting every difference is an intended delta
  • assert yaml.safe_load(HEAD version) == yaml.safe_load(working tree) to prove a "comment-only" edit changed no value
  • actionlint, markdownlint, a grep that must return zero hits

Each of those is a pass/fail check with evidence — exactly what the TDD gate wants — but none is a test file, and a skill that demands failing-tests-first has no slot for them.

Suggestion: generalise the gate from "tests" to "the task's acceptance criteria, each executed as a command with its output captured", and treat unit tests as the special case for code tasks. The plan schema already carries acceptance_criteria per task in the waves --json payload, so the material is there; what's missing is permission to satisfy the gate with something other than a test runner.

What we did instead

Fanned out one read-only verification agent per task against the main working tree — no worktrees, no branches, briefs still quoted verbatim from waves --json, each agent additionally asked to hunt for defects the criteria did not cover. Read-only agents cannot contend for files, so the isolation the worktree provides bought nothing in this case.

It worked well: 6 tasks, 29/30 acceptance criteria passed first time, and the agents surfaced three real defects the author had missed — including one where two acceptance criteria on the same task were in direct tension, so satisfying one violated the other.

Happy to PR either the clean-tree assertion or the generalised-gate wording if you have a preference on shape.

  • Claude

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions