I made my coding agent explain every change to the customer. Here is the skill. A developer has released product-engineer, an open-source skill pack that forces coding agents to explain every code change in plain customer-facing language and to document what was deliberately left unbuilt. The pack, distributed via npx and as a Claude Code plugin, adds a "For the customer:" block to commits and a definition-of-done checklist, with an optional commit-msg hook that rejects commits lacking the block. It supports Claude Code, Codex, Cursor, Copilot, Gemini CLI and other agents, with generated instruction files kept in sync via CI. The diff is never the expensive part. The expensive part comes after: explaining to the person who asked for the change what they actually got, and discovering that "done" meant "tests pass". I spent four years as a founding engineer on a hospitality platform and then alone on a proactive assistant, and the habits that saved me the most time had nothing to do with code. Restate the request as a customer outcome before building. End every commit with a plain-language block. Do not call anything done until you have watched it behave. Write down what you deliberately did not build, next to the code. Never print a number you did not count. Coding agents have none of these habits by default. So I wrote them down as a skill. It has since become a pack of four: product-engineer carries the seven rules, customer-block and done-means-observed carry one habit each for people who want only that, and release-notes is for an agent that writes about software instead of writing it. One installer places whatever you pick. npx skills add Bubblegunn/product-engineer That places the skills for the agents in your project: Claude Code, Codex, Cursor, Copilot, Gemini CLI and the rest. Claude Code can also take it as a plugin: /plugin marketplace add Bubblegunn/product-engineer then /plugin install product-engineer@bubblegunn . Agents that read instruction files rather than skills Cursor rules, Copilot instructions, Gemini, Cline, Kiro, Windsurf get the same seven rules from files generated out of SKILL.md , and CI refuses a commit where the generated copies differ from the source. Source: github.com/Bubblegunn/product-engineer https://github.com/Bubblegunn/product-engineer . Before: fix notifications : classify IsSystem events as idempotent and skip the badge increment After: fix notifications : classify IsSystem events as idempotent and skip the badge increment For the customer: What changed: Things the system handles on its own no longer light up the phone or the menu badge; only items that need a person do. Why it matters: The badge count means "something needs you" again, so people stop ignoring it. Automation effect: Automatic housekeeping events are now fully handled without anyone looking at them. The agent used to report: Done. All 42 tests pass. Now it reports: Watched it in staging logs: 3 of 3 webhook retries logged, final failure recorded with the booking id. Could not check: production, no deploy yet. Not shipped: - Dead-letter queue: three retries cover every failure in 90 days of logs. Not shipped: list with reasons, in the PR, so the decision lives where the code lives. The full text is one file, SKILL.md , 89 lines. The reference files hold the template, the five questions to answer before building, the definition-of-done checklist, the four plain-language tables, the not-shipped format, and three short notes on the press-release restatement, ship-show-ask and appetite. If you want the rule enforced rather than suggested: sh scripts/install-hook.sh That installs a commit-msg hook. A commit without the block is refused with a two-line explanation. Merges, fixups and reverts pass, and no-customer anywhere in the message opts one commit out. The skill's own repository runs it, so every commit there is a demo. The same check runs on its own, on a file, on stdin, or on a pull request body through gh . On a message without the block and then with it: bash $ npx product-engineer check test/fixtures/without-block.txt error no "For the customer:" block 1 error, 0 warnings $ npx product-engineer check test/fixtures/with-block.txt ok "For the customer:" block with "What changed:" ok "Why it matters:" present ok "Automation effect:" present info readability of the block: Flesch 75 easy , LIX 30 ok "Not shipped:" lists 1 item with reasons no errors, 0 warnings It also ships as a commitlint plugin with a shareable config, a pre-commit hook, and snippets for lefthook and husky, so a team that already has one of those adds a line rather than a tool. In CI it reads the pull request body and leaves one comment that it updates on every push, rather than a new one each time. npx product-engineer doctor reports which agents on the machine have the skill and whether their copies are current. Before posting this I ran eight small coding tasks in Claude Code, each once with the skill and once without, and scored the agent's own words and git diff with five yes/no heuristics. The customer block went from 0/8 to 8/8, an observation or an honest "could not check" before "done" from 1/8 to 7/8, naming what was deliberately not built from 2/8 to 7/8. Two metrics did not move: a number with a method next to it scored 1/8 in both conditions, and every change in both conditions stayed within the requested files. The skill runs took about 60% more turns and cost about 45% more, because they verified more and wrote more. One run per task, so it is a smoke test, not a study; the harness, every transcript and the misses are in evals/RESULTS.md https://github.com/Bubblegunn/product-engineer/blob/main/evals/RESULTS.md . One thing that table does not say, and the repository now does: those runs installed the core skill and nothing else. The pack also ships customer-block and done-means-observed , each carrying one rule on its own, and neither extraction has been measured separately; release-notes has no task at all, because none of the eight writes release notes. Each skill file states its own gap. The alternative was to move three of four skills out of the pack and call it discipline, which would have said I shipped four things and believed in one. The evaluation above uses small tasks. Afterwards I ran a real one twice: the same model, the same starting commit, and a defect I had actually shipped. The only source file in one of my packages held a literal NUL byte inside a string, so git classified the file as binary and no diff on it was readable, on GitHub or locally. An outside contributor could not see their own change. Both runs produced a byte-identical fix, both spotted that the fix commit still shows as binary against its binary parent, and both declined to add a .gitattributes override for a reasoned cause. The skill did not make the model a better engineer, and I would not trust an article that claimed otherwise. What differed is what survived the session. Only the skill run's commit message carried the customer block, the observation it had actually made edit a line, run git diff , see a text hunk, revert , a Not shipped: line, and a plain-language paragraph. The bare run said those things in the chat window, where they die with the session. The whole transcript of both is in docs/case-study.md https://github.com/Bubblegunn/product-engineer/blob/main/docs/case-study.md . In that pair the skill run was also cheaper, 18 turns against 29, which is the opposite direction to the 45% above. One pair proves nothing either way; I am recording it because leaving it out would be picking the flattering number, which is rule five. An audit today found the check rejecting a commit whose block was written as Müşteri için: , in a repository that ships its plain-language tables in Turkish, Japanese and Chinese. Inviting a team to work in their language and then failing their commit is worse than not inviting them. The headings are data now, one row per language, read by the check, the hook and the pull request comment from the same file, with a test that fails if those three drift apart. English, Turkish, Japanese and Chinese are accepted with no configuration, a fullwidth colon reads as a colon, and a repository can name a heading for a language the table does not ship yet. The same audit caught the readability helper returning Flesch 0, hard for Japanese. Flesch counts vowel runs as syllables, which is not what a syllable is in Japanese, Chinese, Korean, Arabic or Hebrew. It now refuses to score those scripts and says why, because rule five of this skill is that a number comes from a count, and a tool breaking its own rule on its own README's languages is the worst kind of wrong. It runs no process and owns no workflow; it composes with whatever spec, TDD or review skill you already use. It does not write product strategy. It enforces nothing unless you install the hook. Which of the seven would you throw out, and what would you add? I would rather hear the disagreement than the agreement.