The decision is in a thread, the rationale is in a doc, and a PR is quietly contradicting both.
The decision was made in a thread. Somebody summarised it in a document. The document is now eleven months old, three of its claims have quietly stopped being true, and the pull request in front of you contradicts one of them without anybody involved knowing the constraint existed.
None of that is a discipline problem. It is a storage problem: the decision, the constraint it was made under, the work that upholds it and the document that describes it are four different systems, so nothing can notice when they stop agreeing. Here they are four kinds of node in one graph, and noticing is a query.
Every step is one move, on three surfaces: the shell, a chat client over MCP, and the browser consolevrek-api serves. Pick whichever you would actually touch — the choice holds for the rest of the track.
Where a surface genuinely cannot do a step, it says so rather than pretending.
01
Raise the question with what each answer costs
An option is choice|consequence, written when the context is in someone's head rather than reconstructed from a thread later. --guided-by names the principles the answer must not contradict without an argument, and --serves names the goal it would advance. The question blocks the work it is about, so it is answered rather than outlived.
On the command line
the fork, recorded as a fork
$ vrek ask "Postgres or SQLite for the shared server?" \$ --option "postgres|a service to run from day one" \$ --option "sqlite|single binary, one writer, no ops" \$ --guided-by pri-4kd92mv --serves gol-9wbe2ch \$ --blocks iss-3bg971s --owner mike --revisit 2026-10-01
In Claude, or any client that speaks MCP
an agent that reads the constraints firstvrek over MCP
One of the eleven bears on this directly, and it is weighted 0.9. Postgres contradicts it; SQLite keeps it and costs you a single writer. That is a trade you make, not one I make quietly inside a plan.
⌁vrek_asktool call
question: "Postgres or SQLite for the shared server?"
options: [{ option: "postgres", consequence: "a service to run from day one" },
{ option: "sqlite", consequence: "single binary, one writer, no ops" }]
guided_by: ["pri-4kd92mv"]
serves: ["gol-9wbe2ch"]
blocks: ["iss-3bg971s"]
owner: "mike"
revisit: "2026-10-01"
Raising the question belongs to whoever was about to guess, and that is the shell or the agent. What the console adds is the check you would want before answering: the question's page carries its guided by and serves edges as links, so the principle the answer must not contradict is one click from the answer, not in a thread somebody would have to remember.
02
Attach the decision to the question it answers
A decision is not a paragraph in a folder. It answers a specific question, it can supersede an earlier decision, and work upholds it. show walks all of that: what this replaced, what replaced it, and what is currently standing on it — which is the thing you actually want when someone asks why it is like this.
On the command line
the trail, both directions
$ vrek decide "SQLite in-process; a shared server is a later, separate problem" \$ --answers que-8k3m1qp$ vrek show dec-3s57zg1supersedes ▪ The public site is hand-written HTML with no build stepupheld by ◐ Build and deploy the vrek site on Cloudflare Workers
In Claude, or any client that speaks MCP
why it is like this, asked six months latervrek over MCP
you
Why is this SQLite? Somebody is about to argue for Postgres again.
⌁vrek_showtool call
id: "dec-3s57zg1"
{ "kind": "decision", "title": "SQLite in-process; a shared server is a later, separate problem",
"edges": [
{ "kind": "answers", "other": { "id": "que-8k3m1qp", "title": "Postgres or SQLite…" } },
{ "kind": "supersedes", "other": { "id": "dec-vck585y", "title": "The UI is a live console…" } },
{ "kind": "upholds", "direction": "in", "other": { "id": "iss-3bg971s" } } ] }
claude
It answers que-8k3m1qp, which was asked under pri-4kd92mv, and one open issue is standing on it. Nothing has superseded it. If the argument for Postgres is new information, the move is a finding and a new decision that supersedes this one — not an edit, because then the issue standing on it would be standing on nothing.
In the console
both directions, from the logvrek-api
vrek console#/node/dec-3s57zg1live
Connections
every edge kind, both directions
→answersque-8k3m1qp · Postgres or SQLite for the shared server?
→supersedesdec-vck585y · The UI is a live console
←upheld byiss-3bg971s · Build the shared server
03
State the constraint once, and weight it
A principle is a standing constraint the workspace has already decided — not advice, and not a style guide. It guides the nodes it is linked to, it carries a weight so that two principles in conflict are not automatically equal, and an agent reads the principles in force before it proposes a design. Contradicting one needs an argument rather than an oversight.
On the command line
constraints in force
$ vrek principle "Runtime code depends on nothing outside Node stdlib" \$ --guides iss-3bg971s,dec-3s57zg1pri-4kd92mv# unset is not zero: a principle with no weight is not weightless$ vrek weight pri-4kd92mv 0.9$ vrek principles
In Claude, or any client that speaks MCP
what the agent is reading before it designs anythingvrek over MCP
you
Write down that runtime code depends on nothing outside Node's standard library, and link it to the shared-server work.
Recorded and weighted. This is the list I read at the start of a design task — so the next agent on this workspace, in a session neither of us is in, is constrained by it without anybody remembering to paste it into a prompt.
▪The API is the product: every surface is a client of itweight 0.8 · guides 14
New principle
04
Ask what is pulling against what
tensions is the query that has nowhere to live in a wiki: work that serves a goal while breaching a principle. It does not resolve anything for you — the trade may well be the right one — it just refuses to let the trade happen without anyone seeing it. And a violation can always be recorded: violation is never refused, because a system that cannot record being broken teaches people to stop telling it.
Recorded — this call is never refused, on purpose. The work still serves the goal; it now also breaches a 0.9 principle, and both facts are in the same row rather than one being in the log and the other in somebody's memory of the meeting.
In the console
a trade, arguable rather than discovered latervrek-api
vrek console#/tensionslive
Tensions
1
◐Build the shared server on Postgresserves: An engagement's reasoning survives · works against: Runtime code depends on nothing outside Node stdlib
Violations
05
Write documents that cannot go stale
A document here is a list of blocks, and a block can be a node or a query rather than text. It resolves when it is read, so the section listing open work against a component is right at the moment you look at it. What is left — the prose you wrote — is what you vouch for with ack, and drift tells you which of those documents something has moved under since.
On the command line
a view over facts, not a copy of them
$ vrek doc "Storage architecture"$ vrek block doc-k2m9x1p --text "SQLite in-process, one writer. See dec-3s57zg1."$ vrek block doc-k2m9x1p --node dec-3s57zg1$ vrek block doc-k2m9x1p --query kind=issue,status=open$ vrek render doc-k2m9x1p # resolves against now$ vrek ack doc-k2m9x1p # I vouch for this, as of this moment$ vrek drift # what has moved under one since
In Claude, or any client that speaks MCP
context an agent reads, rather than a page it copiesvrek over MCP
Two of the three blocks resolved just now, so they cannot be out of date — only the sentence I wrote can be wrong. It has never been acknowledged, which is what drift reports until a human vouches for it.
In the console
what its blocks resolve to as you look at itvrek-api
vrek console#/node/doc-k2m9x1plive
Resolved now
3 blocks
¶SQLite in-process, one writer. See dec-3s57zg1.text · the only part that can go out of date
▪SQLite in-process; a shared server is a later, separate problemnode · recorded · resolved on load
?kind=issue, status=openquery · 7 items · re-run every read
!Something has moved under it since it was last acknowledged.and Drift lists every document in that state, with the covered node that moved
06
Draw the system, then check the plan adds up
Components are things that exist, with what they depend on and how often they fail on their own account. A flow is an ordered set of calls over them, each with a latency budget, a per-call failure rate and a call count. flow-budget composes those and tells you where the plan does not add up — before it is built, which is the only time that answer is cheap.
No. Three sequential 120ms queries plus 15ms of its own work is 375ms against a 200ms budget. The cheap answers are a smaller count, a smaller per-query budget, or a bigger end-to-end one — but the plan as stated does not add up, and that is now recorded rather than discovered in staging.
◇Diagramcopy the mermaid source, or download .mmd, SVG or PNG
Alternate flows
← → to move
A finding is not an opinion
A fact from outside — what a benchmark showed, what a vendor's limit is — is vrek finding with its sources and the date it was true. One with no source is refused. One that stops being true is superseded rather than edited, so the decision that rested on it still shows what it rested on.
Software is the first domain, not the only one
Nothing in the graph knows it is describing code. Requirements gathering and analysis run the same loop, and what an agent is told on connect belongs to the workspace rather than to a file in a repository.