0.11.0 - 2026-06-17
Release Notes
Adds a declarative clone-detection track — CSS and HTML/markup, by computed-style and
rendered-DOM equivalence — alongside the imperative value-graph engine, plus a cross-dialect
markup model that converges the same component across HTML/Vue/Svelte/JSX/TSX.
Added
- Declarative CSS clone detection by computed-style equivalence (#413, #415, #416). CSS is
now a first-class language on a new DECLARATIVE track that sits alongside (not inside) the
imperative value-graph moat. A newLang::Css+CssRule/CssSelector/CssDeclIL feeds an
exact fingerprint that is the rule's canonical computed/declared style — value
canonicalization incss_value(color#fff≡#ffffff≡whiteover the full 148-entry
named-color table, length0px≡0, box-shorthand collapse,url()/hsl()spelling
normalization) and selector/order-independent matching, but cascade-preserving (repeated
property last-wins; shorthand+longhand co-occurrence kept order-sensitive). At-rule preludes
(@media/@supports/@container) are canonicalized so equivalent conditions converge while
distinct ones stay apart (closes a latent prelude false merge). Soundness is by construction
(fingerprint is the computed style, no IL rewrite) plus adversarial per-rule batteries and a
domain-namespaced hash that can never collide with an imperative one; registered as the
normalize.css.computed_style(empirical-only) obligation. Measured by a labeled
benchmark (tests/css_html_quality.rs): recall 13/13, soundness 14/14, verify gate 0 false
merges on the frontend corpus. - Declarative HTML markup clone detection by rendered-DOM equivalence (#413). New
HtmlElement/HtmlAttr/HtmlTextIL; every element subtree is a unit. The exact fingerprint
is the canonical rendered DOM — a recursive subtree hash over lowercased tag + attribute
set + ordered children, canon over attribute order, boolean-attr form
(disabled≡disabled=""), class-token set, tag/attr case, and whitespace
(<pre>/<textarea>kept verbatim). Inlinestyle="…"reuses the full CSS computed-style
canon. The structuralnearchannel abstracts declarative leaf values, so a repeated component
shell with different content surfaces as similar while identical DOM surfaces as exact. <script>/<style>/markup multi-region files (#413). A container file
(.html/.vue/.svelte) lowers to several region ILs —<script>as JS/TS,<style>as
CSS, the markup tree as HTML — all sharing the container's path, so a duplicated block in a
component's<style>and a standalone.cssform one cross-file family.- Cross-dialect markup clones across HTML/Vue/Svelte/JSX/TSX (#417). All five dialects lower
into the common declarative markup IL so the same component converges across dialects. JSX/TSX
now lower toHtmlElement/HtmlAttr/HtmlText(was an imperative Call-tree); a new
HtmlControlnode wraps repeat/conditional idioms (Vuev-for/v-if, Svelte
{#each}/{#if}, JSX.map/&&/ternary). Dialect-idiom normalization
(className→class,htmlFor→for,router-link/Link→a,to→href,:src≡src)
runs in the handlers. The exact channel stays sound — a loop wrapper is structurally distinct
from a single element and never exact-merges with it; bound-attribute expressions are kept
verbatim — so cross-dialect matches land onnearunless the rendered DOM is genuinely
identical. Measured on a realworld React+Vue+Svelte corpus: cross-dialect markup families
0 → 9 (3 three-way), pinned by a six-test adversarial soundness battery. - async↔sync twins converge in the near channel — dual-view
await(#412). The #1
real-world Type-4 gap (§K/§CU): anasync fnand its sync twin (identical body moduloawait)
were 0 families. A dual view of the value graph keyed byBuilder.await_transparentevaluates
await e → efor the fingerprint (twins converge) while the witness keeps an
Opaque(VG_PROTOCOL_AWAIT,[e])wrapper so the graded witness labels themasync-mirror(a
transformation, neverequal_modulo_holes). The exact channel is provably inert
(await→Raw→exact_safe=false): verify clean, exact families byte-identical. Synthetic recall
0 → 6/6, hard-negative FP 0/3; modest real-corpus lift (httpx +1, most twins already converge
via the anchor path). - Rust match-arm payload bindings now converge bound-name copies (#404, #390 follow-up). A
match arm's payload binder (thevinSome(v)) lowers to a projection assignment prepended to
the body (v = scrutinee.0), so it is alpha-canonicalized andSome(a) => f(a)≡
Some(b) => f(b). Conservative (rust_bind_arm_pattern): only unambiguous single-payload
tuple-struct bindings and named struct-field projections; multi-element/nested patterns stay
unbound.nose verify --max-violations 0clean, cross-variant (SomevsOk) stays distinct.
Changed
- Compiled/distributed CSS is kept off the default surface (#424, #426). The frontend gold
set measured the CSS-framework default surface at ~10% worthy, mostly preprocessor output. A
content-based filter (looks_compiled_css) now treats a.csscarrying a build marker — a
preserved/*!license banner, a versioned header, a trailingsourceMappingURL, a sibling
.css.map, or a.min.cssname — asgenerated(off the default surface), and
family_is_compiled_css_pipelinedemotes an all-CSS family with at most one non-compiled member
(a source partial echoed through source→compiled→minified). Preprocessor source files stay on
the surface; a genuine dedup spanning ≥2 source files stays. Measured on the gold set:
0 worthy dropped, default-surface worthy-rate 20% → 41% (framework 11% → 31%). nose statsJSON moved under the tool-wide--format json(#420). The one-off--json
flag is removed (not aliased — one way, not two; pre-1.0 diagnostic command, sole in-repo
consumer migrated), sostatsfollows the same--formatcontract asquery/scan/il.- tree-sitter core upgraded
0.24→0.25, grammarstree-sitter-c→0.24.2,
tree-sitter-python/tree-sitter-javascript→0.25.0(#403, unblocks the reverted
#399/#400/#401). The bumped grammars compile to grammar ABI 15; core 0.25 accepts ABI 14
and 15, so the still-pinned 0.23 grammars (java/ruby/typescript) keep loading
alongside them. Internal-only — no Rust API changes were needed, andnose query --format jsonoutput is byte-identical across the bump on C/Python/JS corpora (the normalizer
absorbs the CST-shape deltas), with the soundness gate (verify --max-violations 0) clean.
Validated on acargo cleanrelease build (the stale-grammar-object trap that produced the
#402 false-green; with core at 0.25 both ABI versions load, so it can no longer recur). tree-sitter-rustupgraded0.23→0.24.2(ABI 15; follows #403). Behavior-neutral:
nose query --format jsonis byte-identical before/after on the Rust dogfood tree
(crates/) and a Rust corpus (alacritty), with the dup-gate and soundness gate clean.tree-sitter-goupgraded0.23→0.25.0(ABI 15; follows #403), with the required
go.rsmigration: go 0.25's one structural change is wrappingblock/ switch-case /
select-case statements in a newstatement_listnode, so the frontend now flattens that
wrapper (stmt_children) — without it every nested statement orphans to Raw, spiking the go
Raw ratio 0.7% → 29%. With the migration the Raw ratio is restored to 0.7% andnose query
output is byte-identical to go 0.23 on 12/16 go corpus repos; the remaining four differ by
≤4 families from go 0.25's own parse-behaviour refinements (Raw ratio equal-or-lower, soundness
gate clean — not a lowering regression). (type_aliasalso gained atype_parametersfield for
generic aliases; the frontend ignores type declarations, so it has no effect.)
Fixed
- False merge closed: JS nullish-coalesce map default (adversarial co-evolution series 10,
#411, §CT). A latent oracle-blind merge — JSm.get(k) ?? d(nullish coalesce) shared an
exact fingerprint with the absence-only defaults (has/in/getOrDefault/.get(k, d)/
=== undefined), though they diverge on a present null-valued key. The null-guarded map default
now lowers to the faithfulValueOrDefault(notGetOrDefault), and theeval.rs
=== undefined-over-map.getexception is dropped. Corpus byte-identical; closes #409, #410. nose queryreport locations bounded to EOF + markdown drill-down (#428: #419, #421, #422).
Frontend::span()read tree-sitter'send_position().row + 1verbatim, so a unit ending in a
trailing newline reportedend_line = content_lines + 1(past EOF); the column-0 correction
already used indeclaration_facts.rsis now applied, dropping out-of-bounds locations across
the corpus to 0 (family/location counts unchanged).id=<fam> --format markdownnow renders the
all-copies extraction skeleton (andfullthe representative diff), composing with the
human/JSON views; help/usage/query-json wording corrected so thevaluesort key reads as
duplicated volume, not theremovablefield.nose query top=0now shows all families (matchingnose scan --top 0), instead of
returning an empty result. The dataset build already usedtop=0for "every family"; the
display paths (list,base,reinvented, and the--format markdown/sarifreport) now
agree, sonose query <path> --format sarif top=0is the complete-upload spelling and query
fully subsumes scan's truncation control. Notop=term still defaults to 30.
Notes
- value-graph opaque census instrument (#405, #391 prevalence probe). A hidden
nose value-census(env-gated, zero fingerprint impact) attributes everyValOp::Opaqueto its
IL construct — the value-graph analog ofcoverage_attribution.py. Finding (§CS): the #391
premise was wrong —m[k]/m.get(k)mint no opaque in the value graph (only the oracle
bails toValue::Err), so #391 is a convergence/canonicalization problem, not opaque-coverage;
Index/Field/membership carry ~0 opaque mass and #391 audits below the opaque bar.
Install nose-cli 0.11.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/corca-ai/nose/releases/download/v0.11.0/nose-cli-installer.sh | shInstall prebuilt binaries via Homebrew
brew install corca-ai/tap/noseDownload nose-cli 0.11.0
| File | Platform | Checksum |
|---|---|---|
| nose-cli-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| nose-cli-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| nose-cli-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| nose-cli-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |