A compact, machine-primary notation for cross-session, cross-agent state and instructions.
AXL is a lightweight text format designed to be loaded by AI agents, LLMs, and orchestration harnesses as a persistent memory and instruction layer. It is not a replacement for Markdown — it is the layer underneath: a structured, token-efficient file format that agents read, update, and hand off between sessions without human intervention.
Most AI coding tools and agent harnesses recommend Markdown files for persisting project state, instructions, and memory across sessions. Markdown is excellent for human readability. It is not optimized for machine consumption — it is verbose, structurally ambiguous, and costly in tokens when included in context repeatedly.
AXL addresses this with a purpose-built format:
- Token-efficient — approximately 40% fewer tokens than equivalent structured Markdown
- Machine-primary — unambiguous structure; no prose parsing required
- Self-describing — a single
axl.specfile is sufficient for any agent to read, write, and validate.axlfiles - Cross-agent — designed for handoffs between different agents, models, and sessions
- Human-legible — a developer can glance at an
.axlfile and understand the project state; they just don't need to write it themselves
Load the spec once. From that point on, any agent understands the format.
Load the AXL specification from ./axl.spec, then load ./my-project.axl.
Resume from the current state and continue work.
That's the entire integration surface. No libraries, no parsers, no schemas to install.
| Extension | Purpose |
|---|---|
.axl |
Full project file — state, plan, instructions, log |
.axlp |
Patch file — sparse updates applied to a target .axl |
.axlt |
Type library — reusable field schemas, importable across projects |
An .axl file is composed of blocks. Each block begins with @BLOCKNAME and ends at the next block or end of file. Blocks contain typed key-value pairs, structured lists, and sub-sections.
@meta
id: my-project
v: 0.3
created: @20250510
modified: @20250510T1435
agents: claude-sonnet-4
tags: api|typescript
>>sessions
claude-sonnet-4: ses_abc123xyz
@state
status: WIP
focus: implementing authentication middleware
blockers: ~
next_act: add JWT verification to /api/* routes
>>mem
port: #3000
db_url: :postgres://localhost:5432/mydb
@plan
setup:DONE:P1:Initialize repository and toolchain
auth:WIP:P0:JWT authentication middleware
tests:TODO:P1:Integration test suite
M_mvp:TODO:~:MVP — core API functional
@ctx
> TypeScript strict mode; no implicit any
> write tests before marking any task DONE
? task:blocked => set @state.status BLOCK and halt
@log
@20250510T1400:claude-sonnet-4:ACT:repository initialized; dependencies installed
AXL uses sigil prefixes to eliminate redundant type declarations:
| Sigil | Type | Example |
|---|---|---|
| (none) | plain text | key: some text |
: |
URL | key: :https://example.com |
# |
number | key: #42 |
? |
boolean | key: ?1 |
@ |
date/datetime | key: @20250510T1435 |
$ |
ID reference | key: $other-task-id |
* |
environment variable name | key: *DATABASE_URL |
~ |
null / empty | key: ~ |
!NOW |
computed timestamp | key: !NOW (agent replaces on write) |
Pipe (|) separates list elements inline: tags: api|typescript|postgres
| Block | Required | Description |
|---|---|---|
@meta |
yes | Project identity, version, modification tracking |
@state |
yes | Current session memory, focus, blockers, agent scratchpad (>>mem) |
@plan |
no | Task list in id:STATUS:PRIORITY:title[:notes] format |
@ctx |
no | Behavioral directives, conditional rules, one-time instructions |
@log |
yes | Append-only structured event log |
@done-ctx |
no | Append-only audit log of executed one-time @ctx directives |
@ref |
no | URLs, filesystem paths, environment variable names, glossary |
| Block | Description |
|---|---|
@import |
Read-only references to other .axl or .axlt files |
@mount |
Read-write references to shared .axl files (with @lock guard) |
@export |
Named symbols exposed to files that import this one |
| Block | Description |
|---|---|
@hook |
Event-triggered directives (task:done, session:end, err:raised, …) |
@type |
Custom field schemas for @state>>mem validation |
@diff |
Sparse patch operations for .axlp files |
@fence |
Verbatim content blocks (system prompts, code snippets, templates) |
@lock |
Ephemeral concurrency guard for shared files |
@err |
Structured error and conflict log, separate from @log |
@logref |
External log file reference and rotation policy |
@prompt |
Compressed human instruction, produced by axlc-mcp |
Note on harness-specific config: AXL deliberately has no block for harness instructions (Cursor rules,
CLAUDE.md, Aider flags, etc.). Those belong in each harness's own native config files — they are read once at session start by one harness, so storing them in a shared project file would burn tokens on every context load for zero benefit to any other consumer. If you need a single source of truth that generates those native files, that's a separate tooling concern outside the AXL format.
Agents populate @meta>>sessions from environment variables. Each harness exposes its session context differently — check in the order listed and use the first available value. If none is available, omit the entry entirely.
| Harness | Environment variable | Status |
|---|---|---|
| Codex CLI | $CODEX_THREAD_ID |
✅ Confirmed |
| Claude Code | $CLAUDE_SESSION_ID |
$TERM_SESSION_ID as fallback |
| Cursor | — | ❌ Not exposed; omit entry |
| Aider | — | ❌ Not exposed; omit entry |
| Any CLI on macOS | $TERM_SESSION_ID |
This table will grow as more harnesses are tested. If you discover a session ID variable for an unlisted harness, the spec's >>sessions section is the canonical place to record it.
Tasks in @plan use colon-delimited fields:
id:STATUS:PRIORITY:title[:notes]
Status tokens: TODO WAIT WIP DONE SKIP FAIL BLOCK REVIEW HOLD
Priority tokens: P0 (critical) P1 (high) P2 (normal) P3 (low)
Milestones use M_ prefix and omit priority: M_beta:WIP:~:Beta release
Tasks are grouped with >>SECTIONNAME lines. Agents update status in-place and never delete or reorder task lines.
> DIRECTIVE — always follow
? CONDITION => ACTION — conditional; evaluated before each action
! DIRECTIVE — one-time; agent appends to @done-ctx on execution, then skips on repeat
- NOTE — informational; agent reads but does not act
One-time directives (!) are never mutated in @ctx. Instead, agents append the executed directive verbatim to @done-ctx with a timestamp and agent ID. On subsequent sessions, agents check @done-ctx before executing any ! directive — if it's already there, it's skipped. This keeps @ctx immutable and makes one-time execution auditable.
Built-in condition tokens: task:done task:blocked task:added state:changed session:start session:end file:written import:missing err:raised lock:conflict
@import
team: ./team.axl::ref
types: ./project.axlt
Access imported content in this file with $alias::block::item-id. Missing imports write an ERR: entry to @log and continue — imports are non-fatal.
@mount
shared: ./team-state.axl
Mounted files represent shared mutable state. Agents must check @lock before writing. On conflict, agents write to @err and await user resolution rather than overwriting.
@export
api-version: #2
deploy-url: $state>>mem>>staging_url
Importers access these as $their-alias::export::api-version.
For large projects where only a few fields change per session, agents can write a sparse patch file instead of rewriting the full .axl:
@diff
target: ./project.axl
base_modified: @20250510T1435
STATUS plan::stripe-wh: DONE
NOTE plan::stripe-wh: HMAC fixed — secret was URL-encoded in Railway
STATUS plan::stripe-refund: WIP
SET state::focus: implementing refund endpoint
SET meta::modified: !NOW
APPEND log: !NOW:claude-sonnet-4:ACT:stripe-wh resolved; moving to refund
Patches are applied only when base_modified matches the target's current modified field, preventing stale overwrites.
Embed raw content that the parser should not interpret — system prompts, configuration fragments, code templates:
@fence sys-prompt txt
You are a senior backend engineer. Always use TypeScript strict mode.
Never use `any`. Prefer explicit return types on all functions.
@fence/sys-prompt
React to agent lifecycle events without relying on the agent remembering:
@hook
task:done => > append ACT: entry to @log with task id and completion time
task:blocked => > set @state.status BLOCK and update @state.blockers
session:end => > write NOTE: summary of all actions taken this session
err:raised => > set @state.status BLOCK and halt further actions
Approximate comparison for a mid-sized project file (18 tasks, current state, 10 log entries):
| Format | Tokens (approx.) |
|---|---|
| Equivalent structured Markdown | ~680 |
| AXL v0.1 | ~420 |
| AXL v0.2 | ~310 |
Savings increase with project size. @state>>mem and @plan are the highest-density sections; @log grows linearly but remains compact due to the colon-delimited format.
axl.spec # Canonical specification — the only file agents need to load
README.md # This file
prompts.md # Ready-to-use prompts for agents and harnesses
examples/
project.axl # Full example project file
patch.axlp # Example patch file
types.axlt # Example type library
team.axl # Example shared reference file
The current spec version is 0.3. The version is declared in @meta:
@meta
v: 0.3
v0.3 adds @meta>>sessions — an optional sub-section for agents to record their session ID alongside their model ID. Backward compatible: v0.2 files without >>sessions are valid and parse cleanly.
Agents parsing v0.1 files must accept the older pipe-delimited plan and log formats and must not silently upgrade format on rewrite unless explicitly instructed. Unknown blocks must always be preserved verbatim — forward compatibility is non-negotiable.
Agents are the primary writers. Humans define intent; agents maintain state. The format is optimized for machine generation and parsing, not human authoring.
A single spec file is sufficient. No runtime dependencies, no installed libraries. Any LLM that can read axl.spec can immediately produce valid .axl files.
Append-only logs, in-place task updates. @log and @err are append-only. @plan task statuses update in-place. Neither is ever deleted. This creates a reliable audit trail across sessions and agents.
Secrets never appear as values. Secret values are always referenced by environment variable name using the * sigil. key: *DATABASE_URL is valid. key: postgres://user:pass@host/db is not — use a :url sigil pointing to a non-secret URL, or store the credential name only.
Unknown content is preserved. Agents must pass through any block or field they do not recognize. This ensures files written by future spec versions remain intact when processed by agents trained on earlier versions.
AXL relies on a model's ability to follow structured format rules consistently across a session. Compatibility scales roughly with model size and instruction-following capability.
| Model tier | Examples | AXL suitability |
|---|---|---|
| Large cloud models | Claude Sonnet/Opus, GPT-4o, Gemini 2.5 Pro | ✅ Excellent — reliable format adherence, handles long sessions well |
| Mid local (14B) | Qwen 2.5 Coder 14B, Qwen 3 14B | ✅ Good — solid instruction following; occasional drift on long sessions |
| Mid local (7–9B) | Qwen 2.5 Coder 7B, Qwen 3 9B | |
| Small local (3–4B) | Gemma4 E4B, Qwen 2.5 3B | !NOW and format drift; guardrail prompt helps |
| Tiny (<3B) | Phi-3 mini, Gemma 2B | ❌ Not recommended — insufficient instruction-following for reliable AXL output |
Known issues with smaller models:
- Writing literal
!NOWto disk instead of the actual datetime — see Section 0 of the spec and the guardrail prompt inprompts.md - Treating
@logas editable rather than append-only - Wrapping
.axloutput in markdown code fences
Mitigations for smaller models:
- Always prepend the guardrail prompt from
prompts.mdbefore any AXL session - Keep sessions short and focused — drift increases with context length
- Use the MCP pipeline (
axlc-mcp) where the system prompt is injected fresh on every call, bypassing session drift entirely - For the
convert_markdowntool specifically, use themodeloverride parameter to route to a larger model even if your default is small
MCP pipeline note: The axlc-mcp server injects the AXL spec as a system prompt on every single call. This means model drift across a long session is irrelevant — each call is stateless and spec-aware. For production use, the MCP pipeline is more reliable than CLI chat with any model size.
MIT