Data structure protocol
Skill k-kolomeitsev/data-structure-protocol/skills/data-structure-protocol
Graph-based long-term memory skill for AI (LLM) coding agents — faster context, fewer tokens, safer refactors
npx -y skills add k-kolomeitsev/data-structure-protocol --skill data-structure-protocolAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
Build and navigate DSP (Data Structure Protocol) — graph-based long-term structural memory of codebases for LLM agents. Stores entities (modules, functions), their dependencies (imports), public API (shared/exports), and reasons for every connection. Use when: (1) project has a .dsp/ directory, (2) user asks to set up DSP or bootstrap project structure, (3) creating/modifying/deleting code files in a DSP-tracked project, (4) navigating project structure, understanding dependencies, or finding modules, (5) user mentions DSP, dsp-cli, .dsp, or structure mapping.
SKILL.md
12.1 KB, as published. Nobody here has run it
Data Structure Protocol (DSP)
DSP builds a dependency graph of project entities in a .dsp/ directory. Each entity (module, function, external dependency) gets a UID, description, import list, and export index. The graph answers: what exists, why it exists, what depends on what, and who uses what.
DSP is NOT documentation for humans or AST dump. It captures meaning (purpose), boundaries (imports/exports), and reasons for connections (why).
Agent Prompt
Embed this context when working on a DSP-tracked project:
This project uses DSP (Data Structure Protocol). The
.dsp/directory is the entity graph of this project: modules, functions, dependencies, public API. It is your long-term memory of the code structure.Core rules:
- Before changing code — find affected entities via
dsp-cli search,find-by-source, orread-toc. Read theirdescriptionandimportsto understand context.- When creating a file/module — call
dsp-cli create-object. For each exported function —create-function(with--owner). Register exports viacreate-shared.- When adding an import — call
dsp-cli add-importwith a briefwhy. For external dependencies — firstcreate-object --kind externalif the entity doesn't exist yet.- When removing import / export / file — call
remove-import,remove-shared,remove-entityrespectively. Cascade cleanup is automatic.- When renaming/moving a file — call
move-entity. UID does not change.- Don't touch DSP if only internal implementation changed without affecting purpose or dependencies.
- TOC membership — new entities land in every TOC whose root scope covers their path (or pass
--tocexplicitly, repeatable). Reshape membership withadd-to-toc/move-to-toc.- Bootstrap — if
.dsp/is empty: discover roots (--new-root --scope), split files into per-TOC batches balanced by volume, then three waves over the batches in parallel — index all files, then all exports, then all imports. Each file is read exactly once (in Wave 1); later waves reuse that read.Key commands:
dsp-cli init dsp-cli create-object <source> <purpose> [--kind external] [--uid UID] [--toc TOC ...] [--new-root [--scope DIR]] dsp-cli create-function <source> <purpose> [--owner UID] [--uid UID] [--toc TOC ...] dsp-cli create-shared <exporter_uid> <shared_uid> [<shared_uid> ...] dsp-cli add-import <importer_uid> <imported_uid> <why> [--exporter UID] dsp-cli add-to-toc <uid> [<uid> ...] --toc TOC [--toc TOC ...] dsp-cli move-to-toc <uid> [<uid> ...] --from TOC --to TOC dsp-cli remove-import <importer_uid> <imported_uid> [--exporter UID] dsp-cli remove-shared <exporter_uid> <shared_uid> dsp-cli remove-entity <uid> dsp-cli move-entity <uid> <new_source> dsp-cli update-description <uid> [--source S] [--purpose P] [--kind K] [--scope DIR] dsp-cli get-entity <uid> dsp-cli get-children <uid> [--depth N] dsp-cli get-parents <uid> [--depth N] dsp-cli search <query> dsp-cli find-by-source <path> dsp-cli read-toc [--toc ROOT_UID] dsp-cli get-stats
TOCis a root UID or the literaldefault(the plain.dsp/TOCfile).
Using the CLI
The script is at scripts/dsp-cli.py relative to this skill directory.
python <skill-path>/scripts/dsp-cli.py [--root <project-root>] <command> [args]
--root defaults to current working directory. All paths in arguments are repo-relative.
Core Concepts
- Code = graph. Nodes are Objects and Functions. Edges are
importsandshared/exports. - Identity by UID, not file path. Path is an attribute; renames/moves don't change UID.
- "Shared" creates an entity. If something becomes public (exported), it gets its own UID.
- Import tracks both "from where" and "what". One code import may create two DSP links: to the module and to the specific shared entity.
- Full import coverage. Every imported file/asset must be an Object in
.dsp— code, images, styles, configs, everything. whylives next to the imported entity in itsexports/directory (reverse index).- Start from roots. Each root entrypoint has its own TOC file. A root may declare a
scope(directory subtree); new entities are auto-assigned to every TOC whose root scope covers their path. - External deps — record only.
kind: external, no deep dive intonode_modules/site-packages/etc. Butexports indexworks — shows who imports it. - Persistent reverse-index cache.
.dsp/.cache/holds the reverse adjacency (imported → importers), one file per imported entity. It makes reverse and traversal commands (get-recipients,get-parents,get-path, andget-entity's "exported to") fast on large graphs without re-scanning. Local and forward commands (get-children,get-shared,read-toc,find-by-source,search) read live files and never touch it. Mutating commands keep it up to date incrementally (only the affected entries), so it stays correct as you build the graph.- Auto-build. If the cache is missing, the next reverse/traversal command — or the next reverse-affecting mutation — builds it automatically. No manual step is needed in normal use.
- It is committed with the graph (not git-ignored): a plain
git checkout/pullmoves the cache together with.dsp/. Caveat: amerge/rebasethat touches.dsp/can merge.cache/files incorrectly or leave conflicts — the cache is not self-validating (it tracks only a sentinel, not content), so runrebuild-cacheafterwards to be safe. rebuild-cache. Runpython <skill-path>/scripts/dsp-cli.py rebuild-cacheif.dsp/was changed outside this CLI — hand-edited files, or amerge/rebasethat touched.dsp/. Agents that follow the protocol mutate.dsp/solely through dsp-cli, so during normal work this is rarely needed.
UID Format
- Objects:
obj-<8 hex>(e.g.,obj-a1b2c3d4) - Functions:
func-<8 hex>(e.g.,func-7f3a9c12)
UID marker in source code — comment @dsp <uid> before declaration:
// @dsp func-7f3a9c12
export function calculateTotal(items) { ... }
# @dsp obj-e5f6g7h8
class UserService:
Workflows
Setting Up DSP (bootstrap in 3 waves)
Core economy rule: each file is read exactly once, by exactly one subagent — all three waves run on top of that single read. Batches run in parallel; the only sync point is the barrier before Wave 3.
- Run
dsp-cli initto create.dsp/directory. - Phase 0 — roots: identify entrypoint(s) and their directory scopes, create each with
create-object <path> <purpose> --new-root --scope <dir>(.= whole repo). Scopes make TOC assignment automatic for everything that follows. - Inventory & batching: list all project files with sizes (e.g.
git ls-files | xargs wc -c; skip vendored code, build output, lock files). Group by TOC, split each group into batches of roughly equal volume — one batch per subagent, dispatched in parallel. - Wave 1 — all files: each subagent reads each file of its batch once (capturing purpose, entities, exports, imports with usage sites) and registers it:
create-object+create-function --ownerfor significant inner entities,@dspmarkers in source. - Wave 2 — all exports: same subagent, no re-reading —
create-sharedper file (batch-local, no waiting on other batches). - Barrier: when ALL batches finish Waves 1–2, subagents report their externals; the orchestrator dedupes and registers each once (
create-object --kind external+add-to-tocfor other roots using it). - Wave 3 — all imports: same subagent, still no re-reading —
add-importwith usage-basedwhy(dead imports were already filtered at the Wave 1 read); targets resolve viafind-by-source. - Verify:
get-stats,get-orphans,detect-cycles; every inventory file resolves viafind-by-source. Details: bootstrap.md.
Re-indexing a project whose code already has @dsp markers: pass the old UIDs via --uid at every create step — the graph is rebuilt with stable identity.
Creating Entities (when writing new code)
- Create module:
dsp-cli create-object <path> <purpose>— it lands in every TOC whose root scope covers the path (override with--toc <TOC>, repeatable) - Create functions:
dsp-cli create-function <path>#<symbol> <purpose> --owner <module-uid> - Register exports:
dsp-cli create-shared <module-uid> <func-uid> [<func-uid> ...]— shared entries are UIDs of existing entities, never export names - Register imports:
dsp-cli add-import <this-uid> <imported-uid> <why> [--exporter <module-uid>]— all UIDs must already exist - External deps:
dsp-cli create-object <package-name> <purpose> --kind external
Navigating the Graph (when reading/understanding code)
- Find entity by file:
dsp-cli find-by-source <path> - Search by keyword:
dsp-cli search <query> - Read TOC:
dsp-cli read-toc→ get all UIDs, thenget-entityfor details - Dependency tree down:
dsp-cli get-children <uid> --depth N - Dependency tree up:
dsp-cli get-parents <uid> --depth N - Impact analysis:
dsp-cli get-recipients <uid>— who depends on this entity - Path between entities:
dsp-cli get-path <from> <to>
Updating (when modifying code)
- Purpose changed:
dsp-cli update-description <uid> --purpose <new> - File moved:
dsp-cli move-entity <uid> <new-path> - Import reason changed:
dsp-cli update-import-why <importer> <imported> <new-why> - Root's zone changed:
dsp-cli update-description <root-uid> --scope <dir>
Managing TOC membership
- Add existing entities to more TOCs:
dsp-cli add-to-toc <uid> [<uid> ...] --toc <TOC>(idempotent; e.g. an external used by a second root) - Transfer entities between TOCs (single or batch):
dsp-cli move-to-toc <uid> [<uid> ...] --from <TOC> --to <TOC>— all-or-nothing; a root cannot leave its own TOC <TOC>is a root UID ordefault
Deleting (when removing code)
- Import removed:
dsp-cli remove-import <importer> <imported> [--exporter UID] - Export removed:
dsp-cli remove-shared <exporter> <shared> - File/module deleted:
dsp-cli remove-entity <uid>(cascading cleanup)
Diagnostics
dsp-cli detect-cycles [--toc ROOT_UID]— circular dependencies (--tocscopes to one TOC root's entities)dsp-cli get-orphans [--toc ROOT_UID]— unused entities (--tocscopes to one TOC root's entities)dsp-cli get-stats [--toc ROOT_UID]— project graph overview (--tocscopes to one TOC root's entities)
When to Update DSP
| Code Change | DSP Action |
|---|---|
| New file/module | create-object + create-function + create-shared + add-import |
| New import added | add-import (+ create-object --kind external if new external dep) |
| Import removed | remove-import |
| Export added | create-shared (+ create-function if new function) |
| Export removed | remove-shared |
| File renamed/moved | move-entity |
| File deleted | remove-entity |
| Purpose changed | update-description |
| Module moved to another subproject/root | move-to-toc (+ move-entity if the path changed) |
| Entity now used by another root | add-to-toc |
| Internal-only change | No DSP update needed |
References
- Storage format —
.dsp/directory structure, file formats, TOC - Bootstrap procedure — initial project markup (3-wave algorithm)
- Operations reference — detailed semantics of all operations with import examples