Skip to content

The day the feed posted the same sentence twice

I wrote a rule, then broke it myself, and the community feed put the same fallback line under my name twice in twenty minutes. So I stopped trusting my discipline and moved the rule into CI.

SD
Shubham Datarkar
· 2 min read

Every merged PR on this site auto-posts a little note to the community feed. There is a rule: write a Tweet: line in the PR body, or a small fallback pool of ~14 generic sentences fires instead. Yesterday the feed showed the same fallback sentence twice, twenty minutes apart, both under my name.

The rule lived in a docs file and in whoever remembered it. That is a bad place for a rule to live. Two PRs, #221 and #222, both merged without a Tweet: line, the fallback pool rolled the dice twice, and it landed on the same string both times. A visible duplicate on the public timeline. Nobody died, but it looked sloppy, and it looked sloppy in the one place strangers actually watch.

Here is the part I keep chewing on: #222 was mine. I am the person who wrote the rule down. Then a few hours later I opened a PR and forgot it. If the guy who authored the discipline cannot follow the discipline, discipline is not the mechanism. It never was. I was just getting lucky between misses.

Move the rule to where it cannot be forgotten

So I did what I should have done the first time and turned the rule into code. There is now a check that runs on every PR: if the PR is announce-worthy and has no Tweet: line, it fails. Not a Slack reminder, not a note in the template. A red X you cannot merge past.

The nice part is that the check imports the exact same file the merge webhook uses to decide what counts as announce-worthy. Gate and poster read from one source. They physically cannot disagree about the rules, because there is only one copy of the rules. That was the whole trick -- not the CI, the shared file.

This is also the repo's first GitHub Action, which meant an afternoon of learning things GitHub Actions people already know. A PR title is attacker-controlled text, so it goes into the script through an environment variable, never pasted into a shell line where someone could smuggle in a command. Read-only permissions. Small, boring, locked down.

The pool stays. Needing it is the bug -- so the bug now fails here instead of on the timeline.

the PR that fixed it

The lesson is not new but it landed hard because I was the counterexample: a rule that depends on a human remembering it is not a rule, it is a hope. The feed embarrassing me twice was cheap tuition. Move the check to the machine, wire the machine to the same brain the real thing uses, and stop asking your tired 11pm self to be reliable.

Funny footnote: the PR that added this check is itself a ci: change, which never announces -- so it correctly posted nothing. I checked. After yesterday I am done trusting that things just work.

Reactions

How was this article?

Newsletter

Never miss the next one

Get the latest playbooks, build logs, and the occasional unpublished idea straight to your inbox. One signal a week — no noise.