Separate generated content changes from version adoption→
Build one update planner that distinguishes content changes from receipt and pin adoption, preserves owner-written text, and never writes during inspection.
Field notes · No. 015
Senior DevOps Engineer building in public. Shipped features across Local Fitness, Ghostwriter, and more — the tradeoffs behind them, and what broke.
Build one update planner that distinguishes content changes from receipt and pin adoption, preserves owner-written text, and never writes during inspection.
A generator that splices a block into someone else's file has one hard requirement: the file must mean exactly what it meant before. Here is how to emit a comment banner into a YAML workflow and prove, without a YAML parser, that it changed nothing.
A fan-out bot that opens a pull request per release, and never closes the last one, does not merely leave clutter. The older pull requests fail their own checks by construction, and a bot with permanently red pull requests teaches everyone to ignore it.
A tool that writes generated blocks into other repositories has to decide which of its targets apply to the checkout it is standing in. File presence answers that badly the moment a target's path is a common filename, and the fix is to ask the checkout what repository it is.
An update bot that only acts when the generated values change leaves every consumer carrying several disagreeing version numbers. Open a pull request every release instead, and put the difference between a real change and routine adoption in the title.
Automation that clones other people's repositories has to pick a branch, and both obvious choices are wrong. A shallow clone cannot see the branch the work is on, and the default branch is not the branch teams integrate into.
A workflow triggered by a tag that another workflow created never runs, and nothing anywhere reports an error. Here is how to spot a dead trigger and how to chain the two workflows so the second one actually fires.
A release that adds something consumers must have will fail every consumer the moment they upgrade, unless the same change that raises their version also supplies the thing. Ship the requirement and its fulfillment together, and require consent before creating state.
A fan-out that pushes to several repositories has two ways to lie: it can fail to authenticate while every other step still works, and it can count those failures and exit zero anyway. Both shipped here.
Byte comparison is the cleanest way to prove a refactor changed nothing, until the build embeds something that differs every run. Canonicalize the volatile field instead of deleting it, and keep a control that proves the check can still fail.
A pinned dependency check proves a consumer is intact. It can never prove the consumer is current, because it passes forever against the version it was pinned to. Push freshness from the source instead of waiting for each repo to notice.
A font-family list that resolves perfectly in Chromium can silently pick a different face in a PDF renderer, because one engine never reaches the fallbacks and the other walks them for real. Predict which face wins, then measure the file.
Before moving a product onto a shared token set, read what it already ships and diff it against what the system can actually express. The values that fail that diff are the ones a migration quietly loses.
A shared value copied into eight files is eight values that happen to agree today. Here's how to generate a marked region inside each consumer instead, and gate it in CI so drift fails the build.