DSP CodeGen interface

License Python Tests Claude Code Cursor Codex Hermes OpenClaw

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 editget-recipients/get-parents show 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
  • Verifiabilityverify + 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; Functions func-…) and edges (imports with a why for 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

  1. topology / languages / wiring — 100% from the graph;
  2. contracts and seam stubs — deterministically produced by the tool (template/protoc/schema), not an LLM: one contract → identical stubs in any language;
  3. 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);
  4. 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-tag returns 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) on select (pure query), search (text +) and get-by-tag (tags +) — e.g. select --path auth --contract-kind data --not-tag legacy.
  • Codegen: sheet · apply · plan · verify · snapshot / diff. verify and sheet take a positional target: 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; plan emits a flat tasks + 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 graph shows 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)

  1. Brief and discussion — take the brief, propose a strawman, batch questions with defaults, freeze the intent.
  2. Graph authoring — intent → a typed self-contained graph (public functions/types only).
  3. Contracts — type the seams (native/data/api).
  4. Planning — language clusters, topo/SCC, contracts-first + barrier, batches by module.
  5. Generation — Phase A: deterministic stubs → barrier → Phase B: bodies (in parallel, in a git worktree).
  6. 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.pyCOHERENT: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 the verify + compiler + tests gate. This is the design (bodies are the bounded LLM part), not a missing CLI command;
  • verify is structural lint only; depth comes from the compiler + coherence harness. phantom-import/missing-field are intentionally heuristic — do not harden into per-language parsers. missing-symbol/phantom-import recognize qualified members (Owner::method), fields, enum variants, and multi-token fragments (impl Trait for Type) by member name, not by a qualified literal;
  • missing-field requires 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-orphans returns graph orphans, not dead code (type-level dependencies — dyn Trait, bounds, return types — are not caught by edges); detect-cycles returns only real multi-node cycles (a self-edge from a module re-export is not a cycle);
  • private functions are not modeled; kind is coercive and authoritative (object = any type, function = fn, external = external dependency), the exact form is in contract; 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 except init/serve); the client verifies project identity (root in the ping response) and does not delete the discovery record on a transient ping timeout (only on refused/identity-mismatch);
  • the default port 47777 is fixed; a busy port fails loudly (no "hopping") — pass another via --port;
  • init (local genesis) and serve (resident) are both intentional; the Dsp wrapper without serve is intentional (it blocks);
  • status: contract = the contract is frozen, code not yet generated → no @dsp region, 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-tocs prints is accepted by any --toc/target. Ambiguity exists only where it cannot be resolved deterministically: reading without --toc with 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) and tests/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.