Stop your agents rebuilding what already exists

A coding agent with no picture of your architecture writes a second implementation of something you already have, in the wrong layer, owned by nobody. Architecture memory gives it the map first — the components, who owns them, and what the last scan found wrong.

  • YAML or JSON contracts
  • Python AST + TypeScript imports
  • 5 endpoints
  • Scans a server-local path

Declare the architecture, or infer it

There are two ways in. Import a contract you maintain by hand, or scan a tree and let the imports describe themselves. Most teams end up doing both, and the contract wins where they disagree.

Top level

YAML or JSON. Send it as a parsed object or as a string and it will be parsed for you — YAML first, JSON as the fallback.

  • name · title What this contract is called. Falls back to the project id, then to “Architecture Contract”.
  • systems The largest unit. Each entry becomes a node of type system.
  • domains Bounded areas inside a system. Each becomes a node of type domain.
  • features What the software does for someone. Each becomes a node of type feature — and features are checked for an owner.
  • components The things that actually exist in the tree. Each becomes a node of type component, also checked for an owner.
  • edges · links How the nodes relate. Either key is accepted, so an existing file usually imports without editing.

Inside a node

Each section takes a list of objects, or a mapping of key to object — both shapes import. Anything you add beyond these fields is kept verbatim.

  • name · key · id The identity, in that order of preference. An entry with none of the three imports as “unnamed”, which the doctor will find.
  • description · summary Becomes the node’s searchable text, so a query for “the thing that sends invoices” can reach it.
  • owner · team Either satisfies the ownership check. A feature or component with neither is reported by the doctor as unowned.
  • status · confidence Optional. Status defaults to approved and confidence to 0.8 when you do not set them.

Inside an edge

Edges are stored as their own records, so a dependency is a queryable object rather than a line in a diagram.

  • source · from The dependent side. Either key is accepted.
  • target · to The depended-upon side.
  • relation · type What kind of dependency this is. Defaults to depends_on; a scan writes imports.

The contract record itself is stored with authority imported, truth_state: fact, verification_state: supported and confidence 0.9 — it is a deliberate statement by a person, and it is treated as one. Nodes are upserted on project:node_type:name, so re-importing an amended contract updates the nodes it describes rather than accumulating copies of them.

Scanning a tree

POST /v1/architecture-memory/scan walks a directory. Python files are parsed with the standard library's ast module, which means the import list is the real one — every import and from … import the parser sees, deduplicated and sorted, not a regular expression's guess at it. Each file becomes a component node carrying its path, its language and its imports, and up to fifty import edges per file are written into the graph.

TypeScript and TSX files are read line by line for import statements, and register as component nodes with their import list attached. Import edges are emitted for Python today; the TypeScript arm records the imports on the node.

A file that fails to parse contributes an empty import list rather than failing the scan, so one broken file in a large tree does not cost you the run.

What scan is not

repo_path is a filesystem path on the machine running the API, resolved by that process. It has to be a directory that process can already see — a mounted volume, a checkout on the same host, a path inside the container.

This is not a GitHub or GitLab integration. It does not authenticate to a forge, it does not clone, and it cannot reach a remote repository. Point it at a path that does not exist or is not a directory and it returns HTTP 400 rather than doing something approximate.

In practice that means CI checks out the tree and calls /scan against the workspace, or the API runs on a host that already has the code. Both are ordinary; we would just rather you found out here than after wiring up a webhook.

The doctor finds the two failures that matter

Duplicate responsibility. Two nodes with the same name means two things claim the same job, which is how a codebase ends up with a second renderer nobody knew about. Each duplicate becomes an architecture_violation record with status needs_review — a durable, queryable finding rather than a line of console output that scrolls past.

No owner. Features and components with neither an owner nor a team are reported by count and by name, up to fifty at a time. Unowned code is not a style complaint; it is the reliable predictor of the thing nobody maintains.

Because violations are records, they carry the same envelope as everything else in this tier. Resolving one is a status change with a history, and the violations that are still open are exactly what gets handed to the next agent.

Brief the agent before it edits, not after

GET /v1/architecture-memory/agent-context is the endpoint this whole family exists for. One call, before any code is written.

It returns three things. The architecture nodes relevant to the query — pass q to narrow by text, project_id to narrow by project, and a limit of up to 100. The violations that are currently open, so the agent knows where the ice is thin. And three standing guardrails, returned verbatim on every call:

  • Consult architecture nodes before mutating code.
  • Do not duplicate responsibilities already assigned to a component.
  • Treat imported contracts as higher authority than inferred topology.

That third rule is the one that makes the two ingestion paths coexist. A scan infers what the code currently does; a contract states what it is supposed to do. When they disagree, the contract is the instruction and the scan is the evidence.

And the map itself

GET /topology returns the graph for drawing or analysis: every node with its id, label, type and full payload, every edge with its source, target and relation. It is the same data the doctor reads, unaggregated.

Where this pays off

An agent asked to “add invoice export” gets back the existing invoice components, their owners, the edges into them, and the standing violation saying two things already render invoice PDFs. The instruction to reuse rather than rebuild stops being a hopeful line in a prompt and becomes a fact in the context window.

Five endpoints

Two ways in, three ways to read it back.

EndpointWhat it does
POST /v1/architecture-memory/contracts/import Import a YAML or JSON contract. Writes the contract itself as one record with authority imported, then a node per system, domain, feature and component, and a record per edge.
POST /v1/architecture-memory/scan Walk a server-local directory, parse imports, and register each file as a component node. max_files defaults to 1,000 and caps at 10,000.
GET /v1/architecture-memory/topology The graph as it stands: every node with its type and payload, every edge with its source, target and relation.
GET /v1/architecture-memory/doctor Duplicate responsibilities and unowned features or components, with a violation record written for each duplicate found.
GET /v1/architecture-memory/agent-context The briefing pack: matching nodes, live violations and the standing guardrails — up to 100 nodes, 20 by default.

Give your agents a memory you can audit

Run the whole system on your own hardware under the MIT licence, or ask us about hosted access. Both start from the same place.