Run a sustained bug-farming round — keep a full swarm hunting until N distinct OpenTofu-vs-pulumi-hcl divergences are proven, then ship each as its own draft PR containing only the failing test. Use when the user asks to "find 10 more bugs", "raise the next N issues", or run another bug-hunting round.
npx skills add https://github.com/pulumi/pulumi-hcl --skill farm-tfcompat-bugs
A round ends when N distinct, vetted divergences each have their own draft PR
containing only the failing test. This is the steady-state version of
swarm-tfcompat-bugs, which stops at one bug and
reports. Everything there about partitioning, exclusion lists, and the direction rule
still applies — read it first. This skill adds what only shows up when you run the swarm
to a quota, round after round: **sustained headcount, adjudication under duplicate
pressure, PR staging, and saturation accounting.**
The three jobs that are actually yours (the lead's): de-confliction, adjudication,
and shipping. Hunters never open PRs. You vet every candidate personally.
git fetch && git pull master. Re-derive the exclusion list from *this* master:git log since the last round, gh pr list --state all, gh issue list --state open,
and ls tests/tfcompat/testdata/cases. Last round's fixes are this round's exclusions.
docs/terraform-compatibility.md and put its "Known Limitations" in everyhunter prompt verbatim.** That file is the owner's ruling on which divergences are
*accepted and documented* — unsupported terraform-block fields, the List<Object>
empty-vs-null limit, destroy-ordering of late-created instances, unsupported/extra
functions. A divergence listed there is settled, not a bug, no matter how cleanly it
reproduces. It grows every round (an owner closing one of your PRs by documenting it
lands here), so re-read it each round rather than trusting last round's copy.
branch in an area, that whole area is off-limits — not just the specific bug. State the
fence as a positive constraint in every prompt ("your signal must be a value, type,
mark, or op *kind* — never op *order* and never a dependency edge"), because a negative
fence alone gets rounded down to "avoid that one test".
fold it into the exclusion list. Domains that returned clean negatives twice are not
worth a third agent.
(general-purpose, run_in_background: true, unique name).
WRITE ALL FILES TO THIS WORKTREE: <absolute path> on its own line. Without ithunters write into the main checkout and you will hunt for their deliverables. This is
the single highest-value line in the prompt.
make build, thenPATH="$PWD/bin:$PATH" go test ./tests/tfcompat/ -run 'Test(L1|L2)<Name>' -count=1 -v.
A stale bin/ produces failures that no longer exist on master.
go test … > /tmp/<name>.txt 2>&1, then grep forexpected:|actual|Messages:|error:|does not contain. The harness diff carries ANSI and
wrapping that defeats a naive grep of scrollback.
it retires a domain. A fabricated bug costs more than an empty round.
Hold N hunters *working* until the quota is met — the drain is real: some domains come
back clean, some candidates get rejected, and later bugs cost more than early ones.
Re-task with SendMessage, don't respawn. A hunter that just proved its domain clean
already knows the harness, the providers, and the rejection bar. Send it the new domain
plus the *updated* exclusion list (including whatever just got filed). A fresh Agent
call pays for all that context again.
Expect to re-task roughly half the swarm once or twice to reach 10. If a hunter needs a
third domain, the search space is telling you something — see Saturation.
**The bar: you personally reproduce it 3/3 deterministic FAIL, on freshly built binaries,
with -count=1.** Not the hunter's run — yours. A hunter FAIL that PASSes for you is a
stale-binary artifact; reject it and re-task.
Then walk the rejection classes. Every one of these has cost a real round:
| Reject when | Why |
|---|---|
| pulumi-hcl is *more lenient* than OpenTofu | Wrong direction; no migration depends on it |
| SDKv2 optional-attr null vs "" | Zero-value artifact of the shim, not a divergence |
| Computed field recomputed on update | Self-healing, by design |
| Plan-time unknown-refinement diff | Unobservable to a user |
| Secret / sensitive over-propagation | Pulumi's stickier marks are by design |
| Only difference is number representation | Deferred float64 family — flag it, don't file it |
| Signal is op *order* or a dependency edge | Fenced; belongs to the scheduling work |
| Structurally indistinguishable on the wire | E.g. MaxItemsOne empty-vs-absent block |
| Listed in docs/terraform-compatibility.md | Accepted, documented limitation — settled |
Verify every claimed duplicate before rejecting on it. gh issue view /
gh pr view the number. Issues get closed by a *different* PR than the one that names
them, and a "fixed in #X" that actually landed in #Y still fails on master — that is a
live bug, not a duplicate. Equally: a bug you remember being filed may be closed and
scoped to a path that doesn't cover your case.
Borderline goes in the PR, with the relationship named. If a candidate might be a
sibling of an open issue, or might be intended behavior in the Pulumi model, file it and
say so in the body. The owner folds or closes it in seconds; a silently dropped real bug
is gone. Do not let "possibly by design" become a rejection reason on your own authority.
Take evidence-backed pushback from hunters. A lead's exclusion can be wrong. When a
hunter comes back with an empirical probe contradicting a fence you set, verify it and
change the fence. One round's #430 exists only because a hunter refused a bad exclusion.
Stage from a detached worktree so the session tree stays untouched:
git worktree add --detach <staging> origin/master
Then per bug, in <staging>: branch off origin/master → copy only that bug's
tests/tfcompat/<case>_test.go + testdata/cases/<case>/ (plus its provider file if it
added one) → git add those exact paths → commit → push →
gh pr create --draft.
command, the captured failure output, and any flagged relationship to an open issue.
git add .; never touch the user's index in the session tree.gh pr list) and note any the user has already adopted orretitled — that is the signal the round landed.
worktree. git status --short both, rm/rm -rf the exact untracked scratch paths
(test files, case dirs, deferred/rejected candidates), then `git worktree remove
--force + git worktree prune the staging tree. Never git add/reset/rm --cached`.
master SHA, the PR numbers, the rejections with their reasons, and the
domains proven clean — the next round starts from this note's exclusion list.
the exhausted domains, and cleanup status.
Yield decays across rounds. When most hunters need re-tasking, when negatives repeat a
domain a third time, or when reaching the quota requires filing borderlines — say so
plainly in the closing report and name where the remaining yield is concentrated. "A
round 6 will likely return fewer than 10 without new surface landing first" is more
useful than ten thin PRs. Do not pad a round to hit N.
ghTake pulumi/farm-tfcompat-bugs from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.