Dogfooding specdx on this site

Dogfooding only works if friction leaves a trace. This site runs on a written contract with specdx: when the tool breaks a workflow here, I file the issue instead of routing around it. Over four days in July that contract produced ten issues, ten fixes, and a sharper tool — this log is the full loop.

The contract: friction is a bug report

This repo’s CLAUDE.md contains one sentence that does most of the work: “friction is a bug to report, not a reason to route around the tool.” Solo dogfooding fails by default without it. When you own both the project and the tool, the cheapest move is always a local workaround — and every workaround destroys the evidence that the bug existed. Writing the contract down, where the agent working on the site also reads it, makes the expensive move the default one.

One schema bug, end to end

The first loop was textbook. This site’s spec.config.yaml declared a spec of type project-context — a documented type — and specdx validate rejected it, along with quick-spec and epic: the config schema accepted only six of the nine types the sections schema defined. The workaround (declare it as technical-design, with a comment) went in labelled as debt in DECISIONS.md, and the issue went upstream the same evening with the exact error output. The fix shipped in 0.4.0-alpha.3, the workaround came out the next day, and the gate ran clean. Elapsed time from friction to retired workaround: about 24 hours.

Six issues in one afternoon

Two days later I went looking instead of waiting. This site runs spec-first on non-trivial work, so there were real specs to point the tool at: two new ones joined the suite — a technical design for a crawler-logging experiment, and the content calendar as an epic, the very type the first issue had un-broken — and then every CLI command ran against them. Six issues came out of one afternoon: --version reporting 0.0.0; the dependency graph ignoring the schema’s own references frontmatter; check crashing under pnpm dlx with advice that can’t work there; check reporting “100% implementation coverage” on a spec with zero implementation; pack dropping the exact spec a task named when the token budget got tight; and ready passing a story-coverage check against a PRD that didn’t exist.

The dangerous bugs report green

Three of those six shared a shape worth naming: they were vacuous passes. The drift checker supports three frameworks, and on a site built with none of them it found nothing checkable — and called that 100% coverage. A CI job wired to that output would certify unimplemented specs forever. The crash bugs are cheap: they announce themselves at the moment of failure. The green-checkmark bugs are expensive, because the whole point of the output is to be trusted unread. The fix that matters isn’t the coverage math — it’s that check now says “coverage not assessed” and exits with a distinct code (3) so automation can’t mistake silence for health.

Verifying the fixes found two more

0.4.0-alpha.6 arrived the same day claiming all six fixed. Instead of trusting release notes, I re-ran every repro. All six held — and the verification pass itself surfaced two new bugs. pack, now correctly keeping the task-named spec, trimmed it from 849 tokens to 342 to fit the budget and said nothing: five sections gone, no omission marker, so an agent consuming the packed context would read a spec that ends after Architecture as a spec with no Risks section. And the graph’s new mismatch warning suggested a requires edge that was the exact reverse of an existing one — advice that, followed, produced a circular dependency validate happily accepted. Both went upstream; 0.4.0-alpha.7 closed them the same day, and this time re-verification came back clean.

The tenth issue got a better fix than the one I asked for

The sprint left one thing deliberately unfixed. check now told the truth about this site — exit code 3, “coverage not assessed” — but an honest zero is still a zero: the drift checker supported three frameworks, this site uses none of them, and so nothing here was actually being checked. The morning after the sprint converged I filed the tenth issue asking for Astro support.

It closed in under two hours, and not with what I’d asked for. Rather than an Astro extractor, the fix went framework-agnostic: any spec can declare the artifacts it claims to produce — file paths, plus named exports per file — and check verifies them with no framework knowledge at all. The design was recorded as the specdx suite’s first ADR, with per-framework route extraction explicitly deferred. Asking for support for my stack produced an answer that works for every stack. That’s the reason to file the issue rather than fork the tool: a workaround would have solved it for one Astro repo, and solved it worse.

This site declares 25 such checks across its three specs today, and specdx check runs in the verification gate — so drift detection blocks a commit here, where four days earlier there was nothing to run.

The tally, and one irony

Ten issues in four days, every one closed the day it was filed. What stays behind in this repo: specdx pinned as a devDependency, check-specs in the five-command verification gate and CI — now running drift detection, not just linting — and two real specs feeding real future work. And one irony, found the same afternoon: while writing up vacuous green checkmarks in specdx, a casual “all ok?” revealed this repo’s own CI had failed on every push since the rebuild — a pnpm version conflict that killed setup in ten seconds, invisible because deploys never depended on it. Vacuous signals aren’t a specdx problem. They’re the default state of any system whose outputs are trusted unread, and you have to engineer against them everywhere.

Revisions

  1. Published.
  2. Pre-publish: corrected the issue count from eight to nine against the GitHub record, and added the tenth issue — framework-agnostic declared artifacts in 0.4.0-alpha.8, now running in this site's gate.
  3. Wrote the full body; the dogfooding arc converged with specdx 0.4.0-alpha.7.
  4. Created.