99df14c792cd09f8e10f4cb26c00b8cebe81f71e
115 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
54f0ced148 |
workflow: delete docs/journals/ and docs/journal-archive.md
Strict application of the "Future-Use, not Verlauf" criterion to the
two remaining journal artefacts:
- `docs/journals/` (110 files): the per-iter and audit journals from
2026-05-11 onward. Pure history; no live reader after the previous
sweep. Entire directory removed.
- `docs/journals/2026-05-19-design-decision-records.md`: the one file
that had live readers (`docs_honesty_pin.rs:108`, `parse.rs:80`,
`duplicate_ctor_pin.rs:8`, three roadmap.md mentions) and was framed
as a "relitigation guard". On re-examination its three asserted
pinned phrases ("Regions were considered and rejected", the
"demands annotations *because*" rationale, the prose-render
placeholder statement) are rationale-prose, not load-bearing
invariants — the test pinned itself, not a system property. Any
Decision that still holds lives in the code, `design/contracts/`,
or `design/models/`. Removed with the rest.
- `docs/journal-archive.md` (pre-2026-05-11 history): content-frozen
long-tail history with no live reader. Removed; if anyone ever
needs the pre-cutoff rationale they can `git log --before=2026-05-11
--grep=<keyword>`.
Live-file consequences:
- `docs_honesty_pin.rs` `design_md_present_tense_anchors_present`:
the three `records.*` assertions and the `read("docs/journals/…")`
are dropped; the contract/model anchor pins remain.
- `duplicate_ctor_pin.rs`: the "why-two-overlays rationale lives in
docs/journals/…" comment is replaced with the rationale inline.
- `parse.rs`: the "form-refinement rationale lives in docs/journals/…"
comment is dropped (the rule body already states the refinement).
- `roadmap.md`: the decision-records mention in the DESIGN.md →
design/ entry's body is dropped; the journal-archive.md `context:`
pointers across ~12 closed entries are either rephrased to point
at the relevant iter/audit (no path), or simplified to a one-line
comment when the iter name alone carries the story.
- `CLAUDE.md` Roles section: the `docs/journal-archive.md` slot is
removed; vocabulary note rephrased.
- `design/INDEX.md`: the Docs bullet drops `journal-archive.md`.
- `skills/README.md` bootstrap-rationale pointer rephrased.
`docs/specs/` and `docs/plans/` (per-milestone specs and per-iteration
plans) are unaffected — they remain the structured-design artefacts
they were before this sweep.
Workspace builds, full test suite green.
|
||
|
|
8e586f493f |
workflow: replace per-iter journal system with git log + BLOCKED.md
The per-iter journal under docs/journals/ duplicated the iter commit body's substance and accumulated as Verlauf-Doku with no Future-Use. Sweep across all live control documents: CLAUDE.md, the 7 SKILL.md files, the 11 agent files, design/INDEX.md and the contracts/models that referenced journals, docs/roadmap.md, and the handful of source comments + tests that pointed at journal files for rationale. Mechanism changes: - Standing-reading-lists in every agent now read `git log -N --format=full` for recent project state, never per-iter journal files. The architect reads `git log <prev-milestone-close>..HEAD --format=full` for audit scope. - implement-orchestrator no longer writes a journal file. DONE outcomes emit just code + stats; the end-report is the per-task summary the Boss uses to write the commit body. PARTIAL/BLOCKED outcomes emit BLOCKED.md at the repo root — uncommitted by convention, Boss removes on repair or discard. New iron-law line + four-rationalisation row + red-flag bullet codify it. - audit ratify mechanic: --update-baseline is now paired with an explicit ratify paragraph in the audit-close commit body, not a separate JOURNAL ratify entry. - design/contracts/honesty-rule.md: "history and rationale lives in docs/journals/" → "lives in git log (iter and audit commit bodies)". Pinned phrase preserved verbatim. - CLAUDE.md "Roles of …" section reframed: design/, git log, journal-archive.md (content-frozen), roadmap.md, specs/, plans/. No docs/journals/ slot anymore. - roadmap.md context-lines that pointed at per-iter journals are dropped where the spec/commit already carries the rationale, or rephrased to "shipped in the <iter> iter commit" / "docs/journal- archive.md (<date> entry)" for pre-2026-05-11 references. What stays (this commit): - docs/journals/ directory and contents are NOT touched. Removing the contents is a separate follow-up. - docs/journals/2026-05-19-design-decision-records.md still has live readers (docs_honesty_pin.rs Z 108 + parse.rs + duplicate_ctor_pin.rs + 3 roadmap mentions) — also follow-up. - docs/journal-archive.md still exists; its self-pointer header has been updated to drop the "see docs/journals/INDEX.md" mention. Workspace builds, full test suite green. |
||
|
|
ac4d545570 |
source: scrub iter-code / Decision-N residue from inline comments
Follow-up to
|
||
|
|
bcd41810f4 |
design/ + source rustdoc: replace opaque shorthand with content phrases + links
Reader-facing prose and rustdoc carried opaque shorthand like
"Decision 10", "clause-5", "mq.1", "ct.1", "eob.1", "rpe.1",
"post-mq.3", and "Iter 22b.1:" with no in-repo definition the reader
could follow. This commit replaces every such occurrence in the
durable tier the reader is most likely to land on (design/ ledger +
source //! module headers + the central /// public-item rustdoc) with
an inline content phrase plus, where applicable, a Markdown link to
the file that defines the referenced concept.
design/ ledger — 16 files:
Definition-site headings demoted from "Decision N: <title>" to
"<title>": authoring-surface, tail-calls, memory-model section in
rc-uniqueness.md, dual-allocator section, typeclass design,
effects "pure core + algebraic effects".
Cross-reference sites: "Decision 1" -> canonical-schema principle
(data-model); "Decision 3/4" -> effects + scope-boundaries; "Decision
6" -> authoring-surface; "Decision 8" -> tail-calls; "Decision 9" ->
rc-uniqueness (dual-allocator); "Decision 10" -> memory-model;
"Decision 11" -> typeclasses (model). "clause-5" -> body-link
durability gate. "clause-3" (in language-constraints) ->
bug-class-reintroduction discriminator. "mq.1/2/3", "ct.1/4",
"eob.1", "rpe.1" -> the canonical-form rule / the type-driven
dispatch / the Str carve-out / etc. "post-mq.3" -> "type-driven".
design/contracts/feature-acceptance.md: file-local "clauses 1/2/3"
-> "criteria 1/2/3" (sprachliche Kohärenz mit der File-Überschrift
"Feature-acceptance criterion"); "the clause-3 mechanism" -> "the
bug-class-reintroduction discriminator".
Source //! module headers — 24 files:
Stripped "Iter X.Y:" prefixes and "(Decision N)" / "(mq.X)" tags
from spec_drift, uniqueness, reuse_shape, migrate_canonical_types,
typeclass_22b{2,3,c}, suppress_filter, lift, mono, linearity,
diagnostic, method_dispatch_pin, method_collision_pin,
no_per_type_print_ops, mq3_multi_class_e2e, print_mono_body_shape,
print_no_leak_pin, cli_diag_human_workspace_load_error,
ct1_check_cli, prose snapshot, unbound_in_instance_method_pin,
mono_xmod_ctor_pattern, desugar.
Central /// public-item rustdoc:
ast.rs (full sweep — every "Iter X" + "Decision N" prefix
reformulated; mode/Type::Fn rustdoc now points at memory-model.md;
Constraint / SuperclassRef / InstanceDef / ClassDef rustdoc points
at typeclasses contract).
diagnostic.rs (all "(Iter X)" / "(mq.X)" tags on diagnostic codes
removed).
lib.rs (FORM_A_SPEC rustdoc points at authoring-surface.md
instead of "Decision 6").
canonical.rs (type_hash + Float-literal rustdoc).
Still outstanding (for a follow-up commit): ~500 inline `//`
code-body comments with `Iter X.Y` markers across the workspace, and
a handful of `///` rustdoc items in hash_pin / workspace_pin / lift /
mono / suppress_filter test-pin and internal-function bodies. Code
identifiers (test filenames like `mq3_multi_class_e2e.rs`, function
names like `iter18e_drop_iterative_default_preserves_hashes`) stay
verbatim per the user's "code identifiers stay verbatim" rule.
Tests: design_index_pin 5/5 + docs_honesty_pin 5/5; workspace builds
clean; full `cargo test --workspace` previously green (every
`test result: ok` line, no FAILED line).
|
||
|
|
19dc42f5ca |
design/ ledger: dense cross-linking via three topical splits + 88-link sweep
The first formal-links milestone shipped clause-5 + 8 links across the
existing file layout. Browsing surfaced that file-only granularity is
only as precise as the file boundaries — three files mixed two or
three navigation targets under one address, so the 8 links could not
multiply without ambiguity. This commit fixes the substrate, then
applies the sweep the original milestone deferred.
Splits (each extracts an already-self-contained section into its own
file so links land on the topic, not the parent doc's TOC):
contracts/typeclasses.md
→ +contracts/prelude-classes.md (Eq/Ord/Show ships, polymorphic `print`)
→ +contracts/method-dispatch.md (5-step dispatch rule, candidate index)
contracts/memory-model.md
→ +contracts/language-constraints.md (the 4 binding constraints
making RC sound without a
cycle collector)
models/authoring-surface.md
→ +models/prose-projection.md (Form-B / `ail prose` / merge-prose)
Each new file enters design/INDEX.md as its own row (three contracts
share show_no_instance_e2e.rs / uniqueness.rs as ratifying tests;
prose-projection is a model). Two pre-existing links rebind to the
new topic-files (memory-model.md → method-dispatch.md;
float-semantics.md → prelude-classes.md).
Link sweep: 8 → 88 formal Markdown links over 23 files. Every file
in design/contracts/ + design/models/ now has at least one outgoing
link; the tree is fully connected. Links are file-relative
`[label](path)` per the established convention, fenced code blocks
are skipped (a `](` inside ```jsonc``` is literal text), the durable
tier (design/ + crates/ + runtime/) is enforced by clause-5.
Tests:
- design_index_pin.rs (5/5 clauses): clean-cut, INDEX resolution,
ratifying-test resolution, no decision-record prose in contracts/,
body links durable + resolving.
- docs_honesty_pin.rs (5/5): one assertion rebinds from typeclasses.md
to prelude-classes.md (where the gated sentence now lives);
design_corpus widens to include the 4 new files so the Wunschdenken
/ doc-archaeology sweeps continue to cover everything that used to
live in the parents.
No spec/plan/journal for this batch — interactive collaboration after
the milestone closed; the user gated the splits explicitly before the
sweep.
|
||
|
|
8ad91e7f24 |
iter design-ledger-formal-links.1 (DONE 5/5): clause-5 hard gate + 7 prose-ref conversions + 2 disposition-(b) homeless removals + honesty-rule positive-half (whole milestone in one iter)
Positive-half completion of the DESIGN.md -> design/ split: design/
body cross-references are now formal, file-relative Markdown links
into the durable tier (design/ or source), and a new in-tree hard
gate (design_index_pin.rs clause-5,
design_body_links_are_durable_and_resolve) walks every
design/contracts/*.md + design/models/*.md, strips fenced code
(strip_fences toggles on ```/~~~ lines so a ]( inside a fence is not
treated as a link), extracts every ](path), and asserts the target
resolves file-relative to a real file under design/-or-crates/-or-
runtime/; never docs/, never an in-file #anchor.
RED-first via identity-stubbed strip_fences (four embedded synthetic
vectors -- first one FAILS); replacing the stub with the real
toggle-on-fence impl turns the test GREEN. clause-5 composes with
clause-3 into the complete invariant the milestone establishes:
every contract cross-reference is EITHER a resolving durable
file-link OR clause-3-forbidden decision-record prose.
Conversions (recon-and-corpus-verified closed set):
Task 2 (7 prose refs, 8 link tokens):
float-semantics.md:69 Prelude classes -> [..](typeclasses.md)
float-semantics.md:100 bare-path -> [Str ABI](str-abi.md)
embedding-abi.md:45 "Frozen value layout" -> [..](frozen-value-layout.md) (drop stale "below")
memory-model.md:44 Data model -> [..](data-model.md)
memory-model.md:105-106 Method dispatch -> [..](typeclasses.md) (drop stale "below"; the target heading lives in typeclasses.md:227, not in this file)
scope-boundaries.md:48 Str ABI -> [..](str-abi.md)
scope-boundaries.md:88 mixed split: ailang-core::desugar -> source link + Pipeline -> ../models/pipeline.md (drop stale "above")
Task 3 (2 disposition-(b) homeless removals):
pipeline.md:60-61 (see docs/PROSE_ROUNDTRIP.md) pointer removed, CLI prose preserved
authoring-surface.md:178-181 cross-tier pointer clause removed, ail merge-prose sentence preserved
Task 4: honesty-rule.md positive-half paragraph inserted between L14 and the existing L15-blank-L16; both docs_honesty_pin.rs-pinned phrases byte-identical at L14/L19 (now shifted to L19 -> L25 by the +6 lines).
Out of scope, preserved (asserted independently): every intra-file
"above/below"; embedding-abi.md:51 "frozen value layout below
specifies" (no quoted title, no (see) form); data-model.md
38/66/79/206/226 (in-fence ```jsonc schema annotations -- the inline
analog of the nominal-mention carve-out). INDEX.md and the
decision-records journal byte-unchanged; clauses 1-4 of
design_index_pin.rs source byte-unchanged (the only `-` lines in
the diff are the two-line //! header rewrite Task 1 Step 5 itself
delivers).
Boss-verified independently (not on agent report alone):
cargo test --workspace 647 passed / 0 failed
(+1 vs pre-milestone 646:
the new clause-5)
cargo test --test design_index_pin 5 / 5 passed
cargo test --test docs_honesty_pin 5 / 5 passed (additive
paragraph is pin-safe)
grep ](.../docs/.../) under design/ zero
grep ](#) under design/ zero
](-link count under design/ 8 (closed convert-set)
git diff --quiet design/INDEX.md ok
git diff --quiet decision-records ok
embedding-abi.md:48 pinned phrase byte-identical
One Concerns item: Task-5 Step-7's plan-predicted "`-` line count = 1"
was actually 2 because Task 1 Step 5 rewrote the //! header 5 -> 8
lines (removing the original L4 + L5, not just L5). Planner self-
review-item-8 miss on my part -- a verification-arithmetic error in
the plan, NOT an implementation defect. The substantive assertion
(clauses 1-4 source byte-unchanged) is fully satisfied; the
implementer correctly flagged it and proceeded. The plan stands as
written; the assertion's `1` should have been `2`. Lesson noted for
future header-rewrite tasks.
Spec: docs/specs/2026-05-19-design-ledger-formal-links.md
(grounding-check PASS x3 across two corpus-grounded amendments --
clause-6 + cross-ref definition; clause-5 fence-skip + closed
convert-set enumeration).
Next: mandatory milestone-close audit (no fieldtest -- zero
authoring-surface change, reasoned exclusion).
|
||
|
|
f683f1aec8 |
iter design-md-rolesplit.tidy (DONE 7/7): resolve milestone-close audit drift
Gate-first TDD: widened design_index_pin.rs clause-3 to a hand-rolled
FAITHFUL Sweep-1 superset (case-sensitive digit-anchored line anchors
+ Sweep-1's ^[^/]* path-excluded date + the audit-named
decision-record phrases, case-insensitive); no regex dep; the blanket
iter-detector rejected as unworkable. Sentence-level strip of
faithfully-migrated history/decision-record prose out of 5 contract
files (the audit's 3 spot-checked + roundtrip-invariant.md +
data-model.md the exhaustive scan found) into the decision-record
journal, each replaced by its present-tense contract equivalent;
float-semantics.md stale 'see Str ABI below' -> str-abi.md;
architect_sweeps honesty sweeps re-scoped to design/contracts only
(models/ is the narrative tier) + ailang-architect.md lockstep.
Invariant: clause-3 GREEN => Sweep-1 clean in contracts/.
The prior dispatch correctly BLOCKED on a real plan defect (iso_date
lacked Sweep-1's path-exclusion, over-firing on legit
docs/specs/2026-.. citations); per the two+-defects-in-one-iteration
discipline the audit Resolution mechanism+scope were corrected
upstream in lockstep (
|
||
|
|
176821c2e7 |
iter design-md-rolesplit.1 (DONE 9/9): DESIGN.md -> design/ ledger role-split
The 3020-line docs/DESIGN.md is replaced by the design/ ledger:
design/INDEX.md (sole addressable spine, typed Contracts+Models tables,
polymorphic links — prose file OR authoritative source //!), 14
design/contracts/*.md test-linked invariants + 3 source-link-only
contracts (mangling/env-construction/qualified-xref, no prose file —
code is SoT), 5 design/models/*.md whitepapers, and
docs/journals/2026-05-19-design-decision-records.md (the
relitigation-guard archive — every why/rejected/does-not-do/rollback/
empirical ### moved out at ###-granularity). Clean cut: git rm
docs/DESIGN.md, no stub.
RED-first crates/ailang-core/tests/design_index_pin.rs — the 4-clause
anti-regrowth spine (DESIGN.md-gone / every-INDEX-link-resolves /
every-contract-names-a-resolvable-ratifier /
contracts-carry-no-decision-record-prose) — demonstrably RED before,
GREEN after. Build-atomic by task ordering: design_schema_drift.rs's
include_str! (the only compile-time consumer) retargeted to
design/contracts/data-model.md BEFORE the deletion; its
## Data model/## Pipeline slicer dropped (a simplification the split
enables). 2 NoInstance diagnostics + 2 lockstep E2Es retargeted to
design/contracts/{float-semantics,typeclasses}.md. ~12 agent reading
lists + 5 SKILL bodies + CLAUDE.md + skills/README.md + ~25
code/C/.ail/spec comment xrefs retargeted; OQ7 dangling 'Iter 13b'
cite deleted (no forward target — a pointer would be fiction).
honesty-rule.md rewritten so the rule names the new home
(rationale->journals), resolving the recon-found internal
contradiction; the two docs_honesty_pin.rs:70,72 pinned phrases kept
verbatim+contiguous.
Boss-verified independently: cargo test --workspace 646 passed /
0 failed; design_index_pin 4/4; acceptance grep CLEAN of live
DESIGN.md refs (residuals = only the spec-mandated clause-4
deletion-enforcer). 2 DONE_WITH_CONCERNS routed to the mandatory
milestone-close audit: (a) str-abi.md:23 '(iter str-concat,
2026-05-13)' provenance stamp trips advisory architect_sweeps Sweep-1
— Boss-confirmed byte-identical to DESIGN.md@deeffb1:2062-2065, a
faithfully-migrated PRE-EXISTING anchor (regexes verbatim, only path
retargeted), NOT split-introduced — RATIFY-or-tidy at audit; (b) a
now stale-direction intra-prose 'see Str ABI below' cross-ref in
float-semantics.md — audit-adjudication candidate. Plan defect noted:
Task 9 Step 4's verbatim acceptance grep used a ^./ anchor not
matching the system's grep -rIn output; substance re-verified CLEAN.
Spec grounding-check PASS x2. Journals INDEX + decision-records
pointer appended (Boss-only).
|
||
|
|
4ea8bc5faf |
iter embedding-abi-m3.1 (DONE 6-7): freeze the value layout — DESIGN.md frozen-layout SSOT + lockstep pointers + enforceable byte-pin
Tasks 6-7 of the Boss-repaired split dispatch (Tasks 1-5 committed
|
||
|
|
a80d495ab3 |
iter embedding-abi-m2.tidy: '## Embedding ABI (M1)' -> '## Embedding ABI' lockstep rename (M2 audit doc-honesty fix)
Resolves the M2 milestone-close audit's only actionable drift item (architect [medium] DESIGN.md:2266 stale (M1) header vs its now-M2 present-tense body + coupled [low] :2354 xref). Current-state-mirror: the section describes the current embedding ABI (M1+M2), so the milestone tag is internal history, not current state. 5 live-coupling sites renamed in lockstep, 3 files: - docs/DESIGN.md:2266 header '## Embedding ABI (M1)' -> '## Embedding ABI' - docs/DESIGN.md:2354 schema-block xref 'see §"Embedding ABI"' - docs_honesty_pin.rs:134 test comment + :136 assert-message - crates/ailang-core/specs/form_a.md:89 live forward-xref (Boss-added 5th site: plan-recon-undercount countermeasure surfaced a live dangling-xref the architect's 3-site scope missed; folded in on the merits — a half-rename contradicts the tidy's own thesis) docs_honesty_pin.rs:135 asserted substring (the rename-immune DESIGN.md body sentence) byte-verbatim untouched. Committed append-only history NOT falsified (Boss-verified diff = 3 content files + journal + stats only). Pins green before AND after (docs_honesty_pin 5/0, design_schema_drift 8/0). Zero language/checker/codegen/schema change. |
||
|
|
7d7f04e1a4 |
iter form-a-scalar-param-mode-carveout: scalar-param mode carve-out (docs-honesty tidy)
Resolves the M1-fieldtest [friction] + [spec_gap]#1 shared root:
form_a.md stated an unconditional 'every (fn ...) param needs an
(own/borrow) mode' rule in 4 places, contradicting shipped checker
behaviour (scalars take and require bare (con Int); a mode on a
scalar trips body-pointing use-after-consume/consume-while-borrowed).
All 4 sites rewritten symmetric to the existing return-type carve-out;
DESIGN.md gains the bare-scalar export-param rule + a corpus-grounded
Form-A snippet; RED-first anti-regrowth pin via FORM_A_SPEC + norm().
Zero language/checker/codegen change. Plan
|
||
|
|
818177d835 |
iter embedding-abi-m1.1 (PARTIAL 3/7): schema + surface + baseline prerequisites; plan Repair
Tasks 1-3 of docs/plans/embedding-abi-m1.1.md, a verified known-good subset (cargo build --workspace exit 0; full workspace green; the roundtrip_cli all-examples gate green): - Task 1: CLI build-path MissingEntryMain baseline pin (examples/embed_noentry_baseline.ail + embed_missing_main_baseline.rs; triple-assertion, layered on the green codegen-unit pin lib.rs:3314). - Task 2: additive FnDef.export: Option<String> (serde default + skip_serializing_if, modelled on FnDef.doc); compile-driven thread of export: None across ~85 FnDef/AstFnDef literals / 18 files incl. all in-source #[cfg(test)] mod tests + drift/hash/spec-drift literals + the codegen lib.rs:3320 in-source pin; hash-stability golden pin; DESIGN.md fn-JSON "export" line. DONE_WITH_CONCERNS (plan symbol content_hash_fn -> def_hash per hash_pin.rs, plan- pseudo-vs-reality class, plan corrected). - Task 3: Form-A (export "<sym>") modifier — parse_export + parse_fn thread + write_fn_def emission + form_a.md grammar; byte-identical round-trip. Task 4 BLOCKED (Boss plan-defect: fixtures declared module != file stem; AILang's loader hard-enforces module==stem at workspace.rs:438, so ail check panicked on load before the gate). Boss resolution (Option 2, overriding the orchestrator's Option 1, rationale in the journal + plan Design-decision 6): keep descriptive embed_* filenames, rename fixture MODULES to their stems — the C symbol backtest_step stays via (export ...), demonstrating spec Decision 2's mangling- decoupling rather than violating it; archive becomes libembed_backtest_step.a, internal symbol @ail_embed_backtest_step_step, host.c unchanged. Plan fixed (Design-decision 6 + Task-4 module==stem + Task-5/6/7 dependent strings + the no-*. Float-head + content_hash_fn concerns folded). Headline fixture corrected; blocked-Task-4 WIP discarded (recreated by the [4,7] re-dispatch from the fixed plan). PARTIAL commit (not the iter's final commit): the implement Repair path mandates committing the known-good subset so the [4,7] re-dispatch starts from a clean tree that includes Tasks 1-3 (Tasks 4-7 structurally depend on the schema field + surface). INDEX line added when the full iter lands post-re-dispatch. |
||
|
|
7580d434f0 |
iter docs-honesty-lint.1: canonical docs present-tense-honest + wrap-robust pin + Sweep 5
Strips Wunschdenken (forward intent stated as fact) and non-citation post-mortem / doc-archaeology from docs/DESIGN.md (14 corrections incl. 1 Step-14 procedural-catch-all site the spec's illustrative regex would have missed) + docs/PROSE_ROUNDTRIP.md; genuine forward intent (LLM tool-use / MCP / LSP) relocated to roadmap P3. Codifies the tense+modality discriminator as a present-tense DESIGN.md meta- subsection. Guards two ways: wrap-robust enumerated absent/present pin (crates/ailang-core/tests/docs_honesty_pin.rs, norm() whitespace- collapse helper — structurally discharges the recurring grep/contains line-wrap failure family) + wrap-robust advisory Sweep 5 in architect_sweeps.sh (contiguous-fragment regex) with full sweep-count lockstep + new ailang-architect.md "DESIGN.md honesty drift" bullet. KEEPs preserved (Diverge-reserved, regions-rejected, self-labelled tiebreaker). No language/checker/codegen/runtime change — sole crates/ change is the additive pin; ail check/run/build byte-unchanged. Workspace 609/0 (delta +4 pin), sentinel trio green, build clean. |
||
|
|
4837871ddf |
iter remove-mut-var-assign.tidy: form_a.md grammar deletion + audit close
Milestone-close audit for remove-mut-var-assign (48e7774..07f0802).
Architect drift: one [high] — crates/ailang-core/specs/form_a.md
still documented the full mut/var/assign construct (EBNF 286-288,
prose 308-328, dangling `like mut` at 332). The spec named it for
deletion; the iter missed it. Root cause = the recon-undercount
class (4th recurrence) plus a Boss error: a plan-time grep checked
only docs/form_a.md (dir-scoped), got "No such file", and that
unverified non-existence claim propagated into the recon brief —
the real file is crates/ailang-core/specs/form_a.md. A stale doc
never compile-fails so the implement compile-sweep could not catch
it. Fixed inline as a Boss .tidy (CLAUDE.md context-already-loaded
carve-out; full review discipline kept — tree-wide verified zero
construct residue except the roadmap entry naming the milestone
itself; grammar now reads coherently reuse-as -> loop -> recur).
Bench causally exonerated: HEAD vs
|
||
|
|
07f080256c |
iter remove-mut-var-assign.1: atomic removal of mut/var/assign
mut/var/assign removed from AILang entirely and atomically. Deleted: Term::Mut/Term::Assign/struct MutVar; the three Form-A keywords + parse_mut/parse_assign + grammar EBNF; the 4 mut CheckError variants; the mut_scope_stack synth threading (param dropped from synth + every internal/external/test caller); the two lower_term arms; and every exhaustive no-_ Term::Mut/Term::Assign match arm across 17 source files — cut in lockstep with DESIGN.md, fixtures, the drift trio, carve-out and roadmap so the schema is honest at every commit. No catch-all wildcard introduced (verified). loop/recur + let/if are the surviving forms. The shared codegen alloca machinery survives (loop reuses it): mut_var_allocas renamed binder_allocas (representation-only, loop codegen byte-identical) and the shared Term::Lam escape guard simplified to !loop_stack.is_empty() with the loop half (LoopBinderCapturedByLambda) byte-equivalent. Feature-acceptance applied inverted: the removed feature fails clause 2 (redundant) and clause 3 (IS the iterated-mutable-state bug class). Behaviour preservation is executable: mut_counter/mut_sum_floats still print 55 after the faithful let/if rewrite. The removal is made executable by the new mut_removed_pin.rs (4 must-fail pins). Independent verification: cargo test --workspace 605/0, zero residual mut symbols in any crate source, loop/recur non-regression all green (55 / 500000500000 / infinite-compiles / the lambda_capturing_loop_binder pin), roundtrip_cli PASS. One DONE_WITH_CONCERNS: a 4th recurrence of the recon-undercount class (in-source mod tests + a drift-pin fn + 5 orphaned mut .ail.json carve-outs + a non-enumerated E0599); all resolved within implementer remit, no behaviour change. Milestone-close audit then fieldtest remain. spec docs/specs/2026-05-18-remove-mut-var-assign.md (grounding PASS) plan docs/plans/remove-mut-var-assign.1.md |
||
|
|
2ee97943bd |
iter loop-recur.tidy GREEN: reject lambda-captures-loop-binder at check + milestone-close audit resolution
GREEN side of the RED audit-trail commit
|
||
|
|
1566ce0b29 |
iter loop-recur.2: typecheck semantics — binder typing, recur checks, verify_loop_body
Second of three iterations of the standalone loop/recur milestone
(plan
|
||
|
|
a179ec30a0 |
iter loop-recur.1: additive Term::Loop / Term::Recur / LoopBinder foundation
First of three iterations of the standalone loop/recur milestone
(spec
|
||
|
|
a29700cc9e |
iter effect-doc-honesty: make the effect-system documentation true
Standalone documentation-honesty tidy (no language/checker/codegen change; `ail check`/`run` byte-unchanged by construction). Corrects three false effect-system claims the effect-subsystem recon surfaced, plus two satellite mentions, guarded by a new doc-presence pin: - DESIGN.md Decision 3: removed the "row-polymorphic (`![IO | r]`)" claim (no EffectRow / row variable exists in any crate — effect sets are a flat, unordered, closed set unified by set-equality); reconciled "`IO` and `Diverge` are wired up" to IO-only + `Diverge` reserved/unimplemented (zero code in any crate), modelled on Decision 4's reserved-refinements precedent. - DESIGN.md "What is not (yet) supported": "IO and Diverge ops" bullet -> IO-only + Diverge-reserved. - ast.rs Term::Do doc-comment: "resolved against the effect-handler table at link time" -> the real mechanism (typecheck lookup in Env::effect_ops + literal lower_effect_op codegen match; no handler table, no link-time resolution). - form_a.md:226 + rule 3, and main.rs merge-prose CONTRACT example: Diverge mentions reconciled in lockstep. - new crates/ailang-core/tests/effect_doc_honesty_pin.rs: 4 tests, fiction-absent + corrected-anchor-present, single-line wrap-robust substrings. cargo test --workspace 600 -> 604 (4 new pin tests, 0 regressions); design_schema_drift / spec_drift / schema_coverage stay green, empirically confirming none scans the effect-prose region. |
||
|
|
37ac704bf3 |
iter revert: back out the Iteration-discipline milestone (it.1 + it.2)
One forward iteration; main never rewound. |
||
|
|
a4be1e58a3 |
iter it.2: structural-guardedness checker + first real Diverge effect
Iteration-discipline milestone, 2 of 3. Strictly additive (nothing tail-related removed; that is it.3). New whole-body pass verify_structural_recursion sibling of verify_tail_positions (DD-1): smaller-set algorithm with implicit candidate inference + unconstrained accumulators (DD-2, foldl=structural), self/mutual via inline ADT-family union-find (DD-3), it.2-only tail==false grandfather. CheckError::NonStructuralRecursion. term_contains_loop (stops at Term::Lam, DD-4) injects Diverge so existing UndeclaredEffect enforces it, no new variant; lam-arrow + LetRec sub-effect sites wired. DESIGN.md Decision 3 synced. Four it.1 loop fixtures gained !Diverge. Two spec-premise boundary defects surfaced + resolved within the additive invariant (corpus clean, check not weakened), recorded as corrected it.3 corpus-migration scope: (1) the "21 tail-app fixtures" grandfather premise under-counts the corpus — no-ADT-candidate counter recursions have no structural position to verify, deferred to it.3; (2) two RC-regression fixtures joined the spec's transitional tail-app grandfather as the other 20 do (RC==GC guards verified still green). cargo test --workspace 622/0; 9 acceptance pins non-vacuous. Spec |
||
|
|
96db54d15d |
iter it.1: loop/recur additive — Term::Loop/Term::Recur/LoopBinder end-to-end
Iteration-discipline milestone, 1 of 3. Adds named loop + recur as strictly-additive first-class AST nodes: parse/print/prose/serde/ round-trip/schema lockstep, typecheck (binder typing + recur arity/type unification via loop_stack threaded as mut.2's mut_scope_stack; recur-tail-position via verify_loop_body), codegen (loop-header + one phi per binder; recur back-edge br with a NEW parallel block_terminated setter; lambda-boundary loop_frames save/restore). Four Recur* CheckError variants. Strictly additive: zero deletions touch tail-app/tail-do or the seven existing block_terminated sites — this is what makes the destructive it.3 safe. recur synth = fresh metavar (resolves the plan's flagged Type::unit() risk). loop_counter->55, loop_in_lambda->49, four negatives fire, tail-app byte-identical, cargo test --workspace green. Spec |
||
|
|
20add51112 |
iter mut.4-tidy + audit close — mut-local milestone closed
Tidy iter addressing the audit-mut-local drift. Plus paired baseline
update on bench/baseline_compile.json (audit-skill discipline).
Architect [high] items closed:
1. CheckError::MutVarCapturedByLambda rejects lambdas whose body
free vars include a mut-var of the enclosing mut_scope_stack.
Uses the existing ailang_core::desugar::free_vars_in_term walker
(which honours Term::Match pattern bindings). The scan runs only
when mut_scope_stack is non-empty.
2. crates/ailang-codegen/src/lambda.rs: the capture-not-in-locals
path that previously raised CodegenError::Internal blaming the
typechecker now uses unreachable!. The companion comment block
was rewritten to state the current reality.
Architect [medium] items closed (stale mut.1-stub history comments):
3. docs/DESIGN.md §'Term (expression)' mut/assign block: the
trailing paragraph describing the iter mut.1 stub state was
replaced with one describing the current mut.3-end-state. The
inline jsonc comment on {'t': 'assign'} was updated to drop the
'deferred to mut.2/mut.3' language.
4. crates/ailang-codegen/src/lib.rs: the stale comment block above
the real Term::Mut arm describing the mut.1 stub was removed
entirely.
Other:
- Short-circuit on empty mut_scope_stack in synth's Term::Var arm:
the iter mut.2 prepend now skips the iter-and-find walk when
the stack is empty, eliminating any per-Var-resolution overhead
for the common case (programs with no mut blocks). The
short-circuit did NOT close the check_ms regression — see ratify
below.
- Spec docs/specs/2026-05-15-mut-local.md §'Out of scope' amended
with the lambda-capture rejection bullet.
- Negative fixture examples/test_mut_var_captured_by_lambda.ail.json
+ driver test extension in crates/ailang-check/tests/
mut_typecheck_pin.rs (6th test) +
crates/ailang-core/tests/carve_out_inventory.rs EXPECTED bumped
12 → 13.
Bench-regression ratify:
bench/compile_check.py check_ms showed a uniform ~30-50% relative
shift across the entire 11-fixture suite (~0.5ms absolute on a
1.4-1.5ms baseline). The uniformity across fixtures of very
different Var counts argues for a fixed-cost-per-invocation tax,
not a Var-proportional hot path. The Term::Var short-circuit
falsified the hot-path hypothesis. The plausible remaining causes
(synth-parameter-passing through ~19 recursive sites, and binary-
size startup tax from mut-* adding ~1400 LOC to typecheck/codegen)
are both feature-cost, not pathological. Ratified by paired
journal entry; bench/baseline_compile.json updated to the post-
mut-local numbers via 'bench/compile_check.py --update-baseline'.
bench/check.py continues to show the established tail-latency-noise
envelope from audit-pd (2026-05-14) — no separate ratify needed.
bench/cross_lang.py clean.
Tests: 594 → 598 green (4 new lib.rs mod tests + 1 driver test +
1 fixture-corpus uptake).
Journal: docs/journals/2026-05-15-iter-mut.4-tidy.md.
mut-local milestone end-to-end status:
- mut.1 (
|
||
|
|
b24718a5ff |
iter mut.2: typecheck for Term::Mut + Term::Assign
Replaces the iter mut.1 dispatch stubs in synth with real typecheck
logic. Codegen stubs stay (deferred to mut.3); examples/mut.ail now
typechecks clean but still cannot run end-to-end.
Concretely:
- crates/ailang-check/src/lib.rs: three new CheckError variants
(MutAssignOutOfScope, AssignTypeMismatch, UnsupportedMutVarType)
with #[error('[code]: msg')] Display attrs matching the cli-diag-
human bracketed-code convention; code() + ctx() arms emit
kebab-case codes and structured JSON payloads.
- synth signature: new parameter mut_scope_stack: &mut Vec<IndexMap<
String, Type>> after locals, threaded through every recursive call
site (synth's 18 internal calls + mono.rs:712/1337 re-synth +
lift.rs:723 + builtins.rs test helpers + the mq.3 in-test helper
at lib.rs:6663). Stack is per-walk and lexically scoped — push on
Term::Mut entry, pop on exit. Each frame is an IndexMap so
declaration order is preserved for the diagnostic 'available' list.
- Term::Var resolution: prepended with innermost-first mut-scope
lookup. Mut-vars are monomorphic — no Forall instantiation, no
free_fn_owner recording. Innermost-wins shadowing falls out of
the iteration order.
- Term::Mut arm: gate var types to Int/Float/Bool/Unit only via
a small is_supported_mut_var_type helper; each var's init is
synth'd in the in-progress scope (the var itself is not yet
bound, so it does not self-shadow during init); push completed
frame, synth body, pop. Block's type is body's type.
- Term::Assign arm: walk mut_scope_stack innermost-first looking for
name. On miss: MutAssignOutOfScope with available flattened
across all frames (innermost-first, deduplicated, preserving
order). On hit: synth value, unify against declared type — on
mismatch emit AssignTypeMismatch with rendered types; on success
produce Type::unit().
- Five .ail.json fixtures under examples/ exercising each
diagnostic plus a positive nested-shadow sanity case, driven by
the new pin test crates/ailang-check/tests/mut_typecheck_pin.rs
(load_workspace + check_workspace + assert exact codes).
- carve_out_inventory.rs EXPECTED extended 7 → 12 to cover the new
negative-fixture set (consistent with the form-a-default-authoring
spec §C4(b) precedent for type-rejection fixtures staying as
.ail.json-only).
Plan deviations from recon: three additional synth() call sites
beyond the four enumerated (lift.rs:723, builtins.rs x2,
lib.rs:6663) surfaced via the build-red structural signal; each
threaded with a fresh empty stack. carve_out_inventory.rs extension
was required by the existing pin but not named in the recon — a
documentary concern for the next planner pass.
Tests: 579 → 592 green; examples/mut.ail typechecks clean
('ok (26 symbols across 2 modules)') via cargo run --bin ail --
check examples/mut.ail; cargo build green; full workspace test
green.
Journal: docs/journals/2026-05-15-iter-mut.2.md.
Refs: docs/specs/2026-05-15-mut-local.md, docs/plans/2026-05-15-iter-mut.2.md.
|
||
|
|
7b92719244 |
iter mut.1: AST extension + Form A surface for local mutable state
First iteration of the mut-local milestone (foundation step on the
Stateful-islands roadmap path). Lands the schema + surface tier:
Term::Mut, Term::Assign, and the nested MutVar struct become
first-class AST nodes that round-trip cleanly through Form A.
Typecheck and codegen recognition are deferred to mut.2 and mut.3
per the spec's out-of-iteration boundary; reaching either dispatch
entry point with these variants produces CheckError::Internal /
CodegenError::Internal with a 'deferred to iter mut.{2,3}' message.
Concretely:
- crates/ailang-core/src/ast.rs: two new Term variants behind
#[serde(tag = 't')]; pub struct MutVar { name, ty, init } adjacent
to Arm. Two canonical-bytes pin tests for the explicit-empty-vars
serialisation and the assign round-trip.
- ~25 substantive Term-walker arms across ailang-core/desugar,
ailang-core/workspace, ailang-check (lib + lift + linearity + mono
+ pre_desugar_validation + reuse_shape + uniqueness),
ailang-codegen (escape + lambda + lib), ailang-prose, and
crates/ail/src/main.rs. Universal policy: substantive recurse-into-
children at every site; only the two dispatch entry points
(synth in ailang-check, lower_term in ailang-codegen) stub with
Internal-error. One test-side walker arm in
crates/ail/tests/codegen_import_map_fallback_pin.rs not
enumerated by the plan was added as well (defensive recursion).
- ailang-surface: parse_mut + parse_assign helpers; Term::Mut
body desugared from a flat statement sequence into a right-folded
Term::Seq chain inside the JSON-AST. Print arms in print.rs match
the parser convention. EBNF prologue + crates/ailang-core/specs/
form_a.md productions updated. Four new parser pin tests cover
the empty-mut, single-var, body-required, and vars-only-no-body
cases.
- Drift + coverage tests extended: design_schema_drift.rs adds two
exemplars + match arms; schema_coverage.rs adds two VariantTag
entries + EXPECTED_VARIANTS + visit_term arms; spec_drift.rs adds
two exemplars + match arms. DESIGN.md §'Term (expression)' gets
jsonc-blocked schemas for the two new variants.
- examples/mut.ail: six-fn round-trip fixture exercising empty mut,
single-var, two-var, nested-shadow, and the four supported scalar
return types (Int, Float, Bool, Unit). The round_trip auto-glob
and schema_coverage corpus walker both pick it up.
Plan deviation: the plan named lib.rs:2572 as the typecheck
dispatch stub site, but that line is actually verify_tail_positions
(substantive walker). The real dispatch is synth (3403-area, stub
at 3489); the orchestrator routed correctly.
Tests: 564 → 579 green; cargo build green; round-trip green for
the new fixture; all drift + coverage tests green.
Journal: docs/journals/2026-05-15-iter-mut.1.md.
Refs: docs/specs/2026-05-15-mut-local.md, docs/plans/2026-05-15-iter-mut.1.md.
|
||
|
|
9a8d3850e7 |
iter pd.3: prelude.ail.json retired — milestone prelude-decouple closed
Deleted examples/prelude.ail.json (-559 lines). The cross-form-identity preflight test that ratified pd.3's load-bearing assumption at module hash 3abe0d3fa3c11c99 has discharged its purpose and was removed along with its supporting bytes; the long-term prelude_parse_yields_canonical_hash anchor stays. The ail-CLI migrate-bare-cross-module-refs subcommand's defensive include + lockstep skip-branch removed; the rewrite logic's prelude-fallback capability silently retired with the synthetic insert (known debt, doc-comment updated to reflect). One in-mod core test relocated to crates/ailang-core/tests/workspace_pin.rs (pd.2.4 dev-dep cycle precedent — in-mod call to ailang_surface still structurally impossible). carve_out_inventory.rs: 8 → 7 carve-outs, §C4(b) row dropped, header sentence updated. form-a-default-authoring.md §C4(b) gets a "RETIRED 2026-05-14 by milestone prelude-decouple" status marker; original historical text preserved. New prelude_decouple_carve_out_pin.rs asserts examples/prelude.ail.json does NOT exist. Milestone prelude-decouple closed: prelude exists on disk only as examples/prelude.ail; ailang-core embeds zero prelude bytes; CLAUDE.md "authors write .ail" doctrine holds without exception. cargo test --workspace 573 green; bench/cross_lang.py + bench/compile_check.py exit 0; bench/check.py exit 1 with the established noise envelope (14th observation of bench_list_sum.bump_s since audit-cma; pd.3 is filesystem + spec text + test deletion only, no codegen / runtime / typecheck path edited — baseline pristine). Folds in the orphan pd.2 INDEX entry that was left uncommitted in the working tree. |
||
|
|
008d18bb18 |
iter pd.2: surface owns prelude embed; pd.1 shim retired
Moved PRELUDE_AIL const + parse_prelude fn to ailang-surface; re-exported from surface's lib.rs. surface::load_workspace rewritten 3-line shim-call → 7-line composition: load_modules_with → reserved-name check → inject parse_prelude → build_workspace(&["prelude"]). Five deletions in core: PRELUDE_JSON, load_prelude, load_workspace_with (the pd.1 shim), the top-level cfg(test) load_one fn, and the cfg(test) crate::load_module import. Production literal-"prelude" count in crates/ailang-core/src/ is now zero. Cross-form-identity preflight PASSED at module_hash 3abe0d3fa3c11c99 — parse(prelude.ail) ≡ deserialize(prelude.ail.json), so pd.3's deletion of the JSON is safe. Three new pin tests in ailang-surface/tests/: prelude_parse_pin (succeeds + min defs); prelude_module_hash_pin (cross-form-identity + literal-hex anchor); prelude_injection_pin (inject + reservation). Plan deviations (recorded in journal): switched the regression-pin fixture from non-existent examples/hello.ail.json to hello.ail; relocated 10 in-mod core tests to crates/ailang-core/tests/workspace_pin.rs because the dev-dep cycle (core ↔ surface) prevents in-mod aliasing in the lib-test target (form-a.1 T5 precedent); preserved load_one production-symbol-deletion by adding a test-mod-private load_one helper in core's mod tests for the 2 pd.1-introduced tests still consuming it. cargo test --workspace 573 green (+9 net from pd.1 baseline). bench/cross_lang.py + bench/compile_check.py exit 0; bench/check.py exit 1 with established noise envelope (bench_list_sum.bump_s, 13th consecutive observation; pd.2 is workspace-loader-only, no codegen touch — baseline pristine). |
||
|
|
ffa80326a3 |
iter pd.1: core API split — load_modules_with + build_workspace + implicit_imports threading
Carved load_workspace_with's inline DFS+inject+validate block into two new public fns: load_modules_with (DFS only) and build_workspace (validation + registry, takes implicit_imports: &[&str]). Threaded the new parameter through validate_canonical_type_names + check_class_ref + check_type_con_name; the four hardcoded literal-"prelude" fallback blocks in the diagnostic helpers became iter-loops over implicit_imports. load_workspace_with survives as a 3-line shim composing the new fns + the still-in-place prelude inject, so surface (ailang-surface) and all downstream crates compile + test unchanged. Retired pub fn load_workspace (zero production callers; 9 in-mod test callers migrated to load_workspace_with(&entry, load_one); load_one + the load_module import gated under cfg(test) since they're now test-only). Production literal-"prelude" count in workspace.rs dropped 8 -> 4 (all 4 in the shim's inject path; none in the diagnostic helpers, which was the spec's pd.1 target). pd.2 will move the prelude embed + the inject step into ailang-surface and retire the shim; pd.3 will delete examples/prelude.ail.json. cargo test --workspace green; bench/check.py + compile_check.py + cross_lang.py all exit 0. |
||
|
|
6fdb45d2f2 |
iter rpe.1: retire per-type print effect-ops
Single iter shipping the post-milestone-24 follow-up named in
docs/specs/2026-05-14-retire-per-type-print-effects.md. After this
iter the only surviving direct-output effect-op is `io/print_str`;
all per-type print primitives are replaced by the polymorphic
`print` helper (prelude, iter 24.3).
Components:
- 92 examples/*.ail fixtures migrated (do io/print_<T> x) →
(app print x); 6 .prose.txt snapshots regenerated via `ail prose`.
- Four-site lockstep compiler deletion: crates/ailang-check/src/builtins.rs
(3 effect_ops.insert blocks + 3 list() rows + the
install_io_print_float_signature test + module + EffectOpSig
doc-comments); crates/ailang-codegen/src/lib.rs lower_app
(3 arms + lowers_io_print_float test); crates/ailang-codegen/src/synth.rs
builtin_effect_op_ret match-arm pattern. Dead `intern_string`
helper removed as a follow-up.
- Five incidental test-body migrations (ailang-check x2, ailang-core
spec_drift + design_schema_drift, ailang-surface/src/lex.rs,
ailang-prose/src/lib.rs round-trip test).
- Cat B test-harness patch: six IR-shape tests in
crates/ail/tests/e2e.rs gained a monomorphise_workspace call
before lower_workspace_with_alloc so they follow the same
pipeline as `ail build` (the home-rolled desugar+lift loop
stayed because mono's precondition is "already lifted"; mono
inserts after lift).
- Six doc-comment touch-ups (lex.rs module doc, parse.rs
diagnostic example, ail/src/main.rs x2, runtime/str.c %g anchor,
crates/ailang-core/specs/form_a.md surface-spec example).
- DESIGN.md seven-site sweep (Decision 11 example, Polymorphic
print past-tense, Heap-Str output sentence, effect-op
invocation comment, Float NaN paragraph re-anchored on
float_to_str, two "What is supported" lists).
- Three E2E test-comment polish + four IR-snapshot refresh + one
canonical-hash pin update (plan-unanticipated downstream
consequences of the corpus migration).
- bench/{check,compile_check,cross_lang}.py: all exit 0; no
ratification needed.
- Roadmap entry struck through; per-iter journal at
docs/journals/2026-05-14-iter-rpe.1.md.
Tests 564/0/3. cargo clippy and cargo doc: zero warnings.
Two upstream codegen bugs surfaced during the first BLOCKED
attempt and were fixed in separate iters before this retry:
-
|
||
|
|
abcdd05991 |
iter clippy-sweep: clear all 61 cargo clippy warnings
Hygiene sweep across the workspace under `cargo clippy --workspace
--all-targets`. Before: 61 warnings. After: zero. Tests stay 563
green, `cargo doc` stays at zero warnings, and all three bench
scripts exit 0 against existing baselines.
Twelve lint classes, three fix shapes:
- Documentation hygiene (~32 hits): `doc_lazy_continuation` resolved
by adding blank lines before continuation paragraphs or rephrasing
the offending line; plus four `empty_line_after_doc_comments` hits
in workspace.rs where eight `///` blocks left orphan by form-a.1
T5 test relocation were converted to plain `//`.
- Idiomatic refactors (~16 hits): `.err().expect()` → `.expect_err()`,
redundant `.into_iter()` and `.into()` removed, `|f| g(f)` →
`g`, `.trim().split_whitespace()` → `.split_whitespace()`,
`push_str("x")` → `push('x')`, two manual `impl Default` flipped
to `#[derive(Default)]` + `#[default]`, two nested `if let Some(_)
= …` collapsed.
- Block extraction (1 hit): a 13-line block inside an `else if`
condition in synth's Var-arm extracted to a new helper
`is_class_method_dispatch(name, env)` next to
`qualifier_is_class_shape`.
Three `#[allow]`s with inline rationale where clippy's suggestion
would lose meaning: `only_used_in_recursion` on walk_pattern (the
parameter preserves the five-fn walker-framework signature
uniformity), 2× `if_same_then_else` on qualify_local_types shapes
(two branches encode semantically distinct disqualification
reasons), `too_many_arguments` on `Emitter::new` (8 workspace-flat
tables; bundling would just rename boilerplate).
|
||
|
|
b638abf1e2 |
tidy: rustdoc-sweep + drift-test-narrowing — autonomous batch
Two unrelated hygiene iters bundled because they shipped together in one autonomous-while-Boss-away batch: - iter rustdoc-sweep: cleared all 23 `cargo doc --workspace --no-deps` warnings (17 in ailang-check + 4 in ailang-core + 2 in ailang-surface). Two warning classes: pub-doc-comment links to pub(crate)/private items (15 hits — replaced with plain backtick-code-spans), and unresolved/ambiguous links (8 hits — fully-qualified, disambiguated to fn-form, or escaped). No behaviour change; tests 562 → 562. - iter drift-test-narrowing: design_schema_drift.rs now scans §"Data model" only via new helper data_model_section(), instead of full DESIGN.md. Closes the audit-form-a-precursor [high] "anchor-elsewhere-passes-silently" failure mode. All 38 anchors verified pre-edit to live in §"Data model" so the tightening doesn't regress to red. New pin data_model_section_is_bounded guards the extractor against silent regression. Tests 562 → 563. |
||
|
|
e809f45e67 |
iter form-a.tidy: form_a.md class/instance/constraints + 3 documentary drift items
Six-task post-fieldtest documentary tidy. No production-code behaviour
changes; 559 tests green at every per-task gate.
T1-T3: form_a.md additions
- §Definitions intro "Three kinds" -> "Five kinds".
- New `### Class — (class ...)` subsection: EBNF carries optional
superclass (0 or 1 per ClassDef.superclass schema), method
signatures with optional defaults; anchored to
examples/test_22c_user_class_e2e.ail (ok 24/2).
- New `### Instance — (instance ...)` subsection: EBNF carries the
(method NAME (body LAM-TERM))* shape; canonical-form CLASS-REF
rule explicitly stated (bare same-module / qualified cross-module);
two examples — same-module abbreviated from
mq3_class_eq_vs_fn_eq_classmod.ail and cross-module qualified
from show_user_adt.ail; bare-cross-module-class-ref diagnostic
named inline.
- §Types `(forall ...)` line extended with optional `(constraints
(constraint CLASS-REF TYPE)+)?` clause; explanatory paragraph
added with no-instance diagnostic anchor; fifth example added
to the Examples block anchored to cmp_max_smoke.ail.
T4: docs/specs/2026-05-13-form-a-default-authoring.md "seven
carve-outs" → "eight carve-outs" at 6 sites contradicting the
§C4(b) compile-time-embed amendment (commit
|
||
|
|
9fdc4cacff |
iter form-a.1 (Tasks 6-12): milestone close
Second half of the form-a-default-authoring milestone-close iter
(Boss-decided strategy C, big-bang). All seven tasks DONE; cargo
test --workspace green at every per-task boundary.
T6 — Bench-driver suffix flip from .ail.json to .ail across 4
Python scripts + run.sh. compile_check.py + cross_lang.py exit 0.
T7 — Re-author e2e.rs raw-JSON-inspect tests:
- diff_detects_changed_def — derive sum.ail.json on-the-fly via
`ail parse examples/sum.ail` into tempdir, then mutate + diff.
- borrow_own_demo_modes_are_metadata_only — same pattern.
- reuse_as_demo_under_rc_uses_inplace_rewrite — same pattern.
- render_parse_round_trip_canonical — RETIRED (subsumed by T1's
cli_parse_then_render_then_parse_is_idempotent over whole corpus).
- ail_run_accepts_ail_source_with_same_stdout_as_ail_json —
re-authored to derive hello.ail.json in a per-process tempdir
from hello.ail via `ail parse`, then assert dual-form stdout.
T8 — Bulk-delete 156 non-carve-out .ail.json. Inventory:
8 .ail.json (carve-outs, alphabetical: broken_unbound + prelude
+ 3× test_22b2_* + 3× test_ct1_*) + 157 .ail. carve_out_inventory
test un-#[ignore]'d and green. Forward-pulled 20 repairs that the
T1-5 dispatch's recon missed (12 Group-B suffix + 5 Group-A
load_workspace + 3 ail_run sites). Also forward-pulled T9 Step 5
(schema_coverage corpus flip from .ail.json to .ail) to satisfy
T8's green-gate.
T9 — Retire obsolete roundtrip tests:
- print_then_parse_round_trips_every_fixture (round_trip.rs)
- every_ail_fixture_matches_its_json_counterpart (round_trip.rs)
- cli_render_then_parse_preserves_canonical_bytes_on_every_fixture
- Dead helpers: list_json_fixtures ×2, round_trip_one,
strip_trailing_newlines.
Schema-coverage corpus already flipped in T8 (forward-pull).
T10 — DESIGN.md §"Roundtrip Invariant" (lines 2027-2109) restated
with parse-determinism + idempotency + CLI-pipeline-idempotency +
carve-out-anchor framing. Five surviving enforcement tests named.
§"Float literals" and §"Why anchored at top level" preserved.
T11 — §A4 doctrine edits: CLAUDE.md:5-6 + DESIGN.md:465-466.
Canonical form remains JSON-AST; authoring projection is .ail;
build derives JSON-AST in-process via ailang_surface::parse.
T12 — Milestone close:
- WhatsNew entry: user-facing language, lead with the change.
- Roadmap: [milestone] form-a struck [x] with closing note.
- Final inventory verified: 8 .ail.json + 157 .ail.
- Final cargo test --workspace: 557 passed, 0 failed, 3 ignored.
- bench/compile_check.py + bench/cross_lang.py: exit 0.
Test math: pre-iter 558 baseline + 3 new T1 tests = 561, − 1
(T7 retire) − 3 (T9 retire) = 557 final.
INDEX.md appended with the full iter summary covering T1-T12 (the
T1-5 commit at
|
||
|
|
77b28ad64d |
iter form-a.1 (Tasks 1-5): additive phase + relocation
First half of the form-a-default-authoring milestone-close iter (Boss-decided strategy C, big-bang). All five tasks DONE; cargo test --workspace green at every per-task boundary. T1 — Add three new tests: - parse_is_deterministic_over_every_ail_fixture (round_trip.rs) - cli_parse_then_render_then_parse_is_idempotent (roundtrip_cli.rs) - carve_out_inventory.rs (new file; #[ignore]'d until T8 deletion) T2 — Bulk-render the 99 missing examples/<stem>.ail files via `ail render`. Corpus 58 .ail (pre-iter) -> 157 .ail. Eight .ail.json carve-outs (7 §C4(a) subject-matter + 1 §C4(b) prelude) preserved. One re-loop triggered: load_workspace prefers .ail siblings since ext-cli.1, so the newly-rendered imports broke seven Group-A entries whose JSON entry-paths now resolved imports to fresh .ail. Repair: pre-emptive forward-pull of five T3 migrations + 4 transient #[ignore]s on workspace.rs mod tests (cleanly relocated in T5). T3 — 14 Group-A test files migrated from ailang_core::load_workspace to ailang_surface::load_workspace + .ail paths. Carve-out lines preserved verbatim (7 sites in typeclass_22b2.rs / typeclass_22b3.rs). T4 — 12 Group-B test files: bulk regex flip on build_and_run / build_and_run_with_alloc / build_and_run_with_rc_stats call sites (~70 e2e.rs invocations + 11 subprocess sites). Four files mis-classified Group-A as Group-B in plan recon (mono_hash_stability, prelude_free_fns, print_mono_body_shape, show_mono_synthesis); two files mis-classified Group-B as Group-A (mono_recursive_fn, mono_xmod_qualified_ref). Migrated per actual shape, not plan label. T5 — Relocated #[cfg(test)] mod tests from production source to integration test crates with ailang-surface dev-dependency: - crates/ailang-core/tests/hash_pin.rs (10 tests from hash.rs) - crates/ailang-core/tests/workspace_pin.rs (10 non-carve-out tests from workspace.rs) - crates/ailang-codegen/tests/eq_primitives_pin.rs (3 tests from codegen/src/lib.rs:3717-3799) - ailang-prose/tests/snapshot.rs migrated (helper + 8 fixtures) to .ail + ailang_surface::load_module Carve-out tests in workspace.rs mod tests preserved in-place (3× 22b2 + 3× ct1 = 6 tests). Tempdir-based loader-mechanism tests (3 sites) also preserved — they don't consume examples/. Tests: 560 passed, 0 failed, 4 ignored (was 558 + 3 T1 new - 1 transit carve_out_inventory #[ignore] = 560 active). Tasks 6-12 (bench-driver suffix flips, e2e diff-test rewrite + 4 additional raw-JSON-inspect handlers, .ail.json deletion, retiring obsolete roundtrip tests + schema_coverage corpus flip, §C3 DESIGN.md restatement, §A4 doctrine edits, WhatsNew + roadmap strike) ship in the next dispatch on this iter ID. Known debt inherited to T6-12: 4 raw-JSON-inspect tests in e2e.rs (borrow_own_demo / reuse_as_demo / render_parse_round_trip_canonical / ail_run_accepts_ail_source_with_same_stdout_as_ail_json dual-form smoke) need rewrite or #[ignore] before T8 deletion; recorded in journal Concerns + Known debt sections. |
||
|
|
3286117605 |
iter 24.2: class Show + 4 primitive instances + 22b TShow migration
Ships class Show a where show : (a borrow) -> Str in the prelude
plus primitive Show Int / Bool / Str / Float instances. Each
instance body is a single-application lambda invoking the
corresponding runtime primitive (int_to_str, bool_to_str,
str_clone, float_to_str) — first prelude instance bodies to call
runtime primitives directly. Float is included in Show (unlike
Eq/Ord) because textual representation is well-defined modulo the
NaN-spelling caveat at DESIGN.md §Float semantics.
The 22b typeclass test corpus is migrated preemptively: 21 fixture
files and 6 consumer files (typeclass_22b{2,3}.rs +
hash.rs ct4 pin + workspace.rs iter22b1 tests + the
instance_present.prose.txt snapshot) rename class Show / show
to class TShow / tshow, analogous to the existing TEq/TOrd
convention. Migration runs before the prelude additions so the
workspace stays green throughout.
Three new tests pin the post-mono shape: show_mono_synthesis.rs
(existence of show__Int/Bool/Str/Float as Def::Fn entries in the
prelude post-mono module), show_dispatch_pin.rs (bare show and
tshow both Step-2 singletons workspace-globally),
mono_hash_stability.rs::primitive_show_mono_symbol_hashes_stay_bit_identical
(body hashes pinned for the 4 new mono symbols).
DESIGN.md §Prelude (built-in) classes drops Show from the
deferred-features list and appends a milestone-24 paragraph
naming the class signature + the 4 instances + the body shape.
print rewire stays deferred to iter 24.3.
Tests: 552 passed (was 548 + 4 new). bench/compile_check +
bench/cross_lang exit 0. bench/check exits 1 on the recurring
noise envelope per the conservative-call lineage.
|
||
|
|
99d3968656 |
iter mq.3: retire MethodNameCollision + multi-class E2E + DESIGN.md sync
Closes the module-qualified-class-names milestone. Deletes the workspace-load workaround; the two-libraries case now resolves via type-driven dispatch at the call site with explicit qualifier as the LLM-author's disambiguation tool. Resolves both mq.2 known-debt items (active_declared_constraints plumbing, (class,method) re-key). New synth warnings channel emits class-method-shadowed-by-fn at all three fn-precedence branches per spec section Class-fn collisions. Three new E2E fixtures + integration tests cover the ambiguous, qualified, and class-fn-shadow trajectories. DESIGN.md class-names paragraph rewritten, new section Method dispatch added. Roadmap P2 marked done, milestone-24 unblocked for re-brainstorm. 9/9 tasks. 545 tests green. bench/compile_check.py + cross_lang.py exit 0; bench/check.py exit 1 (2 noise-class regressions, runtime uncoupled to typecheck iter). |
||
|
|
0eb33235eb |
iter mq.1: class-ref canonical-form extension + workspace-internal class-name qualification
Three schema fields move bare → canonical (bare for same-module,
<module>.<Class> for cross-module): InstanceDef.class,
Constraint.class, SuperclassRef.class. Symmetric to ct.1's
Type::Con.name rule. ClassDef.name stays bare.
validate_canonical_type_names gains three field-walks via new
check_class_ref helper + two sibling diagnostics
BareCrossModuleClassRef / BadCrossModuleClassRef.
check_class_name_fields narrowed to ClassDef.name-only.
Workspace-internal class-name keys all qualified:
class_def_module, class_by_name, registry entries.0,
ClassMethodEntry.class_name, class_superclasses, mono's
class_index, Origin::Class.class_name. New qualify_class_ref
helper at workspace.rs scope plus sibling
qualify_class_ref_in_check in ailang-check.
Two new positive on-disk fixtures (mq1_xmod_constraint_class{,_dep})
exercise the qualified Constraint.class shape.
Recon claim that all existing fixtures' class-refs are intra-module
was empirically wrong — 10 fixtures required minimal canonical-form
migration. Duplicate-instance test architecturally restructured:
post-mq.1 orphan-freedom makes two-modules-on-same-(class,type)
structurally impossible; new test_22b1_dup_same_module fixture
lands both instances in the class's module.
Plan defects fixed inline: Origin::Class.class_name single
construction site (plan recon off-by-one); build_class_index in
mono.rs needed qualifying; build_module_globals needed Def::Instance
carve-out; check_fn declared-constraint matching needed inline
canonical-form lifting; NoInstance Float-aware message arm needed
prelude.Eq/prelude.Ord match; coherence type-leg needed qualified-
head split-and-lookup. Tasks 3-6 ran as one coherent fix.
7/7 tasks, 520 tests green. bench/compile_check.py + cross_lang.py
exit 0; bench/check.py exit 1 (4 improvements-beyond-tolerance,
audit-ratifiable, not a regression).
MethodNameCollision + dispatch path unchanged; iters mq.2 + mq.3
land them.
|
||
|
|
0d3f44bee1 | iter ctt.3: KindMismatch retire | ||
|
|
9d01d0884c | iter ctt.2: Registry.type_def_module re-key to (owning_module, bare_name) | ||
|
|
79cc78507b |
iter ext-cli.1: CLI accepts .ail (Form A) sources alongside .ail.json
Closes the loop opened by this morning's .ailx → .ail rename. Every
path-taking ail subcommand (check, build, run, manifest, render,
prose, merge-prose, describe, emit-ir, diff, workspace, deps) now
accepts a .ail (Form A) input as well as .ail.json (Form B). For
.ail paths the subcommand parses through ailang_surface::parse
in-line and proceeds with the same loaded Module value .ail.json
would have produced; binary semantics are extension-agnostic.
Architecture: a new ailang_surface::{load_module, load_workspace}
pair dispatches on file extension. A new injection point
ailang_core::workspace::load_workspace_with<F> lets the surface
crate plug in an extension-aware loader without core having to
import surface (which would close a crate cycle). Import resolution
in workspace::visit now prefers a .ail sibling over a .ail.json
sibling. The CLI binary's 18 callsites switched to the surface
loaders. Surface parse failures route through
workspace_error_to_diagnostic as a new SurfaceParse variant of
WorkspaceLoadError, so `ail check --json foo.ail` on a malformed
.ail returns a parseable JSON diagnostic with code
surface-parse-error instead of the misleading
`json: expected value at line 1 column 1` fall-through.
Boss pre-commit correction: the implementer followed the plan
verbatim and used snake_case `surface_parse_error`, but every
existing diagnostic code in the codebase is kebab-case (verified
via `git grep` over crates/ailang-check/src and crates/ail/src);
realigned to `surface-parse-error` at three sites (the diagnostic
arm + two ct1 test strings). Journal Concerns section records
the correction.
Tests: cargo test --workspace 487 green (+7 from this iter:
1 core unit, 4 surface integration, 1 ail E2E pair, 1 ct1 CLI
diagnostic-shape). bench/check.py, compile_check.py, cross_lang.py
clean on re-run; the latency.{explicit,implicit}_at_rc tail
cluster occasionally flagged on first runs is the same
nondeterministic cluster the previous two audits documented;
baseline left pristine for the third consecutive iter.
Roadmap follow-up "ail check/build/run accept .ail extension" (P2)
removed.
DESIGN.md §Decision 6 / "CLI" bullet gained the symmetric upward
sentence: both .ail and .ail.json are first-class CLI inputs.
|
||
|
|
72e54f4fd3 |
iter ext-rename: .ailx → .ail across the live toolchain
The surface-form file extension changes from .ailx to .ail. AILang's
authoring surface now uses the same .ail stem as its canonical JSON
form (.ail.json), giving the language a single coherent extension
family: .ail is the LLM-authored Form A, .ail.json is the canonical
JSON-AST Form B.
Scope (touched):
- 61 example renames examples/**/*.ailx → .ail (git mv)
- 1 rename experiments/.../rendered/ailx.md → ail.md
- 35 content-edited live-toolchain files (crates/, docs/DESIGN.md,
docs/roadmap.md, docs/PROSE_ROUNDTRIP.md, skills/, bench/reference/*.c,
experiment crates under experiments/.../{render,harness,master})
- Experiment-crate cohort rename Cohort::Ailx → Cohort::Ail,
Form::Ailx → Form::Ail, per_cohort/ailx → per_cohort/ail,
{form-only: ailx} → {form-only: ail}, ```ailx → ```ail
Out of scope (deliberately untouched, to preserve honest history):
- docs/journal-archive.md (content-frozen per CLAUDE.md)
- docs/journals/, docs/specs/, docs/plans/, bench/orchestrator-stats/
- experiments/.../runs/ (frozen LLM-output artefacts; models actually
saw .ailx — renaming would falsify the experimental record)
Verification: cargo build/test --workspace green; experiment crate
cargo test green; bench/check.py + compile_check.py + cross_lang.py
all 0-regressed; negative grep for ailx|Ailx|AILX outside the
out-of-scope paths returns zero matches.
Opens immediate follow-up: roadmap.md P2 todo `ail check`/build/run
accept .ail extension — after this rename, .ail is canonical
authoring surface but the CLI still produces a misleading JSON-parse
error on `ail check foo.ail`. That's the next iter.
|
||
|
|
098fa7e9be |
iter rt.1: roundtrip-invariant audit tests — 3 new tests, all PASS first-shot
Three new reader-only tests pin the .ail.json / .ailx bijection plus AST-variant coverage. All three passed first observation across the current corpus — no roundtrip, schema-coverage, or CLI-drift gaps surfaced. - every_ailx_fixture_matches_its_json_counterpart (replaces the 3-pair handwritten exhibits): 57 paired .ailx fixtures pass. - every_ast_variant_is_observed_in_the_fixture_corpus (new): 34/34 AST variants exercised across 136 fixtures; exhaustive matches without _ wildcard so AST drift fails the build. - cli_render_then_parse_preserves_canonical_bytes_on_every_fixture (new): 136 fixtures round-trip through ail render → tempfile → ail parse with BLAKE3 identity on canonical bytes. No production code, no DESIGN.md changes (those follow in later iterations). Tests are pure readers of the repo per spec acceptance #7 — tempfile crate added as workspace dep for the CLI roundtrip's intermediate file outside the repo. |
||
|
|
92e830e6c2 |
iter 23.5: prelude free fns + E2E — Eq/Ord milestone close
Five polymorphic prelude free fns (ne/lt/le/gt/ge) plus three E2E fixtures: positive composition over Int/Bool/Str, user-ADT with user-written Eq/Ord on IntBox, and Float-NoInstance with a Float-aware diagnostic addendum cross-referencing DESIGN.md §"Float semantics". Two checker-side fixes were needed alongside the prelude additions, both symmetric to the iter 23.4-prep visibility fix: - linearity::check_module_with_visible — workspace-aware callee-mode resolution so a Borrow-mode user fn forwarding into a prelude poly fn (e.g. `at_most x y = not (gt x y)`) doesn't false-fire consume-while-borrowed. - mono::collect_mono_targets — distinguish rigid forall vars from unbound metavars; rigid-bearing free-fn targets skip (they get re-collected with concrete subst when the enclosing fn itself monomorphises). Without this, a polymorphic body calling another polymorphic free fn produced a spurious __Unit specialisation whose body referenced compare at Unit. Roadmap split: shipped Eq/Ord half checked, Show + print-rewire half remains pending behind the heap-Str ABI prerequisite. DESIGN.md §"Prelude classes" amended. One pre-existing fixture (test_22b1_missing_method) renamed its class method ne → tne to avoid collision with the new prelude `ne`. Bench: check.py + cross_lang.py exit 0; compile_check.py exits 1 with one sub-ms check_ms.local_rec_capture at +26% (tolerance 25%) — prelude-growth-related noise-band fluctuation, ratifiable per spec §248-249 and journal Concerns. |
||
|
|
1d039b782e | iter ct.4.2: pin canonical-form hashes for migrated fixtures + unmigrated sanity | ||
|
|
f22f88b547 | iter ct.1.7: on-disk fixture tests for canonical-form diagnostics | ||
|
|
727d2c13ec | iter ct.1.6: wire validator into load_workspace; refresh kind-mismatch test | ||
|
|
5e653ff536 |
iter ct.1.5a-followup: forall-constraints recursion + docstring + drop iteration-tag comments
Three Minor items from the ct.1.5a quality review: 1. normalize_type_for_registry now recurses into Type::Forall.constraints[].type_, symmetric to walk_type. Closes a latent gap: a Forall with bare cross-module constraint types reaching the registry would have produced a key disagreeing with the qualified form. RED test: ct1_5a_normalize_recurses_into_forall_constraints. 2. Documented the bare-name limitation on Registry::type_def_module: two modules each defining 'type Foo' collide at insert; the proper fix (re-key as (owning_module, bare_name)) is out of ct.1's scope. 3. Removed the four iter-tag explanatory paragraphs at the normalize_type_for_lookup call sites (check/lib.rs check_fn, mono.rs three sites). Replaced each with a one-line pointer to the helper's docstring per project comment policy. |
||
|
|
7872ccfc23 | iter ct.1.5a: registry-side normalize-before-hash | ||
|
|
219908ed54 | iter ct.1.3-followup: test for Constraint.class in ClassDef-method Forall | ||
|
|
9e2e84332f | iter ct.1.3: validator rejects qualified class-name fields |