From 7321826c663c18b8da6e99bf670f8165d70f4c64 Mon Sep 17 00:00:00 2001 From: Brummel Date: Sat, 30 May 2026 15:18:49 +0200 Subject: [PATCH] =?UTF-8?q?audit:=20cycle-close=20tidy=20for=20#44=20?= =?UTF-8?q?=E2=80=94=20`$`-lexer-reservation=20(refs=20#44)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cycle-close audit for the #44 Form-A `$`-reservation cycle (b151990..HEAD). One drift item fixed inline. Both regression scripts exited 1; investigated and attributed to machine load this session, NOT a code regression — baseline deliberately NOT updated. Architect drift review: - [fixed] design/models/0001-authoring-surface.md — the line "The only reserved tokens are `(`, `)`, and whitespace" was made stale by this cycle (the lexer now rejects any identifier token containing `$`). Corrected to distinguish token *delimiters* (`(`/`)`/whitespace) from the `$` *character reservation within a token*, scoped to the Form-A authoring surface, naming the enforcement site (`LexError::ReservedDollar`) and the intentional `.ail.json` non-guard. - [carry-on] honesty sweep otherwise clean: the only other `$`-mentions in the ledger (0008-memory-model.md, 0003-pipeline.md) describe synthetic mint names (buf$1, $lr_N) and remain correct. No over-broad "no Module can contain `$`" prose exists. The corrected fresh_binder doc-comment (feat commit) is Form-A-scoped and honest. - Decision: no new ledger *contract* added. 0015-language-constraints holds the four RC-soundness preconditions (strict eval, no recursive value bindings, no shared mutable refs, acyclic ADTs); a lexical character reservation is a different category and is documented in the authoring-surface model doc with its enforcement site, which is its natural home. Architect confirmed absence of `$`-contract prose is not itself drift. Lockstep pairs: none apply (architect walked each against the diff): - Pattern::Lit typecheck <-> pre_desugar_validation.rs — neither changed. - lower_app <-> is_static_callee — codegen unchanged. - INTERCEPTS <-> (intrinsic) markers — intercepts.rs + kernel sources unchanged. Regression scripts: - bench/cross_lang.py: EXIT 0. 25 metrics; 0 regressed, all stable. - bench/compile_check.py: EXIT 1; "12 regressed" — ALL are check_ms.* (type-check wall-clock), baseline ~1.8-3.3ms vs actual ~2.5-4.4ms, ~+0.8ms absolute and roughly UNIFORM across every fixture regardless of size (hello +39%, bench_list_sum +51%). All 12 build_O0_ms.* (the ~93ms LLVM portion of the same `ail build`) are within tolerance but also uniformly shifted +12-15%. Attribution: NOT this cycle's code. The guard adds one `raw.contains('$')` byte-scan per token; a tiny module is ~50 tokens, so the added cost is nanoseconds — physically cannot account for +0.8ms on a 2ms check. The uniform ~12-15% shift across the WHOLE `ail` invocation (check AND build) is a global machine-slowdown fingerprint (heavy concurrent subagent/cargo load this session); it crosses the 25% tolerance only on the tiny-baseline check_ms metrics. Localised by reasoning, not by the bencher (the effect is environmental, nothing to localise in code). - bench/check.py: EXIT 1; throughput metrics flipping run-to-run with identical code (rc_over_bump moved in/out of REGRESSION across four runs; bump_s swung -9.5/-11/-14/-15%). Same machine-load variance; rc_over_bump is a ratio of two independently-noisy wall-clock measurements, the noisiest metric in the set. Baseline NOT updated on either script: ratifying a machine-load delta would bake an under-load baseline in and mask a future real regression. OPEN VERIFICATION: re-run bench/compile_check.py and bench/check.py on a quiet machine to confirm green; tracked as the cycle's one open item. Cycle #44 closes (code + docs clean; regression gate pending a clean-machine confirmation, baseline untouched). --- design/models/0001-authoring-surface.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/design/models/0001-authoring-surface.md b/design/models/0001-authoring-surface.md index 92b6fb7..29fd3bb 100644 --- a/design/models/0001-authoring-surface.md +++ b/design/models/0001-authoring-surface.md @@ -58,7 +58,18 @@ Every other maximal token is classified post-hoc: Consequence: arithmetic operators like `+` / `-` / `*` / `/` / `%`, qualified names like `io/print_str`, and cross-module references like `std_list.map` are all single ident tokens with no special -lex rule. The only reserved tokens are `(`, `)`, and whitespace. +lex rule. The only token *delimiters* are `(`, `)`, and whitespace. + +One character is *reserved within* a token: `$`. Any identifier +token containing `$` is rejected by the lexer +(`LexError::ReservedDollar` in `crates/ailang-surface/src/lex.rs`), +because `$` is reserved for the compiler's synthetic namespace +(match-scrutinee, lifted-let-rec, and shadow-rename binders). +Making `$` unauthorable keeps that synthetic namespace +collision-free by construction. The reservation is on the +Form-(A) authoring surface only — `$` inside a string literal or +a comment is unaffected, and the canonical `.ail.json` +deserialization path is intentionally not guarded. Bool literals (`true`, `false`) and unit (`(lit-unit)`) are disambiguated by parser context, not by lex. Comparison and equality are class methods (`eq` / `compare`) and named fns