mcpbeat Sign in

Fix Tfcompat Bug Skill for Claude

Fix a proven OpenTofu-vs-pulumi-hcl divergence and ship it as its own PR. Use after find-tfcompat-bug has produced a failing tfcompat test — fix the runtime to match OpenTofu, sweep for the sibling bug, add unit coverage, and open a PR. Use when asked to fix/ship a known tfcompat mismatch.

2k tokens
context cost
the whole folder, loaded on every use
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
4
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/pulumi/pulumi-hcl --skill fix-tfcompat-bug

The instruction itself

7 sections, as written by the author

Fix & ship a proven tfcompat bug

This skill starts from a proven failing tfcompat test — a divergence already found and

captured by the find-tfcompat-bug skill, with a

tests/tfcompat/<l1|l2>_<name>_test.go + testdata/cases/<name>/ fixture that **fails on

master for the right reason** (a genuine OpenTofu-vs-pulumi diff or matched ExpectErr,

not a harness error). Edit the pulumi-hcl runtime to faithfully match OpenTofu, verify

failing-before / passing-after, sweep for the sibling bug, and ship it as its own PR.

If you do not yet have such a failing test, stop and run find-tfcompat-bug first —

do not invent a fix without a proven failure.

This skill ships exactly one bug fix on its own branch off master with its own

changelog entry and PR. Do not stack fixes.

Ground rules (from CLAUDE.md — do not violate)

  • Confirm assumptions with research. Don't guess. Verify every behavioral claim

against OpenTofu source, the cty stdlib source, the docs, or an empirical tofu probe.

  • Faithfully match OpenTofu. The goal is parity, not "better". No hacks.
  • git add specific files only, never git add .. Don't touch staging the user left.
  • Don't self-attribute as commit co-author. Complete punctuation in commit bodies.
  • Commit / push / open PR only when the user asks (the find+fix+verify is automatic;

shipping waits for the modeled flow below, which the user has standing-approved for

this loop — confirm if unsure).

Source-of-truth locations

The Go module cache root is !go env GOPATH/pkg/mod.

  • OpenTofu funcs: !go env GOPATH/pkg/mod/github.com/pulumi/opentofu@*/lang/funcs/*.go
  • OpenTofu binding map: .../opentofu@*/lang/functions.go
  • cty stdlib: !go env GOPATH/pkg/mod/github.com/zclconf/go-cty@*/cty/function/stdlib/
  • pulumi-hcl runtime: pkg/hcl/ (expression eval, resource / module / provider handling)
  • pulumi-hcl function registry: pkg/hcl/eval/functions.go
  • Function unit tests: pkg/hcl/eval/functions_test.go

Step 1 — Fix the divergence

Edit the pulumi-hcl runtime to match OpenTofu. For a function, fix the Impl in

pkg/hcl/eval/functions.go — and prefer delegating to the cty stdlib.*Func when

OpenTofu itself binds that stdlib function (check the binding map); several bugs were

hand-rolled reimplementations that should have been stdlib.IndentFunc /

stdlib.FormatDateFunc. For an L2 bug the fix lives elsewhere under pkg/hcl/. Fix

minimally and faithfully; run go mod tidy if you add a dependency.

Add unit coverage next to the code you changed. For functions, add cases to

pkg/hcl/eval/functions_test.go; note evalExpr calls t.Fatalf on diagnostics, so

error-path cases must call <fn>.Call(...) directly rather than via evalExpr.

Step 2 — Verify failing-before / passing-after

make build
PATH="$PWD/bin:$PATH" go test ./pkg/hcl/... -run '<UnitTest>' -count=1 -v
PATH="$PWD/bin:$PATH" go test ./tests/tfcompat/ -run 'Test(L1|L2)<Name>' -count=1 -v

Both must now PASS (with -count=1 — the rebuilt binary won't be picked up otherwise).

The tfcompat case that failed on master must now pass against your fix.

**Before you open the PR, check whether the same root cause produces a sibling

bug, and fix it in the same PR.** A divergence almost never lives alone: the same

helper, library choice, or code path usually backs a *sibling* operation, and

shipping only one half leaves the matching bug behind for the next migration to

hit.

Look in particular for:

  • The inverse operation. encode ↔ decode, parse ↔ format, marshal ↔

unmarshal, get ↔ set. Fixing yamlencode (hand-rolled on yaml.v3 instead of

go-cty-yaml) left an *identical* yamldecode bug — same wrong library, mirror

symptom — that had to ship as a separate follow-up. Don't make that mistake:

when you fix one direction, probe the other in the same session.

  • Sibling functions sharing the impl. Functions bound to the same hand-rolled

helper or the same family (base64*, file*, cidr*, the to* converters).

Grep for other call sites of any helper you touched.

For each candidate, run a quick tofu probe against the equivalent pulumi-hcl

result. If it also diverges in the migration-affecting direction, fold the fix

into this PR and extend the one tfcompat case to cover both (one combined

l1_<family> case is fine — e.g. l1_yaml exercising both yamlencode and

yamldecode). If a sibling is genuinely out of scope, say so explicitly in the

PR body rather than leaving it silently unfixed.

Step 4 — Changelog + branch + PR

Consult the submit-pr skill for opening a PR.

When writing your PR & changelog:

  • Keep the changelog body terse — a short phrase naming the change from the

user's point of view (e.g. Coerce a variable default to its declared type

or Add base64gunzip, urldecode and cidrcontains ), not a sentence explaining

it, and with no trailing punctuation. Keep the <thing> (function/feature name)

in backticks.

  • The changelog body must not mention OpenTofu (no "match OpenTofu", "like

OpenTofu", "instead of erroring", or other comparison framing). Parity with OpenTofu

is the whole project's premise, so stating it in the changelog is noise — just

describe what changed. The OpenTofu-vs-pulumi divergence narrative belongs in the **PR

description**, where you do say "OpenTofu" (never "Terraform").

  • Code comments must not mention OpenTofu either. Do not add comments saying the

code "matches OpenTofu" / "mirrors OpenTofu's X" — that applies to the entire codebase

and restates the premise. A comment should explain something the code itself doesn't

(e.g. *why* a helper is reimplemented rather than imported); if the only thing a

comment would say is "this matches OpenTofu," delete it.

Then, on its own branch off master (not stacked on a prior fix):

Report the divergence, the fix, the verification results, and the PR URL.

How to use it

Copy the folder

Take pulumi/fix-tfcompat-bug from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

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.