Skip to content

Commit dda2794

Browse files
committed
feat: preserve pre-heal output in NodeTrace for audit compliance
- NodeTrace gains pre_heal_output: Option<Value> — the original failing output before LLM healing, preserved for compliance audits - Field is only set when healing actually occurred (nil otherwise) - Aether Lens detail panel shows Pre-Heal Output in amber when present - README: note custom namespace registries on roadmap, update count to 37 intents
1 parent 47107f3 commit dda2794

3 files changed

Lines changed: 33 additions & 3 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ See [`docs/benchmark.md`](docs/benchmark.md) for the full token-cost comparison.
7676
- **5-tier safety model** — L0 (pure math) → L4 (system root). Nodes above your threshold are blocked before execution.
7777
- **Typed state ledger** — outputs are written to an immutable address space (`$0xADDR`) with type validation on every write.
7878
- **Parallel DAG execution** — independent nodes run concurrently via `tokio::spawn`; dependencies resolved with Kahn's topological sort.
79-
- **ASL registry**32 canonical intents (`std.io.*`, `std.proc.*`, `std.ml.*`, `std.sec.*`, etc.) with safety and language defaults.
79+
- **ASL registry**37 canonical intents (`std.io.*`, `std.proc.*`, `std.ml.*`, `std.sec.*`, etc.) with safety and language defaults. Unknown `std.*` intents are hard errors; custom namespaces (`myorg.etl.*`) are accepted. Custom namespace registries with org-defined safety rules are on the roadmap (`--registry myorg.json`).
8080
- **Self-healing RETRY**`ASSERT expr OR RETRY(3)` sends failing code + assertion to Claude for repair. Requires your own `ANTHROPIC_API_KEY`.
8181
- **English Toggle**`aether gen "description"` turns plain English into a `.ae` program via Claude. Requires your own `ANTHROPIC_API_KEY`.
8282
- **MCP server**`aether-mcp` exposes validate/execute/inspect as tools for Claude Code and other MCP-compatible clients.

lens/index.html

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,11 @@ <h3>Node Detail</h3>
683683
? trace.heal_log.map(h => `<div class="heal-log-item">${esc(h)}</div>`).join('')
684684
: `<span style="color:var(--muted)">none</span>`;
685685

686+
// Pre-heal output (compliance audit field)
687+
const preHealStr = trace.pre_heal_output
688+
? JSON.stringify(trace.pre_heal_output, null, 2)
689+
: null;
690+
686691
// Output
687692
const outputStr = JSON.stringify(trace.output, null, 2);
688693

@@ -727,7 +732,12 @@ <h3>Node Detail</h3>
727732
<div class="detail-section">
728733
<div class="detail-label">Heal Log</div>
729734
${healHtml}
730-
</div>`;
735+
</div>
736+
${preHealStr ? `
737+
<div class="detail-section">
738+
<div class="detail-label" style="color:var(--amber)">Pre-Heal Output</div>
739+
<pre class="json-view" style="border-left:2px solid var(--amber);padding-left:6px">${esc(preHealStr.length > 500 ? preHealStr.slice(0, 500) + '\n…' : preHealStr)}</pre>
740+
</div>` : ''}`;
731741
}
732742

733743
// ── Utility ───────────────────────────────────────────────────────────────────

src/executor.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ pub struct NodeTrace {
4949
/// Log of self-healing attempts (populated on RETRY)
5050
#[serde(skip_serializing_if = "Vec::is_empty")]
5151
pub heal_log: Vec<String>,
52+
/// Output produced by the first (pre-heal) execution attempt, if healing occurred.
53+
/// Preserved for audit compliance — the post-heal output is in `output`.
54+
#[serde(skip_serializing_if = "Option::is_none")]
55+
pub pre_heal_output: Option<serde_json::Value>,
5256
}
5357

5458
#[derive(Debug, Serialize, Deserialize)]
@@ -932,6 +936,7 @@ async fn run_node_with_retry(
932936

933937
let mut current_code = node.code.clone();
934938
let mut heal_log: Vec<String> = Vec::new();
939+
let mut pre_heal_output: Option<serde_json::Value> = None;
935940

936941
// Initial execution
937942
let mut last_result = execute_single_node(node, &current_code, ledger, config, registry).await;
@@ -970,6 +975,11 @@ async fn run_node_with_retry(
970975

971976
let actual_output = failed_trace.output.clone();
972977

978+
// Capture the original pre-heal output on the first failure only
979+
if attempt == 0 {
980+
pre_heal_output = Some(actual_output.clone());
981+
}
982+
973983
heal_log.push(format!(
974984
"[attempt {}] validation failed on: {}",
975985
attempt + 1,
@@ -1002,14 +1012,16 @@ async fn run_node_with_retry(
10021012
}
10031013
}
10041014

1005-
// Attach heal_log to whichever trace we're returning
1015+
// Attach heal_log and pre_heal_output to whichever trace we're returning
10061016
match last_result {
10071017
Ok(mut t) => {
10081018
t.heal_log = heal_log;
1019+
t.pre_heal_output = pre_heal_output;
10091020
Ok(t)
10101021
}
10111022
Err(mut t) => {
10121023
t.heal_log = heal_log;
1024+
t.pre_heal_output = pre_heal_output;
10131025
Err(t)
10141026
}
10151027
}
@@ -1094,6 +1106,7 @@ async fn execute_single_node(
10941106
asl_match,
10951107
asl_warnings,
10961108
heal_log: vec![],
1109+
pre_heal_output: None,
10971110
});
10981111
}
10991112

@@ -1112,6 +1125,7 @@ async fn execute_single_node(
11121125
asl_match,
11131126
asl_warnings,
11141127
heal_log: vec![],
1128+
pre_heal_output: None,
11151129
});
11161130
}
11171131

@@ -1132,6 +1146,7 @@ async fn execute_single_node(
11321146
asl_match,
11331147
asl_warnings,
11341148
heal_log: vec![],
1149+
pre_heal_output: None,
11351150
});
11361151
}
11371152
Err(e) => {
@@ -1148,6 +1163,7 @@ async fn execute_single_node(
11481163
asl_match,
11491164
asl_warnings,
11501165
heal_log: vec![],
1166+
pre_heal_output: None,
11511167
});
11521168
}
11531169
_ => (),
@@ -1212,6 +1228,7 @@ async fn execute_single_node(
12121228
asl_match,
12131229
asl_warnings,
12141230
heal_log: vec![],
1231+
pre_heal_output: None,
12151232
});
12161233
}
12171234
}
@@ -1273,6 +1290,7 @@ async fn execute_single_node(
12731290
asl_match,
12741291
asl_warnings,
12751292
heal_log: vec![],
1293+
pre_heal_output: None,
12761294
})
12771295
} else {
12781296
Ok(NodeTrace {
@@ -1287,6 +1305,7 @@ async fn execute_single_node(
12871305
asl_match,
12881306
asl_warnings,
12891307
heal_log: vec![],
1308+
pre_heal_output: None,
12901309
})
12911310
}
12921311
}
@@ -1302,6 +1321,7 @@ async fn execute_single_node(
13021321
asl_match,
13031322
asl_warnings,
13041323
heal_log: vec![],
1324+
pre_heal_output: None,
13051325
}),
13061326
}
13071327
}

0 commit comments

Comments
 (0)