working on next-broken #228
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/228-working-on-next-broken"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
working on next-broken
Along the way:
Memtier, so a unit that wants a word at an offset stops spelling the machineTouches 27 files:
.claude/commands/next-broken.md,docs/13-floor.md,docs/examples/console/Main.ck,docs/examples/console/expect,docs/examples/console/project.ck,fix_console.mdand 21 more.Yes. The commit messages carried me through all 27 files, which is not always true.
@ -193,6 +214,7 @@ const CONDEMNED_ALLOC_SITES: &[(&str, usize)] = &[("tests/corpus_test_run/bytes_roundtrip/Main.ck", 2),("tests/corpus_test_run/clock_tick/Main.ck", 1),("tests/corpus_test_run/freestanding_mem/Main.ck", 3),("tests/corpus_test_run/console_io/Main.ck", 2),I had to read
Maintwice. Nothing wrong with it, but one line of comment would save the next person the second read.@ -562,0 +579,4 @@// not the leaf. Pinned by `a_floor_load_in_a_test_fn_body_does_not_lower`. It// blocks nothing: the console reads its control block through// `Stdio::last_len`, a cross-module call that was the better surface anyway.const BROKEN_CELL_COUNT: usize = 44;Where does the number beside
BROKEN_CELL_COUNTcome from? It reads as measured rather than chosen, and nothing on the line says which, at src/rust/crates/ck_driver/tests/capability.rs:582.@ -0,0 +31,4 @@fn out(s: String) -> Unit = write_str(1::i64, s)// fd 2.fn err(s: String) -> Unit = write_str(2::i64, s)errreads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?Measured. It is a named constant in
src/rust/crates/ck_driver/tests/capability.rsnow, with a line saying how, at src/rust/crates/ck_driver/tests/capability.rs:582.Comment added on
Maininsrc/rust/crates/ck_driver/tests/architecture/alloc.rs. It was obvious when I wrote it and it will not be in a month.Split. The decision in
erris a pure function now, with its own tests intests/corpus/stdlib/Stdio.ck.`Stdio` (fd 0/1/2, direct), `effect Console` + `StdioConsole` (the dispatched, config-selectable face), six capability cells, a run-gated corpus app, and a section in the book. No Rust, no new intrinsic, no floor change: `write_bytes`/`read_bytes` have been in `Floor.ckh` since the IO widening. WHY NOW. `tests/corpus/ml_tictactoe` had hand-written a console — a `say`, a byte writer, and a buffered fd-0 reader with its own two-word control block — which is the standard library admitting it had none. And nothing in this repo RAN a program that read fd 0 at all: that app is size-pinned, never executed, so the reader every interactive CkLang program needs was unproven at runtime. THREE CONSTRAINTS WROTE THE API, and each is a gap showing through rather than a preference (plan.md §4g carries the long form): * no `Alloc` — it is condemned and the self-hosted stdlib excises it, so the three units hold ZERO condemned sites and port unchanged. The price is visible: `line` is two writes because `s ++ "\n"` mmaps, and integers go through `int_into(scratch, n)` because `Prelude::show_i64` mmaps twice and releases one of them; * end of input is `-1` — `String` is structurally non-empty and `match` over a union does not lower, so it cannot be `""`, `Option`, or `Result`; * every `Console` operation returns `Unit` — binding an effect operation's result in a free `fn` leaves the frame slot unresolved and E0760 shuts the door, so `read_line` answers through `ctl` and `Stdio::last_len` reads it. RATCHETS, all three moved deliberately: * `CELL_COUNT` 440 -> 446; * `BROKEN_CELL_COUNT` 43 -> 44 — `a_floor_load_in_a_test_fn_body_does_not_lower`, found building this. `Floor::load` written directly in a `test fn` body reaches codegen unresolved while the `Floor::store` beside it lowers and the same load through a cross-module `fn` lowers on both entries, so it is which bodies the test entry stamps, not the leaf. It blocked nothing; * `CONDEMNED_ALLOC_SITES_COUNT` 380 -> 394, seven rows, OWNER-BLESSED. The rule's own remedy is "an unavoidable site is an owner decision, not a table edit" — it was asked and granted. `read(2)` writes into caller memory and `Alloc::os_acquire` is CkLang's only way to obtain a writable `Region`; `Floor::mmap` reaches the same syscall past the counter and was NOT used. All 14 are in what exercises the console, none in the console. MEASURED AND SAID OUT LOUD: the bundled test mock is declared, not dispatched to. A `test fn` calling `Console::line` reaches `StdioConsole` and the bytes land on fd 1; `Log::info` behaves identically. `Log.ckh`'s claim that the test path resolves the mock automatically does not hold at that position, and `Console.ckh` states what is true rather than repeating it. Gates: 4 legs green, integration sweep 251 passed / 1 known-red (the standing `lib.rs` R3), fmt and clippy clean.Owner, 2026-08-18: "You should not have to use `Floor` that much. It means our surface is not correct yet." Correct, and this file is the measurement plus the three fixes it names. `Stdio.ck` spells `Floor::` 43 times in code. SEVEN are the two syscalls the unit exists to make; the other 36 are reading and writing a word at an offset — five sixths of its floor traffic is not its subject. The callers, by contrast, are already clean (zero `Floor::` in the corpus app, the docs example and all six cells), and they got that way by exactly one move: naming the operation (`Stdio::prepare`) instead of making the caller spell the offset. The three items are that same test applied inward. 1. `Alloc::read`/`write` are typed load/store, allocate nothing, and are imprisoned in a CONDEMNED unit — so a unit that must not depend on `Alloc` drops past it to the machine. Wants a `Mem` tier (recommended over `Bytes::get`/`set`, whose unit is not `Alloc`-free either). No compiler change; takes `Stdio.ck` from 43 `Floor::` to 7. It would also have routed around `a_floor_load_in_a_test_fn_body_does_not_lower` unnoticed, which is the sharper argument: a wrong surface walks its users into compiler bugs. 2. `fd: i64` in six public signatures — `Stdio::newline(1::i64)` makes the caller name a kernel constant, and `7::i64` type-checks. Wants `newtype Stream` with `stdin`/`stdout`/`stderr`, the pattern `Floor.ckh` already uses for `Address`. Needs one probe first. 3. No buffer type, so every caller hand-carves offsets out of one mmap and nothing checks they are disjoint. Not fixable here: blocked on const expressions in size positions, `12-range-view` (E0735, filed as "and the retirement of `Region`"), and the 2-word record ABI ceiling. The console's only correct output is the record that it is a consumer waiting on the slot model — when that lands, the 14 M1 sites retire together. Deliberately NOT under `docs/`: that directory is gated prose whose every behavioural sentence is reconciled on each gate run (CLAUDE.md §6). This is a work list, and the long form of plan.md §4g. Gates unchanged: 4 legs green.Memtier, so a unit that wants a word at an offset stops spelling the machine 6ae6b47f00ITEM 1 — LANDED (`acda5a97`), and the estimate held exactly. `Stdio.ck` went **43 `Floor::` occurrences to 7**, the 7 being `5 write_bytes + 2 read_bytes` — the two syscalls the unit exists to make. 18 statements rewritten, 36 occurrences gone, no ratchet moved, and the one pin that did move (`ml_tictactoe.pin`, two new `Mem` rows) declares 49 B and emits 0 B, which is what makes it a re-tiering rather than a change. ITEM 2 — BLOCKED, and nothing of it landed. The item ordered a probe before the surface was written on top of it, and the probe was right to be ordered: two of its three halves work and the third refuses. * `newtype Stream = i64` in a unit — works. * `Stream(1::i64)` built inline and `.value` read in argument position at a `Floor::write_bytes(to.value, ..)` call — works, checks clean. * `const stdout: Stream = Stream(1::i64);` — refuses the moment a function body REFERENCES it. `ConstUnfoldable` at the reference, surfacing as E0760 on the enclosing `fn`. Module-local and cross-module, argument position and `let` binding, all four the same. It is neither a syntax problem nor a `const` problem, and three positive controls say so: a scalar `const two: i64 = 1::i64 + 1::i64;` folds; the same scalar const folds ACROSS modules; and `Stream(one)` — the newtype constructed FROM a scalar const — folds. Declaring the unfoldable const and never referencing it is clean too. Only the reference to a newtype-ASCRIBED const refuses. The seam is `ck_driver/src/compile/const_fold.rs` · `DeclaredShape`, and it is by construction rather than by accident: the fold admits a CLOSED scalar frame matched on the ascription's SPELLING (`declared_shape`), and its own doc says "anything else keeps the const out of the table". `Stream` IS `i64` by the newtype transparency law — the fold simply never asks. So the const never reaches `ConstFacts` and `ck_lower/.../const_facts.rs:81` raises at the reference. WHAT WAS NOT DONE, deliberately. No workaround: the available dodge is spelling the three constants as zero-argument `fn`s, which puts a call where the design says a compile-time value goes — the same class of defect this file exists to report. No capability cell: it would be `STATUS: BROKEN` and `BROKEN_CELL_COUNT` is an owner call. No ratchet moved, in either direction. The `Order of work` table now carries a state column and says what this costs the report's own framing: item 2's "no compiler change expected" was written without checking whether a newtype-ascribed const folds. Two of the three items turning out to be language gaps rather than surface fixes is itself the finding. Gates: 4 legs green (1354 unit, 107 corpus apps, 446 cells, 37 book expectations), integration sweep 251 passed / 1 known-red (the standing `lib.rs` R3) / 1 ignored, fmt and clippy clean.