AIKernel.NET
version: 0.0.4 / status: Implementation / edition: Draft / published: 2026-06-04 / updated: 2026-06-04

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. LoopUntil also requires maxIterations.
  • Unknown node types, unknown capabilities, malformed ROM names, and invalid
  • node fields fail closed.

  • Map projections do not create replay nodes. Executed DSL nodes and loop
  • transitions are observed through ResultStep replay entries.

  • Suspend is a deterministic stop point, not background waiting.

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 maxIterations or negative timeout.
  • 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.
Source: architecture/18.DSL_PIPELINE_AND_ROM_SPEC.md