Skip to content

Axiom Graph Engine

The axiom module is Spry’s core engine that transforms markdown documents into executable, queryable graphs. Think of it as the translator between your markdown runbooks and Spry’s execution system—it reads your markdown structure and converts it into a network of interconnected nodes that can be analyzed, filtered, and executed.

When you write a runbook in markdown, you’re creating:

  • Hierarchical structure (headings and sections)
  • Executable code blocks (tasks)
  • Dependencies between tasks
  • Metadata and configuration

Axiom takes all of this and creates a directed graph where:

  • Every element (heading, code block, paragraph) becomes a node
  • Relationships (containment, dependencies) become edges
  • The entire document becomes queryable and executable

This graph representation enables Spry to:

  • Execute tasks in the correct order based on dependencies
  • Filter and select specific parts of your runbook
  • Generate different views (tree, runbook, custom)
  • Validate document structure and rules
lib/axiom/
├── edge/ # Graph edge rules - define how nodes connect
│ └── rule/ # Individual classification rules
├── fixture/ # Test fixtures for development
├── io/ # Input/output handling for resources
├── mdast/ # Markdown Abstract Syntax Tree utilities
├── projection/ # Different views of the same graph
├── remark/ # Remark plugin integration for parsing
├── text-ui/ # Terminal/CLI interfaces
├── web-ui/ # Web-based interfaces
├── graph.ts # Core graph data structures
└── mod.ts # Public module exports

At its heart, axiom represents your markdown as a directed graph:

Markdown Document:
# Deploy Application
## Prerequisites
```bash task setup
npm install
```
## Deploy
```bash task deploy --depends setup
npm run deploy
```
Becomes Graph:
┌─────────────────┐
│ Deploy App (h1) │
└────────┬────────┘
│ contained-in
┌────┴────┬────────────┐
│ │ │
┌───▼────┐ ┌──▼────┐ ┌───▼────┐
│ Prereq │ │ setup │ │ deploy │
│ (h2) │ │ task │ │ task │
└────────┘ └───────┘ └───┬────┘
│ depends
└────────→ setup

Key Types:

interface GraphNode {
id: string; // Unique identifier
type: string; // 'heading', 'code', 'paragraph', etc.
data: unknown; // Node-specific data
edges: GraphEdge[]; // Outgoing connections
}
interface GraphEdge {
type: string; // 'contained-in', 'depends', etc.
target: string; // Target node ID
}

Building a graph:

import { buildGraph } from "./graph.ts";
const markdownSource = `
# My Runbook
## Task 1
\`\`\`bash task1
echo "First task"
\`\`\`
`;
const graph = await buildGraph(markdownSource);
// graph now contains nodes and edges representing the document

Edge rules are the intelligence that determines how nodes connect. Each rule examines the document and creates specific types of edges.

RulePurposeExample
contained-in-headingCreates parent-child relationships based on heading hierarchyH2 is contained in H1
contained-in-sectionGroups content within sectionsCode blocks belong to their section
frontmatter-classificationParses YAML frontmatter at document topExtracts metadata, tags, config
node-dependencyResolves --depends flags in taskstask2 --depends task1 creates edge
nodes-classificationClassifies nodes by typeMarks code blocks as ‘task’ vs ‘content’
governanceValidates document structureEnsures required sections exist
section-frontmatterParses section-level metadataConfiguration per section

Example of how rules work:

---
project: my-app
---
# Setup
```bash install --depends check-env
npm install
```
# Deploy
```bash deploy --depends install
npm run deploy
```

Rules create:

  1. frontmatter-classification: Extracts project: my-app
  2. contained-in-heading: Links tasks to their headings
  3. nodes-classification: Marks code blocks as tasks
  4. node-dependency: Creates edges: install → check-env, deploy → install

A projection is a specific view or representation of the graph optimized for different purposes. The same graph can be projected multiple ways.

ProjectionPurposeUse Case
runbookTask execution view with dependenciesRunning tasks in correct order
treeHierarchical tree structureDisplaying document outline
flexibleCustomizable projectionBuilding custom views/filters
tree-textText-based tree renderingCLI display of structure
import { runbookProjection } from "./projection/runbook.ts";
const tasks = runbookProjection(graph);
// tasks is now an array of executable task objects
for (const task of tasks) {
console.log(`Task: ${task.identity}`);
console.log(` Dependencies: ${task.dependencies.join(", ")}`);
console.log(` Command: ${task.command}`);
}
// Output:
// Task: install
// Dependencies: check-env
// Command: npm install
// Task: deploy
// Dependencies: install
// Command: npm run deploy
import { treeProjection } from "./projection/tree.ts";
const tree = treeProjection(graph);
// Render as hierarchical structure
// Setup
// └─ install [task]
// Deploy
// └─ deploy [task]

The foundation of axiom. Defines the graph data structure and provides the main buildGraph() function that orchestrates the entire parsing pipeline.

Key exports:

  • buildGraph(markdown: string): Promise<Graph>
  • Graph and Node type definitions
  • Edge type definitions

Public API exports. This is what other modules import from axiom.

Contains individual rule implementations. Each file is a self-contained rule that knows how to create one type of edge.

FileWhat It Does
contained-in-heading.tsLinks content to their parent headings (H2 under H1, H3 under H2)
contained-in-section.tsGroups all content within document sections
frontmatter-classification.tsExtracts and parses YAML frontmatter at document start
governance.tsValidates document structure meets requirements
node-dependency.tsParses --depends flags and creates dependency edges
nodes-classification.tsDetermines node types (task, content, metadata)
section-frontmatter.tsHandles section-specific metadata blocks
section-semantic-id.tsGenerates human-readable IDs for sections
selected-nodes-classification.tsFilters nodes based on classification criteria

Utilities for working with Markdown Abstract Syntax Trees (MDAST). These are helper functions that extract information from AST nodes.

FilePurpose
code-frontmatter.tsExtracts frontmatter from code block info strings
data-bag.tsProvides metadata storage for nodes
node-content.tsGets textual content from any node type
node-issues.tsTracks validation issues and warnings
node-src-text.tsRetrieves original source text
statistics.tsCalculates document statistics (word count, task count)

Different ways to view and traverse the graph.

FileCreates
runbook.tsExecution-ready task list with dependency order
tree.tsGeneric hierarchical tree structure
tree-text.tsASCII/Unicode text rendering of trees
flexible.tsBase for custom projection implementations

Integration with the Remark markdown processor. These are plugins that run during parsing.

FileHandles
code-directive-candidates.tsIdentifies code blocks that are directives
doc-frontmatter.tsProcesses document-level frontmatter
import-placeholders-generator.tsCreates placeholders for external imports
import-specs-resolver.tsResolves and loads external markdown files
node-decorator.tsAdds metadata and IDs to nodes during parsing
spawnable-code-candidates.tsMarks code blocks that can be executed

Command-line interfaces for interacting with graphs.

FileProvides
cli.tsAxiom graph viewer CLI tool
runbook.tsMain runbook execution CLI

Input/output abstractions for reading resources.

import { MarkdownDoc } from "../markdown/fluent-doc.ts";
import { buildGraph } from "./graph.ts";
import { visit } from "unist-util-visit";
// Create markdown programmatically
const doc = new MarkdownDoc();
doc.h1("Database Migration");
doc.h2("Backup");
doc.codeTag("bash backup --descr 'Backup database'")`
pg_dump mydb > backup.sql
`;
doc.h2("Migrate");
doc.codeTag("bash migrate --depends backup")`
./run-migrations.sh
`;
// Build the graph
const graph = await buildGraph(doc.write());
// Query: Find all tasks
const tasks: string[] = [];
visit(graph.ast, "code", (node) => {
if (node.meta?.includes("task")) {
tasks.push(node.lang + " task");
}
});
console.log("Found tasks:", tasks);
// Output: Found tasks: ['bash task', 'bash task']

Example 2: Executing Tasks in Dependency Order

Section titled “Example 2: Executing Tasks in Dependency Order”
import { buildGraph } from "./graph.ts";
import { runbookProjection } from "./projection/runbook.ts";
import { executionPlan } from "../universal/task.ts";
const markdown = `
# Deployment Pipeline
\`\`\`bash build
npm run build
\`\`\`
\`\`\`bash test --depends build
npm test
\`\`\`
\`\`\`bash deploy --depends test
./deploy.sh
\`\`\`
`;
// Build graph
const graph = await buildGraph(markdown);
// Get task projection
const tasks = runbookProjection(graph);
// Generate execution plan (topological sort by dependencies)
const plan = executionPlan(tasks);
// Execute in order
for (const task of plan) {
console.log(`Executing: ${task.identity}`);
// await executeTasks([task], state);
}
// Output:
// Executing: build
// Executing: test
// Executing: deploy
import { buildGraph } from "./graph.ts";
import { runbookProjection } from "./projection/runbook.ts";
import { compileCqlMini } from "../markdown/notebook/cql.ts";
const graph = await buildGraph(myMarkdown);
const tasks = runbookProjection(graph);
// Create filter: only bash tasks with tag "deploy"
const filter = compileCqlMini("lang=bash AND tag=deploy");
const deployTasks = filter(tasks);
console.log("Deploy tasks:", deployTasks.map(t => t.identity));

Axiom uses markdown utilities for parsing and querying:

import { compileCqlMini } from "../markdown/notebook/cql.ts";
const filter = compileCqlMini("lang=python");
const pythonTasks = filter(tasks);

Task execution leverages axiom’s dependency graph:

import { executeTasks } from "../task/mod.ts";
import { executionPlan } from "../universal/task.ts";
const plan = executionPlan(runbookTasks);
await executeTasks(plan, executionState);

Web interfaces can render axiom graphs:

import { SqlPagePlaybook } from "../sqlpage/playbook.ts";
const playbook = SqlPagePlaybook.instance(project);
const webFiles = playbook.sqlPageFiles({
mdSources: ["Spryfile.md"]
});

Template interpolation works with axiom’s parsed structure:

import { runbookProjection } from "./projection/runbook.ts";
const tasks = runbookProjection(graph);
// Tasks contain interpolated values from environment
Terminal window
# Run all axiom tests
deno test lib/axiom/
# Run specific component tests
deno test lib/axiom/graph_test.ts
deno test lib/axiom/projection/
# Run with coverage
deno test --coverage=coverage/ lib/axiom/
deno coverage coverage/
  1. Axiom is a transformer: Markdown → Graph → Executable structure
  2. Rules define relationships: Edge rules create the intelligence in the graph
  3. Projections enable flexibility: One graph, many views for different purposes
  4. Integration is key: Axiom connects markdown authoring to task execution