diff --git a/.github/workflows/ecosystem.yml b/.github/workflows/ecosystem.yml index b105b78..e537d72 100644 --- a/.github/workflows/ecosystem.yml +++ b/.github/workflows/ecosystem.yml @@ -2,7 +2,7 @@ # # Criterion 1 of `RELEASE-CRITERIA-1.0.md` names three soak subjects, and this # is the second: "one full run per day for every day of the window, all exiting -# 0, on all four release triples", owned by Ash (WLA-7). What a run *is* lives +# 0, on all four release triples". What a run *is* lives # in `scripts/ecosystem-check.sh` -- sharpie built and tested, Foundry's own # check, and every published release installed, compiled against and tested, # plainly and under stress -- so that it means the same on a laptop as here. diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index d1e43ae..c5d389b 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -248,7 +248,7 @@ jobs: conformance: name: conformance (${{ matrix.triple }}) runs-on: ${{ matrix.os }} - # Minutes, not hours: `wsharp check` over the 64 rejection cases twice is + # Minutes, not hours: `wsharp check` over the 68 rejection cases twice is # seconds. The budget is the build. timeout-minutes: 60 strategy: diff --git a/.github/workflows/release-gates.yml b/.github/workflows/release-gates.yml index 73e1c55..86d3680 100644 --- a/.github/workflows/release-gates.yml +++ b/.github/workflows/release-gates.yml @@ -256,11 +256,12 @@ jobs: # Gate 6d. Every command the repository tells a contributor to run works. # - # The three commands are `CLAUDE.md`'s, verbatim, in its order -- the build - # before the test because the case suite's AOT pass links against an archive - # `cargo test` does not build. The third is issue #8 and fails today; this job - # is what turns that from an afternoon lost per contributor into a red check - # with an owner. + # The first two are `CLAUDE.md`'s, verbatim, in its order -- the build before + # the test because the case suite's AOT pass links against an archive `cargo + # test` does not build. The third, a filtered build, is the regression guard + # for issue #8 (fixed): a test-name filter used to pull `wsharp-start`, which + # defines `main`, into a test harness and fail to link. This job keeps that + # from coming back as an afternoon lost per contributor. # # No `nix-shell` wrapper: `CLAUDE.md` uses it because a bare NixOS box has no # `cc`, and the GitHub runner has one already. The commands inside are what diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index ee10cea..1b3721a 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -1,7 +1,7 @@ # What W# 1.0 promises -**Status: in force for the v1.0 campaign.** The terms — which surfaces are -frozen, and for how long — are fixed in +**Applies from v1.0.0.** The terms — which surfaces are frozen, and for how long +— are fixed in [Part five of RELEASE-CRITERIA-1.0.md](RELEASE-CRITERIA-1.0.md#part-five-the-api-and-stability-freeze). This file states the same promise to users, surface by surface. If the two ever disagree, Part five is the one that is right and this file gets fixed. @@ -261,11 +261,12 @@ extracting anything.** **Not promised:** that the digest is *signed*. A sidecar protects against a corrupted or truncated transfer and not against whoever can serve the tarball, -because they can serve the sidecar too. `RELEASE-CRITERIA-1.0.md` criterion 5 -puts the signing question to Johnny before the tag; **whichever way it is -decided, this section states the answer plainly rather than leaving it to be -inferred.** Until it is decided, the honest reading of this paragraph is the -weaker one. +because they can serve the sidecar too. **1.0 ships with the digest sidecar, and +the integrity it promises is against transport corruption, not against a +compromised distribution point** (decided 2026-09-26; criterion 7 of +`RELEASE-CRITERIA-1.0.md`). Signing — minisign or cosign, with the public key in +the installer and the signature verified before the digest — is scheduled for +1.1. **Not promised.** Reproducible builds. A third party cannot rebuild an artefact and get the same bytes; see `LIMITATIONS.md`. @@ -298,12 +299,9 @@ repository with one workflow (`deploy.yml`) that publishes and does not check. Gate 6a of `RELEASE-CRITERIA-1.0.md` — every documented example is a real file executed in CI — therefore does not reach it as things stand. -**This is fixable rather than a reason to narrow the promise**, and the proposal -is a check workflow in that repository which clones `sinisterMage/WSharp` at the -tag and runs `scripts/check-doc-examples.sh` over `content/docs/**`, using the -same `` marker convention. That makes the site's -examples the same files the case suite runs. - -Until that job exists and is green, **the 1.0 documentation promise covers the -in-repository documentation**, and the release notes say so. This sentence is -the one to delete once the job lands. +**The 1.0 documentation promise covers the in-repository documentation**, and +the release notes say so. Extending it to the site needs a check workflow in that +repository which clones `sinisterMage/WSharp` at the tag and runs +`scripts/check-doc-examples.sh` over `content/docs/**`, using the same +`` marker convention, so that the site's examples +are the same files the case suite runs. diff --git a/LIMITATIONS.md b/LIMITATIONS.md index 541d7b5..8613e82 100644 --- a/LIMITATIONS.md +++ b/LIMITATIONS.md @@ -3,12 +3,9 @@ **Status: live, against `wsharp 0.2.3`.** Every limitation below was verified against that release and the output shown is what it printed. -**The six tracked issues are dispositioned, and all six are documented -limitations.** Criterion 4 of [RELEASE-CRITERIA-1.0.md](RELEASE-CRITERIA-1.0.md) -is the gate; each entry now carries a **Disposition** line stating the call and -the reason for it, so the decision is readable here rather than only in an issue -thread. Not one of them was closed on a comment: each has the entry below and, -where one can exist, a named `tests/cases` guard. +The six issues tracked by criterion 4 of +[RELEASE-CRITERIA-1.0.md](RELEASE-CRITERIA-1.0.md) are all documented +limitations; each entry's **Disposition** line says why. A guard for a limitation is an uncomfortable-looking test — it asserts that something does *not* work. That is the point. A limitation nothing checks can @@ -36,15 +33,12 @@ program at two in the morning. | [#14](https://github.com/sinisterMage/WSharp/issues/14) | `net.shutdown` does not stop an acceptor on the BSDs | Documented — kernel behaviour, and the API does not offer the operation | `tests/cases/err_shutdown_listener.ws`, `tests/cases/net_poller.ws` | | [#15](https://github.com/sinisterMage/WSharp/issues/15) | `std/tls` cannot verify a chain through a P-521 key | Documented — 521 bits is not a whole number of 32-bit limbs | `tests/cases/x509_p521.ws` | -Each row's reasoning is in its entry below, under **Disposition**. Four of the -cases named — `err_computed_const.ws`, `const_array_alias.ws`, -`err_shutdown_listener.ws` and `x509_p521.ws` — were written for this file and did -not exist before: a limitation nobody checked was a limitation that could drift. +Each row's reasoning is in its entry below, under **Disposition**. ## How to read an entry -Every entry carries four things, and an entry missing any of them is not -finished: +Every entry carries the fields below — **Disposition** only where an issue asked +for fix-or-document — and an entry missing one it needs is not finished: | Field | Means | |---|---| @@ -356,9 +350,9 @@ as uncomparable with a diagnostic naming the field is still here.** Until the bound landed, the program above died on a signal with no W# diagnostic and no exit status a program could act on. By clause 3 of the P1 definition in -`RELEASE-CRITERIA-1.0.md` that is a P1 whatever the documentation says — Johnny -ruled on 2026-09-26 that clause 3 carries no exemption for documented behaviour, -because documentation changes who is surprised, not what the process does. Form +`RELEASE-CRITERIA-1.0.md` that is a P1 whatever the documentation says: clause 3 +carries no exemption for documented behaviour (decided 2026-09-26), because +documentation changes who is surprised, not what the process does. Form C says the same thing from the other side: a limitation whose behaviour is a crash is not documentable, so it gets bounded into a diagnostic first and the bound is what gets documented. This entry is that bound. @@ -466,10 +460,9 @@ is a language decision, not one to slip in under a freeze. `try` does anywhere: both `First` and `Last` then reach the caller. Or write the error set down, `!{First, Last}i64`. -**Disposition: documented limitation for 1.0, pending the language owner's -confirmation on #62.** The rule is also stated in `CLAUDE.md` ("`return f(x)` -unifies two error sets; `try f(x); return;` widens one"), which is where -`std/tls`'s dispatchers learnt to be written the second way. +**Disposition: documented limitation for 1.0.** Whether to change the rule is an +open language decision on #62. `std/tls`'s message dispatchers use `try` for +exactly this reason. **Guarded by** `tests/cases/err_tail_call_error_set.ws`, the refusal, and `tests/cases/tail_call_error_set_try.ws`, the workaround reaching both errors. @@ -713,9 +706,9 @@ backend this project actually tests, not a rewrite of the walk. **Disposition: documented limitation**, and of the six it is the one that is a *scope statement* rather than an unfinished piece. A supported platform is not a `cfg` arm: it is a register read, a Cranelift backend this project tests, and a CI -job on real hardware that a release is held to. Adding one during the 1.0 campaign -would mean shipping a platform whose collector nobody had watched fail, which is -the opposite of what the campaign is for. +job on real hardware that a release is held to. Adding one for 1.0 would mean +shipping a platform whose collector nobody had watched fail, which is the +opposite of what 1.0 is for. **Guarded by** two mechanisms, neither of which is a `tests/cases` entry — a case cannot be written for an architecture that does not build. The @@ -749,8 +742,7 @@ The reason it is worth an entry rather than a line in the README is **who meets it**. `cc` on a Windows machine is whatever is first on `PATH`, and a developer machine with Git for Windows, MSYS2 or a MinGW toolchain installed has one there without having chosen it. GitHub's `windows-latest` image is exactly that -machine, which is how this was found: criterion 7's Windows row failed on it -while the same release built fine under clang. +machine. **Why.** The Windows flags are not decoration. `-Xlinker -subsystem:console` is there because clang picks a subsystem by looking for `main` in the *objects* it @@ -762,10 +754,10 @@ the static CRT is not a near miss. Both are written out at `crates/wsharp-cli/src/link.rs:91`. Supporting GNU `ld` as well means a second set of flags, selected by detecting -the linker flavour, and a second Windows link configuration to keep green — on -a platform where, per the note in `CLAUDE.md`, almost nothing can be checked -from the machines this project is developed on. The released artefact is -`x86_64-pc-windows-msvc`, and MSVC is the toolchain that target names. +the linker flavour, and a second Windows link configuration to keep green. +Windows is covered by CI, so that would be a second Windows CI configuration as +well. The released artefact is `x86_64-pc-windows-msvc`, and MSVC is the +toolchain that target names. **Workaround.** Use clang, which drives the MSVC linker and libraries: install the Visual Studio Build Tools for those, `winget install LLVM.LLVM` for clang, @@ -789,8 +781,8 @@ answer on Windows and says that `cc` is MinGW, what to install, and how to set **Guarded by** `link::tests::a_mingw_link_failure_is_told_apart_from_an_msvc_one`, which holds the recognition to #41's own linker output and to the MSVC linker's, -on every platform. Criterion 7's Windows row no longer meets MinGW at all: it -sets `CC=clang`, the documented toolchain. There is deliberately no `tests/cases` +on every platform. The Windows clean-machine install check sets `CC=clang`, the +documented toolchain. There is deliberately no `tests/cases` entry: a case cannot assert which C compiler is first on `PATH` without becoming a case about the machine it ran on. @@ -805,13 +797,14 @@ bytes. What is published is a digest per artefact, and the promise is that the digest matches the bytes served. **Why.** A Rust release build is not bit-identical across machines without work -the 1.0 campaign has not scoped. +1.0 has not scoped. **Workaround.** Verify the published digest, which both installers do before extracting. -**Tracked.** Stated as a non-promise in `RELEASE-CRITERIA-1.0.md`, "What this -document does not promise". +**Tracked.** Stated as a non-promise in +[COMPATIBILITY.md](COMPATIBILITY.md#release-artefacts-and-the-feed), "Release +artefacts and the feed". ## The runtime's C symbols are not a stable ABI @@ -819,9 +812,10 @@ document does not promise". an internal boundary between the compiler and its own runtime. Linking against them from outside is not supported and they may change in a patch release. -**Tracked.** Stated as a non-promise in `RELEASE-CRITERIA-1.0.md`; repeated here -because the symbols are visible in a built binary and therefore look like an -interface. +**Tracked.** Stated as a non-promise in +[COMPATIBILITY.md](COMPATIBILITY.md#what-is-outside-the-promise-entirely), "What +is outside the promise entirely"; repeated here because the symbols are visible +in a built binary and therefore look like an interface. --- diff --git a/README.md b/README.md index 1ab66bd..f33e474 100644 --- a/README.md +++ b/README.md @@ -978,16 +978,12 @@ Numbered by the original feature list: Since that list ran out, two things. -**Raython**, an MVC framework, which is a design rather than a program so far -and whose repository is private. Its author settled every "can W# do this?" by -running the compiler rather than from memory, and left 28 probes behind as the -record. The design ends with nine things it wanted from the language and one -thing that was a bug: a conversion inside an abstract-typed body pinned that -parameter to the converted type, and with a second overload beside it that got -past inference and produced IR Cranelift refused. All ten are closed, which is -where `--emit=api`, `std/map`, a linear `str.join`, `net.shutdown`, `os.exit`, -`fs.modified_at`, struct `==` and `main` returning ending the process came from. -Item 14 of [ROADMAP.md](ROADMAP.md) is the write-up. +**Raython**, an MVC framework designed against W#, exercised the language with 28 +probe programs; the nine things it asked of the language and the one bug it found +are all closed, which is where `--emit=api`, `std/map`, a linear `str.join`, +`net.shutdown`, `os.exit`, `fs.modified_at`, struct `==` and `main` returning +ending the process came from. Item 14 of [ROADMAP.md](ROADMAP.md) is the +write-up. And [**sharpie**](https://github.com/sinisterMage/sharpie), a version manager, the second real program written in W#. It finds releases by reading this @@ -1015,7 +1011,7 @@ Four documents govern it, and each has one job: | File | Answers | |---|---| -| [RELEASE-CRITERIA-1.0.md](RELEASE-CRITERIA-1.0.md) | What 1.0 has to be true of, how each thing is measured, who owns it, and what proof closes it | +| [RELEASE-CRITERIA-1.0.md](RELEASE-CRITERIA-1.0.md) | What 1.0 has to be true of, how each thing is measured, and what proof closes it | | [COMPATIBILITY.md](COMPATIBILITY.md) | What 1.0 promises not to break, surface by surface, and for how long | | [LIMITATIONS.md](LIMITATIONS.md) | What W# does not do, with a workaround or the word *None* | | [docs/defects.md](docs/defects.md) | Where a defect lands, what severity it gets, and what happens to it after that | diff --git a/RELEASE-CRITERIA-1.0.md b/RELEASE-CRITERIA-1.0.md index d381930..4e99d1d 100644 --- a/RELEASE-CRITERIA-1.0.md +++ b/RELEASE-CRITERIA-1.0.md @@ -1,33 +1,32 @@ # What v1.0 means -**Status: in force for the v1.0 campaign, revision 2, 2026-09-26.** Published by -Johnny as the single release-blocking gate list. Every other v1.0 task is -measured against this file; where a task and this file disagree, this file is -either right or gets amended here, never worked around locally. Amendments are -recorded in "Revision history" at the end. +**The release gates for W# 1.0; amendments are listed at the end**, in +[Revision history](#revision-history). Where another document and this one +disagree about what 1.0 requires, this one is either right or gets amended here, +never worked around locally. W# is at **0.2.3**. Every numbered item in [ROADMAP.md](ROADMAP.md) is marked done, so the gap between here and a credible 1.0 is not features. It is *sustained real use*, the defects that only sustained real use finds, and evidence that anyone can re-run. This document is therefore a stability and evidence bar rather than a feature checklist: it says what has to be true, how -each thing is measured, who owns it, and what artefact closes it. +each thing is measured, and what artefact closes it. A criterion that needs a judgement call is not a criterion. Each of the eight below is written so that a script, or a person reading a script's output, can answer yes or no without arguing. Where a criterion still contains a judgement, -that judgement is named and given an owner rather than left implicit. +that judgement is named and recorded as a dated decision rather than left +implicit. ## How to read a criterion -Each one carries four fields: +Each one carries these fields: | Field | Means | |---|---| | **Gate** | The yes/no question. Where a command answers it, the command is given. | -| **Owner** | One name and one task. The person who makes the gate answer yes. | | **Evidence** | The artefact that closes it — a run, a file, a table. Not an assertion. | -| **Depends on** | Work owned by somebody else that the gate cannot pass without. | +| **Depends on** | Work elsewhere that the gate cannot pass without, where there is any. | "Verified" in this document means a script ran and its output was recorded. A thing checked by hand once is a thing that regresses silently, so a manual check @@ -39,7 +38,7 @@ and every criterion points there. | If you want | Read | |---|---| -| Your own task's gate | [Part two](#part-two-the-eight-criteria), then [the issue map](#part-three-the-nine-open-issues) | +| A criterion's gate | [Part two](#part-two-the-eight-criteria), then [the issue map](#part-three-the-issue-map) | | What you must produce to close it | [Part four: the proof standard](#part-four-the-proof-standard) | | Whether a change is allowed during the freeze | [Part one](#part-one-definitions) | | What 1.0 promises users | [Part five](#part-five-the-api-and-stability-freeze), then [COMPATIBILITY.md](COMPATIBILITY.md) | @@ -139,13 +138,13 @@ Any one of the following: 8. **A credential in a workflow file, a commit, or a published artefact.** **Clause 3 carries no exemption for documented behaviour.** This was asked -directly on [#13](https://github.com/sinisterMage/WSharp/issues/13) and the -answer is no, ruled by Johnny on 2026-09-26. A crash is a crash whether or not a +directly on [#13](https://github.com/sinisterMage/WSharp/issues/13). +**Decision (2026-09-26): no.** A crash is a crash whether or not a file says it will happen; documentation changes who is surprised, not what the process does. A limitation may be *refused with a diagnostic* and documented — that is a limitation. A limitation that aborts the process is a P1 and gets bounded. The consequence for #13 is recorded in -[Part three](#part-three-the-nine-open-issues). +[Part three](#part-three-the-issue-map). ### P2 — does not block the tag on its own; must be fixed or documented @@ -172,28 +171,23 @@ line in a CI job. ### Who decides -The filer proposes a severity; the triage owner for the surface confirms or -changes it within one working day: +The filer proposes a severity; a maintainer confirms or changes it at triage, +within one working day. The `area:` labels in [docs/defects.md](docs/defects.md) +name the surface a report belongs to. -| Surface | Triage owner | -|---|---| -| Compiler, runtime, stdlib, the collector | Mira (WLA-3, WLA-4) | -| Harness, conformance, fuzzing, differential testing | Dex (WLA-6) | -| Benchmarks, collector measurement, published numbers | Ridge (WLA-5) | -| Release pipeline, install, artefacts, digests, sharpie, `.wsharp` ecosystem | Ash (WLA-7) | -| Raython and its sample application | Wren (WLA-8) | - -Disagreement about a severity goes to Johnny and is settled within one working -day. **An unconfirmed report counts at the severity the filer proposed** until it -is triaged, so "no open P1" cannot be satisfied by leaving reports untriaged. +Disagreement about a severity goes to the repository owner and is settled within +one working day. **An unconfirmed report counts at the severity the filer +proposed** until it is triaged, so "no open P1" cannot be satisfied by leaving +reports untriaged. ## The freeze clock The freeze is a **window of 28 consecutive days** ending at the tag, during which no breaking change lands and no P1 is open. -- **Day 0** is the commit on `main` declared as the freeze start. It is recorded - in this file, under "Freeze record", by the person who declares it (Johnny). +- **Day 0** is the commit on `main` declared as the freeze start. The repository + owner declares it, and records it in this file under "Freeze record" and in + `.github/freeze-start`, which `scripts/freeze-check.sh` reads. - The clock **resets to day 0** on either of: a merge to `main` labelled `change:breaking`, or the confirmation of a P1 in the compiler, runtime, stdlib, release pipeline, install scripts or sharpie. On a P1 the clock @@ -203,10 +197,10 @@ no breaking change lands and no P1 is open. addition, a CI or workflow change, or a P1 confined to a driver program that is not itself shipped as an artefact. A driver-program P1 resets *that program's* soak counter under criterion 1 instead. -- Only Johnny may grant an exception, only in writing on the campaign thread, and - every exception is recorded in the "Exception log" below with what changed, why - it could not wait, and what the clock does. Granting an exception means - deciding the clock question explicitly; silence is a reset. +- Only the repository owner may grant an exception, only in writing on a GitHub + issue, and every exception is recorded in the "Exception log" below with what + changed, why it could not wait, and what the clock does. Granting an exception + means deciding the clock question explicitly; silence is a reset. The consequence worth saying out loud before anyone is surprised by it: **a P1 confirmed on day 27 costs four weeks.** That is the intended cost. If it is the @@ -222,25 +216,17 @@ buys nothing: any of those landing during the window would reset it. # Part two: the eight criteria -The draft of this document had seven. Criterion 8 is new, because -[#18](https://github.com/sinisterMage/WSharp/issues/18) established that the -numbers three of the other criteria imply cannot currently be obtained at all, -and an unobtainable number is a gate nobody can pass. Criterion 0 is also new, -and exists because the documents this file cites were not in the tree when it was -written. - ## 0. The governing documents are in the tree -Four documents govern the campaign. Until they are on `main`, every criterion -that cites one is citing a file that does not exist — which is the state this -revision was written to end. +Four documents govern the release, and every criterion that cites one needs it +to exist on `main`. -| File | What it is | Recovered from | -|---|---|---| -| `RELEASE-CRITERIA-1.0.md` | This file: the bar | PR #7 (`ce6b3ff`), revised | -| `LIMITATIONS.md` | What W# does not do, per entry | PR #16 (`1119247`) | -| `COMPATIBILITY.md` | What 1.0 promises not to break | PR #16 (`1119247`) | -| `docs/defects.md` | Intake, triage, verification | PR #6 (`80efa89`) | +| File | What it is | +|---|---| +| `RELEASE-CRITERIA-1.0.md` | This file: the bar | +| `LIMITATIONS.md` | What W# does not do, per entry | +| `COMPATIBILITY.md` | What 1.0 promises not to break | +| `docs/defects.md` | Intake, triage, verification | **Gate.** All four files exist on `main`, and no open issue cites a path that does not resolve. Checkable in one line: @@ -251,19 +237,8 @@ for f in RELEASE-CRITERIA-1.0.md LIMITATIONS.md COMPATIBILITY.md docs/defects.md done ``` -**Owner.** Johnny. - -**Evidence.** The merge commit, and a comment on each of the nine open issues -confirming the path it cites now resolves. - -**Why this was needed.** The drafts were written on four branches (PRs #6, #7, -#16, #17); all four were closed unmerged on 2026-09-26 and their branches -deleted, so nine open issues cite clause numbers in files that are not on `main`. -The content survives at the commits above and is restored here rather than -rewritten, because the issues cite it by clause and a rewrite would silently -renumber what they point at. `tests/harness/` (PR #17, `39e5a76`) is restored -separately under criterion 5, because it is Dex's and Ridge's to run rather than -mine to land. +**Evidence.** The merge commit, and a comment on each open issue that cites one +of these files confirming the path it cites now resolves. ## 1. Real programs, soaked @@ -271,30 +246,23 @@ Programs written in W#, each doing something this project actually wants done, each exercised continuously for the freeze window. "Continuously" cannot mean the same thing for all of them, so each gets the form of soak that fits it. -The draft named four subjects, two of which (a docs site generator, a CI -log/metrics pipeline) no task owns. An unowned soak subject is not a gate, so -this revision names **three**, each against the task that owns it: +There are **three** subjects: -| Subject | What it is | Soak gate | Owner | -|---|---|---|---| -| Raython sample application | A long-running HTTP server | Up for the whole window with no unplanned restart, no crash, no OOM kill. Resident set at the end within 2x of the resident set at hour 24. Zero 5xx responses attributable to the runtime or the collector. | Wren, WLA-8 | -| The `.wsharp` ecosystem check | A batch program run daily | One full run per day for every day of the window, all exiting 0, on all four release triples. | Ash, WLA-7 | -| The compiler under its own harness | A nightly batch | `tests/harness/nightly.sh` once a day for every day of the window: parity across the three execution modes, the fuzz corpus, and the soak driver, zero failures. | Dex, WLA-6 | +| Subject | What it is | Soak gate | +|---|---|---| +| Raython sample application | A long-running HTTP server | Up for the whole window with no unplanned restart, no crash, no OOM kill. Resident set at the end within 2x of the resident set at hour 24. Zero 5xx responses attributable to the runtime or the collector. | +| The `.wsharp` ecosystem check | A batch program run daily | One full run per day for every day of the window, all exiting 0, on all four release triples. | +| The compiler under its own harness | A nightly batch | `tests/harness/nightly.sh` once a day for every day of the window: parity across the three execution modes, the fuzz corpus, and the soak driver, zero failures. | **Gate.** `scripts/soak-report.sh` prints one row per subject per day and exits non-zero if any day is missing or any row failed. Each subject appends its own daily TSV row; a missing row is a failure, because a soak that stopped reporting is a soak that stopped. -**Owner.** Dex owns `soak-report.sh` and the collection; Wren and Ash own their -own rows. - **Evidence.** The soak log, and one `scripts/soak-report.sh` run showing 28 complete days for all three subjects. -**Depends on.** The Raython sample application does not exist yet (WLA-8). This -is the longest pole in the campaign and the criterion is honest about it rather -than discovering it in week three. +**Depends on.** The Raython sample application being deployed. ## 2. A stability freeze @@ -308,15 +276,16 @@ mechanically: Its output is the evidence. Every term in it is defined in Part one. -**Owner.** Ash runs the script and owns the `change:*` label CI job; Johnny -declares day 0 and grants any exception. +The `change:*` label rule is enforced by the `change-label` job in +`.github/workflows/release-gates.yml`. The repository owner declares day 0 and +grants any exception; see [the freeze clock](#the-freeze-clock). **Evidence.** A `freeze-check.sh` run dated within 24 hours of the tag, exiting 0, pasted into the release checklist. -**Depends on.** The `change:*` label rule being enforced by CI from day 0, and -#13 being fixed (it is an open P1 under clause 3, so question 3 cannot pass while -it is open). +**Depends on.** The `change:*` label rule being enforced by CI from day 0, and no +P1 being open. #13, a P1 under clause 3 when this criterion was written, has +since been fixed. ## 3. Every defect gets a regression guard @@ -338,9 +307,6 @@ closed it touches at least one path under that surface's guard directory. A fix with no guard is listed by name. What the guard itself must show is [Form A of the proof standard](#form-a--a-fix). -**Owner.** Mira for compiler, runtime and stdlib defects; Ash for release, -install and sharpie defects; Dex for anything his harness found. - **Evidence.** A `guard-check.sh` run listing zero unguarded fixes. ## 4. Every remaining limitation is resolved or documented @@ -355,8 +321,7 @@ stating what does not work, why it is not fixed for 1.0, and what would fix it. number, and exits non-zero on one that is neither. An issue left open is a failure; so is a closed one with no `LIMITATIONS.md` section. -**Owner.** Mira (WLA-4) decides fix-or-document and writes the prose; Johnny -signs off on what the promise in `COMPATIBILITY.md` then says. +What `COMPATIBILITY.md` then promises is signed off by the repository owner. **Evidence.** `followups-check.sh` exiting 0, plus `LIMITATIONS.md` on `main` with a section per documented limitation, each carrying @@ -368,9 +333,8 @@ the collector must root; row polymorphism or an equivalent), one is an architecture list, one needs a read-only reference the type system has no way to express, and two are a BSD kernel behaviour and a curve whose 521 bits are not a whole number of 32-bit limbs. That is a legitimate way to close this criterion; -it is written here so that it is a decision rather than a discovery. Mira may -convert any of the six to a fix by landing one under Form A; she may not leave -one undecided. +it is written here so that it is a decision rather than a discovery. Any of the +six may still become a fix under Form A; none may be left undecided. ## 5. Conformance, parity and adversarial input, in CI @@ -393,9 +357,6 @@ four release triples. Specifically: 3. **Flake rate.** `tests/harness/flake-rate.sh` reports zero non-deterministic cases over its repeat count. A flaky gate cannot gate. -**Owner.** Dex (WLA-6). `tests/harness/` was written on PR #17 and closed -unmerged at `39e5a76`; restoring it is the first step of WLA-6 and not a rewrite. - **Evidence.** A green scheduled CI run, plus one `parity.sh` table per release triple. @@ -440,20 +401,19 @@ nix-shell --run "cargo test --workspace" nix-shell --run "cargo test --workspace arithmetic --no-run" ``` -The third is [#8](https://github.com/sinisterMage/WSharp/issues/8) and fails -today. A documented command that does not work is a P2 under the scale, and the -reason it gets a gate of its own rather than a line in the limitations list is -that `CLAUDE.md` tells every contributor to run a filtered test and the failure -reads as "my change broke the build". - -**Owner.** Johnny owns `COMPATIBILITY.md` and the prose (6b); Ash owns the CI -jobs for 6a and 6d; Mira owns the fix behind 6d and the pin at 6c. +The third is the regression guard for +[#8](https://github.com/sinisterMage/WSharp/issues/8), which is fixed: a +test-name filter used to pull `crates/wsharp-start`, which defines `main`, into a +test harness, and the link failed. A documented command that does not work is a +P2 under the scale, and the reason it gets a gate of its own rather than a line +in the limitations list is that a contributor running a filtered test reads that +failure as "my change broke the build". **Evidence.** A `check-doc-examples.sh` run exiting 0; `COMPATIBILITY.md` on `main` with every surface covered; a green `api.rs`; a green 6d job. **Depends on.** The documentation at wsharp.io is outside this repository. -**Decision, Johnny, 2026-09-26: gate 6a covers the in-repo documentation only.** +**Decision (2026-09-26): gate 6a covers the in-repo documentation only.** wsharp.io is checked by whatever gates its own repository has, and the release notes say which documentation the compatibility promise covers. Extending 6a across repositories is post-1.0 work; pretending it is covered would be worse @@ -491,8 +451,6 @@ previous toolchain working. Each rung also asserts that `sharpie show` names the rung that answered, since that is the first question when the answer surprises somebody. -**Owner.** Ash (WLA-7). - **Evidence.** A four-row table — one per release triple — each row carrying the OS image, the version installed, the digest observed, the digest published, and the exit status; plus four `rungs.sh` runs. A platform claimed by inference from @@ -503,7 +461,7 @@ another platform's run is not a row. - **Signing.** Today the release publishes a per-target `.sha256` sidecar and both installers check what they downloaded against it. That protects against a corrupted or truncated transfer and not against whoever can serve the tarball, - because they can serve the sidecar too. **Decision, Johnny, 2026-09-26: 1.0 + because they can serve the sidecar too. **Decision (2026-09-26): 1.0 ships with the digest sidecar and the release notes state plainly that 1.0 promises integrity against transport corruption and not against a compromised distribution point.** Signing (minisign or cosign, public key in the installer, @@ -540,8 +498,6 @@ are gone by the time anything can read them. of more than 2x on any case in `tests/cases`" is a comparison rather than an impression, and a re-run command that reproduces it. -**Owner.** Ridge (WLA-5). - **Evidence.** The three artefacts above, each under Form B, with raw data committed rather than summarised. @@ -553,55 +509,33 @@ Form B. --- -# Part three: the nine open issues - -Every open issue in `sinisterMage/WSharp` as of 2026-09-26, each mapped to -exactly one gate, one disposition and one owner. An issue appears once. A tenth -issue filed tomorrow gets a row here before it gets work. - -| Issue | What it is | Gate | Disposition | Owner / task | -|---|---|---|---|---| -| [#13](https://github.com/sinisterMage/WSharp/issues/13) | Comparing a cyclic struct value aborts with no W# diagnostic (P1) | 2 | **Fix** | Mira, WLA-3 | -| [#8](https://github.com/sinisterMage/WSharp/issues/8) | `cargo test --workspace ` fails: two mains in `wsharp-start` (P3) | 6d | **Fix** | Mira, WLA-3 | -| [#18](https://github.com/sinisterMage/WSharp/issues/18) | No per-pause data, so a pause p99 cannot be measured | 8 | **Measurement** | Ridge, WLA-5 | -| [#9](https://github.com/sinisterMage/WSharp/issues/9) | Computed top-level `const` is rejected | 4 | **Documented limitation** | Mira, WLA-4 | -| [#10](https://github.com/sinisterMage/WSharp/issues/10) | Field access needs a known type | 4 | **Documented limitation** | Mira, WLA-4 | -| [#11](https://github.com/sinisterMage/WSharp/issues/11) | x86-64 and aarch64 only | 4 | **Documented limitation** | Mira, WLA-4 | -| [#12](https://github.com/sinisterMage/WSharp/issues/12) | A top-level `const` array can be written through an alias | 4 | **Documented limitation** | Mira, WLA-4 | -| [#14](https://github.com/sinisterMage/WSharp/issues/14) | `net.shutdown` does not stop an acceptor on the BSDs | 4 | **Documented limitation** | Mira, WLA-4 | -| [#15](https://github.com/sinisterMage/WSharp/issues/15) | `std/tls` cannot verify a chain through a P-521 key | 4 | **Documented limitation** | Mira, WLA-4 | - -## #13 in detail, because it needed a ruling - -#13 asked whether the severity scale should carry an exemption for a documented -crash. **It should not, and does not** — see the note under clause 3. The -disposition is therefore a fix, and it is the shape #13 itself proposed as option -1: bound the recursion in the generated exact-comparison function and panic with -a W# diagnostic naming the type, in the shape of `std/json`'s `MAX_DEPTH` and -`std/x509`'s `MAX_CHAIN` — a named bound with no knob. That converts an abort into -a reported error, which is a documentable limitation; an abort is not. - -Two things for whoever picks this up. A version of this fix already exists, -written on PR #6 and closed unmerged at `80efa89`: it touches -`crates/wsharp-codegen/src/equality.rs` and `lower.rs`, adds -`tests/cases/eq_cycle_bounded.ws`, and changes `tests/cases/struct_eq.ws`. Read it -before writing a second one, and hold it to Form A anyway — recovered work is not -verified work. And `LIMITATIONS.md`'s entry "Comparing a value that reaches itself -aborts" is true of 0.2.3 and must be rewritten, not deleted, when the fix lands: -the bound is itself a limitation, and a user who meets it deserves to find it -documented. - -## What the map does not contain - -Nothing about closed issues #1–#7, #16 and #17. Four of those are pull requests -closed unmerged, and what happens to their content is criterion 0 (for the three -documents) and criterion 5 (for `tests/harness/`). +# Part three: the issue map + +Revision 2 mapped every issue then open in `sinisterMage/WSharp` — nine, on +2026-09-26 — to one gate and one disposition. **All nine are now closed.** +[#13](https://github.com/sinisterMage/WSharp/issues/13), the P1 under clause 3, +was fixed by bounding the comparison into a W# diagnostic, and the bound is +documented in `LIMITATIONS.md` as "Comparing a value that reaches itself is +refused". [#8](https://github.com/sinisterMage/WSharp/issues/8) was fixed and is +guarded by gate 6d. [#18](https://github.com/sinisterMage/WSharp/issues/18) was +closed by the per-pause data criterion 8 requires. +[#9](https://github.com/sinisterMage/WSharp/issues/9), +[#10](https://github.com/sinisterMage/WSharp/issues/10), +[#11](https://github.com/sinisterMage/WSharp/issues/11), +[#12](https://github.com/sinisterMage/WSharp/issues/12), +[#14](https://github.com/sinisterMage/WSharp/issues/14) and +[#15](https://github.com/sinisterMage/WSharp/issues/15) were closed as documented +limitations, each with its entry in `LIMITATIONS.md`. The issues themselves carry +the detail. + +A new issue gets a gate and a disposition before it gets work; +[docs/defects.md](docs/defects.md) is the intake. --- # Part four: the proof standard -The owner's constraint, in one place: **every bug fix and every benchmark carries +The rule, in one place: **every bug fix and every benchmark carries concrete, re-runnable proof.** Three forms, one per kind of claim. A claim in any other shape is not closed, whoever makes it and however confident they are. @@ -618,7 +552,7 @@ A fix is proved by a **committed test that fails before it and passes after it** 4. A fix whose test cannot fail before it is not proved. If the defect needs `--gc-stress`, a specific platform or a repeat count to reproduce, the test carries that condition and the PR says which pass exercises it. -5. A defect found on a platform this machine cannot run is proved on that +5. A defect found on a platform you cannot run locally is proved on that platform, in CI or on real hardware, and the run is linked. "It should work there" is not Form A. @@ -699,7 +633,7 @@ breaking change for the life of the 1.x line: ## For how long -**Decision, Johnny, 2026-09-26.** +**Decision (2026-09-26).** - **The 1.x line is source-compatible with 1.0.** A program that compiles and behaves correctly on 1.0 does so on every later 1.x, for every frozen surface @@ -744,15 +678,15 @@ it. Consequences worth stating before somebody assumes otherwise: - **`aarch64-unknown-linux-gnu` is not a 1.0 release target.** The architecture is supported by the collector — it is half of what #11 allows — but no triple - ships, because nothing in this campaign builds, tests or installs on it. - Adding it post-1.0 is additive. + ships, because nothing in the release pipeline builds, tests or installs on + it. Adding it post-1.0 is additive. - **The BSDs are not release targets.** `crates/wsharp-runtime/src/sys/bsd.rs` - exists and is checked but not run here, and + exists, but no CI job builds or runs it, and [#14](https://github.com/sinisterMage/WSharp/issues/14) is a BSD-specific limitation. A BSD build may work; 1.0 promises nothing about it. - **A platform's result is that platform's run.** Claimed-by-inference is not a row, in criterion 7 or anywhere else. The four `struct stat` offsets in - `sys/bsd.rs` that no machine here can check are exactly why this rule exists. + `sys/bsd.rs`, which no CI job checks, are exactly why this rule exists. - **32-bit is not supported and will not be.** A limb is 32 bits, a slot is a machine word, and the collector reads a frame pointer per architecture; this is an architecture list, not an oversight. @@ -761,35 +695,11 @@ it. Consequences worth stating before somebody assumes otherwise: # Part six: the shared parts -## Who owns what - -This document is a contract between six people, not one person's opinion. - -| Owner | Task | Owes | -|---|---|---| -| Mira | WLA-3, WLA-4 | The two open compiler defects with guards (#13, #8); fix-or-document for the six limitation issues and the `LIMITATIONS.md` prose; the `--emit=api` pin (6c); triage for compiler, runtime, stdlib, the collector | -| Ridge | WLA-5 | Per-pause data and the published distribution (#18); the benchmark baselines the 2x threshold is measured against; every number under Form B | -| Dex | WLA-6 | `tests/harness/` restored and in CI; parity across the three execution modes on four triples; fuzzing; the flake rate; `soak-report.sh` and the daily collection | -| Ash | WLA-7 | The release pipeline and published digests; clean-machine install verification; sharpie and its rung matrix; the `.wsharp` ecosystem check; the `change:*` label CI job; the 6a and 6d CI jobs | -| Wren | WLA-8 | Raython and the deployed sample application, and its soak row for the whole window | -| Johnny | WLA-2 | This document and `COMPATIBILITY.md`; declares day 0; grants or refuses exceptions; settles severity disagreements; **decides the release date** | - -A criterion whose owner is not on this list is a criterion nobody owns, which is -the state criterion 0 was written to end. - ## What this will cost in calendar time -Worth saying once, plainly, because it is the number a release date is built -from. Criterion 1 requires three subjects soaking for 28 days, and one of them — -the Raython sample application — is not written. So the earliest possible tag is: - -> (time for Wren to build and deploy Raython's sample app) + (time for Dex to -> stand up the harness and the daily report) + 28 days, **assuming no P1 and no -> breaking change in those 28 days**. - -Every confirmed P1 in the window adds up to 28 more days. The freeze window is -the one number in this document that is a policy choice rather than a -measurement, and it is the one to argue about if the date comes out wrong. +The earliest possible tag is 28 days after the Raython sample application is +deployed and the harness is reporting daily, **assuming no P1 and no breaking +change in those 28 days**. ## Freeze record @@ -798,7 +708,7 @@ measurement, and it is the one to argue about if the date comes out wrong. | Day 0 commit | *not declared* | | Day 0 date | *not declared* | | Window | 28 days | -| Declared by | *pending — Johnny, once gates 0, 2, 3, 4, 6d and 8 are closed* | +| Declared by | *pending — the repository owner, once gates 0, 2, 3, 4, 6d and 8 are closed* | ## Exception log @@ -807,71 +717,44 @@ it could not wait, who granted it, and whether the clock reset. ## The state of the checks -Nine gates, fifteen checks — several gates take two apiece, because a gate with -one mechanism and one artefact, or one script per repository, is two things that -can be separately missing. Fourteen of the fifteen are written; one is not, -and is named here so that it is owed rather than assumed. +One row per check a gate needs: fifteen checks for the nine criteria, because a +gate with both a mechanism and an artefact, or a script in each of two +repositories, has two things that can be separately missing. The State column +says whether each check exists; whether it *passes* is the gate's own question. **Read against `main` at `4e9249a`, and held to the tree by -`scripts/gate-table-check.sh` on every pull request.** This table claims which -files exist, which is a claim that goes stale on any merge that adds one — and -did, within twenty minutes of revision 2, on six of ten rows. So it carries the -commit it was read at, and a check now reads every row: a row saying "written" -whose file is missing, and a row saying "not written" whose file somebody has -since written, both turn CI red. Thirteen of the fifteen rows are checked that -way, and the script names the other two out loud on every run — criterion 0's -inline test, which names no path, and criterion 7's `tests/rungs.sh`, which -names one in another repository. (Criterion 8's baselines named no path until -`benchmarks/baselines/` was decided; the row now names the three still owed, so -the check will say so the day they land.) **An unchecked row is where this table can still be wrong**, and -`rungs.sh` is the proof: it was merged into sharpie twenty-one minutes before -this revision was first drafted saying it was not written, and no check in this -tree could have noticed. It was caught by reading sharpie, which is the only way -an unchecked row ever will be. - -**The State column is read by that script, so two spellings are load-bearing.** A -verdict is the bold run `**written**` or `**not written**` at the start of the -cell, and a CI job is claimed as ``as `a`, `b` and `c` in -`.github/workflows/file.yml` ``. Writing either differently does not make a row -false; it makes it unchecked, which is the failure this table has already had -once. +`scripts/gate-table-check.sh` on every pull request.** A row saying "written" +whose file is missing, or "not written" whose file exists, turns CI red. Two +rows name nothing this tree can check — criterion 0's inline test, and +criterion 7's `tests/rungs.sh`, which lives in another repository — and the +script reports them as unchecked on every run. + +**Two spellings in the State column are load-bearing**, because the script reads +them. A verdict is the bold run `**written**` or `**not written**` at the start +of the cell, and a CI job is claimed as ``as `a`, `b` and `c` in +`.github/workflows/file.yml` ``. A cell that does not begin with a verdict fails +the check; a job named any other way is simply not checked. | Check | Gate | State | Owed by | |---|---|---|---| -| the four-file test in criterion 0 | 0 | **written and run** — inline above | Johnny | -| `scripts/gate-table-check.sh`, with `scripts/tests/gate-table-check.test.sh` | 0 | **written**, as `gate-table` in `.github/workflows/release-gates.yml`. It reads this table, so the table describes itself and drifting from the tree is now a red CI run | Johnny | -| `scripts/soak-report.sh`, with `scripts/soak-report-selftest.sh` | 1 | **written** (#20), and wired up as `soak-row` in `.github/workflows/nightly.yml`. What is owed is 28 days of rows, not the script | Dex to run | -| `scripts/ecosystem-check.sh`, with `scripts/tests/ecosystem-check.test.sh` | 1 | **written**, as `ecosystem` and `soak-row` in `.github/workflows/ecosystem.yml`. One run is sharpie built and tested (plainly, under stress, and its rungs), Foundry's own check fetching every release against its tree hash, and every published release installed, checked, built, run and tested plainly and under stress. The workflow writes one row per release triple, and `soak/subjects.tsv` names all four, so `soak-report.sh` fails a day any one of them did not report. What is owed is 28 days of rows | Ash to run | -| `scripts/freeze-check.sh` | 2 | **written**, on `main` | Ash to run | -| `scripts/check-change-label.sh`, with `scripts/tests/check-change-label.test.sh` | 2 | **written** (#21), as `change-label` in `.github/workflows/release-gates.yml`. Revision 2 did not list this check at all | Ash, enforcing from now | -| `scripts/guard-check.sh`, with `scripts/tests/guard-check.test.sh` | 3 | **written**, as `guards` in `.github/workflows/release-gates.yml`. Asks GitHub which defects closed after the freeze start and what closed them, so, like criterion 4's, it exits 2 when it cannot ask. The live run waits for `.github/freeze-start`; before Day 0, `--since` runs it over any window | Ash | -| `scripts/followups-check.sh`, with `scripts/tests/followups-check.test.sh` | 4 | **written** (#45), as `followups` in `.github/workflows/release-gates.yml`. The only check in this table that asks GitHub rather than the tree, so it exits 2 — never 0 — when it cannot ask | Ash, against Mira's decisions | -| `tests/harness/nightly.sh` | 5 | **written** (#20), as `parity`, `nightly` and `soak-row` in `.github/workflows/nightly.yml`. What is owed is green runs on a schedule | Dex to run | -| `scripts/check-doc-examples.sh`, with `scripts/tests/check-doc-examples.test.sh` | 6a | **written** (#21), as `doc-examples` and `examples` in `.github/workflows/release-gates.yml`, and **green** since #33 marked the documented programs up | Ash — held | -| the 6d CI job | 6d | **written** (#21), as `contributor-commands` in `.github/workflows/release-gates.yml` | Ash — held | -| `scripts/verify-install.sh` | 7 | **written** (#21), as `verify-install` in `.github/workflows/release-gates.yml`, a matrix job. What is owed is a run against a real release | Ash to run | -| `tests/rungs.sh` | 7 | **written**, in [`sinisterMage/sharpie`](https://github.com/sinisterMage/sharpie) (its PR #2, merged at `6af434d`), not in this repository — so no check here can see its state either way, and this row is one of the two the gate-table check reports as unchecked. What is owed is green runs on four triples | Ash to run | -| `tests/harness/gc-pauses.sh` | 8 | **written** (#20), and called by `tests/harness/nightly.sh` | Ridge — held | -| a committed benchmark baseline per release triple | 8 | **not written** for three of the four: `benchmarks/baselines/x86_64-pc-windows-msvc.tsv`, `benchmarks/baselines/x86_64-apple-darwin.tsv` and `benchmarks/baselines/aarch64-apple-darwin.tsv` are owed. The x86-64 Linux baseline is committed beside where they go, written by the harness's bench.sh with `--record`, which keeps every sample and the Form B fields; `--compare` is the 2x check. The nightly's bench job records the other three on their runners as artifacts, to be reviewed and committed | Ridge | - -A criterion whose check is not written is a criterion nobody can fail, which is -why this table is here rather than in somebody's head. The one that is not -written is gate 8's baselines; everything else now fails on evidence rather than -on absence. - -Two rows used to be one each, and splitting them was the honest fix. Criterion 7 -as a single row said "not written" while `verify-install.sh` was finished, and -criterion 8 as a single row would have said "written" while the baselines were -not. One row per thing that can be separately missing is the rule, and the reason -the table has fifteen rows for nine gates. - -**Two rows name nothing this tree can be asked about, and the check says so on -every run rather than passing them silently.** Criterion 0's four-file test is -inline above, and criterion 7's `tests/rungs.sh` is another repository's, which -no check here can stat. "Thirteen of fifteen rows were held to the tree" is the -number each run prints, because a check that reports its own coverage cannot -quietly lose it — and the two it does not cover are exactly where this table -has to be read rather than trusted. +| the four-file test in criterion 0 | 0 | **written and run** — inline above | maintainers | +| `scripts/gate-table-check.sh`, with `scripts/tests/gate-table-check.test.sh` | 0 | **written**, as `gate-table` in `.github/workflows/release-gates.yml`. It reads this table, so the table describes itself and drifting from the tree is now a red CI run | CI, on every pull request | +| `scripts/soak-report.sh`, with `scripts/soak-report-selftest.sh` | 1 | **written** (#20), and wired up as `soak-row` in `.github/workflows/nightly.yml`. What is owed is 28 days of rows, not the script | `nightly.yml`, daily | +| `scripts/ecosystem-check.sh`, with `scripts/tests/ecosystem-check.test.sh` | 1 | **written**, as `ecosystem` and `soak-row` in `.github/workflows/ecosystem.yml`. One run is sharpie built and tested (plainly, under stress, and its rungs), Foundry's own check fetching every release against its tree hash, and every published release installed, checked, built, run and tested plainly and under stress. The workflow writes one row per release triple, and `soak/subjects.tsv` names all four, so `soak-report.sh` fails a day any one of them did not report. What is owed is 28 days of rows | `ecosystem.yml`, daily | +| `scripts/freeze-check.sh` | 2 | **written**, on `main` | maintainers, within 24 hours of the tag | +| `scripts/check-change-label.sh`, with `scripts/tests/check-change-label.test.sh` | 2 | **written** (#21), as `change-label` in `.github/workflows/release-gates.yml` | CI, on every pull request | +| `scripts/guard-check.sh`, with `scripts/tests/guard-check.test.sh` | 3 | **written**, as `guards` in `.github/workflows/release-gates.yml`. Asks GitHub which defects closed after the freeze start and what closed them, so, like criterion 4's, it exits 2 when it cannot ask. The live run waits for `.github/freeze-start`; before Day 0, `--since` runs it over any window | CI | +| `scripts/followups-check.sh`, with `scripts/tests/followups-check.test.sh` | 4 | **written** (#45), as `followups` in `.github/workflows/release-gates.yml`. The only check in this table that asks GitHub rather than the tree, so it exits 2 — never 0 — when it cannot ask | CI | +| `tests/harness/nightly.sh` | 5 | **written** (#20), as `parity`, `nightly` and `soak-row` in `.github/workflows/nightly.yml`. What is owed is green runs on a schedule | `nightly.yml`, on its schedule | +| `scripts/check-doc-examples.sh`, with `scripts/tests/check-doc-examples.test.sh` | 6a | **written** (#21), as `doc-examples` and `examples` in `.github/workflows/release-gates.yml`, and **green** since #33 marked the documented programs up | CI — held | +| the 6d CI job | 6d | **written** (#21), as `contributor-commands` in `.github/workflows/release-gates.yml` | CI — held | +| `scripts/verify-install.sh` | 7 | **written** (#21), as `verify-install` in `.github/workflows/release-gates.yml`, a matrix job. What is owed is a run against a real release | maintainers, against a real release | +| `tests/rungs.sh` | 7 | **written**, in [`sinisterMage/sharpie`](https://github.com/sinisterMage/sharpie) (its PR #2, merged at `6af434d`), not in this repository — so no check here can see its state either way, and this row is one of the two the gate-table check reports as unchecked. What is owed is green runs on four triples | sharpie's CI, on four triples | +| `tests/harness/gc-pauses.sh` | 8 | **written** (#20), and called by `tests/harness/nightly.sh` | `nightly.yml` — held | +| a committed benchmark baseline per release triple | 8 | **not written** for three of the four: `benchmarks/baselines/x86_64-pc-windows-msvc.tsv`, `benchmarks/baselines/x86_64-apple-darwin.tsv` and `benchmarks/baselines/aarch64-apple-darwin.tsv` are owed. The x86-64 Linux baseline is committed beside where they go, written by the harness's bench.sh with `--record`, which keeps every sample and the Form B fields; `--compare` is the 2x check. The nightly's bench job records the other three on their runners as artifacts, to be reviewed and committed | maintainers, from the nightly bench artifacts | + +The one check not yet written is gate 8's baselines for the three triples other +than x86-64 Linux. ## The tag checklist @@ -895,12 +778,13 @@ claim that the script is missing. - [ ] Every fix in the window carries Form A; every number carries Form B; every limitation carries Form C. - [ ] The rollback path for the tag itself is written down: how to un-ship it, and who does. - [ ] No credential appears in any workflow file, commit, or published artefact. -- [ ] Johnny has authorised the tag in writing. +- [ ] The repository owner has authorised the tag in writing. ## Revision history | Revision | Date | What changed | |---|---|---| -| 1 | 2026-09-26 | Written by Milo on PR #7. Seven criteria, three definitions, `freeze-check.sh`. Closed unmerged at `ce6b3ff`. | -| 3 | 2026-09-26 | "The state of the checks" refreshed by Johnny against `main` at `f2f2b40`. Revision 2 was accurate when it was written and wrong on six of ten rows within twenty minutes, because #20, #21 and #23 landed behind it. Now written: `soak-report.sh`, `nightly.sh` and `nightly.yml`, `check-doc-examples.sh`, the 6d job as `contributor-commands`, `verify-install.sh`, `gc-pauses.sh`. Also written, in `sinisterMage/sharpie` rather than here: `tests/rungs.sh` (its PR #2, at `6af434d`), which was merged twenty-one minutes before this revision was first drafted saying it was not — the one row no check in this tree can see, found by reading that repository. Still not written: `guard-check.sh`, `followups-check.sh`, and criterion 8's baselines. Criteria 7 and 8 split into one row per separately-missing thing; `check-change-label.sh` added as criterion 2's second check, which revision 2 omitted. The table now carries the commit it was read at. Added `scripts/gate-table-check.sh` and its tests, run as the `gate-table` job, which holds eleven of the fourteen rows to the tree on every pull request and names the other three out loud — so this table cannot drift again without turning CI red, and the State column's two spellings are now read by a script and documented as such. No criterion, gate, threshold or owner changed — this revision is a correction of the document's account of itself, plus the check that keeps it correct. | -| 2 | 2026-09-26 | Published by Johnny as the campaign's gate list. Part one preserved so existing citations still resolve — clause 3 and criterion 4 mean what #8 through #18 say they mean. Added: criterion 0 (the governing documents), criterion 8 (the numbers), gate 6d (#8), Part three (the nine open issues), Part four (the proof standard), Part five (the API and stability freeze), the platform matrix. Owners remapped from the campaign's earlier roster to its current one. Rulings recorded: clause 3 carries no exemption (#13 is a fix); 6a covers in-repo documentation only; 1.0 ships with digest sidecars and says so; rollback is `sharpie default `; criterion 1 drops two unowned soak subjects. | +| 1 | 2026-09-26 | First draft, on PR #7: seven criteria, three definitions, `freeze-check.sh`. Closed unmerged at `ce6b3ff`. | +| 2 | 2026-09-26 | Published as the release gate list. Part one preserved so existing citations still resolve — clause 3 and criterion 4 mean what #8 through #18 say they mean. Added: criterion 0 (the governing documents), criterion 8 (the numbers), gate 6d (#8), Part three (the issue map), Part four (the proof standard), Part five (the API and stability freeze), the platform matrix. Decisions recorded: clause 3 carries no exemption (#13 is a fix); 6a covers in-repo documentation only; 1.0 ships with digest sidecars and says so; rollback is `sharpie default `; criterion 1 names three soak subjects rather than four. | +| 3 | 2026-09-26 | "The state of the checks" re-read against `main` at `f2f2b40`, after #20, #21 and #23 landed behind revision 2. Criteria 7 and 8 split into one row per separately-missing check; `check-change-label.sh` added as criterion 2's second check. The table now carries the commit it was read at, and `scripts/gate-table-check.sh` holds it to the tree as the `gate-table` job. No criterion, gate or threshold changed. | +| 4 | 2026-09-29 | Rewritten for users and contributors: per-person ownership removed, Part three condensed now that all nine issues are closed, and the gate 6d note corrected now that #8 is fixed. No criterion, gate, threshold, severity definition, promise or decision changed. | diff --git a/docs/defects.md b/docs/defects.md index 576d498..d65ef27 100644 --- a/docs/defects.md +++ b/docs/defects.md @@ -12,20 +12,9 @@ discovered at the worst possible moment. ## Where a defect lands **GitHub issues on [`sinisterMage/WSharp`](https://github.com/sinisterMage/WSharp/issues) -are the record.** Every defect gets one, whoever found it — a user, a driver -program, the soak harness, or release verification. - -The repository is the record rather than any internal tracker for one reason: a -defect history that lives somewhere private is invisible to the people deciding -whether to depend on this language, and it evaporates when whatever produced it -moves on. An issue filed today is still readable in five years by someone asking -"was this ever broken, and when was it fixed". That is most of what a defect -record is *for*. - -Internal task trackers may mirror an issue where somebody needs to own the fix on -a schedule. When they do, the task links the issue and the issue is still the -place the reproduction, the cause and the fix are written down. The mirror -carries scheduling; it does not carry facts. +are the public record.** Every defect gets one, whoever found it — a user, a +driver program, the soak harness, or release verification — and the reproduction, +the cause and the fix are written down there. ## What an issue must carry @@ -58,8 +47,8 @@ when it is most expensive. What matters for intake: -- The **filer proposes** a severity on the report form; the **triage owner for - the surface confirms or changes it**, within one working day. +- The **filer proposes** a severity on the report form; **a maintainer confirms + or changes it** at triage, within one working day. - **An unconfirmed report counts at the severity the filer proposed.** So "no open P1" cannot be satisfied by leaving reports untriaged — which is the property that makes the gate mean anything. @@ -69,17 +58,8 @@ What matters for intake: Rarity is not a mitigation: a silently wrong answer that happens once a month is still a silently wrong answer. -Triage owners, from the same document: - -| Surface | Owner | -|---|---| -| Compiler, runtime, stdlib, the collector | Mira (WLA-3, WLA-4) | -| Release pipeline, install, artefacts, digests, sharpie, the `.wsharp` ecosystem | Ash (WLA-7) | -| Raython and its sample application | Wren (WLA-8) | -| Harness, conformance, fuzzing, soak infrastructure | Dex (WLA-6) | -| Benchmarks, collector measurement, published numbers | Ridge (WLA-5) | - -Disagreement about a severity goes to Johnny. +A maintainer confirms severity at triage; the `area:` labels, below, name the +surface. Disagreement about a severity goes to the repository owner. ## Triage @@ -129,8 +109,8 @@ Two rules keep this from becoming a place to hide defects: listing mandatory rather than editorial — an open P2 that is neither fixed nor listed becomes a P1, because it makes the documentation wrong. -Limitation-only issues are **excluded from the weekly counts** and reported as -their own line. Mixing them in would make "open P2" mean nothing. +Limitation-only issues are **excluded from defect counts** and reported +separately. Mixing them in would make "open P2" mean nothing. Labels beyond severity: `area: syntax`, `area: sema`, `area: codegen`, `area: runtime`, `area: cli`, `area: start`, `area: stdlib` for the crate or @@ -161,9 +141,8 @@ failed has not been shown to guard anything, and one written afterwards frequently does not — it is easy to write a case that passes for a reason unrelated to the bug. -The fix itself names, in the issue, which crate was wrong and which invariant it -violated. `CLAUDE.md` is the list of invariants this project actually runs on; if -the one that broke is not in it, add it there in the same change. +The fix itself names, in the issue and the PR, which crate was wrong and which +invariant it violated. ## Verification @@ -216,49 +195,13 @@ cargo test -p wsharp- Touched the runtime or the collector? The `--gc-stress` pass above is the one that matters, plus `the_collector_survives_stress_in_a_built_program`. -One more trap, because it costs a confusing five minutes: **`cargo test ---workspace ` fails to compile.** Given a filter argument, cargo builds -`wsharp-start` with `--test` despite its `test = false`, and that crate defines -`main`, so the linker is asked to place two and refuses: - -```text -error: entry symbol `main` declared multiple times -``` - -Nothing is wrong with your change. Use `-p wsharp-cli --test cases` as above, or -`--workspace --exclude wsharp-start`. Plain `cargo test --workspace`, with no -filter, is fine and is what CI runs. +`cargo test --workspace ` used to fail to link with ``entry symbol +`main` declared multiple times``, because a filter pulled `wsharp-start` — which +defines `main` — into a test harness +([#8](https://github.com/sinisterMage/WSharp/issues/8)). That is fixed, and the +`contributor-commands` job in `.github/workflows/release-gates.yml` builds a +filtered workspace test on every pull request so it stays fixed. A filter still +matches test functions rather than cases, so the trap above applies to it too. Paste the commands and their output into the issue. Paraphrase loses the detail that turns out to matter. - -## The weekly report - -Posted weekly, on the triage pass. The format: - -```markdown -## Defect report — week ending YYYY-MM-DD - -| | P1 | P2 | P3 | Total | -|---|---|---|---|---| -| Open at start | | | | | -| Opened | | | | | -| Fixed | | | | | -| **Open now** | | | | | - -**Untriaged:** N (target: 0) -**Open P1:** N — *list each one, with owner and next action* -**Tracked limitations:** N open — *not defects, counted separately* - -**Load this week:** what was actually exercising the compiler — driver programs, -soak hours, platforms covered. A defect count means nothing without it. - -**Trend:** is the defect rate falling under constant or rising load? - -**Notes:** anything the numbers do not say. -``` - -The load line is the one that is tempting to skip and carries most of the -information. Zero defects in a week when nothing ran is not evidence of anything. -A falling defect rate under constant or rising load is the actual signal that -v1.0 is close; a flat rate means it is not, however small the absolute number is. diff --git a/docs/runtime-shared-flags.md b/docs/runtime-shared-flags.md index b4df882..553148f 100644 --- a/docs/runtime-shared-flags.md +++ b/docs/runtime-shared-flags.md @@ -1,9 +1,12 @@ # Shared runtime flag publication -Fix for [WSharp #64](https://github.com/sinisterMage/WSharp/issues/64). -This is separate from the request/phase publication repair in -[PR #63](https://github.com/sinisterMage/WSharp/pull/63); neither repair alone -proves the whole collector protocol sound. +A design note for the two process-wide GC flag words -- `ws_gc_poll_flag` and +`ws_gc_evacuating_flag` -- and the per-worker contributions behind them +([#64](https://github.com/sinisterMage/WSharp/issues/64)). It covers how a +worker's request becomes visible to every thread. The ordering between a +worker's pause request and the phase that services it is a separate protocol +([PR #63](https://github.com/sinisterMage/WSharp/pull/63)); the collector needs +both to hold. ## Invariant and ordering @@ -16,13 +19,13 @@ publish a value derived from an obsolete count after another writer returns. Stronger atomic ordering alone would not make those separate operations one transaction. -The mutex synchronizes writers; existing AcqRel bit/count operations and Release +The mutex synchronizes writers; the AcqRel bit/count operations and Release flag publication remain. Runtime readers use Acquire loads. Generated readers -retain their existing byte-load fast paths and exported symbols. The flag is -not a snapshot of intermediate count/bit values inside an unfinished transition. +keep their byte-load fast paths and exported symbols. The flag is not a +snapshot of intermediate count/bit values inside an unfinished transition. The caller must finish request publication before publishing the phase that allows servicing it (PR #63), and must finish arming evacuation before resuming -the mutator. This patch does not change generated-load memory-order assumptions. +the mutator. The memory-order assumptions of generated loads are unchanged. The lock protects both aggregates. It never acquires heap, mark-state, worker registry or park locks, never allocates, and never invokes production callbacks. @@ -32,64 +35,39 @@ thread-local callbacks suspend publication for the deterministic scheduler. The state contains no managed pointers: the collector's root set does not grow. No runtime dependency is added. -## Production impact and limits +## Why it matters, and what it does not cover `emit_gc_poll` branches around `ws_gc_poll` when the aggregate byte is zero; -`gc::ws_gc_poll` also checks it. The old interleaving can therefore suppress a -pending worker's poll until another transition restores the byte, even though -that worker's request is still set. `emit_load_barrier` similarly bypasses -resolution when the evacuation aggregate is zero. Because both use the same -helper, the stale publication violates the prerequisite for that fast path too. -These are source-level consequences of the demonstrated aggregate defect, not -an end-to-end demonstration of memory corruption or a GC safety failure. - -Workers remain process-lifetime allocations. Existing phase, parked-mutator and +`gc::ws_gc_poll` also checks it. Without the lock, an interleaving of one +worker's clear with another's raise can leave the byte at zero while a request +is still set, suppressing that worker's poll until another transition restores +the byte. `emit_load_barrier` similarly bypasses resolution when the evacuation +aggregate is zero, and both flags go through the same helper, so a stale byte +there would skip a load barrier while objects move. + +Workers remain process-lifetime allocations. The phase, parked-mutator and shutdown/abandon protocols remain responsible for pairing contributions and -settling workers. This repair neither adds worker deregistration nor validates +settling workers. The lock neither adds worker deregistration nor validates all exit paths. It also does not turn duplicate per-worker request bits into queues: callers still must obey the per-worker phase protocol. The mutex adds -writer contention; measurement belongs to Ridge, and no performance claim is -made here. Release gate definitions are unchanged; this defect and its guard -must be assessed through the existing defect/stability gates. - -## Deterministic proof - -The regression lives in `tests/cases/runtime_shared_aggregate.rs` and is included -by the runtime unit-test module. It calls the actual `set_shared` implementation -with private atomics so unrelated worker activity cannot change the count. -The scheduler suspends A after its count update. B probes the serialization -boundary: without a held lock it completes its transition before A publishes; -with the lock held it acknowledges contention, then blocks in the real helper -until A completes. Channels enforce the order, with no sleeps, retries or -ignored tests. The second case covers a request overlapping another worker's -request/clear round trip. Both check counts, flags, duplicate transitions and -final cleanup. The existing shared-worker assertion remains unchanged. - -Baseline main: `4e9249a6850f9721db13dbd61c63c57250eced11`. -The fix's parent, containing only the regression and test hook: -`d70fa36e878349dfdd7fb497b36efbcf508bdd70`. +writer contention; no performance claim is made. -```sh -cargo test -p wsharp-runtime --lib --locked aggregate_tests -- --test-threads=1 -``` +## Test -Before (Rust 1.95.0, Debian 13, glibc 2.41): exit 101, 1 passed, 1 failed: - -```text -worker::aggregate_tests::clearing_worker_cannot_hide_new_request ... FAILED -assertion `left == right` failed: aggregate flag must reflect remaining worker contributions - left: 0 - right: 1 -``` +The regression tests live in `tests/cases/runtime_shared_aggregate.rs`, which +`crates/wsharp-runtime/src/worker.rs` includes as the `aggregate_tests` unit-test +module: -After: exit 0, 2 passed, 0 failed. The failure occurs after both threads finish; -count is 1 and B's contribution is true, but the flag was 0. +- `clearing_worker_cannot_hide_new_request` -- one worker's clear is suspended + after its count update while another raises; the flag must still read 1. +- `raising_worker_survives_other_worker_round_trip` -- a request overlapping + another worker's request/clear round trip. -Additional checks: +Both call the real `set_shared` with private atomics, so unrelated worker +activity cannot change the count, and a channel-driven scheduler enforces the +interleaving with no sleeps or retries. Each checks counts, flags, duplicate +transitions and final cleanup. Run them with: ```sh -cargo test -p wsharp-runtime --lib --locked -cargo clippy -p wsharp-runtime --all-targets --locked -- -D warnings -cargo fmt --all --check -cargo build --workspace --locked +nix-shell --run "cargo test -p wsharp-runtime --lib aggregate_tests" ``` diff --git a/scripts/freeze-check.sh b/scripts/freeze-check.sh index 0c476dd..5c4a076 100755 --- a/scripts/freeze-check.sh +++ b/scripts/freeze-check.sh @@ -52,7 +52,7 @@ command -v gh >/dev/null 2>&1 || { note "" note " \$(git rev-parse HEAD) $(date -u +%Y-%m-%d)" note "" - note "Only Johnny declares day 0. See RELEASE-CRITERIA-1.0.md, \"The freeze clock\"." + note "Only the repository owner declares day 0. See RELEASE-CRITERIA-1.0.md, \"The freeze clock\"." exit 2 } diff --git a/scripts/soak-report-selftest.sh b/scripts/soak-report-selftest.sh index 78f92eb..166431b 100755 --- a/scripts/soak-report-selftest.sh +++ b/scripts/soak-report-selftest.sh @@ -33,7 +33,7 @@ END=2026-09-26 fresh() { rm -rf "$WORK/d" mkdir -p "$WORK/d" - printf 'compiler\tDex\tthe nightly\n' >"$WORK/d/subjects.tsv" + printf 'compiler\tnightly.yml\tthe nightly\n' >"$WORK/d/subjects.tsv" } append() { @@ -170,7 +170,7 @@ fi four() { rm -rf "$WORK/d" mkdir -p "$WORK/d" - printf 'ecosystem\tAsh\tthe check\tlinux,windows,intel-mac,arm-mac\n' >"$WORK/d/subjects.tsv" + printf 'ecosystem\tecosystem.yml\tthe check\tlinux,windows,intel-mac,arm-mac\n' >"$WORK/d/subjects.tsv" } row() { sh "$REPORT" --dir "$WORK/d" --append --subject ecosystem --verdict "$1" \ diff --git a/scripts/soak-report.sh b/scripts/soak-report.sh index e365186..67465b6 100755 --- a/scripts/soak-report.sh +++ b/scripts/soak-report.sh @@ -14,7 +14,7 @@ # # ## The row format # -# Each subject's owner appends its own rows; this script only reads them. One +# Each subject's daily job appends its own rows; this script only reads them. One # file per subject under `$SOAK_DIR`, named `.tsv`, six tab-separated # fields and no header: # @@ -23,7 +23,7 @@ # # date UTC, `YYYY-MM-DD`, the day the run *covered* # subject must match the file's name, so a row appended to the wrong file -# is caught rather than silently credited to the wrong owner +# is caught rather than silently credited to the wrong subject # verdict `ok` or `fail`; anything else is a malformed row and fails # commit the full SHA the subject was exercised at # platform a target triple, or `-` where the subject is not per-platform @@ -46,8 +46,8 @@ # `subjects.tsv`, so adding a subject is a diff somebody reviews rather than a # change to this script: # -# compilerDextests/harness/nightly.sh, once a day -# ecosystemAshthe ecosystem checkx86_64-unknown-linux-gnu,aarch64-apple-darwin +# compilernightly.ymltests/harness/nightly.sh, once a day +# ecosystemecosystem.ymlthe ecosystem checkx86_64-unknown-linux-gnu,aarch64-apple-darwin # # An optional fourth field lists the platforms every day must cover. A day with # a row for some of them and not others is *incomplete*, and fails exactly as a @@ -148,9 +148,9 @@ fi [ -f "$SUBJECTS" ] || { note "cannot answer: no subject manifest at $SUBJECTS" note "" - note "One line per soak subject -- name, owner, what it is:" + note "One line per soak subject -- name, what writes its rows, what it is:" note "" - note " compilerDextests/harness/nightly.sh, once a day" + note " compilernightly.ymltests/harness/nightly.sh, once a day" note "" note "See RELEASE-CRITERIA-1.0.md, criterion 1." exit 2 @@ -186,7 +186,7 @@ subject_count=0 note "Calendar, oldest day first:" note "" -while IFS=' ' read -r subject owner what platforms; do +while IFS=' ' read -r subject writer what platforms; do case "$subject" in '' | '#'*) continue ;; esac @@ -252,7 +252,7 @@ while IFS=' ' read -r subject owner what platforms; do "$file" done fi - printf ' %-12s %s\n' "owner:" "$owner${what:+ — $what}" + printf ' %-12s %s\n' "written by:" "$writer${what:+ — $what}" fi done <"$SUBJECTS" diff --git a/scripts/tests/gate-table-check.test.sh b/scripts/tests/gate-table-check.test.sh index b6ab715..e06d87b 100755 --- a/scripts/tests/gate-table-check.test.sh +++ b/scripts/tests/gate-table-check.test.sh @@ -76,46 +76,46 @@ expect() { # --- a table that agrees with its tree -------------------------------------- d="$(fixture agree \ - '| `scripts/there.sh` | 1 | **written** (#20) | Dex |' \ - '| `scripts/absent.sh` | 2 | **not written** | Ash |')" + '| `scripts/there.sh` | 1 | **written** (#20) | CI |' \ + '| `scripts/absent.sh` | 2 | **not written** | CI |')" mkdir -p "$d/scripts" && : >"$d/scripts/there.sh" expect "both rows true" "$d" 0 "2 of 2 rows" # --- the drift in each direction -------------------------------------------- d="$(fixture claims_missing \ - '| `scripts/ghost.sh` | 1 | **written** (#20) | Dex |')" + '| `scripts/ghost.sh` | 1 | **written** (#20) | CI |')" expect "claims a file that is not there" "$d" 1 "does not exist" d="$(fixture missed_arrival \ - '| `scripts/arrived.sh` | 3 | **not written** | Ash |')" + '| `scripts/arrived.sh` | 3 | **not written** | CI |')" mkdir -p "$d/scripts" && : >"$d/scripts/arrived.sh" expect "missed an arrival" "$d" 1 "is not written, and it is there" # --- a row with no verdict is a row nothing can check ----------------------- d="$(fixture no_verdict \ - '| `scripts/there.sh` | 1 | being thought about | Dex |')" + '| `scripts/there.sh` | 1 | being thought about | CI |')" mkdir -p "$d/scripts" && : >"$d/scripts/there.sh" expect "no verdict" "$d" 1 "states no verdict" # --- "not written" contains "written", and must not be read as it ----------- d="$(fixture not_written_prefix \ - '| `scripts/ghost.sh` | 4 | **not written.** The data it needs is on `main` | Ash |')" + '| `scripts/ghost.sh` | 4 | **not written.** The data it needs is on `main` | CI |')" expect "a full stop inside the bold run" "$d" 0 "1 of 1 rows" # --- workflow jobs ---------------------------------------------------------- d="$(fixture job_present \ - '| `scripts/there.sh` | 6d | **written** (#21), as `the-job` in `.github/workflows/gates.yml` | Ash |')" + '| `scripts/there.sh` | 6d | **written** (#21), as `the-job` in `.github/workflows/gates.yml` | CI |')" mkdir -p "$d/scripts" "$d/.github/workflows" : >"$d/scripts/there.sh" printf 'name: Gates\njobs:\n the-job:\n runs-on: ubuntu-latest\n' >"$d/.github/workflows/gates.yml" expect "a job that exists" "$d" 0 "1 of 1 rows" d="$(fixture job_renamed \ - '| `scripts/there.sh` | 6d | **written** (#21), as `old-name` in `.github/workflows/gates.yml` | Ash |')" + '| `scripts/there.sh` | 6d | **written** (#21), as `old-name` in `.github/workflows/gates.yml` | CI |')" mkdir -p "$d/scripts" "$d/.github/workflows" : >"$d/scripts/there.sh" printf 'name: Gates\njobs:\n new-name:\n runs-on: ubuntu-latest\n' >"$d/.github/workflows/gates.yml" @@ -124,7 +124,7 @@ expect "a job that was renamed" "$d" 1 "has no \`old-name\` job" # A `run:` line mentioning the name must not satisfy the claim: the point of # checking a job name is that a reader can find it in a CI run. d="$(fixture job_only_in_run \ - '| `scripts/there.sh` | 6d | **written** (#21), as `the-job` in `.github/workflows/gates.yml` | Ash |')" + '| `scripts/there.sh` | 6d | **written** (#21), as `the-job` in `.github/workflows/gates.yml` | CI |')" mkdir -p "$d/scripts" "$d/.github/workflows" : >"$d/scripts/there.sh" printf 'name: Gates\njobs:\n other:\n steps:\n - run: echo " the-job: not a job"\n' \ @@ -132,7 +132,7 @@ printf 'name: Gates\njobs:\n other:\n steps:\n - run: echo " the-job: expect "a name that is only in a run line" "$d" 1 "has no \`the-job\` job" d="$(fixture jobs_listed \ - '| `scripts/there.sh` | 5 | **written**, as `a`, `b` and `c` in `.github/workflows/gates.yml` | Dex |')" + '| `scripts/there.sh` | 5 | **written**, as `a`, `b` and `c` in `.github/workflows/gates.yml` | CI |')" mkdir -p "$d/scripts" "$d/.github/workflows" : >"$d/scripts/there.sh" printf 'name: Gates\njobs:\n a:\n x: 1\n b:\n x: 1\n' >"$d/.github/workflows/gates.yml" @@ -141,11 +141,11 @@ expect "the third of three jobs is missing" "$d" 1 "has no \`c\` job" # --- another repository's path ---------------------------------------------- d="$(fixture external_named \ - '| `tests/rungs.sh` | 7 | **not written.** It belongs to `sinisterMage/sharpie`, not to this repository | Ash |')" + '| `tests/rungs.sh` | 7 | **not written.** It belongs to `sinisterMage/sharpie`, not to this repository | CI |')" expect "an external path, whose owner is named" "$d" 0 "cannot check its state" d="$(fixture external_unnamed \ - '| `tests/rungs.sh` | 7 | **not written** | Ash |')" + '| `tests/rungs.sh` | 7 | **not written** | CI |')" expect "an external path, whose owner is not" "$d" 1 "and this row does not say so" # Both verdicts are accepted on an external path, and neither is believed. The @@ -153,7 +153,7 @@ expect "an external path, whose owner is not" "$d" 1 "and this row does not say # has not written it yet" into a check about *this* tree -- and sharpie wrote it # the same day, so the check then stood in the way of the table being right. d="$(fixture external_written \ - '| `tests/rungs.sh` | 7 | **written**, in `sinisterMage/sharpie` | Ash |')" + '| `tests/rungs.sh` | 7 | **written**, in `sinisterMage/sharpie` | CI |')" expect "an external path said to be written elsewhere" "$d" 0 "cannot check its state" # It must still be unchecked rather than quietly passed -- the number of rows @@ -163,7 +163,7 @@ expect "an external row counts as unchecked" "$d" 0 "0 of 1 rows" # --- a row this check cannot hold to anything, reported rather than passed --- d="$(fixture unnameable \ - '| a committed baseline per triple | 8 | **not written.** No baseline exists | Ridge |')" + '| a committed baseline per triple | 8 | **not written.** No baseline exists | CI |')" expect "a row naming no path" "$d" 0 "names no path in this tree" # --- the stamp -------------------------------------------------------------- @@ -173,7 +173,7 @@ mkdir -p "$d" { echo "| Check | Gate | State | Owed by |" echo "|---|---|---|---|" - echo '| `scripts/ghost.sh` | 1 | **not written** | Ash |' + echo '| `scripts/ghost.sh` | 1 | **not written** | CI |' } >"$d/CRITERIA.md" expect "no commit stamp" "$d" 1 "carries no" diff --git a/scripts/verify-install.sh b/scripts/verify-install.sh index 5413ecc..975f9d1 100755 --- a/scripts/verify-install.sh +++ b/scripts/verify-install.sh @@ -43,7 +43,8 @@ EOF # --------------------------------------------------------------------------- # Reporting. Every check prints one line beginning "ok" or "FAIL", so the -# transcript pasted into a task comment is readable without the script. +# transcript pasted into an issue or a release checklist is readable without +# the script. # --------------------------------------------------------------------------- failures=0 diff --git a/soak/README.md b/soak/README.md index 4fd4c7f..74a25ed 100644 --- a/soak/README.md +++ b/soak/README.md @@ -2,8 +2,8 @@ Criterion 1 of `RELEASE-CRITERIA-1.0.md` asks whether three subjects were exercised continuously for the freeze window. This directory is the answer's -raw data: one TSV per subject, one row per day, appended by whoever owns that -subject. +raw data: one TSV per subject, one row per day, appended by the job that +exercises that subject. ```sh scripts/soak-report.sh # the 28-day window ending today @@ -33,7 +33,7 @@ that looks like evidence. ## Appending a row `scripts/soak-report.sh --append` is the only writer, so the format has one -definition. Each owner runs it at the end of their daily job: +definition. Each subject's daily job runs it at the end: ```sh scripts/soak-report.sh --append \ @@ -46,7 +46,7 @@ scripts/soak-report.sh --append \ Six tab-separated fields, no header: date, subject, verdict (`ok` or `fail`), the full commit SHA, a target triple or `-`, and free-text detail. The subject field is checked against the file's name, so a row appended to the wrong file is -caught rather than credited to the wrong owner. +caught rather than credited to the wrong subject. Several rows for one subject on one day are expected — a subject run on four triples writes four. The day counts as reported when at least one row exists, @@ -59,15 +59,15 @@ three of four reported is `-` in the calendar and fails as a missing day does: ## Subjects `subjects.tsv`, so adding one is a diff somebody reviews rather than a change to -the script: name, owner, what it is, and optionally the comma-separated -platforms every day must cover. +the script: name, what writes its rows, what it is, and optionally the +comma-separated platforms every day must cover. -| Subject | Owner | What it is | +| Subject | Written by | What it is | |---|---|---| -| `compiler` | Dex | `tests/harness/nightly.sh` once a day — parity across the three execution modes, the fuzz corpus, the pause measurements | -| `ecosystem` | Ash | `scripts/ecosystem-check.sh`, run by `.github/workflows/ecosystem.yml` once a day on each of the four release triples, each of which must report | -| `raython` | Wren | the Raython sample application, a long-running HTTP server | +| `compiler` | `nightly.yml` | `tests/harness/nightly.sh` once a day — parity across the three execution modes, the fuzz corpus, the pause measurements | +| `ecosystem` | `ecosystem.yml` | `scripts/ecosystem-check.sh`, run by `.github/workflows/ecosystem.yml` once a day on each of the four release triples, each of which must report | +| `raython` | the Raython deployment | the Raython sample application, a long-running HTTP server | A subject with no rows is not a subject that is doing fine — it is a `?` for -every day and it fails the gate. That is the intended reading while `raython` -does not exist yet. +every day and it fails the gate. That is the intended reading until the Raython +sample application is deployed and reporting. diff --git a/soak/subjects.tsv b/soak/subjects.tsv index 6abf605..cd9a64f 100644 --- a/soak/subjects.tsv +++ b/soak/subjects.tsv @@ -1,3 +1,3 @@ -compiler Dex tests/harness/nightly.sh, once a day, x86_64-unknown-linux-gnu -ecosystem Ash scripts/ecosystem-check.sh through .github/workflows/ecosystem.yml, one full run per day on all four release triples x86_64-unknown-linux-gnu,x86_64-pc-windows-msvc,x86_64-apple-darwin,aarch64-apple-darwin -raython Wren the Raython sample application, a long-running HTTP server +compiler nightly.yml tests/harness/nightly.sh, once a day, x86_64-unknown-linux-gnu +ecosystem ecosystem.yml scripts/ecosystem-check.sh through .github/workflows/ecosystem.yml, one full run per day on all four release triples x86_64-unknown-linux-gnu,x86_64-pc-windows-msvc,x86_64-apple-darwin,aarch64-apple-darwin +raython the Raython deployment the Raython sample application, a long-running HTTP server diff --git a/tests/conformance/README.md b/tests/conformance/README.md index 326d5ce..bbc2676 100644 --- a/tests/conformance/README.md +++ b/tests/conformance/README.md @@ -52,10 +52,10 @@ identically. One row per rejection case in `target/harness/conformance/results.tsv`, and a report beside it naming the compiler, the commit, the platform and the totals. -| Verdict | Means | Who owns it | +| Verdict | Means | What it calls for | |---|---|---| -| `PINNED` | stderr matched the snapshot; `check` and `run` agreed | nobody, this is the gate met | -| `CHANGED` | stderr differs from the snapshot | read the diff: either a diagnostic regressed (a defect for Mira) or it improved (a bless commit) | +| `PINNED` | stderr matched the snapshot; `check` and `run` agreed | nothing: this is the gate met | +| `CHANGED` | stderr differs from the snapshot | read the diff: either a diagnostic regressed (a defect to file) or it improved (a bless commit) | | `ACCEPTED` | the case says `// error:` and the compiler compiled it | a defect, and a severe one: the compiler started accepting something the language refuses | | `NOSPAN` | no `--> file:line:col`, or it names a line the case does not have | a defect | | `CHECKRUN` | `check` and `run` refused it differently | a defect, and the `err_unpinned_generic` class | @@ -93,38 +93,29 @@ leaving the case unblessed — and it refuses a case whose snapshot and whose `// error:` header no longer describe the same refusal, because that is the one finding a reviewer skimming a bless diff would not notice. -**`expected/` holds 63 snapshots, blessed from a real `conformance` job's -artifact, not from a local build.** Nobody on this campaign has a machine that -can build W# — no `rustc`, no `cargo`, no `cc` — so guessed diagnostics would -have produced failures on the first real run that looked like compiler defects -and were typing mistakes. What happened instead: the run of the `conformance` -job (dispatched manually on 2026-09-27, run `36296023948`) exited 3 and uploaded +**`expected/` holds one snapshot per rejection case — 68 of them.** They are +blessed from the `conformance` CI job's artifact rather than written by hand or +from one local build. A run that finds a case `UNBLESSED` exits 3 and uploads every diagnostic it *would* have pinned as -`conformance-/diffs/*.proposed.diag`; the snapshots here are those -bytes, normalised (see below) and committed. They came from four independent -triples and, after normalisation, were **byte-identical on all four** — which is -itself the first evidence that a diagnostic is a platform-independent claim. -The sixty-third (`err_const_string_array`) was added when `main` gained that -case (#58) while the bless PR was open; the scheduled run `36306746001` -proposed it and re-confirmed the other sixty-two with zero mismatches. - -The four `err_ffi_*` cases were left un-blessed while they reported `NOSPAN` -(their `-->` named a line of `std/ffi`, not of the user's file -- -[#37](https://github.com/sinisterMage/WSharp/issues/37)), deliberately, because -pinning a diagnostic that points at the wrong file would pin the defect as -correct. They were blessed with the fix that points them at the program's own -`ffi.bind` call, so every rejection case is now pinned. The compiler subject's -soak row reads `fail` on any night a case is `NOSPAN` again -- because criterion -5 genuinely is not met while that is true. +`conformance-/diffs/*.proposed.diag`; the snapshots are those bytes, +normalised (see below) and committed. The job runs on all four release triples, +and the proposals are matched across them before they are committed: after +normalisation they are byte-identical, which is the evidence that a diagnostic +is a platform-independent claim. + +A case reported as `NOSPAN`, `ACCEPTED` or `CHECKRUN` stays un-blessed until its +defect is fixed, because pinning it would pin the defect as correct. The four +`err_ffi_*` cases were held back this way while their `-->` named a line of +`std/ffi` rather than the user's file +([#37](https://github.com/sinisterMage/WSharp/issues/37)), and were blessed with +the fix that points them at the program's own `ffi.bind` call. Every rejection +case is now pinned. The compiler subject's soak row reads `fail` on any night a +case is `NOSPAN` again, because criterion 5 is not met while that is true. ## What this suite does not cover Written here rather than left in anyone's head. -- **The four `err_ffi_*` cases are `NOSPAN` and un-blessed.** Their `-->` names - a line of `std/ffi`, not of the user's file, so there is no span to pin; this - is [#37](https://github.com/sinisterMage/WSharp/issues/37) and the gate is - correctly red while it stands. - **Only the 68 cases in `tests/cases` whose header carries `// error:`.** It adds no new rejection cases. The gaps in what the language refuses are still gaps; this pins the refusals that exist. @@ -144,9 +135,9 @@ Written here rather than left in anyone's head. Windows runner compares against the same snapshot, and the C library's `strerror` words in an `(os error N)`. The errno is kept — `ENOENT` against `EACCES` still differs — but the sentence in front of it is the platform's, - and the first real run had the four triples disagree on nothing but that - string (`err_import_missing_file`: Linux says "No such file or directory", - Windows says "The system cannot find the file specified."). Nothing else is + and without this the four triples disagree on nothing but that string + (`err_import_missing_file`: Linux says "No such file or directory", Windows + says "The system cannot find the file specified."). Nothing else is normalised — every caret column is compared. - **`--gc-stress` changes nothing here and is accepted anyway.** A program refused at compile time never allocates. The flag is plumbed through so that diff --git a/tests/harness/README.md b/tests/harness/README.md index f83247b..a7aef2b 100644 --- a/tests/harness/README.md +++ b/tests/harness/README.md @@ -20,7 +20,7 @@ nix-shell --run "cargo build --workspace" | `selftest.sh` | does the harness report what actually happened? | seconds, **no compiler needed** | | `conform-selftest.sh` | can the conformance gate tell a rotted diagnostic from an intact one? | seconds, **no compiler needed** | | `parity.sh` | do the three execution modes agree, per case? | 1–3 h for the corpus on one core | -| `conform.sh` | does every refusal still print the same diagnostic, span and help line? | seconds for the 64 rejection cases | +| `conform.sh` | does every refusal still print the same diagnostic, span and help line? | seconds for the 68 rejection cases | | `fuzz.pl` | does arbitrary input crash or hang the front end or a stdlib parser? | bounded by `--max-seconds` | | `flake-rate.sh` | how often does one case do something different, and how long does it take when it does? | attempts × the case | | `gc-pauses.sh` | how long does the collector stop the program for? | ~10 min for `gc_*` at 5 repeats | @@ -287,11 +287,12 @@ resident size that climbs anyway is the collector's. ### The soak procedure -A soak is a job, not a heartbeat. Start it, write down where its output lands, -and come back. +A soak window runs for days, so start it detached and check on it rather than +holding a terminal open. 1. **Start the window.** Give it `--label` and a real `--duration` (a week is - `604800`). Use `setsid nohup` so it outlives the shell: + `604800`). Run it under `nohup` and `setsid` so it outlives the shell that + started it: ```sh nohup setsid tests/harness/soak.sh --label foundryd-w1 \ @@ -300,12 +301,13 @@ and come back. --out target/harness/soak-foundryd-w1 >/dev/null 2>&1 & ``` -2. **Record in the task**: the output directory, the command, the probe, the - window's intended length, the commit, and the platform. A later heartbeat - resumes from that, and nothing else. +2. **Note what you started**: the output directory, the command, the probe, the + window's intended length, the commit, and the platform. The report records + them as well, but only once it is written; a note taken at the start is what + lets anybody find the run and read it while it is still going. -3. **Check in without waiting.** `tail counters.tsv` says whether it is still - sampling. Do not sit in a loop watching it. +3. **Check progress** with `tail` on the output directory's `counters.tsv`. A new + row every `--interval` seconds means it is still sampling. 4. **Close the window** by reading `report.md`, then compare against the previous one: @@ -315,8 +317,8 @@ and come back. ``` 5. **Say what actually ran.** If the window did not elapse, the report says so - and so should the summary: *what ran, and for how long*. A window that was cut - short is not a clean window. + and so should any summary of it: *what ran, and for how long*. A window that + was cut short is not a clean window. ## `flake-rate.sh` — a rate, not an adjective @@ -437,7 +439,8 @@ A suite's limits belong where somebody reading its green run will see them. that pauses more says `TRUNCATED` in its trailer and `gc-pauses.sh` reports it. - **Timing numbers here are not published numbers.** Pause and duration figures are for triage — is this a stall or a busy box — and anything quoted as a - result goes through Ridge's harness under Form B of the proof standard. + result is measured with `tests/harness/bench.sh` or `gc-pauses.sh` and + published under Form B of the proof standard. - **CI covers four triples and one older-glibc container.** A platform not in that matrix has no evidence, and a pass on one triple is evidence about one triple. 32-bit and non-x86-64/aarch64 targets are out of scope (#11).