Repository navigation
Conversation
…l 5.26 5.0 left open that 5.18 retires `Display` and `Show` while (g) keeps `clazel-text`, which uses them, until 5.26. Decision: 5.18 adds `Paint` and the new messages beside `Display` and `Show`, which stay unchanged with their proven theorems; the restated theorems cover both; 5.26 deletes the old path with `clazel-text` in one pull request. Retiring `Display` early would leave no working browser and no end-to-end evidence until 5.26, and the "doubling" (g) feared does not arise since the `Show` theorems already exist. Taken by a Claude session on the maintainer's delegation, from the plan's goals. The Status table and the roadmap line are left as they are, to be refreshed separately. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…st form Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crates/clazel-sys/src/macos/window.rs gets its own lists: ten foreign functions (the Objective-C runtime, pthread_main_np, CGSessionCopyCurrentDictionary, and the Core Foundation and Core Graphics calls that make a CPU image) and 19 casts of objc_msgSend. No new form: the file uses #153's dispatcher cast exactly. A planted test shows the lists are per file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d contracts (5.17c) The decisions are in each module's header: the blocklist format and its matching (src/blocklist.rs, data/blocklist, shipped empty), the start that checks the adapter before any list (src/start.rs), and the crash policy as a pure state machine for the kernel to take over in 5.18 (src/policy.rs), with its contracts: invariants kept, contained, sites, restarts bounded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h policy (5.17c)
phase-5.md item 5.17c. Gpu::start is now the only way to make a Gpu: it
reads the backend's adapter (a new Backend::adapter) and checks it against
the shipped blocklist before any list, answering GpuReady { software } or a
refusal. The blocklist parser is ours (rule 8), bounded and panic-free. The
crash policy steps one tab's record and the shared site record per event,
rules 1 to 4 of the item.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…used, Viewport, Input, GpuViewport) The schema gains the messages of phase-5.md item 5.18 beside Display (tag 10), which stays unchanged until 5.26, as decided under 5.0 on 2026-11-09. No tag is reused. A GPU process's frames are decoded with decode_gpu_message, which accepts only GPU-to-kernel messages; Title joins the renderer-to-kernel set. Spec and contract only (CLAUDE.md rule 3); the codec and proofs follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CSS 2.1 §17 with CSS Tables 3 (Editor's Draft at csswg-drafts e13fe879) for automatic layout and HTML §4.9.12.1 for the grid. The states (HTML's forming variables, a running share, a column's measures, the table width input, a row's extent, cell alignment), their invariants, and each step's contract, before the implementation. The module's header lists every modelling choice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…5.17c) Blocklist: the shipped table parses and is empty; each field; hostile text (tabs, CR, non-ASCII, NUL, bad fields, empty ranges, limits) refused with its line; matching at range ends, wildcards, unknown versions, software adapters; arbitrary bytes never panic. Start: every answer. Policy: every transition, the restart bound over an hour of crashes, and containment and the invariants over a pseudo-random trace. The plan's test: a fake adapter the table lists is refused, the policy starts software, and every scene drawn there equals the reference. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…essages The exec codec reads and writes Title, FrameReady, GpuReady, GpuRefused, Viewport, Input and GpuViewport, and decode_gpu_message accepts only a GPU process's messages; both are proven against the spec (Verus: 210 verified, 0 errors). lemma_gpu_decode_direction: no message is accepted from both kinds of process. Twelve new mutations, each with a certificate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New macos/window.rs (Apple silicon): the shared application, an NSWindow, its content view's layer and sublayers, a CPU image as layer contents (CFDataCreate, CGImageCreate), an IOSurface as layer contents, and events read with nextEventMatchingMask:untilDate:inMode:dequeue: and handed back with sendEvent: only when the caller says so. No class, delegate or callback is defined in Rust. MainThread (pthread_main_np) gates everything; App refuses to start without a window server (CGSessionCopyCurrentDictionary), where AppKit would end the process. Accessors are sent only to event types that have them; rectangles are checked finite; a window is not released when closed. metal::Surface gains a crate-internal as_raw. The audit is outstanding: this session wrote the change and must not audit it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/appkit.rs: Window::open, size, next_event, draw, set_title and dpi as on Windows, plus show_page/hide_page for the page's IOSurface, activate and close. Everything runs on the main thread and polls AppKit's queue; size, scale and closing are polled as on Windows. Two layers in a layer-hosting content view: the page below, clipped to its rectangle, and the host's strip above. The decisions are at the top of the file. src/nsevent.rs: what an AppKit event means, as pure functions with tests on recorded events (clicks, wheel and trackpad deltas, keys and typed code units, which events go back to AppKit, closing, placing layers). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests/smoke_macos.rs is its own main (harness = false), since AppKit needs the main thread. It opens a 240 by 160 point window that takes no focus, checks the first Expose, draws, refuses a wrong length and a NUL title, reads the dpi, shows and hides a page surface twice the content area, refuses AppKit off the main thread, closes and sees Event::Closed through the queue. A 20 s watchdog ends the process. It skips, saying why on stderr, under Miri, off Apple silicon macOS, and without a window server session. examples/pattern.rs now runs on macOS too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`cargo xtask check` rejects any `admit(` as Verus's proof escape hatch (CLAUDE.md rule 4), whatever crate it is in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…orems' statements
Spec and statements only (CLAUDE.md rule 3); the exec kernel and the proofs
follow. As decided under 5.0 on 2026-11-09, the new path is added beside
Display and Show, which are unchanged.
- Events FromGpu (a frame from the GPU process paired with renderer id),
Resize and Input; outputs ToGpu and Paint; Policy::Gpu.
- Every renderer is spawned with its GPU process, named by the same id;
Kill ends both. A cross-origin navigation replaces both and gives the new
pair the tab's viewport before the commit.
- State gains the tabs' viewports and the renderers' titles (each entry
tagged with the renderer's lock, forgotten when the renderer goes).
- from_gpu: FrameReady gives Paint { tab, url, title, buffer, width,
height } from kernel state when the renderer is committed, the buffer is
in the ring of 3 and the frame fits the viewport; a frame over the
viewport is dropped (a resize race), anything else disallowed kills the
pair.
- Statements: paint_only_own_tab, paint_address_bar (the restated
address-bar theorem's second half), paint_fits_viewport,
channel_only_own_tab, input_only_to_own_tab, viewport_only_to_own_tab;
reply_only_to_sender, net_only_to_tab and event_tab restated over the new
outputs and events; noninterference's domains, purge, addressing and
relation extended to them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(5.21) 227 unsafe blocks (468 lines), of which 40 dispatcher casts: 21 for Metal and 19 for the window. Was 138 (338 lines) with 21 casts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CLAZEL_WINDOW_TAKE_FOCUS=1 also runs Window::activate (Regular policy, activateIgnoringOtherApps, makeKeyAndOrderFront) and checks the window keeps working. The default run does not take focus and says so on stderr. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sh policy (5.17c) Proved for every size: the matcher against its statement (matches_any), and from any state meeting the invariants, the site rule (note_exit_any) and one tab's step (tab_step_any: invariants kept, the restart bound's step, demotion by refusal only before GpuReady). Bounded: the version, hex and drivers parsers on short inputs, and containment over two tabs. Each ran alone under the 20-minute and 8 GB caps; all passed, none was capped (peak 4.62 GB, policy_contained_any). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (5.15) Replaces 5.14's table placeholder. grid.rs: HTML §4.9.12.1 forming a table over the box tree (CSS Tables 3 §3.3.1), colspan clamped to 1000 and rowspan to 65534, downward-growing cells, then §3.3.2's track merging; at most 65,536 columns. measure.rs: §3.8.2 cell measures, §3.8.3 column measures by increasing span, GRIDMIN/GRIDMAX and §3.9.1's used width. distribute.rs: an exact running share, §3.9.3's sizing guesses and excess rules, CSS 2.1 §17.5.2.1 fixed layout. rows.rs: §17.5.3 row extents and vertical-align, spanning rows, §3.10.5's table height. The walk (flow/block.rs) sizes the wrapper from its table, lays each cell out with 5.14's code as a BFC root at its columns' width, then places cells (extra padding for vertical-align), rows, columns and their groups. Quirks §3.8, §3.9, §3.10, §3.13. Two ERRATA entries (CSS Tables 3 §3.9.1 table offsets, §3.8.3 zero baseline max-content width). The golden dumps are updated in the next commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What is built (the blocklist, the checked start, the crash policy for the kernel), the decisions, the evidence with the Kani and Miri tables, and what is not done with what each part waits for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A table wrapper's children are its captions and its table box, which is inline-level for inline-table; has_inline_content took that for an inline formatting context, so the wrapper laid its table out twice (as a line's atomic and in its flow). The wrapper's children are always in its flow (CSS 2.1 §17.4). Found by the inline table baseline test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
30 unit tests in src/table/tests.rs, each expected position worked out by hand in its comment: the grid (spacing, colspan, track merging, the ERRATA equal share, rowspan, rowspan=0, header and footer groups, the column bound), column widths (excess by max-content, percent and pixel columns, between two guesses with exact rounding, border-box width, min-width, CAPMIN, auto margins, fixed layout and its excess), heights (each vertical-align, baseline alignment, row and cell height in both modes, table height to auto rows, inline table baseline), HTML's row align, the quirks §3.8, §3.9, §3.10 and §3.13, 100,000 nested tables without recursion, and the steps alone. The golden dumps change only in documents with tables (23 files): tables are now laid out as tables. The HN-shaped page (page-table-news.txt) was checked by hand against the rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Step harnesses from any state satisfying the invariant (every size): HTML's forming steps (start_row, place, end_row, end_group), a column's measures (widen), a row's extent; any input: the colspan/rowspan clamps, a cell's outer measures, §3.9.1's used width, the table's intrinsic widths, a cell's extra padding; bounded: a percentage of a length (up to 100% of 2^14 px). The floors are now exact 64-bit arithmetic by quotient and remainder (no i128), and a share's weights are capped at 2^31 (larger sums are shifted down). Removed at the 20 minute cap (CBMC's division): percent_of_any, share_step_any and its shrunk share_step_small; the running share is not proved by Kani, and a native test checks both floors against 128-bit arithmetic. flow::proofs::role_any re-run for the new roles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the new events The exec step implements FromGpu, Resize and Input, the GPU spawn of every renderer, the viewports and the lock-tagged titles (exec/surfaces.rs), and is proven equal to the spec. Every existing theorem is re-proven over the enlarged event and output sets: the noninterference unwinding gains FromGpu (its renderer's domain, no declassified part), Resize and Input (kept by every purge, with no new hypothesis); rel gains equal viewports and equal titles under the observer's locks (surface_rel.rs). New proofs: theorem_paint_only_own_tab, theorem_paint_fits_viewport, theorem_channel_only_own_tab (restated over ids before its proof), theorem_input_only_to_own_tab, theorem_viewport_only_to_own_tab, and the Paint half of theorem_address_bar_integrity. Verus: 640 verified, 0 errors. Five existing mutations' find strings follow the code they mutate (their expect is unchanged); eight new ones. The host shell starts no GPU process in text mode. README for the crate, with the decisions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e their lemma fails Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r again Its find text also matched the new is_from_gpu's last arm, which comes first now; it names is_from_renderer's own arm instead. expect unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d, StyleFailed Tags 14 and 110 to 113, none reused or shared with 5.18's. A renderer's request id names its own fetch, not an identity. Spec only (rule 3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Audited by a session that wrote no part of clazel-sys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The host shell now carries out SubConnect, SubSend and SubClose on the same network Driver as a navigation's connections, and turns a sub-fetch connection's reports into SubNet. clazel-net's SubConns numbers the kernel's (renderer, serial, hop) names from 2^63 up (the kernel's navigation connections count from 0) and maps reports back; it decides nothing. clazel-net gains clazel-kernel (ours) for the records. The host shell (crates/clazel/src) is 292 lines, under its limit of 300. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GPU process reads the kernel's channel and the list socket on two threads, so a list the renderer wrote after the kernel's Viewport can be read before the kernel's GpuViewport when both are already waiting (a process slow to start). It was drawn on a 1 by 1 target and its frame was lost. Lists are now held while there is no viewport (at most 16, and 256 MiB), and drawn in order, each giving a frame, when one arrives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d (5.26) The stand-in that writes a hand-built list now reads the kernel's frames (Perl, sysread) and writes its list once BodyEnd has arrived, when a FrameReady is allowed, instead of sleeping a fixed second. Its handle on the list socket lives as long as the script: a lexical one closed the socket at the end of the loop iteration, and the GPU process, seeing its list socket closed, ended before it took the kernel's viewport (traced: 4 runs in 20 lost the frame that way). Measured after both fixes: a_hand_built_list_reaches_a_paint_under_the_kernels_url passed 20 of 20. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…through real processes (5.19, 5.26) `Host::event` stepped a leading `Tick` and dropped its outputs, so the kernel's sweep (a timed-out sub-fetch's `SubClose` and `StyleFailed`) never reached the network or the renderer. Its outputs are now carried out with the event's. `pump` steps a `Tick` when nothing arrives, so the kernel's clock moves on an idle page and its 60 s sheet timeout fires. Tests through the host, the kernel, the real renderer and the real GPU process against the in-process server (a module of `browse_gpu.rs`, which shares its support): - a page styled only by its sheets is painted with them: green from the same-origin sheet, blue from the cross-origin one; - the same-origin sheet request carries the page's cookie, the cross-origin one none, and goes to its own host (the server's record); - a sheet that never answers blocks painting until the host's clock passes the kernel's timeout, then the page is painted without it; - the HN-shaped fixture is served with its sheet, which is asked for and changes the frame (against a 404 sheet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
….25) `Render` for `clazel browse --headless OUT --size 800x600 URL`: each page is served by the in-process server under its WPT path (WPT's content type by extension), and the PNG it writes (RGB) is decoded to the runner's RGBA. A module of `browse_gpu.rs`. - Plumbing, with pages written here: a green square matches its border-drawn reference and mismatches a red one (pass); a test whose match is the red square fails with 10 000 pixels different. - The vendored WPT reftests in the subset (`clazel-style`'s `wpt-mediaqueries`, 17) are run and counted, not asserted. Here: 0 pass, 0 fail, 4 not run (script: `relative-units-001` to `-004`), 13 not run because their reference `css/reference/ref-filled-green-100px-square.xht` is not vendored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… over three runs (5.25, 5.26) Stored with its provenance in the fixture's README. Nothing compares against it yet. CI's lavapipe comparison is a different driver and needs its own image. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ces (5.19) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Default 30 s as before; the address bar tests use 5. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Perl stand-in GPU process paired with the real renderer: a frame claimed larger than the viewport is dropped by the kernel (no Paint, nothing killed, 5.18's decision); pixels larger than claimed, a header that is not a buffer, a buffer too small and a truncated one are painted by the kernel (it cannot see bytes) but never shown, and browse --headless ends with its error. A URL-shaped title is only Paint.title; a page with -1e9px margins paints within the content area, whose layer sits below the strip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The crash policy (5.17c) is not in the kernel yet, so this records rather than requires: after the GPU process is killed, the renderer's next list write fails (broken pipe, exit 70), the kernel drops the tab's renderer, and nothing restarts either; the host stays up and logs both exits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each case through a real sandboxed renderer and GPU process, with its outcome, a stated time limit, the renderer's peak against item 4.13's and 5.20's terms (plus a stated 64 bytes per byte of style text, which the README leaves unbounded) and the GPU process's against 1 GiB: CSS bombs in inline style elements, seven malicious fonts (tests/support/fonts.rs edits DejaVu Serif), a million elements flat and nested, a million-column table, colspan=1000 everywhere, and 1e9px font sizes, widths and margins. Measured on an M1 Pro, debug build, each case alone under an 8 GB and 10-minute cap. Two findings, ignored with their numbers: 10,000 rules over 100,000 elements, and a million nested elements, paint nothing within 600 s. Six more cases that are slow or allocate gigabytes are ignored with theirs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (5.26) Server::kernel now gives the frames of held lists; a viewport with none held gives none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No conflicts to resolve: git merged every file on its own. Brings in the GPU process's held list (ce60b42), the stand-in renderer waiting for its commit (9b2c093), browse --headless --wait, and 5.26's hostile-page, address-bar and GPU-kill tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ssed (5.23, 5.26)
`spawn_gpu` and `spawn_with_list` clear a socket's close-on-exec flag, and
every program started before the host's copy is closed inherits it. Their
documentation put the duty on the caller ("the host must start nothing
else"), which the host shell meets with one thread. A process with several
hosts does not: `browse_gpu.rs` runs its tests, each with its own host, on
parallel threads.
Measured on this branch (merged with hostile-pages' held-list fix), 15
parallel runs of `browse_gpu`: 3 failed, with renderers SIGKILLed right after
commit, GPU processes and renderers whose `--list-fd` was not their socket
(`cannot open the list socket N: Permission denied`: a read-only file sat at
that number), and lost frames. 15 runs with `--test-threads 1`: none failed.
So `launch` now takes one process-wide lock (`alone`) for every start, from
clearing the flag to closing the host's copy, and makes it public. The tests'
own starts (`clazel browse --headless`, the fixture's `cargo build`) take it
through `support/gpu.rs`'s `output`. With the lock in `launch` only: 2 of 15
runs failed, both in tests that start `browse` outside it. With the tests'
starts locked too: 1 of 15 (below).
Not fixed: in 1 run of 15, `a_real_page_repaints_for_viewports_and_scrolls_
and_follows_a_click` got commit and `Title`, no process ended, and no `Paint`
came in 20 s. The host's log does not record the GPU process's messages, so
where that frame was lost is not known.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The renderer draws a notice for any type but HTML and plain text, and WPT serves `.xht` and `.xhtml` as `application/xhtml+xml`. So an XHTML test, or a test whose reference is XHTML, is "not run: XHTML". Decided by the path, so a reference that is not vendored is judged too, before anything is drawn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`css/reference/ref-filled-green-100px-square.xht` (568 bytes) and `css/mediaqueries/reference/ref-green-body.xht` (434 bytes), byte for byte from WPT 4ad507b28db0a636f528def9161d109fe11b93a3, with WPT's licence and their SHA-256 in `tests/wpt-references/README.md`. They were fetched by another session with the maintainer's approval, and checked against those hashes here. They are laid out by WPT path beside the runner, not in `tests/data/`, which holds only the inputs of `xtask`'s converters. The runner loads them, and the vendored-reftests test now asserts every reference its 17 tests name is present. The counts do not change: both references are XHTML. HTML's navigation hands an XML MIME type such as `application/xhtml+xml` to its XML document loading, which needs an XML parser this renderer does not have. So the 13 stay "not run: XHTML", and the 4 `relative-units` pages "not run: script". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eline (5.26) The page and its linked sheet from the loopback rustls server the host tests use (TLS 1.3, ChaCha20-Poly1305, X25519, the clazel-crypto test certificates): the sheet comes through FetchStyle on its own TLS connection (SubConnect honours tls), its request carries the cookie the same-origin page set, and the painted frame has the sheet's colour. Written while trying the https demo on news.ycombinator.com, which worked in fetch, headless and window mode with no change: this pins down the path it took. Nothing fetched from the real site is committed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A release clazel looks for clazel-gpu and clazel-renderer beside itself in target/release, but the fallback ran a plain cargo build, which puts them in target/debug, so the binary it wanted never appeared. Pass --release when the running binary is in a release directory. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
A chevron at the strip's left, drawn by the host without the font: dark when the tab's list of committed URLs has somewhere to go back to, pale when not. A click on it goes back as Command-[ does and drops any edit. The field starts 6 points after it. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Part 1: renderer: the graphical pipeline, documents to display lists with Viewport, Input and Title (5.20, before 5.19)(The building session's description of this part of the branch, posted as a comment for length. Where it says something waits, see the PR description: most of it was done further up the branch. Its statement that the This branch joins the stacks; merge it after all of these:
The merged branches are untouched. CI has not run on this branch or any branch below it. Item 5.19 ( WhatItem 5.20 of
Messages: The text path is unchanged. New dependencies, all our own crates: Decisions (maintainer away; recorded in the crate README and
|
| Record | Bytes |
|---|---|
| computed style | 432 |
| box | 120 |
| fragment | 96 |
| line box | 56 |
| glyph | 24 |
| paint item | 56 |
| 5.17a item | 32 |
The computed table at the 4,000,000-node limit is about 1.7 GB. The bound is not measured: 5.26's hostile pages measure it.
Evidence
- Tests (
src/page/tests.rs, 7, plus 9 existing). Each runs documents throughrun_graphicswith in-memory sockets and the vendored DejaVu Serif:- A
styleelement and astyleattribute to pixels: every list passesclazel-displaylist'sValidator(kept across lists, as in the GPU process). Drawn byclazel-gpu's software backend: the body's red at (50,5), the div's blue at (5,5), dark text pixels below. The title "A page" is stripped and collapsed. - Viewports lay out again: a narrower one wraps a line lower, and fewer masks are uploaded the second time.
- Scrolling is clamped: to 450 in a 500px page with a 50px viewport, and back to 0. A scroll that changes nothing sends no list.
- A click on a link sends
Navigatewith the URL resolved against the base; a click elsewhere sends nothing. text/plainand animage/pngnotice are drawn as glyphs.- Titles are sent only when they change; nothing is painted before a viewport.
- The sheet wait seam, with and without fetches:
media=printdoes not block; an answer clears its link.
- A
cargo test -p clazel-renderer -j 4: 16 + 1 + 11 + 13 pass.cargo test -p clazel-layout -j 4 --lib: 158 pass.cargo test -p clazel -p clazel-kernel -j 4(Phases 2 to 4 through the real sandboxed renderer's text path, and the kernel): all pass (2 ignored, as before).cargo fmt --all -- --check,cargo clippy --workspace --all-targets --locked -j 4 -- -D warnings, andcargo xtask check(RESULT: STATIC CHECKS PASSED) pass.- No Kani or Miri for this item: the renderer is Tier 0, and the brief asked for tests of the pipeline. The kani lock was not taken.
What waits
- Item 5.19 (
FetchStyle,Style*) is not pushed yet, so linked sheets do not load.- The seam is
SheetWait:start(sources, device, fetch = true, encoding), aFetchStyleper index inpending,answer(index, response, encoding)per reply, and render whenready(). Page::loadcallsstartwithfetch = falsetoday;run_graphicsmust also hold a page until it is ready.- I will merge
claude/style-fetchesand finish this when it is pushed.
- The seam is
- The list socket is not wired, so the renderer binary still runs the text path.
- The host must create a socket pair at the GPU spawn and pass the renderer its end.
- Adopting an inherited descriptor and clearing its close-on-exec flag needs a new audited
clazel-syscall (phase-5.md, 5.18's plan), outside this item. The renderer has nounsafeto do it. run_graphicsis complete and tested with in-memory sockets.
- The GPU process's list reader (decode, validate, draw per frame from the socket) is not built. The framing it will read is this branch's.
- The memory bound is stated, not measured (5.26).
- WPT or reference-page comparisons wait for 5.25.
Verification status
cargo xtask verifywas not run locally in any form, as instructed.- CI has not run. Its proof jobs need a
full-verificationapproval, and the branches listed above must merge first. - The Status table, roadmap line,
docs/STATUS.mdand its SVGs,SPEC-REVIEWS.md,SYS-AUDITS.md,CLAUDE.mdandclazel-sysare untouched.
Part 2: clazel browse with a window on macOS, and browse --headless (5.23)(The building session's description of this part of the branch, posted as a comment for length. Where it says something waits, see the PR description: most of it was done further up the branch. Its statement that the Branch: Stacked. It contains, and must merge after:
CI has not run. WhatItem 5.23 of
Commits:
Decisions (each also recorded where it is made)
For 5.20 (the renderer pipeline)When paired with a GPU process, the renderer is started with
In text mode no Evidence (Apple M1 Pro, macOS 26.7.2)
Verification status
Not done, unverified, or macOS only
|
Part 4: tests: the HN-shaped fixture, geometry dump and comparison, reference script, and the reftest runner's pure parts (5.25, 5.26, partial)(The building session's description of this part of the branch, posted as a comment for length. Where it says something waits, see the PR description: most of it was done further up the branch. Its statement that the Stacked on WhatThe parts of items 5.25 and 5.26 that need neither
Decisions
Evidence
What waits
Verification status
|
Off macOS the window loop is not compiled, so the address bar, history, scroll helpers and the whole strip were dead code in the clazel binary and clippy (-D warnings) failed on Linux and Windows. They are now built on macOS on Apple silicon and under test; input, STRIP_POINTS, FONT_PATHS and system_font, which no test uses, on macOS only. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
clazel opens a window with no page and the address bar focused; the first URL typed opens the tab. clazel URL opens the window at URL. The browse argument is gone: --text, --headless OUT, --size, --wait and --gpu=hardware are options of clazel itself. Text mode and --headless still need the URL. clazel URL SCRIPT, demo and fetch are unchanged. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…s' fonts The Miri job failed twice: - `clazel`'s strip tests read DejaVu Serif with `std::fs::read`, and Miri's isolation refuses `open`. They now embed it with `include_bytes!`. - `clazel-renderer`'s page tests built `Fonts` from a copy of the embedded font, which `from_bytes` leaks with `Box::leak`; Miri reported 16 leaks. The new `Fonts::from_static` takes bytes that already live for the process, and the tests pass the embedded bytes to it. `from_bytes` (the production path, one leak per file at startup) now goes through it. Under Miri locally: the six strip tests and `page::tests::the_sheet_wait` pass with no leak reported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What is this PR's own (everything not in the PRs below it), in the order built:
claude/renderer-pipeline).clazel browsewith a window on macOS andbrowse --headless, 5.23 (claude/browse-window). Its oneclazel-syscall (clear_close_on_exec) is audited: row267abea0, hash18677bf4…, approved, by a separate audit session.claude/browse-pipeline).claude/reftest-harness).claude/sheets-wired).claude/hostile-pages).claude/phase5-top).clazelbuilds its missing helper binaries in release (8739173f), and a back button in the address bar strip (c6887bad).For the maintainer's eye:
1a1b1d37).35ea4ea2). Worth a review as a design, not only as a fix.a_real_page_repaints_for_viewports_and_scrolls_and_follows_a_click, cause unknown; CI may fail on it intermittently.<center>is centred too deep, so the story list sits about 200 px too far right. Not fixed: it needs a reading of HTML's "align descendants" and anERRATA.mdentry.The parts below are the building sessions' own descriptions, newest last. Where an earlier part says something "waits", check the later parts: most of those were done further up this same branch. The descriptions of parts 1, 2 and 4 are in the comments on this PR, for length.
Part 3: clazel browse draws pages: the renderer pipeline joined to the GPU process and the window (5.20 + 5.23)
Branch:
claude/browse-pipeline, tip22b19f7b479a59f8fa077ce1fe4499fdd36105c6.Stacked. It contains, and must merge after:
claude/browse-window(5.23's host side,50631b18), including itsclazel-sysaudit row267abea0. That branch containsclaude/gpu-sandbox(5.21a),claude/mac-window-08480b(5.21),claude/gpu-fallback-cfed1c(5.17c), clazel-gpu: batching, shaders and the Metal backend #153claude/gpu-drawing-82b82a(5.17b) andclaude/ipc-surfaces-cfd15e(5.18, with thedocs/phase5-518-decisioncommit).claude/renderer-pipeline(5.20,eb8fa5b6), which contains the same 5.18 and GPU branches, and 5.16's paint and the layout stack below it.CI has not run.
clazel-sysdid not change on this branch. Its one change (clear_close_on_exec, 5.23) is audited at18677bf4…, andcargo xtask checkpasses in full:STATIC CHECKS PASSED.What
clazel browse URL(macOS, Apple silicon) shows real pages: the renderer of 5.20 lays a page out and writes display lists; the sandboxed GPU process draws them; the kernel'sPaintputs them in the window, orbrowse --headless OUT URLwrites them to a PNG or PPM.origin/claude/renderer-pipeline. The only conflict wasCargo.lock'sclazel-rendererdependency list, resolved as the union of both sides. The auto-merged manifest then namedclazel-gputwice (a dependency from 5.23, a dev-dependency from 5.20); I dropped the dev-dependency. Both are in the merge commit.page::frame::frame_bytesnow writes throughclazel_gpu::channel::write_list, the one definition.channel::read_list.the_gpu_process_reads_what_the_renderer_writesruns the renderer's bytes through the GPU process's own loop (process::Server: framing, 5.17a's decoder and validator, batching) to the same pixels.--list-fd Nit opens/dev/fd/Nbefore its sandbox, reads its--font PATHfiles then too, and runsrun_graphics. Without it, the text path (Display) runs, as 5.18 decided.clazel browsebuilds that graphics host only for window and headless modes. Renderers there get the system's TrueType faces as--font(Arial, Arial Bold, Times New Roman, Courier New on macOS), plus their GPU process.--text, and every host without a window, is unchanged.Inputs. Left and Right do nothing.clazel-sandbox's measurement, and the top README.The GPU sandbox: one Mach service allowed (a policy change, measured)
The first real pages crashed the sandboxed GPU process.
SIGABRT) inside Apple's driver. The crash report showsAGXMetalG13X,findOrCreateDriverProgramVariant<BlitComputeProgramVariant>, on Metal's compiler queue.launchd … denied lookup: name = com.apple.MTLCompilerService … Sandbox restriction. The driver compiles blit variants when first needed. 5.21a's measurement drew only the fixed scenes, which never needed one.seatbelt::GPUis now(deny default)plus(allow mach-lookup (global-name "com.apple.MTLCompilerService")). The plan's 5.21a text names "the IOKit and Mach services Metal needs", and Chromium's GPU sandbox allows the same service.clazel-gpu'stests/sandbox_macos.rsstill sees every escape attempt denied (file, TCP, UDP, Unix socket, WindowServer, fork, exec);mach-lookupofcom.apple.analyticsd(the driver's telemetry), without effect, and left denied.clazel-sandbox/src/seatbelt.rsand its README, andtests/policies.rs's shape test, nowgpu_seatbelt_profile_is_deny_default_and_the_metal_compiler. This is a sandbox widening decided without the maintainer; it is flagged here for review.Evidence (this machine)
End to end, with the real renderer and the real sandboxed GPU process on Metal, against an in-process server (
crates/clazel/tests/browse_gpu.rs).a_real_page_repaints_for_viewports_and_scrolls_and_follows_a_clickuses a page with inline styles: a 40 px green link block, a 300 px red block and white text. The renderer lays it out in DejaVu Serif, vendored, so the layout does not depend on the machine.Viewportchange repaints at 240×120.Navigate, the kernel fetches/two, and the next page paints yellow underhttp://pages.test/two. The server saw/then/two.headless_writes_a_real_pages_pngrunsclazel browse --headless page.png --size 200x120on a blue page with white text.#2050c0.5.23's hand-built-list tests also pass: the
Paint, the PNG, the PPM.The window, opt-in (
CLAZEL_WINDOW_TAKE_FOCUS=1, becauseclazel browseactivates its window).tests/browse_window.rsrunsclazel browse http://127.0.0.1:…/against a served page until it logsclazel: showing http://…; this took 1.7 s. It then endsclazel, whose renderer and GPU process end with their sockets. The default run skips this step and says so; its other, no-focus window step passes (2.5 s).A manual headless run (
clazel browse --headless out.ppm --size 320x200on a blue page with white and yellow text) gave 63,103 background pixels and white and yellow ink.Renderer:
tests/pipeline.rs13;escape.rs11.Checks:
cargo test -j 4 -p clazel-renderer -p clazel-sandbox -p clazel-gpu -p clazel -p clazel-window: all pass, apart from one intermittent failure (next bullet).a_hand_built_list_reaches_a_paint_under_the_kernels_urltimed out waiting for itsPaint. No crash report was written. Three reruns, one of them the same command, passed. That test's stand-in renderer waits a fixed 1 s before writing its list, which may be the cause; not investigated further.cargo fmt --all -- --check: clean.cargo clippy --workspace --all-targets --locked -- -D warnings: clean.cargo xtask check:STATIC CHECKS PASSED.Verification status
full-verificationenvironment.cargo xtask verify --changedwas not run, by instruction. No Kani, Miri or Verus run was made, and no lock was taken.clazel-sysis unchanged here, and its audit row is merged.Not done, unverified, or macOS only
Inputevents to the host, and the address bar's logic by unit tests.FetchStyle). Pages render withstyleelements, attributes and the user agent sheet.Inputs, so the host sends scrolls for the scrolling keys, and Left/Right do nothing. Retina scale 2 through the whole pipeline was not run; the live screen reported scale 1.clear_close_on_execin itsclazel-sysand a GPU backend; Windows has neither. Onlyaarch64-apple-darwinwas compiled.GpuReady,GpuRefusedor a GPU process's exit.docs/phases/phase-5.mdwas not edited, including its 5.21a text's measured profile.Part 5: Linked style sheets end to end: renderer FetchStyle, host sub-fetch connections, process tests, reftests through
browse --headless(5.19, 5.20, 5.25, 5.26)Branch
claude/sheets-wired, tipdb361ae5. CI has not run on this branch or on any branch it contains.Contains, and should merge after:
claude/reftest-harness(tip5158380), which is stacked on:claude/renderer-pipeline(eb8fa5b);claude/paint-display-list(58a9383);claude/table-layout-1d3f0b(aff7a24);claude/flow-layout-050856;claude/ipc-surfaces-cfd15e;claude/gpu-sandbox.origin/claude/browse-pipeline(22b19f7b, 5.20 joined with 5.23), indcbbe273;origin/claude/style-fetches(261a1215, item 5.19), in65d144bd. Its one conflict was incrates/clazel/src/lib.rs: browse-pipeline's realSpawnGpu/ToGpuarms were kept, next to style-fetches' placeholder arm for theSub*outputs. The merge commit says so.What
FetchStyle, render-blocking (faa76a0f).Page::loadstartsSheetWaitwith the document's URL and encoding.run_graphicssends aFetchStyle { request, url }for each pending<link rel=stylesheet>whosemediamatches.StyleResponse/StyleBody/StyleEnd, or takesStyleFailed.MAX_BODY, or one the kernel timed out is left out.href.MAX_BODYeach, 8 in flight, 64 per document, so at most 512 MiB of bodies in flight.SubConnect,SubSend,SubCloseand feedsSubNet(20d303a6,067223ac).clazel_net::Driveras navigations.clazel_net::SubConns(crates/clazel-net/src/named.rs, 84 lines) maps the kernel's(renderer, serial, hop)names to Driver numbers from 2^63 up. It is a map: which connection, which bytes and which credentials are the kernel's.crates/clazel/src/lib.rs; the limit is 300).Host::eventstepped a leadingTickand dropped its outputs, so the kernel's sweep (SubCloseplusStyleFailedfor a timed-out sub-fetch) went nowhere. Those outputs are now carried out.pumpsteps aTickwhen nothing arrives, so the kernel's 60 s timeout fires on an idle page.crates/clazel/tests/browse_gpu/sheets.rs, a module ofbrowse_gpu.rsso the GPU support is shared without unused items.sid=s3cret). The cross-origin one carries none and goes to its ownHost. The server records both./site.css?v=7is requested, and the frame differs from the frame with a 404 sheet.browse --headlessas its renderer (9006b6e9,crates/clazel/tests/browse_gpu/reftests.rs).clazel browse --headless OUT --size 800x600 URL, and the RGB PNG is decoded to RGBA.3ccf7b35):crates/clazel/tests/fixtures/hn/screenshot-macos-metal.png.browse --headlessruns on this machine (macOS 26.7.2, M1 Pro, Metal, the system Arial/Times/Courier thatbrowseloads) were byte-identical. SHA-25689d079fc…0e955.db361ae5):Sub*outputs;clazel-netlistsSubConnsamong its choices;<base>.Reftest counts (WPT reftests vendored here that are in the subset)
The only vendored WPT reftests are the 17 in
clazel-style/tests/data/wpt-mediaqueries/(WPT4ad507b2). Nothing was tuned.min-width-001.xht,mq-deprecated-001,mq-invalid-media-type-001to-004and-006,mq-invalid-media-type-layer-001,mq-negative-range-001,-002,mq-range-001,negation-001,-002. Each needscss/reference/ref-filled-green-100px-square.xht.relative-units-001to-004None reached the renderer. Vendoring the one reference file (plus
reference/ref-green-body.xhtfor themedia-queries-00x.xhtpages, which the runner did not detect as reftests) would let the 13 run. Those 13 are XHTML, served asapplication/xhtml+xml, which the renderer may not take. This was not tried.Decisions
href, for HTML's quirks content-type exception. The kernel's credentials decision is separate and unchanged.StyleResponsehas no status, so any final response counts as success. Recorded under "What waits".pump: without it, a page waiting only on a hung sheet would never time out.browse_gpu.rs, not as their own test targets, because the shared GPU support has items that only some files use, and lint overrides are not allowed.Unverified, and known problems
Title, but noPaintfollows within the cap (20 s in tests, 30 s inbrowse --headless).browse-pipeline's own tip (22b19f7b, 11 tests, none of this branch's): 1 failure in 5 runs, inheadless_writes_a_real_pages_png.reftests::browse_headless_renders_reftests, which startsbrowse --headlesssix times; also each of the sheets tests and the oldera_real_page_…. Clean runs pass all 16.StyleResponsecarries no status (as above).<base href>is not used for sheets.scripts/reference-rects.mjshas still never been run.Evidence (run here,
-j 4)cargo xtask check:RESULT: STATIC CHECKS PASSED.cargo fmt --all -- --check, andcargo clippy --workspace --all-targets --locked -- -D warnings: clean.cargo test -p clazel-net -p clazel-renderer: pass. The renderer has 20 lib tests, including the newlinked_sheets_block_rendering_until_answeredandthe_stylesheet_content_type_quirk.cargo test -p clazel: every target passes exceptbrowse_gpu, which passes in full on some runs and fails one or two tests on others (the race above).Verification status
cargo xtask verifywas not run locally in any form, as instructed.full-verificationapproval, and the branches below must merge first.docs/STATUS.mdand its SVGs,SPEC-REVIEWS.md,SYS-AUDITS.md,CLAUDE.mdandclazel-sys. Noclazel-syschange was needed.Part 6: Hostile pages, address bar and GPU process tests through real processes (5.26, part)
Branch:
claude/hostile-pages, tipd848741ea1b95efd4d8fc3041c1d12e3ce22d08c.Stacked on
claude/browse-pipeline(22b19f7b), and must merge after it, with everything it contains (5.17b, 5.17c, 5.18, 5.20, 5.21, 5.21a, 5.23 and theclazel-sysaudit row).CI has not run.
clazel-sysdid not change.cargo xtask check:STATIC CHECKS PASSED.This covers the parts of item 5.26 that need neither linked style sheets nor TLS. It does not touch
crates/clazel/src,clazel-renderer/src/page/sheets.rsor the fixture; every test is in a new file, so it merges cleanly withclaude/sheets-wired.What
a_hand_built_list_reaches_a_paint_under_the_kernels_urlnow passes 20 of 20.clazel-gpu, product code). It reads the kernel's channel and the list socket on two threads. When both were already waiting (a process slow to start), a list could be read before the kernel'sGpuViewport. That list was drawn on a 1×1 target and its frame lost. A real renderer can hit this. Lists are now held while there is no viewport (at most 16 lists and 256 MiB; more ends the process), then drawn in order, each giving a frame.BodyEndhas arrived.crates/clazel/tests/hostile_graphics.rs, andtests/support/fonts.rsfor the fonts). Each runs through a real sandboxed renderer and GPU process at 800×600, with inline<style>where the item says "sheet". Each records:ps(the pid recorded by/bin/sh, as in Phase 4), against item 4.13's terms plus 5.20's graphical terms;tests/address_bar.rs), with a Perl stand-in GPU process paired with the real renderer, and the real pipeline for the title and margins cases.tests/gpu_killed.rs): what happens today is recorded, not required.browse --headless --wait S, a CLI option (default 30 s) the address-bar tests use.Results (Apple M1 Pro, macOS 26.7.2, debug build, 2026-11-13)
font-size: 1e9pxwidth: 1e9px-1e9pxlocanot monotonecmapformat 4 segmentsnumGlyphsdisagreeing withloca{nested a million deep:is(nested 10,000 deepdivs)colspan=1000everywhere (1,000 rows × 100 cells)Each ignored case's
ignorereason holds these numbers. Run one alone with:No case exceeded its memory bound, and no process crashed. The GPU process never passed 24 MiB.
What the font cases show, and don't: all seven painted. The test does not tell whether the renderer used the broken face or skipped it as "not a TrueType face" (5.20's rule); either is acceptable here.
Address bar
margin-top: -1e9pxlayer_frame) has its top edge at the strip's bottom edge, and the strip is the host's own layer, drawn from the kernel's URL.Paint). Neither process is killed.browseshows nothing (sizes differ).browse --headlessexits 1: "no frame … within 5s".https://bank.example/loginPaint.title.Paint.urlis the kernel'shttp://hostile.test/; the title bar puts it after the URL (title_bar, unit-tested), and the strip draws only the URL.A GPU process killed (today, without the crash policy)
The tab paints, then its GPU process is killed. On the next scroll the renderer's list write fails with a broken pipe, and the renderer exits with status 70. The kernel drops the tab's renderer, and nothing restarts either process. The host stays up and logs both exits. Killing "repeatedly" therefore stops after the first kill: there is no second GPU process to kill.
Escape attempts from inside the GPU process
These are already committed tests (
crates/clazel-gpu/tests/sandbox_macos.rs, 5.21a): open a file, TCP, UDP, a Unix socket, the WindowServer, fork, exec. All were re-run passing afterclaude/browse-pipeline's profile change (the Metal compiler service). Nothing was added here.Findings
.cN span, over 100,000divs each holding aspan. The likely cause is that every rule is matched against every element, with no rule index, but this was not profiled. Not fixed (not a one-line bug).hostile_pages.rsruns only 20,000 there). Not fixed.What of 5.26 waits
browse https://news.ycombinator.com/): Phase 3's TLS in the kernel.FetchStylewith and without the cookie, the stored screenshot, the reftest runner):claude/sheets-wired, in another session.cookies/,pages.rsas box tree dumps,hostile_pages.rswith the new schema): not addressed here.hostile_pages.rsand the existing escape tests still pass on this branch.Unverified
pssamples every 25 ms plus one after the outcome, so a short spike between samples can be missed.cargo xtask verifywas not run, by instruction.Part 7: Phase 5 top of stack: hostile-pages merged, one process start at a time, XHTML not run in reftests (5.23, 5.25, 5.26)
Branch
claude/phase5-top, tip4f6df6e3. CI has not run on this branch or on any branch it contains.Contains, and should merge after:
claude/sheets-wired(db361ae5), which contains:claude/reftest-harness(5158380);claude/renderer-pipeline(eb8fa5b);claude/paint-display-list(58a9383);claude/table-layout-1d3f0b(aff7a24);claude/flow-layout-050856;claude/ipc-surfaces-cfd15e;claude/gpu-sandbox;claude/browse-pipeline(22b19f7b);claude/style-fetches(261a1215).origin/claude/hostile-pages(d848741e, stacked onclaude/browse-pipeline), merged in9ecca749. No conflicts. It brings:ce60b42d);9b2c0931);browse --headless --wait;What
clazel-renderer: one process start at a time while descriptors are passed (35ea4ea2).spawn_gpuandspawn_with_listclear a socket's close-on-exec flag, so every program started before the host closes its copy inherits that socket.browse_gpu.rs's tests, does not.launchnow holds one process-wide lock (alone, public) for every start, from clearing the flag to closing the host's copy.clazel browse --headless, and the fixture'scargo build) take the same lock throughsupport/gpu.rs'soutput.Reftest runner: XHTML is not run, by reason (
ee0a6945)..xhtand.xhtmlasapplication/xhtml+xml.page/mod.rs, "Other types"). An XHTML test against an XHTML reference would therefore compare two identical notices and "pass".The media queries reftests' two WPT references, vendored (
4f6df6e3).4ad507b28db0a636f528def9161d109fe11b93a3, with WPT's licence and each file's SHA-256 incrates/clazel/tests/wpt-references/README.md:css/reference/ref-filled-green-100px-square.xht(568 bytes,b7d98bb3…a4f0);css/mediaqueries/reference/ref-green-body.xht(434 bytes,f100f6bf…b0fa).tests/data/, which holds only the inputs ofxtask's converters.browse_gpu, 15 runs (one run at a time,cargo test -p clazel --test browse_gpu, its 16 tests in parallel)35ea4ea2launchonly35ea4ea2(the tests' starts locked too)sheets::linked_sheets_apply_with_the_cookie_only_same_origina_real_page_repaints_for_viewports_and_scrolls_and_follows_a_clicksheets::the_hn_shaped_fixture_is_served_with_its_sheetheadless_writes_a_ppmheadless_writes_a_real_pages_pngreftests::browse_headless_renders_reftests--test-threads 1had no failures in 15 runs.--list-fdwas not its socket (cannot open the list socket N: Permission denied: the child opens its fonts first, so a read-only file sat at that number);gpu 0: cannot start: No such file or directory;launchonly, both failures were in tests that startbrowse --headlessfrom the test process outside the lock. One showedclazel-renderer: cannot open the list socket 3: Permission denied.a_real_page_…got commit andTitle, no process ended, and noPaintcame within 20 s. That is the original "lost frame" symptom, without the descriptor symptoms. The host's log does not record the GPU process's messages (FrameReady, lists read), so it is not known whether the list was written, read, drawn, or its frame dropped by the kernel. Finding it needs that logging. No retries or longer waits were added.find /I had started by mistake. The two later sets ran with the machine otherwise idle.Reftests: the WPT reftests vendored here that are in the subset (
clazel-style'swpt-mediaqueries, WPT4ad507b2), with their references vendored (4f6df6e3)min-width-001.xht(the test itself). Through their referencecss/reference/ref-filled-green-100px-square.xht:mq-deprecated-001,mq-invalid-media-type-001to-004and-006,mq-invalid-media-type-layer-001,mq-negative-range-001and-002,mq-range-001,negation-001and-002.relative-units-001to-004(their reference,css/mediaqueries/reference/ref-green-body.xht, is XHTML too).xhtasapplication/xhtml+xml.text/htmlwould be outside the standard, so it was not done. Nothing was tuned or special-cased.media-queries-001to-003.xhtare not reftests: they have norel=matchorrel=mismatch.application/xhtml+xml, which needs an XML parser (one of our own, under rule 8).The six crates' tests, once on the merged branch
cargo test -j 4 --locked -p clazel -p clazel-renderer -p clazel-gpu -p clazel-net -p clazel-kernel -p clazel-ipc: exit 0.#[ignore]d hostile cases: 8 inhostile_graphics.rs, 2 inhostile_pages.rs.browse_gpupassed 16 of 16 in that run.Checks
cargo xtask check:RESULT: STATIC CHECKS PASSED.cargo fmt --all -- --check, andcargo clippy --workspace --all-targets --locked -- -D warnings: clean.Unverified
clazel_renderer::launch, and test starts that usesupport/gpu.rs'soutput. Other test files that start programs with a plainCommandbeside in-process hosts are not changed:address_bar.rs,gpu_killed.rs,hostile_graphics.rsandhost.rs. They passed in the full run above.full-verificationapproval, and the branches below must merge first.cargo xtask verifywas not run locally in any form, as instructed.docs/STATUS.mdand its SVGs,SPEC-REVIEWS.md,SYS-AUDITS.md,CLAUDE.mdandclazel-sys. Noclazel-syschange was needed.Part 8: The https demo on Hacker News works; one test pins its path
Branch:
claude/https-demo, tipf67c8592ed5965b46d3ae680dd5742a58f097f66, cut fromorigin/claude/phase5-top(ee0a6945). It contains one commit on top: a test only, no product change.CI has not run.
clazel-sysdid not change.cargo xtask check:STATIC CHECKS PASSED; clippy onclazelis clean; the new test passes.The maintainer is right: TLS is in the kernel, and the https demo works today without any change. The plan's "waits for Phase 3's TLS in the kernel" is out of date.
What I ran (9 requests to the site in all, no loops)
Machine: Apple M1 Pro, macOS 26.7.2, debug build, from
ee0a6945.clazel fetch https://news.ycombinator.com/: works.200 OKfrom nginx,Transfer-Encoding: chunked, 34,901 bytes, 1.4 s.Accept-Encoding.clazel browse --headless …/hn.png --size 1280x900 https://news.ycombinator.com/: works, 2.46 s end to end.connection 0 … (TLS).FetchStylefornews.css?…, which goes on its own TLS sub-connection (SubConnect { tls: true }), andStyleResponsecomes back.Titleis "Hacker News", and the PNG is written.item?id=50478515(IceWM, 36 comments) rather than the 3,000-comment thread.clazel browse https://news.ycombinator.com/in a window: works.clazel: showing https://news.ycombinator.com/ (1024 by 732, slot 0).screencapturefailed with "could not create image from display" because the display was asleep, so there is no window screenshot. Its content is the same pipeline as the headless image.No layer failed:
Connect { tls: true }in headless and window mode;So nothing in host wiring needed fixing, and no change touches TLS, X.509 or certificate validation.
Committed
crates/clazel/tests/https_sheet.rs: an https page with an https sheet through the real renderer and GPU process, against the loopback rustls server the host tests use. It checks:FetchStyleon its own TLS connection (two TLS connections in the log);It passes in 1.4 s. Nothing fetched from the real site is committed.
The real front page against a real browser, in words
Screenshot:
/private/tmp/claude-503/hn/hn.png(1280×900).Right:
#f6f6efbackground;Missing, as expected: images (Phase 6). There are no vote arrows and no Y logo; the logo's white border shows as an empty box, which is right without the image.
Wrong: the story list starts about 200 px too far right.
clazel-layout/clazel-style's HTML "align descendants". HN wraps everything in<center>and has no doctype (quirks mode).4c54fae1text) has<center>align all its block-level descendants. That includes the story table nested in a cell of#hnmain, so our layout centres it within its cell, as an auto-width table at its max-content width.centerby inheritingtext-align(-webkit-center), and in quirks mode the HTML table ruletable { text-align: initial }stops it at#hnmain, so the inner table is not centred./private/tmp/claude-503/hn/www/q-center.html):<center><table width=85%><tr><td><table>…. Ink starts at x = 530 under<center>, and at x = 6 without it, in quirks and standards mode alike.news.cssthe offset is the same, so the sheet is not the cause.ERRATA.mdentry, saying where align descendants stops) inclazel-layout/clazel-style, not host wiring or a one-line bug.The item page
Screenshot:
/private/tmp/claude-503/hn/item.png.Right:
replylinks;Wrong:
<center>, so the same align-descendants behaviour centres every comment narrower than the page. HN's indent cells (an images.gifwith awidthattribute) do reserve their width: long comments show the right indentation steps.Screenshots and scratch files (none committed)
/private/tmp/claude-503/hn/hn.png: the front page, headless, 1280×900./private/tmp/claude-503/hn/item.png: an item page, headless, 1280×900./private/tmp/claude-503/hn/local-nocss.png: the saved front page served locally withoutnews.css(the offset is still there)./private/tmp/claude-503/hn/q-plain.pngand/private/tmp/claude-503/hn/q-center.png: my minimal reproduction without and with<center>./private/tmp/claude-503/hn/fetch.outand/private/tmp/claude-503/hn/www/front.html: the fetched front page, kept in scratch only.Unverified
🤖 Generated with Claude Code