Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
172a4ce
docs(brief): add m1.1.14 milestone brief
guysenpai Aug 14, 2026
a35fd30
docs(brief): confirm specs read for m1.1.14
guysenpai Aug 14, 2026
522c129
docs(brief): activate m1.1.14
guysenpai Aug 14, 2026
accf8f0
feat(math): add the deterministic cosine (ARCH-031 rule 4)
guysenpai Aug 14, 2026
ff950b3
feat(platform): install the engine float environment on each thread
guysenpai Aug 14, 2026
ed16f7d
feat(forge): assert the float environment at the physics entry point
guysenpai Aug 14, 2026
81227fa
feat(forge): drop the libm cosine from the character controller
guysenpai Aug 14, 2026
b7915b3
chore(ci): pin the CPU feature set on every cell
guysenpai Aug 14, 2026
86cfb7f
docs(brief): journal update for m1.1.14 gate A
guysenpai Aug 14, 2026
a92f6d6
fix(tools): close two false negatives in the asm inventory
guysenpai Aug 14, 2026
0d60025
refactor(math): move float_env under the forge_3d dependency whitelist
guysenpai Aug 14, 2026
35b34b7
docs(brief): journal the gate A review round
guysenpai Aug 14, 2026
bba33f4
fix(math): use the only MXCSR constraint pair both backends accept
guysenpai Aug 14, 2026
420ef53
docs(brief): journal the first matrix run and its finding
guysenpai Aug 14, 2026
0088fea
docs(math): render the C1.1 citation in English with its pointer
guysenpai Aug 14, 2026
e893003
fix(forge): prune the retained pair set on fat-AABB separation
guysenpai Aug 14, 2026
15b5a23
test(forge): add the canonical determinism scenario
guysenpai Aug 14, 2026
b5b54df
feat(forge): add the determinism instrument and its build step
guysenpai Aug 14, 2026
042f149
test(forge): pin the divergence threshold to the per-body scale
guysenpai Aug 14, 2026
95df2ba
chore(ci): add the determinism witness generation workflow
guysenpai Aug 14, 2026
d8ac0a4
docs(brief): journal the gate C provenance blocker
guysenpai Aug 14, 2026
ec56be0
chore(ci): gate witness generation on a commit trailer
guysenpai Aug 14, 2026
80de9d9
fix(ci): read the trailer from the PR head, not the merge commit
guysenpai Aug 14, 2026
16b3b88
chore(ci): retain both sides of the cross-mode measurement
guysenpai Aug 14, 2026
4e3551c
docs(brief): journal the gate C cross-mode blocker B2
guysenpai Aug 14, 2026
a61ea1c
fix(math): fold float lane reductions in source, not via @reduce
guysenpai Aug 15, 2026
3ed5804
test: sweep the dead tests before any witness is generated
guysenpai Aug 15, 2026
824a79d
fix(render): own the pass resource slices in the config
guysenpai Aug 15, 2026
9389d0d
fix(core): guard the shm_posix wire-in by target OS
guysenpai Aug 15, 2026
761728b
feat(lint): add the dead-test closure analyser, not yet wired
guysenpai Aug 15, 2026
d486d71
fix(ci): pin the CPU axis in the Zig cache key
guysenpai Aug 15, 2026
9e8bd78
fix(ci): make the Zig cache save all-or-nothing
guysenpai Aug 15, 2026
a12984d
test(lint): restrict the dead-test fixpoint to live files
guysenpai Aug 15, 2026
be11898
docs(brief): close out the session with the measured facts
guysenpai Aug 15, 2026
5b12ccb
docs(lint): record the module-boundary edge for the resumption
guysenpai Aug 15, 2026
02a6cc7
fix(lint): close five false-alive defects in the dead-test closure
guysenpai Aug 16, 2026
b5e3b89
feat(lint): add the closure control and activate the dead-test guard
guysenpai Aug 16, 2026
479e507
docs(brief): journal the closed-list items 1 to 5
guysenpai Aug 16, 2026
e97b0cc
chore(ci): record the reported zig version and per-witness mode
guysenpai Aug 16, 2026
662cf07
fix(lint): declare the closure gap per platform, measured not inherited
guysenpai Aug 16, 2026
534bbdb
feat(forge): commit the determinism witness set
guysenpai Aug 16, 2026
7e04375
feat(forge): read and compare the committed determinism witnesses
guysenpai Aug 16, 2026
a7b3d54
docs(brief): journal the witness set and its reader
guysenpai Aug 16, 2026
3fc21e5
chore(ci): run the determinism harness on every matrix cell
guysenpai Aug 16, 2026
9d63eba
docs(brief): close gate C on its measurement, not on a green tick
guysenpai Aug 16, 2026
8fec1d1
chore(ci): raise the cache pre-check to measure who actually caps it
guysenpai Aug 16, 2026
571814b
chore(ci): add the ubuntu-24.04-arm cell and record the cache result
guysenpai Aug 16, 2026
c1ee5ee
docs(brief): record the gate D blocker on the arm64 cell
guysenpai Aug 16, 2026
9a31746
fix(ipc): declare the shm mapping at page_size_min, not pageSize()
guysenpai Aug 16, 2026
a18b408
test(ci): plant a temporary cell-level counter-factual for lint
guysenpai Aug 16, 2026
c3d6073
test(ci): remove the counter-factual, cell reddening observed
guysenpai Aug 16, 2026
9ee4eb2
docs(brief): record the warm timings and the f64 axis constraint
guysenpai Aug 16, 2026
4290b31
fix(ci): apply the cache size guard to both save steps, and unrestrict
guysenpai Aug 16, 2026
dafe122
chore(ci): record the cache archive size in the timing report
guysenpai Aug 16, 2026
bd29cfe
fix(forge): state the residual discriminant in exact ULP form
guysenpai Aug 16, 2026
a5ee88e
feat(ci): build the f64 axis, and restore a step that had vanished
guysenpai Aug 16, 2026
3a71e74
docs(brief): record the twelve-cell matrix and its verified verdicts
guysenpai Aug 16, 2026
9e23dd9
docs(brief): close M1.1.14 and patch CLAUDE.md inside the PR
guysenpai Aug 16, 2026
2c29c7c
fix(forge): regenerate witnesses on mismatch, install env at ten sites
guysenpai Aug 17, 2026
dc22a72
docs(math): state the install rule instead of enumerating its sites
guysenpai Aug 17, 2026
e851ff0
fix(forge): give the determinism scenario a walk, a slope, an observer
guysenpai Aug 17, 2026
840b523
feat(forge): re-baseline the determinism witnesses on the fixed scenario
guysenpai Aug 17, 2026
6f4dc46
test(math): pin the deterministic cosine to an oracle that is not @cos
guysenpai Aug 17, 2026
d627b89
test(forge): make the fourth trace an oracle, refuse a bad window
guysenpai Aug 17, 2026
125bb89
docs(brief): patch CLAUDE.md on the measured state, and audit the close
guysenpai Aug 17, 2026
7909898
docs(brief): record three CI infra causes and one wrong expectation
guysenpai Aug 17, 2026
7732fed
fix(lint): execute the dead-test conservation instead of printing it
guysenpai Aug 17, 2026
3326202
fix(ci): run the dead-test conservation on every cell, not one
guysenpai Aug 17, 2026
d237f82
test(ci): deliberate red — prove the passed total reaches the cell
guysenpai Aug 17, 2026
a4f6e82
fix(ci): restore the honest total and declare the step shell
guysenpai Aug 17, 2026
a6baad3
docs(brief): record the final head, the red run and three identifiers
guysenpai Aug 17, 2026
8116831
test(ci): deliberate red — prove the windows constant is confronted
guysenpai Aug 17, 2026
2cc1b49
fix(lint): restore the windows constant, second negative witness done
guysenpai Aug 17, 2026
d02e4b7
docs(brief): record the prediction that did not match its observation
guysenpai Aug 17, 2026
944bee6
fix(forge): survive a structural regen, and make the traces vary
guysenpai Aug 17, 2026
3c78c8d
chore(forge): intermediate local witnesses, replaced by the next commit
guysenpai Aug 17, 2026
db34f86
docs(brief): record the regeneration deadlock and re-carry the trailer
guysenpai Aug 17, 2026
0db05fd
feat(forge): authoritative witnesses, and the chain always measures
guysenpai Aug 18, 2026
1d03885
fix(forge): close the nine-element sweep, sensor and groups in scope
guysenpai Aug 18, 2026
47dfbec
fix(forge): sensor discriminations per set, groups by membership
guysenpai Aug 18, 2026
400a7b4
fix(forge): assert the sensor record on the chain dump, not the helper
guysenpai Aug 18, 2026
28a598b
docs(forge): unattribute the coarse island probe, sweep element indices
guysenpai Aug 18, 2026
5b05003
test(forge): observe the sleeper by identity, element 3 by membership
guysenpai Aug 18, 2026
8e2f447
test(forge): bound the discrete observations to the compared window
guysenpai Aug 18, 2026
44a8953
test(forge): read an identity per element, run the claimed span
guysenpai Aug 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
586 changes: 556 additions & 30 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions .github/workflows/nightly-fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,16 @@ name: Nightly IPC fuzz
# promoted to nightly CI at M0.7 / E4. Runs on Linux + Windows and
# archives the stdout digest as an artifact (G3 gate). Scheduled runs
# only fire from the default branch (GitHub rule), so this activates once
# the M0.7 branch is squash-merged to `main`; `workflow_dispatch` lets it
# be triggered manually from the Actions tab in the meantime.
# the M0.7 branch is squash-merged to `main`.
#
# M1.1.14 correction, MEASURED: the sentence that stood here — that
# `workflow_dispatch` lets it be triggered manually from the Actions tab in the
# meantime — is FALSE, and had been dormant since M0.7 because nobody tried it
# from a branch. `gh workflow run <file> --ref <feature-branch>` returns
# `HTTP 404: workflow not found on the default branch`: a `workflow_dispatch`
# workflow must ALSO exist on the default branch before it can be dispatched at
# all, whatever `--ref` says. Dispatch is available for this file today only
# because it is already on `main`.
on:
schedule:
# 04:00 UTC daily — off-peak for the shared runner pool.
Expand Down
36 changes: 26 additions & 10 deletions CLAUDE.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bench/forge_3d_mesh.zig
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ pub fn main() !void {
rand.float(Real) * 2 - 1,
rand.float(Real) * 2 - 1,
});
if (@reduce(.Max, @abs(raw.data)) == 0) continue;
if (raw.maxAbsComponent() == 0) continue;
const unit = raw.scale(1 / raw.length());
const q = query.RayQuery{
.origin = unit.scale(60),
Expand Down
79 changes: 79 additions & 0 deletions briefs/artifacts/m1.1.14-zig-reduce-order-issue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Upstream issue draft — `stage2_x86_64` reorders a floating-point `@reduce`

> **Status: NOT FILED.** Filing is an outward-facing act and belongs to the repository
> owner. This file is the prepared text and its evidence; the reproducer it refers to
> is `briefs/artifacts/m1.1.14-zig-reduce-order-repro.zig`, self-verified.

## Title

`stage2_x86_64` does not honour the specified order of a floating-point `@reduce`

## Toolchain, exactly

`zig version` reports **0.16.0**. That is **not a public upstream release**: `ziglang/zig`
carries no `0.16.0` tag (the ref lookup 404s), its newest release is `0.15.1` and its
newest tag `0.15.2`, over 24 tags. So this is reported **against master**, not against a
release, and the compiler under test is whatever `0.16.0` build the distribution shipped
(Homebrew `zig 0.16.0_1`, labelled `stable` on its side). Host `aarch64-macos`,
cross-compiling; the defect is in the `x86_64` self-hosted backend, not in the host.

## What the language specifies

From the langref on **master** (verbatim, and identical at tag `0.15.2`):

> Transforms a vector into a scalar value (of type E) by performing a **sequential
> horizontal reduction** of its elements using the specified operator

> Note that `.Add` and `.Mul` reductions on integral types are wrapping; when applied on
> floating point types **the operation associativity is preserved**, unless the float mode
> is set to `Optimized`.

The sequential fold of a 3-lane vector `p` is therefore `(p₀ + p₁) + p₂`.

## What the two backends emit

Both built at `-target x86_64-linux-gnu -mcpu=baseline`, from the same source file, on a
3-lane `f32` reduction:

| backend | instruction sequence | fold |
|---|---|---|
| `stage2_llvm` (`-fllvm`) | `movaps` seeds `p₀`; `shufps $0x55` → `p₁`; `addss`; `unpckhpd`/`movhlps` → `p₂`; `addss` | `(p₀ + p₁) + p₂` ✅ |
| `stage2_x86_64` (`-fno-llvm`) | `movhlps` → `p₂`; `addss p₀`; `shufps $0x1` → `p₁`; `addss` | `p₁ + (p₂ + p₀)` ❌ |

`p₁ + (p₂ + p₀)` is neither sequential in lane order nor an associativity-preserving
rendering of the sequential fold, so it contradicts both halves of the specified sentence.

## It is not the float mode

The sequence is **identical, instruction for instruction**, under the default float mode
and under an explicit `@setFloatMode(.strict)`, at `-O Debug`, `-O ReleaseSafe` and
`-O ReleaseFast` — six listings. That rules out a mis-propagated float mode as the cause,
and it rules out the specification's own `Optimized` escape.

The reproducer places `probe()` and `expected()` in **one object, compiled by one backend
in one float mode**: `expected()` — which writes the fold in source — emits the plain
sequential `addss` chain that `probe()` does not. So the backend is able to produce the
specified order and does when the source states it.

## Why it is observable

Floating-point addition is not associative. The two orders disagree on **313 816 of
1 000 000** random `f32` triples drawn uniformly from `[-50, 50]` — **31.4 %** — by one
ULP. First witness `(-12.127813, 21.12078, -40.04462)`: `(p₀+p₁)+p₂` = `0xC1F869C9`,
`p₁+(p₂+p₀)` = `0xC1F869C8`.

In the reporting project — a physics engine whose exit criterion is bit-exact replay — this
surfaced as the simulation state diverging between a Debug build and a ReleaseSafe build of
the same commit on the same machine, at the first stepped frame, amplifying to 2.53e-3 m
after 38 frames. At `f64` both backends produce `(p₀ + p₁) + p₂` (LLVM by
`mulpd`/`unpckhpd`, the self-hosted backend by a sequential `addsd` loop over memory), which
is why only the `f32` leg diverged.

## Reproducing

```
zig build-obj repro.zig -target x86_64-linux-gnu -mcpu=baseline -O Debug -fllvm
zig build-obj repro.zig -target x86_64-linux-gnu -mcpu=baseline -O Debug -fno-llvm
```

then disassemble `probe` in each object and compare against `expected` in the same object.
46 changes: 46 additions & 0 deletions briefs/artifacts/m1.1.14-zig-reduce-order-repro.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! Minimal reproducer — `stage2_x86_64` does not honour the specified order of
//! a floating-point `@reduce`. Prepared at M1.1.14, NOT yet filed upstream.
//!
//! The langref states that `@reduce` performs "a sequential horizontal reduction
//! of its elements", and that on floating point types "the operation
//! associativity is preserved, unless the float mode is set to `Optimized`".
//! The sequential fold of a 3-lane vector is `(v₀ + v₁) + v₂`.
//!
//! Build both ways and disassemble `probe`:
//!
//! zig build-obj repro.zig -target x86_64-linux-gnu -mcpu=baseline -O Debug -fllvm
//! zig build-obj repro.zig -target x86_64-linux-gnu -mcpu=baseline -O Debug -fno-llvm
//!
//! LLVM emits `(v₀ + v₁) + v₂`:
//! movaps seeds v₀ ; shufps $0x55 → v₁ ; addss ; unpckhpd → v₂ ; addss
//!
//! `stage2_x86_64` emits `v₁ + (v₂ + v₀)`:
//! movhlps → v₂ ; addss v₀ ; shufps $0x1 → v₁ ; addss
//!
//! That is a permutation AND a reassociation, so it contradicts both halves of
//! the specified sentence. Identical output under the default float mode and
//! under the explicit `@setFloatMode(.strict)` below, at `-O Debug`,
//! `ReleaseSafe` and `ReleaseFast` — six listings, instruction for instruction.
//!
//! Observable consequence, since float addition is not associative: the two
//! orders disagree on 313816 of 1000000 random f32 triples drawn uniformly from
//! `[-50, 50]`, by one ULP. First witness `(-12.127813, 21.12078, -40.04462)`:
//! `(v₀+v₁)+v₂` = `0xC1F869C9`, `v₁+(v₂+v₀)` = `0xC1F869C8`.
//!
//! Measured with Zig 0.16.0 on an `aarch64-macos` host, cross-compiling.

/// The reduction under test. `@setFloatMode(.strict)` is explicit so the
/// specified "unless Optimized" escape cannot account for the difference.
export fn probe(a: *const [3]f32, b: *const [3]f32) f32 {
@setFloatMode(.strict);
const va: @Vector(3, f32) = a.*;
const vb: @Vector(3, f32) = b.*;
return @reduce(.Add, va * vb);
}

/// The sequential fold the language specifies, for comparison in the same object.
export fn expected(a: *const [3]f32, b: *const [3]f32) f32 {
@setFloatMode(.strict);
const p: @Vector(3, f32) = @as(@Vector(3, f32), a.*) * @as(@Vector(3, f32), b.*);
return (p[0] + p[1]) + p[2];
}
Loading