Cohere
A coherence layer for Elixir/Phoenix projects.
Docs live at mhyrr.github.io/cohere: the ladder, the loop, the document kinds, and the FAQ, with an llms.txt for agents.
A project is coherent to the degree that, at any point in its development, it can furnish any actor — human or model — with the minimal context sufficient to act in line with the user's intent, and can mechanically detect when the project has drifted from that intent.
Models are intelligent but context-starved, and the failure mode of context starvation is incoherence: sessions rediscover the same facts expensively, and codebases drift as hundreds of locally-reasonable changes accumulate with no shared frame. Cohere makes coherence a measurable property of the project instead of a hoped-for behavior of the agent.
It does this with three document kinds in one directory, each checked as hard as its nature allows:
- The map — the actual shape of your system, derived from the compiled application: contexts and their public API, Ecto schemas with real types and associations, Phoenix routes (LiveView unwrapped), Oban workers with queue and cron wiring. Never hand-edited, regenerated on demand — therefore it cannot lie.
- Intent cards — one small authored file per context holding only what cannot be derived: purpose, invariants, decisions with their rejected alternatives, non-goals. Each card is hash-bound to its context's public surface. Living constraints, kept fresh by the check gate.
- Design docs — one authored file per design: problem, existing ground, shape, promised surface, decisions. Drafts are work in flight; accepted designs are immutable, dated history. The design conversation you'd have anyway, landing in one format the tooling reads — instead of a hairy pile of wiki pages and gists no gate keeps honest.
No LLM calls. Zero runtime dependencies. Everything is deterministic and CI-runnable; models consume the outputs but are never required to produce them.
Quickstart
# mix.exs
{:cohere, "~> 0.1", only: [:dev, :test]}
$ mix cohere.init # scaffold cohere/, derive the first map
$ mix cohere # where does this project stand?
Coherence ladder — my_app
✓ L1 static guidance — AGENTS.md
✓ L2 derived map — map present and fresh
✗ L3 authored intent, checked — no intent cards
~ L4 governed verbs / runtime verification — tidewave installed; write paths not compiler-governed (boundary)
✓ L5 delivered context — work packets available via `mix cohere.packet`
Then, incrementally:
$ mix cohere.gen.intent deals # card skeleton, born bound to the current surface
$ mix cohere.check # CI gate: exit 1 on any drift
$ mix cohere.packet deals billing # assemble delivered context for a task
$ mix cohere.packet --diff # …or for exactly the contexts this branch touches
The feature loop
The developer surface is three verbs; everything else is plumbing their output points at.
$ mix cohere.design deal-reversals --contexts deals # START
... design in the doc, against its Existing ground ...
$ mix cohere.check # CHECK — anytime; fix, repeat
... build ...
$ mix cohere.check # same command, new findings
$ mix cohere.complete deal-reversals # COMPLETE — when check is quiet
Start scaffolds cohere/design/deal-reversals.md (status: draft) and
assembles its Existing ground: each anchored context's current API from
the map, plus the invariants and decisions from its intent card. A no-LLM
tool can't judge that your design contradicts INV-DEA-002 — it delivers
INV-DEA-002 onto the page where you're designing, so the contradiction
gets seen. The tool assembles; the mind judges.
Check is one iterative command, identical locally and in CI. Hard
findings exit 1: stale map, drifted cards, dead card references. Design
findings are advisories, never failures — an accepted design is a dated
record, and drift on history is information, not a bug. A drifted card
means: re-read it against the new surface, update what your change
invalidated, then mix cohere.check --accept deals. Accepted drift is
documented drift; the failure mode this tool exists to kill is the
silent kind.
Complete is the land step, one command. It regenerates the map,
requires the check hard-clean (which forces the card re-review where the
design's durable decisions get distilled into cards), then verifies every
backticked ref in the design's Promised surface exists in the compiled
app. A design that promised reverse_deal/1 cannot complete until
reverse_deal/1 exists — the design doc is a mechanically checkable
spec, not aspirational prose. On success: draft → accepted, dated,
immutable. New thinking later? A new design with supersedes: in its
frontmatter — history is superseded, never rewritten.
The PR that lands a feature carries the map delta (the ontology change), the card delta (the intent change), the accepted design (the why), and the code — reviewable together.
What the map looks like
Derived from a real ~185-module Phoenix app, entries read like the language of the business, because they are the business — reflected out of the compiled code:
### MyApp.Deals — domain `[surface:df8be63a83b8]`
**API** (32): approve_deal/1 create_deal/1 extract_deal_data/1 get_financial_summary/1 …
**Schemas:** Deal, DealParty, DealPartyFeeComponent
**Support:** AccountKeys, DealReset, EditTracker, Insights, JournalCalculator
### MyApp.Deals.Deal → `deals`
- fields: …, side:enum(listing|buyer|both|lease|referral),
status:enum(draft|needs_review|approved|posting|posted|posting_failed|reversed), …
- belongs_to reviewed_by → MyApp.Users.User via reviewed_by_user_id
- belongs_to agency → MyApp.Agencies.Agency via agency_id
Enum vocabularies, custom foreign keys, queue/cron wiring from config — the facts agents otherwise rediscover by grepping, delivered in one git-tracked file whose PR diff is the ontology change.
What check finds
$ mix cohere.check
✗ cohere/intent/deals.md
surface drifted: +approve_deal/1
→ re-review the card, then `mix cohere.check --accept deals`
⚠ cohere/design/deal-reversals.md — draft, advisory only
anchor "Reversals" not in the map — fine if this design introduces it;
`mix cohere.complete` verifies it lands
drift detected
$ mix cohere.check --accept deals
cohere/intent/deals.md — rebound to surface df8be63a83b8, drift annotated
The coherence ladder
| Level | Property | Mechanism |
|---|---|---|
| 0 | Context by discovery | raw repo; agents grep and hope |
| 1 | Static guidance | AGENTS.md / usage_rules |
| 2 | Derived truth | the map (mix cohere.map) |
| 3 | Authored intent, checked | intent cards + drift sentinel |
| 4 | Governed verbs, verified behavior | boundary enforcement, Tidewave runtime introspection |
| 5 | Delivered context | work packets (mix cohere.packet) |
Each level is useful alone; none requires the previous. Phoenix 1.8 ships every new project at level 1. Cohere is levels 2–5, adopted incrementally.
Cohere probes for what's present rather than requiring anything:
Ecto present → objects and links appear in the map; Oban present → the job
surface appears; boundary present → level 4 lights up; Tidewave present →
work packets direct agents to verify behavior in the running app
(project_eval, execute_sql_query) instead of inferring it from source.
Mix tasks
| Task | Does |
|---|---|
mix cohere | ladder status + designs in flight |
mix cohere.init | scaffold cohere/, first map, workflow README |
mix cohere.design [<slug>] | no args: list designs + statuses; with a slug: start a design (--contexts a,b) |
mix cohere.check | the iterative check + CI gate; exit 1 on hard drift (--accept <card> to rebind) |
mix cohere.complete <slug> | verify the design's promises landed, flip it to accepted |
mix cohere.map | regenerate the derived map |
mix cohere.gen.intent <ctx> | intent card skeleton (--all, --force) |
mix cohere.packet <ctx…> | assemble a work packet; --diff for the contexts this branch touches (--base REF, --out FILE) |
Configuration
Everything is optional, via config :cohere, ...:
config :cohere,
dir: "cohere", # artifact directory
namespace: MyApp, # default: camelized app name
web_namespace: MyAppWeb, # default: <Namespace>Web when present
ignore: [MyApp.DevHelpers] # modules to exclude from derivation
Design constraints
- Derived or checked, nothing else. Every artifact gets the strongest binding its nature allows: the map is regenerated from code (cannot drift), cards are authored-but-hash-bound (drift fails the build), and designs are authored-but-dated — anchored to the map, promise-verified at completion, advisory ever after. Unbound prose is future lies.
- Zero runtime dependencies. Ecto/Phoenix appear only as test deps for fixture reflection. Consumers inherit nothing.
- No LLM calls. Deterministic, CI-runnable, same bytes for same code.
- Link, don't restate. Packets carry source records and pointers; they never paraphrase code into a second truth.
The full research and design rationale — what Palantir's Ontology and 8090's Software Factory are actually selling, why the failure modes of spec-driven development all point the same direction, and why Elixir is the cheapest stack to build this on — lives in cohere/design/design-spec.md, an accepted design doc in cohere's own coherence layer. The feature loop's own design is cohere/design/feature-loop.md — completed by the tool it specifies.
License
MIT