DSL Pipeline and ROM Specification
This document defines the standard AIKernel DSL pipeline format, the DSL ROM execution model, and HistoryROM storage model implemented in AIKernel.Core.
The DSL is not C# source code. It is a structured semantic IR represented as JSON. AI-generated plans can be validated, compiled into deterministic ResultStep pipelines, saved as immutable ROM material, and invoked later as reusable capabilities.
1. Design Rules
- The DSL is structured data, not executable code.
- The root node must be
Pipeline. - All loops are finite.
LoopUntilalso requiresmaxIterations. - Unknown node types, unknown capabilities, malformed ROM names, and invalid
Mapprojections do not create replay nodes. Executed DSL nodes and loop- Suspend is a deterministic stop point, not background waiting.
node fields fail closed.
transitions are observed through ResultStep replay entries.
2. JSON Node Schema
The package-public DTO vocabulary uses canonical node type names from DslNodeTypes. For paper/example compatibility, parsers may accept CapabilityCall as an alias for CallCapability and SuspendForApproval as an alias for Suspend, but normalized DSL documents should emit the canonical names.
2.1 Pipeline
{\n "type": "Pipeline",\n "steps": []\n}
steps is required and must be an array of pipeline nodes.
2.2 Step
{\n "type": "Step",\n "name": "start"\n}
Step records a deterministic execution node. It does not call an external capability and carries the current DSL pipeline value forward.
2.3 CallCapability
{\n "type": "CallCapability",\n "name": "Observe",\n "args": {\n "mode": "summary"\n }\n}
name must resolve through IDslCapabilityRegistry. args is optional. When present, it must be a JSON object. String values are preserved as strings; non-string values are passed as their JSON text.
2.4 Loop
{\n "type": "Loop",\n "maxIterations": 3,\n "body": [\n { "type": "CallCapability", "name": "Observe" },\n { "type": "CallCapability", "name": "Decide" }\n ]\n}
maxIterations must be an integer greater than or equal to zero. Each iteration executes body, then records a loop transition delta with loop_iteration and loop_decision.
2.5 LoopUntil
{\n "type": "LoopUntil",\n "timeout": "00:00:10",\n "maxIterations": 10,\n "body": [\n { "type": "CallCapability", "name": "Observe" }\n ]\n}
timeout is required and may be either a number of seconds or a TimeSpan string. maxIterations is also required so that the loop remains finite even when the clock never reaches the timeout. Each transition records loop_iteration, loop_decision, and, when available, loop_timestamp.
2.6 Suspend
{\n "type": "Suspend",\n "reason": "user_approval"\n}
Suspend returns a suspended ResultStep with semantic delta kind suspend. Resume is a separate execution phase and must use the prior replay log as the causal record.
3. Complete Example
{\n "type": "Pipeline",\n "steps": [\n { "type": "Step", "name": "start" },\n {\n "type": "Loop",\n "maxIterations": 2,\n "body": [\n { "type": "CallCapability", "name": "Observe" },\n { "type": "CallCapability", "name": "Decide" }\n ]\n },\n { "type": "Suspend", "reason": "user_approval" },\n { "type": "CallCapability", "name": "ExecutePlan" }\n ]\n}
Compilation flow:
JSON DSL\n -> DslDocument.FromJson\n -> IDslPipelineCompiler.Compile\n -> IKernelPipeline.Execute\n -> ResultStep + ReplayLog + ReplayLogHash
4. DSL ROM
AI-generated DSL can be saved as immutable ROM material and reused as a capability.
4.1 VFS Path and Capability Name
The implemented VFS path is:
rom/dsl/{namespace}/{name}.json
The callable capability name is:
dsl://{namespace}/{name}
Both namespace and name must be single normalized path segments. Nested capability names such as dsl://agent/nested/plan1 are rejected.
4.2 Save and Load Flow
SaveDslAsRomAsync(namespace, name, jsonDsl)\n -> parse and compile DSL\n -> compute SHA-256 ROM hash\n -> write JSON to VFS when missing\n -> reject if an existing path has different content\n -> register DslRomSnapshot in IDslRomRegistry
Loading requires the expected ROM hash. If VFS content differs from the expected hash, loading fails closed.
4.3 Calling a DSL ROM
{\n "type": "CallCapability",\n "name": "dsl://agent/plan1"\n}
DslRomCapabilityRegistry resolves the ROM snapshot, validates its metadata, executes the compiled inner pipeline, and returns the resulting DslPipelineValue. ROM calls are also represented in replay metadata.
5. Replay and Metadata
DSL execution writes semantic deltas for node execution, loop transitions, capability calls, suspend points, and DSL ROM calls.
The stable ROM metadata keys are:
| Key | Meaning |
|---|---|
dsl_rom_hash |
SHA-256 hash of the JSON DSL content. |
dsl_rom_call |
Capability name, for example dsl://agent/plan1. |
dsl_rom_path |
VFS path, for example rom/dsl/agent/plan1.json. |
dsl_rom_namespace |
ROM namespace. |
dsl_rom_name |
ROM name. |
dsl_rom_replay_log_count |
Replay entry count produced by the inner ROM pipeline. |
dsl_rom_replay_log_hash |
Replay hash produced by the inner ROM pipeline. |
These values allow a host to prove which DSL ROM was executed and which inner pipeline replay was folded into the outer execution.
6. HistoryROM
Chat history can be saved as immutable signed ROM material. Unlike DSL ROM, HistoryROM is not an executable capability. It is history material that can be loaded through the same VFS and ROM signature verification path as other Core ROM assets.
6.1 VFS Path and ROM Id
The implemented VFS path is:
rom/history/{namespace}/{name}.md
The registered ROM id is:
history://{namespace}/{name}
Both namespace and name must be single normalized path segments. Nested ROM ids such as history://agent/nested/demo are rejected.
6.2 Save and Load Flow
SaveHistoryAsRomAsync(namespace, name, records)\n -> normalize ordered chat records\n -> generate signed Markdown ROM\n -> write Markdown to VFS when missing\n -> reject if an existing path has different content\n -> load via RomLoader and verify signature\n -> register HistoryRomSnapshot in IHistoryRomRegistry
SaveMarkdownAsRomAsync can be used when a host already has signed Markdown HistoryROM content. LoadHistoryRomAsync optionally accepts an expected ROM hash and fails closed when VFS content does not match.
6.3 Stable Metadata Keys
| Key | Meaning |
|---|---|
history_rom_hash |
Verified SHA-256 ROM hash. |
history_rom_id |
ROM id, for example history://agent/demo. |
history_rom_path |
VFS path, for example rom/history/agent/demo.md. |
history_rom_namespace |
History ROM namespace. |
history_rom_name |
History ROM name. |
7. Failure Rules
The following conditions fail closed:
- Empty or non-object DSL JSON.
- Root node other than
Pipeline. - Unknown node type.
- Missing or invalid required fields.
- Negative
maxIterationsor negativetimeout. - Unknown capability names during compilation.
- Capability registry exceptions.
- Capability returning a successful null value.
- Invalid DSL pipeline value keys or values.
- DSL ROM hash mismatch.
- Re-registering different content for the same ROM capability.
- Malformed DSL ROM capability names.
- HistoryROM signature mismatch.
- HistoryROM expected hash mismatch.
- Re-registering different content for the same HistoryROM id.
- Malformed HistoryROM ids.
8. Host Integration Notes
Server/API hosts may allow an AI system to propose JSON DSL, but the host must validate and save it as DSL ROM before treating it as a reusable capability. Browser/WASM clients should call the server boundary rather than write trusted ROM directly.
Chat history exporters or scrapers should pass deterministic timestamps into IHistoryRomStore / HistoryRomStore instead of reading the current system time inside the ROM generation path. This keeps HistoryROM reproducible and makes the verified hash stable across replay.
For external modules, register an IDslCapabilityRegistry that routes names to assembly-referenced providers, process-backed adapters, or dsl:// ROM capabilities. The registry remains the boundary: Core does not know provider implementations.
As of v0.0.4, the public contract surface for this model lives in AIKernel.Abstractions.Dsl, AIKernel.Abstractions.History, AIKernel.Abstractions.Time, AIKernel.Dtos.Dsl, AIKernel.Dtos.History, and AIKernel.Dtos.Time. Core may keep Result-based internals, but public adapters should target these contract packages.
Changelog
- v0.0.4 (2026-06-04): Added standard DSL pipeline, DSL ROM, HistoryROM specification, and public contract package mapping.
architecture/18.DSL_PIPELINE_AND_ROM_SPEC.md