Skip to content

Mac-first completion: platform crate, AArch64 tiers, emit and runtime conformance, review fixes - #229

Open
rabbitson87 wants to merge 293 commits into
mainfrom
completion/mac-first
Open

rabbitson87 wants to merge 293 commits into
mainfrom
completion/mac-first

Conversation

@rabbitson87

@rabbitson87 rabbitson87 commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

What this is

Two stretches of the completion plan, done on an Apple M3 Max (aarch64-apple-darwin), plus a review round. The first made the workspace build and pass on macOS and added local CI and sweep tools. The second makes emitted JavaScript and the runtime behave correctly. The review round fixes what two reviewers and an independent read-only audit found in the second. 291 commits against main. The hosted workflows are unchanged.

This is not the finished product. The compiler and package roots are still open, and nothing here claims otherwise.

Please merge without squashing. AGENTS.md asks for a bisectable series; see Process notes for the commits that fall short of it.

Results at 4154686

Measure Start of the second stretch Now
test262 (in-process runner, files) 40,563 of 51,946 47,240, identical failing set on interpreter, JIT and AOT
test262 interpreter receipt (applicable rows) not taken 47,111 of 51,830 at eb2eec1; no newer receipt
TypeScript 7.0.2, shard 0/64 223 of 1,044 271, zero regressions
TypeScript 7.0.2, full receipt 14,894 of 66,786 at 4db845c not retaken
just ci pass at eb2eec1 pass at 4154686

The two test262 numbers use different denominators: the receipt counts the catalog's applicable rows, the runner counts files. No JIT, AOT or full TypeScript receipt has been taken at this commit; that is the full-baseline card and it stays open.

Hosted CI at 4154686: Quality, Corpus, Formal, Preflight, Windows native boundary and CodeQL pass (Quality fails on main). The four Conformance receipt shard jobs and Conformance receipt accounting fail: each shard runs into its 45-minute job timeout (.github/workflows/ci.yml:277) and is cancelled, and accounting fails because no shard delivered. main's latest run (4ba4408) fails the same way, so this predates the branch. The workflow is unchanged here, so the timeout stays.

What changed in the second stretch

Area Effect
JavaScript emit Namespaces, enums, nested expressions, import aliases and exported var/let lower correctly.
Lowering Array holes, object literal define semantics, native construct, super through a home object, read-write keys converted once, for-of targets.
Runtime library Intl, Temporal, Atomics, Iterator helpers and the RegExp protocol now run. Most of that code was uncompiled or missing.
Garbage collection Weak holders are found during marking: a 505,000-object loop went from over 120 s to 6.65 s.
Verification test262 host sets TZ=UTC; .types/.symbols headers use the catalog path; target cells are format-neutral across seven targets.

What changed in the review round

Area Effect
Tooling recount now reruns a sample of passing rows (default 500) so --fail-on-regression can fire; doctor checks each tool alone; perf harnesses read only the host facts they use; the status report refuses its own sidecar's path.
Linker The in-process GOT is 8-aligned and keeps a relocation's addend.
GC safety A builtin's Rust locals are rooted until it returns, so toSorted(cmp), Object.groupBy, f(...gen()), promise.then and many others no longer die with an uncatchable heap slot does not exist. Loops that use each callback result once (reduce, forEach, map and their like) keep only the latest 32.
Module output Every ES and Node module kind emits (es2020, es2022, preserve, node16/nodenext, per file by extension and package.json); CommonJS imports stay in source order; function exports hoist above the requires; re-exported imports are live getters; exported locals stay current on every write; export = is module.exports; string export names are quoted from their decoded text.
Import elision An import is kept if the file reads it as a value, in any position. What another file erases is decided from its exports, through barrels and export *, and only for export { x } and export default x. verbatimModuleSyntax, JavaScript files, @jsx, emitDecoratorMetadata and reactNamespace keep every import.
Language semantics A class called without new throws; static name defines instead of throwing; field initializers resolve in the class's scope; an arrow's super() shares the constructor's state; a destructuring target is evaluated before its value; a this parameter takes no argument.
Other Math.abs(new Number(-1)), 'ab'.repeat(0), catch-pattern defaults, an empty ES module keeping export {};, and a lost reply when shutdown raced a request in the API server.

Verified

  • just ci passed at 4154686; catalog, completion and diagnostics regenerate --check pass.
  • No new #[ignore], todo!, unimplemented! or #[allow] across the second stretch and the review round. .github, lockfiles (second stretch onward), rust-toolchain.toml and every generated file are unchanged by this range, except verification/manifest.lock.json, which 1352e7b regenerated for the target cell change.
  • Regression tests run on all three tiers for the new runtime and lowering behavior; the GC test fails with heap slot ... does not exist when its root is removed.
  • Each emit fix was compared with tsc 7.0.2 output or Node.

Not verified

  • Windows runtime. Only compilation is proven. driver.rs still calls cc ... -o, and the platform crate has no Windows implementation at all: its machine-fact, process-region and peak-memory readers return Unsupported there (there is no windows.rs; the fallbacks are in crates/bamts-platform/src/lib.rs). Expect failures; they are the input for the Windows tasks.
  • Linux and x86_64 macOS at run time. Compile-checked only. The x86_64 macOS archive in this series is cross-built and carries an empty runtime archive.
  • Auto-merged work/stdlib code. The seven conflicted files were reviewed, and a read-only audit covered the twelve files both sides touched. The rest of the merge was not audited.
  • Review is by reading and by running small programs. Nothing is proven complete.

Known defects and limits

Known test262 regressions against eda5413 (11 files, down from 12): language/arguments-object/10.6-13-a-{2,3}.js, language/eval-code/{direct,indirect}/non-definable-global-{function,generator}.js, built-ins/ThrowTypeError/unique-per-realm-non-simple.js, built-ins/Date/prototype/setFullYear/15.9.5.40_1.js, and three Temporal toLocaleString/ignore-timezone.js files.

Open and not fixed: f.length counts default parameters; module: preserve with export =; .mts import x = require(); moduleDetection: force and import.meta miss the export {} marker; CommonJS source maps skip require lines and newLine: crlf leaves the prelude LF; a trailing comment after an import is lost; __importDefault/__importStar are not emitted; array destructuring never closes iterators; class A {} interface A {} and <NS.Comp /> raise false errors; @jsx pragmas are ignored by the checker; shebangs are dropped; CommonJS import() is not rewritten; class extends null throws at definition; a builtin that collects very large callback results keeps them all rooted until it returns, so it can reach the heap limit where Node would not (String.prototype.replace with a huge result is one).

Product limits: local-time Date throws unless TZ is UTC or a POSIX form, which includes the normal unset case; locale data is en/en-US only and chinese, dangi and islamic calendars are missing; bamts file.ts writes a native executable and no .js, while a tsconfig.json project writes JavaScript; arguments is still an Array; direct eval cannot see caller locals; Annex B block functions are missing.

Decisions to review

Test expectations changed. The plan's rule is to never change an expected value to make something pass. These changed, with reasons, and still need review:

  • Behavior changed on purpose: 7d321c4, f13e726, 494c9fb, a8a641c (atomics_claims_no_methods, changed by a worker without prior approval), 2a6cb50, f8cdfaa.
  • Test judged wrong against test262, its own assertions or tsc: 0183c1f, 65e026b, 5c09826, 5861c67, cacb3d7, e0521c6, 190379b, 1b67908, and commonjs_export_specifiers_read_what_the_name_became (this series had added that expectation in d086b56; tsc 7.0.2 emits the getter).
  • Deleted: seven option-reader tests in fe8e661, removed with the dead code they covered.
  • host_conditions_come_from_the_platform_crate gained a branch for a Linux host with no cpufreq; its assertions are unchanged.

Held, with tests unchanged. Each pinned test conflicts with test262 or tsc, so the old behavior stays until someone decides:

  • date_parse_extended_years_round_trip (blocks the japanese calendar, 144 files, ready on a local branch)
  • commonjs_class_and_variable_exports_receive_void_0_preamble
  • the GC schedule tests
  • namespace_reports_unlowered_diagnostic_in_javascript
  • iterator_rejects_non_array_receiver
  • three __defineGetter__ and __defineSetter__ tests in annex_b.rs
  • runtime_invalid_date_propagates_and_iso_throws
  • locale_compare_uses_observable_utf16_lexical_signs
  • promise_all_closes_an_abrupt_iterator_and_keeps_the_original_throw
  • installed_methods_have_standard_descriptors_names_and_lengths
  • the own stack accessor tests
  • instance fields use [[Set]], so class B extends A { x = 1 } runs an inherited setter (Node defines); pinned by instance_field_init_after_super
  • decorated classes and decorated static fields keep assigning; pinned by two lower tests
  • export {}; is not added when module is unset; pinned by two emitter tests
  • BAMTS-C026 on an arrow's super(); tsc accepts it
  • a second direct super() refuses before re-running the parent

Review comments not changed, with reasons: the tests without a Linux gate (jit.rs region tests, run_with_telemetry_reports_peak_rss) stay ungated because gating them would be the hiding #[cfg] the plan forbids, they fail on Windows only because the platform readers are unimplemented there, and that failure is the input for the Windows task; the contract leaves that own files not yet written (windows_link.rs and two toolchain records) are real deliverables of open cards, and their ledgers now say which card writes each; no CI job runs ci-npm-release because it needs all five host archives and hosted workflows are deliberately unchanged, so five-host-install runs it by hand.

Process notes

  • af1a13c, 3dda270 and 3cfa106 each hold more than one mechanism, and 49da830 holds two that share their helpers.
  • In the work/stdlib merge, cargo check --all-targets passes on the first 75 of 76 commits, and per-commit unit tests ran only on the first 48 plus six others, so git bisect through that range may be less reliable than elsewhere.
  • Windows-only unused-import warnings in driver.rs and filesystem.rs tests predate this work.
First stretch, as written at 1948470

What this is

The first stretch of the completion plan, done on an Apple M3 Max (aarch64-apple-darwin): make the workspace build and pass there, move OS reads behind one crate, make in-process AOT/JIT work on AArch64, add local CI recipes, and add the tools that turn a conformance sweep into a short list of fixable causes. 55 commits, 57 files. The hosted workflows are unchanged.

Please merge without squashing. AGENTS.md asks for a bisectable series with one mechanism per commit, and the commits are built that way.

What changed

Area Commits (first to last) Effect
Platform boundary b372182 … e885f01, 5614d90 … 232c221, 5ff64cd New bamts-platform crate. Machine facts, CPU facts, process regions and peak memory are read there for Linux (x86_64 and AArch64) and macOS. Nothing outside it reads /proc or /sys. portability-check enforces that.
AArch64 AOT and JIT 883387d, b27b14c, ab6d3dd, f5655e2, 343433f In-process AOT picks its target from the host ABI, links AArch64 GOT relocations, accepts CALL26 as the reader reports it, and flushes the instruction cache. JIT code is checked against the host process map on every host.
Windows build 7fefcbc, b494022 The workspace and its tests now compile for x86_64-pc-windows-msvc. This is a compile result only.
Test isolation 7ecdff8, ee90d06, 7fdeb9b, d96f07a, 7d9951b, 9b4def8, f2d1342 Fixtures get their own scratch directories and derive their platform from the host, so the suite no longer depends on test order or on being on Linux.
Local CI c7bfc36, e92e173, 9f3dc78 just ci runs fmt, check, clippy, workspace tests, npm tests, the corpus check, cross-target cargo check and the portability check on one machine. ci-npm skips the two groups that need all five host archives; ci-npm-release runs everything.
Contract e9450c8 Nine leaves added (105 to 114): B7.2, E2.4, E3.4 to E3.7, E4.6 to E4.8, so the five-host execution scope, including Windows native output and its target cell, is in the root contract. FROZEN_LEAF_COUNT is now 114, with a test. The nine ledgers are in the same commit because validation rejects either half alone.
Sweep tooling 710e645, 4eb2d11, 5fd123c, 42254fc, b42c264, af4b98e, 4a95f44, 3c331d3, 1948470 suite families, suite diff, suite recount, suite status, the matching just recipes, just gate-status, and structural-mismatch messages that name the first differing line. Receipt states are unchanged: a shard rerun before and after the message change gave identical states on all 1,044 rows.
Other a0310fc, 8c592e6, 91b0b04 Removes npm/test/bamti-native-runner.test.mjs, which tested functions 275e12a had already removed; adds the single-worker change-window procedure; ignores the local plan ledger.

Verified

On macOS arm64 at 1948470, just ci printed LOCAL CI PASS:

  • cargo fmt --all --check, cargo check --workspace --locked and cargo clippy --workspace --all-targets --locked -- -D warnings pass.
  • cargo test --workspace --locked: 4,714 passed, 0 failed, 12 ignored.
  • npm: 86 tests, 82 pass, 0 fail, 4 skipped by ci-npm.
  • just xcheck compiles for x86_64 macOS, x86_64 Linux, AArch64 Linux and x86_64 Windows. portability-check passes.
  • The new report code was mutation-tested (18 mutants in the report module alone): each mutant had to make a test fail, and the file was restored byte for byte afterwards.
  • Four real conformance receipts were produced on this Mac (compiler, interpreter, JIT, AOT lanes).

Not verified

  • Windows runtime. Only compilation is proven. driver.rs still calls cc ... -o, which is not an MSVC command line, and the platform crate has no Windows implementation (its readers return Unsupported). Expect Windows test failures. Those failures are the input for the next Windows task.
  • Linux and x86_64 macOS at run time. The Linux targets are compile-checked only. The x86_64-apple-darwin npm archive in this series was cross-built from arm64, so it carries an empty runtime archive; it passes the install-metadata tests and nothing else.
  • ledger rebuild --check rejects all 248,828 obligations. It fails the same way on 9f3dc78, the commit before the contract change, so this PR does not cause it. It stays failing until the measurement baseline is restored.
  • completion regenerate --check and the catalog check pass.

Decisions to review

  • Deleting bamti-native-runner.test.mjs is reversible with git revert a0310fc.
  • The nine contract leaves were added to verification/completion-program.toml by hand, because completion regenerate validates and rewrites that file but does not derive leaves. After the edit it wrote back identical bytes. The reasoning is in e9450c8.
  • A failure "family" is <facet>/<slug> of the diagnostic text. It is defined in one function, family_name in sweep_report.rs.

Known leftovers

  • The types and symbols facets still fall into two large text families (174 and 168 rows) because their record lines have no line kinds yet.
  • 19 JavaScript rows are not routed to the javascript observable for project cases, and 10 rows fail with "cannot resolve ". Both are compiler work for later.
  • api_server::tests::shutdown_answers_then_stops_the_loop failed once in earlier runs and did not recur in three full runs. It is still unexplained.
  • Three #[ignore]d tests are unchanged.

Adds doctor, ci, ci-quality, ci-npm, ci-corpus, ci-accounting, authority, ci-catalog, ci-formal, ci-string-oracle, sweep and nightly beside the existing recipes, so the checks the hosted workflows run can run on this machine. The hosted workflows are unchanged.

conformance-shard now passes the host triple instead of a fixed Linux triple, so a receipt names the platform that produced it.

authority also fetches the TypeScript compiler tree, and sweep exports an adapter for each of the interpreter, JIT and AOT runners and creates the TypeScript snapshot when it is missing. Without these a test262 sweep records every row as having no registered adapter and still exits 0, and a TypeScript sweep stops on a missing authority or snapshot.
ci-npm ran the whole npm suite, but seven of its tests (the five staged leaf archives and the two clean-consumer installs) need the leaf archive of every supported host in npm/dist. One machine cannot build all five, so ci could never pass on a developer machine, and the earlier cards that were meant to produce those archives depend on this one.

ci-npm now skips those two groups by name, and ci-npm-release runs the whole suite unchanged. The release recipe still fails here on the three archives that do not exist (Linux x64, Linux arm64, Windows x64), so no test is dropped; the host-bound ones are gated where the hosts exist.
Widens the frozen scope to the five execution hosts, names aarch64-apple-darwin as the primary evidence host through the repository Justfile, and keeps the hosted workflows beside the local recipes. Historical catalogs are owned and measured locally under the 7.0.2 applicability rule.

Adds nine leaves: B7.2, E2.4, E3.4 to E3.7 and E4.6 to E4.8. That takes the frozen authority from 105 to 114 leaves and the target obligations from 28 to 49 (seven cells of seven obligations), so GATES.md G6 and the native root now expect targets=49/49.

The program file, the contract table and the gate ledgers have to change together: completion validation rejects a program whose leaves differ from the table, and a leaf without a ledger on disk. The nine ledgers are copied from E4.3 with their own row and every gate unchecked and pending.

The frozen leaf count was a literal in two places; it is now one constant, and a new test checks that a table with one leaf fewer, one more, or exactly the frozen count is judged correctly (it fails if the guard is weakened to accept fewer leaves).
Two read-only commands over evidence receipts, for the repair loops that work through a sweep:

  suite families <receipt> [--top N]          counts by state, facet and failure family
  suite families <receipt> --family F --root D  the family's rows, smallest input first
  suite diff <a> <b> [--limit N] [--fail-on-regression]
                                              transitions between two receipts

A family is <facet>/<slug>: the diagnostic text of a row that blocks completion with the parts that differ per case removed (the mode wrapper test262 workers add, backtick spans, the tail of long quoted spans, absolute paths, and the value of numbers). It is only as precise as the text a worker records, and is defined in one function so it can be tightened when the text improves. Rows that pass or are excluded by policy have no family; INAPPLICABLE_CATALOG_ERROR and EXTERNAL_BLOCKED do, because a scoped root accepts neither.

Receipts are read through EvidenceReader, so a truncated or inconsistent receipt is an error, not a short report. A receipt diffed against itself reports zero transitions. EvidenceRow gains a read-only detail accessor.
…uses

run_suite checked the platform token and built the lane binding inline. A command that reruns part of a sweep needs the same two steps, so they are now canonical_platform and fresh_lane_binding. No behavior change: the suite tests pass unchanged.
suite recount reruns only the rows of one failure family named by an earlier receipt, on the platform that receipt recorded, and prints which rows converted to PASS and which regressed. With --fail-on-regression it exits non-zero on any regression, which is the condition a repair loop needs: a fix must convert at least one row and regress none.

It writes no receipt. Every state still comes from the same lane executor and derive_row as a sweep, so a rerun cannot mint a PASS a sweep could not. A baseline row the catalog no longer contains is a set mismatch, and an unknown family is a usage error.

The tests run it against a throwaway repository: only the named family is rerun, an unknown family and a vanished catalog row are refused, and each of three mutations (rerun everything, rerun on the host platform, skip a missing row) makes them fail.
The three recipes a failure-family loop runs, over the commands added to bamts-verification:

  just status <catalog> <runner>        counts by state, facet and failure family
  just family-show <family> ...         the family's rows, smallest input first
  just recount <family> ...             rerun only that family; fail on any regression

Each reads the merged receipt that sweep writes, or the receipt passed as the last argument, so they also work on a single smoke shard. family-show finds each input file under the authority tree for the catalog; recount rebuilds the lane workers first, so it measures the code as it is now.
Every structural facet (types, symbols, declarations, source maps and JavaScript emit) failed with the same text, '<facet> structural mismatch after formatting/order normalization'. In the 2026-09-02 sweep that one sentence was 91% of the JavaScript failures, so a failure family could not be told from any other and a repair loop had nothing to read.

The reason is now '<facet> structural mismatch [category]: where'. The category is one of extra-lines, missing-lines, order, spacing, case, quote-style, punctuation or text. The location gives the first differing line with the expected and actual text in backticks, cut to 100 characters, with any backtick inside the line replaced so the span cannot be broken.

Only the text changes. The comparison, and so every row's state, is untouched, and the phrase 'structural mismatch' stays for existing assertions. Rows that differ the same way share a family under sweep_report::family_name, because the category is plain words and the lines are in backticks, which a family ignores; a test pins that.
The first version of the structural mismatch reason put 127 of 141 failing JavaScript rows into one category, 'text', which under the family rule is a single family again. The first differing lines show they are not one problem: a baseline comment the output replaced with code (45 rows), a class where the baseline lowers it to a function (8), CommonJS exports where the output has a binding (4), and so on.

When the two lines differ in kind, the category now names the pair, as in comment-vs-binding or class-vs-binding. The kinds are a closed set read from each line's first token (comment, import, export, declare, binding, function, class, strict, brace, cjs, blank, other). Lines of the same kind stay 'text'. As before only the reason text changes, never a state.
GATES.md G1 ran a checker at a path under another user's home directory that does not exist here, so the gate could never be evaluated. gate-status does the same job in the repository: it fails while any file in .outline/gates has an unchecked box or pending evidence, lists the open ledgers with their counts, and prints ALL MET otherwise.

An empty ledger directory fails with NO LEDGERS FOUND instead of passing, so a wrong working directory cannot read as success. Evidence marked blocked is not pending; leaf-gate treats it the same way. Checked on scratch ledgers (all met, unchecked box, pending evidence, one open of two, blocked evidence, none) and on the real tree, where it exits 1 with 169 of 177 ledgers open.
suite status --receipts <dir> --out <file> writes a Markdown report of every receipt in a directory: one summary table, then for each lane (catalog, mode, platform, shard) the commit it was measured at and its rows by state. A .json sidecar beside the report keeps the counts, so the next report shows the change per state since the previous one for the same lane (+n, -n, 0, or new).

The report is a function of the receipts and the previous sidecar and carries no timestamp. Every receipt is read through EvidenceReader, and an empty directory, a truncated or non-receipt file, a receipt that mixes lanes, or two receipts for one lane is an error that writes nothing, so a report cannot read as complete when it is not.

ExecutionBinding gains a read-only source_sha accessor for the commit column.
…s in GATES.md

just status-report writes target/evidence-local/status.md from every merged receipt, and sweep refreshes it after each merge, so progress is read from the receipts and not typed into a gate file.

GATES.md carried 29 dated progress notes in gate G3's block (BASELINE through BUILD-INFO-FACET). They are moved unchanged to .outline/notes/gates-progress-2026-09.md, and the block now points at the report. Nothing parses those lines; the test that pins GATES.md checks the RECEIPTS line, which stays.
The committed-record check was one function of about 180 lines that mixed
what an obligation must state with how a failure is reported. The rules are
now a value per obligation. The same records are accepted and rejected.
Target cells were written for ELF under QEMU: `elf-object`, `qemu-functional`,
readelf wording, and a rule that exactly one cell may be native. Seven cells on
three object formats need one meaning per obligation and a format-specific
proof of it, so `elf-object` is now `target-object` and `qemu-functional` is now
`target-functional`, and each format states what shows a hardened link: RELRO on
ELF, an ad-hoc signed PIE on Mach-O, ASLR with DEP on COFF. Reproducibility now
covers the linked image as well as the object, which is how a macOS signature
is covered. The catalog grows to 49 identifiers.

A cell's class now comes from what can run its images: its own host, qemu-user
on Linux, or Rosetta on Apple Silicon. A record cannot claim another route.

The four Linux records are removed rather than renamed. Their obligations no
longer exist under those names, and evidence must be re-measured, not rewritten.
A cell with no record is unmeasured: `toolchain evidence` still refuses, and
naming them, and `suite run --catalog target-cells` gets a blocking row for each
instead of aborting the lane. A record that exists and fails is still an error.
Eighty-eight `formal-redex` identifiers carry more than one `::`, such as
`ecmascript/semantics.rkt::rule::property_get`, and `parse_logical_identifier`
rejected every one, so no receipt could ever be produced for them. The first
`::` now ends the locator.

The mapping itself moves into `logical_obligation`, because the ledger needs the
same answer and currently derives its own.
The ledger could not admit a single row from a real lane, for three reasons.
It looked a row up as `catalog:case`, but a receipt's case has the authority
prefix stripped and the configuration held apart, so every real row was an
unknown obligation. It expected the configuration without its observable. And
it bound every slot to the interpreter, while compiler lanes run as AOT and a
test262 case needs three modes, which it could only have read as duplicates.
Its tests passed because their fixtures built keys by hand in the same wrong
shape.

The index now maps each manifest identifier to its lane key through
`logical_obligation`, and the fixtures use that too. A row holds one outcome per
mode: it settles only when every mode its catalog declares has reported the same
state, so a JIT row without an AOT row leaves a test262 case incomplete. There
is still one ledger row per identifier, so modes do not grow the ledger. A
receipt in a mode the catalog does not run is rejected even when the row is
otherwise complete.

One test walks every committed manifest identifier through its lane key and back.
…lob limit

A full ledger in the v1 format would be 94 MB against GitHub's 100 MiB limit,
with no margin and half of it repeated text: `case` is the tail of `id`, and a
PASS row has four empty prose fields. Schema `bamti.completeness-ledger/v2` drops
`case` and omits empty prose, which brings a full ledger to about 48 MB, so it
does not need to be split by catalog.

A test measures the real thing: every identifier in the committed manifest
streamed as a PASS row, under 90 MB. It replaces an extrapolation from 2,000 rows.

A row is now three required fields. Prose stays mandatory on non-PASS rows, and a
row id must name a case in its own catalog. The committed v1 ledger is left alone
and now reads as stale, which it already was: a ledger lands only at root closure.
Both historical catalogs were owned by nothing: `observe_compiler_lane` blocks any
key outside `typescript-7.0.2`, so a sweep of either could only fail everywhere.

A historical cell now takes the 7.0.2 measurement when its input is byte-identical
to 7.0.2, including the project files for a `project/` case, and the receipt row
records `reused typescript-7.0.2 cell with byte-identical input`. All 19,344
6.0.2 inputs qualify. In 5.9.3, 7,476 of 19,253 do. A 5.9.3 cell whose input
differs has no measurement to take: a historical baseline is never an oracle, so
it stays a blocking row saying why until it is run against the 7.0.2 oracle.

`nightly` now sweeps both catalogs.
A3.1 G6 still said receipts were structurally blocked. Receipt landing has since
been shown end to end, with a control, so the note is corrected. The gate stays
open.
`TargetDescriptor::lookup` accepted only the four Linux triples, so the macOS
and Windows target cells could not emit their object through the production
path. Each registered target now emits its own format: ELF, Mach-O, or COFF.
Target-cell evidence states facts about a file, and the old records took them
from the flags it was linked with and from readelf text. This reads ELF64 and
Mach-O 64 headers directly: machine, type, W+X regions, RELRO, the stack header,
the dynamic section, and the PIE flag. A file cut off inside a header is an
error rather than a shorter answer.
Target-cell records were hand-run shell output: `generate_cell` only formatted
values passed to it, and the start and helper objects those runs linked were
never committed, so no cell could be measured again.

`toolchain measure --target <triple> [--write]` now does the whole job. It emits
the probe twice through production codegen, links it twice with the committed
freestanding support, runs it natively or under the cell's emulator, and reads
the image's own headers, so a claim like RELRO is what the file declares rather
than what the linker was asked for. A step that fails leaves a gap for
`generate_cell` to classify, and `--write` refuses any record the committed-record
loader would refuse.
Measured with `toolchain measure --write`, each 7/7: aarch64-apple-darwin native,
aarch64-unknown-linux-gnu native in the bamti-arm VM, and s390x and riscv64gc
under qemu-user there. x86_64 Linux needs an x86_64 rustc, Intel macOS needs
Rosetta, and Windows needs native output, so those three stay unmeasured.
Every failing test262 row read `ExpectationMismatch { expected: "success", actual:
"Runtime/Test262Error" }`, so 111 different assertions in one shard formed one
family and a fix loop had nothing to start from.

The runtime now reads the thrown object's own `message` data property where it
already resolves the constructor, without running guest code. Rows read
`expected success, got Runtime/Test262Error: Expected SameValue(«1», «2»)`, and
parse rejections name the diagnostic. The message is never part of a verdict.
An engine ReferenceError for an unknown global is thrown with no object behind it,
so a report could only say `global is not defined`, and 144 rows of one test262
shard shared that text. `load_global` now keeps the name, and the reporting
boundary turns it into `<name> is not defined`.
The scanner pre-scans a file before the parser sees it, and read every `/` as
division. In `${key.replace(/'/g, "x")}` the quote then opened a string that ran
past the `}`, so the template never closed and the file failed with `unterminated
string literal`, whatever the parser did afterwards. This one line in test262's
`temporalHelpers.js` stopped all 2,809 tests that include it from compiling.

The default pass now forms a regex when the token before `/` cannot end an
expression, which needs no grammar: there is nothing to divide. It leaves `)`,
`}`, `]`, `!` and `>` to the parser, and `<`, which opens a closing JSX tag. A
regex that does not close on its line stays a slash, so the pass reports nothing
new. The corpus test no longer waives clean lexing for regex files.
`class C { a() {}; }` is valid, and the parser stopped at the `;` with `expected a
property name`. In one test262 shard that was 11 rows, all the same bug.

A `;` is now its own `ClassMember::Semicolon`, appended to the enum, rather than
skipped, because tsc keeps it in JavaScript output and drops it from declarations.
It declares nothing, so every other pass ignores it.
Each composed script was written to a scratch directory as `composed-<n>.js`, so
every relative import in a test failed with `cannot resolve "./x_FIXTURE.js"`, and
a module test importing itself by name failed too. Those are harness failures
reported against the engine, across the 947 test files that use fixtures.

A case now gets its own directory, under its test's file name, beside copies of
the `_FIXTURE` files from the test's directory.
Class members were installed through the object-literal path, so `for (k in
C.prototype)` listed every method, and each class test checking a descriptor
failed. Methods now use `DefineDataProperty`, which is already writable,
non-enumerable and configurable. Accessors need the same, so `AccessorKind` gains
`ClassGetter` and `ClassSetter`, with new wire tags and helper selectors. An old
reader rejects those tags rather than misreading them.
…y in JavaScript

Outside a generator, sloppy JavaScript reads `yield` as an identifier, but the
parser always read a yield expression, so `yield = 0` failed. And `static
constructor() {}`, `async constructor() {}` and `*constructor() {}` all became the
class constructor, losing what they were: the first is an ordinary static method,
and the others must be rejected, which needs the method to survive.

TypeScript sources keep their current shape, because the checker reports these
there and its diagnostics are held to the baselines.
`es2020`, `es2022`, `preserve` and the Node modes failed every file with
TS-EMIT-1501, including `target: es2020`, which implies that module, and the
`nodenext` of our own `tsconfig` template. A Node mode now emits per file: the
extension decides, then the nearest `package.json` `type`.
Upstream emits each require at its statement, so `a(); import './b'` runs in
that order. Ours hoisted them into a prelude. The lines now print through a
marker statement in the same position, which also keeps their comments.
`./m.js` produced `const m.js_1`, a syntax error in every CommonJS file that
writes its import extensions.
A builtin's Rust locals are invisible to the collector, and a callback can reach
a collection, so `[..].toSorted(cmp)`, `Object.groupBy`, `f(...gen())` and many
others died with an uncatchable `heap slot does not exist`. Hand rooting reached
few of them. Everything a builtin allocates or gets back from user code now stays
rooted until it returns, in one place every tier passes through. A running
microtask leaves the queue, so it is rooted too, which fixes `promise.then`.
`'ab'.repeat(0)` returned `'ab'`.
`Math.abs(new Number(-1))` was NaN: these used the coercion that cannot run user
code, so an object never reached `valueOf`.
A class without `new` ran as a function, and a derived one died uncatchably, so
constructors now throw a TypeError first. A static field assigned, so the
stricter function `name` and `length` threw at class definition. Fields of an
undecorated class now define; decorated classes keep assigning, as tests pin.
An import used only as a type stayed in the output, so an ES module naming an
interface failed at load, and CommonJS required files it did not need. A binding
now survives only if the file reads it as a value, found by the scan lowering
uses, extended over JSX, decorators and catch defaults. JavaScript files and
`verbatimModuleSyntax` keep every import, as tsc does.
Native fields keep a class's heritage in place, so `class D extends b { [k] = 1 }`
panicked debug builds.
A re-exported import copied its value once, so importers missed later updates.
A named one is now a live getter at its require, as upstream emits, and default
and namespace imports assign there. String export names use `exports["a b"]`,
which also stops `export { fn as "a-b" }` from vanishing and `exports.a b` from
being a syntax error. `commonjs_export_specifiers_read_what_the_name_became` is
corrected to tsc's output: this series added that expectation.
`export { I }` of an imported interface, `export { I } from`, and a const enum
named in `export` all failed at load. A file cannot tell these from values, so
the program now hands emit the names each dependency declares only as types or
as const enums. `export { type T } from` under CommonJS no longer requires the
module. `.d.ts` targets are covered the same way.
A catch pattern's default went unchecked, so `catch ({ c = missing })` reported
nothing, and it skipped the rewrite, so a namespace export there read a bare name.
Keeping every callback result held each intermediate of `reduce`, so joining
6000 lines hit the heap limit. Builtins now keep their latest 32 results, and
keep without limit only what they allocate or read through a getter, a trap or
an iterator, which they may be collecting into a Rust list. Nested native code
stays in the outer scope, so a trap's result survives its dispatch. Operands of
a binary operator are covered too: converting one can collect the other.
Elision trusted scope analysis, so `function f(a = x) { var x }` lost its import
and threw. Every value read now counts, shadowed or not, and aliases count at any
depth. What another file erases now comes from its exports, not its scope, so
`export { value as Foo }` beside `interface Foo` stays and a type stays erased
through barrels and `export *`. `export default T` of a type goes with its import,
and `jsx: preserve` keeps the factory.
`export { x }` copied the value once at that statement, so a later write left
importers stale and an export above its declaration hit the TDZ. A local now
publishes right after its declaration and on every write, in tsc's forms, and
`export =` is `module.exports` with no `__esModule` marker.
`function f(this: any, a) {}` took its first argument as `this`, so `f(5)` left
`a` undefined when run. JavaScript emit already dropped it.
Node decides a file's format from the nearest `package.json`, wherever it is, so
`type: module` above `tsconfig.json` emitted CommonJS.
`export { s as 'quo"te' }` printed an unterminated string, an escaped name was
escaped twice, `x²` became `exports.x²`, and `import { "a b" as ab }` left `ab`
unbound.
`import { type I } from`, `import {} from` and `export { type T } from` still run
their module under that option, so they print with an empty list.
When `shutdown` won the race, `offer` dropped a frame the reader had already
parsed, so that client never got a reply, and the test covering it failed under
load. The request now reaches the queue, and the drain, which runs after the
reader exits, cancels it.
The 32-result window lost values builtins were still collecting, such as nested
`toJSON` results and `TypedArray.from` mappers, and they died uncatchably. Results
now stay rooted by default, and only loops that use each result once, `forEach`,
`map`, `reduce` and their like, take the bounded `call_callback`.
An explicit export now shadows what `export *` brings, `export declare` and
`import type` count as types, and a local value outranks an erased import of its
name. `export =` runs after the module body, an ambient publishes nothing, and
native constructor parameters are rewritten like any other.
`select_payload` strips an archive's single root, but macOS can write `.DS_Store`
into any new directory, so every source landed one level deeper and every
authority path broke.
Iterator helpers and the TypedArray predicates use each result once, so they
take `call_callback`. `values().reduce` over 6000 lines hit the heap limit.
A local declared beside an erased import is what an export means, including for
the importing module, and `import type` no longer hides it. An ambient namespace
can hold values, so it stays, and `declare class` publishes nothing.
tsc rejects reading a type as a value, so a read proves the name is a value, and
another file's exports can only mislead: ambient modules, augmentation and
export conflicts all made a read import vanish. Reads now always keep their
import, inlined const enum uses do not count, and only `export { x }` and
`export default x` ask the program. Files whose imports are read unseen, through
`@jsx`, `emitDecoratorMetadata` or `reactNamespace`, keep them all. An explicit
export outranks a same-named type, and `export import x = require()` publishes.
@rabbitson87 rabbitson87 changed the title Mac-first completion: platform crate, AArch64 tiers, emit and runtime conformance Mac-first completion: platform crate, AArch64 tiers, emit and runtime conformance, review fixes Nov 2, 2026
The harnesses use per-fact readers, but both condition tests still compared
against `machine_facts` and `thread_placement`, which also ask for the NUMA
policy, so they failed in a sandbox that blocks `get_mempolicy`. The stage 0
test had only handled a missing governor.
E3.5, E4.7 and E4.8 own files that do not exist yet, as E4.2 does. Each now names
the card that writes it, so an ownership report reads as an open leaf and not
as an orphan.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant