Skip to content

Commit ae98ee1

Browse files
Phase H.4: safe keyword — runtime self-healing as user syntax
examples/self_healing_h4.omc — the user can now DECLARE self-healing intent in source code, not just rely on the compiler to detect it. H.1-H.3 all worked on STATIC bugs. H.2's divide-by-singularity check only fires when the divisor is a LITERAL the compiler can inspect at compile time. Variables don't trigger it — and the variable case is where the bug actually ships in real code. H.4 surfaces a new keyword `safe` that opts an expression into runtime self-healing semantics. The parser recognises `safe EXPR` and wraps it as ["SAFE_EXPR", inner]. The encoder rewrites SAFE_EXPR containing a BIN / to a CALL_BUILTIN safe_divide, UNCONDITIONALLY — regardless of whether the divisor is a literal or a variable. Demo 3 is the headline. Same function in Demo 2 (no `safe`) and Demo 3 (`safe`): fn compute(count, mod) { return safe count / mod; <-- new in Demo 3 } print(compute(144, 0)); <-- compute on the singular case Without `safe`: would crash on mod=0. With `safe`: returns 144 (safe_divide → fold_escape(0)=1 → 144/1 = 144). One-keyword annotation flips a runtime crash into a finite answer on attractor. No `if mod == 0` boilerplate. The static healer (H.2) and the user-declared `safe` (H.4) are COMPLEMENTARY, not redundant. Same primitive (safe_divide); different trigger conditions. Demo 4 stitches all three Phase H stages together: token-level recovery (missing SEMI), AST-level healing (compue → compute typo, 7 → 8 harmonic), and user-declared safe semantics on a dynamic divisor. Output: 8 (Fibonacci attractor). The bigger context: for LLM-generated code, failures cluster around typos, off-by-one constants, and unguarded edge cases — exactly the three classes Phase H handles. Self-healing as a property of the target language reduces the defensive-coding burden on the generator. What's not yet done: - `safe` only meaningfully rewrites BIN /. Other expressions wrapped in `safe` are no-ops, reserving the slot for future runtime guards. - Indentation-aware brace placement (H.3.1). - The "stuck" and "exhausted" iteration outcomes remain unexercised; designing a meaningful demo is future work. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 4c2256b commit ae98ee1

2 files changed

Lines changed: 2288 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,69 @@ All notable changes to OMNIcode will be documented in this file.
44

55
## [Unreleased]
66

7+
### Added (Phase H.4: `safe` keyword — runtime self-healing as user syntax, 2026-05-14)
8+
9+
🎯 **`examples/self_healing_h4.omc` — the user can now DECLARE self-healing intent in source code, not just rely on the compiler to detect it.**
10+
11+
H.1–H.3 all worked on STATIC bugs: things the compiler could detect by inspecting tokens or AST nodes without running the program. The hard case in real code is **dynamic singularities**`x / count` where `count` could be zero at runtime for inputs the author didn't anticipate. The static healer's divide-by-singularity check (H.2) only fires when the divisor is a literal. Variables don't trigger it. The bug ships.
12+
13+
H.4 surfaces a new keyword `safe` that lets the user opt expressions into runtime self-healing semantics:
14+
15+
```omc
16+
fn compute(count, mod) {
17+
return safe count / mod;
18+
}
19+
print(compute(144, 3)); // 48 — normal division
20+
print(compute(144, 0)); // 144 — fold_escape catches the zero divisor
21+
```
22+
23+
The parser recognises `safe EXPR` and wraps it as `["SAFE_EXPR", inner]`. The encoder rewrites `SAFE_EXPR` containing a `BIN /` to a `CALL_BUILTIN safe_divide` **unconditionally** — regardless of whether the divisor is a literal or a variable. The compile-time healer (H.2) and the runtime user-intent declaration (H.4) are complementary:
24+
25+
| Trigger | Active when | Catches |
26+
|---------|-------------|---------|
27+
| H.2 static healer | divisor is a literal `0` (or near-zero) | Obvious compile-time bugs |
28+
| H.4 `safe` keyword | user explicitly wrote `safe` | Dynamic divisors at runtime |
29+
30+
Both rewrite to the same primitive (`safe_divide`). The difference is the trigger.
31+
32+
#### Four demos
33+
34+
**Demo 1** (H.2 regression) — `89 / 0`. Static healer fires (literal `0`). Rewritten to `safe_divide(89, 0)`. Output: 89. Unchanged from H.2.
35+
36+
**Demo 2** (baseline, no `safe`) — `return count / mod;` with mod variable. Compiles to bare `/`. Runs with `mod = 3` so no crash, but the bug is shipping. Output: 48.
37+
38+
**Demo 3** (**the headline**) — same shape as Demo 2 but with `safe count / mod`. Compiler unconditionally rewrites to `safe_divide(count, mod)`. Two calls:
39+
- `compute(144, 3) → 48` (normal division)
40+
- `compute(144, 0) → 144` (**runtime crash converted to finite answer on attractor**)
41+
42+
The one-keyword annotation flipped a runtime crash into a working program. No `if mod == 0` boilerplate.
43+
44+
**Demo 4** (integrated) — five things in one source:
45+
- Token-level: missing SEMI between `h target = 7` and `print(...)`.
46+
- AST-level (H.1): `compue` typo → `compute` (edit distance 1).
47+
- AST-level (H.1): `7` close-miss Fibonacci → `8` (|Δ|=1).
48+
- AST-level (H.2): the `numerator / divisor` is dynamic, so H.2's static check DOESN'T fire (correctly — no compile-time signal).
49+
- H.4: the user wrote `safe`, so the division is rewritten to `safe_divide` at encode time.
50+
51+
Final: `compute(8, 0) → safe_divide(8, 0) → fold_escape(0)=1 → 8/1 → 8`. All three Phase H stages contributed; all converged; the program lands on a Fibonacci attractor.
52+
53+
#### The bigger picture (LLM-generated code)
54+
55+
For language-model-generated programs, the failures cluster around three classes:
56+
1. Typos and naming drift (variables, function names).
57+
2. Off-by-one numeric constants (loop bounds, array sizes).
58+
3. Unguarded edge cases (division, indexing, null derefs).
59+
60+
Phase H handles all three. A self-healing target language reduces the burden on the generator: it doesn't have to write defensive boilerplate because the language's compiler does the defense automatically — partly at compile time (static healer), partly at user-declared opt-in (`safe`), partly with primitive operations that fold_escape singularities at runtime (`safe_divide`).
61+
62+
This is a real architectural difference from conventional target languages. Most existing autocomplete/heal tooling lives OUTSIDE the language (IDE plugins, linters in a different language). H.1–H.4 live INSIDE OMC, reusing the same lex/parse/encode/execute machinery the rest of the language uses, all sitting on the Phase O φ-math substrate.
63+
64+
#### What still isn't done
65+
66+
- `safe` currently only meaningfully rewrites BIN `/`. Other expressions wrapped in `safe` encode as their inner form (no-op), reserving the slot for future runtime guards (fold_escape on function-call return values, value_danger threshold on arithmetic chains).
67+
- Indentation-aware brace placement (H.3.1) — still naive append-at-EOF.
68+
- The `stuck` and `exhausted` outcomes of the iteration loop remain unexercised. Designing a demo that hits them in a meaningful way is future work.
69+
770
### Added (Phase H.3: parse-level recovery — token-stream healing, 2026-05-13)
871

972
🎯 **`examples/self_healing_h3.omc` — the healer gains a stage BELOW the parser.**

0 commit comments

Comments
 (0)