One Engine, Several Surfaces
The cleanest way to read the architecture is to start with the invariant and work outward. The invariant is the practice contract: ten role prompts, eighty-one skill sources that ship as eighty skills, thirty runtime tools, thirty-eight registered artifact-kind contracts, thirty-nine catalog rows, seven authored templates, and a corpus of two hundred twenty-eight resources. The compiler then projects that contract into surfaces without changing the contract itself.
That distinction matters because a host can radically change the user experience while leaving the runtime semantics untouched. The CLI gives me local operator flow over ASD_WORKSPACE. The MCP bundle exposes newline-delimited JSON-RPC. Claude Code and other coding-agent packs add role prompts, skill files, and pack-local MCP wiring. BUZZ contributes persona-pack definitions and a collaboration layer. DSH adds a host adapter, Studio UI, and loopback RPC. None of those surfaces are allowed to change what counts as evidence, what method-run returns, how artifact acceptance works, or who may advance a phase.
That is why the book keeps returning to the phrase one engine, twelve surfaces. I am describing a projection model, not a federation of loosely related tools. If a host pack says something the CLI would reject, the host pack is wrong.
| Emitted target | Primary job | What it adds beyond the engine |
|---|---|---|
cli |
Local operator workflow and CI surface | Pack-local launcher, shell ergonomics, deterministic exit codes |
mcp |
Generic stdio integration | JSON-RPC transport, resources, prompts, client configuration snippets |
claude-code, codex, kimi, opencode, omp, pi, grok, agy |
Coding-agent host packs | Host layout, prompt packaging, skills, vendored MCP wiring, commands |
buzz |
Persona Pack for open agent collaboration | Ten keyless persona definitions, relative MCP binding, Canvas linkage conventions |
dsh |
DeepSeek Harness Studio adapter | @asd/ui bundle, Studio client, loopback /asd/app and /asd/rpc routes |
The distribution repositories make the same point at release time. imehr/asd-plugin is not an independently authored product line. It is generated from emitted host packs by scripts/publish-distribution-repos.mjs, and it carries a provenance.json file so the release can point back to the exact source commit. The same is true of imehr/asd-mcp. This is less glamorous than hand-maintained downstream repos, and that is precisely why I trust it. Drift is boring until it corrupts a live integration.
Once I accept that rule, a lot of architectural choices become obvious. The emitted packs are generated, not hand-curated. Host READMEs interpolate inventory counts instead of hardcoding them. MCP entrypoints are always pack-local. Release evidence distinguishes emitted-pack verification from host execution. Even the documentation is written under receipt discipline because the docs are part of the contract, not marketing afterthoughts.
Seen this way, the architecture is almost anti-charismatic. There is no special host that counts as the “real” ASD. The CLI is real. The MCP bundle is real. The plugin packs are real. BUZZ and DSH are real within their stated boundaries. That lack of hierarchy is a design choice. It prevents the organization from treating one favored interface as the product and everything else as a second-class afterthought. The product is the contract. The surfaces are different ways to enter it.
The Compiler and the Pack Tree
The compiler is the hinge between canonical source and distributable surfaces. Three files do the heavy conceptual work: compile.mjs, generate-skills.mjs, and emit-packs.mjs. They are not interchangeable. Each one narrows a different kind of ambiguity.
compile.mjs scans the canonical trees under platform/agents, platform/skills, and platform/templates, derives inventory using the capability matrix, and emits deterministic manifests. No timestamps. No absolute paths. Sorted traversal. Byte-identical regeneration is part of the contract. In other words, compile.mjs answers the question: what exactly exists in the canonical practice tree right now?
generate-skills.mjs takes the method manifests in suite/packages/knowledge/data/methods and turns them into real SKILL.md files. That matters because eighty skills are only useful if they are discoverable and distinct. The generator builds frontmatter, descriptions, trigger language, provenance references, and review-gate wording from method metadata so the skills remain synchronized with the canonical method contract.
emit-packs.mjs is where the projection happens. It vendors the runtime, builds the standalone CLI and MCP bundles, synthesizes host-specific READMEs and configs, syncs emitted trees without recursively trashing unrelated files, and writes twelve distributable targets. It is also where pack-local server rules get enforced. The emitted MCP server is always launched relative to the installed pack root. No ambient executable assumptions. No “works on my PATH” shortcuts.
node platform/compiler/compile.mjs
node platform/compiler/generate-skills.mjs
node platform/compiler/emit-packs.mjs --output /tmp/asd-packs
PACK=/tmp/asd-packs/cli
mkdir -p "$HOME/.local/bin"
ln -sf "$PACK/asd" "$HOME/.local/bin/asd"
export PATH="$HOME/.local/bin:$PATH"
asd --version
asd help
I like this pipeline because each step leaves evidence. The first step defines the inventory. The second produces the skill surface. The third projects that inventory into installed form. If something drifts, I can ask which stage lied. That is a much better debugging question than “why is the plugin weird?”
platform/packs/
agy/
buzz/
claude-code/
cli/
codex/
dsh/
grok/
kimi/
mcp/
omp/
opencode/
pi/
.claude-plugin/
The hidden elegance here is that the pack tree is both broad and conservative. Broad, because it targets twelve distinct surfaces. Conservative, because every target is synthesized from the same source-of-truth inventory. Even README prose is generated from deriveInventory() so a corpus or method change cannot leave a stale literal lying around in a shipped pack. That is the sort of move people call pedantic right up until a customer installs from docs that have drifted six counts away from reality.
There is also an architectural humility in the emitter. It does not claim more than it proves. Pack emission is evidence of deterministic packaging. It is not evidence that every host executed natively. That is why the release story keeps separate receipts for emitted bundles, DSH integration, BUZZ collaboration, and downstream publication repos.
Suite Packages and the Runtime Registry
The monorepo’s suite packages tell you where responsibility lives. @asd/core carries the foundational types and shared contracts. @asd/knowledge packages the corpus data and method metadata. @asd/engine owns the runtime tool logic. @asd/agents packages the role-facing layer. @asd/render handles deterministic output transformation. @asd/runtime binds the engine into the CLI and MCP entrypoints. @asd/ui is the DSH-facing host adapter and Studio client.
If I had built all of that in one package, the emitted surfaces would be fragile. By splitting the concerns, the compiler can vendor what a target needs without making the whole system depend on the quirks of one host. The runtime package is especially important here: its bin exports define asd and asd-mcp, which means the CLI and MCP server are twins over the same registry rather than cousins that happen to share branding.
| Package | Architectural role | Why it exists separately |
|---|---|---|
@asd/core |
Shared types, status vocabulary, contract primitives | Keeps every surface aligned on the same domain model |
@asd/knowledge |
Method metadata, corpus data, templates, bundle patches | Lets the corpus travel with emitted packs without folding it into runtime logic |
@asd/engine |
Thirty sd_* tools and execution rules |
Protects runtime semantics from host packaging noise |
@asd/agents |
Role-facing layer and agent contracts | Separates role logic from tool implementation |
@asd/render |
Deterministic rendering and export helpers | Keeps presentation deterministic and testable |
@asd/runtime |
CLI and MCP binding layer | Provides stable entrypoints for emitted bundles |
@asd/ui |
DSH Studio plugin and capability adapter | Contains the host-owned UI seam without contaminating the core runtime |
This package split also explains why the host stories differ. The CLI needs @asd/runtime and knowledge data. The MCP bundle needs the same plus the stdio server surface. DSH needs @asd/ui because it has to bind into host slots and services. BUZZ does not need the Studio client at all; it needs pack-local MCP wiring and persona-pack definitions. One engine. Different shells.
The runtime registry is where that unity becomes concrete. Thirty tool names are derived from source rather than curated by hand. Tool groups cover project operations, evidence handling, method lookup and execution, artifact reads and writes, review, export, knowledge access, fact checking, orchestration, and the human phase gate. That registry is the mechanical spine of the system. The surfaces are merely access patterns.
{
"target": "cli",
"inventory": {
"tools": 30,
"methods": 80,
"schemas": 38,
"authoredTemplates": 7,
"agents": 10,
"skillSources": 81,
"catalogRows": 39,
"corpusResources": 228
},
"liveHostExecution": "unverified"
}
Notice the last line. Even the manifest speaks with boundary discipline. It advertises what is emitted and refuses to lie about host proof.
Verified Counts and Receipt Discipline
Architectural integrity lives or dies on count discipline. A small mismatch is never small when a host pack, website page, and article each claim a different inventory. ASD therefore treats counts as source-derived facts, not prose flourishes. The current manifests for emitted packs carry the canonical release counts: ten roles, eighty shipped skills from eighty-one sources, thirty tools, thirty-eight schemas, thirty-nine catalog rows, seven authored templates, and two hundred twenty-eight corpus resources. The capability-matrix tests assert that the committed corpus really derives to 228. The site index and top-level README repeat the same number.
This matters because historical material in the repository still contains superseded inventory snapshots in older receipts and article boundaries. Those records are worth keeping because they document what a prior release actually proved. They are not allowed to override the current manifests, tests, and documentation contract. Receipt history is part of the architecture, but current counts still need a single canonical answer.
| Release fact | Current canonical value | Grounding surface |
|---|---|---|
| Roles | 10 | pack manifests, source tree, site index |
| Skills shipped / skill sources | 80 / 81 | pack manifests, generated skills tree |
| Runtime tools | 30 | runtime registry, pack manifests |
| Artifact-kind contracts | 38 | pack manifests, capability matrix |
| Catalog rows | 39 | pack manifests, practice metadata |
| Authored templates | 7 | pack manifests, templates tree |
| MCP corpus resources | 228 | pack manifests, site index, emitter tests |
| Role prompts | 10 | MCP prompt surface, docs, facts sheet |
| Emitted targets | 12 | emitter receipt, pack tree |
The receipt discipline is just as important as the counts themselves. docs/reports/shared-pack-emission.receipt.json is the emitted CLI and MCP matrix receipt. It captures the twelve-target emission run, isolation details, a representative CLI workflow, and disposable MCP bundle checks. docs/reports/shared-pack-release-ledger.json records what the release scope covers and what exceptions remain bounded instead of being hand-waved away. docs/reports/dsh-live-integration.receipt.json is the DSH-specific proof surface, with its own bundle, UI, and capability claims. docs/reports/buzz-case-study-ledger.json holds the BUZZ evidence boundary.
Those receipts do more than prove success. They define honesty. A shared-pack receipt does not magically prove native Claude Code execution. A DSH receipt does not prove BUZZ persistence. A BUZZ screenshot does not prove ACP transport. By separating receipts, ASD keeps distribution evidence from mutating into mythology.
The downstream repos reinforce this discipline. imehr/asd-plugin and imehr/asd-mcp each carry a provenance.json pointer back to the source commit used for publication. That sounds mundane. Good. Provenance should feel mundane. If release lineage feels magical, it is probably broken.
I treat that distinction as operational hygiene. The shared-pack receipt tells me what the emitter and disposable bundles proved. The release ledger tells me what exceptions were consciously bounded rather than quietly ignored. The DSH receipt tells me what the Studio adapter actually exercised in a live host. The BUZZ ledger tells me exactly how far the collaboration evidence goes before claims must stop. Together they form a proof stack. Separately they prevent the worst architectural habit in AI tooling: letting one successful demo imply ten untested capabilities.
A Tour of the Common Loop
All of that structure would be academic if the everyday workflow were messy. It is not. The common loop is extremely plain, which is why it travels well. First I create or open a project and capture evidence. Second I ask sd_method_run for the executable SOP. Third I write a typed artifact. Fourth the Critic records a pass or veto with evidence-cited rows. Fifth I run sd_factcheck. Sixth the human phase gate decides whether the project advances. Same loop in the CLI. Same loop through MCP. Same loop behind host packs. Same loop under DSH. BUZZ can collaborate around it, but BUZZ does not redefine it.
{
"mcpServers": {
"asd": {
"command": "node",
"args": ["mcp/bin/server.mjs"]
}
}
}
That pack-local entrypoint rule is one of those unglamorous design decisions that saves weeks of pain. It means the installed pack carries the exact server it expects. I do not need to hope the right binary is somewhere on the machine. The same philosophy applies to the CLI pack, which links to the emitted asd shim rather than an ambient executable.
The same loop also explains why the platform can support both strict automation and careful judgment. The early steps are highly automatable because the contracts are explicit. The final step remains human because no receipt can make an organizational commitment on somebody else's behalf. In architecture terms, ASD is opinionated about state transitions and conservative about authority. That combination is what keeps the common loop portable instead of brittle.
Once the server is live, the loop becomes operationally simple. I can call sd_project_create, add evidence, inspect the method contract, write an artifact, ask for review, and run sd_factcheck. A failed artifact remains a draft with check rows; the system does not erase the attempt. A Critic veto writes a review-veto-log. A pass may promote the artifact to final. Then, and only then, does the human gate decide whether to move forward.
This is where the architecture stops feeling abstract. The common loop is the place where one engine proves it deserves many surfaces. If the CLI, MCP bundle, plugin pack, BUZZ-linked workflow, and DSH Studio all express the same loop without semantic drift, the architecture is working. If one surface invents extra states, hides the human gate, or weakens the evidence rules, the architecture is leaking.
That is the real lesson of the ASD platform architecture. The interesting part is not that it emits twelve targets. The interesting part is that twelve targets are allowed to exist without splintering the practice. The compiler, the package boundaries, the manifests, the receipts, and the distribution repos all serve that one outcome. Service design becomes portable because the contract stays singular.
A skeptic will ask what stops the loop from decaying as it travels. Three mechanisms do the work. The compiler refuses semantic drift at the source: a surface that wants different rules has to change the canonical manifest, in review, where the change is visible. The release receipts pin each surface to a named run, so "verified" always names the exact bundle and environment that ran it. And the fail-closed defaults mean a surface that loses its corpus roots or its workspace degrades loudly rather than quietly improvising. Together these three turn a twelve-surface distribution from a maintenance nightmare into a boring, repeatable release.