Skip to content

Commit dc6a893

Browse files
docs(adr): ESC-04 — ADR-017 ReScript block-module disposition (Refs #262 #229) (#271)
AffineScript is strictly one-module-per-file (ADR-011 file=module; grammar parser.mly:130-134 — `module A { }` parse-errors). #229 ports carry ReScript block modules; single-per-file is mechanical (canonical map), multi-block-per-file had no clean target (ESC-04 #262). ADR-017 (accepted): one module per file — split, do not nest. Each `module X { body }` -> its own X.affine with a `module X;` header; N block-modules -> N files. Grammar NOT extended (ADR-011/ADR-012). No compiler change — settles the porting doctrine + #229 canonical-map structural rule. The split-file Resolve.UndefinedModule is cross-module graph coherence (INT-02), tracked in RESCRIPT-ELIMINATION Tier-4, NOT conflated here. ADR-017 is sequential between ADR-016 (effect-threaded async-boundary, #234/#270) and ADR-018 (no-raw-escape, #245/#272); inserted in order in SETTLED-DECISIONS + META.a2ml + RESCRIPT-ELIMINATION ESC-04 row. Reconstructed cleanly on current main (the original branch's ADR-016 ->017 renumber history is collapsed to one in-order commit; supersedes the prior #271 head). Docs-only; gate unaffected. Refs #262 #229. Co-authored-by: hyperpolymath <hyperpolymath@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a93efe8 commit dc6a893

3 files changed

Lines changed: 92 additions & 3 deletions

File tree

.machine_readable/6a2/META.a2ml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,57 @@ references = [
10961096
"docs/specs/SETTLED-DECISIONS.adoc (ADR-016 section)",
10971097
]
10981098

1099+
[[adr]]
1100+
id = "ADR-017"
1101+
status = "accepted"
1102+
date = "2026-05-19"
1103+
title = "ReScript block-module disposition: one module per file (split, do not nest)"
1104+
context = """
1105+
ReScript `module Name { … }` block modules have no AffineScript block
1106+
form: the grammar (parser.mly:130-134) is a single optional `module
1107+
Path;` header, before imports — `module A { }` parse-errors. ADR-011
1108+
settled "real modules": the file IS the module. The estate #229 ports
1109+
carry block modules; a SINGLE one per file is mechanical (hoist header,
1110+
drop braces, dedent — in the #229 canonical map, verified to parse), but
1111+
MULTIPLE block-modules in one file (e.g. standards/lol/.../OpenCyc.affine:
1112+
module Config{} module Concepts{} module Types{}…; 14 estate
1113+
occurrences) had no clean target. Escalated language-side as ESC-04
1114+
(#262) — the bidirectional-evidence discipline of ADR-014 / #228.
1115+
(ADR-016 is effect-threaded async-boundary, #234/#270; ADR-018 is the
1116+
no-raw-escape doctrine, #245 — this is ADR-017, sequential.)
1117+
"""
1118+
decision = """
1119+
One module per file — split, do not nest. Each ReScript `module X {
1120+
body }` becomes its own `X.affine` whose first declaration is `module
1121+
X;`, body dedented, `use` after the header. A file with N block-modules
1122+
is split into N files. The grammar is NOT extended with a block/nested
1123+
module form: that contradicts ADR-011 (file = module) and is a major
1124+
conflict-risky grammar change for cosmetic gain — the contortion ADR-012
1125+
forbids. No compiler change (the file-header form already parses); this
1126+
ADR settles the porting doctrine + the #229 canonical-map structural
1127+
rule. Adjacent and explicitly OUT of scope: a split file still hits
1128+
Resolve.UndefinedModule until the repo module-path<->file-layout matches
1129+
the loader — cross-module graph coherence (INT-02 loader-bridge),
1130+
tracked in RESCRIPT-ELIMINATION.adoc Tier-4, never conflated here.
1131+
"""
1132+
consequences = """
1133+
- The #229 block-module residue (idaptik-dlc-vm, standards/lol, parts of
1134+
burble) has a defined target: split-per-file. Full validation still
1135+
needs the per-repo module graph (INT-02), tracked separately.
1136+
- No language/compiler change; no estate consumer churn from this ADR.
1137+
- This decision is settled; do not reopen without amending this ADR.
1138+
ESC-04 #262; #229 canonical map in docs/RESCRIPT-ELIMINATION.adoc.
1139+
"""
1140+
references = [
1141+
"https://github.com/hyperpolymath/affinescript/issues/262",
1142+
"https://github.com/hyperpolymath/affinescript/issues/229",
1143+
"lib/parser.mly (module_decl; one `module Path;` header per file)",
1144+
"docs/specs/SETTLED-DECISIONS.adoc (ADR-017 section)",
1145+
"docs/RESCRIPT-ELIMINATION.adoc (#229 canonical map; Tier-4 INT-02)",
1146+
"META.a2ml [[adr]] ADR-011 (real modules: file = module)",
1147+
"META.a2ml [[adr]] ADR-012 (grammar changes are correctness assertions)",
1148+
]
1149+
10991150
[[adr]]
11001151
id = "ADR-018"
11011152
status = "accepted"

docs/RESCRIPT-ELIMINATION.adoc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,13 @@ ReScript `import P [as A]`→`use P [as A];`, single block-module
273273
`module P;` header (hoisted before imports — `parser.mly:130-134`
274274
requires the header first), braces dropped, body dedented one level.
275275
Oracle-verified to parse.
276-
* *Tier-3 ESC-04 (#262):* *multi*-block-module per file (e.g.
277-
`standards/lol/.../OpenCyc.affine`) has *no clean target* —
278-
AffineScript is one-module-per-file. Escalated, not guessed.
276+
* *ESC-04 (#262) — SETTLED by ADR-017:* AffineScript is strictly
277+
one-module-per-file. Doctrine: *split, do not nest* — each ReScript
278+
`module X { body }` (incl. each of N in a multi-block file like
279+
`standards/lol/.../OpenCyc.affine`) becomes its own `X.affine` with a
280+
`module X;` header. No grammar change (ADR-011 file=module; a
281+
block-module form is the ADR-012-forbidden contortion). Full
282+
validation still needs the per-repo module graph (INT-02, Tier-4).
279283

280284
=== Reclassified OUT of #229 — permanently (scanner over-scope)
281285

docs/specs/SETTLED-DECISIONS.adoc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,40 @@ This decision is settled; do not reopen without amending the ADR. Full
343343
ADR in `.machine_readable/6a2/META.a2ml` (ADR-016); ledger #234 /
344344
CORE-02 in `docs/TECH-DEBT.adoc`.
345345

346+
== ReScript Block-Module Disposition: One Module Per File (ADR-017)
347+
348+
ReScript `module Name { … }` block modules have no AffineScript block
349+
form: the grammar (`parser.mly:130-134`) is a single optional
350+
`module Path;` *header*, before imports — `module A { }` parse-errors.
351+
ADR-011 already settled "real modules": *the file is the module*. The
352+
estate ReScript→AffineScript ports (#229) carry block modules — a single
353+
one per file is mechanical (hoist the header, drop the braces, dedent;
354+
already in the #229 canonical map and verified to parse), but *multiple*
355+
block-modules in one file (e.g. `standards/lol/.../OpenCyc.affine`:
356+
`module Config { } module Concepts { } module Types { } …`, 14 estate
357+
occurrences) had no clean target. Escalated language-side as ESC-04
358+
(#262), the same bidirectional-evidence discipline as ADR-014 / #228.
359+
360+
Decision: *one module per file — split, do not nest.* Each ReScript
361+
`module X { body }` becomes its own `X.affine` whose first declaration is
362+
`module X;`, body dedented, `use` imports after the header. A file with N
363+
block-modules is split into N files. The grammar is **not** extended with
364+
a block/nested-module form: that would contradict ADR-011 (file = module)
365+
and is a major, conflict-risky grammar change for cosmetic gain — exactly
366+
the contortion ADR-012 forbids. No language/compiler change is needed
367+
(the file-header form already parses); this ADR settles the *porting
368+
doctrine* + the #229 canonical-map structural rule.
369+
370+
Adjacent, explicitly NOT in scope here: a split file still hits
371+
`Resolve.UndefinedModule` until the repo's module-path↔file-layout
372+
matches the loader — that is cross-module graph coherence (INT-02
373+
loader-bridge territory), tracked in `RESCRIPT-ELIMINATION.adoc` Tier-4
374+
and the INT-02 ledger, never conflated with this disposition.
375+
376+
This decision is settled; do not reopen without amending the ADR. Full
377+
ADR in `.machine_readable/6a2/META.a2ml` (ADR-017); #229 canonical map in
378+
`docs/RESCRIPT-ELIMINATION.adoc`; escalation issue #262.
379+
346380
== No Raw/FFI Escape: Typed `extern` Is the Only Host Bridge (ADR-018)
347381

348382
ReScript `%%raw("<host source>")` / `%raw` injects arbitrary untyped host

0 commit comments

Comments
 (0)