1 unstable release
| new 0.1.0 | Mar 9, 2026 |
|---|
#17 in #refactoring
170KB
4.5K
SLoC
Draxl
Draxl is an agent-native source language for deterministic, high-volume concurrent code editing.
It makes syntax identity, ordering, and attachment explicit in the source itself, so tools can apply semantic operators over stable nodes instead of patching text spans.
This repository contains the first Draxl language profile, which targets Rust
through .rs.dx files and deterministic lowering to Rust.
Under the hood, Draxl is AST-native: the source carries the structure, identity, and attachment data that replay, merge, audit, and lowering workflows need.
agent edits -> semantic ops over stable ids -> validated Draxl -> canonical .rs.dx -> Rust
The .rs.dx extension is intentional. .dx names the Draxl layer and .rs
names the current language profile. That keeps the profile-specific surface
distinct from the higher-level source model and leaves room for future profiles
such as .go.dx, .ts.dx, and .py.dx.
What This Unlocks
Draxl is more than a merge-friendly source format. Stable node IDs, ranks, anchors, and structured annotations create room for higher-level program control that ordinary text files do not represent cleanly.
-
Node-level ownership and policy. Functions, types, fields, match arms, and statements can carry explicit owners, required reviewers, stability levels, or security policy tags. Those rules stay attached to the syntax they govern instead of living at file scope or drifting with line edits and refactors.
-
Durable review, provenance, and audit state. Approvals, benchmark results, security findings, and agent provenance can attach to specific nodes and survive surrounding changes that preserve identity. Tools can then invalidate evidence precisely when the underlying semantic target changes.
-
Machine-readable contracts and capability summaries. Nodes can carry structured annotations for effects, resource access, unsafe boundaries, API guarantees, or other higher-level constraints. That gives agents and tooling a durable substrate for reasoning that is stronger than comments and cheaper than re-deriving intent from raw source.
Example Draxl source
@m1 mod demo {
@d1 /// Add one to x.
@f1[a] fn add_one(@p1[a] x: @t1 i64) -> @t2 i64 {
@c1 // Cache the intermediate value.
@s1[a] let @p2 y = @e1 (@e2 x + @l1 1);
@s2[b] @e3 y
}
}
The same file lowers deterministically to ordinary Rust:
mod demo {
/// Add one to x.
fn add_one(x: i64) -> i64 {
// Cache the intermediate value.
let y = (x + 1);
y
}
}
The metadata prefix stays compact:
@id[rank]->anchor
@idgives the next supported node a stable identity[rank]orders siblings inside ranked slots->anchorattaches detached docs or comments to an existing sibling id
Doc and line comments attach implicitly to the next semantic sibling when an explicit anchor is absent.
Why now
Draxl is a bet on a near-future software workflow.
As inference gets cheaper, code generation becomes abundant and integration becomes the bottleneck. Repositories will need to absorb far more agent-produced edits, many more concurrent branches and PRs, and long-lived forks maintained for specific users, products, and deployments.
Line-based source and text diffs are a poor fit for that environment. Byte ranges create false conflicts, rebase churn, and weak replayability because syntax identity is positional and tooling has to reconstruct structure from surrounding text.
Draxl makes syntax identity explicit in the source itself. Tools can patch, replay, merge, and audit code semantically by addressing stable node IDs and ranked slots instead of guessing intent from lines and spans.
Why Draxl
- stable node ids let tools target syntax directly instead of guessing by line and column
- ranks make ordered inserts explicit, so concurrent edits do not depend on textual history
- anchors make detached docs and comments attach deterministically across replay and merge
- canonical printing keeps human-readable source and machine output stable
- the current Rust profile preserves compatibility with the existing Rust toolchain through deterministic lowering
| Concern | Text diffs | Draxl |
|---|---|---|
| Edit target | byte ranges, lines, spans | stable node ids |
| Ordered insert | textual position | ranked slot under a parent |
| Comment/doc attachment | proximity heuristics | explicit anchors |
| Replay and audit | surrounding text | semantic ops over node ids |
| Branches and forks | repeated rebase repair | semantic replay by identity |
| Merge conflicts | overlapping text | overlapping semantic regions |
Semantic patching
Draxl treats semantic patch operators as first-class infrastructure for agent-native editing, not as a convenience wrapper around text replacement.
A text diff says "these bytes changed here." A Draxl patch says "replace node
@e2", "insert into @f1.body[ah]", or "attach @d2 to @f1." That is a
better execution format for replay, merge, and audit because the patch names
the semantic target directly instead of depending on nearby lines to rediscover
intent.
Text diffs are good for human review, but they are a weak machine interface for structural edits:
- nearby inserts, formatting, and unrelated rewrites can invalidate context or create false conflicts
- ordered inserts, moves, and comment attachment have to be reconstructed from text layout
- replay across branch stacks and long-lived forks depends on matching byte neighborhoods, not stable program identity
Draxl patch ops target the program tree itself:
- replace a node body while preserving outer identity
- insert into a ranked slot under a parent
- put a new occupant into a single-child slot
- move or delete a specific node
- attach docs and comments explicitly
- set or clear scalar fields such as names, operators, and semicolon state
Same change, two representations:
Text diff:
@@
- y
+ let z = (y + 1);
+ z
Semantic ops:
insert @f1.body[ah]: @s3 let @p3 z = @e4 (@e6 y + @l2 1);
replace @e3: @e5 z
The text diff relies on the exact surrounding lines. The semantic ops say what
changed: insert a new statement into @f1.body and replace the expression node
@e3. As long as those ids survive, the edits remain targetable through
formatting, nearby inserts, and many refactors.
The structured Rust API already uses this semantic model. The textual notation shown here mirrors that model, but it is not parsed by the CLI yet.
The same model can also express edits that are awkward in unified diffs, such
as attach @d2 -> @f1, move @s2 -> @f1.body[ah], or set @f1.name = run.
The current path-op subset is intentionally narrow. It supports scalar fields
such as @f1.name, @d1.text, @e7.op, and @s2.semi, not arbitrary source
text replacement.
Concurrent edit example
Starting block:
@s1[a] @e1 fetch();
@s2[am] @e2 log();
@s3[b] @e3 validate();
If two agents express their edits as text diffs, both changes land in the same textual neighborhood:
Agent A:
@@
fetch();
+trace();
log();
validate();
Agent B:
@@
fetch();
-log();
+audit();
validate();
Those diffs both depend on the same hunk around log();, so overlap is likely
even though one change is an insertion and the other is a replacement.
Draxl keeps the operations separate:
Agent A:
insert @f1.body[ah]: @s4 @e4 trace();
Agent B rewrites expression @e2:
replace @e2: @e2 audit()
Merged result:
@s1[a] @e1 fetch();
@s4[ah] @e4 trace();
@s2[am] @e2 audit();
@s3[b] @e3 validate();
The edits compose cleanly because they target different semantic regions: one
addresses the ranked body slot and the other replaces node @e2.
What works today
Draxl currently implements the Rust profile through .rs.dx files
with parsing, validation, canonical formatting, JSON dumping, Rust lowering,
and bootstrap semantic patch application.
The current milestone supports:
- parsing the bootstrap Rust profile into a typed Draxl IR
- validating ids, ranks, anchors, sibling attachment, and ranked-slot uniqueness
- printing canonical Draxl source with deterministic ordering
- re-parsing canonical output while preserving semantics
- dumping deterministic JSON for the IR
- lowering the current profile to ordinary Rust
- applying semantic patch ops over ids, schema-defined slots, attachments, and the current scalar path subset: names, trivia text, operators, and statement semicolon state
Architecture
The current workspace is intentionally split by responsibility:
draxl: public facade over the workspace cratesdraxl-ast: typed AST and metadata modeldraxl-parser: lexer and parser for the Draxl surface syntaxdraxl-validate: structural validation for ids, ranks, and anchorsdraxl-printer: canonicalization and source printingdraxl-lower-rust: lowering from validated Draxl to ordinary Rustdraxl-patch: structured patch operators over ids, profile-defined slots, attachments, and scalar pathsdraxl-cli: command-line entry point
source text
-> draxl-parser
-> draxl-validate
-> draxl-printer / draxl-lower-rust / draxl-patch
-> draxl facade and draxl CLI
The core model is profile-agnostic. This repository currently implements the
Rust profile and lowers validated .rs.dx input to Rust source.
More detail:
Library crate
The root crate is draxl: a thin public facade over the parser, validator,
printer, lowering, and patch APIs.
use draxl::{format_source, lower_rust_source, parse_and_validate};
let file = parse_and_validate("@m1 mod demo {}") ?;
let formatted = format_source("@m1 mod demo {}") ?;
let lowered = lower_rust_source(
"@m1 mod demo { @f1[a] fn run() { @s1[a] @e1 work(); } }",
) ?;
Examples
The examples/ directory is a guided tour of the current Rust profile:
examples/01_add.rs.dxis the smallest end-to-end example: stable ids, ranked params and statements, implicit doc/comment attachment, and lowering to ordinary Rustexamples/02_shapes.rs.dxshows item-level ordering over astructand anenum, including ranked fields and variantsexamples/03_match.rs.dxexercisesmatchexpressions, ranked arms, guard expressions, binary<, and unary minusexamples/04_ranks.rs.dxisolates lexicographic ranks such asa,am, andbto show how deterministic insertion order works inside a blockexamples/05_use_tree_group.rs.dxcovers groupedusetrees,selfand glob imports, qualified paths, and a multi-parameter function
Try it
cargo run -p draxl-cli -- parse examples/01_add.rs.dx
cargo run -p draxl-cli -- fmt examples/01_add.rs.dx
cargo run -p draxl-cli -- dump-json examples/01_add.rs.dx
cargo run -p draxl-cli -- validate examples/01_add.rs.dx
cargo run -p draxl-cli -- lower-rust examples/01_add.rs.dx
License
Licensed under MIT OR Apache-2.0.