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
- Published.
- 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.
- Wrote the full body; the dogfooding arc converged with specdx 0.4.0-alpha.7.
- Created.