StyleProof 7.0.0: Trustworthy adoption
StyleProof 7.0.0: Trustworthy adoption
This major release establishes the supported 7.x line after the breaking Phase 0
and Release Confidence API removals that shipped in 6.4.0. It also includes
one-command setup, typed configuration, advisory adoption, exact audit output,
stricter known-truth browser evidence, and the pre-push audit-file repair.
Existing 6.x users must upgrade the package and Action together, refresh generated
workflows, and regenerate baselines.
Added
-
Framework-neutral SPA route discovery (#584).
defineCrawlCapturenow
observes programmatic navigation —history.pushState,replaceState, and
popstate, the APIs every client router funnels through — during the crawl,
and adds each newly observed same-origin route to the surface set. Routes the
app lands on through router redirects or asettlehook's client-side click
are captured without router-specific adapters; in-page<a href="#…">
anchors never reach the observer. Discovery is bounded (64 observed routes,
3 passes), logged, reconciled by theexpected/excludecoverage guard, and
can be disabled withobserveNavigation: false. -
Phase-0 detection corpus v2 (#447).
bench/detection-corpus-v2.json
freezes 23 reviewed mutants — resting computed-style changes across color,
typography, spacing, borders, effects, layout, and CSS variables; pseudo-element
and forced-state divergence; a sibling-insertion structural change; one
rendered image change with no computed-style correspondence (a designed,
counted miss); and four no-op controls including a computed-but-not-rendered
border color.npm run bench:detectionnow supportssmoke,pilot(v1
corpus),diagnostic(--case ID[,ID...]),sharded(--shard I/N), and
fullscopes; afullreceipt must execute the complete frozen corpus and is
the only scope that may claim the run. Render proof decouples computed-property
change from pixel change so computed-but-not-rendered mutants validate
honestly, and proof screenshots cover themaincontainer so out-of-box
effects are measured. The committed full-corpus receipt recorded 23 executed
and valid cases: 18 detected, 1 designed miss, 1 no-op false positive, 3
true negatives. -
Known-truth browser oracles now cover light/dark color scheme, flex/grid layout,
:focus-visiblewith:focus, box shadow/radius, and transform/opacity fidelity.
Each fixture fails closed on missing or wrong exact computed values. (#626, #627,
#628, #629, #630) -
Dogfood approve stub. StyleProof now ships
.github/workflows/styleproof-approve.yml,
the same thin caller adopters get fromstyleproof-init, pointing at
styleproof-approve-reusable.yml@main. Reviewer ticks stay inert until advisory
dogfood enablesrequire-approval. Does not change evidence confidence. (#644) -
StyleProof-on-StyleProof dogfood. Root
styleproof.config.tsdeclares the
realexample/demosurfaces in advisory mode (blocking: 'advisory',
requireApproval: false). Same-repo PRs run.github/workflows/styleproof-dogfood.yml:
capture those surfaces, publish maps tostyleproof-maps, and run the Action
withfail-on-diff: false/mode: advisoryso a report comment links the
run's report artifact. This check is advisory and is not part of hosted
required CI. The syntheticaction-dogfood.ymlcontract suite is unchanged.
(#642, #643) -
Action
comment-markerinput. Defaults to<!-- styleproof-report -->.
Set a distinct marker when more than one StyleProof Action runs on the same
pull request so the comments do not overwrite each other. -
Critical state obligations. Declare the state/surface IDs that must
produce certifying evidence instyleproof.critical-states.json
({"<surface>": {"owner": "...", "reason": "..."}}), via
--critical-states,$STYLEPROOF_CRITICAL_STATES, or
productState.criticalinstyleproof.config.ts. When the obligation file
is armed, a declared ID with unproven or incomparable paired evidence fails
closed, a declared ID with no paired evidence (lost capture, removed
surface, or unknown ID) fails closed, and a declared ID that is also
coverage-excluded is contradictory policy and fails closed. Obligations
only tighten certification; comparable pairs still certify and unasserted
scopes stay unasserted — no score, tier, or denominator is manufactured.
Missing explicit files, malformed entries, and unbounded owner/reason
metadata exit 2. (#442) -
Legacy product-state pair inventory. Declare known-legacy pairs in
styleproof.product-state.json({"<surface>": "<why>"}), via
--legacy-pairs,$STYLEPROOF_PRODUCT_STATE, or
productState.legacyPairsinstyleproof.config.ts. When that ledger is
armed, undeclared unproven pairs fail closed and cannot certify; declared
pairs stay advisory (certifiesFully: false). MatchingproductState {id, revision}remains the only certifying declare path.
productState.requireIdentitypins--require-state-identityin config.
Live StyleProof-on-StyleProof arms the ledger (styleproof.config.ts/
styleproof.config.jsonproductState.legacyPairs+
example/styleproof.product-state.json+$STYLEPROOF_PRODUCT_STATEon
styleproof-dogfood.yml). The declare-file path resolves from the
discovered config directory. Flag and$STYLEPROOF_PRODUCT_STATEoverride
that config path (empty env unarms it) so the syntheticaction-dogfood
suite does not inherit the livehomeledger and stale-fail
identity-stamped fixtures.classifyStyleProofVerdictreadslegacyPairs
so undeclared pairs areCERTIFICATION_FAILEDfor CLI, Action, and
comment — not a soft-greenNO_REVIEWABLE_STYLE_CHANGES. (#649) -
Coverage config manifest (
coverage.manifest): declare expected surfaces via
an external JSON file instead of programmaticexpected. The manifest format is
{ "version": 1, "surfaces": ["home", "dashboard", ...] }. Manifest surfaces
union with programmatic expected (neither can hide a hole); config exclude wins
over programmatic for the same key. Also addscoverage.strictand
coverage.excludefor config-level coverage settings. (#599) -
Optional
maxForcedStateElementsandmaxForcedStateScanWorkcapture limits.
Direct capture and explicit/crawl runners can increase or decrease resource
allowances without dropping interactive targets. Runner, surface, variant,
and live-state settings apply to primary captures, self-checks, and popups.
Defaults remain 2,000 elements per read and 32,000 total element reads. -
Config-driven prune schedule defaults. Map store prune now reads
mapStore.pruneRetentionDays(default 14) andmapStore.pruneBudgetBytes
(default 1.5GB) fromstyleproof.config.ts. Report store prune reads
reportStore.pruneRetentionDays(default 30) andreportStore.pruneBudgetBytes
(default 2GB). CLI flags still override config values. (#596) -
Approval reusable workflow.
styleproof-initnow scaffolds a thin caller
workflow (styleproof-approve.yml) that invokes the upstream reusable workflow
atBenSheridanEdwards/StyleProof/.github/workflows/styleproof-approve-reusable.yml@v7.
Logic updates ship with releases instead of drifting in copied files. (#598) -
Lint-artifacts workflow.
styleproof-initscaffolds
styleproof-lint-artifacts.yml, a belt-and-suspenders guard that fails the PR
if map artifacts (.json.gz,styleproof-manifest.json) are accidentally
committed to a PR branch. Mirrors the.gitignorepatterns. (#597) -
liveTextdeclaration ondefineStyleMapCapture/defineCrawlCapture.
Opt-in (trueor{ freeze?, selectors? }) so live/age/clock copy is classified
and, whenfreeze: true, fail-closed if it still drifts. RequirescaptureText: true.
Changed
-
Report delivery defaults to workflow artifacts (#587). The Action gains a
report-storageinput:artifact(default) uploadsstyleproof-report/as
a workflow artifact on the run — the PR comment links the artifact entry, and
report-retention-days(default 30) bounds its lifetime. Nothing is written
to repository git history, and the generated scaffold drops the
report-branch publish, the close-triggered and scheduled prune jobs, and
contents: write.report-storage: branchkeeps the proven
styleproof-reportsorphan-branch path with its commit-bound rendered link,
receipt readback, and prune jobs —styleproof-init --storage branchemits
it. The approval workflow verifies the same receipt either way: it reads
report.md/report.jsonat the publication commit in branch mode, or out of
the run's immutable report artifact in artifact mode, so reviewer approval
never lowers the evidence bar. Approval callers now grantactions: read
(artifact readback) andcontents: read(branch readback) — adopters with a
hand-written or pre-v7 approve caller should add both. -
Adoption surface consolidated (#480).
styleproof-initnow scaffolds the
smallest honest gate by default: onepull_requestworkflow that captures
base and head in the same job and diffs them, maps carried as workflow
artifacts, and an advisory gate — nostyleproof-mapsbranch, no report
branch requirement, no pre-push hook, no approval workflow. Three explicit
opt-ins reproduce the previous architecture:--workflow split(read-only
capture job plus trustedworkflow_runreport job for fork/Dependabot PRs),
--storage branch(map-store branch plus the pre-push publish hook), and
--mode certify|review-gate(blocking diff or reviewer approval).
split + branch + review-gateemits byte-identical files to the previous
default. A# styleproof-scaffold:marker in the generated workflow records
the chosen axes so--checkand--upgradeverify that mode's file set;
scaffolds created before the marker are inferred from their existing files. -
styleproof-ci --no-store. Skips every map-store restore probe and
ancestor-baseline reuse, captures base and head in the same job, and implies
--no-upload. The default single-workflow scaffold emits it. -
Named binaries are compatibility aliases. The unified
styleproofCLI
remains canonical; everystyleproof-*binary documents itself as an alias
for one major version. -
src/index.tstrimmed to the public contract. The package root now
exports the spec API (defineStyleMapCapture,defineCrawlCapture,
defineConfig, discovery/recipe/determinism/affected-surface helpers), the
three core functions (captureStyleMap,diffStyleMaps,
generateStyleMapReport), and their types. Internal machinery stays
importable only through leaf modules. (#480) -
README split. The README keeps the quickstart, the two modes, and the
certification boundary; the long-form contract moved to
docs/REFERENCE.md. (#480) -
Ancestor baseline reuse is now default-on (opt-out). When the exact base
commit has no map in the store, CI automatically falls back to the nearest
ancestor whose bundle exists and is compatible. Configure via
ancestorBaseline.enabled: falseinstyleproof.config.tsor
STYLEPROOF_ANCESTOR_BASELINE=0to disable. TheancestorBaseline.roots
config key (default['src']) controls which directories' changes count as
capture-relevant. (#595) -
Platform mismatch warning is suppressed by default. The compatibility-key
mismatch warning that fires when local and CI browsers differ is now quiet
unless you explicitly setsuppressPlatformWarning: falsein config or
STYLEPROOF_SUPPRESS_PLATFORM_WARNING=0. The warning was noisy for adopters
who understood the tradeoff; the exit-code behavior is unchanged. (#600)
Changed
- Generated report workflows, supported examples, the README, and repository skills
now use thev7Action and reusable-workflow aliases. - Package and lockfile metadata now identify version 7.0.0. Dependency resolution is
unchanged.
Fixed
-
.tsconfig package resolution uses the config file's package root.
Evaluatingstyleproof.config.tsresolvesstyleproofand peer packages from
the config file's directory and the nearestpackage.jsonwalking up from
that file — not onlyprocess.cwd(). Linkedstyleproof-ciprobe worktrees
(nonode_modulesof their own) also search the main working tree's matching
package roots so a host install stays visible. A subdirectory config with the
package installed at the package root — or only on the host checkout — now
loads. If the package still cannot be resolved, the fail-closed error lists
the searched package roots. Unloadable.tsstill does not fall back to
e2e/styleproof.spec.tsor sibling JSON. (#659) -
Action PARTIAL/DEGRADED_BASELINE copy names the real failure.
PARTIAL_BASELINEPR-comment, commit-status, and fail-echo strings now
interpolate the receipt surface key and SHA instead of pointing at the report
only.DEGRADED_BASELINEcopy claims a base capture/recapture failure only
whenbase-capture-failed=true; the head-only path with the flag false does
not say the base capture failed. Fail-closed gates are unchanged. (#657) -
Honest baseline-failure attribution. When
base-capture-failed=falseand a
baseline or compare fault occurs, report/audit/Action text names the failing
surface and SHA and no longer implies that a base recapture failed. Public
baselineFailuresreceipts now include the bounded baselinesha.
DEGRADED_BASELINE(base-capture-failed=true) remains the head-only recapture
failure. (#651) -
Live/age/clock text no longer masquerades as a stylesheet regression. Server-rendered
ages (open 102.1d→open 103.1d) are clock/live data, not a product CSS change.
DeclareliveText(requirescaptureText: true) so age-only drift stays advisory and
is notSTYLE_REVIEW_REQUIRED.{ freeze: true }fail-closes with a visible
CERTIFICATION_FAILEDerror when a freeze was declared but captured ages still
drift — never a soft-green, never a style approval. Undeclared age-driven geometry
still reviews as before; real stylesheet changes next to an age still gate. -
CERTIFICATION_FAILEDintegrity reasons now ship an adopter-legible repair
path.connector-partial,duplicate-id, andintegrity-mismatchstay
unapprovable as style. The report, audit trail, and Action comment name what
broke, what to fix, and how to verify. Dogfood fixtures reproduce each state.
(#650) -
Config discovery from a package subdirectory.
styleproof-map,styleproof-ci,
and the shared config loader walk upward from cwd to the git root for
styleproof.config.ts/.mjs/.js/.json. Relative file-path fields
(spec, crawl setup/exclude/out,coverage.manifest,affected.graph)
resolve from the config file's directory, notprocess.cwd(). A missing spec
after that walk fails closed and names every config path searched — no silent
fallback toe2e/styleproof.spec.tswhen a parent config exists. A JS-shaped
.ts(no type syntax) is evaluated on every supported Node, including 18/20.
Node 22 type-stripping can evaluate typed files; if that file cannot be
evaluated (unknown.tsextension, ordefineConfigcannot resolve the
styleproofpackage), the loader fails closed and names the path and reason.
A siblingstyleproof.config.jsonnever overrides a discovered.tsfor
policy or spec. It never returns{}or the defaulte2e/styleproof.spec.ts
while that file was found. A.mjs/.jsthat cannot resolve the package
still fails closed. (#645) -
A forced-state capture that uses its exact work allowance on the final state
is complete. Further required reads still fail closed, and truncation warnings
name both the document and aggregate limits. -
styleproof-initnow addsstyleproof-audit.jsonto the consumer.gitignore,
so machine audit output written by the pre-push hook can no longer turn a
docs-only commit into a render-affecting capture; the quarantined pre-push
dogfood E2E is restored. (#593) -
The README called its embedded StyleProof block "the unmodified product report". It is a
genuine run, but not unmodified:scripts/live-readme-report.mjsfeeds a hard-coded CSS
string to the staticexample/demo/index.html, then reorders the report's element-level
sections, dropsreport.jsonfrom the committed bundle, repoints the crop links, and
appends the approval box — so the block is the PR comment, not the report. The preamble
now states that provenance exactly.test/readme-live-report-provenance.test.mjspins each
disclosed fact to a detectable feature of the script in both directions, so the prose and the
script cannot drift apart again, and the count of edits is asserted rather than described. (#479)