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 (f2cdd67) before re-dispatch, not patched a
third time.
Boss-verified independently: cargo test --workspace 646/0,
design_index_pin 4/4 (clause-3 RED->GREEN), architect_sweeps.sh exit
0 'All five sweeps clean' (acceptance criterion 9 met), acceptance
grep CLEAN, 3 docs_honesty_pin pinned runs each exactly 1 contiguous
match. Zero spec/quality re-loops. FINAL design-md-rolesplit
iteration — milestone functionally complete, audited, drift-resolved,
hard gate enforces the honesty spirit.
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
# bench/architect_sweeps.sh
|
||||
#
|
||||
# Run the five honesty/anchor sweep-invariant greps against the
|
||||
# design/ prose set (design/contracts + design/models); the spine is
|
||||
# design/contracts (the honesty-bound contract surface; design/models
|
||||
# is the narrative tier, out of honesty-sweep scope); the spine is
|
||||
# design/INDEX.md. Output: matched lines, prefixed by sweep name.
|
||||
# Exit 0 if all five sweeps are clean, 1 if any anchor was found,
|
||||
# 2 if the spine (design/INDEX.md) is not found. Sweeps 1-4 are the
|
||||
@@ -22,7 +23,7 @@
|
||||
|
||||
set -u
|
||||
INDEX="design/INDEX.md"
|
||||
DESIGN_GLOB="design/contracts design/models"
|
||||
DESIGN_GLOB="design/contracts" # contracts only — design/models is the narrative tier; "as of milestone N" context is legitimate there
|
||||
HITS=0
|
||||
|
||||
if [[ ! -f "$INDEX" ]]; then
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"iter_id": "design-md-rolesplit.tidy",
|
||||
"date": "2026-05-19",
|
||||
"mode": "standard",
|
||||
"outcome": "DONE",
|
||||
"tasks_total": 7,
|
||||
"tasks_completed": 7,
|
||||
"reloops_per_task": { "1": 0, "2": 0, "3": 0, "4": 0, "5": 0, "5b": 0, "6": 0, "7": 0 },
|
||||
"review_loops_spec": 0,
|
||||
"review_loops_quality": 0,
|
||||
"blocked_reason": null,
|
||||
"notes": "Re-dispatch after Boss plan re-derivation; the corrected plan ran first-shot clean across all tasks with zero spec/quality re-loops. Task 1 RED verified against un-stripped tree before strips; clause-3 flipped RED->GREEN at Task 7. One Edit retry on roundtrip-invariant.md:73 due to a soft-wrap boundary in the match string (tool-level, not a review re-loop)."
|
||||
}
|
||||
@@ -129,29 +129,124 @@ fn every_contract_names_a_resolvable_ratifying_test() {
|
||||
|
||||
#[test]
|
||||
fn contracts_carry_no_decision_record_prose() {
|
||||
// clause 3 — the conflation tripwire.
|
||||
// clause 3 — the conflation tripwire, widened (design-md-rolesplit.tidy,
|
||||
// audit Resolution-4) to a FAITHFUL SUPERSET of
|
||||
// bench/architect_sweeps.sh Sweep-1's history-anchor regex over the
|
||||
// design/contracts/ scope, PLUS the audit-named decision-record
|
||||
// phrases (the deliberate widening that closes the case-variance
|
||||
// dodge the 6 literal markers left open).
|
||||
//
|
||||
// Invariant: clause-3 GREEN ⟹ Sweep-1 finds nothing in design/contracts/.
|
||||
//
|
||||
// Sweep-1 = 'Iter [0-9]+[a-z]?(\.[0-9]+)?|Family [0-9]+
|
||||
// |^[^/]*2026-[0-9]{2}-[0-9]{2}|\*\*Status: |pre-[0-9]+[a-z]?
|
||||
// |[0-9]+[a-z]? sketch|21\.g'
|
||||
// Faithfulness, per Sweep-1 alternative:
|
||||
// - Iter / Family / pre- / <n> sketch / 21.g / **Status:
|
||||
// → sweep1_line_anchor() reproduces each verbatim,
|
||||
// case-SENSITIVE (grep -E, no -i) — exactly Sweep-1 here,
|
||||
// never narrower.
|
||||
// - ^[^/]*2026-NN-NN → date_anchor() reproduces Sweep-1's
|
||||
// ^[^/]* PATH-EXCLUSION (only the line prefix before the
|
||||
// first '/' is scanned) and generalises 2026→20NN
|
||||
// (⊇ Sweep-1, never narrower). A `docs/specs/2026-..`
|
||||
// citation has '/' before the date ⇒ NOT flagged — faithful
|
||||
// to Sweep-1, no over-fire on legit present-tense spec
|
||||
// cross-refs (the iter-.1 plan defect this repairs).
|
||||
// - PHRASES: literal decision-record idioms (case-insensitive) —
|
||||
// the deliberate widening that closes the capital-variance
|
||||
// dodge ("An earlier draft" vs "an earlier draft").
|
||||
//
|
||||
// A blanket case-insensitive iter/milestone detector was evaluated
|
||||
// and REJECTED (audit Resolution-4 corrected): it conflates the
|
||||
// memory-model rule-names "Iter A"/"Iter B", and ordinary words
|
||||
// "pre-existing"/"pre-set"/"pre-Boehm"/"pre-tail-call", with
|
||||
// provenance stamps — unworkable. faithful-Sweep-1 (the capital-I,
|
||||
// digit-anchored form) already excludes those by construction and
|
||||
// is confirmed ZERO across every contract file; lowercase
|
||||
// `(iter <code>)` provenance is removed by the strip tasks, not by
|
||||
// a fragile hard-gate regex. The load-bearing invariant
|
||||
// (clause-3 GREEN ⟹ Sweep-1 clean in contracts/) holds with
|
||||
// sweep1_line_anchor + date_anchor + PHRASES alone.
|
||||
let dir = root().join("design/contracts");
|
||||
let markers = [
|
||||
"we rejected",
|
||||
"an earlier draft",
|
||||
"Why not other",
|
||||
"was retired in iter",
|
||||
"rollback plan",
|
||||
"previously all",
|
||||
|
||||
const PHRASES: &[&str] = &[
|
||||
"we rejected", "an earlier draft", "earlier draft committed",
|
||||
"why not other", "was retired", "were retired", "rollback plan",
|
||||
"previously all", "deliberately deferred", "new-baseline decision",
|
||||
"amends the above", "out of scope per",
|
||||
"the journal records when", "unchanged from the original draft",
|
||||
];
|
||||
|
||||
// Sweep-1's non-date alternatives, hand-rolled verbatim, case-SENSITIVE
|
||||
// (grep -E without -i), on the raw physical line.
|
||||
fn sweep1_line_anchor(line: &str) -> Option<&'static str> {
|
||||
let kw_digit = |kw: &str| -> bool {
|
||||
let mut from = 0;
|
||||
while let Some(i) = line[from..].find(kw) {
|
||||
let p = from + i + kw.len();
|
||||
if line[p..].chars().next().map_or(false, |c| c.is_ascii_digit()) {
|
||||
return true;
|
||||
}
|
||||
from = p;
|
||||
}
|
||||
false
|
||||
};
|
||||
if kw_digit("Iter ") { return Some("Iter <n>"); }
|
||||
if kw_digit("Family ") { return Some("Family <n>"); }
|
||||
if kw_digit("pre-") { return Some("pre-<n>"); }
|
||||
if line.contains("**Status: ") { return Some("**Status:"); }
|
||||
if line.contains("21.g") { return Some("21.g"); }
|
||||
if let Some(s) = line.find(" sketch") {
|
||||
let pre = line[..s].as_bytes();
|
||||
if pre.last().map_or(false, u8::is_ascii_alphanumeric)
|
||||
&& pre.iter().rev()
|
||||
.take_while(|c| c.is_ascii_alphanumeric())
|
||||
.any(u8::is_ascii_digit)
|
||||
{
|
||||
return Some("<n> sketch");
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// Sweep-1's `^[^/]*2026-NN-NN`, generalised to 20NN-NN-NN,
|
||||
// PATH-EXCLUDED: scan only the line prefix BEFORE the first '/'.
|
||||
fn date_anchor(line: &str) -> Option<String> {
|
||||
let prefix = line.split('/').next().unwrap_or(line).as_bytes();
|
||||
for w in prefix.windows(10) {
|
||||
if w[0] == b'2' && w[1] == b'0'
|
||||
&& w[2].is_ascii_digit() && w[3].is_ascii_digit()
|
||||
&& w[4] == b'-' && w[5].is_ascii_digit() && w[6].is_ascii_digit()
|
||||
&& w[7] == b'-' && w[8].is_ascii_digit() && w[9].is_ascii_digit()
|
||||
{
|
||||
return Some(String::from_utf8_lossy(w).into_owned());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
for entry in fs::read_dir(&dir).expect("design/contracts/ exists") {
|
||||
let p = entry.unwrap().path();
|
||||
if p.extension().and_then(|e| e.to_str()) != Some("md") {
|
||||
continue;
|
||||
}
|
||||
let body = norm(&fs::read_to_string(&p).unwrap());
|
||||
for m in &markers {
|
||||
let raw = fs::read_to_string(&p).unwrap();
|
||||
let fname = p.file_name().unwrap().to_string_lossy().into_owned();
|
||||
let low = norm(&raw).to_lowercase();
|
||||
for ph in PHRASES {
|
||||
assert!(
|
||||
!body.contains(m),
|
||||
"decision-record marker {:?} found in contract {:?} — re-conflation",
|
||||
m,
|
||||
p.file_name().unwrap()
|
||||
!low.contains(ph),
|
||||
"clause-3: history phrase {ph:?} in contract {fname:?}"
|
||||
);
|
||||
}
|
||||
for line in raw.lines() {
|
||||
if let Some(a) = sweep1_line_anchor(line) {
|
||||
panic!("clause-3: Sweep-1 anchor {a:?} in contract {fname:?}: {line:?}");
|
||||
}
|
||||
if let Some(d) = date_anchor(line) {
|
||||
panic!("clause-3: history date {d:?} in contract {fname:?}: {line:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ are real surface forms.
|
||||
// lowers as in-place rewrite under `--alloc=rc`.
|
||||
{ "t": "reuse-as", "source": Term, "body": Term }
|
||||
|
||||
// loop-recur iter 1: strict iteration block. `binders` declares
|
||||
// loop: strict iteration block. `binders` declares
|
||||
// one or more loop parameters (name, type, init), evaluated in
|
||||
// order on loop entry; `body` is in scope of all binders. The
|
||||
// loop's value is `body`'s value on the iteration that exits via a
|
||||
@@ -158,7 +158,7 @@ are real surface forms.
|
||||
"binders": [ { "name": "<id>", "type": Type, "init": Term }, ... ],
|
||||
"body": Term }
|
||||
|
||||
// loop-recur iter 1: re-enter the lexically innermost enclosing
|
||||
// recur: re-enter the lexically innermost enclosing
|
||||
// `loop`, rebinding its binders positionally to `args`. Transfers
|
||||
// control (no fall-through); valid only in tail position of its
|
||||
// enclosing loop (enforced at typecheck, `recur-not-in-tail-position`).
|
||||
|
||||
@@ -97,8 +97,8 @@ LLM-author wants. Use ordering operators (`<`, `>`, ...) and
|
||||
|
||||
`float_to_str` (Float → Str) and `int_to_str` (Int → Str) are
|
||||
fully wired through checker, codegen, and runtime. Both allocate
|
||||
a fresh heap-Str slab at the call site (see "Str ABI" below for
|
||||
the dual realisation) and carry `ret_mode: Own` so the let-binder
|
||||
a fresh heap-Str slab at the call site (see design/contracts/str-abi.md
|
||||
for the dual realisation) and carry `ret_mode: Own` so the let-binder
|
||||
for the call result is RC-tracked and the slab is freed at scope
|
||||
close.
|
||||
|
||||
|
||||
@@ -69,8 +69,7 @@ inherit the gate automatically):
|
||||
Pins drift internal tests cannot see.
|
||||
- `crates/ailang-core/tests/schema_coverage.rs::every_ast_variant_is_observed_in_the_fixture_corpus`
|
||||
— every variant of `Def`, `Term`, `Pattern`, `Literal`, `Type`,
|
||||
and `ParamMode` appears in at least one `.ail` fixture (corpus
|
||||
flipped from `.ail.json` to `.ail` at iter form-a.1). New AST
|
||||
and `ParamMode` appears in at least one `.ail` fixture. New AST
|
||||
variants fail compile until the visitor and corpus are extended
|
||||
in lockstep.
|
||||
- `crates/ailang-core/tests/carve_out_inventory.rs::examples_ail_json_inventory_matches_carve_outs`
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
## What is not (yet) supported
|
||||
|
||||
Snapshot of the current boundary. Items move out of this list
|
||||
as iterations land; the JOURNAL records when.
|
||||
Snapshot of the current boundary.
|
||||
|
||||
- No effect handlers — only the built-in `IO` op (`io/print_str`).
|
||||
`Diverge` is a reserved effect name with no op and no codegen.
|
||||
@@ -13,8 +12,7 @@ as iterations land; the JOURNAL records when.
|
||||
a body, lambdas check monomorphically against their declared type.
|
||||
- Polymorphic fns must be **directly called** at the use site.
|
||||
Passing a polymorphic fn as a value (`let f = id in f(42)`) is
|
||||
not yet supported — it would need one closure-pair global per
|
||||
instantiation, deferred.
|
||||
not yet supported.
|
||||
- No higher-rank polymorphism. Passing a polymorphic fn to another
|
||||
polymorphic fn (`apply(id, 42)`) is not supported.
|
||||
- No recursive `let` for non-fn values. Plain `let x = … in …` only
|
||||
@@ -32,10 +30,8 @@ What **is** supported (and used as the smoke test for the pipeline):
|
||||
- Int, Bool, Unit, **Str**, **Float** as primitive types.
|
||||
- `if`, `let`, function calls, recursion.
|
||||
- Effects on function signatures, with `do op(args)` for direct effect
|
||||
ops (`io/print_str`). The per-type print ops
|
||||
`io/print_int`, `io/print_bool`, `io/print_float` were retired in
|
||||
iter rpe.1; the polymorphic `print` (§"Polymorphic print") is the
|
||||
canonical output path for non-Str values.
|
||||
ops (`io/print_str`). The polymorphic `print` (§"Polymorphic print")
|
||||
is the canonical output path for non-Str values.
|
||||
- **Builtins.** Arithmetic operators (`+`, `-`, `*`, `/`) of type
|
||||
`forall a. (a, a) -> a` (codegen-restricted to `{Int, Float}`);
|
||||
`%` of type `(Int, Int) -> Int` (Int-only — `fmod` semantics for
|
||||
|
||||
+12
-16
@@ -11,16 +11,16 @@ and return an owned `Str`. Each is registered as a builtin in
|
||||
`crates/ailang-codegen/src/lib.rs::lower_app` to a `call ptr @ailang_<name>`,
|
||||
and backed by a `runtime/str.c` C helper.
|
||||
|
||||
- `int_to_str : (borrow Int) -> Str` (iter 24.1) — decimal rendering
|
||||
- `int_to_str : (borrow Int) -> Str` — decimal rendering
|
||||
of an `Int`. Backs `Show Int` in the prelude.
|
||||
- `bool_to_str : (borrow Bool) -> Str` (iter 24.1) — `"true"`/`"false"`.
|
||||
- `bool_to_str : (borrow Bool) -> Str` — `"true"`/`"false"`.
|
||||
Backs `Show Bool` in the prelude.
|
||||
- `float_to_str : (borrow Float) -> Str` (iter 24.1) —
|
||||
- `float_to_str : (borrow Float) -> Str` —
|
||||
type-installed; codegen is reserved and not yet shipped.
|
||||
- `str_clone : (borrow Str) -> Str` (iter 24.1) — allocates a fresh
|
||||
- `str_clone : (borrow Str) -> Str` — allocates a fresh
|
||||
heap-Str copy of the input's bytes. Backs `Show Str` in the prelude.
|
||||
- `str_concat : (borrow Str, borrow Str) -> Str` (iter str-concat,
|
||||
2026-05-13) — combines two `Str` values into a single owned `Str`.
|
||||
- `str_concat : (borrow Str, borrow Str) -> Str` — combines two `Str`
|
||||
values into a single owned `Str`.
|
||||
General-purpose; commonly used in Show bodies for labelled output
|
||||
(`(app str_concat "label=" (app int_to_str x))`).
|
||||
|
||||
@@ -35,16 +35,12 @@ directly; values of other primitive types route through the
|
||||
polymorphic `print` helper (§"Polymorphic print"), which feeds
|
||||
the heap-Str result of `show x` into `io/print_str`.
|
||||
|
||||
`==`, `<`, `<=`, `>`, `>=` REMAIN primitive operators (unchanged
|
||||
from the original draft). Class methods are accessed by name (`eq x y`,
|
||||
`lt x y`, …), not via these operators. Routing operators through
|
||||
classes is deliberately deferred — it would require migrating every
|
||||
existing fixture and would re-baseline the bench corpus, which is a
|
||||
new-baseline decision rather than an iter detail.
|
||||
`==`, `<`, `<=`, `>`, `>=` REMAIN primitive operators. Class methods
|
||||
are accessed by name (`eq x y`, `lt x y`, …), not via these
|
||||
operators.
|
||||
|
||||
`Num` is NOT in milestone 22. Arithmetic operators (`+`, `-`, `*`,
|
||||
`/`) stay primitive and per-type. Class-based numeric overloading
|
||||
would invoke literal-defaulting which axis-7 already excluded.
|
||||
Arithmetic operators (`+`, `-`, `*`, `/`) stay primitive and
|
||||
per-type.
|
||||
|
||||
**Str ABI.** A `Str` is a pointer to a structure with `i64 len` at
|
||||
offset 0 followed by `len` bytes plus a trailing `NUL` at offset 8.
|
||||
@@ -71,7 +67,7 @@ the previous global in `.rodata` and reading them is undefined.
|
||||
**The static-Str non-RC invariant is enforced at codegen.** Two
|
||||
mechanisms keep static-Str pointers out of `ailang_rc_dec` along
|
||||
every shipping execution path: (1) the non-escape lowering pass
|
||||
(iter 18b) and the move-tracking partial-drop logic (iter 18d.3)
|
||||
and the move-tracking partial-drop logic
|
||||
prevent let-binders or pattern-binders for static-Str literals
|
||||
from reaching scope-close drop emission; (2) the `Type::Con { name: "Str" }`
|
||||
carve-outs in `field_drop_call` and in the `Term::App` arm of
|
||||
|
||||
@@ -74,8 +74,7 @@ user-ADT call site `print (MkIntBox 7)` causes synthesis of
|
||||
user-defining module per Decision 11 coherence). The synthesised body
|
||||
crosses a module boundary the source template did not.
|
||||
|
||||
Three invariants make this work, all installed in milestone 24's iter
|
||||
24.3 and worth keeping load-bearing:
|
||||
Three invariants make this work:
|
||||
|
||||
1. **`MonoTarget::FreeFn::type_args` carries canonical types
|
||||
post-collection.** At every site where `subst.apply(m)` produces a
|
||||
@@ -179,11 +178,10 @@ wording is fixed; the categories and their triggers are:
|
||||
- `OverridingNonExistentMethod` — instance specifies a method not in
|
||||
the class.
|
||||
|
||||
(`MethodNameCollision` was retired at iter mq.3. Cross-class method
|
||||
sharing is now structurally legal; ambiguity surfaces at the call
|
||||
site via `AmbiguousMethodResolution` or, for class-fn name overlap,
|
||||
via the `class-method-shadowed-by-fn` warning. See §"Method
|
||||
dispatch" below.)
|
||||
Cross-class method sharing is structurally legal; ambiguity surfaces
|
||||
at the call site via `AmbiguousMethodResolution` or, for class-fn
|
||||
name overlap, via the `class-method-shadowed-by-fn` warning. See
|
||||
§"Method dispatch" below.
|
||||
|
||||
**Class-schema diagnostics** (validation of class declarations):
|
||||
|
||||
@@ -196,8 +194,7 @@ A class param appearing in applied position (e.g., `f a` where `f`
|
||||
is the class param) is rejected earlier by the canonical-form
|
||||
validator as `BareCrossModuleTypeRef` — `f` is a bare,
|
||||
non-primitive name not declared as a `TypeDef` in the owning
|
||||
module — so the dedicated `KindMismatch` diagnostic was retired
|
||||
at iter ctt.3.
|
||||
module.
|
||||
|
||||
**Typecheck diagnostics** (per function body):
|
||||
|
||||
@@ -276,40 +273,23 @@ warning surfaces the shadow.
|
||||
|
||||
## Prelude (built-in) classes
|
||||
|
||||
Milestone 22 ships **no built-in Prelude classes**.
|
||||
An earlier draft committed to a fixed Prelude (Show / Eq / Ord on the
|
||||
primitives), but the implementation work to wire `int_to_str` as a
|
||||
heap-allocated-string runtime primitive proved substantively
|
||||
separable from the typeclass machinery itself, and the LLM-utility
|
||||
case for primitive `Show` is weak (LLM-natural form is `int_to_str
|
||||
x`, not `show x` through a single-instance class). The user-class
|
||||
end-to-end path is the milestone's typeclass acceptance gate
|
||||
(the user-defined-class fixture: `class Foo a` + `data IntBox` +
|
||||
`instance Foo IntBox`); see `docs/specs/2026-05-09-22-typeclasses.md`
|
||||
"Amendments" for the substantive rationale.
|
||||
The prelude ships the `Ordering` ADT, the `Eq` and `Ord` classes,
|
||||
primitive `Eq Int/Bool/Str` and `Ord Int/Bool/Str` instances, and
|
||||
the five polymorphic free-fn helpers `ne`/`lt`/`le`/`gt`/`ge`. Float
|
||||
has neither `Eq` nor `Ord` instance per §"Float semantics"; a
|
||||
polymorphic helper invoked at Float fires `NoInstance` at typecheck
|
||||
with a Float-aware diagnostic cross-referencing this section.
|
||||
|
||||
Milestone 23 amends the above: the prelude now ships the `Ordering`
|
||||
ADT, the `Eq` and `Ord` classes, primitive `Eq Int/Bool/Str` and
|
||||
`Ord Int/Bool/Str` instances, and the five polymorphic free-fn helpers
|
||||
`ne` / `lt` / `le` / `gt` / `ge`. Operator routing through `Eq`/`Ord`
|
||||
and `print`-rewire remain out of scope per their original substantive
|
||||
reasons (bench rebaseline, milestone-24 dependency on the post-mq
|
||||
dispatcher); `Show` itself ships in milestone 24. Float has neither
|
||||
`Eq` nor `Ord` instance per §"Float semantics"; a polymorphic helper
|
||||
invoked at Float fires `NoInstance` at typecheck with a Float-aware
|
||||
diagnostic cross-referencing this section.
|
||||
|
||||
Milestone 24 amends the above further: the prelude ships `class Show
|
||||
a where show : (a borrow) -> Str` and primitive `Show Int`,
|
||||
`Show Bool`, `Show Str`, `Show Float` instances (iter 24.2). Float
|
||||
**is** included in Show (unlike Eq/Ord) — IEEE-754 makes structural
|
||||
equality and total ordering semantically dubious, but textual
|
||||
representation of a Float is well-defined modulo the NaN-spelling
|
||||
caveat in §"Float semantics". Each `Show <T>` instance body is a
|
||||
single-application lambda invoking the corresponding runtime primitive
|
||||
(`int_to_str`, `bool_to_str`, `str_clone`, `float_to_str`); no codegen
|
||||
intercept is required. The polymorphic helper `print : forall a. Show
|
||||
a => a -> () !IO` shipped in iter 24.3 with body
|
||||
The prelude ships `class Show a where show : (a borrow) -> Str` and
|
||||
primitive `Show Int`, `Show Bool`, `Show Str`, `Show Float`
|
||||
instances. Float **is** included in Show (unlike Eq/Ord) — IEEE-754
|
||||
makes structural equality and total ordering semantically dubious,
|
||||
but textual representation of a Float is well-defined modulo the
|
||||
NaN-spelling caveat in §"Float semantics". Each `Show <T>` instance
|
||||
body is a single-application lambda invoking the corresponding
|
||||
runtime primitive (`int_to_str`, `bool_to_str`, `str_clone`,
|
||||
`float_to_str`); no codegen intercept is required. The polymorphic
|
||||
helper `print : forall a. Show a => a -> () !IO` has body
|
||||
`\x -> let s = show x in do io/print_str s` (explicit let-binder for
|
||||
heap-Str RC discipline per eob.1 Str carve-out). The let-binder is
|
||||
structurally pinned by `crates/ail/tests/print_mono_body_shape.rs`.
|
||||
|
||||
@@ -429,3 +429,51 @@ post-ct.2.2 — a different consumer story, hence a different
|
||||
overlay shape. The asymmetry between the check side and the mono
|
||||
side is by design and is pinned by
|
||||
`crates/ailang-check/tests/duplicate_ctor_pin.rs`.
|
||||
|
||||
## Migrated from design/contracts/ at design-md-rolesplit.tidy (2026-05-19)
|
||||
|
||||
History/decision-record prose extracted from contract files by the
|
||||
post-audit tidy (the `###`-whole relocation in iter .1 dragged these
|
||||
into contracts; they are relitigation-guard content, not contract).
|
||||
|
||||
### typeclasses.md — Prelude-classes evolution
|
||||
|
||||
An earlier draft committed to a fixed Prelude (Show/Eq/Ord on the
|
||||
primitives), but the implementation work to wire `int_to_str` as a
|
||||
heap-allocated-string runtime primitive proved substantively
|
||||
separable from the typeclass machinery itself, and the LLM-utility
|
||||
case for primitive `Show` is weak (LLM-natural form is `int_to_str
|
||||
x`, not `show x`). Milestone 23 added `Ordering`/`Eq`/`Ord` +
|
||||
primitive instances + the five helpers; operator routing through
|
||||
`Eq`/`Ord` and `print`-rewire were held out for bench-rebaseline /
|
||||
post-mq-dispatcher reasons; `Show` shipped in milestone 24 (iters
|
||||
24.2/24.3). `MethodNameCollision` was retired at iter mq.3
|
||||
(cross-class method sharing became structurally legal). The
|
||||
dedicated `KindMismatch` diagnostic was retired at iter ctt.3
|
||||
(`BareCrossModuleTypeRef` subsumes it).
|
||||
|
||||
### str-abi.md — heap-Str builtin provenance + deferred-operator rationale
|
||||
|
||||
Builtin iter provenance: `int_to_str`/`bool_to_str`/`float_to_str`/
|
||||
`str_clone` shipped iter 24.1; `str_concat` shipped iter str-concat
|
||||
(2026-05-13); the non-escape static-Str lowering pass landed iter
|
||||
18b, move-tracking partial-drop iter 18d.3. Operator routing through
|
||||
classes is deliberately deferred — it would migrate every fixture
|
||||
and re-baseline the bench corpus (a new-baseline decision, not an
|
||||
iter detail). `Num` is not in milestone 22: class-based numeric
|
||||
overloading would invoke literal-defaulting which axis-7 excluded.
|
||||
|
||||
### scope-boundaries.md — retired ops + deferral rationale
|
||||
|
||||
Polymorphic-fn-as-value is deferred because it would need one
|
||||
closure-pair global per instantiation. The per-type print ops
|
||||
`io/print_int`/`io/print_bool`/`io/print_float` were retired in iter
|
||||
rpe.1 (the polymorphic `print` is the canonical non-Str output path).
|
||||
|
||||
### roundtrip-invariant.md / data-model.md — corpus + loop-recur provenance
|
||||
|
||||
The Form-A round-trip corpus was flipped from `.ail.json` to `.ail`
|
||||
at iter form-a.1. The `loop`/`recur` schema entries were added at
|
||||
loop-recur iter 1 (strictly additive; pre-existing fixtures hash
|
||||
bit-identically). Provenance only — the present-tense schema/contract
|
||||
is the entry shapes themselves.
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
# iter design-md-rolesplit.tidy — contract-history strip + clause-3 widening
|
||||
|
||||
**Date:** 2026-05-19
|
||||
**Started from:** f2cdd67e69574f564b63bfd4b7f5715046d75a5a
|
||||
**Status:** DONE
|
||||
**Tasks completed:** 7 of 7 (Tasks 1, 2, 3, 4, 5, 5b, 6, 7 — task_range [1,7] covers the 5b sub-task)
|
||||
|
||||
## Summary
|
||||
|
||||
Closes the `[medium]`+`[low]` spirit-vs-letter drift from
|
||||
audit-design-md-rolesplit: the `###`-whole relocation in iter .1
|
||||
dragged decision-record/history prose into 5 contract files. This
|
||||
tidy extracts that prose into
|
||||
`docs/journals/2026-05-19-design-decision-records.md` (each removed
|
||||
history sentence replaced by its present-tense contract equivalent),
|
||||
fixes the one stale `(see "Str ABI" below)` cross-ref in
|
||||
float-semantics.md, re-scopes `architect_sweeps.sh` from
|
||||
`design/contracts design/models` to `design/contracts` only (the
|
||||
narrative tier is not honesty-bound) with its lockstep
|
||||
`ailang-architect.md` sentence, and widens `design_index_pin.rs`
|
||||
clause-3 into a hand-rolled **faithful superset** of Sweep-1 over
|
||||
the contracts scope (case-sensitive digit-anchored `sweep1_line_anchor`
|
||||
+ path-EXCLUDED `date_anchor` + case-insensitive decision-record
|
||||
PHRASES; no `regex` dep, no blanket iter detector). Gate-first TDD:
|
||||
clause-3 was verified RED against the un-stripped tree (PHRASES hit
|
||||
`"an earlier draft"` in typeclasses.md) before any strip, then
|
||||
flipped GREEN after Tasks 2–5b. End state: design_index_pin 4/4,
|
||||
`architect_sweeps.sh` exit 0, `cargo test --workspace` 646 passed /
|
||||
0 failed, acceptance grep CLEAN.
|
||||
|
||||
## Per-task notes
|
||||
|
||||
- Task 1: replaced `contracts_carry_no_decision_record_prose`
|
||||
(:130-157) with the widened provable-superset matcher (verbatim
|
||||
from plan; other 3 clauses + helpers untouched). RED verified
|
||||
against un-stripped tree; other 3 clauses + build GREEN.
|
||||
- Task 2: typeclasses.md — 6 spans rewritten present-tense (:77-78,
|
||||
:182-186, :197-200, :279-289 deleted wholesale, :291-300, :302-317);
|
||||
Prelude-classes evolution history appended to the decision-record
|
||||
journal. `io/print_str` pin contiguous at :297; docs_honesty_pin 5/5.
|
||||
- Task 3: str-abi.md — `(iter …)` parentheticals + deferred-operator
|
||||
+ `Num`/axis-7 rationale stripped (:14, :16, :18, :20, :22-23
|
||||
soft-wrap collapsed, :74, :38-43, :45-47); journal appended.
|
||||
`type-installed; codegen is reserved…` pin contiguous at :19.
|
||||
- Task 4: scope-boundaries.md — process-meta line plain-deleted
|
||||
(:5-6), closure-pair-global + rpe.1-retirement rationale stripped
|
||||
(:14-17, :36-38); journal appended. Diverge over-strip guard:
|
||||
pin byte-untouched, effect_doc_honesty_pin 4/4 (pin line shifted
|
||||
:9→:8 as the expected consequence of the authorized :5-6 two-line
|
||||
collapse — content match unaffected).
|
||||
- Task 5: float-semantics.md:99-100 stale `(see "Str ABI" below …)`
|
||||
→ `(see design/contracts/str-abi.md …)`. eq_float_noinstance 1/1.
|
||||
- Task 5b: roundtrip-invariant.md:73 corpus-flip parenthetical
|
||||
dropped; data-model.md:149/161 `// loop-recur iter 1:` → `// loop:`
|
||||
/ `// recur:`; :154 `pre-existing` left untouched (ordinary word);
|
||||
journal appended. No lowercase iter-code provenance remains;
|
||||
design_schema_drift 7/7, round_trip 2/2.
|
||||
- Task 6: architect_sweeps.sh DESIGN_GLOB narrowed to
|
||||
`design/contracts` with substantive-reason inline comment + header
|
||||
prose; ailang-architect.md `:77-84` bullet gains the scope sentence.
|
||||
All five sweeps clean, exit 0.
|
||||
- Task 7: whole-tree gate — design_index_pin 4/4 (clause-3 RED→GREEN),
|
||||
3 pins + 2 E2Es PASS, workspace 646/0, sweep exit 0, acceptance
|
||||
grep CLEAN.
|
||||
|
||||
## Concerns
|
||||
|
||||
(none)
|
||||
|
||||
## Known debt
|
||||
|
||||
(none — the audit Resolution scope is fully covered: Resolution-1
|
||||
history→journal across the corrected 5-file set, Resolution-2 stale
|
||||
below, Resolution-3 sweep re-scope+lockstep, Resolution-4 faithful-
|
||||
superset clause-3, Resolution-5 exit state.)
|
||||
|
||||
## Blocked detail
|
||||
|
||||
(none — DONE)
|
||||
|
||||
## Files touched
|
||||
|
||||
- Test gate: `crates/ailang-core/tests/design_index_pin.rs`
|
||||
- Contract strips: `design/contracts/typeclasses.md`,
|
||||
`design/contracts/str-abi.md`,
|
||||
`design/contracts/scope-boundaries.md`,
|
||||
`design/contracts/float-semantics.md`,
|
||||
`design/contracts/roundtrip-invariant.md`,
|
||||
`design/contracts/data-model.md`
|
||||
- Decision-record archive: `docs/journals/2026-05-19-design-decision-records.md`
|
||||
- Sweep re-scope + lockstep: `bench/architect_sweeps.sh`,
|
||||
`skills/audit/agents/ailang-architect.md`
|
||||
|
||||
## Stats
|
||||
|
||||
bench/orchestrator-stats/2026-05-19-iter-design-md-rolesplit.tidy.json
|
||||
@@ -117,3 +117,4 @@
|
||||
- 2026-05-19 — iter design-md-rolesplit.1 (DONE 9/9, whole milestone): the 3020-line `docs/DESIGN.md` 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` onboarding 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` 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→GREEN. Build-atomic by task ordering (the only compile-time consumer, `design_schema_drift.rs` `include_str!`, retargeted to `design/contracts/data-model.md` BEFORE the deletion; the `## Data model`/`## Pipeline` slicer dropped — a simplification the split enables). 2 NoInstance diagnostics + their 2 lockstep E2Es retargeted to `design/contracts/{float-semantics,typeclasses}.md` (the contiguity-across-`\`-continuation hazard scrubbed). ~12 agent reading lists + 5 SKILL bodies + CLAUDE.md + skills/README.md + ~25 code/C/.ail/spec comment xrefs retargeted to the Appendix destinations; 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 preserved verbatim+contiguous. Boss-verified independently: whole `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), honesty-rule repair carries no clause-3 marker. Spec grounding-check PASS ×2 (one re-dispatch after a corrected commitment-4 pin-status claim; one after the Boss-adjudicated relocation-appendix amendment resolving plan-recon's 7 open questions — ledger completed to 17 contract rows incl. the qualified-xref/str-abi/scope-boundaries additions the 12-list under-counted). 2 DONE_WITH_CONCERNS routed to the mandatory milestone-close `audit`: (a) `design/contracts/str-abi.md:23` `(iter str-concat, 2026-05-13)` API-provenance stamp trips advisory `architect_sweeps.sh` Sweep-1 — Boss-confirmed **byte-identical to DESIGN.md@deeffb1:2062-2065**, a faithfully-migrated PRE-EXISTING anchor (sweep regexes verbatim, only the path retargeted), NOT a split-introduced regression — RATIFY-or-tidy at audit; (b) the now stale-direction `(see "Str ABI" below)` intra-prose cross-ref in `float-semantics.md` (the Appendix is heading-level; no task prescribes intra-prose cross-ref rewrite) — audit-adjudication candidate. One plan defect recorded (Task 9 Step 4's verbatim acceptance grep used a `^\./` anchor not matching the system's `grep -rIn` output; substance independently re-verified CLEAN). → 2026-05-19-iter-design-md-rolesplit.1.md
|
||||
- 2026-05-19 — design-decision-records (migration): relitigation-guard archive — every why/rejected/does-not-do/rollback/empirical ### moved out of the former docs/DESIGN.md by the design-md-rolesplit milestone. Companion to spec 2026-05-19-design-md-rolesplit. → 2026-05-19-design-decision-records.md
|
||||
- 2026-05-19 — audit design-md-rolesplit (milestone close): DRIFT (one tidy iteration) + bench causally-exonerated (baseline pristine). Architect drift_found, relocation byte-faithful (MIXED Decisions / dual-link / source-link / rewritten honesty-rule all match the spec Appendix vs deeffb1; build-atomicity holds; design_index_pin 4/4; honesty-rule.md correct + pins retargeted; agent contracts coherent) — one [medium] spirit-finding: faithfully-migrated decision-record/history prose in design/contracts/{typeclasses,str-abi,scope-boundaries}.md DODGES the 6 literal clause-3 markers (case+wording variance) but IS the relitigation content the split's spirit sends to journals; plus [low] float-semantics.md stale-direction "see Str ABI below". Both routed items adjudicated strippable-doc-archaeology-to-TIDY-not-ratify (the str-abi.md:23 advisory Sweep-1 exit-1 correctly diagnoses real pre-existing history residue, byte-identical DESIGN.md@deeffb1:2062-2065, faithfully migrated — not split-introduced). Bencher: NO-ratify, causally-exonerated DECISIVE on byte-evidence — emitted IR AND final -O2 binaries byte-identical 176821c vs pre-milestone dd5b183 for all 6 check.py firings (sha256+cmp), all ~11 changed code/runtime files audited comment/docstring/diagnostic-string-only, zero IR-path change; the firings are tracked-P2 (*.bump_s staleness, *_at_rc.max_us -n5 tail jitter, *.gc_s Boehm-scan variance — NOT the M5 gc_rss trio); compile_check 24/24 + cross_lang 25/25 pristine corroborate; baseline untouched (identical to M2/M3/M5). Resolution: one tidy iter design-md-rolesplit.tidy — (1) move the history prose to the decision-record journal, (2) fix the stale "below", (3) re-scope architect_sweeps honesty sweeps to design/contracts only (models/ is the explicitly-narrative tier — scanning it for history-anchors is a post-split category error), (4) widen design_index_pin.rs clause-3 to SUBSUME Sweep-1's history-anchor regex over contracts/ (invariant: clause-3 GREEN ⟹ Sweep-1 clean in contracts/ — the hard gate enforces the spirit, closing the literal-marker dodge permanently). No fieldtest (zero authoring-surface change). Milestone closes after the tidy lands Boss-verified. → 2026-05-19-audit-design-md-rolesplit.md
|
||||
- 2026-05-19 — iter design-md-rolesplit.tidy (DONE 7/7): resolves the milestone-close audit DRIFT (2ba5e16). Gate-first TDD: widened design_index_pin.rs clause-3 to a hand-rolled FAITHFUL Sweep-1 superset — case-sensitive digit-anchored Sweep-1 line anchors (confirmed ZERO across all contracts) + Sweep-1's `^[^/]*` path-EXCLUDED date_anchor (so docs/specs/2026-.. citations are not flagged) + the audit-named decision-record PHRASES (case-insensitive, closing the capital-variance dodge); NO regex dep, the blanket case-insensitive iter-detector REJECTED as unworkable (it conflated the memory-model rule-names "Iter A/B" + ordinary "pre-existing/pre-set/pre-Boehm" with provenance). Sentence-level strip of the faithfully-migrated history/decision-record prose out of 5 contract files (typeclasses/str-abi/scope-boundaries — the audit's spot-check — PLUS roundtrip-invariant.md:73 + data-model.md:149,161, the 2 the exhaustive plan-time scan found the spot-check missed) into docs/journals/2026-05-19-design-decision-records.md, each removed sentence replaced by its present-tense contract equivalent; float-semantics.md stale `(see "Str ABI" below)` → `(see design/contracts/str-abi.md)`; architect_sweeps.sh honesty sweeps re-scoped design/contracts+design/models → design/contracts only (models/ is the explicitly-narrative tier; scanning it for history-anchors is a post-split category error) + skills/audit/agents/ailang-architect.md lockstep. Invariant established: clause-3 GREEN ⟹ Sweep-1 finds nothing in contracts/. The prior dispatch correctly BLOCKED on a real plan defect (iso_date lacked Sweep-1's path-exclusion → over-fired on legit spec-path citations); per the "two+ defects in one iteration ⇒ fix the upstream artifact, not a third patch" discipline the audit Resolution mechanism+scope were corrected in lockstep (f2cdd67) before re-dispatch. Three docs_honesty_pin pinned-byte runs survived contiguous (str-abi.md:19, typeclasses.md:297, scope-boundaries.md:8 — shifted from :9 by the authorized :5-6 collapse, content byte-untouched). Boss-verified independently: cargo test --workspace 646/0, design_index_pin 4/4 (clause-3 RED→GREEN), architect_sweeps.sh exit 0 "All five sweeps clean" (acceptance criterion 9 met), acceptance grep CLEAN, 3 pins each exactly 1 contiguous match. Zero spec/quality re-loops. This is the FINAL design-md-rolesplit iteration — the milestone (DESIGN.md → design/ ledger role-split) is now functionally complete, audited, drift-resolved, and the in-code hard gate enforces the honesty spirit. → 2026-05-19-iter-design-md-rolesplit.tidy.md
|
||||
|
||||
@@ -81,7 +81,10 @@ If `milestone_scope` is empty, return a structural error and stop.
|
||||
review each match — a legitimate quote (e.g. a journal-entry
|
||||
citation inside a block-quote) is fine; a fresh history anchor /
|
||||
REVERTED narrative / workflow detail / stale cross-reference is
|
||||
drift to flag.
|
||||
drift to flag. Sweeps 1–4 scan `design/contracts/` only —
|
||||
`design/models/` is the narrative tier (§ reading-list: "context,
|
||||
not a drift surface"), so a milestone-context phrase there is not a
|
||||
regrowth.
|
||||
- **design/ honesty drift.** Sweep 5 of `bench/architect_sweeps.sh`
|
||||
(the docs-honesty-lint invariant) flags Wunschdenken / non-citation
|
||||
post-mortem. Apply the discriminator from
|
||||
|
||||
Reference in New Issue
Block a user