CSS Phase C sprint C1: shared testimonials component (7 consumers) + fast-gate test infrastructure#371
Conversation
… in under a minute The full per-commit stack (test:critical + bin/test + bin/dtest, each with an in-harness Hugo rebuild) costs >5 min - unworkable for Phase C's ~20 micro-commits per sprint. qtest builds once (~11s, PRECOMPILED_ASSETS) and runs only the changed files' pages via a Minitest -n filter derived from the ownership map (~2.5s/test; measured 42s for a two-page run), plus the orphan guard and color-system check. Site-wide or unmapped files escalate to the full critical suite automatically. Spec gate stack re-tiered (items 4-5): qtest per micro-commit; full test:critical + bin/test + bin/dtest per component milestone and on the branch head before the PR - the macOS full suite stays mandatory as the only dedup-trap catcher. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…r the build Bare bin/qtest built the site and only then errored 'no pages given'. Now no-args means --changed (the common case), and unknown page keys fail fast pre-build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…wapped (1/7) 133-rule component = the 7-consumer intersection of the shared testimonials partial's rules (node-id + pp-review scoped), canonical order from about-us.css, font stacks normalized to var(--font-system-ui). Wired as a concat-slice member before pages/about-us.css - NOT @import: postcss-import silently skips post-prelude imports (only critical/ base.css sits in the concat prelude), verified by plugin bisect. Excluded from the component by preflight order-audit: - ud8jroeig5h2 @860 margin rule: ties with per-page @1115 keeps (equal specificity, both active <860px); extraction reordered the winner and shifted the heading module +60px on mobile - caught by qtest, root- caused via last-wins winner maps + DOM computed-style probe. - 38ejkdz2v4cq spacing cluster + homepage swiper-svg sizing stay per-page (real value divergence; variance logged in tracker). Gates: converged build x2; only the about-us bundle fingerprint changed; decl-set diff = exactly 4 font-family literal->var swaps (stack identical, def inlined site-wide via baseof); winner map clean modulo cssnano de-grouping; DOM probe byte-identical to pre-change control; qtest about-us 10 screenshots green, ZERO baseline changes; gzip 14971 -> 14936 (noise). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…variance log, order-audit traps Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
Same protocol as 782507e: 133 intersection rules removed from pages/clients.css (removal set == component keys, verified), component added as slice member before it. Gates: only clients bundle fingerprint changed; decl-diff = the 4 font-family var-swaps; winner map clean modulo cssnano de-grouping; qtest clients 9 screenshots green, zero baseline changes; gzip 18651->18636. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…3/7) Protocol of 782507e. Gates: only services bundle fingerprint changed; decl-diff = the 4 font var-swaps; qtest services green (one transient nav-hover error, clean on rerun), zero baseline changes; gzip 19943->19973 (+30B noise). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…ls.css (4/7) Protocol of 782507e. Gates: only single-use-cases bundle fingerprint changed; decl-diff = the 4 font var-swaps; qtest green, zero baseline changes; gzip 18845->18872 (+27B noise). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
….css (5/7) Protocol of 782507e. Gates: only single-service bundle fingerprint changed; decl-set IDENTICAL (this file already used var() stacks); qtest green, zero baseline changes; gzip 19066->19100 (+34B noise). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…ep, 6a/7) pages/use-cases.css's two top-of-file @imports only worked because critical/base.css is a pure-import prelude; inserting the testimonials component before pages/use-cases.css would push them post-prelude where postcss-import silently drops them. css-variables import dropped outright (base.css's own prelude @import already inlines it, skipDuplicates deduped the second copy); fl-builder-common-base becomes a template slice member at the identical concat position. Gate: compiled use-cases bundle BYTE-IDENTICAL, zero fingerprint changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…ting extras Per review: Ruby not bash (repo is Ruby-tooled; the bash version had already collected quoting bugs), YAGNI (component map only lists files that exist). Scoped tests always run; leftover budget (default 90s, --budget N) runs randomly-sampled non-scoped desktop+mobile tests so repeated micro-commit runs rotate through the whole suite. Full test:critical + bin/test + bin/dtest remain the pre-merge bar. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…(6/7) Protocol of 782507e, on top of the 6a slice-member prep. use-cases carries NO 38ejkdz2v4cq wrapper cluster (variance log) - its only keeps are the two ud8jroeig5h2 margin rules. Gates: only use-cases bundle fingerprint changed; decl-set IDENTICAL (already var() stacks); qtest green, zero baseline changes; gzip 16965->16977 (+12B noise). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
.fl-node-08kl1yzxeout .pp-swiper-button svg {24px} (line 1277) was
shadowed by the identical-specificity 48px rule at 10317 (last wins) -
production output already discarded it (all bundle fingerprints
byte-identical after deletion, winner map identical). Removing it
BEFORE the component swap prevents a keep-fires-last flip to 24px.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…hes ran 0 tests and failed the gate Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…7/7) Final consumer. The splitter removed BOTH in-file copies of the block (263 removals, unique set == component keys; the second copy is 5 rules short - verified subset). Slice member inserted before pages/homepage.css in homepage-css-resources.html. Gates: only the homepage bundle fingerprint changed; decl-set IDENTICAL (already var() stacks; production had already deduped the second copy); winner map clean; qtest homepage + random extras green, zero baseline changes; gzip 26644->26632. C1.1 extraction complete: testimonials styles live ONCE in components/testimonials.css for all 7 consumers (~130 rules x 6 copies deduplicated at source; shipped bytes unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…test-all background runner - test:critical now runs only the highest-risk pages (git-churn analysis 2026-07-19: blog + homepage + course + site-wide dominate 6-month history; plus the services/contact/free-consultation funnel and the mermaid/codeblock special-content locks). Full system suite moves to test:system; test:all unchanged (CI runs test:all already). - qtest refuses to start with dirty screenshot fixtures - a run REWRITES baselines, so starting dirty makes results unjudgeable (bit twice this session). - bin/dtest-all: whole suite (rake test:all) in a detached Docker container against its own _dest/public-dtest-all snapshot, logging to tmp/dtest-all.log (EXIT=n last line) - local work continues while it runs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…nt-skip warnings) The postcss-import bisect during C1.1 exposed warnings for @imports that postcss-import silently skips (post-prelude position). Audit result: - component-bundle.css lines 2886-2907: seven c-pp/c-hero/c-cta imports, skipped in every production build of blog-list + free-consultation; content ships site-wide anyway via the components.css INLINE bundle in baseof.html (verified: c-hero selectors present in compiled bundles) - dead lines, deleted with a tombstone comment. - pages/single-service.css + pages/homepage.css: css-variables imports, skipped; the def arrives via critical/base.css's prelude import chain and the baseof inline nav bundle - dead lines, deleted. Gate: converged build x2, ALL bundle fingerprints byte-identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
Replaces the C1.1 scratchpad awk pipeline (which caused the wrong-line dedupe and truncated-header defects). Modes: extract (pattern -> keys), keys / component / remainder (keyset-driven split preserving document order and @media grouping). Built-in guards: brace balance + comment closure on every input. Validated against the C1.1 ground truth: identical key multiset on the original about-us.css. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
|
Warning Review limit reached
Next review available in: 21 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (10)
📒 Files selected for processing (26)
📝 WalkthroughWalkthroughThe change adds scoped visual-test and full-suite runners, introduces CSS splitting utilities, extracts shared testimonials styling, wires it into page bundles, and replaces duplicated page-specific review CSS with shared rating and infolist rules. ChangesCSS testing and migration
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Developer
participant qtest
participant Hugo
participant ScreenshotTests
participant CriticalSuite
Developer->>qtest: run --changed
qtest->>qtest: map changed files to page keys
qtest->>Hugo: build scoped assets
qtest->>ScreenshotTests: run filtered desktop/mobile tests
qtest->>CriticalSuite: escalate for site-wide or unmapped changes
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…pped (1/6) 10-rule intersection of the shared CTA partial (partials/page/cta.html) across its 6 real consumers (tracker guessed 8; clients + contact-us carry no cta node rules). Zero selector+property ties with any keep - order-safe by construction. Variance kept per-page: the rujwd9mzxche rich-text pair (5 pages; services has none) and about-us's fdsvgxpowi03 spacing cluster. Remainders generated by bin/css-split span-cut mode (untouched regions byte-identical). Gate: only about-us fingerprint changed; decl-set identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
Gate: only homepage fingerprint changed; decl-set identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…ases swapped to cta-banner (3-6/6) C1.2 complete: CTA banner styles live once for all 6 consumers. Gate: exactly these 4 bundle fingerprints changed; per-bundle decl-set identical (0 missing / 0 extra each). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@bin/css-split`:
- Around line 31-34: Update normalize so rule-key canonicalization removes
comments and insignificant formatting without stripping semantic whitespace
between CSS tokens. Ensure selectors such as “.a .b” and “.a.b” produce distinct
keys, while applying the same token-preserving normalization to the media
portion.
- Around line 39-90: Update parse so both the initial brace validation and the
main scanner ignore braces and comment markers occurring inside quoted strings.
Track single- or double-quote state plus backslash escapes while scanning,
process comment delimiters only outside strings, and only update nesting depth
or split rules for syntax encountered outside comments and strings.
- Around line 105-106: Update the selector extraction and matching in the
css-split flow so leading section comments are removed before evaluating pat.
Ensure r.key is printed only when pat matches actual selector text, not comment
content, while preserving the existing rule extraction behavior.
In `@themes/beaver/assets/css/pages/about-us.css`:
- Around line 2949-2955: Remove the duplicated second font-style declaration
from the .pp-rating i rule in themes/beaver/assets/css/pages/about-us.css
(2949-2955), themes/beaver/assets/css/pages/clients.css (3487-3493),
themes/beaver/assets/css/pages/services.css (4490-4496),
themes/beaver/assets/css/pages/single-service.css (3586-3592),
themes/beaver/assets/css/pages/single-use-cases.css (3432-3438), and
themes/beaver/assets/css/pages/use-cases.css (2324-2330), leaving one
font-style: normal declaration in each rule.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5967bdb7-4a49-464f-8316-8816ce1390f5
⛔ Files ignored due to path filters (1)
bun.lockbis excluded by!**/bun.lockb
📒 Files selected for processing (25)
.okf/build/test-gates.md.okf/log.mdRakefilebin/css-splitbin/dtest-allbin/qtestdocs/projects/2509-css-migration/2026-07-12-css-maintainability-redesign.mddocs/projects/2509-css-migration/TASK-TRACKER.mdpackage.jsonthemes/beaver/assets/css/component-bundle.cssthemes/beaver/assets/css/components/testimonials.cssthemes/beaver/assets/css/pages/about-us.cssthemes/beaver/assets/css/pages/clients.cssthemes/beaver/assets/css/pages/homepage.cssthemes/beaver/assets/css/pages/services.cssthemes/beaver/assets/css/pages/single-service.cssthemes/beaver/assets/css/pages/single-use-cases.cssthemes/beaver/assets/css/pages/use-cases.cssthemes/beaver/layouts/page/about.htmlthemes/beaver/layouts/page/clients.htmlthemes/beaver/layouts/page/services.htmlthemes/beaver/layouts/page/use-cases.htmlthemes/beaver/layouts/partials/assets/homepage-css-resources.htmlthemes/beaver/layouts/services/single.htmlthemes/beaver/layouts/use-cases/single.html
…d for its 3 consumers Byte-identical across use-cases/clients/services (verified); zero selector+property ties. Gate: exactly the 3 bundle fingerprints changed; per-bundle decl-set identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
Orphaned by the dead-import cleanup (5a11cc1): their ONLY reference was component-bundle.css's post-prelude @imports, which postcss-import always skipped - so these files never reached any compiled bundle. components.css (the inline site-wide bundle) does not import them. Caught by the orphan guard in the qtest run. Gate: all bundle fingerprints identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…selines test/system/pages/careers_page_test.rb never ran under test:critical (single-star glob never descended into pages/) and cannot hold a baseline: a FORCE_SCREENSHOT_UPDATE pass followed by an immediate rerun fails 5/5 - the captures are unstable run-to-run (no stability waits; baselines predate the Course nav link and the 2604 typography work). The careers page keeps stable coverage via test_careers in the desktop AND mobile suites. Surfaced by the new test:system/dtest-all coverage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…-identical The first version re-wrapped every surviving @media rule into fresh shells, reflowing whole files (remainders came out LONGER than inputs and every consumer diff became a full-file rewrite). Remainders are now produced by cutting removed-rule spans from the original text; a media shell is only cut whole when ALL its inner rules are removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…modal load-time state The linux capture alternates between two internally-stable render states (gf_browser_* classes decided at load; ~10px form shift) - a FORCE-refreshed baseline failed the very next run, and stability_time_limit cannot help a per-load fork. Masked .gform_wrapper (precedent: blog/index .blog-post) and re-recorded BOTH platform baselines; verified stable across consecutive docker runs. This was the pre-existing master-level dtest failure documented in PR #371. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…omponents section Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
…aration cleanup - css-split normalize keeps descendant-combinator spaces (.a .b vs .a.b no longer collide as keys; the 6 collisions this reveals in C1 scope were benign - every compiled decl-set gate showed 0 missing/0 extra). - Scanner + brace-balance precheck are now string-literal aware (content: "}" parses instead of aborting). - extract mode strips leading comments before pattern-matching selectors. - Removed the doubled font-style:normal in the copied .pp-rating i block across 7 page files. CORRECTION vs the review thread: cssnano does NOT dedupe within-rule declarations - the 7 touched bundles' fingerprints DO change; gate = per-bundle declaration-set identical (0 missing / 0 extra) + scoped screenshot run green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V
f991038 to
e696847
Compare
What this is
Phase C sprint C1 — complete (spec:
docs/projects/2509-css-migration/2026-07-12-css-maintainability-redesign.md): the shared-partial styles the R-sprint delta ports copied into siblingpages/*.cssfiles now live ONCE each undercomponents/, plus the fast-gate test infrastructure that makes micro-commit sprints affordable, plus cleanup/baseline work surfaced along the way.The three extractions (C1.1-C1.3)
components/testimonials.csscomponents/cta-banner.cssbin/css-splitcomponents/header-cta.cssWired as concat-slice members (never
@import— postcss-import silently skips post-prelude imports; proven by plugin bisect; onlycritical/base.cssis prelude). Page-specific variance (spacing variants, homepage swiper sizing, the rujwd9mzxche pair) stays per-page — full variance log inTASK-TRACKER.md§C1.Gates on every extraction commit: converged build → only-target-bundle fingerprint change → per-bundle declaration-set equivalence (only the intended
font-familyliteral→var(--font-system-ui)normalizations; the var resolves to the identical stack) → winner-map clean → scoped screenshot gate, zero baseline changes. Notable catch: a cross-media equal-specificity tie (ud8jroeig5h2@860 vs @1115) flipped a mobile winner (+60px heading shift) — caught by the screenshot gate, rule excluded from the component; the preflight order-audit is now part of the method.Evidence: per-bundle gzip ±35B (noise, by design — source dedup, not transfer optimization);
fl-noderefs inpages/*.css~3,700 → 2,834. Final verification: full macOS system suite 65 runs / 119 screenshots / 0 failures, zero baseline changes; Linux full suite viabin/dtest-all.Test infrastructure
bin/qtest(Ruby): scoped visual gate — builds once, runs only affected pages' tests, spends the remaining 90s wall budget on randomly-sampled extras (rotating whole-suite coverage). ~25-60s per micro-commit vs >5 min. Refuses to run on dirty screenshot fixtures.rake test:critical: churn-based highest-risk subset (git-history analysis);rake test:system= full system suite (fixes the glob that silently excludedtest/system/pages/);test:allunchanged (CI runs it).bin/dtest-all(Ruby): whole suite in a detached Docker container against its own snapshot — local work continues while it runs.bin/css-split(Ruby): the extraction tool — span-cut remainders (untouched regions byte-identical), brace/comment guards, validated against C1.1 ground truth.Cleanups & baseline-drift fixes
@imports deleted (7 incomponent-bundle.css, 2 in pages files) — silently skipped in every build; output byte-identical.c-pp-*.cssfiles deleted (2,623 lines) — their only reference was the dead import chain; they never reached any compiled bundle. Output byte-identical.free_consultationLinux drift root-caused: Gravity Forms appliesgf_browser_*classes at load, producing two internally-stable render states (~10px form shift) — a force-refreshed baseline failed the very next run. Fixed by masking.gform_wrapper(repo precedent: blog-index.blog-post); verified stable across consecutive Docker runs. This was the pre-existing master-level dtest failure.careers_pagecomponent tests deleted: never ran under the oldtest:criticalglob, cannot hold a baseline (force-pass → immediate fail), baselines predated the Course nav + 2604 typography work; the careers page keeps stable desktop+mobile coverage viatest_careers.🤖 Generated with Claude Code
https://claude.ai/code/session_01GYRU9wGepomJo5hSuqtu7V