by Raja0sama
Architecture diagrams and checkable docs from your codebase — ERD, C4, API and lifecycle — generated from Prisma, OpenAPI or GraphQL. One HTML file, no server.
# Add to your Claude Code skills
git clone https://github.com/Raja0sama/vibexGuides for using cli tools skills like vibex.
See how vibex compares with popular alternatives.
vibex is an open-source cli tools skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by Raja0sama. Architecture diagrams and checkable docs from your codebase — ERD, C4, API and lifecycle — generated from Prisma, OpenAPI or GraphQL. One HTML file, no server. It has 68 GitHub stars.
vibex's catalog security scan is still queued. You can run an instant dependency and prompt-injection check now with the "Scan for vulnerabilities" button above.
Clone the repository with "git clone https://github.com/Raja0sama/vibex" and add it to your Claude Code skills directory (see the Installation section above). vibex ships a SKILL.md manifest, so compatible agents can discover and load it automatically.
vibex is primarily written in JavaScript. It is open-source under Raja0sama on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other CLI Tools skills you can browse and compare side by side. Open the CLI Tools category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh vibex against similar tools.
No comments yet. Be the first to share your thoughts!
Top skills in this category by stars
⚠️ Third-Party Software Notice
This skill is third-party open-source software developed and hosted independently on GitHub. SkillsLLM is an informational directory and does not control or maintain the underlying repository.
Any security checks, ratings, or warnings displayed by SkillsLLM are automated and limited in scope. They do not constitute a security certification or guarantee that the software is safe, error-free, or free from malicious code, vulnerabilities, compromised dependencies, or prompt-injection risks.
Review the source code, permissions, dependencies, and configuration before installing or running any third-party skill. Use is at your own risk. To the maximum extent permitted by applicable law, SkillsLLM is not liable for losses arising from third-party software.
The deep catalog scan for this skill is still queued. Run an instant dependency check now instead.
You vibed it into existence. vibeX shows you what you actually built.
Output is always two files: <name>.<type>.json (the spec, the source of truth) and <name>.<type>.html (self-contained viewer: pan/zoom, search, click-for-details, dark/light, SVG/PNG export). Not fancy. Correct and readable.
Skill root: the directory containing this file. Every command below is node <skill-root>/bin/vibex.mjs ….
Pick the type from the ask. One diagram per type per question; do not mix.
| Ask | Type | Spec file |
|---|---|---|
| tables, models, schema, relations, FKs, data model | erd |
schemas/erd.schema.json |
| context, containers, components, who talks to what, system boundary | c4 |
schemas/c4.schema.json |
| endpoints, routes, API surface, GraphQL operations, events/topics | endpoints |
schemas/endpoints.schema.json |
| statuses, state machine, lifecycle, what happens after X, allowed transitions | lifecycle |
schemas/lifecycle.schema.json |
Find a machine-readable source first. Search the repo before reading code:
endpoints: openapi.*, swagger.*, *.graphql, schema.gql, or a generated schema endpoint dump. Run import openapi or import graphql.erd: schema.prisma → import prisma. A GraphQL SDL can also seed an ERD with import graphql <file> out.json --erd.c4: never importable. Author it (step 3).node bin/vibex.mjs import openapi path/to/openapi.yaml out/api.endpoints.json
node bin/vibex.mjs import graphql path/to/schema.graphql out/api.endpoints.json
node bin/vibex.mjs import prisma prisma/schema.prisma out/db.erd.json
After an import, open the JSON and edit it like a human would: rename groups, drop noise endpoints (health, metrics), add entities links on endpoints, add sources. The import is a starting point, not the deliverable.
No source file? Dig in the code. Read the schema file for your type once, then read one example in examples/. Author the JSON fresh. Where to look:
@Controller('prefix') + @Get/@Post/@Put/@Patch/@Delete('path') → one endpoint each, path = prefix + path with :id rewritten as {id}. @UseGuards(...)/@Roles(...) → auth. @Body() DTO class → request. Return type / @ApiOkResponse → response. One group per controller. Put sources: [{path, line}] on every endpoint pointing at the handler method. Prepend app.setGlobalPrefix() and @Version()/VersioningType.URI segments to every path. @Param() → params[].in: "path", @Query() → "query", @Headers() → "header"; a DTO class in @Query() becomes one param per property.@Query(), @Mutation(), @Subscription() in *.resolver.ts → method QUERY/MUTATION/SUBSCRIPTION, path = name(arg: Type). @ObjectType/@InputType classes → types.router.get('/x', …), app.post(...), fastify.route({method, url}).@Entity('table') classes; @PrimaryGeneratedColumn/@PrimaryColumn → pk; @Column({nullable, unique, type}); @ManyToOne + @JoinColumn → FK on this side, relationship from this entity to target with from_cardinality: many; @OneToOne → one/zero-or-one; @ManyToMany + @JoinTable → a join entity or a many↔many relationship.sources (path + model line); add description and groups by hand. It names entities by the lowercased model name (OrderItem → orderitem; @@map only changes the label).CREATE TABLE, REFERENCES, PRIMARY KEY, UNIQUE.docker-compose.yml, k8s/, serverless.yml, infra/ for containers and datastores; package.json/*.module.ts for tech; HTTP clients, queue clients, SDK imports (stripe, @aws-sdk/client-sqs, nodemailer) for external systems and relationships. People come from auth roles.Keep IDs stable and boring: user, order_item, bff, list-orders. Reuse the same ID for the same thing across diagrams so ERD entity IDs can go into endpoints[].entities. With a Prisma-imported ERD, use exactly the importer's ids (orderitem, not order_item); run dashboard and check the "which endpoints touch which tables" table is non-empty.
Validate, then render.
node bin/vibex.mjs validate out/db.erd.json
node bin/vibex.mjs render out/db.erd.json out/db.erd.html
Exit 1 means errors: fix the named field and rerun. Warnings never block; fix the ones about layout (row/col hints) when they appear, ignore missing-summary warnings unless the user wants prose. Add --open to the render to open the browser.
Several diagrams for one system? Build the dashboard too. Keep all specs in one folder (e.g. docs/arch/), then:
node bin/vibex.mjs dashboard docs/arch/index.html docs/arch --title "My system"
The overview page lists every diagram, shows which endpoints touch which tables (from endpoints[].entities), and resolves C4 link values that name another spec file in the folder (link: "bff.c4.html" → the bff.c4.json panel; both specs must be in the same dashboard invocation, matching is by file name). Re-run it after every spec change; it is cheap.
Report: the two paths, counts (entities/elements/endpoints and relationships), what was imported vs inferred, and anything you left out on purpose. If code reading left ambiguity (cardinality, auth), say so in one line and put it in a cards note with tone: "warning".
docs)A docs spec is not a diagram. Its unit is a claim: one checkable sentence with a source. The build turns claims plus the diagram specs into docs.json — the fact graph humans read in the dashboard and agents read directly.
node bin/vibex.mjs docs docs/arch/system.docs.json docs/arch --repo . --reanchor # pin anchors
node bin/vibex.mjs docs docs/arch/system.docs.json docs/arch --repo . --check # exit 1 on drift
node bin/vibex.mjs docs docs/arch/system.docs.json docs/arch --repo . --md DOCS.md # a file for the repo
Commit docs.lock.json next to the spec. It records the commit each anchor last verified at, so a rebuild only re-reads the files git says have moved — and so a claim can say when it was last true, not just that the build ran. Delete it to force a full check.
Read schemas/docs.schema.json once, then examples/shop.docs.json (a system overview) and examples/auth.docs.json (one topic, in depth). Build the diagrams first: a docs spec documents specs that already exist.
Put the docs spec in the same folder as the diagrams and it becomes a Documentation panel in the dashboard, first in the sidebar:
node bin/vibex.mjs dashboard docs/arch/index.html docs/arch --title "My system" --repo .
Pass --repo or every anchored claim renders as unverifiable.
Write a separate *.docs.json per topic — auth.docs.json, payments.docs.json, onboarding.docs.json — not one document trying to be the whole system. Every docs spec in the folder becomes its own entry under Documentation in the dashboard.
A topic document answers one question a person actually asks. It covers whatever specs it needs to point at, generates little or nothing, and earns its keep through prose and anchored claims. Undocumented nodes in a spec are only reported as gaps when the document derives facts from that spec, so a focused document is not nagged about the forty things it was never about.
Keep one system overview alongside them: it generates the structural facts (elements, entities, operations) and stays shallow. Do not restate a topic document's claims in it.
This is the main way a good document gets written, and the path the hard rules below are built around.
anchored claim and their word became a pointer, not the evidence.asserted claim with their name and today's date, and their reasoning in source.rationale.coverage.out_of_scope with "Not documented yet" and what specifically is unknown. This is the most valuable part of a document written from a conversation, because it is the part nobody remembers to write.Ask in this order and stop at the first yes.
erd.entities, erd.relationships, c4.elements, c4.relationships, c4.boundaries, endpoints.operations, endpoints.types, lifecycle.states, lifecycle.transitions, coverage. Column counts, keys, cardinalities, delete rules, who-calls-what, transitions, guards, operation lists — all derived. Writing them by hand is the most common way to make this feature worthless.anchored, pointing at that file and symbol. This is where most real documentation lives: invariants, ownership of a write path, what a guard actually enforces.asserted — and see the hard rule below.coverage.out_of_scope with a reason.A document nobody reads top to bottom is a database with headings. Give each section a narrative: Markdown that a person reads straight through, citing claims with [[claim-id]].
"narrative": "There is exactly one way in. The BFF is the only container reachable from the public internet [[network.public-entry]], and nothing behind it accepts outside connections [[network.private-isolated]]."
asserted claim. source.by names a human who stands behind it, and source.at is the date they confirmed it. You may only write one when the user told you the fact in this conversation, or it is signed in a file you can cite (an ADR, a CODEOWNERS entry, a README with an author). Otherwise anchor it or omit it. An asserted claim you made up is a human's name on your guess — it is the one failure this whole design exists to prevent.confidence. The validator rejects it. You declare evidence; the build computes trust., and or a semicolon, it is two claims. The validator warns; split rather than rephrase around it. Separate claims can be cited, checked and retracted on their own — a compound one cannot.hash yourself. Write "hash": "000000000000" and run --reanchor. Anchor the smallest region that proves the claim: a method, not a file. A whole-file anchor goes stale on every unrelated edit and trains the reader to ignore the warning.should, probably, might, will be, TODO — those are not claims about the system. No instructions to the reader. Reasons go in source.rationale, not in text.subject (shop.c4#bff, orders.erd#order) unless it is about the whole system. That is what puts it on the node's page and what lets an agent ask "what is known about X". Subjects that do not resolve are reported in coverage.unknown — fix them, do not leave them.coverage.out_of_scope is mandatory in practice. Name every area a reader might expect and not find, with a reason. "Not documented yet" is a valid reason; silence is not. A document that implies completeness gets believed exactly where it is wrong.CI proves a claim's evidence has not moved. It cannot prove the claim was ever true — that was your reading of the code at authoring time. The review is the only moment initial truth is established, and after it passes, arithmetic will defend a wrong claim just as faithfully as a right one.
So hand over a review list, do not just hand over a document. In your report, name:
asserted claim and whose name is on itTell the reviewer to work in this order, which catches the most per minute:
coverage.out_of_scope, before any claim. A thin or generic gap list means the document is silently incomplete, which is more dangerous than any single wrong sentence — and it calibrates how far to trust the rest. "Future work" is not a gap; "whether the domain APIs authenticate each other is unknown" is.asserted claim, one by one. These carry a person's name and nothing checks them. Did that person actually say it? Is the date the day they confirmed it, not the day it was typed? This is where fabrication is easiest and most damaging.Push back on: an asserted claim whose by is a team, a role, or a model rather than a person; a claim that reads as two facts; a gap list that names nothing specific.
Set meta.repository and the rendered pages grow a way to report things: a quiet flag on every claim, one on every node's details panel, and three buttons in the docs toolbar for requests that are not about a single claim.
Each opens a prefilled issue whose body carries a fenced block an agent can read:
intent: doc-problem
document: relay.docs
claim: session.ttl
confidence: verified
spec: relay.c4#bff
commit: 42d3ee6c27fdc16a1933a96ec9e2d7293161d251
Nothing is sent anywhere — the link opens a form the person still has to submit. When you pick one of these issues up, read that block first: it tells you exactly which claim, which node and which tree the reader was looking at, which is usually more precise than the prose above it. If the block is missing or the prose is too vague to act on, reply asking rather than guessing.
When the user asks what a change would look like, set meta.proposed: true and meta.proposal (who, when, which issue, one sentence of why) on every spec you write for it.
A proposal renders with a banner, a purple badge, a stamp burned into the SVG so an exported image still says what it is, and its claims read proposed rather than verified. --check ignores it — there is nothing for it to drift from.
Two rules the validator enforces, because breaking either would make a proposal indistinguishable from the system:
meta.proposal without meta.proposed renders as though it describes something real, and is warned about.Put what the proposer has not worked out in coverage.out_of_scope. On a proposal that section is the most valuable one — it is the difference between a design and a daydream.
Issues labelled intake come from someone reading a diagram or a document. Read the fenced vibex block first: it names the claim, spec or node they were looking at and the commit they saw. That is usually more precise than the prose above it.
.github/workflows/intake.yml triages before you see it, and refuses to guess — an unresolvable claim id or an unnamed diagram gets a question, not a pull request. If you pick one up by hand, hold the same line: reply asking rather than produce a confident wrong change.
Whatever you do, open a pull request and let the drift check run on it. Nothing here merges on its own.
Add this once, in the project being documented. It is the step that makes the rest binding rather than advisory — without it a stale claim is a warning nobody reads.
- uses: actions/checkout@v4
with: { fetch-depth: 0 } # a shallow clone cannot diff against the lock's commit
- run: npx @vibex/vibex docs docs/arch/system.docs.json docs/arch --repo . --check --no-lock
--no-lock on purpose: the lock file is an optimisation for local iteration, and it reaches CI from a contributor's machine asserting that claims were verified. CI re-reads every anchor from scratch rather than taking that on trust. A full check of ~60 claims runs in well under a second, with no dependencies, no network and no model — so there is no reason to skip it.
--check reports driftstale — the anchored code changed. Re-read the code and decide: still true → --reanchor; no longer true → rewrite the claim, or delete it and add a replacement with supersedes pointing at the old id. Never --reanchor without reading; it silently re-certifies a claim that may now be false.broken — the file or symbol is gone, or --repo was not given. Re-anchor to where the code moved, or drop the claim.expired — nobody has confirmed the assertion inside review_window_days. Ask the user; do not refresh the date yourself.A claim that did not verify is re-read on every build until it does, so a problem cannot go quiet just because nobody touched the file again.
--md writes the document as a Markdown file containing no raw HTML, so it survives a paste into Confluence, a wiki, or a README unchanged. Every claim appears with its confidence marker and source; the panel can hide evidence behind a disclosure, a file that travels cannot.
node bin/vibex.mjs docs docs/arch/auth.docs.json docs/arch --repo . --md docs/AUTH.md
Regenerate and re-paste rather than editing the Markdown: it is an output, and an edit there is lost on the next build and invisible to --check. If the user maintains docs in Confluence, the spec and its lock file are what lives in the repository and gets reviewed; the Confluence page is a copy that is republished, the same way the HTML is.
Give the counts line verbatim (N claims — X verified, …), every coverage.unknown entry, and which claims you anchored versus which the user asserted. If you left something undocumented, say so — it should already be in out_of_scope. When the document came out of a conversation, list what you attributed to them by name, so they can correct it before it hardens into documentation.
changelog)node bin/vibex.mjs changelog v1.2.0..main --specs docs/arch --md RELEASE.md -o changelog.json
Reads the commits in a range and writes two things: a JSON artefact to keep in the repository, and Markdown with no raw HTML so it survives a paste into release notes or a wiki.
--specs <dir> is what makes it worth running here. It builds the fact graph at both ends of the range and reports what the release did to the documentation: claims written, reworded, superseded, removed. Derived facts are counted, never listed.internal when every path it touched was.Write changelog.json next to the specs and vibex dashboard picks it up as a Changes panel, where each entry's specs are chips that open the diagram they touched:
node bin/vibex.mjs changelog v1.2.0..main --specs docs/arch -o docs/arch/changelog.json
node bin/vibex.mjs dashboard docs/arch/index.html docs/arch --repo .
Report the counts line as it prints, and if claims were removed, say so out loud — a claim that vanished took whatever it documented with it, and that is worth a human checking.
groups, C4 level, or endpoint group, and link with link (C4) or a card.from is the FK/child side, to is the referenced/parent side. Default many → one. Set to_cardinality: zero-or-one when the FK column is nullable. Unique FK → from_cardinality: one or zero-or-one. Use groups to frame bounded contexts; the renderer lays each group out as its own block.description. Every relationship gets a verb label; add technology when it is not obvious. external: true for anything the team does not own. One meta.level per diagram; drill down with link to another rendered HTML.subject per diagram (Order.status, not the whole system). State ids = the enum values in code. Exactly one kind: initial; every state either reaches a terminal or is explicitly failure. Each transition names event and actor; put conditions in guard (no brackets) and side effects in action. kind: auto|timeout for system-driven moves, failure for error paths. Find them in code: status enums, switch (status) / state-machine tables, guards like assertTransition(from, to), service methods that set status =.{param}. GraphQL path is the signature name(arg: Type!). Use EVENT for topics, queues, webhooks the service publishes. Put ERD entity IDs in entities so the reader can jump from an endpoint to the tables it touches. Do not paste full descriptions into summary; one line.meta.repository: {"url": "https://github.com/org/repo", "revision": "main"} (web root, no .git) plus sources: [{"path": "src/orders/orders.controller.ts", "line": 42}] makes the viewer link to <url>/blob/<revision>/<path>#L<line>. Works for GitHub and GitLab; omit revision to link HEAD. Fill them whenever the diagram came from code.row/col when a layout warning asks for it or the user complains.validate <spec.json> [--json]
render <spec.json> [out.html] [--linked] [--open] [--json]
import openapi <file.json|yaml> [out.json] [--title T] [--all-types]
import graphql <schema.graphql> [out.json] [--title T] [--erd]
import prisma <schema.prisma> [out.json] [--title T]
dashboard <out.html> <spec.json|dir>... [--title T] [--subtitle S] [--linked] [--open] [--json]
one HTML: sidebar of all diagrams, overview tiles, entity↔endpoint cross-links
docs <docs.json> <spec.json|dir>... [-o docs.json] [--md doc.md] [--repo dir]
[--check] [--reanchor] [--lock docs.lock.json] [--no-lock] [--json]
fact graph: derived facts + authored claims, each with provenance
and a computed confidence. --check exits 1 on stale/broken/expired.
--md also writes the document as Markdown. Re-reads only the anchors
git says moved since the commit in the lock file.
demo [dir] [--linked] render examples/ into dir (+ dashboard.html)
--linked write the HTML as a placeholder page: data in <name>.data.js, viewer in
vibex-viewer.js/.css beside it. Opens from disk; keep the files together.
outdated [dir] which generated files this version would now render differently.
Exits 1 if any is stale, so CI can gate on it.
types list types with schema and example paths
Never answer from a generated file without checking it first. Run
vibex outdated <dir>. If it reports anything stale, regenerate from the spec and
read the new file — a stale artifact and a broken feature look identical to
whoever opens one, and reasoning from the wrong one wastes everybody's time.
Regenerate; never hand-edit generated HTML to bring it up to date. The file is derived from the spec the same way a binary is derived from source, and a hand-patched artifact is one no version of this tool would ever have produced. If the spec is what is wrong, fix the spec and re-render.
YAML OpenAPI needs the optional yaml package: run npm install inside the skill root once (already present if the skill came from a global npm i -g @vibex/vibex), or convert the file to JSON.
Click a node for details (columns, fields, params, sources, relationships). / searches, Esc clears, t toggles theme, 0 fits, +/- zoom. Dragging a box moves it (a C4 boundary or an endpoint card carries what is inside it) and re-routes its lines; the layout lives in memory until reload, r or Reset layout puts it back. #node=<id> in the URL deep-links to a node. SVG and PNG export buttons produce standalone files in the current theme.
vibeX reads the thing that can't lie — your Prisma schema, your OpenAPI document, your GraphQL SDL, or the source itself — and renders the diagram that's actually true. Then it documents the system in claims that fail CI when the code moves underneath them.
One standalone HTML file. No server. Nothing leaves your network.
→ one command, run against the schema that actually ships.
cd ~ && npx skills add Raja0sama/vibex # install the skill, then just ask
npx @vibex/vibex demo out # or look first — installs nothing
vibe — how code gets written now. Fast, AI-assisted, more of it than anyone can hold in their head. X — the crossings. Which table joins which. Which service calls which. What happens after approval.
You vibed it into existence. vibeX shows you what you actually built.
The mark is the same idea: two edges crossing. Everything interesting in a system is a line between two things, not the things themselves.
| Type | What it draws | Import from | |
|---|---|---|---|
| 🔵 | erd |
tables, columns, PK/FK badges, crow's-foot cardinality that knows nullable from not, bounded contexts as groups | Prisma, GraphQL SDL (--erd) |
| 🟣 | c4 |
persons, systems, containers, components, databases, queues, each with its own fill, inside tinted nested boundaries | hand-authored |
| 🟢 | endpoints |
REST routes, GraphQL operations, published events — grouped by resource, with method badges, auth, params, status codes | OpenAPI 2/3, GraphQL SDL |
| 🟡 | lifecycle |
every state a thing can reach and every legal move between them, with actor, event, guard and side effect on each arrow | hand-authored |
Each is a validated JSON spec plus a rendered viewer. The spec is the artifact you keep; the HTML is disposable. Documentation and changes are two more views over the same specs — six panels in all, in one dashboard file.
# 1. import — your schema becomes a draft spec
vibex import prisma prisma/schema.prisma docs/db.erd.json
vibex import openapi openapi.yaml docs/api.endpoints.json
vibex import graphql schema.graphql docs/api.endpoints.json
# 2. validate — dangling refs and unreachable states are errors that name the field
vibex validate docs/db.erd.json
# 3. render — one file, CSS and JS inlined, spec embedded
vibex render docs/db.erd.json --open
# or --linked: the HTML is only a placeholder; data goes in db.erd.data.js
# and the viewer in vibex-viewer.js/.css beside it (still opens from disk)
# every spec in the folder, one page, cross-linked
vibex dashboard docs/index.html docs --title "Payments platform" --repo .
No source schema? It reads the code — NestJS controllers, GraphQL resolvers, TypeORM entities, Express routers, SQL migrations — and writes the spec itself.
vibeX is a Claude Code / Cursor skill first and a CLI second. Installed as a skill, the
whole command surface collapses into a sentence — Claude reads SKILL.md, finds your
schema, picks the diagram type, writes the spec and renders it:
> show me the data model
wrote db.erd.json · db.erd.html
> now the endpoints
wrote api.endpoints.json · api.endpoints.html
> what happens after a request is approved?
wrote request.lifecycle.json · request.lifecycle.html
> document the auth service and fail CI when it drifts
wrote auth.docs.json · 41 claims, 38 verified
Install it. One command, from your home directory:
cd ~ && npx skills add Raja0sama/vibex
The directory matters, and it is the thing people get wrong: skills add installs
relative to where you run it. From ~ the skill is available in every project. From a
project directory it travels with that repository and nowhere else — which is what you
want when the skill should be checked in alongside the code.
Check it landed:
node ~/.claude/skills/vibex/bin/vibex.mjs types
Then just ask. Claude picks the skill up from the folder name, and the CLI underneath is what the skill drives — it is there when you want it, not something you have to learn first.
Per-project, so the skill is checked in beside the code it documents:
cd my-project && npx skills add Raja0sama/vibex
Pinned to a published version, if you would rather have a release than a clone of
main. SKILL.md ships inside the npm package, so a global install plus one symlink
does it:
npm i -g @vibex/vibex
ln -s "$(npm root -g)/@vibex/vibex" ~/.claude/skills/vibex
Under nvm,
npm root -gis scoped to the Node version you are on (~/.nvm/versions/node/v22.18.0/...). Install a new Node and both thevibexbinary and this symlink stop resolving, silently.skills addhas no such problem.
From source, if you are changing vibeX itself — the symlink tracks your working copy, so edits apply the moment you save:
git clone https://github.com/Raja0sama/vibex && cd vibex
ln -s "$(pwd)" ~/.claude/skills/vibex
skills add clones the repository rather than the npm package, and it has no
node_modules — run npm install inside the skill folder if you need YAML OpenAPI.
The npm paths above already have it.
An AI wrote the sentence once. Arithmetic checks it forever.
A diagram can't drift from the schema, because it's generated from it. Prose can, and
always does. So vibex docs documents a system the same way it draws one — the unit
isn't a page, it's a claim: one sentence with a source attached.
vibex docs system.docs.json specs/ --repo . --check # exit 1 on any claim that no longer holds
There is no model in the verification path — only fs, path, crypto and git.
That is the whole point, and it is why a full drift check costs ~70ms and nothing per run.
It catches drift, not initial error. If the first draft misreads the code, the hash still matches, CI stays green, and a wrong claim can stay verified indefinitely. Reviewing the spec once, at authoring time, is the only thing that establishes truth.
The honest version: you get a reviewable first draft in one pass, and after you have read it once, arithmetic keeps it honest.
| Source | What holds it up |
|---|---|
derived |
Computed from a diagram spec by one of ten fixed generators. Cannot disagree with the diagram beside it, because it is the diagram. |
anchored |
Prose pinned to a file and a symbol by a content hash. In languages where whitespace is not syntax (TypeScript, JSON, Go, …) the hash ignores it — reformat the file and nothing moves; change the line and the claim flags itself. Everywhere else (Python, YAML, Makefiles, any unknown extension) indentation counts, and only trailing whitespace and line endings are ignored. |
asserted |
A person's decision, with their name and the date. For what no file can prove — and it expires, so "we decided this in March" can't pass for fact forever. |
Confidence is computed, never written. A claim cannot declare how trustworthy it is. The build assigns one of five states from the evidence and the clock, and the validator rejects any claim that tries to rate itself:
verified · stated · needs re-reading · out of date · unverifiable
vibex docs system.docs.json specs/ --repo . --reanchor # re-pin hashes after a deliberate edit
vibex docs system.docs.json specs/ --repo . --md doc.md # portable Markdown, zero raw HTML
Incremental, keyed on git. docs.lock.json records the commit each claim last
verified at, so a rebuild re-reads only what git says moved — and every uncertainty
resolves toward reading more, never less. CI passes --no-lock: a lock file arrives
from a contributor's machine asserting that claims were verified, and CI re-reads every
anchor rather than taking that on trust.
It tells you what it doesn't cover. Every document renders three lists: what was read, what is deliberately out of scope and why, and what the build noticed but could not account for. A document that implies completeness gets believed exactly where it is wrong.
One artefact, two readers. People read the dashboard panel; agents read docs.json —
every fact once, addressable, with resolved edges, so "what breaks if I change this" is a
single lookup rather than a search. Narrative sections carry Markdown that cites claims
inline with [[claim-id]], rendering each citation as a coloured pip showing that claim's
confidence, so a reader sees which words are load-bearing.
One document per question. auth.docs.json, payments.docs.json — each becomes its
own dashboard en