Derived, Not Written
A project index is a list that starts lying within a month. 〇.day publishes one that is a derivation of its sources, grades its own confidence, and proves its gate isn't vacuous.
Every project ecosystem eventually grows a page listing what it contains. Ours is 〇.day — a punycode domain you cannot say out loud, which is its own small joke, and which this site has been quietly linking to without ever explaining.
The interesting thing about that page is not the design. It’s that almost every page of its kind is a lie with a slow fuse.
The index is a second copy
A hand-written project index is a set of claims about facts that live somewhere else. This depends on that. This is a Rust crate. This one talks to that one over a wire protocol. Every one of those is already recorded — in a Cargo.toml, a go.mod, a package.json, a server.json, a Taskfile — and the index restates them from memory.
So it is a cache with no invalidation. A dependency gets bumped and the page still names the old version. A project stops being used and the page still lists the edge. Nobody notices, because nothing checks, and the page keeps rendering exactly as confidently as it did when it was true.
The fix is the same fix as everywhere else on this site: stop writing down the fact, and derive it from the thing that already holds it.
The graph is a derivation, and a gate says so
deps:derive reads a committed lock of what each repository actually declares and folds it into graph.json. deps:check fails when the graph is not the derivation of that lock. The published document names its own generator, in the file, above the data:
Generated by
task deps:derive. Do not edit by hand —task deps:checkfails when this file is not the derivation of its sources.
Two details make that more than decoration.
The check is pure and offline. It needs no sibling checkouts and no network, so it runs identically on a laptop and in CI. Freshness — has an upstream repository moved since the lock was taken — is a separate task on a schedule, because it needs the ecosystem present and a check that needs the world is a check that fails for reasons unrelated to correctness.
And CI invokes the task; it does not re-implement the command. That is what makes “passes locally” and “passes CI” the same sentence instead of two implementations that agree until one of them is edited.
It declares which of its own claims it can support
Before any data, the document draws a line through its own fields:
authored — Statements a maintainer makes. Do not infer, override, or second-guess them from activity signals such as stars, commit recency, or release cadence.
derived — Read from a machine-readable source. Each carries the file it was read from and how confidently its target resolved.
Status, purpose, and visibility are authored. Nobody derives them, because they are not derivable: a quiet repository might be finished, abandoned, deliberately private, or waiting on something upstream, and nothing in a commit log distinguishes those. Dependencies are derived, and each one names the repository, the file path, and the format it was read out of.
The instruction against activity signals is the part worth stealing. It is a rule against inferring a maintainer’s stated position from their behavioral exhaust — cheap to read, and usually wrong.
Confidence is a field, not an assumption
An edge is not simply present. It resolved by a particular method, and the document publishes what each method is worth:
- exact — a Go module path is a repository coordinate by the language spec. A git URL is the coordinate. An OCI identifier matched in full. Seven more of these.
- by-name — an image reference stripped to its bare name, an executable’s basename, the tail segment of an MCP server name. Probably right. Not proven.
- convention — a namespaced metadata key whose owner is identified by a constant in the tool itself.
Seventeen methods, each with a stated basis. Twenty-seven edges, of which nine are kept weak — neither promoted to certainty because the graph would look tidier, nor dropped because a weak edge is untidy. A tenancy edge that resolved because a project declares itself mountable under another’s contract is real information and weaker than a compiled-in dependency, and the document says which it is.
There is also a list of edge kinds the schema has reserved and does not yet emit — schema, lineage — named so the absence reads as a decision, not an oversight.
The gate is proven not to be vacuous
This is the part almost nobody ships, and the part that decides whether any of the rest is worth believing.
A passing check proves only that its implemented predicate accepted this run — not that the predicate is the property you meant to protect. The selftest’s own header puts it less politely:
A check that passes today proves nothing. It could pass because the graph is correct, or because it compares nothing to nothing.
So deps:selftest takes the real committed pair, breaks it in each way it could realistically break, runs the real gate over each broken copy, and requires every one to be rejected — with the expected reason, so a mutation caught for an unrelated reason doesn’t count as caught. Two invariants through two doors: divergence, where the graph stops being the lock’s derivation; and privacy, where an edge touching a non-public repository carries detail it shouldn’t.
The privacy one is instructive, because derive already redacts and no lock can produce a violating graph. The invariant exists to catch a hand-edited pair — the failure mode that only appears when someone bypasses the generator, which is exactly the failure mode a generator-shaped defense doesn’t see.
And the wrong graphs are generated from the real one rather than committed as fixtures, for a reason the file states: a committed fixture is a second copy of the graph, and it would rot exactly the way the hand-authored index this project replaces would have. The mechanism refuses to reintroduce its own disease in the test suite.
Loudness costs something
I argued recently that a harness does not fail loudly by nature — that loud failure is engineered, and the essay’s own opening bug was a green property test sitting over a defect for its entire life.
This is that argument with the invoice attached. deps:check is the harness. deps:selftest is what it cost to know the harness works: a whole second program whose only job is to break things and confirm the alarm sounds for the right reason. Nobody makes you write it, the graph looks identical either way, and without it “the graph is checked” is a claim resting on the same evidence as “the tests are green.”
The same instinct shows up in the deployment config, in the form of a refusal. Its Worker serves static assets and has no script, so the observability block that would log invocations was deleted with a note explaining that there is nothing to invoke and enabling it would declare something that cannot take effect. Real-user data comes from a client-side beacon instead. Declining to claim a capability you don’t have is the cheapest honesty available and it is still unusual.
What it is for
The site’s own framing: 〇.day locates the system, q-q.dev explains it, and the project domains are instruments while GitHub stays the source of record. A map, not a pitch.
What makes it worth a post is that it is the first thing in this stack whose audience is partly machines and whose honesty is therefore structural, not editorial. It publishes graph.json with a schema, carries its evidence per edge, grades its confidence, and states which of its claims are testimony and which are derivation. An agent reading it does not have to trust the page; it can read the resolution method and decide what the edge is worth.
Which is the same trade as every other layer here — structure over bytes, a hash over an authority, a predicate over an opinion — arriving at the least likely place for it. Even the index page gets to be checkable.
The graph is public: xn–w6j.day/graph.json. The repository isn’t, yet — which means for now this post is testimony about a derivation, and you can check the artifact but not the generator. I would rather name that asymmetry than gloss it.