semantifold 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +2 -0
  2. package/build/src/backends/effects.js +5 -2
  3. package/build/src/backends/java.js +33 -14
  4. package/build/src/backends/javascript.js +83 -18
  5. package/build/src/backends/php.d.ts.map +1 -1
  6. package/build/src/backends/php.js +88 -16
  7. package/build/src/backends/ruby.js +37 -12
  8. package/build/src/backends/scalars.js +2 -2
  9. package/build/src/backends/shared.d.ts +2 -1
  10. package/build/src/backends/shared.d.ts.map +1 -1
  11. package/build/src/backends/shared.js +299 -42
  12. package/build/src/backends/typescript.js +83 -18
  13. package/build/src/backends/writer.d.ts +18 -4
  14. package/build/src/backends/writer.d.ts.map +1 -1
  15. package/build/src/backends/writer.js +40 -4
  16. package/build/src/frontends/java.d.ts +9 -1
  17. package/build/src/frontends/java.d.ts.map +1 -1
  18. package/build/src/frontends/java.js +90 -43
  19. package/build/src/frontends/javascript-typescript.d.ts +3 -1
  20. package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
  21. package/build/src/frontends/javascript-typescript.js +133 -52
  22. package/build/src/frontends/php.d.ts +13 -1
  23. package/build/src/frontends/php.d.ts.map +1 -1
  24. package/build/src/frontends/php.js +226 -59
  25. package/build/src/frontends/ruby.d.ts +4 -1
  26. package/build/src/frontends/ruby.d.ts.map +1 -1
  27. package/build/src/frontends/ruby.js +113 -37
  28. package/build/src/frontends/types.d.ts +9 -2
  29. package/build/src/frontends/types.d.ts.map +1 -1
  30. package/build/src/frontends/types.js +26 -9
  31. package/build/src/language-registry.d.ts.map +1 -1
  32. package/build/src/language-registry.js +14 -6
  33. package/build/src/mapping.d.ts.map +1 -1
  34. package/build/src/mapping.js +4 -2
  35. package/build/src/semantic/effects.d.ts.map +1 -1
  36. package/build/src/semantic/effects.js +20 -6
  37. package/build/src/semantic/lifetime.js +28 -4
  38. package/build/src/semantic/provenance.d.ts.map +1 -1
  39. package/build/src/semantic/provenance.js +61 -9
  40. package/build/src/semantic/types.d.ts +214 -23
  41. package/build/src/semantic/types.d.ts.map +1 -1
  42. package/build/src/semantic/types.js +58 -11
  43. package/build/src/semantic/validate.d.ts +31 -2
  44. package/build/src/semantic/validate.d.ts.map +1 -1
  45. package/build/src/semantic/validate.js +450 -60
  46. package/docs/adr/20261026-typed-error-handling-extension.md +55 -0
  47. package/docs/adr-bounded-subtyping.md +180 -0
  48. package/docs/adr-numeric-tower.md +156 -0
  49. package/docs/architecture.md +1 -1
  50. package/docs/language-support.md +8 -8
  51. package/docs/testing.md +5 -3
  52. package/package.json +1 -1
  53. package/src/backends/effects.js +3 -1
  54. package/src/backends/java.js +33 -14
  55. package/src/backends/javascript.js +83 -18
  56. package/src/backends/php.js +89 -16
  57. package/src/backends/ruby.js +36 -12
  58. package/src/backends/scalars.js +1 -1
  59. package/src/backends/shared.js +298 -47
  60. package/src/backends/typescript.js +83 -18
  61. package/src/backends/writer.js +41 -3
  62. package/src/frontends/java.js +95 -43
  63. package/src/frontends/javascript-typescript.js +145 -53
  64. package/src/frontends/php.js +243 -58
  65. package/src/frontends/ruby.js +117 -35
  66. package/src/frontends/types.js +26 -8
  67. package/src/language-registry.js +13 -5
  68. package/src/mapping.js +3 -1
  69. package/src/semantic/effects.js +18 -5
  70. package/src/semantic/lifetime.js +29 -3
  71. package/src/semantic/provenance.js +60 -14
  72. package/src/semantic/types.js +62 -10
  73. package/src/semantic/validate.js +481 -61
@@ -0,0 +1,55 @@
1
+ # ADR: Typed error handling extension — ordered catches, finally/ensure, and rethrow
2
+
3
+ Date: 2026-10-26
4
+ Task: [011 — Typed errors and handling](../../todo/011-typed-errors-and-handling.md) completion (STOE-62)
5
+ Status: Accepted for the original-five cohort (PHP, Ruby, JavaScript/JSDoc, TypeScript, Java)
6
+
7
+ ## Context
8
+
9
+ Task 011 delivered exactly one nominal catch per `TryStatement` and explicitly rejected `finally`/`ensure`, multiple catches, retry/rethrow, and checked-error effects. Every cohort language has had multi-handler and completion-block syntax from its first release, so the delivered subset could not express ordinary idiomatic error handling. This ADR records the extension decisions and the boundaries that stay closed.
10
+
11
+ ## Decision 1 — `TryStatement` owns an ordered handler list plus one optional completion block
12
+
13
+ `TryStatement` now carries `handlers: CatchHandler[]` and an optional `finallyBody: Block`. `CatchHandler` is a first-class semantic node (`catchType`, immutable `catchBinding`, `catchBody`) and joins the `SemanticNode` union so provenance, symbols, and mappings treat each handler independently.
14
+
15
+ - Handlers are evaluated strictly top to bottom and match their exact nominal `ErrorType` identity only. Broad, union, multi-type, and default handlers remain rejected.
16
+ - Semantic validation keeps the Task 011 reachability rule per handler: a handler whose exact nominal identity cannot escape the protected body — including one shadowed by an earlier handler for the same identity — fails with `UNREACHABLE_HANDLER`. Duplicate or shadowed binding names fail with `DUPLICATE_BINDING`.
17
+ - Flow analysis composes as before: the protected body's escaping raises minus each handled identity, unioned with each handler body's raises. Catch entry still discards known-value and optional-presence facts for every outer mutable binding assignable anywhere in the protected body.
18
+ - `finallyBody` runs on every normal and abrupt completion of the try. A completion block that completes abruptly replaces the pending completion **on the paths where it does**, and its own raises escape; on the paths where it completes normally the pending completion passes through unchanged. Both path sets are therefore reachable: the completion block's raises escape *and* the pending raises — from the protected body, from handler bodies, and from earlier statements of the enclosing block — still escape. The try completes normally only when the completion block completes normally and some protected or handler path completes normally. A `try` with no handler is accepted only when it has a completion block.
19
+ - `lifetime.js`, `effects.js`, and `provenance.js` traverse every handler and the completion block; the completion block is a distinct scope that cannot see any handler binding.
20
+
21
+ ## Decision 2 — Rethrow is an explicit statement against one in-scope catch binding
22
+
23
+ `RethrowStatement {binding, bindingNode}` re-propagates the exact caught value of a handler binding that is lexically in scope. `bindingNode` is the exact `CatchBinding` node, so provenance and generated mappings resolve a rethrow to the handler that introduced the binding rather than to a free name.
24
+
25
+ - Frontends accept only a rethrow whose target is a known in-scope catch binding of error type: JavaScript/TypeScript `throw <binding>`, PHP `throw $<binding>`, Java `throw <binding>`, Ruby `raise <binding>` and bare `raise` inside an exact rescue (bare `raise` re-raises the handler's own exception, which is exactly the current binding).
26
+ - A rethrow adds the binding's nominal identity to the enclosing block's escaping raises, so an outer handler for that identity stays reachable and an outer handler for an unraisable identity still fails `UNREACHABLE_HANDLER`.
27
+ - Rethrowing a binding that is not in scope, or throwing any non-binding value, stays rejected (`INVALID_RETHROW` semantically, `UNSUPPORTED_SYNTAX` in a frontend).
28
+ - Ruby `retry`, `redo`, rescue modifiers, and `else` clauses remain rejected. `retry` re-executes the protected body, which is a control-flow construct rather than error propagation, and the semantic model has no node for it; this is a permanent exclusion unless a later task designs one explicitly.
29
+
30
+ ## Decision 3 — JavaScript and TypeScript express ordered multi-catch as the guarded dispatch chain
31
+
32
+ JavaScript and TypeScript have no sibling `catch` clauses for one `try`, so the exact native profile for an ordered handler list is a guarded dispatch chain: each clause keeps the Task 011 `instanceof` guard, and the unmatched branch of a non-final guard rethrows its own binding into a nested `try` whose single clause is the next handler, while the matched branch is the handler body in the guard's `else` block. The final guard keeps the plain unmatched rethrow and its handler body as the following statements.
33
+
34
+ - The backend emits this chain and the frontend flattens it back to one ordered handler list, so generated source reparses to the same meaning and never exposes target-only boilerplate as semantic statements.
35
+ - Faithfulness is the reason for the shape. Nesting the protected body inside successive `try` blocks would let a later handler catch an error raised by an earlier handler body, which the ordered-list meaning forbids: a handler-body raise must escape outward past every remaining sibling handler. In the dispatch chain each handler body runs outside its own guard, so a handler-body raise propagates past all remaining handlers, exactly like a sibling clause in PHP, Ruby, and Java. The `else` branch is what keeps a handled error from also reaching the next clause.
36
+ - The chain is recognized only in its exact emitted form: one binding per clause, the exact guard, the exact rethrow of that clause's own binding, and for a dispatch clause no finalizer or extra statement in the dispatch `try` and no statements after the single guarded `if` in the clause body. A single-handler clause may spell its handler body either as the statements after the plain rethrow or as the guard's `else` block, which is what the backend emits for a one-element list with a completion block. Any other shape is `UNSUPPORTED_SYNTAX`.
37
+
38
+ ## Decision 4 — Checked-error effects in signatures stay permanently excluded
39
+
40
+ No `throws`/`raises` clause, error union, or result type is added to function, constructor, or method signatures, and this is a permanent exclusion for this feature rather than deferred work.
41
+
42
+ - Propagation stays implicit, exactly as Task 011 established: internal fixed-point analysis follows direct calls so a `try` around a helper call is reachable without any declaration, and each call site's escaping error set is derived from resolved callee effects.
43
+ - Cohorts where checked propagation is mandatory are not approximated. Java generation keeps using unchecked `RuntimeException` subclasses and rejects `throws` clauses and checked types; the deferred cohort (Kotlin, Python, C#, Go, C, C++, Rust, Swift, Wasm) continues to reject any typed-error IR with located `UNSUPPORTED_CAPABILITY` — there is no C status code, Rust `Result`, Go error return, or Wasm trap substitute.
44
+ - Constructors and reference-class members may catch errors internally but still cannot expose an unchecked-error effect through a receiver call, consistent with Task 033.
45
+
46
+ ## Decision 5 — Cohort boundaries and diagnostics are unchanged
47
+
48
+ The extension adds no capability to the deferred ten targets and no new public feature flag: `typedErrors` remains true only for the original five. Frontend form errors remain `UNSUPPORTED_SYNTAX`, semantic type/control errors keep their stable semantic codes (`INVALID_ERROR_HANDLER`, `INVALID_RETHROW`, `UNREACHABLE_HANDLER`, `DUPLICATE_BINDING`, `NON_ERROR_RAISE`, `UNKNOWN_ERROR`), and target inability remains `UNSUPPORTED_CAPABILITY` raised before any writer allocation.
49
+
50
+ ## Consequences
51
+
52
+ - Idiomatic multi-handler and `finally`/`ensure` programs in the original five now parse, generate, reparse to one meaning, and execute on real toolchains.
53
+ - Every previously accepted single-handler program keeps its exact meaning; the single-handler shape is the one-element case of the ordered list, and the historical Task 011 and Task 013 fixtures are unchanged.
54
+ - Rejected constructs stay rejected with the same codes, so the extension is additive: broad/default handlers, arbitrary thrown values, union catches, `retry`/`else`/rescue modifiers, try-with-resources, checked signatures, causes/suppression, and non-cohort lowering remain outside the model.
55
+ - JavaScript/TypeScript generated output for multi-handler IR is longer than the source-level ideal, because the guard chain is the price of faithful ordered semantics on a target with one clause per `try`.
@@ -0,0 +1,180 @@
1
+ # ADR: Bounded subtyping — nominal interfaces with exact implementation contracts
2
+
3
+ - Status: `accepted` (adopted for PHP in this issue's implementation; the remaining cohort languages land per the follow-up plan below)
4
+ - Issue: STOE-60 (Semantifold core — inheritance, interfaces and trait/mixin composition beyond closed reference classes)
5
+ - Depends on: Task 033 reference classes (`docs/language-support.md`, `todo/033-reference-classes-methods-and-constructors.md`) for the closed nominal class, receiver, privacy, and initialization model this ADR extends and never weakens.
6
+
7
+ ## Context
8
+
9
+ Task 033 delivered closed reference classes: nominal identities, private mutable state, one exact constructor, a closed method set, and purely nominal closed dispatch. Inheritance, interfaces, traits/mixins, reopening, and virtual/dynamic dispatch are explicitly excluded, so no polymorphic domain model can be expressed: a function parameter, return, local, or collection can only name one concrete class, and there is no way to state a shared method contract across classes.
10
+
11
+ The repository's guiding principles (AGENTS.md, `todo/README.md`) constrain the design:
12
+
13
+ 1. Model meaning once: the semantic IR carries types and resolution facts, never target spelling.
14
+ 2. Fail loudly: a target that cannot honor a contract reports `UNSUPPORTED_CAPABILITY` before any partial artifact.
15
+ 3. Prefer a precise portable profile: dispatch, override, and visibility rules require explicit contracts, not symbol substitution.
16
+ 4. No structural substitution: a lookalike never satisfies a nominal type.
17
+ 5. Task 033 semantics are frozen: freshness, exact-once field initialization, receiver-before-argument order, declaring-receiver privacy, and aliasing-not-copying stay exactly as delivered.
18
+
19
+ This ADR decides the shape of subtyping. Per-language source spellings, generated shapes, and acceptance corpora are separate per-language specs (see Follow-ups).
20
+
21
+ ## Decision 1 — Interfaces with exact implementation contracts; no class-to-class inheritance
22
+
23
+ The admitted model is **nominal interfaces plus exact `implements` contracts**. A class may implement one or more interfaces; a class never extends another class, and there are no abstract classes, superclass constructors, inherited fields, inherited methods, or `super` calls.
24
+
25
+ Rationale:
26
+
27
+ - The polymorphism that domain models actually need — a shared method contract behind parameter/return/local/collection positions — is fully expressible with interfaces; implementation inheritance mostly buys code reuse, which the model can later supply through composition (holding records or references as private fields) without inheriting state or construction semantics.
28
+ - Implementation inheritance collides with Task 033's construction contract: "exactly one constructor" and "every field initialized exactly once before the reference escapes" do not survive parent-constructor chaining, inherited private fields, or partial initialization across a class hierarchy.
29
+ - Ruby has single class inheritance but no native interface construct, JavaScript has `extends`/`class` but no nominal interface, and PHP/Java/TypeScript have both — a class-inheritance contract would be the least common denominator and would import constructor-chaining and visibility-variance questions this model does not need.
30
+ - Excluding class inheritance keeps the class cohort closed and enumerable per module, which is what makes the override contract statically checkable.
31
+
32
+ `extends` in the admitted grammar means **interface-extends-interface only** (Decision 3). Every source spelling of class-to-class `extends`, `super`, abstract classes, and inherited members remains `UNSUPPORTED_SYNTAX`, exactly as today.
33
+
34
+ ## Decision 2 — `InterfaceDeclaration` is a closed signature-only nominal type
35
+
36
+ The semantic IR gains:
37
+
38
+ ```
39
+ InterfaceDeclaration
40
+ kind: "InterfaceDeclaration"
41
+ id?: string // stable module-local identity (`interface:N`), required after validation
42
+ name: string // source-visible name
43
+ extends?: InterfaceType[] // direct interface parents in source order; omitted when empty
44
+ methods: InterfaceMethodDeclaration[] // ordered closed signature set
45
+
46
+ InterfaceMethodDeclaration
47
+ kind: "InterfaceMethodDeclaration"
48
+ id?: string // stable interface-scoped identity (`interface:N:method:N`), required after validation
49
+ name: string
50
+ parameters: Parameter[] // exact positional required parameters with explicit types
51
+ returnType: SemanticFunctionReturnType // explicit value or void return
52
+
53
+ InterfaceType
54
+ kind: "InterfaceType"
55
+ declarationId: string // exact interface declaration identity
56
+ ```
57
+
58
+ `ClassDeclaration` gains an optional `implements?: InterfaceType[]` member (source order; omitted when empty) and each `MethodDeclaration` gains an optional resolved `implements?: {interfaceId: string, methodId: string}` fact after validation.
59
+
60
+ Hard rules:
61
+
62
+ - Interfaces carry **no fields, no constructors, no static members, and no method bodies**. A body on an interface method (PHP/Python/Go-style default methods) is `UNSUPPORTED_SYNTAX`.
63
+ - Interface method signatures are explicit and complete exactly like Task 033 method signatures: named parameters, explicit types, explicit value-or-void return. No optional parameters, no defaults, no overloads, no variadics.
64
+ - Interface names join the existing nominal-name registry: one interface name per module, unique against classes, records, errors, functions, and other interfaces, and against the target reserved-name/collision baselines Task 033 already enforces.
65
+ - `InterfaceType` joins `SemanticValueType` and composes with the existing type grammar exactly like `ReferenceType`: parameters, returns, locals, list/map/ordered-map/optional constituents.
66
+
67
+ ## Decision 3 — Interface composition is a closed extends DAG
68
+
69
+ An interface may extend zero or more interfaces. The transitive method set is the flattened union in deterministic declaration order (parents first, then the interface's own methods, in source order). Cycles, duplicate inherited names, and a redeclared method whose signature differs from the inherited one are located diagnostics (`INTERFACE_CYCLE`, `INTERFACE_MEMBER_DUPLICATE`). Interface-to-interface `extends` is the only admitted `extends` spelling.
70
+
71
+ ## Decision 4 — The override contract is exact and total
72
+
73
+ A class with `implements [I1..In]` must supply, in its own closed method set, exactly one method matching each flattened interface signature:
74
+
75
+ - exact name, exact ordered parameter types, exact arity, exact return type (value type or void — never widened or narrowed);
76
+ - no interface method may be private to the class (visibility rules per Decision 7);
77
+ - duplicate class methods satisfying the same interface member, a missing member, a signature mismatch, and an arity mismatch are located diagnostics (`INTERFACE_MEMBER_MISSING`, `INTERFACE_MEMBER_SIGNATURE_MISMATCH`), reported before any writer allocation;
78
+ - a class may implement several interfaces, and one class method may satisfy several interfaces' identical signatures;
79
+ - interface method names follow the same target preflight rules as Task 033 method names (for example Ruby target preflight still rejects setter-style names ending in `=`).
80
+
81
+ The contract is checked at the declaration, once, statically. Nothing about it is deferred to runtime.
82
+
83
+ ## Decision 5 — Dispatch is nominal at the call site; the target's native lookup selects the implementation
84
+
85
+ `MethodCallExpression` on an `InterfaceType` receiver resolves against the **interface's flattened signature set**: the resolved `ResolvedMethodSignature` records the declaring interface identity and the exact signature, exactly as Task 033 records the declaring class. Call-site validation never inspects which concrete classes exist.
86
+
87
+ At runtime the target's own native method lookup performs the selection (PHP interface dispatch, Ruby module inclusion, JavaScript prototype lookup, TypeScript compile-time erasure plus native lookup, Java virtual dispatch). This is target-native lowering, not a Semantifold dispatch model: the semantic IR carries no dispatch tables, virtual slots, method objects, vtables, or message sends, and Semantifold never resolves a method against a concrete class behind an interface receiver. Two classes implementing the same interface produce byte-identical generated call sites.
88
+
89
+ Explicitly still excluded (unchanged from Task 033): computed/dynamic method names, `send`/`apply`/`public_send`, `method_missing`, function-valued "lambdas stored in fields", reflection, and any construct that turns a method name into a runtime value.
90
+
91
+ ## Decision 6 — Value flow: implicit upcast only, no casts, no type tests
92
+
93
+ - A `ReferenceConstruction` of class `C` is assignable to an `InterfaceType` `I` (and to `J`) exactly when `C implements` includes `I` (or a transitively extended `J`). This is the only admitted widening, and it is checked statically at every assignment, argument, return, and collection-element position.
94
+ - An interface-typed value never flows back to a class type or to a narrower interface: there are no downcasts, no `as`/`instanceof`/`is_a?`/`is`/`kind_of?`/type predicates on interface receivers, no exhaustive switch over implementations, and no runtime type queries of any kind. The existing general exclusion of casts/assertions stands.
95
+ - Interface-typed locals require the same explicit type carriers Task 033 requires wherever the source language does not enforce them (JSDoc `@type`, Ruby type comments, etc.).
96
+ - Task 033 identity semantics carry over unchanged: interface-typed bindings alias the same fresh instance; there is still no identity equality, hashing, ordering, cloning, or serialization.
97
+
98
+ ## Decision 7 — Visibility and encapsulation
99
+
100
+ - Interface members are public to the module cohort by definition; the implementing class method must be public in the language profile (Java package-private follows the existing Task 033 Java profile: package-private interface, package-private implementing class methods).
101
+ - Private fields remain declaring-class-only through the semantic receiver. An interface receiver can never reach any field: `PrivateFieldRead`/`PrivateFieldWriteStatement` remain valid only on the declaring-class `ReceiverExpression` inside the declaring class, never on an interface-typed binding.
102
+ - Interfaces do not constrain a class's private state or extra methods: a class may have any Task 033-legal fields and methods beyond its interface contract.
103
+
104
+ ## Decision 8 — Capability, cohort, and fail-loud boundaries
105
+
106
+ The registry gains one capability flag `boundedSubtyping`, valid only where `referenceClasses: true` (a language cannot advertise interfaces before it advertises closed reference classes). Adoption order follows the existing cohort discipline:
107
+
108
+ 1. **Original five first** (PHP, Ruby, JavaScript/JSDoc, TypeScript, Java) — the Task 033 cohort, in the same single-module scope Task 033 shipped with. PHP is adopted today; Ruby, JavaScript/JSDoc, TypeScript, and Java keep `boundedSubtyping: false` until their per-language interface profiles land.
109
+ 2. **Each Wave-2 language when it adopts `referenceClasses`** (Kotlin, Python, C#, Go, Rust, Swift, Dart, then C/C++ and Zig as their reference-class lanes land), each with its own exact native profile spec.
110
+
111
+ A language that advertises `boundedSubtyping: false` — today every language outside the original five, plus browser Wasm, Android/Flutter/iOS application roles until their lanes say otherwise — rejects any interface-bearing module transactionally with `UNSUPPORTED_CAPABILITY` before source, bytes, or a partial artifact set is exposed, exactly as non-cohort languages reject Task 033 class IR today.
112
+
113
+ ## Decision 9 — Explicit permanent exclusions
114
+
115
+ Unless a future ADR admits them, all of the following stay out of the model and stay `UNSUPPORTED_SYNTAX`/`UNSUPPORTED_CAPABILITY`:
116
+
117
+ - class-to-class inheritance, abstract classes, `super`, inherited constructors/fields/methods, constructor chaining;
118
+ - multiple implementation inheritance, mixins/traits with state or behavior, Ruby module `prepend`/refinement, JavaScript prototype mutation, Python MRO beyond a single base;
119
+ - reopening, monkey-patching, duck typing, structural interface satisfaction, implicit interface conformance without an explicit `implements` declaration;
120
+ - default/implemented interface methods, static or constant interface members, interface generics (until Task 012 generics compose with interfaces in a separate decision), variance annotations (interfaces are invariant);
121
+ - downcasts, type tests, exhaustive matching over implementors, runtime type inspection, reflection;
122
+ - virtual/dynamic sends beyond the target's own closed native lookup for a statically resolved interface method (Decision 5);
123
+ - identity equality/hashing/ordering, serialization, cloning, destructors, async interface methods, concurrency.
124
+
125
+ ## Per-language source profiles (exact native forms)
126
+
127
+ Frontends recognize exactly one canonical spelling per language and account for every parser child, modifier, and annotation; anything else is `UNSUPPORTED_SYNTAX`.
128
+
129
+ | Language | Interface declaration | Implementation | Interface-typed binding |
130
+ | --- | --- | --- | --- |
131
+ | PHP | `interface Name { public function m(T $p): R; }` (signature only) | `final class C implements I1, I2` | native parameter/return/local types |
132
+ | Ruby | `interface Name` declaration with per-method type-comment signatures (compiler-qualified spelling, mirroring the bounded RBS-style convention) | `class C` with `include Name` and matching `def` bodies | type comments, as Task 033 |
133
+ | JavaScript/JSDoc | `/** @interface */` class-shaped signature scaffold with complete `@param`/`@return` per method | `@implements {Name}` on the Task 033 canonical class | JSDoc `@type` carriers |
134
+ | TypeScript | `interface Name { m(p: T): R }` (non-exported, signature only) | `class C implements I1, I2` (Task 033 canonical class body) | native type annotations |
135
+ | Java | package-private `interface Name { R m(T p); }` | package-private `final class C implements I1, I2` (Task 033 canonical class) | native types, `Main.java` contract unchanged |
136
+ | Python (Wave 2) | `typing.Protocol`-shaped method stubs with full annotations | explicit inheritance of the protocol base in the class header | annotations |
137
+ | C# (Wave 2) | `interface IName { R M(T p); }` | `sealed class C : I1, I2` | native types |
138
+ | Go (Wave 2) | named `type I interface { M(p T) R }` method set | method-set satisfaction proven against the explicit named interface declaration only | named interface type |
139
+ | Rust (Wave 2) | `trait I { fn m(&self, p: T) -> R; }` with explicit `impl I for C` | `impl I for C` blocks | trait-object position decided per spec (no dynamic dispatch unless the spec pins a closed form) |
140
+ | Swift (Wave 2) | `protocol IName { func m(_ p: T) -> R }` | `final class C: I1, I2` | native protocol types |
141
+ | Kotlin (Wave 2) | `interface IName { fun m(p: T): R }` | `class C : I1, I2` | native types |
142
+ | Dart (Wave 2) | `abstract class IName { R m(T p); }` signature-only form | `class C implements I1, I2` | native types |
143
+ | C / C++ / Zig | no conformant native interface construct in the current profiles — remain excluded until a per-language spec decides a compiler-owned closed dispatch shape | — | — |
144
+
145
+ ## Per-target backend representations
146
+
147
+ Backends emit the target's native interface construct and native call spelling from validated semantic nodes; they never erase an interface into a map, function table, or tagged union.
148
+
149
+ | Target | Representation |
150
+ | --- | --- |
151
+ | PHP | native `interface` + `implements`; typed signatures |
152
+ | Ruby | compiler-owned generated `module Name` with the contract documented and `include Name` on the class; method bodies remain the class's own |
153
+ | JavaScript | JSDoc `@interface`/`@implements` scaffold plus native method calls (nominal guarantee lives in the compiler, generated code stays plain ES) |
154
+ | TypeScript | native `interface` + `implements` |
155
+ | Java | package-private `interface` + `implements`; single-file `Main.java` composition rules extended to nested interfaces |
156
+ | Python / C# / Go / Rust / Swift / Kotlin / Dart | native protocol/interface/trait/protocol/interface/abstract-class forms per the Wave-2 specs; each spec pins the exact dispatch lowering and proves it with real toolchains |
157
+ | C / C++ / Zig / browser Wasm / application roles | `UNSUPPORTED_CAPABILITY` for interface-bearing IR until their lanes admit it |
158
+
159
+ Backend preflight revalidates the complete interface graph — identities, flattened method sets, override contracts, name collisions against target baselines, receiver types, and upcast legality — before writer allocation, and generated code reparses to the same interface/member identities wherever the target has a reparse lane.
160
+
161
+ ## Diagnostics
162
+
163
+ New stable codes, all located and all raised before artifact exposure: `DUPLICATE_INTERFACE`, `UNKNOWN_INTERFACE`, `INTERFACE_CYCLE`, `INTERFACE_MEMBER_DUPLICATE`, `INTERFACE_MEMBER_MISSING`, `INTERFACE_MEMBER_SIGNATURE_MISMATCH`, `INTERFACE_NAME_COLLISION`, `INTERFACE_TYPE_MISMATCH` (illegal upcast/assignment), `INTERFACE_RECEIVER_INVALID` (non-interface receiver or private-field reach through an interface binding). Malformed caller-owned IR reports the existing transactional `UNSUPPORTED_CAPABILITY`/`TYPE_MISMATCH` family. Frontends locate excluded inheritance, `super`, reopened classes, default methods, implicit conformance, and type tests as `UNSUPPORTED_SYNTAX`.
164
+
165
+ ## Rejected alternatives
166
+
167
+ - **Single-root class inheritance with virtual dispatch** (PHP/Java/TypeScript-shaped): rejected by Decision 1 — it reopens constructor chaining, partial initialization, inherited private state, and visibility variance, and its least-common-denominator across Ruby/JavaScript profiles is weaker than the interface contract.
168
+ - **Interface-only composition without interface `extends`**: rejected as unnecessarily restrictive; a closed extends DAG is statically checkable and native in every cohort language.
169
+ - **Structural/duck typing**: rejected outright — it violates the repository's nominal-typing principle and cannot be validated fail-loudly.
170
+ - **Composition-only (no subtyping; hand adapters)**: rejected as insufficient — it cannot express polymorphic parameters, which is the gap this issue names.
171
+
172
+ ## Follow-ups
173
+
174
+ 1. Semantic IR/validation: `InterfaceDeclaration`/`InterfaceMethodDeclaration`/`InterfaceType`, `implements` on classes, registry `boundedSubtyping` flag, shared acceptance/rejection specs (one top-level `describe` per spec file).
175
+ 2. Original-five frontend/backend per-language specs: exact source spellings, generated shapes, reparse proofs, and real-toolchain execution (PHP, Ruby, Node, `tsc`, `javac`/`java`) including override-contract negatives.
176
+ 3. Wave-2 per-language specs as each language's `referenceClasses` lane lands.
177
+ 4. Docs: extend the Task 033 section of `docs/language-support.md` with the admitted interface profile once implemented.
178
+ 5. Real-toolchain acceptance and exact-head TensorBuzz CI per the repo gates.
179
+
180
+ Recorded as roadmap task [048 — Bounded subtyping implementation](../todo/048-bounded-subtyping-implementation.md).
@@ -0,0 +1,156 @@
1
+ # ADR: Portable numeric tower — FloatType and DecimalType
2
+
3
+ - Status: `proposed` (accepted on merge of this ADR; implementation proceeds per the follow-up plan below)
4
+ - Issue: STOE-57 (Semantifold core — floating-point and decimal numeric types across all 15 languages)
5
+ - Depends on: the STOE-56 integer-operator contract (division, remainder, exponentiation, bitwise/shift, compound/update assignment) for the integer side of every shared spelling; this ADR does not decide those semantics and never changes them.
6
+
7
+ ## Context
8
+
9
+ Today every program is integer/boolean/string only. Floating point, decimal, and bigint are explicitly unsupported (`docs/language-support.md`, "Diagnostics and limitations"). Idiomatic numeric programs — money, ratios, measurements, unit conversions — cannot be written at all, and the STOE-56 operator work makes the gap sharper: once integer `/` and `%` exist, users will expect `/` on non-integer operands to mean division, not truncation.
10
+
11
+ The repository's guiding principles (AGENTS.md, `todo/README.md`) constrain the design:
12
+
13
+ 1. Model meaning once: the semantic IR carries types and operations, never target spelling.
14
+ 2. Fail loudly: a target that cannot honor a contract reports `UNSUPPORTED_CAPABILITY` before any partial artifact.
15
+ 3. Prefer a precise portable profile: overflow, rounding, and formatting require explicit contracts, not symbol substitution.
16
+ 4. No implicit coercion: equality and arithmetic never mix types silently.
17
+ 5. Existing integer semantics are frozen: safe-range literals, checked arithmetic, and the exit-70 overflow convention stay exactly as delivered.
18
+
19
+ This ADR decides the shape of the numeric tower. Per-language literal spellings, generated-helper shapes, and acceptance corpora are separate per-language specs (see Follow-ups).
20
+
21
+ ## Decision 1 — The tower has three implemented numeric types now; bigint is a separate follow-up
22
+
23
+ `SemanticTypeName` grows from `"integer" | "boolean" | "string"` to include `"float"` and `"decimal"`. `integer` keeps its exact current contract (JavaScript safe-integer literals, per-target checked arithmetic, single unsigned mathematical zero). `float` and `decimal` are new, distinct, non-coercible scalar types.
24
+
25
+ `bigint` is **not** part of this tower. It is deferred to its own task (see Decision 5) because unbounded width changes formatting, equality, collection-key, and target-native stories in ways orthogonal to fixed-width float/decimal.
26
+
27
+ ## Decision 2 — FloatType is IEEE-754 binary64 with a strict fail-loud subset
28
+
29
+ FloatType is exactly the target's native double (`binary64`). The language profile is the **stricter fail-loud subset**, not the full IEEE-754 program: NaN and ±Infinity are **unobservable** — no literal produces them, no accepted operation can produce them, and no builtin (`sqrt`, `log`, …) exists to produce them.
30
+
31
+ ### Literals
32
+
33
+ Canonical decimal float literals only:
34
+
35
+ ```
36
+ float-literal = [ "-" ] digits [ "." digits ] [ ( "e" | "E" ) [ "+" | "-" ] digits ]
37
+ ```
38
+
39
+ - A float literal must carry a fractional part or an exponent: `1.5`, `-2.25`, `1.0`, `1e3`, `1.0e-9` are float; `1` is integer. `1.` and `.5` are rejected (`UNSUPPORTED_SYNTAX`).
40
+ - Underscore separators, hexadecimal/binary floats (`0x1p3`), `f`/`d` suffixes, `inf`, `nan`, and any spelling whose correctly-rounded value is ±Infinity are rejected at the frontend (`NUMERIC_LITERAL_OUT_OF_RANGE` for magnitude, `UNSUPPORTED_SYNTAX` for spelling).
41
+ - A decimal literal maps to its correctly-rounded binary64 value (round-half-even ties). The semantic value is that binary64 value, not the source digits.
42
+
43
+ ### Operations
44
+
45
+ `+`, `-`, `*`, `/`, unary `-`, `<`, `<=`, `>`, `>=`, `==`, `!=` on float operands are the IEEE-754 binary64 operations of the target. `/` is selected by operand type: `integer / integer` is the STOE-56 integer-division contract, `float / float` is IEEE division, `decimal / decimal` is the decimal contract (Decision 3). Mixed numeric operands are rejected (Decision 4).
46
+
47
+ ### Fail-loud policy
48
+
49
+ - **Division by zero** (`x / 0.0`, including a computed zero divisor) is a runtime failure: the program terminates with exit code `70` and the exact single stderr line `semantifold: float arithmetic failure\n`. This matches the delivered Rust/Zig integer-overflow convention (`semantifold: integer overflow\n`, exit 70).
50
+ - **Overflow to non-finite** (a `+`, `-`, `*`, or `/` whose exact result rounds away from the finite binary64 range) is the same runtime failure. Generated code checks finiteness after each float operation using the target's native classification (`isfinite`, `Float.isFinite`, `f64::is_finite`, `f64.isFinite`, `Float::finite?`, `math::isfinite`, …) and raises the identical failure.
51
+ - **Compile-time-known** overflow or division by zero in a literal operation tree is rejected before emission, exactly as integer trees are rejected today.
52
+ - **Underflow** to zero is permitted and observable as `0.0` (or `-0.0` internally). Denormal values are permitted.
53
+ - **NaN is unreachable**: because no accepted operation can produce it (the only IEEE-invalid candidate, `0.0 / 0.0`, is division by zero and fails first), no NaN policy, payload, or quiet/signaling distinction is ever observable. This is the ADR's NaN/Infinity policy: they are excluded from the language, not formatted or compared.
54
+
55
+ ### Equality and ordering
56
+
57
+ Float equality is IEEE value equality on binary64. `-0.0 == 0.0` is true; the sign of zero is otherwise unobservable (printing canonicalizes `-0.0` to `0`, matching the delivered JavaScript/TypeScript `-0` handling). Ordering is IEEE total ordering with the NaN case unreachable. Equality never coerces: `float == integer` and `float == decimal` are validation errors.
58
+
59
+ ### Canonical printed form
60
+
61
+ Printed floats use one canonical text contract, independent of each target's native `toString`/`printf` defaults:
62
+
63
+ 1. **Shortest round-trip**: the shortest decimal digit string that parses back to the exact same binary64 value (Ryu/Grisu-class correctly-rounded shortest representation).
64
+ 2. **Notation rule**: positional notation when the decimal exponent of the value is in `(-5, 17)`; otherwise scientific notation `d.dddde±X` with a lowercase `e`, explicit exponent sign, and no exponent leading zeros.
65
+ 3. **Type-preserving text**: the canonical form always carries a decimal point or an exponent, so a printed float re-parses as a float, never as an integer: `1.0`, not `1`; `2.5e10`, not `25000000000`.
66
+ 4. Negative zero prints as `0` (the single unsigned zero convention).
67
+
68
+ Backends must not print floats through a target's default scalar rendering unless a per-language spec **proves** that native rendering is exactly this contract for the qualified toolchain (JavaScript `Number.prototype.toString`, Python `repr`, Ruby `Float#to_s`, Go `strconv.FormatFloat(v, 'g', -1, 64)`, Rust `{}` for `f64`, Java `Double.toString`, .NET Core-style `double.ToString()`, and Zig `std.fmt` are candidates; C `printf`, C++ iostreams, PHP's `precision`-controlled conversion, and Swift/Dart defaults are not). Where native rendering is not provably conformant, the backend emits a compiler-owned generated shortest-round-trip formatter (the same private-support-module pattern already used for Kotlin checked helpers, Swift Unicode-scalar equality helpers, and the C/C++ ordered-expression runtime). The acceptance corpus pins exact printed bytes per target.
69
+
70
+ ## Decision 3 — DecimalType is fixed-precision 28-significant-digit decimal with round-half-even
71
+
72
+ DecimalType models a fixed-precision decimal number:
73
+
74
+ - **Coefficient**: at most 28 significant digits (chosen because it is the exact intersection of `.NET System.Decimal` and Python `decimal` default precision, and is trivially representable by `java.math.BigDecimal`/`ruby BigDecimal`/`BigInt`/128-bit-integer helpers on every other target).
75
+ - **Scale**: 0 through 28 fractional digits. Scale is part of the value: `1.50` and `1.5` are different decimal values for arithmetic and printing, and both are legal literals.
76
+ - **Literals**: canonical decimal spelling `[ "-" ] digits [ "." digits ]` with at most 28 significant digits and at most 28 fractional digits. No exponent spelling for decimal literals (it would make scale ambiguous). Out-of-range literals report `NUMERIC_LITERAL_OUT_OF_RANGE`. Source spelling per language is a facade question (Decision 6).
77
+ - **Arithmetic**: `+`, `-`, `*`, unary `-`, `/`, `<`, `<=`, `>`, `>=`, `==`, `!=`.
78
+ - `+`/`-`: exact; result scale is `max(scale_a, scale_b)`. A result needing more than 28 significant digits is a runtime failure (exit 70, `semantifold: decimal arithmetic failure\n`).
79
+ - `*`: exact; result scale is `scale_a + scale_b`. If the exact product needs more than 28 significant digits **or** more than 28 fractional digits, the result is rounded to the contract with **round-half-even (banker's rounding)**; if the rounded coefficient still exceeds 28 significant digits, it is the same runtime failure.
80
+ - `/`: division is total for nonzero divisors. The exact quotient is used when representable within 28 significant digits and scale ≤ 28; otherwise the quotient is rounded to scale 28 with **round-half-even**. Division by zero is the exit-70 runtime failure.
81
+ - Compile-time-known decimal overflow or division by zero is rejected before emission.
82
+ - **Rounding rule is round-half-even everywhere** this type rounds (literal rounding is exact by grammar; only `*` scale reduction and `/` round). This is the ADR's explicit "banker's rounding" decision; no target's native half-up default may leak through — backends must pin the rounding mode explicitly (`MathContext(28, RoundingMode.HALF_EVEN)`, `decimal.localcontext(prec=28, rounding=ROUND_HALF_EVEN)`, `MidpointRounding.ToEven`, generated helpers' explicit tie rule).
83
+ - **Equality** is numeric-value equality and is **scale-insensitive**: `1.50 == 1.5` is true. Ordering compares numeric values. Equality never coerces across `integer`/`float`/`decimal`.
84
+ - **Printed form** is exact positional notation preserving the value's scale, including trailing zeros: `1.50` prints as `1.50`. No exponent form. This makes decimal printing deterministic on every target by construction.
85
+
86
+ ## Decision 4 — No implicit promotion, ever
87
+
88
+ There is no implicit `integer → float`, `integer → decimal`, or `float → decimal` conversion in any operand position: not in arithmetic, comparisons, equality, assignments, initializers, arguments, returns, or print. A mixed numeric operand pair is a located `TYPE_MISMATCH` at the operator, exactly like today's mixed-type rules. `float` and `decimal` are first-class declared types with their own per-language source spellings (per-language specs).
89
+
90
+ Explicit conversion builtins (`toFloat`, `toDecimal`, `truncate`, `round`) are **out of scope for this issue** and are a follow-up candidate: they are value-changing operations whose rounding contracts must be specified separately, and the fail-loud design does not need them to make the tower usable (literals already carry their type).
91
+
92
+ ## Decision 5 — bigint is a separate follow-up task
93
+
94
+ bigint is deliberately excluded from this tower:
95
+
96
+ - Its width is unbounded, so "target-native representation" means `java.math.BigInteger`, Python `int`, Ruby `Integer`, .NET `System.Numerics.BigInteger`, `BigInt` in JS, and generated multi-limb helpers in C/C++/Go/Rust/Swift/Dart/Zig/PHP/Wasm — a materially different support matrix than "native double" for float.
97
+ - Its equality/formatting interaction with the existing safe-integer contract is a trap: Python and Ruby `int` are already unbounded, Go `int64` is architecture-sized, and Java `int` is 32-bit. Adding bigint without re-deciding the integer contract would silently change what `integer` means on those targets.
98
+ - The practical value (exact large counting, IDs) is separable from the practical value of float/decimal (ratios, money).
99
+
100
+ Follow-up: a dedicated bigint task that first decides whether `integer` itself becomes unbounded on dynamic targets or bigint remains a separate type, then proceeds with the same ADR → per-language specs → acceptance chain.
101
+
102
+ ## Decision 6 — Target-native representations
103
+
104
+ Float is native on every target; decimal is native where conformant and compiler-owned-support elsewhere, consistent with the hub-and-spoke stdlib design (`docs/standard-library-portability.md`): source-language compatibility facades carry the literal/operation spellings, canonical contracts join them, and target host providers own the native bindings. Application source never resolves provider names.
105
+
106
+ | Target | `float` representation | `decimal` representation |
107
+ | --- | --- | --- |
108
+ | PHP | native `float` | generated compiler-owned fixed-precision helper (no conformant native decimal; GMP is not guaranteed) |
109
+ | Ruby | native `Float` | native `BigDecimal` (compiler-owned `require "bigdecimal"` scaffold, pinned precision context) |
110
+ | JavaScript | native `number` | generated compiler-owned helper over `BigInt` (unscaled + scale) |
111
+ | TypeScript | native `number` | same generated helper, typed |
112
+ | Java | native `double` | native `java.math.BigDecimal` with pinned `MathContext(28, HALF_EVEN)` |
113
+ | Kotlin | native `Double` | native `java.math.BigDecimal` with pinned `MathContext` |
114
+ | Python | native `float` | native `decimal.Decimal` under a compiler-owned `decimal.localcontext(prec=28, rounding=ROUND_HALF_EVEN)` scaffold |
115
+ | C# | native `double` | native `System.Decimal` (28–29 digits; the 28-digit contract sits inside it; division truncation at scale 28 is pinned explicitly) |
116
+ | Go | native `float64` | generated compiler-owned helper (no standard decimal type; `math/big` imports stay outside the generated-support boundary) |
117
+ | C | native `double` | generated compiler-owned helper over `__int128` unscaled + scale (qualified Clang 21) |
118
+ | C++ | native `double` | generated compiler-owned helper over `__int128` unscaled + scale |
119
+ | Rust | native `f64` | generated compiler-owned helper over `i128` unscaled + scale (checked) |
120
+ | Swift | native `Double` | generated compiler-owned helper over `Int128` (Swift 6.3 stdlib) unscaled + scale |
121
+ | Dart | native `double` | generated compiler-owned helper over `BigInt` unscaled + scale |
122
+ | Zig | native `f64` | generated compiler-owned helper over `i128` unscaled + scale (checked) |
123
+ | Wasm | native `f64` ops (`f64.add/sub/mul/div`, `f64.lt/le/gt/ge/eq`) | generated multi-limb helper over `i64` pairs in linear memory |
124
+
125
+ Every generated helper is compiler-owned private support (same class as the existing Kotlin checked-integer helpers, Swift Unicode-scalar helpers, C/C++ ordered-expression runtime, and Wasm scratch arena), emitted only when reachable, and never name-resolvable from portable source.
126
+
127
+ ## Interaction with existing integer checked arithmetic
128
+
129
+ - Nothing in this ADR changes integer literals, integer checked arithmetic, the safe-integer range, Java's 32-bit profile, Go's wrap behavior exception, or the exit-70 overflow convention. `integer` is untouched.
130
+ - The `/` and `%` spellings introduced by STOE-56 are **type-selected operations**, not overloaded symbols: the semantic operation is chosen by the resolved operand types before any target spelling exists (`IntegerDivide` per STOE-56's contract; `FloatDivide`; `DecimalDivide`). A mixed pair never selects an operation.
131
+ - Integer operands may not appear where a float or decimal operation is selected, and vice versa; there is no promotion path and no common "numeric" supertype.
132
+ - The fail-loud runtime convention is shared: numeric failures exit 70 with one fixed stderr line per family (`semantifold: integer overflow`, `semantifold: float arithmetic failure`, `semantifold: decimal arithmetic failure`).
133
+
134
+ ## Rejection cases and diagnostics
135
+
136
+ - Mixed numeric operands (arithmetic, comparison, equality, assignment, argument, return): located `TYPE_MISMATCH`.
137
+ - Unsupported or malformed float/decimal literal spelling: located `UNSUPPORTED_SYNTAX` (including `1.`, `.5`, underscores, hex floats, suffixes, `inf`/`nan`).
138
+ - Literal magnitude/scale outside the type contract: `NUMERIC_LITERAL_OUT_OF_RANGE`.
139
+ - Compile-time-known float non-finite result or decimal overflow/division-by-zero in a literal tree: located `NUMERIC_LITERAL_OUT_OF_RANGE` before emission (same posture as integer tree rejection).
140
+ - A target whose qualified toolchain cannot honor the contract for a requested module: located `UNSUPPORTED_CAPABILITY` before any partial artifact (fail-loud, never a silently different behavior).
141
+ - Runtime float division-by-zero/overflow and decimal overflow/division-by-zero: exit 70 with the family's fixed stderr line.
142
+
143
+ ## Consequences
144
+
145
+ - The semantic IR gains `FloatLiteral`, `DecimalLiteral`, `FloatType`/`DecimalType` scalar names, and the corresponding typed operations; all fifteen frontends and all text backends plus Wasm must implement the exact profiles or reject transactionally.
146
+ - Printed float output becomes a specified canonical contract, which means several backends gain generated support code rather than using native printing — accepted deliberately: determinism outranks emission brevity, and the private-support-module pattern is already established.
147
+ - Decimal's scale-sensitive value model makes `1.50 != 1.5` as values but equal as numbers; documentation must state this plainly because it surprises most readers.
148
+ - The 28-digit decimal contract is a real limitation (no 34-digit `decimal64`, no arbitrary precision); it is chosen for exact native conformance on the widest set of qualified toolchains rather than maximum precision.
149
+ - Existing programs are unaffected: no existing literal, operator, or print behavior changes, and every existing spec stays green.
150
+
151
+ ## Follow-ups (implementation chain)
152
+
153
+ 1. Per-language specs (one focused spec file per language, per repo convention): exact frontend literal/type spellings, facade shapes for decimal where native spelling is absent, backend emission and helper shapes, native-rendering conformance proofs for float printing, and the accept/reject corpus including division-by-zero, overflow, rounding-tie, and scale cases.
154
+ 2. Shared semantic validation and IR extension specs; Wasm lowering spec.
155
+ 3. Real-toolchain acceptance per target in the canonical lane (and exact-head TensorBuzz CI), with byte-exact printed output pinned.
156
+ 4. Separate follow-up issues: explicit conversion builtins (with their own rounding contracts) and the bigint decision task.
@@ -5,7 +5,7 @@ Semantifold has three explicit layers.
5
5
  Task 018's C path follows these layers through the unchanged root-bundled private legacy CST boundary. `src/frontends/c.js` owns all C trees and exhaustive profile validation. `src/backends/native-validation.js` checks native graph/location limits and `c-validation.js` checks C memory limits; `ordered-expressions.js` plans occurrence-specific evaluation without modifying IR; `c.js` emits mapped `program.c`; and `c-runtime.js` supplies the synthetic immutable-slice/arena header. The C frontend reconstructs candidates only from complete CST regions, resolves them with the existing semantic validator, and compares the original whole CST against the re-parsed canonical backend plan before returning a module. This same-language validation keeps target generation free of parser objects. See the [C profile](c.md).
6
6
 
7
7
  1. Frontend adapters in `src/frontends/` invoke mature language parsers and translate only recognized syntax. Babel owns JavaScript and TypeScript parsing, `php-parser` owns PHP, Prism owns Ruby, Lezer owns Java, and qualified Tree-sitter bindings and grammars own Python, C#, Go, C, CPP, Rust, Swift, Kotlin, Dart, and Zig. Their AST/CST objects never appear in the public API. Adapters enumerate supported children and reject every other parser node; filtering syntax away is not a normalization strategy. Prism node offsets and comment byte ranges are converted from UTF-8 to JavaScript UTF-16 offsets before semantic location construction and metadata source slicing. Tree-sitter adapters reject lone surrogates, exhaustively traverse named/anonymous/comment/directive/error/missing children, and verify reported boundaries through the shared UTF-8-byte-to-UTF-16 converter. The Go adapter additionally collapses only exact `package main`, optional `fmt`, ordered semantic functions, and a final zero-argument `func main()` into the shared module/entry shape; assignment occurrences, not Go's uniformly mutable `var` syntax, determine semantic local mutability.
8
- 2. `src/semantic/` defines the shared JSDoc-typed discriminated representation and one-based, filename-bearing UTF-16 source locations. `provenance.js` indexes exact source content, deterministic semantic paths/node IDs, resolved symbol IDs, parser-token subranges, and closed source/derived/synthetic origins without exposing parser objects. Task 010's `SemanticProgram` adds dependency-first logical modules, a caller-order multi-source registry, explicit import/export nodes, one selected entry module, and stable module-qualified declaration identities; native filenames, package names, and target paths remain outside semantic resolution. Value types are scalar `integer`/`boolean`/`string` references or recursive acyclic `ListType`, unordered string-keyed `MapType`, insertion-ordered `OrderedMapType`, `OptionalType`, nominal `RecordType`, nominal Task 033 `ReferenceType`, and Task 034 linear `OwnedResourceType`/`OwnedReferenceType`; `void` exists only as a callable return. Task 012 adds invariant unbounded declaration-owned `TypeParameter` values, scoped `TypeVariableReference` leaves, and exact ordered arguments on generic record types. Task 011 adds nominal `ErrorType` only for catch bindings and error constructions, never as an ordinary parameter/local/return value. Task 034 adds compiler-authorized capability/resource/failure/operation declarations, complete resolved `EffectCallExpression` sites, and explicit `OwnedMoveExpression`/`OwnedBorrowExpression` nodes. Every function and reference class/member has a deterministic declaration identity; every list, ordered-map, condition-controlled loop, capability member, and effect site has a deterministic module-local identity. Every direct function, constructor, receiver method, and effect call carries exact resolution facts. Literal and access nodes distinguish scalars, collections, presence/absence, nominal values/references/resources, errors, receivers, and private fields. Unary and binary expressions carry a closed semantic `operation` and explicit result `type`, never a source operator symbol. Every function, constructor, method, entry point, branch, loop, try body, and catch body owns a located `Block` containing ordered `Statement` nodes. Statements additionally include exact `ForEachMapStatement`, strict-Boolean precondition `WhileStatement`, identity-resolved `BreakStatement`/`ContinueStatement`, private receiver-field writes, `RaiseStatement`, and one-handler `TryStatement`; general exception constructs have no semantic node. `lifetime.js` retains distinct normal/return/raise/break/continue ownership states and proves every owner terminal or transferred at every reachable scope exit. Task 033 class identity/private state remains single-module semantics; Task 034's effect/resource nodes stay compiler-authorized and single-module by default, Task 035 extends Task 010 `SemanticProgram` with versioned canonical capability modules and negotiated provider link requirements, and Task 036 adds exact facade-resolution evidence plus compiler-owned source/module roles and a complete selected-facade descriptor.
8
+ 2. `src/semantic/` defines the shared JSDoc-typed discriminated representation and one-based, filename-bearing UTF-16 source locations. `provenance.js` indexes exact source content, deterministic semantic paths/node IDs, resolved symbol IDs, parser-token subranges, and closed source/derived/synthetic origins without exposing parser objects. Task 010's `SemanticProgram` adds dependency-first logical modules, a caller-order multi-source registry, explicit import/export nodes, one selected entry module, and stable module-qualified declaration identities; native filenames, package names, and target paths remain outside semantic resolution. Value types are scalar `integer`/`boolean`/`string` references or recursive acyclic `ListType`, unordered string-keyed `MapType`, insertion-ordered `OrderedMapType`, `OptionalType`, nominal `RecordType`, nominal Task 033 `ReferenceType`, and Task 034 linear `OwnedResourceType`/`OwnedReferenceType`; `void` exists only as a callable return. Task 012 adds invariant unbounded declaration-owned `TypeParameter` values, scoped `TypeVariableReference` leaves, and exact ordered arguments on generic record types. Task 011 adds nominal `ErrorType` only for catch bindings and error constructions, never as an ordinary parameter/local/return value. Task 034 adds compiler-authorized capability/resource/failure/operation declarations, complete resolved `EffectCallExpression` sites, and explicit `OwnedMoveExpression`/`OwnedBorrowExpression` nodes. Every function and reference class/member has a deterministic declaration identity; every list, ordered-map, condition-controlled loop, capability member, and effect site has a deterministic module-local identity. Every direct function, constructor, receiver method, and effect call carries exact resolution facts. Literal and access nodes distinguish scalars, collections, presence/absence, nominal values/references/resources, errors, receivers, and private fields. Unary and binary expressions carry a closed semantic `operation` and explicit result `type`, never a source operator symbol. Every function, constructor, method, entry point, branch, loop, try body, and catch body owns a located `Block` containing ordered `Statement` nodes. Statements additionally include exact `ForEachMapStatement`, strict-Boolean precondition `WhileStatement`, identity-resolved `BreakStatement`/`ContinueStatement`, private receiver-field writes, `RaiseStatement`, `RethrowStatement` with its exact `CatchBinding` node, and `TryStatement` with an ordered `CatchHandler[]` list plus an optional `finallyBody` completion block; general exception constructs beyond that model have no semantic node. `lifetime.js` retains distinct normal/return/raise/break/continue ownership states and proves every owner terminal or transferred at every reachable scope exit. Task 033 class identity/private state remains single-module semantics; Task 034's effect/resource nodes stay compiler-authorized and single-module by default, Task 035 extends Task 010 `SemanticProgram` with versioned canonical capability modules and negotiated provider link requirements, and Task 036 adds exact facade-resolution evidence plus compiler-owned source/module roles and a complete selected-facade descriptor.
9
9
  3. Source and binary backends in `src/backends/` validate their semantic capabilities before emitting target syntax or bytes. Shared validation owns recursive block shape, reachability and all-path abrupt-completion analysis, recursively complete expressions, exhaustive operation/type signatures, function signatures, lexical binding scopes, mutability, declaration-before-use, strict condition/return/argument/initializer/assignment types, fixed-point propagation of nominal unchecked errors, and Task 034 capability/effect/resource graph integrity. `backends/effects.js` rejects unsupported authority/target/context combinations before writer allocation and plans nested eager effects into statement-local collision-safe temporaries in semantic evaluation order. Dependency-first Task 010 validation carries ordinary inferred error sets across resolved function-import identities; both single-module generation and program mode accept compiler-authorized capability modules, while program mode additionally resolves their canonical standard-library contract references and links negotiated target host provider artifacts before any writer allocation. Target-specific validation adds identifier rules, entry-scaffolding name collisions, numeric/memory/resource bounds, and target string representation. The single-module original five retain one fixed compiler-owned Task 034 probe binding, and program mode materializes the same probe behavior as a tree-shaken provider artifact under a protected native binding. Text emitters write through `SourceWriter`; the Wasm encoder writes through an annotated binary writer so every byte range is semantic or explicitly synthetic. A backend either returns a complete executable program/project/application set or raises `UNSUPPORTED_CAPABILITY`; it never silently drops nodes, exposes a partial artifact, or lets an emitter discover malformed shape through a native error.
10
10
 
11
11
  `src/language-registry.js` is the sole ordered dispatch and discovery source. Its deeply frozen records bind implementations to independent frontend, text-backend, binary-backend, application-backend, interoperability, and non-executing developer-check capabilities. Public descriptors derive role booleans, semantic feature flags, artifact multiplicity, round-trip support, rich/v3/byte mapping forms, acceptance stages/toolchains, and the immutable check `supported`/stage/tool subset from those records; all fifteen text targets advertise target-owned check plans. `generalFunctionsAndCalls` is true for PHP, Ruby, JavaScript, TypeScript, Java, Dart, and Zig; every later feature flag remains true only for the original five. `supportedLanguages` is the derived compatibility view containing IDs with both a frontend and text backend. Duplicate IDs, unknown record fields, malformed capabilities, unordered acceptance stages, executing developer-check stages, and undeclared check tools fail as `INVALID_REGISTRY`.
@@ -170,7 +170,7 @@ Construction arguments evaluate left to right and all fields must be initialized
170
170
 
171
171
  The source profiles are intentionally exact. TypeScript uses non-exported classes with explicit `private` fields, one unmodified constructor, and ordinary typed instance methods. JavaScript uses native `#private` fields with adjacent complete JSDoc on fields, constructor parameters, method parameters/returns, and reference-typed bindings. PHP uses `final class`, explicit private typed properties, one public `__construct`, and public typed instance methods. Ruby uses a closed class body with one documented `initialize`; its ordered required parameters initialize the correspondingly named instance variables, and every ordinary method has complete adjacent type comments. Ruby target preflight rejects setter-style method names ending in `=` because native setter invocation returns the assigned argument independently of the method body. Java uses a package-private `final class` with explicit private mutable fields, one package-private constructor, package-private ordinary typed methods, and a separate canonical public `Main`; when a reference method calls a semantic function, generated package-visible static `Main` methods and explicit `Main.method(...)` calls preserve access and evaluation order. All five generated forms reparse to the same semantic class/member identities and execute through the real registered toolchains.
172
172
 
173
- Inheritance, interfaces, traits/mixins, class reopening or monkey-patching, prototypes, decorators/annotations, static/singleton members, computed or dynamic sends, overloads, virtual/duck dispatch, arbitrary host construction, reflection and host lifecycle/protocol hooks, public mutable fields, private access from outside the declaring receiver, identity equality/hashing/ordering, cloning, serialization, finalizers, generic or async methods/classes, and concurrency remain excluded. Constructors and methods may catch Task 011 errors internally but cannot expose an unchecked-error effect through a receiver call; that cross-feature effect contract is not invented by Task 033. Kotlin, Python, C#, Go, C, C++, Rust, Swift, and browser Wasm advertise `referenceClasses: false` and reject class-bearing modules transactionally. Task 033 is single-module: `parseProgram` and `generateProgramArtifactSet` reject class-bearing Task 010 modules before resolution or artifact allocation. It adds no Task 034 resource/effect model and no standard-library contract, facade, provider, or pairwise rewrite.
173
+ Inheritance, interfaces, traits/mixins, class reopening or monkey-patching, prototypes, decorators/annotations, static/singleton members, computed or dynamic sends, overloads, virtual/duck dispatch, arbitrary host construction, reflection and host lifecycle/protocol hooks, public mutable fields, private access from outside the declaring receiver, identity equality/hashing/ordering, cloning, serialization, finalizers, generic or async methods/classes, and concurrency remain excluded. The bounded subtyping model that extends this cohort — nominal interfaces with exact `implements` override contracts and no class-to-class inheritance — is decided in the [bounded subtyping ADR](adr-bounded-subtyping.md). It is adopted for PHP only today (`boundedSubtyping: true`); Ruby, JavaScript, TypeScript, and Java keep `boundedSubtyping: false` until their per-language interface profiles land, and every other target rejects interface-bearing modules transactionally. Nothing in that ADR changes the Task 033 semantics documented above. Constructors and methods may catch Task 011 errors internally but cannot expose an unchecked-error effect through a receiver call; that cross-feature effect contract is not invented by Task 033. Kotlin, Python, C#, Go, C, C++, Rust, Swift, and browser Wasm advertise `referenceClasses: false` and reject class-bearing modules transactionally. Task 033 is single-module: `parseProgram` and `generateProgramArtifactSet` reject class-bearing Task 010 modules before resolution or artifact allocation. It adds no Task 034 resource/effect model and no standard-library contract, facade, provider, or pairwise rewrite.
174
174
 
175
175
  ## Task 034 effectful capabilities and owned resource lifetime
176
176
 
@@ -206,18 +206,18 @@ Each parser adapter recognizes only its canonical native module form: named rela
206
206
 
207
207
  ## Task 011 typed errors and handling
208
208
 
209
- The original-five cohort implements one nominal unchecked-error model: `ErrorDeclaration`, identity-bound `ErrorType`, `ErrorConstruction` with exactly one semantic string message, abrupt `RaiseStatement`, and `TryStatement` with one exact `catchType`, one immutable block-local `CatchBinding`, and one `catchBody`. `ErrorMessageRead` is the only portable member operation. A handler removes only its exact nominal identity from the protected block's escaping errors; unmatched errors and errors raised by the handler continue outward. Catch entry discards known-value and optional-presence facts for every outer mutable binding assignable anywhere in the protected body, because a raise may transfer control after any preceding assignment. Internal fixed-point analysis follows direct calls so a catch around a helper call is reachable without adding checked exception declarations or public effect lists. Raise, return, break, and continue remain abrupt for reachability and return-completeness analysis.
209
+ The original-five cohort implements one nominal unchecked-error model: `ErrorDeclaration`, identity-bound `ErrorType`, `ErrorConstruction` with exactly one semantic string message, abrupt `RaiseStatement`, `RethrowStatement` re-propagating one in-scope catch binding through its exact `CatchBinding` node, and `TryStatement` with an ordered `CatchHandler[]` list plus an optional `finallyBody` completion block. Each handler carries one exact `catchType`, one immutable block-local `CatchBinding`, and one `catchBody`. `ErrorMessageRead` is the only portable member operation. Handlers are evaluated top to bottom, and a handler removes only its exact nominal identity from the protected block's escaping errors; unmatched errors and errors raised by a handler continue outward past every remaining sibling handler, and a handler whose exact identity cannot escape — including one shadowed by an earlier handler for the same identity — fails `UNREACHABLE_HANDLER`. Catch entry discards known-value and optional-presence facts for every outer mutable binding assignable anywhere in the protected body, because a raise may transfer control after any preceding assignment. `finallyBody` runs on every normal and abrupt completion; a completion block that itself completes abruptly replaces the pending completion and its raises, while a normally completing completion block passes the pending completion through unchanged. A `try` without handlers is accepted only with a completion block. Internal fixed-point analysis follows direct calls so a catch around a helper call is reachable without adding checked exception declarations or public effect lists. Raise, return, break, and continue remain abrupt for reachability and return-completeness analysis.
210
210
 
211
211
  The accepted parser-backed forms are deliberately exact:
212
212
 
213
- - Ruby uses an empty `class E < StandardError`, `raise E, message`, and `begin` with one explicit `rescue E => error`. Default/broad or multiple rescue, rescue modifiers, bare raise/re-raise, retry, else, ensure, and fatal/system classes are rejected.
214
- - JavaScript/JSDoc and TypeScript use an empty `class E extends Error`, `throw new E(message)`, and one catch whose first statement is the exact `if (!(error instanceof E)) { throw error }` unmatched-value guard. An imported error alias resolves as a constructor only when no parameter, local, iteration, or catch binding shadows that spelling. Unguarded catches, alternate narrowing/assertions, arbitrary thrown values, `AggregateError`, causes, and `finally` are rejected. The guard is target syntax only and reparses back into the single semantic handler.
215
- - PHP uses `final class E extends RuntimeException`, exact construction/throw, and one `catch (E $error)`. Multi-catch, `Throwable`/`Exception`/`Error`, previous exceptions, throw expressions in value contexts, and `finally` are rejected. Generated namespaced projects qualify the native `\RuntimeException` base explicitly.
216
- - Java uses a final unchecked `RuntimeException` subclass with exactly one `String message` constructor forwarding to `super(message)`, exact construction/throw, and one typed catch. Checked exceptions, `throws`, multi-catch, resources, broad catches, suppressed/cause behavior, and `finally` are rejected.
213
+ - Ruby uses an empty `class E < StandardError`, `raise E, message`, `raise <binding>` or bare `raise` inside one exact rescue, and `begin` with an ordered list of explicit `rescue E => error` clauses plus one optional `ensure` block. Default/broad rescue, rescue modifiers, retry, redo, else, and fatal/system classes are rejected.
214
+ - JavaScript/JSDoc and TypeScript use an empty `class E extends Error`, `throw new E(message)`, `throw <binding>`, and catches whose first statement is the exact `if (!(error instanceof E)) { throw error }` unmatched-value guard. Because the language has no sibling catch clauses, an ordered multi-handler list is written and emitted as a guarded dispatch chain: each non-final guard rethrows its own binding into a nested `try` whose single clause is the next handler, and each handler body runs outside its guard so a handler-body raise escapes past every remaining handler. An imported error alias resolves as a constructor only when no parameter, local, iteration, or catch binding shadows that spelling. Unguarded catches, alternate narrowing/assertions, arbitrary thrown values, `AggregateError`, causes, and any deviation from the exact chain shape are rejected. The guards and dispatch nesting are target syntax only and reparse back into one ordered semantic handler list, together with an optional native `finally` block.
215
+ - PHP uses `final class E extends RuntimeException`, exact construction/throw, `throw $<binding>`, an ordered list of `catch (E $error)` clauses, and one optional `finally` block. Broad `Throwable`/`Exception`/`Error` catches, previous exceptions, throw expressions in value contexts, and rethrow of a non-binding value are rejected. Generated namespaced projects qualify the native `\RuntimeException` base explicitly.
216
+ - Java uses a final unchecked `RuntimeException` subclass with exactly one `String message` constructor forwarding to `super(message)`, exact construction/throw, `throw <binding>`, an ordered list of typed catches, and one optional `finally` block. Checked exceptions, `throws`, union catches, resources, broad catches, and suppressed/cause behavior are rejected.
217
217
 
218
218
  Error declaration/type/construction, raise, try/body, catch type/binding/body, and message-read nodes participate in deterministic source provenance, symbols, rich mappings, and Source Map v3. Task 010 programs preserve module-qualified `module#error:N` identities through imports, generation, and reparse. All backend graph, location, target-name, handler-shape, binding, payload, and effect validation completes before writer allocation. Kotlin, Python, C#, Go, C, C++, Rust, Swift, and browser Wasm advertise `typedErrors: false` and reject any Task 011 IR with located `UNSUPPORTED_CAPABILITY`; there is no C status-code, Rust `Result`, Wasm trap, or other substitute.
219
219
 
220
- This feature does not model arbitrary error values, multiple/union/broad handlers, checked effects, error result types, stack traces, causes, suppression, fatal/process errors, asynchronous rejection, `finally`/`ensure`, general classes/methods, or standard-library portability. It adds no pairwise rewrite and does not expand Task 013's historical Tasks 001–008 cohort.
220
+ This feature does not model arbitrary error values, union/broad/default handlers, checked effects or `throws` signatures (a permanent exclusion — see the [typed-error handling extension ADR](adr/20261026-typed-error-handling-extension.md)), error result types, stack traces, causes, suppression, fatal/process errors, asynchronous rejection, Ruby `retry`/`redo`/`else`/rescue modifiers, try-with-resources, general classes/methods, or standard-library portability. It adds no pairwise rewrite and does not expand Task 013's historical Tasks 001–008 cohort.
221
221
 
222
222
  ## Task 012 type parameters and generic declarations
223
223
 
@@ -354,7 +354,7 @@ All fifteen frontends preserve exact input content and parser-backed semantic to
354
354
 
355
355
  `UNSUPPORTED_LANGUAGE` is exclusive to unknown or misspelled IDs. `UNSUPPORTED_ROLE` reports a known ID without the requested frontend, text/binary/application backend, artifact, or interoperability role, including C#, Go, C, Rust, Dart, and Zig through a legacy single-artifact API. `UNSUPPORTED_CAPABILITY` reports a role that exists but cannot represent the requested semantic module or target feature. `UNSUPPORTED_SYNTAX` reports parsed constructs outside the subset with the best parser-provided source location. Missing public annotations use `MISSING_TYPE`; a present unsupported type is `UNSUPPORTED_SYNTAX` at that annotation. Parser failures use `PARSE_ERROR`. Semantic resolution/type failures use `DUPLICATE_BINDING`, `UNRESOLVED_BINDING`, `USE_BEFORE_DECLARATION`, `IMMUTABLE_ASSIGNMENT`, and `TYPE_MISMATCH`; Task 005 additionally uses `VOID_AS_VALUE`, `VOID_RETURN_VALUE`, and `MISSING_RETURN_VALUE`. Task 033 adds `DUPLICATE_CLASS`, `DUPLICATE_PRIVATE_FIELD`, `DUPLICATE_CONSTRUCTOR`, `DUPLICATE_METHOD`, `INCOMPLETE_INITIALIZATION`, `ILLEGAL_PRIVATE_ACCESS`, `UNKNOWN_CLASS`, `INVALID_METHOD_RECEIVER`, `UNKNOWN_METHOD`, `CONSTRUCTOR_ARITY_MISMATCH`, and `METHOD_ARITY_MISMATCH`. Task 034 adds `MISSING_CAPABILITY_AUTHORITY`, `INVALID_CAPABILITY_AUTHORITY`, `UNDECLARED_EFFECT`, `UNDECLARED_FAILURE`, `INVALID_EFFECT_ORDER`, `RESOURCE_TYPE_MISMATCH`, `RESOURCE_MOVE_REQUIRED`, `INVALID_RESOURCE_TRANSFER`, `RESOURCE_ALIAS`, `INVALID_RESOURCE_BORROW`, `BORROW_ESCAPE`, `USE_AFTER_MOVE`, `USE_AFTER_CLOSE`, `RESOURCE_STATE_MISMATCH`, `LEAKED_RESOURCE`, and `FAILURE_ABSENCE_CONFLATION`; a repeated close in the bounded probe uses its declared terminal failure rather than `DOUBLE_CLOSE`. Task 035 adds `STDLIB_CONTRACT_INVALID`, `STDLIB_MODULE_UNKNOWN`, `STDLIB_OPERATION_UNDECLARED`, `STDLIB_VERSION_INCOMPATIBLE`, `STDLIB_VERSION_MALFORMED`, `STDLIB_PROVIDER_INVALID`, `STDLIB_PROVIDER_MISSING`, `STDLIB_PROVIDER_AMBIGUOUS`, `STDLIB_PROVIDER_DEPENDENCY_MISSING`, `STDLIB_PROVIDER_CYCLE`, `STDLIB_LINK_FAILURE`, and `STDLIB_NAME_COLLISION`. Task 036 adds `STDLIB_FACADE_INVALID`, `STDLIB_FACADE_IDENTITY_UNPROVED`, `STDLIB_FACADE_MEMBER_UNSUPPORTED`, `STDLIB_FACADE_VERSION_MALFORMED`, `STDLIB_FACADE_VERSION_INCOMPATIBLE`, `STDLIB_FACADE_DYNAMIC_REFERENCE`, `STDLIB_FACADE_REFLECTION_UNSUPPORTED`, `STDLIB_FACADE_NATIVE_EXTENSION_UNSUPPORTED`, `STDLIB_FACADE_SHADOWED`, `STDLIB_FACADE_REOPENED`, `STDLIB_FACADE_AUTHORITY_FORGED`, `STDLIB_FACADE_NAME_COLLISION`, `STDLIB_FACADE_DECLARATION_MISMATCH`, and `STDLIB_FACADE_DESCRIPTOR_INVALID`; Task 037 additionally uses `STDLIB_PROTECTED_BINDING_ACCESS` for portable attempts to reach canonical operations directly. Resolution failures retain parser locations, while descriptor/link failures are transactional. Flow failures use `NON_BOOLEAN_CONDITION`, `MISSING_RETURN`, `UNREACHABLE_STATEMENT`, `ILLEGAL_RETURN_CONTEXT`, `ILLEGAL_BREAK_CONTEXT`, `ILLEGAL_CONTINUE_CONTEXT`, `INVALID_LOOP_TARGET`, `CROSS_FUNCTION_LOOP_TARGET`, and `UNSUPPORTED_STATEMENT`. Backends validate complete recursive block shape, flow, scalar payloads, binding/type agreement, call resolutions, mutability, loop context, identifiers, target bounds, and fixed artifact contracts before emission. Missing or structurally invalid caller IR reports located `UNSUPPORTED_CAPABILITY` rather than leaking a native error or producing partial source. A non-`Main.java` Java basename, non-`Program.cs` C# filename, non-`main.go` Go filename, non-`program.c` C filename, non-`src/main.rs` Rust filename, non-`bin/program.dart` Dart filename, or non-`src/main.zig` Zig filename uses that same contract. Artifact-set generation requires a non-empty string language/target ID and accepts only the explicit `text`, `binary`, and `application` backend-role strings; malformed IDs or roles report `INVALID_ARTIFACT_SET` without coercing caller input, while an unknown string ID reports `UNSUPPORTED_LANGUAGE` and a valid role absent from a known registry record reports `UNSUPPORTED_ROLE`. Optional registry roles and backend metadata may be omitted with `undefined`; present malformed values, including `null`, report `INVALID_REGISTRY`. Invalid registry, artifact-set, byte-map, discovery, and runner inputs have their own normalized diagnostics rather than leaking native type or subprocess errors.
356
356
 
357
- Boxed or coercive types, inferred public types, TypeScript literal/enum/`any`/`unknown`/arbitrary-union types, PHP aliases or implicit nullable declarations, Ruby `boolish`, Java `Boolean`/`char`, universal null values, floating point, decimal, bigint, symbols, regex values, and byte strings are unsupported. Task 048 admits exactly two expression families: strict-Boolean conditional (ternary) expressions in the original-five cohort, and parser-proved string interpolation in PHP, Ruby, JavaScript, and TypeScript lowered to checked `StringConcat` concatenation of pre-rendered parts with `string`-typed segments only. Division, remainder, exponentiation, bitwise/shift/update/compound operations, Ruby/PHP `and`/`or`, loose equality, nullish forms, optional/safe navigation, ranges, general casts/assertions, collection/object equality, operator overloading, implicit coercion, and constant folding are also excluded; the sole assertion exception is Task 006's exact TypeScript `Map#get(...)!` wrapper, whose lookup must be independently total. Task 007 supplies only the exact explicit optional model, Task 008 only exact ordered list loops, Task 010 only explicit supplied source projects, Task 011 only the exact nominal unchecked-error model above, Task 012 only the exact unbounded invariant generic profile above, Task 014 only the exact ordered-map pair loop above, Task 032 only the exact strict-Boolean precondition loop above, Task 033 only the exact closed reference-class profile above, Task 034 only the explicit single-module probe effect/resource profile above, and Task 048 only the exact cohort ternary and interpolation lowering above. There is no truthiness, implicit-return or general statement-value model, async/generator model, open/inherited/virtual class model, implicit import discovery, post-condition/basic/range loops, arbitrary or ordinary-map iteration, switches, exception behavior beyond Task 011 and declared Task 034 failures, overloads, advanced generic/effect type systems, implicit resource cleanup, unsafe native handles, package-manager resolution, or source-format preservation. The sole value-free expression statements are resolved void function and method calls in the original-five cohort. Ruby type comments remain a bounded RBS-style convention rather than a full signature parser, and single-module Java generation retains the fixed `Main.java` contract.
357
+ Boxed or coercive types, inferred public types, TypeScript literal/enum/`any`/`unknown`/arbitrary-union types, PHP aliases or implicit nullable declarations, Ruby `boolish`, Java `Boolean`/`char`, universal null values, floating point, decimal, bigint, symbols, regex values, and byte strings are unsupported. The portable floating-point/decimal numeric tower is decided in the [numeric tower ADR](adr-numeric-tower.md) and remains unimplemented; nothing in that ADR changes the integer semantics documented above, and `bigint` stays a separate follow-up decision. Task 048 admits exactly two expression families: strict-Boolean conditional (ternary) expressions in the original-five cohort, and parser-proved string interpolation in PHP, Ruby, JavaScript, and TypeScript lowered to checked `StringConcat` concatenation of pre-rendered parts with `string`-typed segments only. Division, remainder, exponentiation, bitwise/shift/update/compound operations, Ruby/PHP `and`/`or`, loose equality, nullish forms, optional/safe navigation, ranges, general casts/assertions, collection/object equality, operator overloading, implicit coercion, and constant folding are also excluded; the sole assertion exception is Task 006's exact TypeScript `Map#get(...)!` wrapper, whose lookup must be independently total. Task 007 supplies only the exact explicit optional model, Task 008 only exact ordered list loops, Task 010 only explicit supplied source projects, Task 011 only the exact nominal unchecked-error model above, Task 012 only the exact unbounded invariant generic profile above, Task 014 only the exact ordered-map pair loop above, Task 032 only the exact strict-Boolean precondition loop above, Task 033 only the exact closed reference-class profile above, Task 034 only the explicit single-module probe effect/resource profile above, and Task 048 only the exact cohort ternary and interpolation lowering above. There is no truthiness, implicit-return or general statement-value model, async/generator model, open/inherited/virtual class model, implicit import discovery, post-condition/basic/range loops, arbitrary or ordinary-map iteration, switches, exception behavior beyond Task 011 and declared Task 034 failures, overloads, advanced generic/effect type systems, implicit resource cleanup, unsafe native handles, package-manager resolution, or source-format preservation. The sole value-free expression statements are resolved void function and method calls in the original-five cohort. Ruby type comments remain a bounded RBS-style convention rather than a full signature parser, and single-module Java generation retains the fixed `Main.java` contract.
358
358
 
359
359
  CPP uses its own [C++20 scalar and scaffold profile](cpp.md), official CPP grammar, one `program.cpp`, by-value owned strings and checked arithmetic. The shared native occurrence planner is backend-only; CPP owns its exact marker and CST recognizer. Native O0/O2 and ASan/UBSan/leak execution are mandatory.
360
360
 
package/docs/testing.md CHANGED
@@ -53,7 +53,7 @@ Task 013 is the separate terminal original-five aggregate gate. Task 005 does no
53
53
 
54
54
  ## Task 011 typed errors and handling
55
55
 
56
- Task 011 uses six focused specs. Semantic validation covers exact nominal types, string-only construction, abrupt raise, exact handler consumption, unmatched and handler-originated propagation, immutable scoped catch bindings, catch-entry invalidation of optional-presence facts after protected-body assignments, message-only access, unreachable handlers, and fixed-point direct-call effects without checked signatures. Frontend coverage parses the canonical Ruby, JavaScript/JSDoc, TypeScript, PHP, and Java forms through their real parser nodes and rejects arbitrary throws, broad or unguarded catches, JavaScript-family imported error aliases shadowed by parameter/local/iteration/catch bindings, multiple handlers, checked exceptions, retry/rethrow, resources, and `finally`/`ensure`/`else`. Backend coverage reparses all five generated targets, checks target guard spelling, rejects PHP assigned-only special names in catch bindings, validates caller-authored shapes before allocation, and requires deterministic `UNSUPPORTED_CAPABILITY` from every other registered target. Registry and provenance specs lock the public `typedErrors` flag, error/catch symbols, source roles, and rich/v3 mapping behavior.
56
+ Task 011 uses eight focused specs. Semantic validation covers exact nominal types, string-only construction, abrupt raise, exact handler consumption, unmatched and handler-originated propagation, immutable scoped catch bindings, catch-entry invalidation of optional-presence facts after protected-body assignments, message-only access, unreachable handlers, and fixed-point direct-call effects without checked signatures. Frontend coverage parses the canonical Ruby, JavaScript/JSDoc, TypeScript, PHP, and Java forms through their real parser nodes and rejects arbitrary throws, broad or unguarded catches, JavaScript-family imported error aliases shadowed by parameter/local/iteration/catch bindings, union or default handlers, checked exceptions, Ruby `retry`/`redo`/`else`/rescue modifiers, resources, and any deviation from the exact JavaScript/TypeScript guarded dispatch chain. Backend coverage reparses all five generated targets, checks target guard spelling, rejects PHP assigned-only special names in catch bindings, validates caller-authored shapes before allocation, and requires deterministic `UNSUPPORTED_CAPABILITY` from every other registered target. Registry and provenance specs lock the public `typedErrors` flag, error/catch symbols, source roles, and rich/v3 mapping behavior. The handling-extension spec locks the ordered multi-catch, `finally`/`ensure`, and rethrow extension across the cohort: one semantic meaning from each language's exact native form, generation and reparse for all five targets, handler-body raises escaping past remaining sibling handlers, completion-block interaction with return/raise and nested try, rethrow of a lexically visible outer catch binding, `UNREACHABLE_HANDLER` for a handler shadowed by an earlier handler of the same identity, and multi-file program preservation.
57
57
 
58
58
  The multi-file runtime spec parses five caller-supplied TypeScript modules, preserves both exported `module#error:0` identities plus a transitive two-hop function effect through every generated project and reparse, and executes PHP, Ruby, Node, strict TypeScript through `tsc` plus Node, and Java through `javac` plus `java`. Its exact `ok\nbad\ndeep\n` output proves the non-throw path, one exact catch, and an unmatched error raised by the inner handler propagating to the outer exact handler. Missing commands fail; no test skips, downloads, substitutes, or scans source text.
59
59
 
@@ -65,10 +65,12 @@ npx velocious-test spec/typed-error-frontends.spec.js
65
65
  npx velocious-test spec/typed-error-backends.spec.js
66
66
  npx velocious-test spec/typed-error-language-registry.spec.js
67
67
  npx velocious-test spec/typed-error-provenance-and-mapping.spec.js
68
+ npx velocious-test spec/typed-error-handling-extension.spec.js
68
69
  LANG=C.UTF-8 LC_ALL=C.UTF-8 npx velocious-test spec/typed-error-multifile-runtime.spec.js
70
+ LANG=C.UTF-8 LC_ALL=C.UTF-8 npx velocious-test spec/typed-error-handling-extension-runtime.spec.js
69
71
  ```
70
72
 
71
- Task 011 adds no checked exceptions/effects, arbitrary thrown values, multiple/union/broad catches, `finally`/`ensure`, async rejection, result types, non-original-five lowering, standard-library portability, pairwise rewrites, publication, or deployment behavior. Task 013 remains the historical Tasks 001–008 compatibility gate.
73
+ Task 011's handling extension adds ordered multi-catch, `finally`/`ensure`, and explicit rethrow for the original five and still adds no checked exceptions/effects or `throws` signatures, arbitrary thrown values, union/broad/default catches, Ruby `retry`/`redo`/`else`/rescue modifiers, try-with-resources, async rejection, result types, non-original-five lowering, standard-library portability, pairwise rewrites, publication, or deployment behavior. Task 013 remains the historical Tasks 001–008 compatibility gate.
72
74
 
73
75
  ## Task 012 type parameters and generic declarations
74
76
 
@@ -379,7 +381,7 @@ Task 016 extends the same fail-loud discovery and staged runner to configured Py
379
381
 
380
382
  Task 017 adds the distinct ordered `restore` stage and configured .NET SDK 10 discovery. C# acceptance generates exactly `Program.cs` and `Semantifold.csproj`, then invokes `dotnet restore Semantifold.csproj --source . --no-cache --force --disable-parallel --nologo`, `dotnet build Semantifold.csproj --configuration Release --no-restore --nologo --warnaserror`, and `dotnet exec bin/Release/net10.0/Semantifold.dll` as separate stages. Tests set the .NET telemetry/first-run/no-logo/invariant variables and an isolated absolute `NUGET_PACKAGES` path. They require exact exit/stdout and no warning output. Two fresh directories must produce identical ordered artifact text/mappings/provenance and identical `Semantifold.dll`, `.deps.json`, and `.runtimeconfig.json`; path-bearing restore bookkeeping under `obj/` is intentionally not compared. Missing .NET, restore dependency/network demand, warnings, SDK major mismatch, or nondeterministic compiler output fails without a skip.
381
383
 
382
- Task 024 adds configured Go 1.26.x discovery restricted to Linux/amd64; the patch release is read from the discovered toolchain rather than fixed by runtime tests. Acceptance obtains `GOROOT` from that exact `go`, requires the companion `gofmt` beside the configured or discovered executable to resolve to the same `GOROOT/bin/gofmt`, and checks `gofmt -d` before native execution. Each dependency-free generated module is run with `GOTOOLCHAIN=local`, `GOPROXY=off`, `GOSUMDB=off`, `GOVCS=off`, `CGO_ENABLED=0`, `GOENV=off`, `GOWORK=off`, `GOOS=linux`, `GOARCH=amd64`, and isolated HOME/GOPATH/build/module caches. The short-lived discovery fixture also writes exact `off` mode into its isolated Go telemetry directory before invoking the tool, then asserts that the mode file is the only telemetry state: Go 1.26 otherwise starts an unawaited uploader sidecar even in default `local` mode, which can recreate `upload/` while the fixture root is being removed. Cleanup errors remain fatal. Stages run offline `go build`, mandatory `go vet`, and `go run`, while compiler-only differential cases use the matching `go tool compile`. Tests require exact status/stdout, unchanged `go.mod`/`main.go`, no `go.sum`/`go.work`/vendor tree, and byte-identical binaries and output in two fresh directories. Missing, wrong-minor, wrong-architecture, mismatched-gofmt, download, cgo, workspace, vet, formatting, nondeterminism, telemetry leakage, or cleanup evidence fails without a skip.
384
+ Task 024 adds configured Go 1.26.x discovery restricted to Linux/amd64; the patch release is read from the discovered toolchain rather than fixed by runtime tests. Acceptance obtains `GOROOT` from that exact `go`, requires the companion `gofmt` beside the configured or discovered executable to resolve to the same `GOROOT/bin/gofmt`, and checks `gofmt -d` before native execution. Each dependency-free generated module is run with `GOTOOLCHAIN=local`, `GOPROXY=off`, `GOSUMDB=off`, `GOVCS=off`, `CGO_ENABLED=0`, `GOENV=off`, `GOWORK=off`, `GOOS=linux`, `GOARCH=amd64`, and isolated HOME/GOPATH/module caches over one content-addressed run-scoped Go build cache, which is removed when the test process exits. The short-lived discovery fixture also writes exact `off` mode into its isolated Go telemetry directory before invoking the tool, then asserts that the mode file is the only telemetry state: Go 1.26 otherwise starts an unawaited uploader sidecar even in default `local` mode, which can recreate `upload/` while the fixture root is being removed. Cleanup errors remain fatal. Stages run offline `go build`, mandatory `go vet`, and `go run`, while compiler-only differential cases use the matching `go tool compile`. Tests require exact status/stdout, unchanged `go.mod`/`main.go`, no `go.sum`/`go.work`/vendor tree, and byte-identical binaries and output in two fresh directories. Missing, wrong-minor, wrong-architecture, mismatched-gofmt, download, cgo, workspace, vet, formatting, nondeterminism, telemetry leakage, or cleanup evidence fails without a skip.
383
385
 
384
386
  Task 023 adds exact Kotlin/JVM 2.4.20 discovery on pinned OpenJDK 25 Ubuntu runtimes. Ubuntu26.04 development uses exact `25.0.4.1+1-1~26.04.4`; Ubuntu24.04 TensorBuzz uses exact `25.0.4.1+1-1~24.04.4`. Bootstrap and discovery accept only those two qualified compiler-reported package identities, never arbitrary output. Original and generated `Program.kt` files compile with `-language-version 2.4 -api-version 2.4 -jvm-target 25 -Werror -include-runtime` into runnable `Program.jar` files, then execute through the exact `java25` discovery contract. Every process uses a fresh directory and exact status/stderr/UTF-8 stdout assertions; no test downloads a compiler or dependency.
385
387
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "semantifold",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Language-neutral semantic code analysis, transformation, generation, and execution toolkit",
5
5
  "type": "module",
6
6
  "main": "build/index.js",