Skip to content

StyleProof 7.0.0: Trustworthy adoption

Choose a tag to compare

@github-actions github-actions released this 16 Sep 18:58
· 44 commits to main since this release
e8291d1

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). defineCrawlCapture now
    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 a settle hook'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 the expected/exclude coverage guard, and
    can be disabled with observeNavigation: 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:detection now supports smoke, pilot (v1
    corpus), diagnostic (--case ID[,ID...]), sharded (--shard I/N), and
    full scopes; a full receipt 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 the main container 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-visible with :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 from styleproof-init, pointing at
    styleproof-approve-reusable.yml@main. Reviewer ticks stay inert until advisory
    dogfood enables require-approval. Does not change evidence confidence. (#644)

  • StyleProof-on-StyleProof dogfood. Root styleproof.config.ts declares the
    real example/demo surfaces in advisory mode (blocking: 'advisory',
    requireApproval: false). Same-repo PRs run .github/workflows/styleproof-dogfood.yml:
    capture those surfaces, publish maps to styleproof-maps, and run the Action
    with fail-on-diff: false / mode: advisory so a report comment links the
    run's report artifact. This check is advisory and is not part of hosted
    required CI. The synthetic action-dogfood.yml contract suite is unchanged.
    (#642, #643)

  • Action comment-marker input. 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 in styleproof.critical-states.json
    ({"<surface>": {"owner": "...", "reason": "..."}}), via
    --critical-states, $STYLEPROOF_CRITICAL_STATES, or
    productState.critical in styleproof.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.legacyPairs in styleproof.config.ts. When that ledger is
    armed, undeclared unproven pairs fail closed and cannot certify; declared
    pairs stay advisory (certifiesFully: false). Matching productState {id, revision} remains the only certifying declare path.
    productState.requireIdentity pins --require-state-identity in config.
    Live StyleProof-on-StyleProof arms the ledger (styleproof.config.ts /
    styleproof.config.json productState.legacyPairs +
    example/styleproof.product-state.json + $STYLEPROOF_PRODUCT_STATE on
    styleproof-dogfood.yml). The declare-file path resolves from the
    discovered config directory. Flag and $STYLEPROOF_PRODUCT_STATE override
    that config path (empty env unarms it) so the synthetic action-dogfood
    suite does not inherit the live home ledger and stale-fail
    identity-stamped fixtures. classifyStyleProofVerdict reads legacyPairs
    so undeclared pairs are CERTIFICATION_FAILED for CLI, Action, and
    comment — not a soft-green NO_REVIEWABLE_STYLE_CHANGES. (#649)

  • Coverage config manifest (coverage.manifest): declare expected surfaces via
    an external JSON file instead of programmatic expected. 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 adds coverage.strict and
    coverage.exclude for config-level coverage settings. (#599)

  • Optional maxForcedStateElements and maxForcedStateScanWork capture 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) and mapStore.pruneBudgetBytes
    (default 1.5GB) from styleproof.config.ts. Report store prune reads
    reportStore.pruneRetentionDays (default 30) and reportStore.pruneBudgetBytes
    (default 2GB). CLI flags still override config values. (#596)

  • Approval reusable workflow. styleproof-init now scaffolds a thin caller
    workflow (styleproof-approve.yml) that invokes the upstream reusable workflow
    at BenSheridanEdwards/StyleProof/.github/workflows/styleproof-approve-reusable.yml@v7.
    Logic updates ship with releases instead of drifting in copied files. (#598)

  • Lint-artifacts workflow. styleproof-init scaffolds
    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 .gitignore patterns. (#597)

  • liveText declaration on defineStyleMapCapture / defineCrawlCapture.
    Opt-in (true or { freeze?, selectors? }) so live/age/clock copy is classified
    and, when freeze: true, fail-closed if it still drifts. Requires captureText: true.

Changed

  • Report delivery defaults to workflow artifacts (#587). The Action gains a
    report-storage input: artifact (default) uploads styleproof-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: branch keeps the proven
    styleproof-reports orphan-branch path with its commit-bound rendered link,
    receipt readback, and prune jobs — styleproof-init --storage branch emits
    it. The approval workflow verifies the same receipt either way: it reads
    report.md/report.json at 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 grant actions: read
    (artifact readback) and contents: read (branch readback) — adopters with a
    hand-written or pre-v7 approve caller should add both.

  • Adoption surface consolidated (#480). styleproof-init now scaffolds the
    smallest honest gate by default: one pull_request workflow that captures
    base and head in the same job and diffs them, maps carried as workflow
    artifacts, and an advisory gate — no styleproof-maps branch, 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 trusted workflow_run report 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-gate emits byte-identical files to the previous
    default. A # styleproof-scaffold: marker in the generated workflow records
    the chosen axes so --check and --upgrade verify 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 styleproof CLI
    remains canonical; every styleproof-* binary documents itself as an alias
    for one major version.

  • src/index.ts trimmed 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: false in styleproof.config.ts or
    STYLEPROOF_ANCESTOR_BASELINE=0 to disable. The ancestorBaseline.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 set suppressPlatformWarning: false in 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 the v7 Action and reusable-workflow aliases.
  • Package and lockfile metadata now identify version 7.0.0. Dependency resolution is
    unchanged.

Fixed

  • .ts config package resolution uses the config file's package root.
    Evaluating styleproof.config.ts resolves styleproof and peer packages from
    the config file's directory and the nearest package.json walking up from
    that file — not only process.cwd(). Linked styleproof-ci probe worktrees
    (no node_modules of 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 .ts still does not fall back to
    e2e/styleproof.spec.ts or sibling JSON. (#659)

  • Action PARTIAL/DEGRADED_BASELINE copy names the real failure.
    PARTIAL_BASELINE PR-comment, commit-status, and fail-echo strings now
    interpolate the receipt surface key and SHA instead of pointing at the report
    only. DEGRADED_BASELINE copy claims a base capture/recapture failure only
    when base-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=false and 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
    baselineFailures receipts now include the bounded baseline sha.
    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.
    Declare liveText (requires captureText: true) so age-only drift stays advisory and
    is not STYLE_REVIEW_REQUIRED. { freeze: true } fail-closes with a visible
    CERTIFICATION_FAILED error 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_FAILED integrity reasons now ship an adopter-legible repair
    path.
    connector-partial, duplicate-id, and integrity-mismatch stay
    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, not process.cwd(). A missing spec
    after that walk fails closed and names every config path searched — no silent
    fallback to e2e/styleproof.spec.ts when 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 .ts extension, or defineConfig cannot resolve the
    styleproof package), the loader fails closed and names the path and reason.
    A sibling styleproof.config.json never overrides a discovered .ts for
    policy or spec. It never returns {} or the default e2e/styleproof.spec.ts
    while that file was found. A .mjs / .js that 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-init now adds styleproof-audit.json to 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.mjs feeds a hard-coded CSS
    string to the static example/demo/index.html, then reorders the report's element-level
    sections, drops report.json from 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.mjs pins 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)