A coherence layer for Elixir/Phoenix projects.

Docs live at mhyrr.github.io/cohere.

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 framing. Cohere makes coherence a measurable property of the project instead of a hoped-for behavior of the agent.

It does this with three document types in one directory:

  • 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. This is regenerated on demand.
  • Intent cards. One small authored file per context holding what cannot be derived: purpose, invariants, decisions with their rejected alternatives, etc. Each card is hash-bound to its context's public surface.
  • 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. These are the design conversations you'd have anyway, in one format agents read too.

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 my-design --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 my-design                  # COMPLETE: when check is quiet

Start scaffolds cohere/design/my-design.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 does something better suited to a deterministic tool: it delivers INV-DEA-002 onto the page where you're designing, where the contradiction is hard to miss.

Check is one iterative command, identical locally and in CI. Hard findings exit 1: stale map, drifted cards, dead card references. Design findings are advisory and surfaced to the human: 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 context-name. 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 is a new design naming the old one in its supersedes: frontmatter; history is superseded, not 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, all reviewable together.

What the map looks like

Entries read in the language of the business, reflected straight out of the compiled code:

### MyApp.Deals — domain `[surface:df8be63a83b8]`

**API** (32): approve_deal/1 create_deal/1 extract_deal/1 get_summary/1 …
**Schemas:** Deal, DealParty, DealPartyComponent
**Support:** AccountKeys, DealReset, EditTracker, Insights, MyCalculator

### MyApp.Deals.Deal → `deals`
- fields: …, status:enum(draft|needs_review|approved|reversed), …
- belongs_to reviewed_by → MyApp.Users.User via reviewed_by_user_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 context-name`
⚠ cohere/design/my-design.md — draft, advisory only
  anchor "My Design" not in the map — fine if this design introduces it;
  `mix cohere.complete` verifies it lands

drift detected

$ mix cohere.check --accept context-name
cohere/intent/deals.md — rebound to surface df8be63a83b8, drift annotated

The coherence ladder

LevelPropertyMechanism
0Context by discoveryraw repo; agents grep and hope
1Static guidanceAGENTS.md / usage_rules
2Derived truththe map (mix cohere.map)
3Authored intent, checkedintent cards + drift sentinel
4Governed verbs, verified behaviorboundary enforcement, Tidewave runtime introspection
5Delivered contextwork 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. With Ecto, objects and links appear in the map; with Oban, the job surface; with boundary, level 4 lights up; with Tidewave, work packets direct agents to verify behavior in the running app (project_eval, execute_sql_query) instead of inferring it from source.

Mix tasks

TaskDoes
mix cohereladder status + designs in flight
mix cohere.initscaffold 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.checkthe 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.mapregenerate 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 lives in cohere/design/design-spec.md, an accepted design doc in cohere's own coherence layer: 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. The feature loop's design is cohere/design/feature-loop.md, completed by the tool it specifies.

License

MIT