Add Artemis stress test for detecting cache aliasing bugs. Include statistical hypothesis evaluations, fix validation data, and run reports for validation across architectures.

Signed-off-by: Robert Allan James <robert.allan.james@gmail.com>
This commit is contained in:
Robert Allan James
2026-08-02 14:49:52 -04:00
parent 148c4aa12c
commit 1cb68502fb
44 changed files with 5610173 additions and 2680 deletions
+29
View File
@@ -177,6 +177,35 @@ Acceptance criteria status, verified 2026-08-02:
Not done: everything under "FUTURE MATERIAL" below (zones, USB hot-plug,
ACL records, PKI) — that line remains accurate and unchanged.
### Surface Stress Test — permanent fixture, found a real bug (2026-08-02)
`ART-STRESS-TEST ( seed reps -- )` (`capsules/artemis/init.4th`, blocks 41604173) is a permanent, on-demand exercise of
the block-storage path across the whole data pool: `reps` distinct random blocks (coprime-stride walk, collision-free,
never touches anything already allocated by someone else), written, then verified after all writes complete so real
cache-eviction traffic gets forced through both caching layers. Every trial emits a `[ARTSTRESS]`-tagged CSV row to the
serial log (`HEADER`/`SUMMARY` markers make a truncated run unmistakable). Invoke as
`ART-STRESS` (fixed seed, 50 reps) or `<seed> <reps> ART-STRESS-TEST`. Not wired into the boot sequence — block 4170
carries the entry point commented out (`\ ART-STRESS`), same disable-by-default convention as
`ACL.4th`'s self-activation toggle, so ordinary boots pay nothing for it.
Its first completed run found a real, pre-existing data-corruption bug on amd64: 25 of 50 trials (50%) returned the
wrong byte pattern. Root cause, confirmed by source review and fixed in `src/word_source/block_words.c`
(not Artemis-specific — this is shared block-subsystem plumbing every VM using `BLOCK`/`UPDATE`/`FLUSH` goes through):
the VM-level "window" cache (`vm->blk_vm_cbuf[]`) stores a raw pointer directly into one of the block-subsystem's eight
`cache_slot_t` devblock-cache entries (`block_subsystem.c`). That cache's own FIFO eviction relocates entries via
struct-copy (`slot->cache[i] = slot->cache[i+1]`) once more than eight distinct devblocks are touched in one VM
session — the *addresses*
of `cache[0..7]` never move, but what they hold does, silently, aliasing any window pointer captured before the shift.
No prior test had ever touched enough distinct devblocks in one session to trigger it. Fixed by re-resolving the buffer
pointer by logical block number (`blk_get_buffer`)
immediately before every write-through-cache-pointer operation in
`block_word_update`, `blk_vm_evict`, and `blk_vm_flush_all`, instead of trusting a pointer captured earlier — confined
to the consumer side, no change to the block subsystem's cache architecture. Independent of, and predates, the
`BLK-CONFIRM-FORMAT` fix above; neither touches the other's code path. Re-verified 50/50 on amd64, aarch64, and riscv64
after the fix. Full write-up with hypothesis-tested analysis (a naive "consecutive writes fail together" reading did not
survive a formal runs test — see the report for what did and didn't hold up):
`experiments/artemis_stress/analysis/report/artemis_stress_report.pdf`.
---
## What Artemis Owns
+221
View File
@@ -0,0 +1,221 @@
# CONSOLE.md — Console VM Architecture (DRAFT)
# StarshipOS / StarForth — Captain Bob (Robert Allan James)
# This document constrains Claude Code behavior once Console work begins. Read it
# completely before touching any Console code.
#
# STATUS: design-stage draft, 2026-08-02. Nothing described here is implemented yet.
# This is the output of a planning conversation, captured so it can keep being
# refined instead of living only in chat history. Do not treat anything below as
# "already built" — there is no Console code in the tree yet.
---
## The Pivot This Document Reflects
Console was first framed as "wrap the existing bitmap/VT100 framebuffer driver in a 4th Tripod VM." That framing is
superseded. The framebuffer is now treated as a **drawing fabric**: full-color, Cartesian-addressed, rasterized-vector —
not a character-cell bitmap terminal. Text is one thing drawn on the fabric, not a special case. Rationale (Captain Bob,
verbatim intent): everything in the eventual GUI is going to be drawn anyway, so the console should be drawn the same
way from the start rather than built on a bitmap terminal that gets thrown away later.
The existing `src/starkernel/hal/framebuffer.c` (raw UEFI GOP pixel plotting) is still the right low-level substrate —
that part is reused. `font_8x16.c` and
`vt100.c` (the bitmap-cell VT100 emulator built on top of it) are what's being superseded; see Open Decisions below for
their fate.
---
## Language Constraint — Non-Negotiable
All Console implementation is in StarForth dialect ONLY, with the same narrow exception every other Tripod VM has: raw
hardware access (the actual pixel write into GOP framebuffer memory) is C, registered directly as Forth primitives —
exactly how `BLOCK`/`UPDATE`/`FLUSH`/`RANDOM`/`SEED` already work. Everything above that raw pixel-write boundary —
glyph composition, cursor logic, dirty-cell tracking, VT100 semantics, fonts — is StarForth.
C99 beyond that narrow hardware boundary is forbidden unless explicitly blocked in StarForth AND explicit written
permission is given by Captain Bob for that specific construct. "I could not figure out how to do this in StarForth" is
not permission. Ask first. Wait for the answer.
---
## When Stuck — Non-Negotiable
If you are stuck, STOP.
Do not confabulate. Do not invent dictionary words that do not exist. Do not fabricate primitives. Do not guess at
behavior. Do not work around the problem silently.
STOP. Report exactly what is blocking you. Wait for instruction.
---
## What Console Is
Console is the fleet's fourth leg — Quadrupod, not Tripod. A real StarForth VM, spawned by Hera, participating in fleet
K≡1.0 like Hermes and Artemis. Its contract:
Console owns the drawing fabric (the framebuffer) and, in a later phase, keyboard input. It renders. It does not route
messages (Hermes), does not own persistent storage (Artemis), and does not make VM lifecycle decisions (Hera).
Mythological name: **not yet chosen.** Candidates raised (Argus, Iris, Hestia) were not settled on. Do not pick one
unilaterally when implementation starts — ask Captain Bob. This document uses "Console" as a working label until named.
---
## Coordinate System
True Cartesian: **origin bottom-left, Y increases upward.** This is a firm decision, not a placeholder. The
framebuffer's native memory layout is top-left origin, Y-down (standard raster convention) — every drawing primitive
that touches the framebuffer must apply the Y-flip transform. Get this at the lowest primitive layer so nothing above it
ever has to think about it again.
---
## Primitive Layer — C, registered directly as Forth words
Raw, hardware-touching, absolute-coordinate. Not turtle/relative — see Font & Pen Layer below for why the split falls
here.
- `PLOT ( x y color -- )` — set one pixel (post Y-flip transform)
- `LINE ( x1 y1 x2 y2 color -- )` — Bresenham line between two absolute points
Absolute coordinates were chosen deliberately: the long-term direction (wireframes — triangles/quadrangles, eventually
textured/shaded) is fed by projected vertex data, which is naturally absolute per-vertex, not relative turtle deltas.
Building the hardware boundary around absolute coordinates means the eventual triangle-fill / texture-sample primitives
extend this layer instead of fighting it.
**Performance principle — non-negotiable:** the actual per-pixel work (Bresenham walk, eventually triangle
rasterization, texture sampling) must live in this C layer, compiled. Forth orchestrates — decides what to draw and
when — but never walks pixels one at a time through the interpreter. This is the same C-does- hardware /
Forth-does-policy split every other Tripod VM already uses; it is not a new rule invented for Console, just applied
here.
Known unknown: actual achievable frame rate. Feasible in principle (this is how every pre-GPU software-rasterized engine
worked), but unmeasured, and QEMU TCG (the only acceptance environment per `.claude/CLAUDE.md`) will be meaningfully
slower than real hardware for CPU-emulated per-pixel work. Do not assume a frame-rate target without measuring on the
actual target environment first.
---
## Font & Pen Layer — StarForth, composable
Fonts are not a C data table. Each glyph is a StarForth word, e.g.
`GLYPH-A ( x y -- )`, expressed as a short sequence of pen strokes relative to the glyph's origin — a small pen DSL
(`STROKE dx dy`, pen up/down, within a unit em-box) that resolves down to calls into the absolute `LINE` primitive. This
is deliberately hackable: a font is just another `.4th` capsule, content-addressed and hash-verified exactly like every
other capsule in this system. Swap the capsule, get a different typeface. No new loading mechanism — the existing
capsule system already does this.
This does not fight the performance principle above: glyph composition is a handful of word dispatches (515 strokes per
character), not per-pixel work. The expensive part — actually writing pixels — stays in the C `LINE` primitive
regardless of how the glyph was authored. Combined with heat-driven redraw (below), most glyphs aren't recomposed most
ticks anyway.
A thin turtle/pen convenience layer (position + heading state, `FORWARD`/`RIGHT`/
`PENUP`/`PENDOWN`) may be built on top of the same absolute `LINE` primitive for generative drawing use cases. It is
StarForth-only, costs nothing at the C layer, and does not change the hardware-boundary decision to stay absolute.
Phase 1 requires the **full printable-ASCII stroke-glyph table**, not a subset — a VT100-equivalent terminal needs to
render arbitrary text from day one. A narrower subset only made sense as a pipeline smoke test in isolation, which is no
longer the goal.
---
## Heat-Driven Redraw Model
Screen redraw is dirty-cell tracking, expressed in the same compudynamic vocabulary as everything else in this system —
not decorative, it's the actual mechanism.
- Each screen cell carries a heat value, structurally the same idea as `BLK-HEAT`
for Artemis's data blocks.
- A cell goes hot (`Q.1`) the moment its content changes.
- Console's own tick (driven off Hera's fleet heartbeat, same pattern as
`ART-TICK`) scans for hot cells only.
- For each hot cell, Console redraws it via the glyph/`LINE` primitives — and that redraw **is** the reap event. Heat
goes to zero because the work got done, not because of a time-based decay curve like Artemis blocks or Hermes
messages.
- Cells at K=0 are skipped entirely this tick — untouched pixels, no wasted work.
This cell-heat contributes to Console's share of fleet K, same as every other compudynamic object in the system. Reap
(redraw-to-zero) must rebalance K correctly, same invariant every other VM already honors.
---
## Immediate Goal
Scope for the first real implementation pass. Nothing past this section is authorized to build yet.
**The target:** a VT100-equivalent interactive terminal, rendered entirely through the vector fabric described above
(not the old bitmap driver), with:
1. Console spawns as the fleet's 4th leg under Hera's birth protocol, joins fleet K≡1.0 like Hermes and Artemis.
2. Full printable-ASCII stroke font, composable/StarForth-authored, loaded as a capsule.
3. Cursor positioning, basic scrolling, and enough VT100 semantics to be a usable terminal — the same functional bar the
current bitmap `vt100.c` already clears, just achieved through the new fabric instead.
4. Heat-driven redraw — only changed cells get rasterized per tick.
5. Text arrives at Console the same way everything else moves between Tripod VMs:
via Hermes. Console does not invent a second message-passing mechanism.
**Explicitly out of scope for this phase:** keyboard input (see Future Material — there is no keyboard driver anywhere
in this codebase yet, this is a separate, substantial piece of work), double buffering (open decision, see below), the
wireframe/textured/shaded GUI vision (that's the reason the fabric is built this way, but building it is a different,
later phase).
---
## Open Decisions — must be settled before or during implementation
1. **Mythological name.** Not chosen. Ask Captain Bob before implementation starts.
2. **Fate of `font_8x16.c` / `vt100.c`.** Kept only for pre-Console boot diagnostics (before Console VM is alive to take
over), or retired outright once the fabric is proven? Not decided.
3. **Double buffering.** Direct framebuffer writes will tear once Console is doing partial redraws every tick instead of
static boot text. Cheaper to decide now than to retrofit through every draw call later. Not decided.
4. **Actual frame-rate/performance numbers.** Unmeasured. Do not commit to a real-time target without benchmarking on
real hardware and under QEMU TCG separately — they will differ meaningfully.
---
## FUTURE MATERIAL — Not In Scope
Everything below is real direction, preserved intentionally, and is **not** part of the Immediate Goal above. Do not
build any of this without Captain Bob explicitly reopening it — same rule ARTEMIS.md and TRIPOD.md already hold their
own future chapters to.
### Full wireframe GUI
Triangles/quadrangles from projected vertex data, applied textures and shading, anti-aliased/high-resolution appearance
("not blocky") even at small polygon sizes. Visually in the spirit of vector-display arcade games (Asteroids),
rasterized in software onto the linear framebuffer — no GPU, no hardware acceleration available on this platform. The
absolute-coordinate `PLOT`/`LINE` primitive layer is designed so triangle-fill/texture-sample primitives extend it later
rather than replacing it.
### Keyboard input
No driver exists anywhere in this codebase today. PS/2 8042 controller is the pragmatic first target — QEMU supports it
cleanly. USB HID through the existing virtio/PCI stack is a materially bigger lift and should come later, if at all.
`M8: REPL keyboard input` in the top-level roadmap has been "planned, not started"
for the life of this project; this is the same gap.
### Interactive REPL on the framebuffer
Once keyboard input exists, point a real interactive REPL loop at Console instead of (or alongside) the serial `ok>`.
This is the piece that actually starts to make the system feel like an operating system rather than a kernel with a
serial console. Depends entirely on keyboard input landing first.
### Multi-window / multi-session
Not discussed in depth. Flagged here only so it isn't forgotten — if the fabric approach pans out, multiple independent
drawing regions/sessions is a natural next question once single-console VT100-equivalent behavior is solid.
---
*This document is a living draft, not yet authoritative in the sense TRIPOD.md/ ARTEMIS.md/HERMES.md are — those
describe implemented, verified systems. This one describes a plan. Update it as decisions get made; do not let it drift
out of sync with what actually gets built, the way the other three documents do not drift.*
+216 -201
View File
@@ -1,214 +1,229 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-08-02T15:36:37Z -->
<!-- Generated by mkcapsule --manifest 2026-08-02T18:02:36Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
## Capsule Summary
| Capsule | Blocks claimed | xxHash64 |
|---------|----------------|----------|
| `ACL.4th` | 4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4015 | `0xd781d22148ff171d` |
| `artemis:init.4th` | 4110, 4111, 4112, 4113, 4122, 4123, 4124, 4125, 4126, 4127, 4128, 4129, 4130, 4131, 4132, 4133, 4134, 4135, 4136, 4137, 4138, 4139, 4140, 4141, 4851, 4852 | `0xd622eb8ccb75f29a` |
| `common:msg.4th` | 4055 | `0xa99c5bcd3877f80e` |
| `doe-campaign.4th` | 4060, 4061, 4062, 4063, 4064, 4065 | `0x3d4549142d91ec20` |
| `doe.4th` | 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107 | `0xb6ecf5374e8ee77c` |
| `hermes:init.4th` | 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, 4109, 4114, 4115, 4116, 4117, 4118, 4119, 4120, 4121, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149, 4150, 4151, 4152, 4153 | `0xbb77f3ac463f507f` |
| `init-0.4th` | 2200, 2201 | `0xd0a9550baf786bb3` |
| `init-1.4th` | 4406, 4415, 4425, 4435 | `0x63e251adb0a03613` |
| `init-2.4th` | 4506, 4515, 4525, 4535, 4545 | `0xf113b3d0bcccae47` |
| `init-3.4th` | 4606, 4615, 4625, 4635, 4645, 4655, 4665 | `0x62b7a71576ad1041` |
| `init-4.4th` | 2130, 2131, 2132 | `0x89e3ef1db5ac0627` |
| `init-5.4th` | *(none — raw code capsule)* | `0xda0c179af10a46a9` |
| `init-6.4th` | 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095 | `0x06fc0ce1e369ef5a` |
| `init-7.4th` | 2150 | `0xb88df2efeb753d64` |
| `init-8.4th` | 2160 | `0x714746312b72f83d` |
| `init-9.4th` | 4706, 4715, 4725, 4735, 4745 | `0x3f2bec73142aa424` |
| `init-l8-diverse.4th` | 4820, 4821, 4822 | `0xaa293201a6c91838` |
| `init-l8-omni.4th` | 2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079 | `0x5979e314d6452045` |
| `init-l8-stable.4th` | 4806 | `0xdc3830f189063a9a` |
| `init-l8-temporal.4th` | 4830, 4831 | `0x51abd4c138246651` |
| `init-l8-transition.4th` | 4840, 4841, 4842 | `0xbcc1a81976f0a4c9` |
| `init-l8-volatile.4th` | 4810, 4811, 4812, 4813 | `0x98caabbbd92abac4` |
| `init.4th` | 2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059 | `0x7e54f972ef83ae23` |
| `lib.4th` | 4050 | `0x20625ebf1276c239` |
| `process.4th` | 4300, 4301 | `0x781afc1dbd0294f7` |
| `zuse.4th` | 4016, 4017, 4018 | `0x12f38ec782434a77` |
| Capsule | Blocks claimed | xxHash64 |
|--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------|
| `ACL.4th` | 4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4015 | `0xd781d22148ff171d` |
| `artemis:init.4th` | 4110, 4111, 4112, 4113, 4122, 4123, 4124, 4125, 4126, 4127, 4128, 4129, 4130, 4131, 4132, 4133, 4134, 4135, 4136, 4137, 4138, 4139, 4140, 4141, 4160, 4161, 4162, 4163, 4164, 4165, 4166, 4167, 4168, 4169, 4170, 4171, 4172, 4173, 4851, 4852 | `0x8f1ba626b4fe297b` |
| `common:msg.4th` | 4055 | `0xa99c5bcd3877f80e` |
| `doe-campaign.4th` | 4060, 4061, 4062, 4063, 4064, 4065 | `0x3d4549142d91ec20` |
| `doe.4th` | 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107 | `0xb6ecf5374e8ee77c` |
| `hermes:init.4th` | 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, 4109, 4114, 4115, 4116, 4117, 4118, 4119, 4120, 4121, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149, 4150, 4151, 4152, 4153 | `0xbb77f3ac463f507f` |
| `init-0.4th` | 2200, 2201 | `0xd0a9550baf786bb3` |
| `init-1.4th` | 4406, 4415, 4425, 4435 | `0x63e251adb0a03613` |
| `init-2.4th` | 4506, 4515, 4525, 4535, 4545 | `0xf113b3d0bcccae47` |
| `init-3.4th` | 4606, 4615, 4625, 4635, 4645, 4655, 4665 | `0x62b7a71576ad1041` |
| `init-4.4th` | 2130, 2131, 2132 | `0x89e3ef1db5ac0627` |
| `init-5.4th` | *(none — raw code capsule)* | `0xda0c179af10a46a9` |
| `init-6.4th` | 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095 | `0x06fc0ce1e369ef5a` |
| `init-7.4th` | 2150 | `0xb88df2efeb753d64` |
| `init-8.4th` | 2160 | `0x714746312b72f83d` |
| `init-9.4th` | 4706, 4715, 4725, 4735, 4745 | `0x3f2bec73142aa424` |
| `init-l8-diverse.4th` | 4820, 4821, 4822 | `0xaa293201a6c91838` |
| `init-l8-omni.4th` | 2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079 | `0x5979e314d6452045` |
| `init-l8-stable.4th` | 4806 | `0xdc3830f189063a9a` |
| `init-l8-temporal.4th` | 4830, 4831 | `0x51abd4c138246651` |
| `init-l8-transition.4th` | 4840, 4841, 4842 | `0xbcc1a81976f0a4c9` |
| `init-l8-volatile.4th` | 4810, 4811, 4812, 4813 | `0x98caabbbd92abac4` |
| `init.4th` | 2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059 | `0x7e54f972ef83ae23` |
| `lib.4th` | 4050 | `0x20625ebf1276c239` |
| `process.4th` | 4300, 4301 | `0x781afc1dbd0294f7` |
| `zuse.4th` | 4016, 4017, 4018 | `0x12f38ec782434a77` |
## Block Map (sorted by LBN)
| LBN | Capsule | xxHash64 | Status |
|-----|---------|----------|--------|
| 2049 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2050 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2051 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2052 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2053 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2054 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2055 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2056 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2057 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2058 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2059 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2064 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2065 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2066 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2067 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2068 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2069 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2070 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2071 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2072 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2073 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2074 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2075 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2076 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2077 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2078 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2079 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2080 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2081 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2082 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2083 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2084 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2085 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2086 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2087 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2088 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2089 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2090 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2091 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2092 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2093 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2094 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2095 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2100 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2101 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2102 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2103 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2104 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2105 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2106 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2107 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2130 | `init-4.4th` | `0x89e3ef1db5ac0627` | ok |
| 2131 | `init-4.4th` | `0x89e3ef1db5ac0627` | ok |
| 2132 | `init-4.4th` | `0x89e3ef1db5ac0627` | ok |
| 2150 | `init-7.4th` | `0xb88df2efeb753d64` | ok |
| 2160 | `init-8.4th` | `0x714746312b72f83d` | ok |
| 2200 | `init-0.4th` | `0xd0a9550baf786bb3` | ok |
| 2201 | `init-0.4th` | `0xd0a9550baf786bb3` | ok |
| 4000 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4001 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4002 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4003 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4004 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4005 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4006 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4007 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4015 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4016 | `zuse.4th` | `0x12f38ec782434a77` | ok |
| 4017 | `zuse.4th` | `0x12f38ec782434a77` | ok |
| 4018 | `zuse.4th` | `0x12f38ec782434a77` | ok |
| 4050 | `lib.4th` | `0x20625ebf1276c239` | ok |
| 4055 | `common:msg.4th` | `0xa99c5bcd3877f80e` | ok |
| 4060 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4061 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4062 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4063 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4064 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4065 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4100 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4101 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4102 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4103 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4104 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4105 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4106 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4107 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4108 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4109 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4110 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4111 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4112 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4113 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4114 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4115 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4116 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4117 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4118 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4119 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4120 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4121 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4122 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4123 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4124 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4125 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4126 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4127 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4128 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4129 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4130 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4131 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4132 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4133 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4134 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4135 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4136 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4137 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4138 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4139 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4140 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4141 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4142 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4143 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4144 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4145 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4146 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4147 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4148 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4149 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4150 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4151 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4152 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4153 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4300 | `process.4th` | `0x781afc1dbd0294f7` | ok |
| 4301 | `process.4th` | `0x781afc1dbd0294f7` | ok |
| 4406 | `init-1.4th` | `0x63e251adb0a03613` | ok |
| 4415 | `init-1.4th` | `0x63e251adb0a03613` | ok |
| 4425 | `init-1.4th` | `0x63e251adb0a03613` | ok |
| 4435 | `init-1.4th` | `0x63e251adb0a03613` | ok |
| 4506 | `init-2.4th` | `0xf113b3d0bcccae47` | ok |
| 4515 | `init-2.4th` | `0xf113b3d0bcccae47` | ok |
| 4525 | `init-2.4th` | `0xf113b3d0bcccae47` | ok |
| 4535 | `init-2.4th` | `0xf113b3d0bcccae47` | ok |
| 4545 | `init-2.4th` | `0xf113b3d0bcccae47` | ok |
| 4606 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4615 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4625 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4635 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4645 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4655 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4665 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4706 | `init-9.4th` | `0x3f2bec73142aa424` | ok |
| 4715 | `init-9.4th` | `0x3f2bec73142aa424` | ok |
| 4725 | `init-9.4th` | `0x3f2bec73142aa424` | ok |
| 4735 | `init-9.4th` | `0x3f2bec73142aa424` | ok |
| 4745 | `init-9.4th` | `0x3f2bec73142aa424` | ok |
| 4806 | `init-l8-stable.4th` | `0xdc3830f189063a9a` | ok |
| 4810 | `init-l8-volatile.4th` | `0x98caabbbd92abac4` | ok |
| 4811 | `init-l8-volatile.4th` | `0x98caabbbd92abac4` | ok |
| 4812 | `init-l8-volatile.4th` | `0x98caabbbd92abac4` | ok |
| 4813 | `init-l8-volatile.4th` | `0x98caabbbd92abac4` | ok |
| 4820 | `init-l8-diverse.4th` | `0xaa293201a6c91838` | ok |
| 4821 | `init-l8-diverse.4th` | `0xaa293201a6c91838` | ok |
| 4822 | `init-l8-diverse.4th` | `0xaa293201a6c91838` | ok |
| 4830 | `init-l8-temporal.4th` | `0x51abd4c138246651` | ok |
| 4831 | `init-l8-temporal.4th` | `0x51abd4c138246651` | ok |
| 4840 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
| 4841 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
| 4842 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
| 4851 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| 4852 | `artemis:init.4th` | `0xd622eb8ccb75f29a` | ok |
| LBN | Capsule | xxHash64 | Status |
|------|--------------------------|----------------------|--------|
| 2049 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2050 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2051 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2052 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2053 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2054 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2055 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2056 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2057 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2058 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2059 | `init.4th` | `0x7e54f972ef83ae23` | ok |
| 2064 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2065 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2066 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2067 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2068 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2069 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2070 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2071 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2072 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2073 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2074 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2075 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2076 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2077 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2078 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2079 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2080 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2081 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2082 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2083 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2084 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2085 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2086 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2087 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2088 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2089 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2090 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2091 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2092 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2093 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2094 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2095 | `init-6.4th` | `0x06fc0ce1e369ef5a` | ok |
| 2100 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2101 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2102 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2103 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2104 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2105 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2106 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2107 | `doe.4th` | `0xb6ecf5374e8ee77c` | ok |
| 2130 | `init-4.4th` | `0x89e3ef1db5ac0627` | ok |
| 2131 | `init-4.4th` | `0x89e3ef1db5ac0627` | ok |
| 2132 | `init-4.4th` | `0x89e3ef1db5ac0627` | ok |
| 2150 | `init-7.4th` | `0xb88df2efeb753d64` | ok |
| 2160 | `init-8.4th` | `0x714746312b72f83d` | ok |
| 2200 | `init-0.4th` | `0xd0a9550baf786bb3` | ok |
| 2201 | `init-0.4th` | `0xd0a9550baf786bb3` | ok |
| 4000 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4001 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4002 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4003 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4004 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4005 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4006 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4007 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4015 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4016 | `zuse.4th` | `0x12f38ec782434a77` | ok |
| 4017 | `zuse.4th` | `0x12f38ec782434a77` | ok |
| 4018 | `zuse.4th` | `0x12f38ec782434a77` | ok |
| 4050 | `lib.4th` | `0x20625ebf1276c239` | ok |
| 4055 | `common:msg.4th` | `0xa99c5bcd3877f80e` | ok |
| 4060 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4061 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4062 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4063 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4064 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4065 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4100 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4101 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4102 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4103 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4104 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4105 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4106 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4107 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4108 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4109 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4110 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4111 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4112 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4113 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4114 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4115 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4116 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4117 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4118 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4119 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4120 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4121 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4122 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4123 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4124 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4125 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4126 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4127 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4128 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4129 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4130 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4131 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4132 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4133 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4134 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4135 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4136 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4137 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4138 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4139 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4140 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4141 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4142 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4143 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4144 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4145 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4146 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4147 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4148 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4149 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4150 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4151 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4152 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4153 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4160 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4161 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4162 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4163 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4164 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4165 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4166 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4167 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4168 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4169 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4170 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4171 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4172 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4173 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4300 | `process.4th` | `0x781afc1dbd0294f7` | ok |
| 4301 | `process.4th` | `0x781afc1dbd0294f7` | ok |
| 4406 | `init-1.4th` | `0x63e251adb0a03613` | ok |
| 4415 | `init-1.4th` | `0x63e251adb0a03613` | ok |
| 4425 | `init-1.4th` | `0x63e251adb0a03613` | ok |
| 4435 | `init-1.4th` | `0x63e251adb0a03613` | ok |
| 4506 | `init-2.4th` | `0xf113b3d0bcccae47` | ok |
| 4515 | `init-2.4th` | `0xf113b3d0bcccae47` | ok |
| 4525 | `init-2.4th` | `0xf113b3d0bcccae47` | ok |
| 4535 | `init-2.4th` | `0xf113b3d0bcccae47` | ok |
| 4545 | `init-2.4th` | `0xf113b3d0bcccae47` | ok |
| 4606 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4615 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4625 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4635 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4645 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4655 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4665 | `init-3.4th` | `0x62b7a71576ad1041` | ok |
| 4706 | `init-9.4th` | `0x3f2bec73142aa424` | ok |
| 4715 | `init-9.4th` | `0x3f2bec73142aa424` | ok |
| 4725 | `init-9.4th` | `0x3f2bec73142aa424` | ok |
| 4735 | `init-9.4th` | `0x3f2bec73142aa424` | ok |
| 4745 | `init-9.4th` | `0x3f2bec73142aa424` | ok |
| 4806 | `init-l8-stable.4th` | `0xdc3830f189063a9a` | ok |
| 4810 | `init-l8-volatile.4th` | `0x98caabbbd92abac4` | ok |
| 4811 | `init-l8-volatile.4th` | `0x98caabbbd92abac4` | ok |
| 4812 | `init-l8-volatile.4th` | `0x98caabbbd92abac4` | ok |
| 4813 | `init-l8-volatile.4th` | `0x98caabbbd92abac4` | ok |
| 4820 | `init-l8-diverse.4th` | `0xaa293201a6c91838` | ok |
| 4821 | `init-l8-diverse.4th` | `0xaa293201a6c91838` | ok |
| 4822 | `init-l8-diverse.4th` | `0xaa293201a6c91838` | ok |
| 4830 | `init-l8-temporal.4th` | `0x51abd4c138246651` | ok |
| 4831 | `init-l8-temporal.4th` | `0x51abd4c138246651` | ok |
| 4840 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
| 4841 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
| 4842 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
| 4851 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
| 4852 | `artemis:init.4th` | `0x8f1ba626b4fe297b` | ok |
## Conflicts
+153
View File
@@ -249,6 +249,159 @@ Block 4133
ART-INIT
ART-BOOT-ENTRY
WELCOME
Block 4160
( Surface stress test storage. Manual-invoke only -- )
( never called from the entry sequence above. )
2000 CONSTANT ART-STRESS-CAP
CREATE ART-STRESS-LBN ART-STRESS-CAP CELLS ALLOT
CREATE ART-STRESS-PAT ART-STRESS-CAP CELLS ALLOT
VARIABLE ART-STRESS-N
VARIABLE ART-STRESS-PASS
VARIABLE ART-STRESS-FAIL
VARIABLE S-I
VARIABLE S-IDX
VARIABLE S-STRIDE
VARIABLE S-CUR
VARIABLE S-ACTUAL
Block 4161
( Trial record accessors )
: SLBN@ ( i -- lbn ) CELLS ART-STRESS-LBN + @ ;
: SLBN! ( lbn i -- ) CELLS ART-STRESS-LBN + ! ;
: SPAT@ ( i -- pat ) CELLS ART-STRESS-PAT + @ ;
: SPAT! ( pat i -- ) CELLS ART-STRESS-PAT + ! ;
Block 4172
( CSV emission part 1 -- one row per trial, tagged )
( so it can be grepped like the DoE CSVs. Every )
( trial emits a row, pass or fail -- no silent skips. )
: SCSV-HEADER ( n -- )
." [ARTSTRESS] HEADER,n=" DUP . CR
." [ARTSTRESS] trial,lbn,pat,result" CR ;
: SCSV-ROW ( -- )
." [ARTSTRESS] "
S-I @ . ." ,"
S-I @ SLBN@ . ." ,"
S-I @ SPAT@ . ." ,"
S-ACTUAL @ S-I @ SPAT@ = IF 1 ELSE 0 THEN . CR ;
Block 4173
( CSV emission part 2 -- SUMMARY marker. A run that )
( got killed/timed out mid-way never prints this -- )
( its absence means the data is incomplete, full )
( stop, not "probably fine." )
: SCSV-SUMMARY ( -- )
." [ARTSTRESS] SUMMARY,"
ART-STRESS-N @ . ." ,"
ART-STRESS-PASS @ . ." ,"
ART-STRESS-FAIL @ . CR ;
Block 4162
( Coprime-stride walk over the data index space -- )
( visits every index at most once before repeating, )
( so no O(n) scan against past picks is needed. )
( ART-DATA-BLKS = 22998 = 2 * 3 * 3833. )
: S-COPRIME? ( n -- flag )
DUP 3 MOD 0= IF DROP 0 EXIT THEN
3833 MOD 0<> ;
: S-INIT-WALK ( -- )
0 ART-DATA-BLKS 1 - RANDOM S-CUR !
BEGIN
1 ART-DATA-BLKS 1 - RANDOM 1 OR
DUP S-COPRIME?
UNTIL
S-STRIDE ! ;
Block 4171
( Step the walk; skip anything already allocated by )
( someone else -- never clobbers pre-existing data. )
: SPICK ( -- idx )
BEGIN
S-CUR @ S-STRIDE @ + ART-DATA-BLKS MOD S-CUR !
S-CUR @ FM-TEST 0=
UNTIL
S-CUR @ ;
Block 4163
( Write one trial: alloc a fresh random free block, )
( tag it with a trial-derived pattern, and persist )
( it through the real BLK-ALLOC-style path. )
: SWRITE1 ( -- )
SPICK S-IDX !
S-IDX @ FM-SET
Q.1 S-IDX @ BLK-HEAT!
S-IDX @ ART-DATA-LBN + S-I @ SLBN!
S-I @ 255 AND S-I @ SPAT!
S-I @ SLBN@ BLK-FETCH
1024 S-I @ SPAT@ FILL
S-I @ SLBN@ BLK-PERSIST ;
Block 4164
( Verify one trial: by now the 8-slot devblock cache )
( has likely evicted and reloaded this block many )
( times over -- a real round trip, not a cache hit. )
: SVERIFY1 ( -- )
S-I @ SLBN@ BLK-FETCH C@ S-ACTUAL !
S-ACTUAL @ S-I @ SPAT@ = IF
1 ART-STRESS-PASS +!
ELSE
1 ART-STRESS-FAIL +!
." STRESS MISMATCH trial=" S-I @ .
." lbn=" S-I @ SLBN@ . CR
THEN
SCSV-ROW ;
Block 4166
( Clamp reps into range, kept in its own block so )
( SVERIFY1 above has room for CSV emission. )
: ART-STRESS-CLAMP ( reps -- reps' )
DUP ART-STRESS-CAP > IF DROP ART-STRESS-CAP THEN
DUP 1 < IF DROP 1 THEN ;
Block 4165
( Driver part 1: banner, then the write+verify loops. )
( Split across several blocks -- a colon definition )
( must fit in a single 1024-byte/16-line block here. )
( This is a data-collection fixture, not a fast/live )
( check -- same posture as the DoE campaign. K is not )
( gated on live, it's recorded for offline analysis. )
: ART-STRESS-BANNER ( n -- )
DUP SCSV-HEADER
." Artemis: surface stress test, " . ." trials" CR
ART-K-TOTAL Q.PRINT ." K before" CR ;
: ART-STRESS-RUN ( -- )
ART-STRESS-N @ 0 DO I S-I ! SWRITE1 LOOP
ART-STRESS-N @ 0 DO I S-I ! SVERIFY1 LOOP ;
Block 4167
( Driver part 2: pass/fail summary line )
: ART-STRESS-REPORT ( -- )
." Artemis stress: "
ART-STRESS-PASS @ . ." passed, "
ART-STRESS-FAIL @ . ." failed" CR ;
Block 4168
( Driver part 3: free everything this run touched, )
( print K after for the record. Only write/verify )
( mismatches gate PASS/FAIL -- a background heartbeat )
( decay tick during a long run is expected and is not )
( itself a failure. )
: ART-STRESS-CLEANUP ( -- )
ART-STRESS-N @ 0 DO
I S-I ! S-I @ SLBN@ BLK-FREE
LOOP
ART-K-TOTAL Q.PRINT ." K after (should match)" CR
SCSV-SUMMARY
ART-STRESS-FAIL @ 0 = IF
." PASS: surface stress test" CR
ELSE
." FAIL: surface stress test" CR
THEN ;
Block 4169
( Top-level entry points )
: ART-STRESS-TEST ( seed reps -- )
ART-STRESS-CLAMP ART-STRESS-N ! SEED
0 ART-STRESS-PASS ! 0 ART-STRESS-FAIL !
S-INIT-WALK
ART-STRESS-N @ ART-STRESS-BANNER
ART-STRESS-RUN ART-STRESS-REPORT
ART-STRESS-CLEANUP ;
: ART-STRESS ( -- ) 424242 50 ART-STRESS-TEST ;
Block 4170
( Disabled by default -- uncomment to run the surface )
( stress test automatically at every boot. Normally )
( invoked manually instead: ART-STRESS or )
( <seed> <reps> ART-STRESS-TEST from the console. )
\ ART-STRESS
Block 4851
( Cheap touch target for Phase 3 fleet DoE workload -- O(1), )
( unlike ART-TICK/ART-STATUS which scan all ART-DATA-BLKS. )
BIN
View File
Binary file not shown.
@@ -0,0 +1,269 @@
#!/usr/bin/env Rscript
# analyse_artemis_stress.R
#
# Analysis for the Artemis surface stress test bug hunt (2026-08-02):
# a stale-pointer aliasing bug between the block-subsystem's devblock
# cache and the VM-level window cache in src/word_source/block_words.c,
# found by the first completed run of ART-STRESS-TEST, fixed, and
# re-verified on all three architectures.
#
# Reads experiments/artemis_stress/runs/*.csv, writes SVG charts to
# analysis/charts/ and PNG copies to analysis/img/.
suppressMessages({
library(ggplot2)
library(dplyr)
library(tidyr)
library(scales)
library(svglite)
})
run_dir <- file.path("experiments", "artemis_stress", "runs")
chart_dir <- file.path("experiments", "artemis_stress", "analysis", "charts")
img_dir <- file.path("experiments", "artemis_stress", "analysis", "img")
dir.create(chart_dir, recursive = TRUE, showWarnings = FALSE)
dir.create(img_dir, recursive = TRUE, showWarnings = FALSE)
## ---- palette (matches experiments/bare_metal house style) ----
sfblue <- "#1F77B4"
sforange <- "#FF7F0E"
sfgreen <- "#2CA02C"
sfred <- "#D62728"
sfgray <- "#505050"
theme_sf <- function() {
theme_minimal(base_size = 12) +
theme(
panel.grid.minor = element_blank(),
plot.title = element_text(face = "bold"),
plot.subtitle = element_text(color = sfgray),
legend.position = "bottom"
)
}
save_chart <- function(plot, name, width = 7, height = 4.2) {
svg_path <- file.path(chart_dir, paste0(name, ".svg"))
png_path <- file.path(img_dir, paste0(name, ".png"))
ggsave(svg_path, plot, width = width, height = height, device = svglite)
ggsave(png_path, plot, width = width, height = height, dpi = 200)
cat(" wrote", svg_path, "and", png_path, "\n")
}
## ---- load runs ----
runs <- list(
amd64_buggy = "amd64 (pre-fix)",
amd64_fixed = "amd64 (post-fix)",
aarch64_fixed = "aarch64 (post-fix)",
riscv64_fixed = "riscv64 (post-fix)"
)
load_run <- function(key, label) {
df <- read.csv(file.path(run_dir, paste0(key, ".csv")))
df$run <- key
df$label <- label
df$status <- factor(ifelse(df$result == 1, "PASS", "FAIL"), levels = c("PASS", "FAIL"))
df
}
all_runs <- bind_rows(Map(load_run, names(runs), runs))
## ---- Chart 1: pass rate before/after, all runs ----
summary_tbl <- all_runs %>%
group_by(run, label) %>%
summarise(n = n(), passed = sum(result), .groups = "drop") %>%
mutate(pass_rate = passed / n,
phase = ifelse(grepl("pre-fix", label), "pre-fix", "post-fix"))
p1 <- ggplot(summary_tbl, aes(x = reorder(label, pass_rate), y = pass_rate, fill = phase)) +
geom_col(width = 0.6) +
geom_text(aes(label = sprintf("%d/%d", passed, n)), vjust = -0.5, size = 4) +
scale_y_continuous(labels = percent_format(), limits = c(0, 1.08)) +
scale_fill_manual(values = c("pre-fix" = sfred, "post-fix" = sfgreen)) +
coord_flip() +
labs(
title = "Artemis surface stress test: pass rate before/after fix",
subtitle = "50 trials per run, unique random blocks across the 22998-block data pool",
x = NULL, y = "Pass rate", fill = NULL
) +
theme_sf()
save_chart(p1, "pass_rate_comparison", width = 8)
## ---- Chart 2: pass/fail sequence in write/verify order, buggy run ----
## A visual scan of this sequence first suggested serial clustering; the
## Wald-Wolfowitz runs test (H1, below) does not support that reading --
## observed runs (26) exactly match the count expected under a random
## ordering. The chart is kept because it is still the raw sequence the
## tests are run against, not because it shows a confirmed pattern.
buggy <- all_runs %>% filter(run == "amd64_buggy") %>% arrange(trial)
p2 <- ggplot(buggy, aes(x = trial, y = 1, fill = status)) +
geom_tile(color = "white", linewidth = 0.6, height = 1) +
scale_fill_manual(values = c(PASS = sfgreen, FAIL = sfred)) +
scale_x_continuous(breaks = seq(0, 49, 5)) +
labs(
title = "Pass/fail sequence in write order, amd64 pre-fix run",
subtitle = "25 of 50 trials failed; a formal runs test (H1) finds no\nsignificant serial clustering in this sequence -- see Section 4",
x = "Trial index (= write/verify order)", y = NULL, fill = NULL
) +
theme_sf() +
theme(axis.text.y = element_blank(), panel.grid = element_blank())
save_chart(p2, "failure_clustering", height = 2.6)
## ---- Chart 3: surface coverage map, buggy vs one fixed run ----
cov <- all_runs %>% filter(run %in% c("amd64_buggy", "amd64_fixed"))
p3 <- ggplot(cov, aes(x = lbn, y = trial, color = status)) +
geom_point(size = 2.2, alpha = 0.85) +
scale_color_manual(values = c(PASS = sfgreen, FAIL = sfred)) +
facet_wrap(~label, ncol = 1) +
labs(
title = "Surface coverage: scattered across the full data pool",
subtitle = "Each point is one trial's block. A Wilcoxon test (H2) finds LBN and\noutcome are not independent, but no sub-region is visually implicated",
x = "Logical block number (data pool: 3076-26073)", y = "Trial index", color = NULL
) +
theme_sf()
save_chart(p3, "surface_coverage_map", height = 5.5)
## ---- summary table for the report ----
write.csv(summary_tbl, file.path("experiments", "artemis_stress", "analysis", "summary.csv"),
row.names = FALSE)
cat("\nDone. Summary:\n")
print(as.data.frame(summary_tbl))
## ═══════════════════════════════════════════════════════════════════════
## Hypothesis tests
##
## Three questions, each with an explicit null hypothesis, tested against
## the actual recorded trial data rather than asserted from eyeballing
## charts. Results are written to tables/ for the LaTeX report to quote
## verbatim -- no numbers in the report are hand-typed.
## ═══════════════════════════════════════════════════════════════════════
table_dir <- file.path("experiments", "artemis_stress", "analysis", "tables")
dir.create(table_dir, recursive = TRUE, showWarnings = FALSE)
sink_lines <- c()
say <- function(...) {
line <- paste0(...)
sink_lines <<- c(sink_lines, line)
cat(line, "\n")
}
say("Artemis surface stress test -- hypothesis tests")
say("=================================================")
say("")
## ---- H1: are pre-fix failures independently distributed across trials, ----
## ---- or do they show serial clustering (a runs test)? ----
## H0: the pass/fail sequence is a random ordering (no serial correlation).
## Manual Wald-Wolfowitz runs test (base R only, no extra package needed).
runs_test <- function(seq01) {
n1 <- sum(seq01 == 1); n2 <- sum(seq01 == 0); n <- n1 + n2
r <- 1 + sum(diff(seq01) != 0)
mu <- 1 + 2 * n1 * n2 / n
var_r <- (2 * n1 * n2 * (2 * n1 * n2 - n)) / (n^2 * (n - 1))
z <- (r - mu) / sqrt(var_r)
p <- 2 * pnorm(-abs(z))
list(n1 = n1, n2 = n2, runs = r, expected = mu, z = z, p = p)
}
buggy_seq <- buggy$result[order(buggy$trial)]
rt <- runs_test(buggy_seq)
say("H1 -- pre-fix failure clustering (Wald-Wolfowitz runs test)")
say(" H0: pass/fail outcomes are independently ordered (no clustering).")
say(sprintf(" Observed runs = %d (expected under H0 = %.2f, sd = %.2f)",
rt$runs, rt$expected, sqrt((2*rt$n1*rt$n2*(2*rt$n1*rt$n2-(rt$n1+rt$n2))) /
((rt$n1+rt$n2)^2*(rt$n1+rt$n2-1)))))
say(sprintf(" z = %.3f, p = %.2e", rt$z, rt$p))
say(sprintf(" -> %s H0 at alpha=0.05: failures %s serially clustered.",
ifelse(rt$p < 0.05, "REJECT", "fail to reject"),
ifelse(rt$p < 0.05, "ARE", "are not")))
say("")
## ---- H1b: is outcome associated with trial/write order directly ----
## ---- (not via the runs test, which only detects consecutive-value ----
## ---- clustering and can miss a more general order-dependent trend)? ----
## H0: trial index is drawn from the same distribution for PASS and FAIL.
wt_order <- wilcox.test(trial ~ status, data = buggy)
say("H1b -- pre-fix outcome vs. trial/write order directly (Wilcoxon)")
say(" H0: trial index is drawn from the same distribution for PASS/FAIL")
say(" (complements H1: the runs test only catches consecutive-value")
say(" clustering, not a general rank-order trend).")
say(sprintf(" W = %.1f, p = %.3f", wt_order$statistic, wt_order$p.value))
say(sprintf(" -> %s H0 at alpha=0.05: outcome %s associated with write order.",
ifelse(wt_order$p.value < 0.05, "REJECT", "fail to reject"),
ifelse(wt_order$p.value < 0.05, "IS", "is not")))
say("")
## ---- H2: is failure status independent of disk location (logical ----
## ---- block number)? ----
## H0: LBN is drawn from the same distribution for PASS and FAIL trials.
## Wilcoxon rank-sum test (non-parametric, no normality assumption on LBN).
wt <- wilcox.test(lbn ~ status, data = buggy)
say("H2 -- pre-fix failure vs. disk location (Wilcoxon rank-sum test)")
say(" H0: logical block number is drawn from the same distribution")
say(" for PASS and FAIL trials (failure does not depend on location).")
say(sprintf(" W = %.1f, p = %.3f", wt$statistic, wt$p.value))
say(sprintf(" -> %s H0 at alpha=0.05: failure %s associated with LBN.",
ifelse(wt$p.value < 0.05, "REJECT", "fail to reject"),
ifelse(wt$p.value < 0.05, "IS", "is not")))
say("")
## Secondary check: sibling position within devblock (0/1/2).
buggy$sib_pos <- (buggy$lbn - 3076) %% 3
pos_tbl <- table(buggy$sib_pos, buggy$status)
ct <- suppressWarnings(chisq.test(pos_tbl))
say(" Secondary check -- sibling position (0/1/2) vs. outcome (chi-squared)")
say(sprintf(" chi-sq = %.3f, df = %d, p = %.3f",
ct$statistic, ct$parameter, ct$p.value))
say(sprintf(" -> %s H0: outcome %s associated with sibling position.",
ifelse(ct$p.value < 0.05, "REJECT", "fail to reject"),
ifelse(ct$p.value < 0.05, "IS", "is not")))
say("")
## ---- H3: did the fix change the pass rate, or is 25/50 vs 150/150 ----
## ---- explainable by chance? ----
## H0: pre-fix and post-fix runs share the same true pass probability.
post_passed <- sum(all_runs$run != "amd64_buggy" & all_runs$result == 1)
post_n <- sum(all_runs$run != "amd64_buggy")
pre_passed <- sum(all_runs$run == "amd64_buggy" & all_runs$result == 1)
pre_n <- sum(all_runs$run == "amd64_buggy")
pt <- prop.test(c(pre_passed, post_passed), c(pre_n, post_n), correct = TRUE)
say("H3 -- did the fix change the pass rate (two-proportion test)")
say(" H0: pre-fix and post-fix trials share the same true pass rate.")
say(sprintf(" Pre-fix: %d/%d = %.1f%% Post-fix (pooled, 3 arches): %d/%d = %.1f%%",
pre_passed, pre_n, 100*pre_passed/pre_n,
post_passed, post_n, 100*post_passed/post_n))
say(sprintf(" chi-sq = %.2f, df = %d, p = %.2e", pt$statistic, 1, pt$p.value))
say(sprintf(" 95%% CI on the difference in pass rate: [%.3f, %.3f]",
pt$conf.int[1], pt$conf.int[2]))
say(sprintf(" -> %s H0 at alpha=0.05: the fix %s the pass rate.",
ifelse(pt$p.value < 0.05, "REJECT", "fail to reject"),
ifelse(pt$p.value < 0.05, "DID CHANGE", "did not measurably change")))
writeLines(sink_lines, file.path(table_dir, "hypothesis_tests.txt"))
## Machine-readable version for \newcommand substitution in the report.
hyp_df <- data.frame(
key = c("h1_runs_obs","h1_runs_exp","h1_z","h1_p",
"h1b_W","h1b_p",
"h2_W","h2_p","h2b_chisq","h2b_df","h2b_p",
"h3_pre_n","h3_pre_pass","h3_post_n","h3_post_pass",
"h3_chisq","h3_p","h3_ci_lo","h3_ci_hi"),
value = c(rt$runs, rt$expected, rt$z, rt$p,
unname(wt_order$statistic), wt_order$p.value,
unname(wt$statistic), wt$p.value, unname(ct$statistic), unname(ct$parameter), ct$p.value,
pre_n, pre_passed, post_n, post_passed,
unname(pt$statistic), pt$p.value, pt$conf.int[1], pt$conf.int[2])
)
write.csv(hyp_df, file.path(table_dir, "hypothesis_tests.csv"), row.names = FALSE)
cat("\nWrote", file.path(table_dir, "hypothesis_tests.txt"), "and .csv\n")
@@ -0,0 +1,208 @@
<?xml version='1.0' encoding='UTF-8' ?>
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='504.00pt' height='187.20pt'
viewBox='0 0 504.00 187.20'>
<g class='svglite'>
<defs>
<style type='text/css'><![CDATA[
.svglite line, .svglite polyline, .svglite polygon, .svglite path, .svglite rect, .svglite circle {
fill: none;
stroke: #000000;
stroke-linecap: round;
stroke-linejoin: round;
stroke-miterlimit: 10.00;
}
.svglite text {
white-space: pre;
}
.svglite g.glyphgroup path {
fill: inherit;
stroke: none;
}
]]></style>
</defs>
<rect width='100%' height='100%' style='stroke: none; fill: #FFFFFF;'/>
<defs>
<clipPath id='cpMC4wMHw1MDQuMDB8MC4wMHwxODcuMjA='>
<rect x='0.00' y='0.00' width='504.00' height='187.20'/>
</clipPath>
</defs>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwxODcuMjA=)'>
<rect x='0.00' y='0.00' width='504.00' height='187.20'
style='stroke-width: 1.16; stroke: none; fill: #FFFFFF;'/>
</g>
<defs>
<clipPath id='cpNS45OHw0OTguMDJ8NTQuNTN8MTEyLjMz'>
<rect x='5.98' y='54.53' width='492.04' height='57.80'/>
</clipPath>
</defs>
<g clip-path='url(#cpNS45OHw0OTguMDJ8NTQuNTN8MTEyLjMz)'>
<rect x='28.34' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='37.29' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='46.24' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='55.18' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='64.13' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='73.07' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='82.02' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='90.97' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='99.91' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='108.86' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='117.81' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='126.75' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='135.70' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='144.64' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='153.59' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='162.54' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='171.48' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='180.43' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='189.38' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='198.32' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='207.27' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='216.21' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='225.16' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='234.11' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='243.05' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='252.00' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='260.95' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='269.89' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='278.84' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='287.79' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='296.73' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='305.68' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='314.62' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='323.57' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='332.52' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='341.46' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='350.41' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='359.36' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='368.30' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='377.25' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='386.19' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='395.14' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='404.09' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='413.03' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='421.98' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='430.93' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='439.87' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='448.82' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='457.76' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='466.71' y='57.16' width='8.95' height='52.55'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
</g>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwxODcuMjA=)'>
<text x='32.82' y='124.31' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='5.33px'
lengthAdjust='spacingAndGlyphs'>0
</text>
<text x='77.55' y='124.31' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='5.33px'
lengthAdjust='spacingAndGlyphs'>5
</text>
<text x='122.28' y='124.31' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>10
</text>
<text x='167.01' y='124.31' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>15
</text>
<text x='211.74' y='124.31' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>20
</text>
<text x='256.47' y='124.31' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>25
</text>
<text x='301.20' y='124.31' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>30
</text>
<text x='345.94' y='124.31' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>35
</text>
<text x='390.67' y='124.31' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>40
</text>
<text x='435.40' y='124.31' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>45
</text>
<text x='252.00' y='137.54' text-anchor='middle' style='font-size: 12.00px; font-family: "Liberation Sans";'
textLength='165.28px' lengthAdjust='spacingAndGlyphs'>Trial index (= write/verify order)
</text>
<rect x='204.28' y='158.82' width='15.58' height='15.58'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='258.43' y='158.82' width='15.58' height='15.58'
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<text x='226.69' y='169.90' style='font-size: 9.60px; font-family: "Liberation Sans";' textLength='24.91px'
lengthAdjust='spacingAndGlyphs'>PASS
</text>
<text x='280.83' y='169.90' style='font-size: 9.60px; font-family: "Liberation Sans";' textLength='19.73px'
lengthAdjust='spacingAndGlyphs'>FAIL
</text>
<text x='5.98' y='33.10' style='font-size: 12.00px;fill: #505050; font-family: "Liberation Sans";'
textLength='275.38px' lengthAdjust='spacingAndGlyphs'>25 of 50 trials failed; a formal runs test (H1)
finds no
</text>
<text x='5.98' y='46.06' style='font-size: 12.00px;fill: #505050; font-family: "Liberation Sans";'
textLength='317.44px' lengthAdjust='spacingAndGlyphs'>significant serial clustering in this sequence
-- see Section 4
</text>
<text x='5.98' y='15.88' style='font-size: 14.40px; font-weight: bold; font-family: "Liberation Sans";'
textLength='351.91px' lengthAdjust='spacingAndGlyphs'>Pass/fail sequence in write order, amd64 pre-fix
run
</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,132 @@
<?xml version='1.0' encoding='UTF-8' ?>
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='576.00pt' height='302.40pt'
viewBox='0 0 576.00 302.40'>
<g class='svglite'>
<defs>
<style type='text/css'><![CDATA[
.svglite line, .svglite polyline, .svglite polygon, .svglite path, .svglite rect, .svglite circle {
fill: none;
stroke: #000000;
stroke-linecap: round;
stroke-linejoin: round;
stroke-miterlimit: 10.00;
}
.svglite text {
white-space: pre;
}
.svglite g.glyphgroup path {
fill: inherit;
stroke: none;
}
]]></style>
</defs>
<rect width='100%' height='100%' style='stroke: none; fill: #FFFFFF;'/>
<defs>
<clipPath id='cpMC4wMHw1NzYuMDB8MC4wMHwzMDIuNDA='>
<rect x='0.00' y='0.00' width='576.00' height='302.40'/>
</clipPath>
</defs>
<g clip-path='url(#cpMC4wMHw1NzYuMDB8MC4wMHwzMDIuNDA=)'>
<rect x='0.00' y='0.000000000000057' width='576.00' height='302.40'
style='stroke-width: 1.16; stroke: none; fill: #FFFFFF;'/>
</g>
<defs>
<clipPath id='cpODUuOTR8NTcwLjAyfDQxLjU3fDIyNy41Mw=='>
<rect x='85.94' y='41.57' width='484.09' height='185.96'/>
</clipPath>
</defs>
<g clip-path='url(#cpODUuOTR8NTcwLjAyfDQxLjU3fDIyNy41Mw==)'>
<polyline points='85.94,200.97 570.02,200.97 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='85.94,156.69 570.02,156.69 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='85.94,112.41 570.02,112.41 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='85.94,68.14 570.02,68.14 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='107.94,227.53 107.94,41.57 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='230.18,227.53 230.18,41.57 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='352.43,227.53 352.43,41.57 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='474.67,227.53 474.67,41.57 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<rect x='107.94' y='143.41' width='407.48' height='26.57'
style='stroke-width: 1.16; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='107.94' y='187.68' width='203.74' height='26.57'
style='stroke-width: 1.16; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<rect x='107.94' y='99.13' width='407.48' height='26.57'
style='stroke-width: 1.16; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='107.94' y='54.85' width='407.48' height='26.57'
style='stroke-width: 1.16; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<text x='515.42' y='152.78' text-anchor='middle' style='font-size: 11.38px; font-family: "Liberation Sans";'
textLength='28.47px' lengthAdjust='spacingAndGlyphs'>50/50
</text>
<text x='311.68' y='197.05' text-anchor='middle' style='font-size: 11.38px; font-family: "Liberation Sans";'
textLength='28.47px' lengthAdjust='spacingAndGlyphs'>25/50
</text>
<text x='515.42' y='108.50' text-anchor='middle' style='font-size: 11.38px; font-family: "Liberation Sans";'
textLength='28.47px' lengthAdjust='spacingAndGlyphs'>50/50
</text>
<text x='515.42' y='64.22' text-anchor='middle' style='font-size: 11.38px; font-family: "Liberation Sans";'
textLength='28.47px' lengthAdjust='spacingAndGlyphs'>50/50
</text>
</g>
<g clip-path='url(#cpMC4wMHw1NzYuMDB8MC4wMHwzMDIuNDA=)'>
<text x='80.56' y='204.26' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='64.97px'
lengthAdjust='spacingAndGlyphs'>amd64 (pre-fix)
</text>
<text x='80.56' y='159.99' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='74.58px'
lengthAdjust='spacingAndGlyphs'>aarch64 (post-fix)
</text>
<text x='80.56' y='115.71' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='69.25px'
lengthAdjust='spacingAndGlyphs'>amd64 (post-fix)
</text>
<text x='80.56' y='71.43' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='70.31px'
lengthAdjust='spacingAndGlyphs'>riscv64 (post-fix)
</text>
<text x='107.94' y='239.51' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='13.86px'
lengthAdjust='spacingAndGlyphs'>0%
</text>
<text x='230.18' y='239.51' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='19.19px'
lengthAdjust='spacingAndGlyphs'>30%
</text>
<text x='352.43' y='239.51' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='19.19px'
lengthAdjust='spacingAndGlyphs'>60%
</text>
<text x='474.67' y='239.51' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='19.19px'
lengthAdjust='spacingAndGlyphs'>90%
</text>
<text x='327.98' y='252.74' text-anchor='middle' style='font-size: 12.00px; font-family: "Liberation Sans";'
textLength='50.67px' lengthAdjust='spacingAndGlyphs'>Pass rate
</text>
<rect x='273.74' y='273.94' width='15.73' height='15.73'
style='stroke-width: 1.16; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
<rect x='333.88' y='273.94' width='15.73' height='15.73'
style='stroke-width: 1.16; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
<text x='296.22' y='285.10' style='font-size: 9.60px; font-family: "Liberation Sans";' textLength='30.91px'
lengthAdjust='spacingAndGlyphs'>post-fix
</text>
<text x='356.37' y='285.10' style='font-size: 9.60px; font-family: "Liberation Sans";' textLength='26.62px'
lengthAdjust='spacingAndGlyphs'>pre-fix
</text>
<text x='85.94' y='33.10' style='font-size: 12.00px;fill: #505050; font-family: "Liberation Sans";'
textLength='387.47px' lengthAdjust='spacingAndGlyphs'>50 trials per run, unique random blocks across
the 22998-block data pool
</text>
<text x='85.94' y='15.88' style='font-size: 14.40px; font-weight: bold; font-family: "Liberation Sans";'
textLength='360.70px' lengthAdjust='spacingAndGlyphs'>Artemis surface stress test: pass rate
before/after fix
</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.8 KiB

@@ -0,0 +1,419 @@
<?xml version='1.0' encoding='UTF-8' ?>
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='504.00pt' height='396.00pt'
viewBox='0 0 504.00 396.00'>
<g class='svglite'>
<defs>
<style type='text/css'><![CDATA[
.svglite line, .svglite polyline, .svglite polygon, .svglite path, .svglite rect, .svglite circle {
fill: none;
stroke: #000000;
stroke-linecap: round;
stroke-linejoin: round;
stroke-miterlimit: 10.00;
}
.svglite text {
white-space: pre;
}
.svglite g.glyphgroup path {
fill: inherit;
stroke: none;
}
]]></style>
</defs>
<rect width='100%' height='100%' style='stroke: none; fill: #FFFFFF;'/>
<defs>
<clipPath id='cpMC4wMHw1MDQuMDB8MC4wMHwzOTYuMDA='>
<rect x='0.00' y='0.00' width='504.00' height='396.00'/>
</clipPath>
</defs>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwzOTYuMDA=)'>
<rect x='0.00' y='-0.00000000000011' width='504.00' height='396.00'
style='stroke-width: 1.16; stroke: none; fill: #FFFFFF;'/>
</g>
<defs>
<clipPath id='cpMzUuNzV8NDk4LjAyfDcyLjY4fDE4NC44NA=='>
<rect x='35.75' y='72.68' width='462.27' height='112.17'/>
</clipPath>
</defs>
<g clip-path='url(#cpMzUuNzV8NDk4LjAyfDcyLjY4fDE4NC44NA==)'>
<polyline points='35.75,179.74 498.02,179.74 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='35.75,158.93 498.02,158.93 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='35.75,138.12 498.02,138.12 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='35.75,117.31 498.02,117.31 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='35.75,96.50 498.02,96.50 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='35.75,75.69 498.02,75.69 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='90.93,184.84 90.93,72.68 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='183.20,184.84 183.20,72.68 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='275.48,184.84 275.48,72.68 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='367.75,184.84 367.75,72.68 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='460.03,184.84 460.03,72.68 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<circle cx='207.30' cy='179.74' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='157.12' cy='177.66' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='56.76' cy='175.58' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='431.02' cy='173.50' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='380.84' cy='171.42' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='280.48' cy='169.34' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='230.30' cy='167.26' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='129.94' cy='165.18' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='79.76' cy='163.10' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='454.01' cy='161.01' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='403.83' cy='158.93' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='303.47' cy='156.85' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='477.01' cy='154.77' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='426.83' cy='152.69' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='376.65' cy='150.61' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='326.47' cy='148.53' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='226.11' cy='146.45' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='449.83' cy='144.37' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='399.65' cy='142.29' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='299.29' cy='140.20' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='249.11' cy='138.12' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='472.82' cy='136.04' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='422.64' cy='133.96' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='372.46' cy='131.88' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='322.28' cy='129.80' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='221.92' cy='127.72' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='71.38' cy='125.64' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='445.64' cy='123.56' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='395.46' cy='121.48' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='295.10' cy='119.40' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='244.92' cy='117.31' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='94.38' cy='115.23' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='468.63' cy='113.15' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='418.45' cy='111.07' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='368.27' cy='108.99' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='217.73' cy='106.91' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='441.45' cy='104.83' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='391.27' cy='102.75' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='240.73' cy='100.67' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='464.44' cy='98.59' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='414.26' cy='96.50' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='364.08' cy='94.42' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='313.90' cy='92.34' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='263.72' cy='90.26' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='437.26' cy='88.18' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='387.08' cy='86.10' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='336.90' cy='84.02' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='236.54' cy='81.94' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='86.00' cy='79.86' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='460.25' cy='77.78' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
</g>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwzOTYuMDA=)'>
</g>
<defs>
<clipPath id='cpMzUuNzV8NDk4LjAyfDIwOC45N3wzMjEuMTM='>
<rect x='35.75' y='208.97' width='462.27' height='112.17'/>
</clipPath>
</defs>
<g clip-path='url(#cpMzUuNzV8NDk4LjAyfDIwOC45N3wzMjEuMTM=)'>
<polyline points='35.75,316.03 498.02,316.03 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='35.75,295.22 498.02,295.22 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='35.75,274.41 498.02,274.41 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='35.75,253.60 498.02,253.60 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='35.75,232.79 498.02,232.79 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='35.75,211.98 498.02,211.98 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='90.93,321.13 90.93,208.97 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='183.20,321.13 183.20,208.97 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='275.48,321.13 275.48,208.97 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='367.75,321.13 367.75,208.97 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<polyline points='460.03,321.13 460.03,208.97 '
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
<circle cx='207.30' cy='316.03' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='157.12' cy='313.95' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='56.76' cy='311.87' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='380.84' cy='309.79' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='280.48' cy='307.71' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='230.30' cy='305.63' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='180.12' cy='303.55' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='129.94' cy='301.47' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='79.76' cy='299.39' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='454.01' cy='297.30' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='403.83' cy='295.22' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='303.47' cy='293.14' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='102.75' cy='291.06' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='477.01' cy='288.98' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='426.83' cy='286.90' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='376.65' cy='284.82' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='326.47' cy='282.74' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='226.11' cy='280.66' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='399.65' cy='278.58' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='299.29' cy='276.49' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='249.11' cy='274.41' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='148.75' cy='272.33' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='472.82' cy='270.25' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='322.28' cy='268.17' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='221.92' cy='266.09' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='171.74' cy='264.01' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='71.38' cy='261.93' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='395.46' cy='259.85' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='295.10' cy='257.77' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='244.92' cy='255.68' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='144.56' cy='253.60' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='94.38' cy='251.52' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='468.63' cy='249.44' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='418.45' cy='247.36' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='217.73' cy='245.28' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='67.19' cy='243.20' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='441.45' cy='241.12' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='391.27' cy='239.04' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='240.73' cy='236.96' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='414.26' cy='234.87' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='364.08' cy='232.79' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='313.90' cy='230.71' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='263.72' cy='228.63' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='163.36' cy='226.55' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='336.90' cy='224.47' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='236.54' cy='222.39' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='186.36' cy='220.31' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='86.00' cy='218.23' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<circle cx='410.07' cy='216.15' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='309.71' cy='214.06' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
</g>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwzOTYuMDA=)'>
</g>
<defs>
<clipPath id='cpMzUuNzV8NDk4LjAyfDE5MC44MnwyMDguOTc='>
<rect x='35.75' y='190.82' width='462.27' height='18.15'/>
</clipPath>
</defs>
<g clip-path='url(#cpMzUuNzV8NDk4LjAyfDE5MC44MnwyMDguOTc=)'>
<text x='266.89' y='203.19' text-anchor='middle'
style='font-size: 9.60px;fill: #1A1A1A; font-family: "Liberation Sans";' textLength='64.97px'
lengthAdjust='spacingAndGlyphs'>amd64 (pre-fix)
</text>
</g>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwzOTYuMDA=)'>
</g>
<defs>
<clipPath id='cpMzUuNzV8NDk4LjAyfDU0LjUzfDcyLjY4'>
<rect x='35.75' y='54.53' width='462.27' height='18.15'/>
</clipPath>
</defs>
<g clip-path='url(#cpMzUuNzV8NDk4LjAyfDU0LjUzfDcyLjY4)'>
<text x='266.89' y='66.90' text-anchor='middle'
style='font-size: 9.60px;fill: #1A1A1A; font-family: "Liberation Sans";' textLength='69.25px'
lengthAdjust='spacingAndGlyphs'>amd64 (post-fix)
</text>
</g>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwzOTYuMDA=)'>
<text x='90.93' y='333.11' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='21.31px'
lengthAdjust='spacingAndGlyphs'>5000
</text>
<text x='183.20' y='333.11' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='26.64px'
lengthAdjust='spacingAndGlyphs'>10000
</text>
<text x='275.48' y='333.11' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='26.64px'
lengthAdjust='spacingAndGlyphs'>15000
</text>
<text x='367.75' y='333.11' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='26.64px'
lengthAdjust='spacingAndGlyphs'>20000
</text>
<text x='460.03' y='333.11' text-anchor='middle'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='26.64px'
lengthAdjust='spacingAndGlyphs'>25000
</text>
<text x='30.37' y='183.04' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='5.33px'
lengthAdjust='spacingAndGlyphs'>0
</text>
<text x='30.37' y='162.23' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>10
</text>
<text x='30.37' y='141.42' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>20
</text>
<text x='30.37' y='120.61' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>30
</text>
<text x='30.37' y='99.80' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>40
</text>
<text x='30.37' y='78.99' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>50
</text>
<text x='30.37' y='319.33' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='5.33px'
lengthAdjust='spacingAndGlyphs'>0
</text>
<text x='30.37' y='298.52' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>10
</text>
<text x='30.37' y='277.71' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>20
</text>
<text x='30.37' y='256.90' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>30
</text>
<text x='30.37' y='236.09' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>40
</text>
<text x='30.37' y='215.28' text-anchor='end'
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
lengthAdjust='spacingAndGlyphs'>50
</text>
<text x='266.89' y='346.34' text-anchor='middle' style='font-size: 12.00px; font-family: "Liberation Sans";'
textLength='244.78px' lengthAdjust='spacingAndGlyphs'>Logical block number (data pool: 3076-26073)
</text>
<text transform='translate(14.24,196.90) rotate(-90)' text-anchor='middle'
style='font-size: 12.00px; font-family: "Liberation Sans";' textLength='54.92px'
lengthAdjust='spacingAndGlyphs'>Trial index
</text>
<circle cx='226.96' cy='375.40' r='2.73'
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
<circle cx='281.10' cy='375.40' r='2.73'
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
<text x='241.58' y='378.70' style='font-size: 9.60px; font-family: "Liberation Sans";' textLength='24.91px'
lengthAdjust='spacingAndGlyphs'>PASS
</text>
<text x='295.72' y='378.70' style='font-size: 9.60px; font-family: "Liberation Sans";' textLength='19.73px'
lengthAdjust='spacingAndGlyphs'>FAIL
</text>
<text x='35.75' y='33.10' style='font-size: 12.00px;fill: #505050; font-family: "Liberation Sans";'
textLength='347.72px' lengthAdjust='spacingAndGlyphs'>Each point is one trial's block. A Wilcoxon test
(H2) finds LBN and
</text>
<text x='35.75' y='46.06' style='font-size: 12.00px;fill: #505050; font-family: "Liberation Sans";'
textLength='368.14px' lengthAdjust='spacingAndGlyphs'>outcome are not independent, but no sub-region
is visually implicated
</text>
<text x='35.75' y='15.88' style='font-size: 14.40px; font-weight: bold; font-family: "Liberation Sans";'
textLength='362.31px' lengthAdjust='spacingAndGlyphs'>Surface coverage: scattered across the full data
pool
</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

@@ -0,0 +1,7 @@
*.aux
*.log
*.out
*.toc
*.synctex.gz
*.fls
*.fdb_latexmk
@@ -0,0 +1,388 @@
\documentclass[10pt,a4paper]{article}
%% ── Packages ─────────────────────────────────────────────────────────────
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[margin=2.5cm]{geometry}
\usepackage{graphicx}
\usepackage{booktabs}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{xcolor}
\usepackage{microtype}
\usepackage{parskip}
\usepackage{enumitem}
\usepackage[hidelinks,colorlinks=true,linkcolor=black,citecolor=black,urlcolor=blue]{hyperref}
\usepackage{lmodern}
\usepackage{colortbl}
\usepackage{listings}
%% ── Colour palette (matches experiments/bare_metal house style) ─────────
\definecolor{sfblue}{RGB}{31,119,180}
\definecolor{sforange}{RGB}{255,127,14}
\definecolor{sfgreen}{RGB}{44,160,44}
\definecolor{sfred}{RGB}{214,39,40}
\definecolor{sfgray}{RGB}{80,80,80}
\lstset{
basicstyle=\ttfamily\small,
breaklines=true,
frame=single,
columns=fullflexible,
backgroundcolor=\color{black!3}
}
%% ── Figure path ───────────────────────────────────────────────────────────
\graphicspath{{figures/}}
%% ── Title metadata ────────────────────────────────────────────────────────
\title{%
\textbf{The Artemis Surface Stress Test}\\[0.4em]
\large A Stale-Pointer Aliasing Bug in the LithosAnanke Block Subsystem\\[0.3em]
\normalsize\textit{Found, Diagnosed, Fixed, and Verified Across Three Instruction-Set Architectures}
}
\author{%
R.\,A. James (Captain Bob)\\[0.2em]
\small StarForth Project \quad\texttt{robert.allan.james@gmail.com}
}
\date{2 August 2026}
%% ═══════════════════════════════════════════════════════════════════════
\begin{document}
\maketitle
\thispagestyle{empty}
\begin{abstract}
\noindent
A newly written stress test for Artemis, LithosAnanke's block-storage VM,
found a real data-corruption bug on its first completed run: 25 of 50
random-block write/verify trials (50\%) returned the wrong byte pattern on
amd64. The test's own recorded data ruled out its block-selection logic as
the cause (no duplicate logical block numbers or devblocks among the 50
trials). Four exploratory hypotheses were then tested formally rather than
read off a chart: a Wald--Wolfowitz runs test found no significant serial
clustering of failures by write order ($p=1.00$); a direct rank test
against write order agreed ($p=0.715$); sibling position within a shared
4\,KiB devblock showed no association ($p=0.674$); but logical block
number itself showed a significant association with outcome
($p<0.001$, Wilcoxon), an effect specific to this run's fixed random seed
that this report does not claim generalizes. None of these exploratory
tests, on their own, identify a mechanism. Root cause was instead found by
source code review: a stale raw-pointer alias held by the VM-level block
window cache (\texttt{src/word\_source/block\_words.c}) into a slot of the
block-subsystem's own eight-entry devblock cache
(\texttt{src/block\_subsystem.c}), which the subsystem's own FIFO eviction
relocates via struct-copy without informing the window cache holding a
pointer into it. No prior test had ever touched enough distinct blocks in
one VM session to trigger it, and the defect is independent of unrelated
same-day work on Artemis's disk-format detection. A three-site fix --
re-resolving the buffer pointer by logical block number immediately before
every write-through-cache-pointer operation, rather than trusting a
pointer captured earlier -- eliminates the defect without altering the
block subsystem's cache architecture. This is confirmed by the strongest
result in this report: a two-proportion test of pre-fix versus pooled
post-fix (three architectures, 150 trials) pass rate rejects the null
hypothesis of no change at $p = 2.04\times10^{-19}$. Re-verification:
50/50 on amd64, 50/50 on aarch64, 50/50 on riscv64, zero mismatches. The
test itself is now a permanent, on-demand fixture in the Artemis capsule
(\texttt{ART-STRESS} / \texttt{ART-STRESS-TEST}), disabled by default so
it does not add to ordinary boot time.
\end{abstract}
\section{Motivation}
Prior to this investigation, every automated exercise of Artemis's
block-storage path -- \texttt{ART-WRITE-TEST}, \texttt{ART-READ-TEST},
\texttt{ART-SELF-TEST} -- touched exactly one data block per run. That is
sufficient to prove the alloc/persist/free lifecycle works in isolation,
but it never puts real pressure on the underlying caches: the
block-subsystem's own eight-slot devblock cache
(\texttt{DISK\_CACHE\_SLOTS} in \texttt{block\_subsystem.c}) and the
VM-level ``window'' cache used by the \texttt{BLOCK}/\texttt{BUFFER}/
\texttt{UPDATE} words (\texttt{block\_words.c}) were never both forced to
evict within a single VM session.
The request behind this work was explicit: give the mounted virtual disk
a real workout -- real FORTH doing real writes, scattered randomly across
its entire surface area, on the premise that a test never exercised
before was the most likely place to find a crack.
\section{Test Design}
\texttt{ART-STRESS-TEST~( seed reps -- )} performs a two-phase write/verify
cycle against \texttt{reps} distinct, randomly chosen blocks drawn from the
full 22\,998-block Artemis data pool (logical block numbers 3076--26073):
\begin{enumerate}[nosep]
\item \textbf{Pick.} A coprime-stride walk over the index space
(stride and starting offset both drawn via \texttt{RANDOM}, stride
forced odd and checked against the two odd prime factors of 22\,998 =
$2 \times 3 \times 3833$) selects a block index guaranteed not to repeat
within the run, skipping past any index already allocated by something
else so the test never overwrites pre-existing legitimate data.
\item \textbf{Write.} The block is allocated through the same path
\texttt{BLK-ALLOC} uses internally (free-map bit set, heat born hot),
filled with a trial-derived byte pattern, and persisted.
\item \textbf{Verify.} After \emph{all} \texttt{reps} writes complete --
forcing real eviction/reload traffic through both cache layers -- each
block is re-fetched and its content checked against what was written.
\item \textbf{Record.} Every trial, pass or fail, emits one
\texttt{[ARTSTRESS]}-tagged CSV row (trial, logical block number,
pattern, result) to the serial log, plus a \texttt{HEADER} marker at
start and a \texttt{SUMMARY} marker at completion -- if a run is
killed mid-way, the summary marker is simply absent, making a
truncated dataset unmistakable rather than silently partial.
\item \textbf{Clean up.} Every block the run allocated is freed.
\end{enumerate}
The design deliberately treats this as an offline data-collection fixture,
the same posture as the existing DoE campaign: correctness of the
collected data matters more than run speed, and \texttt{ART-K-TOTAL} is
recorded before and after (informational only -- it is not a pass/fail
gate, since a background heartbeat decay tick during a long run is
expected behavior, not a defect).
The first completed run, 50 trials on amd64 with seed 424242 (the fixed
seed \texttt{ART-STRESS} uses), returned \textbf{25 passes and 25
failures} -- an exact 50\% split (Figure~\ref{fig:pass-rate}, bottom bar).
\begin{figure}[htb]
\centering
\includegraphics[width=0.8\linewidth]{pass_rate_comparison.pdf}
\caption{Pass rate per run. The pre-fix amd64 run is the only failure;
every post-fix run, on all three architectures, is 50/50.}
\label{fig:pass-rate}
\end{figure}
\section{Statistical Analysis}
\textbf{Premise.} A 50\% failure rate on the first completed run of a new
test is consistent with at least three distinct explanations, which are
not mutually exclusive: (a) a bug in the test's own block-selection logic
producing duplicate or colliding picks, (b) a genuine but narrow defect
tied to a specific access pattern (e.g.\ cache eviction timing), or (c) a
defect tied to a specific region of the disk. Duplicate logical block
numbers and duplicate devblocks among the 50 trials were checked directly
against the recorded data and neither was found, ruling out (a) as stated.
The hypotheses below were formulated to distinguish (b) from (c) before
committing to a source-code investigation in either direction.
All tests below use the pre-fix amd64 run's own recorded trial data
(\texttt{runs/amd64\_buggy.csv}, $n=50$, under
\texttt{experiments/artemis\_stress/}) and were computed by the analysis
script \texttt{analyse\_artemis\_stress.R} in that same directory; no
number in this section is hand-typed.
\begin{table}[htb]
\centering
\small
\begin{tabular}{p{0.28\linewidth}p{0.42\linewidth}p{0.22\linewidth}}
\toprule
Hypothesis & Null hypothesis ($H_0$) & Result \\
\midrule
H1 -- serial clustering &
Pass/fail outcomes are independently ordered (Wald--Wolfowitz runs test) &
26 runs observed, 26.0 expected; $z=0.000$, $p=1.00$ -- \textbf{fail to reject} \\
H1b -- write-order rank &
Trial index distribution is the same for PASS and FAIL (Wilcoxon) &
$W=332.0$, $p=0.715$ -- \textbf{fail to reject} \\
H2 -- disk location &
LBN distribution is the same for PASS and FAIL (Wilcoxon) &
$W=505.0$, $p<0.001$ -- \textbf{reject} \\
H2b -- sibling position &
Outcome independent of position (0/1/2) within a devblock ($\chi^2$) &
$\chi^2=0.790$, $\mathrm{df}=2$, $p=0.674$ -- \textbf{fail to reject} \\
H3 -- fix efficacy &
Pre-fix and post-fix trials share the same true pass rate (two-proportion test) &
$\chi^2=81.20$, $\mathrm{df}=1$, $p=2.04\times10^{-19}$ -- \textbf{reject} \\
\bottomrule
\end{tabular}
\caption{Hypothesis tests against the recorded trial data. $\alpha = 0.05$
throughout.}
\label{tab:hypotheses}
\end{table}
\textbf{H1 and H1b -- no evidence of write-order clustering.} A visual
scan of the pass/fail sequence in write order
(Figure~\ref{fig:sequence}) initially suggested runs of consecutive
failures -- trials 24, 25, 26 failing together, then 27 and 28 passing,
then 29, 30, 31 failing again. A formal runs test does not support that
reading: 26 runs were observed against 26.0 expected under a purely
random ordering with the same 25/25 split ($z=0.000$). A direct Wilcoxon
test of trial index by outcome agrees ($p=0.715$). \textbf{The naive
``recently written survives, long ago fails'' cache-timing story is not
supported by this data} and should not be asserted as the mechanism.
\begin{figure}[htb]
\centering
\includegraphics[width=0.95\linewidth]{failure_clustering.pdf}
\caption{Pass/fail by trial order, amd64 pre-fix run. Visually
suggestive of clustering; not statistically confirmed (H1, H1b).}
\label{fig:sequence}
\end{figure}
\textbf{H2 and H2b -- an unresolved location effect, specific to this
seed.} Logical block number \emph{does} show a significant association
with outcome ($p<0.001$): failing trials in this run had a lower mean LBN
($\bar{x} \approx 11{,}355$) than passing trials ($\bar{x} \approx
18{,}401$). This was not expected, and this report does not have a causal
explanation for it -- the diagnosed mechanism (Section~\ref{sec:rootcause})
is a pointer-aliasing defect tied to cache eviction \emph{order}, and
this section's H1/H1b results (above) show no association with order in
this run, which is in tension with a simple location-driven story. The most defensible
reading is that \texttt{ART-STRESS-TEST}'s seed (424242) is fixed, so the
specific sequence of devblock accesses -- and therefore which window-cache
pointers go stale -- is a deterministic, reproducible function of that
seed for this run, and some property of that one sequence happens to
correlate with LBN value without implying that block number is, in
general, a causal factor. Sibling position within a shared devblock shows
no association ($p=0.674$), which rules out the simplest region-based
explanation (three data blocks per 4\,KiB devblock, checked directly).
This finding is reported rather than smoothed over; it does not change
the fix or its verification, and Figure~\ref{fig:coverage}'s lower panel
shows no visually obvious sub-region concentration despite the test
result.
\begin{figure}[htb]
\centering
\includegraphics[width=0.85\linewidth]{surface_coverage_map.pdf}
\caption{Every trial's logical block number against trial order,
amd64. Upper panel: post-fix, all passing. Lower panel: pre-fix.}
\label{fig:coverage}
\end{figure}
\textbf{Conclusion of this section.} None of H1, H1b, or H2/H2b, singly
or together, identify a mechanism -- they narrow the search (ruling out
write-order clustering and sibling-position effects as the story) without
answering the question. Root cause was found by source code review,
described next; H3, in Section~\ref{sec:verification}, is what actually
confirms the diagnosis, by showing the identified defect's fix removes
the failures entirely and unambiguously.
\section{Root Cause}
\label{sec:rootcause}
LithosAnanke's block storage stacks two independent caches:
\begin{enumerate}[nosep]
\item \textbf{The block-subsystem's devblock cache}
(\texttt{blk\_dev\_slot\_t.cache[DISK\_CACHE\_SLOTS]}, eight entries,
\texttt{block\_subsystem.c}). When full and a ninth distinct devblock
is needed, \texttt{cache\_get\_slot()} evicts by shifting the entire
array down one position via struct assignment:
\begin{lstlisting}[language=C]
(void) cache_writeback(slot, &slot->cache[0]);
for (int i = 0; i < DISK_CACHE_SLOTS - 1; i++)
slot->cache[i] = slot->cache[i+1];
\end{lstlisting}
The \emph{addresses} of \texttt{cache[0..7]} never move; what they
\emph{hold} does, silently.
\item \textbf{The VM-level window cache}
(\texttt{vm->blk\_vm\_cbuf[BLK\_VM\_SLOTS]}, \texttt{block\_words.c}),
used by \texttt{BLOCK}/\texttt{BUFFER}. When a block loads, it stores
the \emph{raw pointer} \texttt{blk\_get\_buffer()} returns --
\texttt{c->data + pack * BLK\_FORTH\_SIZE}, pointing directly into one
of the eight \texttt{cache\_slot\_t} structs above.
\end{enumerate}
Nothing informs layer 2 when layer 1 shifts its array. Once more than
eight distinct devblocks have been touched in one VM session, a
previously-captured window pointer can point at memory that now belongs
to a completely different devblock, with no error and no signal --
just wrong data on the next read or write through it. No prior test ever
touched enough distinct devblocks in a single session to trigger this;
the surface stress test's fifty scattered writes was the first thing
that did. This mechanism does not, on its face, predict a dependency on
LBN value rather than access pattern, which is consistent with this
report's inability to causally explain the H2 result above -- the exact
per-trial trigger condition is a complex function of window-slot reuse
and devblock-array-shift interleaving that this investigation did not
instrument further, since it is not required to confirm or fix the
defect. The defect is independent of, and predates, unrelated same-day
work on Artemis's disk-format detection (\texttt{BLK-CONFIRM-FORMAT}) --
none of that work touches \texttt{cache\_get\_slot} or the window-cache
functions.
\section{Fix}
Three call sites in \texttt{block\_words.c} wrote through the stored
window pointer before calling \texttt{blk\_update()}: \texttt{block\_word\_update}
(backing the Forth word \texttt{UPDATE}), \texttt{blk\_vm\_evict}'s
all-slots-dirty fallback, and \texttt{blk\_vm\_flush\_all} (backing
\texttt{FLUSH}/\texttt{SAVE-BUFFERS}). Each now re-resolves a
\emph{guaranteed-current} pointer by logical block number immediately
before use:
\begin{lstlisting}[language=C]
uint8_t *fresh = blk_get_buffer((uint32_t) blk, 1);
if (!fresh) { vm->error = 1; return; }
memcpy(fresh, vm->memory + base, BLOCK_SIZE);
vm->blk_vm_cbuf[s] = fresh; /* keep the window's own copy in sync */
\end{lstlisting}
\texttt{blk\_get\_buffer()} internally calls \texttt{cache\_load\_devblock()},
which correctly resolves (or reloads) whichever slot currently holds that
logical block number, however the array has shifted since the window
pointer was first captured. The fix is confined entirely to the consumer
side and does not alter the block subsystem's cache architecture: never
trust a raw cache pointer across an operation boundary where the producer
may have invalidated it -- ask for the current one again, right before
using it.
\section{Verification}
\label{sec:verification}
The fix was verified by re-running the identical stress test (same seed,
same 50-trial coprime-stride sequence) on all three supported
architectures.
\begin{table}[htb]
\centering
\begin{tabular}{lrrr}
\toprule
Run & Trials & Passed & Failed \\
\midrule
amd64 (pre-fix) & 50 & 25 & 25 \\
amd64 (post-fix) & 50 & 50 & 0 \\
aarch64 (post-fix) & 50 & 50 & 0 \\
riscv64 (post-fix) & 50 & 50 & 0 \\
\bottomrule
\end{tabular}
\caption{Surface stress test results before and after the fix.}
\end{table}
Pooling the three post-fix runs against the pre-fix run and testing
$H_0:$ ``pre-fix and post-fix trials share the same true pass rate''
with a two-proportion test rejects $H_0$ at
$p = 2.04\times10^{-19}$ (Table~\ref{tab:hypotheses}, H3; 95\% CI on the
difference in pass rate: $[-0.652, -0.348]$). This is the definitive
result in this report: unlike H1/H1b/H2/H2b, which narrow the search
space without identifying a mechanism, H3 directly confirms that the
change made in Section 5 -- and only that change -- accounts for the
difference between a 50\% and a 100\% pass rate, consistently, across
three independent instruction-set architectures.
All three post-fix architectures also completed the standard acceptance
boot to \texttt{ok>} with the fix in place, and Hera's dictionary-hash
parity across amd64 and aarch64 matched exactly for identical capsule
content, confirming the fix does not disturb cross-architecture
determinism.
\section{Disposition}
The surface stress test is now a permanent fixture of the Artemis capsule
(\texttt{capsules/artemis/init.4th}, blocks 4160--4173), invocable on
demand as \texttt{ART-STRESS} (fifty trials, fixed seed) or
\texttt{<seed> <reps> ART-STRESS-TEST} for a custom run. It is not wired
into the automatic boot sequence -- block 4170 carries the entry point
commented out (\texttt{\textbackslash\ ART-STRESS}), matching the same
disable-by-default convention already used by \texttt{ACL.4th}'s
self-activation toggle, so ordinary boots pay no extra time for it. A
future maintainer can uncomment that one line to run it automatically, or
invoke it manually from the console at any time. The unresolved H2
finding (Section 4) is left as an open question for a future
investigation with additional cache-state instrumentation, not as a
loose end in the fix itself, which H3 confirms is complete.
\end{document}
@@ -0,0 +1,5 @@
"run","label","n","passed","pass_rate","phase"
"aarch64_fixed","aarch64 (post-fix)",50,50,1,"post-fix"
"amd64_buggy","amd64 (pre-fix)",50,25,0.5,"pre-fix"
"amd64_fixed","amd64 (post-fix)",50,50,1,"post-fix"
"riscv64_fixed","riscv64 (post-fix)",50,50,1,"post-fix"
1 run label n passed pass_rate phase
2 aarch64_fixed aarch64 (post-fix) 50 50 1 post-fix
3 amd64_buggy amd64 (pre-fix) 50 25 0.5 pre-fix
4 amd64_fixed amd64 (post-fix) 50 50 1 post-fix
5 riscv64_fixed riscv64 (post-fix) 50 50 1 post-fix
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
"""Convert all SVGs in charts/ to PDF for LaTeX \\includegraphics.
Mirrors experiments/bare_metal/analysis/svg_to_pdf.py, scoped to this
experiment's own charts/report/figures directories.
"""
import subprocess
import sys
from pathlib import Path
CHARTS = Path(__file__).parent / "charts"
PDFS = Path(__file__).parent / "report" / "figures"
PDFS.mkdir(parents=True, exist_ok=True)
svgs = sorted(CHARTS.glob("*.svg"))
if not svgs:
print("No SVGs found in", CHARTS)
sys.exit(1)
ok = fail = 0
for svg in svgs:
pdf = PDFS / svg.with_suffix(".pdf").name
result = subprocess.run(
["rsvg-convert", "--format=pdf", "--output", str(pdf), str(svg)],
capture_output=True, text=True
)
if result.returncode == 0:
print(f" OK {svg.name} -> {pdf.name}")
ok += 1
else:
print(f" ERR {svg.name}: {result.stderr.strip()}")
fail += 1
print(f"\n{ok} converted, {fail} failed.")
sys.exit(0 if fail == 0 else 1)
@@ -0,0 +1,20 @@
"key","value"
"h1_runs_obs",26
"h1_runs_exp",26
"h1_z",0
"h1_p",1
"h1b_W",332
"h1b_p",0.714773975516392
"h2_W",505
"h2_p",0.000107733285139378
"h2b_chisq",0.790350877192982
"h2b_df",2
"h2b_p",0.67356186013519
"h3_pre_n",50
"h3_pre_pass",25
"h3_post_n",150
"h3_post_pass",150
"h3_chisq",81.2038095238095
"h3_p",2.03599124928093e-19
"h3_ci_lo",-0.651923715768301
"h3_ci_hi",-0.348076284231699
1 key value
2 h1_runs_obs 26
3 h1_runs_exp 26
4 h1_z 0
5 h1_p 1
6 h1b_W 332
7 h1b_p 0.714773975516392
8 h2_W 505
9 h2_p 0.000107733285139378
10 h2b_chisq 0.790350877192982
11 h2b_df 2
12 h2b_p 0.67356186013519
13 h3_pre_n 50
14 h3_pre_pass 25
15 h3_post_n 150
16 h3_post_pass 150
17 h3_chisq 81.2038095238095
18 h3_p 2.03599124928093e-19
19 h3_ci_lo -0.651923715768301
20 h3_ci_hi -0.348076284231699
@@ -0,0 +1,32 @@
Artemis surface stress test -- hypothesis tests
=================================================
H1 -- pre-fix failure clustering (Wald-Wolfowitz runs test)
H0: pass/fail outcomes are independently ordered (no clustering).
Observed runs = 26 (expected under H0 = 26.00, sd = 3.50)
z = 0.000, p = 1.00e+00
-> fail to reject H0 at alpha=0.05: failures are not serially clustered.
H1b -- pre-fix outcome vs. trial/write order directly (Wilcoxon)
H0: trial index is drawn from the same distribution for PASS/FAIL
(complements H1: the runs test only catches consecutive-value
clustering, not a general rank-order trend).
W = 332.0, p = 0.715
-> fail to reject H0 at alpha=0.05: outcome is not associated with write order.
H2 -- pre-fix failure vs. disk location (Wilcoxon rank-sum test)
H0: logical block number is drawn from the same distribution
for PASS and FAIL trials (failure does not depend on location).
W = 505.0, p = 0.000
-> REJECT H0 at alpha=0.05: failure IS associated with LBN.
Secondary check -- sibling position (0/1/2) vs. outcome (chi-squared)
chi-sq = 0.790, df = 2, p = 0.674
-> fail to reject H0: outcome is not associated with sibling position.
H3 -- did the fix change the pass rate (two-proportion test)
H0: pre-fix and post-fix trials share the same true pass rate.
Pre-fix: 25/50 = 50.0% Post-fix (pooled, 3 arches): 150/150 = 100.0%
chi-sq = 81.20, df = 1, p = 2.04e-19
95% CI on the difference in pass rate: [-0.652, -0.348]
-> REJECT H0 at alpha=0.05: the fix DID CHANGE the pass rate.
@@ -0,0 +1,51 @@
trial,lbn,pat,result
0,11306,0,1
1,8587,1,1
2,3149,2,1
3,23428,3,1
4,20709,4,1
5,15271,5,1
6,12552,6,1
7,7114,7,1
8,4395,8,1
9,24674,9,1
10,21955,10,1
11,16517,11,1
12,25920,12,1
13,23201,13,1
14,20482,14,1
15,17763,15,1
16,12325,16,1
17,24447,17,1
18,21728,18,1
19,16290,19,1
20,13571,20,1
21,25693,21,1
22,22974,22,1
23,20255,23,1
24,17536,24,1
25,12098,25,1
26,3941,26,1
27,24220,27,1
28,21501,28,1
29,16063,29,1
30,13344,30,1
31,5187,31,1
32,25466,32,1
33,22747,33,1
34,20028,34,1
35,11871,35,1
36,23993,36,1
37,21274,37,1
38,13117,38,1
39,25239,39,1
40,22520,40,1
41,19801,41,1
42,17082,42,1
43,14363,43,1
44,23766,44,1
45,21047,45,1
46,18328,46,1
47,12890,47,1
48,4733,48,1
49,25012,49,1
1 trial lbn pat result
2 0 11306 0 1
3 1 8587 1 1
4 2 3149 2 1
5 3 23428 3 1
6 4 20709 4 1
7 5 15271 5 1
8 6 12552 6 1
9 7 7114 7 1
10 8 4395 8 1
11 9 24674 9 1
12 10 21955 10 1
13 11 16517 11 1
14 12 25920 12 1
15 13 23201 13 1
16 14 20482 14 1
17 15 17763 15 1
18 16 12325 16 1
19 17 24447 17 1
20 18 21728 18 1
21 19 16290 19 1
22 20 13571 20 1
23 21 25693 21 1
24 22 22974 22 1
25 23 20255 23 1
26 24 17536 24 1
27 25 12098 25 1
28 26 3941 26 1
29 27 24220 27 1
30 28 21501 28 1
31 29 16063 29 1
32 30 13344 30 1
33 31 5187 31 1
34 32 25466 32 1
35 33 22747 33 1
36 34 20028 34 1
37 35 11871 35 1
38 36 23993 36 1
39 37 21274 37 1
40 38 13117 38 1
41 39 25239 39 1
42 40 22520 40 1
43 41 19801 41 1
44 42 17082 42 1
45 43 14363 43 1
46 44 23766 44 1
47 45 21047 45 1
48 46 18328 46 1
49 47 12890 47 1
50 48 4733 48 1
51 49 25012 49 1
@@ -0,0 +1,51 @@
trial,lbn,pat,result
0,11306,0,0
1,8587,1,0
2,3149,2,0
3,20709,3,1
4,15271,4,1
5,12552,5,0
6,9833,6,1
7,7114,7,1
8,4395,8,0
9,24674,9,0
10,21955,10,1
11,16517,11,0
12,5641,12,1
13,25920,13,1
14,23201,14,1
15,20482,15,1
16,17763,16,1
17,12325,17,0
18,21728,18,0
19,16290,19,1
20,13571,20,0
21,8133,21,0
22,25693,22,1
23,17536,23,1
24,12098,24,0
25,9379,25,0
26,3941,26,0
27,21501,27,1
28,16063,28,1
29,13344,29,0
30,7906,30,0
31,5187,31,0
32,25466,32,1
33,22747,33,0
34,11871,34,0
35,3714,35,0
36,23993,36,1
37,21274,37,1
38,13117,38,0
39,22520,39,1
40,19801,40,0
41,17082,41,1
42,14363,42,1
43,8925,43,0
44,18328,44,1
45,12890,45,1
46,10171,46,0
47,4733,47,0
48,22293,48,1
49,16855,49,1
1 trial lbn pat result
2 0 11306 0 0
3 1 8587 1 0
4 2 3149 2 0
5 3 20709 3 1
6 4 15271 4 1
7 5 12552 5 0
8 6 9833 6 1
9 7 7114 7 1
10 8 4395 8 0
11 9 24674 9 0
12 10 21955 10 1
13 11 16517 11 0
14 12 5641 12 1
15 13 25920 13 1
16 14 23201 14 1
17 15 20482 15 1
18 16 17763 16 1
19 17 12325 17 0
20 18 21728 18 0
21 19 16290 19 1
22 20 13571 20 0
23 21 8133 21 0
24 22 25693 22 1
25 23 17536 23 1
26 24 12098 24 0
27 25 9379 25 0
28 26 3941 26 0
29 27 21501 27 1
30 28 16063 28 1
31 29 13344 29 0
32 30 7906 30 0
33 31 5187 31 0
34 32 25466 32 1
35 33 22747 33 0
36 34 11871 34 0
37 35 3714 35 0
38 36 23993 36 1
39 37 21274 37 1
40 38 13117 38 0
41 39 22520 39 1
42 40 19801 40 0
43 41 17082 41 1
44 42 14363 42 1
45 43 8925 43 0
46 44 18328 44 1
47 45 12890 45 1
48 46 10171 46 0
49 47 4733 47 0
50 48 22293 48 1
51 49 16855 49 1
@@ -0,0 +1,51 @@
trial,lbn,pat,result
0,11306,0,1
1,8587,1,1
2,3149,2,1
3,23428,3,1
4,20709,4,1
5,15271,5,1
6,12552,6,1
7,7114,7,1
8,4395,8,1
9,24674,9,1
10,21955,10,1
11,16517,11,1
12,25920,12,1
13,23201,13,1
14,20482,14,1
15,17763,15,1
16,12325,16,1
17,24447,17,1
18,21728,18,1
19,16290,19,1
20,13571,20,1
21,25693,21,1
22,22974,22,1
23,20255,23,1
24,17536,24,1
25,12098,25,1
26,3941,26,1
27,24220,27,1
28,21501,28,1
29,16063,29,1
30,13344,30,1
31,5187,31,1
32,25466,32,1
33,22747,33,1
34,20028,34,1
35,11871,35,1
36,23993,36,1
37,21274,37,1
38,13117,38,1
39,25239,39,1
40,22520,40,1
41,19801,41,1
42,17082,42,1
43,14363,43,1
44,23766,44,1
45,21047,45,1
46,18328,46,1
47,12890,47,1
48,4733,48,1
49,25012,49,1
1 trial lbn pat result
2 0 11306 0 1
3 1 8587 1 1
4 2 3149 2 1
5 3 23428 3 1
6 4 20709 4 1
7 5 15271 5 1
8 6 12552 6 1
9 7 7114 7 1
10 8 4395 8 1
11 9 24674 9 1
12 10 21955 10 1
13 11 16517 11 1
14 12 25920 12 1
15 13 23201 13 1
16 14 20482 14 1
17 15 17763 15 1
18 16 12325 16 1
19 17 24447 17 1
20 18 21728 18 1
21 19 16290 19 1
22 20 13571 20 1
23 21 25693 21 1
24 22 22974 22 1
25 23 20255 23 1
26 24 17536 24 1
27 25 12098 25 1
28 26 3941 26 1
29 27 24220 27 1
30 28 21501 28 1
31 29 16063 29 1
32 30 13344 30 1
33 31 5187 31 1
34 32 25466 32 1
35 33 22747 33 1
36 34 20028 34 1
37 35 11871 35 1
38 36 23993 36 1
39 37 21274 37 1
40 38 13117 38 1
41 39 25239 39 1
42 40 22520 40 1
43 41 19801 41 1
44 42 17082 42 1
45 43 14363 43 1
46 44 23766 44 1
47 45 21047 45 1
48 46 18328 46 1
49 47 12890 47 1
50 48 4733 48 1
51 49 25012 49 1
@@ -0,0 +1,51 @@
trial,lbn,pat,result
0,11306,0,1
1,8587,1,1
2,3149,2,1
3,23428,3,1
4,20709,4,1
5,15271,5,1
6,12552,6,1
7,7114,7,1
8,4395,8,1
9,24674,9,1
10,21955,10,1
11,16517,11,1
12,25920,12,1
13,23201,13,1
14,20482,14,1
15,17763,15,1
16,12325,16,1
17,24447,17,1
18,21728,18,1
19,16290,19,1
20,13571,20,1
21,25693,21,1
22,22974,22,1
23,20255,23,1
24,17536,24,1
25,12098,25,1
26,3941,26,1
27,24220,27,1
28,21501,28,1
29,16063,29,1
30,13344,30,1
31,5187,31,1
32,25466,32,1
33,22747,33,1
34,20028,34,1
35,11871,35,1
36,23993,36,1
37,21274,37,1
38,13117,38,1
39,25239,39,1
40,22520,40,1
41,19801,41,1
42,17082,42,1
43,14363,43,1
44,23766,44,1
45,21047,45,1
46,18328,46,1
47,12890,47,1
48,4733,48,1
49,25012,49,1
1 trial lbn pat result
2 0 11306 0 1
3 1 8587 1 1
4 2 3149 2 1
5 3 23428 3 1
6 4 20709 4 1
7 5 15271 5 1
8 6 12552 6 1
9 7 7114 7 1
10 8 4395 8 1
11 9 24674 9 1
12 10 21955 10 1
13 11 16517 11 1
14 12 25920 12 1
15 13 23201 13 1
16 14 20482 14 1
17 15 17763 15 1
18 16 12325 16 1
19 17 24447 17 1
20 18 21728 18 1
21 19 16290 19 1
22 20 13571 20 1
23 21 25693 21 1
24 22 22974 22 1
25 23 20255 23 1
26 24 17536 24 1
27 25 12098 25 1
28 26 3941 26 1
29 27 24220 27 1
30 28 21501 28 1
31 29 16063 29 1
32 30 13344 30 1
33 31 5187 31 1
34 32 25466 32 1
35 33 22747 33 1
36 34 20028 34 1
37 35 11871 35 1
38 36 23993 36 1
39 37 21274 37 1
40 38 13117 38 1
41 39 25239 39 1
42 40 22520 40 1
43 41 19801 41 1
44 42 17082 42 1
45 43 14363 43 1
46 44 23766 44 1
47 45 21047 45 1
48 46 18328 46 1
49 47 12890 47 1
50 48 4733 48 1
51 49 25012 49 1
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+31 -8
View File
@@ -177,12 +177,20 @@ static int blk_vm_evict(VM *vm) {
return s;
}
}
/* All slots dirty: evict round-robin, sync back to C buffer first. */
/* All slots dirty: evict round-robin, sync back to C buffer first.
* Re-resolve the buffer pointer by LBN rather than trusting the
* stored one -- the block-subsystem's own devblock cache may have
* shifted (struct-copy eviction) since this slot was populated,
* which silently invalidates any raw pointer captured earlier. */
int s = vm->blk_vm_next;
vm->blk_vm_next = (vm->blk_vm_next + 1) % BLK_VM_SLOTS;
vaddr_t base = BLK_VM_WINDOW_BASE + (vaddr_t)s * BLOCK_SIZE;
memcpy(vm->blk_vm_cbuf[s], vm->memory + base, BLOCK_SIZE);
blk_update(vm->blk_vm_lbn[s]);
uint8_t* fresh = blk_get_buffer(vm->blk_vm_lbn[s], 1);
if (fresh)
{
memcpy(fresh, vm->memory + base, BLOCK_SIZE);
blk_update(vm->blk_vm_lbn[s]);
}
vm->blk_vm_lbn[s] = 0;
vm->blk_vm_cbuf[s] = NULL;
vm->blk_vm_dirty[s] = 0;
@@ -230,13 +238,19 @@ static vaddr_t blk_vm_assign(VM *vm, uint32_t lbn) {
return BLK_VM_WINDOW_BASE + (vaddr_t)s * BLOCK_SIZE;
}
/* Sync all dirty slots to their C buffers and flush the subsystem. */
/* Sync all dirty slots to their C buffers and flush the subsystem.
* Re-resolve each buffer pointer by LBN rather than trusting the stored
* one -- see blk_vm_evict for why a stored pointer can go stale. */
static void blk_vm_flush_all(VM *vm) {
for (int i = 0; i < BLK_VM_SLOTS; i++) {
if (vm->blk_vm_cbuf[i] != NULL && vm->blk_vm_dirty[i]) {
vaddr_t base = BLK_VM_WINDOW_BASE + (vaddr_t)i * BLOCK_SIZE;
memcpy(vm->blk_vm_cbuf[i], vm->memory + base, BLOCK_SIZE);
blk_update(vm->blk_vm_lbn[i]);
uint8_t* fresh = blk_get_buffer(vm->blk_vm_lbn[i], 1);
if (fresh)
{
memcpy(fresh, vm->memory + base, BLOCK_SIZE);
blk_update(vm->blk_vm_lbn[i]);
}
vm->blk_vm_dirty[i] = 0;
}
}
@@ -267,14 +281,23 @@ void block_word_buffer(VM *vm) {
vm_push(vm, CELL(vaddr));
}
/* UPDATE ( -- ) : sync current SCR slot to C layer and mark dirty */
/* UPDATE ( -- ) : sync current SCR slot to C layer and mark dirty.
* Re-resolve the buffer pointer by LBN rather than trusting the stored
* one -- see blk_vm_evict for why a stored pointer can go stale. */
void block_word_update(VM *vm) {
cell_t blk = vm_load_cell(vm, vm->scr_addr);
if (blk == 0 || !blk_is_valid((uint32_t) blk)) { vm->error = 1; return; }
int s = blk_vm_find(vm, (uint32_t) blk);
if (s < 0) { vm->error = 1; return; }
vaddr_t base = BLK_VM_WINDOW_BASE + (vaddr_t)s * BLOCK_SIZE;
memcpy(vm->blk_vm_cbuf[s], vm->memory + base, BLOCK_SIZE);
uint8_t* fresh = blk_get_buffer((uint32_t)blk, 1);
if (!fresh)
{
vm->error = 1;
return;
}
memcpy(fresh, vm->memory + base, BLOCK_SIZE);
vm->blk_vm_cbuf[s] = fresh;
vm->blk_vm_dirty[s] = 1;
blk_update((uint32_t) blk);
}