Repository navigation
web: HTML and CSS engine (0.2.0) - #1947
Merged
Merged
Conversation
graph/test/web/oracle/ derives the ground truth for every web node and in-web edge kind from parse5, postcss, css-select and acorn (pinned in its package.json; the axiom parser is never imported). run-tests.sh builds each case's graph, normalizes the engine tables onto the oracle's rows (contract in tools/normalize.py) and gates the multiset diff with known-missing support; it fails with "engine not built" until graph/web/engine exists. 51 hand-written cases across nodes, cascade, refs, pages, js and crosslang, two generated scale cases; every expectation was read row by row and agrees with the oracle (--oracle). Refs #1908
…eyframe selector lists Three corpus shapes the oracle-vs-IR comparison exposed: keyframes named only through -webkit-/-moz-/-o-animation, theme sheets annotating every value with a brace comment, and percentage lists in minified keyframes. Declaration values are compared without comments; usemap, codebase and profile join the URL attributes. Refs #1908
…cle skips matching when no kind needs it The suites live on web/torture while the engine is built on feat/web-layer: AXIOM_ROOT points the runner at that checkout. The oracle's matching stage now runs only for kinds that depend on it, so the 2,000-page scale case takes 2 s in the oracle. The 200-page scale case compares styles and cascade rows; var edges are left out (4.5M rows on the generated site). Refs #1908
Clearing the namespace of a universal node threw inside the selector rewrite, so every selector containing `*` became selector_unparsed: thousands of corpus selectors (`* html`, `> *`, `*::before`) had no match rows. Found by listing the selector_unparsed partition on the dev corpus; one invalid selector remains there, as it should. New case cascade/17 covers both. Refs #1908
…larations after a comment holding braces
The web walk skipped dist/, build/ and out/ by name (the source walks' list), so a
template's own compiled stylesheet, linked by every page, was never read. The web
walk now has its own skip list (EXCLUDED_DIRS less those three); javadoc/Dokka and
git-ignored directories are still skipped.
A comment holding a brace inside a declaration value (#ddd/*{borderColor}*/) turned
the rest of the block into bogus nested rules. Braces inside comments are blanked for
the grammar only; comment rows read their text from the source.
Refs #1909
…dies as JavaScript modules of their page The HTML/CSS tables are also written to <ir>/web/ whenever a page or stylesheet was read, so a repository of pages and stylesheets alone has IR for the web engine. The JavaScript front end reads each page's inline classic/module <script> bodies and on* attribute values as modules of their own: file = the page, qualified name <page>#script-<n> / <page>#on-<n> (document order), text = the page with everything else blanked so every line points into the .html file. A virtualKey keeps two modules of one page apart in the module key; ordinary files keep their key. parser/src/test/web-plumbing-tests.ts covers the web folder, the dist walk, brace comments and the inline modules, each checked to fail without its fix. Refs #1908
The oracle matched @scope rules unscoped as conditional at_rule:scope. It now evaluates the scope: subjects under a root matching the start selector and outside any limit below it, nearest root kept with its proximity (new row kind `scope`, read from web_styles.scope_root and scope_proximity); :scope and & name the root; a selector without them is relative to it; a prelude-less @scope in a <style> is rooted at the <style>'s parent; a non-exact bound gives scope_bound:<reason>. New case cascade/18. Refs #1908
…ow caps normalize.py reads var rows from the view web_var(use, def, page, status, reason) with use = the VARIABLE value_ref. tools/var_budget.py fails when web_var_visible exceeds 4 x (var() uses + custom-property declarations), web_var_scope exceeds 2 x the styles rows of var-using rules or 2M, web_var is materialized, or there is no var() use to measure. Wired as check=var_budget on the three var cases and on the 200-page scale case, which compares the view's count with the oracle's 4.5M rows again. Refs #1908
…arent list, disabled sheets
Per the iter1b rulings: an element whose id or attribute is bound at run
time (:id, x-bind:data-state, [attr.aria-expanded], id="row-{{ n }}")
gets one unknown dynamic_attribute row for each selector the binding may
complete (dynamic_attribute;dynamic_class when it needs both kinds), never
a match; & under '#app, .shell' is 1,1,0 for both; <link disabled> and
alternate sheets load conditional alternate_sheet. Cases cascade/19, 20.
Refs #1908
…sing G10 refs/09 (vendor animation shorthands), G11 nodes/04 now loads a sheet that targets the iframe fallback markup (never styled; the normalizer drops elements with inert=iframe_text and reports a styles row on one as an extra), G12 nodes/18 compares selectors (keyframe selectors are absent), G13 refs/05, G14 pages/05 (a site inside a sub-project with its own package.json; keys stay repo-relative). No known-missing file exists. Refs #1908
…I's --json cases/queries/01-admin-site is a hand-written dashboard site with 38 questions (37 graded, Q36 skipped_not_built until fragment hosts exist) over context, impact, path and link. tools/query_oracle.py derives every answer from the oracle's rows (expected/queries/..., each read by hand); tools/grade_query.py grades SET, SET>=, ORDER, CHAIN and TOP-k against the SPEC 6.2 row contract (at, role, status, rank; the JavaScript rows under other_languages.javascript), re-asks with --limit 0 when the answer says more > 0, and never counts a skipped template as correct. The runner prints query_ok per case; --oracle checks every derived answer still matches its reviewed expectation. Refs #1908
…ic attributes, written type names The bundle's web tables follow graph/test/web/tools/normalize.py (uid/file/line/col, element_uid, ...). Selector matching: static reasons per selector, initial-state pseudo-classes true only when the attributes say so, whole-selector unknowns (shadow_dom, column_combinator, selector_unparsed, implied_element) as one unknown row per page, dynamic class/attribute bindings as unknown rows, @scope as exact containment with scope_root/scope_proximity, HTML case-insensitive attribute values, SVG type names taken back as written (the parser lowercases them). Layer ranks and load order per SPEC 3.3; alternate/disabled sheets conditional. var edges stored as web_var_def/web_var_visible/web_var_scope with web_var a SQL view. Refs #1908
…kept it out) The repository-wide `oracle/` and `package-lock.json` ignore rules matched graph/test/web/oracle, so no commit on this branch carried the ground truth the suites are generated from. graph/test/web/.gitignore re-includes oracle/ and its lockfile (an exception to the no-lockfile rule: the oracle is a pinned measuring instrument, its transitive versions included) and keeps oracle/node_modules/ out. Refs #1908
… block with its whole list The grammar rejects the list's leading items as an ERROR inside the keyframe block list; the recovery that reads them as the next block's prelude never saw it, because the ERROR was lifted into its parts. Kept whole there now. Refs #1908
…nts, unknown rows only for undecidable selectors A trailing slash on a non-void HTML element is ignored by HTML; the following siblings become its content (the parser closes it at the slash). Declaration values drop their comments. web_unknown carries the selector/sheet/page unknowns; id references, loads and parse gaps keep their own tables. Refs #1908
…refs, var scope rows per (element, name) Parser: `@keyframes "fade"` is an @Keyframes named fade (the grammar takes only an identifier); the parser torture expectation that encoded the gap is updated. Engine: a link to `docs/` reaches docs/index.html; -webkit-/-moz-/-o-animation names become value references of their own (G10); web_var_scope holds one row per (page, element, name) and only for names something defines. Refs #1908
…r HTML and CSS (#1909); styled_by questions grade their own rows The web walk no longer skips dist/, build/ and out/ by name (the JavaScript walk still does), so refs/08 now loads dist/app.css as a match at load order 1 and the later links shift by one; a link into target/ keeps the not_indexed shape. No other expectation depended on the old walk. Q1, Q23, Q24 and Q28 all read the styled_by rows of one element; each expectation now names its filter (exact non-important / has conditions / state:* / important_count > 0) and grade_query.py applies it before grading. grade_query_selftest.py (run by --oracle) shows one full answer satisfies all four and that a dropped, corrupted or reordered row fails the question it belongs to. Refs #1908
…e SPEC 6.2 row contract
ax_web.py answers impact/path/context on a web graph (the verb scripts hand it a graph
whose run.language is web): rows {at, kind, role, status, reason, rank, ...} for
classes, ids, custom properties, pages, stylesheets, elements (page.html:LINE, the
cascade in §3.3 order), selectors, @Keyframes, @font-face, @layer, @container and
inline scripts; path over links, loads/@import, rules, styles and var(); context ranks
the files a task's words land on. tests/changed exit 3 (nothing to say). The front
door prints the web prose after the other languages' places. axiomcode-index indexes a
web graph's names as symbols (INDEX_VERSION 9); axiomcode-build counts pages and
stylesheets (web is the main language only when nothing else has files) and adds the
javascript graph for pages with inline scripts.
Refs #1908
…he page's markup Read from the JavaScript IR in the bundle (API name, receiver shape, literal argument): querySelector/All, getElementById, getElementsBy*, closest, matches, *Attribute, add/removeEventListener, createElement, insertAdjacentHTML, classList.*, style.setProperty, className=/innerHTML=/outerHTML=/textContent=/on<event>=/style.<prop>=, dataset.<k>, and jQuery's $()/jQuery() and its class, attribute, css, event and traversal methods (only on a jQuery receiver). Literal tokens split (.a #b div [data-x]); a non-literal argument is a non_literal row. Bundled/minified modules are not listed. No row names a web node. Kept out of the Soufflé rules: it needs no resolution and would cost a cold JavaScript engine compile per change. In a fanned-out --json answer the web graph's rows lead and the JavaScript graph's sit under other_languages.javascript (SPEC 6.2). Refs #1908
…is known by; nameless on* calls are not handler rows Refs #1908
…age, a .class/#id or a .js file answers with inline-script functions and DOM touches ext_module_provenance(module, file, qualified_name, provenance) and ext_method_module(method_id, module). On a JavaScript graph, impact <page.html> lists the functions of the page's inline scripts and on* bodies (role inline_script, named by ext_method_name) and its DOM touches; impact <.class|#id> the DOM touches whose tokens name it; impact <file.js> the usual answer plus its DOM touches. Rows of the JavaScript graph only; the fan-out puts them under other_languages.javascript. Refs #1908
… kept as BUNDLED, acorn-loose recovery Per the js rulings: normalize.py names a function COALESCE(ext_method_name.name, methods.name) and adds a provenance column from ext_module_provenance (PROJECT when a module has no row). The oracle keeps a site's own minified functions with provenance BUNDLED (their DOM touches stay outside the recall set) and falls back to acorn-loose 8.5.2 (pinned) when acorn rejects a script, so js/02's broken declaration is expected on both sides with no known-missing entry. New js/07: two inline scripts recovery can find no function in, beside a working one, so an invented function fails as an extra and the case still compares rows. js/01-03 and js/06 re-blessed (provenance column; js/06 gains its .min.js function). Refs #1908
… inline virtual path Refs #1908
…_indexed; SKILL.md names HTML + CSS Refs #1908
…L and CSS only) Reverts the G6 part of ae6c6d1a: the JavaScript front end no longer reads a page's inline <script> bodies and on* values as modules, the module key's virtualKey and the HTML parser's scriptSpans are gone, and the plumbing test for it is removed. The JavaScript parser, the module registry and the HTML parser are byte-identical to 3702fa6f again. The <ir>/web/ folder (G5) stays. Refs #1908
…nswers (scope: HTML and CSS only) Removes ext_dom_touch, ext_method_name, ext_module_provenance and ext_method_module from the JavaScript bundle (graph/bundle/dom-touch.ts), the JavaScript-graph impact on pages, .class/#id and .js files, and the web-first order of the fanned-out --json answer (reverting 39396eb2, aec9e2c2, f8924df8, b9d08f11, 381f2550 and that part of 54f127b9), and the build's javascript graph for pages with inline scripts. The web graph keeps a <script> as markup (src, resolved file, type, body range, body line count, inline_index) and a handler call by the name written (handler_index); nothing reads script text as JavaScript. Refs #1908
- Single-quoted values: the template-quote mask ran from a stray {{ in a
script to a later }} and unquoted every single-quoted attribute between,
so class='a b' split at the space. Template tags are now one line and at
most 500 characters (nodes/26).
- V4-07: a directory URL without the trailing slash resolves to its index
file in reference.resolved_file and links_to (pages/10).
- V4-03: javascript:, javascript:;, void(0) and void 0 are no-op links with
no handler row (handlers/19; handlers/02 re-blessed).
- V4-08: the walk skips only node_modules/, .git/ and the project's
.gitignore files, so target/, env/ and dot directories are read
(pages/09; refs/08 re-blessed for target/site.css).
- V4-01: every SSI directive is a row wherever it sits, top of file and
inside attribute values included (includes/15).
- V4-05: value_text and prelude_text are stored with comments removed and
trimmed, bytes counted on that text (nodes/27).
- V4-02: an <include ... /> parses as an open element; the fragment takes
its place and what parsed inside it follows as siblings (includes/16).
Refs #1908
The CR inside a value is what the case tests. Without -text, git stored the file with LF line endings and the case would compare another file than the one reviewed. Refs #1908
…30) A duplicated answer row was absorbed by set comparison and passed. SET now requires the exact multiset; SET>= requires every expected tuple at least as often as expected and no tuple more often. The grader names the duplicated rows. Self-test 25/25: the exact multiset passes, a duplicate fails under both grades, and a tuple expected twice (two nodes on one line) passes when answered twice. On the impl, queries/01 now fails Q6 and Q12 on duplicate rows (listed in learnings.md); queries/02 and 03 pass. Refs #1908
… rule_at, status Ruling (iter5 orchestrator): a selector-to-element answer is one row per (element, rule), so two rules styling one h2 are two correct rows. Q6, Q36, Q36c and Q51 now grade at,rule_at,status (Q51 with host). The expectation holds each rule's best status on the element, a rule counted once when two of its selectors match. The grader compares every *_at field as file:line. styled_by questions already name the rule in at and are unchanged. Q12 stays as it is (one handler row per attribute, with calls). Refs #1908
…ies, comment-free names, the web walk's skips The iteration-5 fixes on the parser side; their checks (violations.sh V4, in the next commit) fail at 6383fd9: - V4-01: every SSI directive is a row wherever it sits: at the top of a file (no owner element), several in one attribute value, and #comment. An SSI directive inside a quoted attribute value is opaque in the start tag (its own quotes end nothing) and keeps the value whole. - V4-04: a url() body (a data: URI) is kept whole below 64 KiB in its value reference (V2-12), not cut at 1 KiB. - V4-05: a declaration's value and a rule's prelude keep a carriage return as written (EntityUtils.escapeTsv, which every front end shares, folds CRLF; the web rows use their own helper). - V4-06: font-family, font and animation names are read with comments blanked, so `Arial/*{ff}*/` is a FONT_FAMILY reference. - V4-08 ruling: the web walk skips only node_modules/, .git/ and what the repository's ignore files exclude; target/, env/ and dot-directories are read. The JavaScript and TypeScript walks keep EXCLUDED_DIRS. Refs #1909
…t byte counts; one handler row, styled rows per rule The iteration-5 fixes on the engine and query side, each with a check that fails at 6383fd9: - V4-02: the `<div/>` repair no longer takes `<include … />` (a build-time directive the include tool closes): what follows it stayed its children and vanished when the fragment replaced it, so a later include's fragment had no styles and no computed rows (V3-05 on r5 too). - V4-03 ruling: `javascript:`, `javascript:;` and `javascript:void(0)` / `void 0` are no-op links, no handler row. - V4-05 ruling: value_bytes and prelude_bytes are the UTF-8 length of value_text and prelude_text as stored (comments removed, edges trimmed; a value cut at 64 KiB keeps the length of the whole value written), and prelude_text is stored comment-free. - [iter5 ruling] the `handler` answer role is one row per on* attribute or javascript: URL, the functions it calls in `calls`; a template event directive keeps a template_expr row per call. - [iter5 ruling] element-styled answers (`styled`) are one row per (element, rule), per host for a composed fragment, with `rule_at` and the rule's best status on that element; a rule whose two selectors both match counts once. V4-07 needed no engine change: its check passes at 6383fd9 (the oracle was behind). Checks: violations.sh V4-01..V4-08, cli-smoke V5-handler-one-row and V5-styled-per-rule. Refs #1908
…ry css url() is a resource row iter4 ruling: a relative URL inside an included fragment resolves against the HOST page's location in each composed page. Its link href and element src already did (a8cc043); a url() in the fragment's own <style> or in a style attribute inside it still resolved against the fragment file. Each css url() now gives a web_resources row (from_kind declaration, kind URL), owned by what it resolves against: the stylesheet for a sheet file, the page for a <style> or style attribute, and, for an included fragment, one row per host resolved against that host's location in place of the fragment-relative one. web_value_refs keeps the url() as written, resolved against its own file. Check: violations.sh VIA-04 (a fragment's <style> url(), quoted and root-relative, and a style-attribute url(), under two hosts in different directories, beside a page's and a sheet file's own); fails at 78cac39. Refs #1908
Review 1 #23, third bullet. With --src the web graph's page paths are relative to the source directory, while axiomcode-web-links.tsv sits at the repository root with repository-relative paths, so: - web_verdicts keyed the host by its source-relative path and never matched the link it was asked about: it now puts page paths back under run.source_dir before keying, as the links file writes them; - the file table watched only the source directory, so the links file written at the root never made the graph stale and `link` rebuilt nothing: the root links file is watched as well when --src leaves it outside. Checks: cli-smoke R1-23-src-link-taken, R1-23-src-link-list (index --lang web --src site, then link and the listing); both fail at 78cac39, and each still fails with only one of the two changes. Refs #1908
…other; fail a build whose engine has no .parallel marker The web branch adds a Soufflé program (graph/web) that run-souffle.sh looks up in the platform engine package, but build-engines never built it: a user without Soufflé would get 'has no web engine' and no web graph. web joins LANGUAGES and the Windows loop, so it is generated with -j 8, compiled with OpenMP on every platform and marked .parallel. The smoke step now fails when an engine lacks its .parallel marker (run-souffle.sh would solve it at -j 1) and runs each engine at -j 8, as a user's solve does. web stays out of ci.yml's install-and-run loop: that check counts call edges and runs the query verbs, and web has neither. Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… x selectors and uses x definitions Two loops in the web bundle were quadratic in the site, not in its answers: - every page visited every selector of every sheet it loads, though most require a class or id the page does not carry (4,000 pages x 10,000 loaded rules = 40M visits); - every var() use walked every definition of its name (20k uses x ~1,800 defs). Selectors: each sheet is indexed once by a class or id its selectors require (a browser's rule hash). A plain page (one run, no composition, no dynamic class binding) visits the selectors whose key it carries plus those with no key, in sheet order; a skipped selector can only be tallied as loading, unmatched and missing on that page, and those tallies are added in bulk. Undecidable, :root and @scope selectors are never skipped, and a composed, extended or dynamic page still visits every selector. Vars: a name's definitions are grouped by owner once; a use walks owners, and whether a use sheet and a definition sheet share a page is asked once per pair. Output unchanged: every table identical (sorted rows) on all 132 web suite cases and on a generated 1,000- and 4,000-page site; the web suite passes 132/132. Bundle on the 1,000-page site 101 s -> 25 s (styles 39 s -> 10.8 s); 4,000 pages 202 s -> 84 s end to end. Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…touched it) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dl), like every other engine's resolution HTML <-> CSS linking moves toward the engine: element tokens and the tree, a selector's compounds joined right to left (type, class, id; descendant and child), solved by the engine at -j 8 and exported as web_selector_match(selector, page, element) and web_selector_supported(selector) — relations a query or a pack can join, written to the graph as ext_web_selector_*. The bundle reads the engine's matches where they are exact: a plain page (no composition, no class binding) with no quirks mode, no mended <div/>, no upper-case HTML tag, and — for a selector with a type part — no SVG or MathML element (the parser lower-cases a type selector's name). Every other page and selector keeps the bundle's matcher, which also still turns each match into a status, the cascade and computed style. Output unchanged: every existing table identical on all 132 web suite cases (57 of them take matches from the engine) and on the generated 1,000- and 4,000-page sites. Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nt, resolution; tests under graph/test/web
- engine/projections/elements.dl, selectors.dl: column reads of the HTML and CSS facts
- engine/containment/ownership.dl: rules nested in a style rule
- engine/resolution/loads.dl (+ web_page_sheet), selectors.dl (was match.dl), values.dl;
headers in the engines' 'Resolution · NAME' form, and loads.dl no longer says selector
matching is the bundle's
- graph/web/test/{cli-smoke,includes,violations}.sh move to graph/test/web/, beside the suite,
as every language keeps its tests; bin/axiomcode follows
- parser/tools/web-corpus-bench/BASELINE-2026-10-07.md removed: a dated, generated snapshot
Unchanged output: every table identical on all 132 web cases; web suite 132/132, cli smoke 36/36,
include checks 9/9, violation checks 101/101.
Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, its CLI/violation/include checks, its declarations against the parser schema
- the suite matrix gets a web leg: graph/test/web/run-tests.sh (132 oracle-gated cases), then
graph/test/web/{cli-smoke,violations,includes}.sh; AXIOMCODE_ENGINE points the front door
(the web query cases) at this checkout; tests/run.py has no web cases, so that step skips web
- web joins the decls_base drift check; it stays out of check_staging (it stages no library)
- run-tests.sh measured a build's peak memory with BSD 'time -l', which fails every case on
Linux: it now uses GNU 'time -v' there (kB) and 'time -l' on macOS (bytes), and the leg
installs 'time'
Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… check and the nightly e2e-install.sh checked a call graph's edges and the call-graph verbs, which a web graph has neither of: for web it now checks that the installed package used its packaged web engine and wrote pages, selectors, style rows and engine matches. ci.yml and nightly.yml run it on a web fixture (cases sit one level deeper) whose selectors the engine supports. Checked locally: packed tarballs installed into an empty project, souffle hidden, packaged darwin-arm64 web engine used; 1 page, 6 selectors, 12 style rows, 7 engine matches. Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts; path reaches a class only selectors name
- impact (no name), CLI and MCP: the web graph maps each hunk of an edited .html/.css to the element or rule it is
in and answers for those, beside the call graphs' answer (text and --json), against the same baseline `changed`
uses; it says when the graph predates the edit (by content, ax_fresh's file table). Before: "your edits: none".
- path: a .class or #id endpoint is the elements carrying it and the selectors naming it, so `path page.html .x`
reaches a rule for a class no element carries instead of "nothing named '.x'".
- context: a page named by a number or a file name ("the 404 page") is found.
- changed / tests: pages and stylesheets are named as the web graph's, not as files outside every indexed
language; tests answers in text when the JSON answer has no places (it printed the raw document).
- index: the web graph's summary line counts pages, stylesheets, selectors and styled elements (it was empty).
Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nswers), 692 files to 294 A case's site was a directory of a case.conf and a few small pages and stylesheets; it is now one text file the runner unpacks into .work/cases/ (tools/cases.py, byte-exact: binary or CR files go base64, a file without a final newline is marked). The engine and oracle runs read the unpacked tree exactly as before; CI and the nightly unpack their web case. Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…too (.github/e2e/web) The install check stopped at "the web graph has rows" for web. e2e-web-queries.sh is e2e-queries.sh's twin for a site: impact on a class, a stylesheet and a page, path page -> imported sheet, context, --json, the same three verbs where exec does not replace the process (#1640), then an edit to a rule (impact with no name) and tests (exit 3, none). Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
swapnilpaliwal-sd
marked this pull request as ready for review
October 14, 2026 05:31
swapnilpaliwal-sd
requested review from
JaredHLZhang,
Whua689 and
suyashpaliwal26
as code owners
October 14, 2026 05:31
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The web (HTML + CSS) engine, the sixth language, released as 0.2.0. Merges into
apps/integration-0.2.0, then dev for the 0.2.0 release. The parser goes to 0.2.1 (HTML and CSS front-end fixes on top of the 0.2.0 parser that shipped in 0.1.10).What's in it
graph/web/follows the other engines' layout (projections/,containment/,resolution/). Structural selector matching is Datalog (web_selector_match/web_selector_supported). Pages, stylesheets,@import,<style>blocks, inlinestyle="", the cascade (specificity, layers,!important), custom properties across files, and links between pages are all covered. The web graph links HTML and CSS only; scripts and handlers stay as rows.-j 8, OpenMP), and a build fails if an engine lacks its.parallelmarker.impacton a class, id,var(--x), page, stylesheet,page.html:LINEor a selector;pathfrom a page to a sheet or a class;context;impactandchangedwith no name answer for edited pages and stylesheets;testsreports that the web graph selects no tests.--languages=filter. Without the flag, the other languages are parsed exactly as before.graph/test/web/holds 134 cases. Each case is one.casefile thattools/cases.pyunpacks, byte for byte, at run time. That takes the suite from 692 files to 294.oracle/: parse5, postcss, css-select).graph/test/never ships: the npm package excludes it..github/e2e/web,e2e-web-queries.sh).Validation
Known, not blocking
impact .classrepeats an element once for each rule that styles it, without saying which rule.impact #idleaves out the element's inline style.impact page.html:LINEshows it.🤖 Generated with Claude Code