
DSP CodeGen
The missing IR layer for AI code generation — describe the system once in a graph, and your agent deterministically generates polyglot code from it, verifies it against the graph, and evolves the graph instead of the code.
Describe the system in DSP → the agent generates code in the required languages → verifies it against the graph → you change the graph, not the code.
The project graph in the browser: file tree, hierarchical entity packing, and a command panel — all on top of the same runtime that serves the CLI (details — Interface).
DSP CodeGen is an agent-first skill: a set of methodologies + a CLI where the project graph (DSP, Data Structure Protocol) acts as the compiler intermediate representation (IR). One and the same graph serves two purposes: long-term structural memory of the existing code and the source of truth for generating new code.
The problem
Direct code generation by an agent "from brief straight to code" is one big nondeterministic jump: you cannot verify it, split it into parallel parts, reassemble it piece by piece, or guarantee that the modules will fit together. This hurts most in polyglot systems (a module in Go, an admin panel in Node, a core in Rust): pieces in different languages must "get along", and the agent has nowhere to get a single model of how they join. Every session the agent re-"reads" the code, spending tokens on what was already understood last time.
DSP CodeGen inserts a verifiable, stable, decomposable layer between the brief and the code — a graph with typed contracts at the seams. That gives what direct generation lacks: determinism, parallelism across independent branches, impact analysis, and incremental regeneration.
What you get
- Deterministic generation — topology, languages, and seam contracts come from the graph; stubs are produced by the tool (not an LLM): one contract → identical stubs in any language
- Polyglot without chaos — modules in different languages bind to the same stubs; integration is correct by construction, not "by luck"
- Impact analysis before you edit —
get-recipients/get-parentsshow what will break before you touch the code - Incremental regeneration — edit the graph → regenerate exactly what changed via
diff, not the whole project - Structural memory across sessions — the agent does not rescan the repository every time:
.dsp/is a git-friendly plain-text graph that commits and reviews like code - Verifiability —
verify+ compiler + coherence harnesses give a gate, not "seems to work" - Works with Claude Code, Cursor, Codex, Hermes, OpenClaw — a skill, not a platform, no lock-in
Install
The skill is installed into your AI coding harness with one command. The script places the
skills/dsp-codegen folder into the harness's skill directory — project-level or global.
Linux / macOS:
# project (current directory)
curl -fsSL https://raw.githubusercontent.com/Loqira-Labs/dsp-codegen/main/install.sh | bash -s -- --target .
# global (user level)
curl -fsSL https://raw.githubusercontent.com/Loqira-Labs/dsp-codegen/main/install.sh | bash -s -- --global
Windows:
# project (current directory)
irm https://raw.githubusercontent.com/Loqira-Labs/dsp-codegen/main/install.ps1 | iex
# global (user level)
irm https://raw.githubusercontent.com/Loqira-Labs/dsp-codegen/main/install.ps1 | iex -Global
Or from a repository clone (offline): ./install.sh --target <proj> / .\install.ps1 -Target <proj>.
| Harness | Project | Global |
|---|---|---|
| Claude Code | .claude/skills/dsp-codegen/ |
~/.claude/skills/dsp-codegen/ |
| Cursor | .cursor/skills/dsp-codegen/ |
~/.cursor/skills/dsp-codegen/ |
| Codex CLI | .agents/skills/dsp-codegen/ |
~/.agents/skills/dsp-codegen/ |
| Hermes | — (no project-level path) | ~/.hermes/skills/community/dsp-codegen/ |
| OpenClaw | skills/dsp-codegen/ |
~/.openclaw/skills/dsp-codegen/ |
Details, installation checks, update and removal — in GETTING_STARTED.md.
How it works
brief (source) ──► DSP graph + contracts (IR) ──► polyglot modules (machine code)
human agent authors agent generates
- The human gives a brief and at most looks at the graph with brief explanations. The graph is written by the agent.
- After creation the graph — not the brief — is the source of truth. All changes are
DSP-first: edit the graph → regenerate exactly what changed via
diff. - The graph consists of nodes (Objects
obj-…: modules, types, configs, external; Functionsfunc-…) and edges (imports with awhyfor each connection; shared/exports — the public API). Identity is by UID, the path is an attribute; the@dsp <uid>marker in code binds an entity to its region.
Where determinism comes from
- topology / languages / wiring — 100% from the graph;
- contracts and seam stubs — deterministically produced by the tool (template/
protoc/schema), not an LLM: one contract → identical stubs in any language; - module bodies — written by an LLM, not byte-for-byte, but bound and verified: they
must satisfy the frozen contract (compiler) and the
spec(tests); - integration — deterministic by construction: everything binds to the same stubs.
Key invariant: every shared (public) function and type has a contract — the graph is the source of every public signature, so the interface layer is frozen and only bodies vary. Private functions are not modeled — the agent writes them freely inside the file (module tests + the compiler provide the safety net). One line: public/shared vs private.
Contracts (typed seams)
The lightest kind is chosen for the real coupling:
| kind | when | contract | validator |
|---|---|---|---|
| native | one language, in-process call | target-language signature | compiler |
| data | shared store/stream (DB, queue, file) | schema + per-language typemap + serialization |
schema + cross-language coherence + tests |
| api | direct cross-language call | RPC/HTTP schema (protobuf/OpenAPI) + transport | codegen IDL + compiler |
Only serializable DTOs cross the language boundary; a unified error model. Boundaries are expensive — the language is chosen per large module/service, and cross-language chatter is minimized.
Quick start (CLI)
The graph is served by a resident runtime: one process keeps the whole graph in memory and
syncs edits write-through to .dsp/. Start it once per project; the other commands are thin
clients to it (routed over localhost HTTP/JSON, port from .dsp/.runtime):
DSP=skills/dsp-codegen/scripts/dsp-cli.py
python $DSP --root <proj> init # genesis: create .dsp/
python $DSP --root <proj> serve --port 47777 & # start the runtime (background)
python $DSP --root <proj> apply graph.json # declaratively add a subgraph (client → runtime)
python $DSP --root <proj> list-tocs # which roots (TOC) exist in the project
python $DSP --root <proj> read-toc # table of contents: TOC[0] = root
python $DSP --root <proj> sheet # contract sheet (what to generate)
python $DSP --root <proj> plan # plan: language clusters + waves + batches
python $DSP --root <proj> verify [<target>] # structural drift + gate commands
python $DSP --root <proj> snapshot > base.json # graph snapshot
python $DSP --root <proj> diff base.json # semantic changeset + impact
python $DSP --root <proj> reload # reread .dsp/ after git checkout/pull
python $DSP --root <proj> stop # stop the runtime
While the runtime is alive, the graph is also visible in the browser: http://127.0.0.1:47777/ui
(see Interface).
A command without a live runtime fails loudly with an explicit error — no hidden one-shot
fallbacks (except init/serve, which do not need the runtime). The default port is 47777
(uncommon; if busy — a loud error, pass another via --port).
TOC addressing. list-tocs prints the TOC file name (TOC-<uid>, or plain TOC for the
default one); any --toc/target accepts that name, as well as the TOC key (a root uid or
default) and the root uid — all forms resolve through one normalization. read-toc without
--toc reads the default TOC, or in a project built around a root (no default TOC there) its
single TOC; with several TOCs and no default the command lists them and refuses to guess.
Repository checks (requires python, go, node, sqlite3):
python tests/test_dsp_cli.py # CLI regression suite
python tests/spike-events/verify_coherence.py # data seam → COHERENT:PASS
python tests/spike-api/verify_api_coherence.py# api seam → COHERENT:PASS
Example apply document (declarative upsert by path handles, idempotent):
{
"root": { "source": "system.md", "purpose": "events-app", "scope": "." },
"entities": [
{
"source": "schema/events.sql",
"purpose": "events data-contract",
"set": {
"language": "sql",
"contract_kind": "data",
"contract": "events(id INTEGER pk [db-managed]; kind TEXT; payload TEXT; created_at INTEGER)",
"typemap": "INTEGER->{go:int64,node:number}; TEXT->{go:string,node:string}"
}
},
{
"source": "ingest/main.go",
"purpose": "Go ingest: writes events",
"set": { "language": "go" }
},
{
"source": "admin/read.mjs",
"purpose": "Node admin: reads events",
"set": { "language": "node" }
}
],
"edges": [
{
"from": "ingest/main.go",
"to": "schema/events.sql",
"why": "writes rows",
"access": "write"
},
{
"from": "admin/read.mjs",
"to": "schema/events.sql",
"why": "reads rows",
"access": "read"
}
]
}
DSP CodeGen vs alternatives
| Direct generation | Graph/RAG tools | DSP CodeGen | |
|---|---|---|---|
| Core idea | From brief straight to code | Understand code (read/search) | Graph = memory and IR: read and generate |
| What it solves | — | Agent has nothing to search code with | Agent has no deterministic way to generate code |
| Generation determinism | No (one jump) | Does not generate code | Topology/contracts/stubs — from the graph |
| Polyglot seams | Unverifiable | Does not cover generation | Typed contracts at the seams |
| Memory across sessions | None | Yes (graph) | Yes (.dsp/, git-friendly) |
| Impact analysis | No | Partial | Built-in (graph traversal) |
| Incremental regeneration | No | — | diff → only what changed |
| Verifiability | "Seems to work" | — | verify + compiler + coherence harnesses |
Modern agents can plan, write tests, and verify. What they lack is memory and a deterministic bridge between description and code. DSP CodeGen is both.
Core concepts
| Concept | What it is |
|---|---|
| Entity | A graph node: an Object (module/file/class/config/external) or a Function (function/method/handler) |
| UID | Stable identifier (obj-<8hex>, func-<8hex>). The path is an attribute, not identity: entities survive renames |
| imports | Outgoing edges — what the entity uses, with a why for each connection |
| shared / exports | An object's public API and the reverse index — who imports it and why |
| TOC | Per-root table of contents; membership follows root scopes automatically |
| Contract | A typed seam: native / data / api |
| status | planned → contract → impl → verified → stale — entity lifecycle |
| tags | Cross-cutting workflow labels, orthogonal to TOC (#auth = user-entity + api + guards…) |
A UID marker binds identity in code:
// @dsp func-7f3a9c12
export function calculateTotal(items: Item[]): number {
/* ... */
}
# @dsp func-3c19ab8e
def process_payment(order):
...
CLI commands
- Runtime:
serve(start the resident graph server;--port, default 47777) ·reload(reread.dsp/after an external edit) ·stop - Graph (edit):
init·create-object·create-function·create-shared·update-shared-desc(rewrite an export blurb: by text or--from-purpose) ·add-import·update-description(--set K=V,--unset K) ·update-import-why·move-entity·add-to-toc·move-to-toc·remove-import·remove-shared·remove-entity·prune(self-consistency repair: dangling shared/import references and orphaned exports) - Tags (workflow):
add-tags·remove-tags·replace-tags(always arrays; replace swaps--old→--new) ·get-by-tag(--all= intersection) ·list-tags— cross-cutting labels, orthogonal to TOC (#auth= user-entity + api + guards…);get-by-tagreturns ordinary DSP entities. - Navigation:
get-entity·get-shared·get-recipients(impact) ·get-children·get-parents·get-path·search·select·find-by-source·read-toc·list-tocs(the entry point into an unfamiliar project: lists the roots) ·detect-cycles·get-orphans·get-stats - Composite filters (
--path/--tag/--all-tags/--not-tag/--text/--lang/--kind/--contract-kind/--status/--toc, AND across dimensions) onselect(pure query),search(text +) andget-by-tag(tags +) — e.g.select --path auth --contract-kind data --not-tag legacy. - Codegen:
sheet·apply·plan·verify·snapshot/diff.verifyandsheettake a positionaltarget: a TOC, an entity uid, or a path prefix (module) — a gate for a specific change. - Output: global
--json(before or after the subcommand) — machine JSON for any command (read/navigation/codegen result;planemits a flattasks+ready; mutations —{uid,toc}/{ok}); the runtime returns it verbatim as stdout (programmatic / GUI contract). Without the flag — formatted text.
Entity fields: language, contract_kind, contract, typemap, serialization, spec,
status, tags (cross-cutting workflow labels, orthogonal to TOC); on an edge: access.
Language registry (LANGUAGES in dsp-cli.py, extended by one line): go · python · js · ts ·
rust · c · cpp · csharp · java · kotlin · swift · ruby · php · scala · dart · elixir · sql
(aliases node/golang/c++/c#/typescript are normalized).
Interface
The live runtime serves the graph to the browser: http://127.0.0.1:47777/ui (the port — the one
serve printed); the screenshot is at the top of this README.
It is one self-contained file scripts/dsp-ui.html: inline CSS+JS, no frameworks, no build, no
external resources. The page is an ordinary client of the command layer: it reads four GET routes
(/ui, /, /commands, /files), and every mutation goes through the same POST /op as the
CLI — it has no behavior of its own.
- Project files — the real project directory tree (
GET /files) with the entity count per file; clicking a file highlights its entities and connections both ways, double-clicking a directory scopes the selection. - Graph — hierarchical circle packing: directory → subdirectories → files → entities;
show graphshows all direct connections,focus— by depth and direction controls. - Commands — forms are built from
GET /commands(the catalog is taken from the CLI parser, so there is no second copy of the specification); destructive commands require confirmation. - The scope dropdown lists the project TOCs from
list-tocs(name, scope, entity count) plus a "whole graph" option.
Methodology (6 stages)
- Brief and discussion — take the brief, propose a strawman, batch questions with defaults, freeze the intent.
- Graph authoring — intent → a typed self-contained graph (public functions/types only).
- Contracts — type the seams (native/data/api).
- Planning — language clusters, topo/SCC, contracts-first + barrier, batches by module.
- Generation — Phase A: deterministic stubs → barrier → Phase B: bodies (in parallel, in a git worktree).
- Verify & update — the gate (coherence + compiler + tests); DSP-first change via
diff.
Details — in the skill's SKILL.md ("Codegen lifecycle" section + the full command reference).
Repository structure
skills/dsp-codegen/ the single skill (methodology + CLI) — self-contained, copied into a project as-is
SKILL.md all working knowledge: graph model + contracts +
codegen lifecycle (6 stages) + full command reference
references/ situational only:
bootstrap.md indexing an existing codebase (a project with no .dsp/)
storage-format.md .dsp/ on-disk format (manual inspection/repair)
scripts/dsp-cli.py CLI: runtime server (`serve`) + thin client + in-memory engine
scripts/dsp-ui.html graph interface: one self-contained HTML, served at `GET /ui`
dsp-tool-config.json `Dsp` tool wrapper (cli-wrapper)
KNOWN-ISSUES.md open CLI limitations (currently — none)
LICENSE the skill license (same copy lives in the repo root)
install.sh installer for Linux
install-macos.sh installer for macOS
install.ps1 installer for Windows
GETTING_STARTED.md installation guide for harnesses (Claude Code/Cursor/Codex/Hermes/OpenClaw)
tests/test_dsp_cli.py CLI regression suite (stdlib unittest, no dependencies)
tests/spike-events/ data example: Go-ingest + Node-admin over a shared sqlite
verify_coherence.py reproducible COHERENT harness (go→sqlite→node)
tests/spike-api/ api example: Go HTTP server + Node client over one api contract
verify_api_coherence.py reproducible COHERENT harness (go http ↔ node)
docs/design/dsp-codegen-concept.md architecture and design rationale
docs/design/runtime.md runtime design (in-memory graph + write-through, HTTP API, UI)
assets/preview.png interface screenshot
The skill in skills/dsp-codegen/ is self-contained: inside it are only its own files
(SKILL.md, references/, scripts/, dsp-tool-config.json), no links to the repository's
docs and tests — they do not ship to the client. The installer copies the folder as-is into the
harness skill directory (.claude/skills/, .cursor/skills/, .agents/skills/, …), while tests
and examples stay in the repository — they are the skill's gate, not its part.
Status
Production-ready and verifiable: both cross-language seams are reproducible from the repository —
data (tests/spike-events/, Go+Node over a shared sqlite) and api (tests/spike-api/,
Go HTTP server ↔ Node client over HTTP), each with its own verify_*_coherence.py →
COHERENT:PASS. Plus a resident runtime, a full CLI (17 languages), a browser graph interface
(GET /ui), and the regression suite tests/test_dsp_cli.py — 207 tests on stdlib unittest,
no dependencies.
Architectural decisions
Intentional design decisions, not gaps.
- CLI is the deterministic layer; module bodies (Phase B) are written by agent orchestration
(
Agent+ worktree) under theverify+ compiler + tests gate. This is the design (bodies are the bounded LLM part), not a missing CLI command; verifyis structural lint only; depth comes from the compiler + coherence harness.phantom-import/missing-fieldare intentionally heuristic — do not harden into per-language parsers.missing-symbol/phantom-importrecognize qualified members (Owner::method), fields, enum variants, and multi-token fragments (impl Trait for Type) by member name, not by a qualified literal;missing-fieldrequires every contract field at every cross-language recipient — coherence discipline (each language implements the schema independently), not a false positive; a same-language recipient shares the type (the compiler gates it) and is not grepped;get-orphansreturns graph orphans, not dead code (type-level dependencies —dyn Trait, bounds, return types — are not caught by edges);detect-cyclesreturns only real multi-node cycles (a self-edge from a module re-export is not a cycle);- private functions are not modeled;
kindis coercive and authoritative (object = any type, function = fn, external = external dependency), the exact form is incontract; the kind field changes freely (update-description --kind), the uid prefix is an immutable birth-hint (identity/edges/markers stay intact); - the runtime accepts connections concurrently but serializes graph operations under one lock —
one writer without a cross-process lock, while a slow operation does not block another client's
discovery
ping; without a live runtime a command fails loudly (no fallback exceptinit/serve); the client verifies project identity (rootin thepingresponse) and does not delete the discovery record on a transient ping timeout (only on refused/identity-mismatch); - the default port
47777is fixed; a busy port fails loudly (no "hopping") — pass another via--port; init(local genesis) andserve(resident) are both intentional; theDspwrapper withoutserveis intentional (it blocks);status: contract= the contract is frozen, code not yet generated → no@dspregion, this is not "stale";- the
.dsp/format is stable/git-friendly; indexes (rev / source / TOC / why / tags) live in RAM, no disk cache; - TOC is addressable by three forms (file name, key, root uid) through one normalization: what
list-tocsprints is accepted by any--toc/target. Ambiguity exists only where it cannot be resolved deterministically: reading without--tocwith several TOCs and no default — refusal with a list, not a guess.
Documentation
- Skill (agent-facing):
skills/dsp-codegen/SKILL.md+references/ - Architecture and rationale:
docs/design/dsp-codegen-concept.md·docs/design/runtime.md - Tool limitations:
skills/dsp-codegen/KNOWN-ISSUES.md - Live examples:
tests/spike-events/(data seam) andtests/spike-api/(api seam), with their.dsp/ - Harness installation:
GETTING_STARTED.md
License
Apache License 2.0 (LICENSE; the same copy ships inside the skill).
Copyright 2026 Konstantin Kolomeitsev, Loqira Labs.
No comments yet
Be the first to share your take.