Phase 133 path-motion: 0-human-UAT verification automation (carries unshipped v1.4+v1.5 backlog)#49
Open
szTheory wants to merge 125 commits into
Open
Phase 133 path-motion: 0-human-UAT verification automation (carries unshipped v1.4+v1.5 backlog)#49szTheory wants to merge 125 commits into
szTheory wants to merge 125 commits into
Conversation
…AA Gate) Define the v1.34 milestone (phases 128–136): perfect the scrypath_ops admin UI's existing dark/light/system theming in both modes (dark as signature, light at parity) per the brand book, add an automated WCAG AA contrast gate (AAA body text), and continue design-system/IA polish on v1.33's under-touched surfaces. Locked: comprehensive both-themes scope, system-follows-OS default preserved, AA hard gate, keep Tailwind v4 + daisyUI + .ops-* architecture. Writes PROJECT.md (Current Milestone), REQUIREMENTS.md (10 REQ-IDs), ROADMAP.md (9 phases, full coverage), and resets STATE.md for v1.34. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wave 1: install @axe-core/playwright + contrast-pairs.mjs manifest + npm script Wave 2: contrast-checker.mjs with WCAG math / --self-test / D-15 lockstep guards + Makefile targets + DESIGN-TOKENS.md update Wave 3: admin_contrast_matrix.spec.ts axe gate (D-09 discriminated union, D-08 system-dark invariants, D-04/D-20 two-pass axe) + curated 128-CONTRAST-REPORT.md Closes CONTRAST-HARNESS-01 planning. Measurement instruments only — zero pixel/token changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…in-contrast script - Add @axe-core/playwright@^4.11.3 to examples/scrypath_ecommerce devDependencies - Add test:e2e:admin-contrast script pointing to e2e/admin_contrast_matrix.spec.ts - Follow existing test:e2e:admin-matrix script shape exactly (D-16)
- 13-entry MUTED_PAIRS array: 12 contrast-gated + 1 decorative (.ops-trail__sep) - References token names only, no hex (D-10); alpha values as decimals 0-1 - .ops-badge-neutral uses bg_token base-200 (badge background, not base-100) - .ops-handoff__eyebrow has role large (uppercase + font-weight 700) - .ops-trail__sep has role decorative (excluded from checker evaluation) - D-15 lockstep guard input; D-12 sRGB compositing documented
…, --self-test - Dependency-free ESM script: node:fs/promises, node:path, node:url only - WCAG MATH: compositeAlpha (sRGB D-12), toLinear, relativeLuminance, contrastRatio - 11 semantic PAIR_RULES (base-content/base-100/200/300 + all X-content/X), THRESHOLDS (text/large/ui) - CSS parse: regex @plugin daisyui-theme blocks → 20 --color-* tokens per theme - D-15 Guard 1: token-count assertion locked to 20 (not 22) with comment explaining discrepancy - D-15 Guard 2: untracked muted token grep on color: CSS property only (not border-color/background/etc) - D-21: write report BEFORE exit; exit non-zero iff summary.aa_fail > 0 - --self-test: WCAG unit assertions + behavioral end-to-end sub-proof (buildReport + exit-code verification) - CI: GITHUB_STEP_SUMMARY markdown append + GITHUB_ACTIONS annotation (no-ops locally) - scrypath.contrast.v1 report schema (JSON + MD) written to CONTRAST_REPORT_DIR
- Add contrast and contrast-matrix to .PHONY in Makefile
- Add CONTRAST_REPORT_DIR ?= test-results/contrast variable declaration
- Add ADMIN_SCREENSHOT_DIR ?= test-results/admin-screenshots variable declaration
- Add contrast: target (fast, <1s, no browser) with help string
- Add contrast-matrix: target (browser required) with help string
- Both targets follow ##-self-documenting help convention + $${VAR:-$(VAR)} env override
- DESIGN-TOKENS.md: append Muted-Text Contrast Registry section
- Pointer to contrast-pairs.mjs as the D-15 lockstep guard muted registry
- sRGB compositing algorithm note (D-12): out = fg·α + bg·(1−α) per channel
- Threshold table (D-14): text 4.5/7.0, large 3.0/4.5, ui 3.0/4.5
- Create 128-02-SUMMARY.md with accomplishments, deviations (2 Rule 1 fixes), and live measurement results - STATE.md: advance plan 2→3, add decisions for #767676 ratio fix + selector regex fix - ROADMAP.md: update phase 128 progress (2/3 plans complete)
…t gate - D-09 discriminated union ThemeMode (explicit-light, explicit-dark, system-dark) - D-08 assertSystemDarkInvariants: no data-theme + matchMedia=true + data-theme-effective=dark - D-04 AA gate: axe withRules(['color-contrast']), violations[] only — never incomplete[] - D-20 AAA advisory: color-contrast-enhanced scoped to BODY_SELECTORS, severity aaa-body-advisory - D-02 dark-risk supplement: indices 10 (sync-drift), 11 (posture), 12 (playbooks), 13 (search) - D-21 writeContrastReport() before gate assertion; D-19 systemic tagging (>=3 screens) - scrypath.contrast.v1 schema with ContrastFinding type (all D-18 fields) - admin_screenshot_matrix.spec.ts untouched (success criterion #4)
… evidence - 108 total AA failures across 3 scenarios (incident 22, all_green 60, empty 26) - Systemic cluster 1: .leading-4 at 1.08:1 (dark surface-2 ramp collapse — DARKAUDIT-01 finding #1) - Systemic cluster 2: dark form inputs at 1.19:1 (#1f2933 on #141923) - Fast checker: 3 AA failures in light muted text at 3.9:1 (.ops-text-meta etc.) - Documents run environment (lane 4012, stale-image rebuild) and report-overwrite limitation - Sets VALIDATION.md nyquist_compliant and wave_0_complete to true
…pdates - 128-03-SUMMARY.md: 108 AA failures baseline, systemic cluster analysis, decisions - STATE.md: phase 128 marked COMPLETE, P03 metrics added, P02+P03 decisions appended - ROADMAP.md: phase 128 3/3 plans complete (128-03 checked off) - 128-RESEARCH.md: open questions resolved (A1 token count, A2 BODY_SELECTORS, A3 dark-risk supplement)
…ectness CR-01: broaden D-15 Guard 2 muted-color scan to match single-line rules (color: mid-line). .ops-text-meta (app.css:537) is now scanned — guard matches all 13 muted occurrences (was 12/13). Same-line selector detection added; self-test fixture locks the regression. WR-03: track brace depth in the backward selector walk so a muted color: inside an @media block attributes to the inner selector, and fail loudly when nested directly under an at-rule with no inner selector. WR-02: add reverse lockstep — every non-decorative manifest entry must have a matching app.css occurrence, so stale/removed entries are flagged. WR-01: compare the UN-ROUNDED contrast ratio against the AA/AAA threshold (contrastRatioRaw); round only for display so a boundary value is not mis-classified.
CR-02/WR-04: the three describeScenario tests no longer clobber a single
contrast-report.{json,md}. Each writes contrast-report.axe.<scenario>.{json,md}
so incident/all_green/empty reports coexist, and the axe producer's filename
no longer collides with the token checker's report.
CR-03: wrap the AAA color-contrast-enhanced pass in try/catch and only add
includes that actually match on the page (skipping the pass when none match),
so axe-core's 'No elements found for include' throw on body-text-less screens
can never propagate out of axeCheck and fail the gate (D-20: AAA advisory
NEVER affects exit code).
The fast token checker now writes contrast-report.token.{json,md} (and the
GitHub annotation + console line reference it) so it no longer clobbers the
axe matrix's contrast-report.axe.<scenario>.{json,md} when both producers
share CONTRAST_REPORT_DIR. Completes the WR-04 fix begun on the axe side.
Keep DESIGN-TOKENS.md in lockstep with the guard fixes: the muted-text registry guard is now bidirectional (CSS->manifest and manifest->CSS) and scans single-line and @media-nested rules. Documents the new behavior.
Standalone non-product (dev-tooling) milestone: shared Traefik *.localhost ingress to kill multi-demo host-port conflicts. Spec'd + discussed, not built. Does NOT disturb active v1.34. Grounded in .planning/research/docker-multiproject-dx-research.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One-plan, two-task structure: score 6-screen × DD1–DD6 dark brand matrix then assemble 129-DARK-AUDIT-BACKLOG.md mirroring 120-AUDIT-BACKLOG.md format. DD6 AA spine promoted from 128-CONTRAST-REPORT.md; #1B2230 surface-2 ramp gap anchored as finding #1 (DK-01). Closes DARKAUDIT-01. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n backlog - 19 findings (4 blocker / 6 structural / 9 polish) across 6 screens in dark theme - DK-01 (#1B2230 surface-2 ramp gap) anchored as finding #1: systemic DD1+DD4+DD6 blocker - DD6 AA spine promoted from 128-CONTRAST-REPORT.md (3 clusters, 0 ratios re-derived) - 7 systemic promotions across 6 screens routing to phases 130-132 - Every finding carries Phase (130-135) and Req columns from REQUIREMENTS.md §13 map - Scope guard confirmed: no source files outside phase directory modified
- 129-01-SUMMARY.md: 19 findings (4 blocker/6 structural/9 polish), DK-01 anchored - REQUIREMENTS.md: DARKAUDIT-01 marked complete - ROADMAP.md: Phase 129 plan progress updated (1/1 plans complete) - STATE.md: session and position updated for Phase 129 completion
Ship the v1.35 Brand System & Logo Identity milestone (phases 137-143). Logo (direction "C"): wordmark "scry/path" = pristine Familjen Grotesk letters + a weight-matched copper "/" path-separator (the routed-S concept, loop and angular, was killed). Mark = the s/p monogram. - brandbook/: self-contained HTML brand book (light/dark) + full logo family (transparent, no cage, svgo'd), subset woff2 fonts (Familjen Grotesk, Inter, IBM Plex Mono; SIL OFL), tokens (css/json/daisyui mirror), examples, a11y + decision notes. 320K total. - Adoption: scrypath_ops header inlines the s/p monogram (currentColor, so it theme-adapts) + SVG favicon; website brand-mark + OG card; root README logo header. Palette/type unchanged -> app.css untouched -> contrast gate green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…se 133 v1.35 brand milestone shipped directly (commit fcb8fc7), outside the GSD plan/execute artifact flow, so roadmap.analyze shows it 0% by design. Flip the active-milestone pointers back to the paused v1.34 dark-mode work: - STATE.md: milestone v1.34, current_phase 133, status in_progress (5/9) - ROADMAP.md: v1.35 ✅ complete, v1.34 🔨 active (resumed at Phase 133) Next: /gsd-plan-phase 133 (DARKMOTION-01; CONTEXT + DISCUSSION-LOG exist). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Path line-draw via .ops-path-trace::after (scaleX+opacity transition, hover/active-driven, NOT @Keyframes) - Active-path node glow .ops-path-node / --copper via --shadow-ops-glow[-copper] - Active Playbook item (.ops-object-item-active) composes violet glow onto its ring, dual-dark-pathed - Opt-in .ops-code-block--shimmer hover glint (opacity-only pseudo-element, @media (hover: hover)) - Reuses existing --duration-ops-*/--ease-ops-* tokens; no new literals, no forbidden layout props
… path anchor - attr(:shimmer, :boolean, default: false) on ops_code_block/1; conditional .ops-code-block--shimmer class before @Class (default off keeps evidence calm, D-04a/c) - Merge-trace disclosures gain stable .ops-path-trace class so the line-draw fires on hover only (never list-entry, D-06) - Playbook active item / Control Room recommended card wired via existing server-state classes (Task 1 CSS), no new attrs - Evidence code blocks unchanged (no shimmer); compiles warnings-as-errors
…OKENS.md - New Phase 133 section in the A1/A2/A3/A4 cadence: line-draw, node glow, copper node, active-item glow, opt-in shimmer - Lists every new class/token so Phase 134/135 can reuse the vocabulary - A3 'deliberately NOT shipped' restraint boundaries: no result-list stagger, no shimmer on evidence, glow not on text/panels/backgrounds - Extends Animate / Never-animate table (adds stroke-dashoffset, background-position, filter to Never)
- ScrypathOpsWeb.MotionContractTest mirrors design_tokens_contract_test (path-expanded @app_css, File.read!, Regex.scan over app.css) - scans only .ops-path-*/.ops-code-block--shimmer blocks - asserts (a) transform/opacity/box-shadow-only animated props, (b) every transition/animation duration is a --duration-ops-* token with no raw ms/s literal, (c) dual-dark-path symmetry of the active-path glow across [data-theme=dark] and the system-dark mirror - async: true, file-read + regex only, runs inside mix verify.opsui - proven RED on filter/raw-literal/missing-mirror injections Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…refire proof - Focused Playwright proof for DARKMOTION-01: exercises each shipped .ops-path-* anchor in dark AND light (+ system-dark) - Reduced-motion neutralization: each anchor computes ~0.01ms duration via the global prefers-reduced-motion rule, active end state stays visible (D-09) - Patch-refire probe (A3): runningKeyframeAnimationCount counts only running CSSAnimation (excludes patch-safe CSSTransition) on .ops-path-trace and .ops-object-item-active — 0 across single<->multi toggle and Playbook A->B - Merge-trace .ops-path-trace hover line-draw reaches scaleX(1) end state - Evidence code blocks asserted shimmer-off (no .ops-code-block--shimmer) - Targeted screenshot set (recommended card / merge-trace hover / active playbook item; dark + light + system-dark) — not the 40-shot recapture (D-05c) - Reuses waitForLiveConnected + seedScenario from helpers/e2e; manual seeded server per playwright.config.ts (compose dev lane -> current source)
…mport (code review WR-01, IN-01)
… make verify-path-motion Convert the former subjective "deliberate in dark / no light regression" human read into machine assertions in admin_path_motion.spec.ts: --shadow-ops-glow resolves to `none` in light vs a real aura in dark/system-dark, and the recommended card + active playbook item carry the violet glow (rgb 108,92,231) in their composited box-shadow in dark/system-dark but not in light. The spec already runs in CI (phase105-e2e -> npm run test:e2e over all of e2e/), so these are enforced on every push with no new workflow. Add a one-command local repro: `make verify-path-motion` boots the containerized test stack, warms the route, runs the spec with --retries=1 (mirrors CI, absorbs the cold-start seed socket-hang-up), and tears down. Plus a test:e2e:path-motion npm script. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both former human_needed items are now deterministic CI-enforced checks, so flip 133-VERIFICATION.md human_needed -> passed (6/6 truths, behavior_unverified 0) and complete 133-UAT.md (both tests pass, source: automated). Evidence: local `make verify-path-motion` 7/7 green + CI phase105-e2e green on main. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… styled The ops UI mounted at /admin/search loads its own stylesheet (/admin/search/assets/css/app.css) built from scrypath_ops/assets/css/app.css and served from scrypath_ops/priv/static — gitignored and symlinked into the example's _build (path dep). CI never ran `mix assets.build` for scrypath_ops, so it served no .ops-* CSS: --shadow-ops-glow resolved empty, the recommended-card box-shadow and .ops-path-trace rules were absent. admin_path_motion's glow + merge-trace assertions therefore failed in CI (they pass locally only because the baked Docker image carries locally-built ops assets). Add an explicit ops asset-build step mirroring the example asset step. Verified locally: `cd scrypath_ops && mix assets.build` emits priv/static/assets/css/app.css with the glow token + path-motion rules. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…po-hygiene gate) Two unshipped-milestone files failed `mix format --check-formatted` in the repo-hygiene gate (surfaced now that this PR brings the backlog to CI for the first time): motion_contract_test.exs (Phase 133, multi-line Enum.map_join) and layouts.ex (v1.5 brand_mark `attr` paren style). Format only, no behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ld logo.svg) The 4 OpsShellContractTest "ops shell markers" failures were the deferred v1.5 brand debt: assert_ops_shell! still required <img src="/ops/images/logo.svg">, but v1.5 replaced the header logo with an inline-SVG brand_mark (copper "/" accent, #C17A3E, aria-hidden) — confirmed no /ops/images/logo.svg references remain in lib. Update the contract to assert the brand mark's copper accent; "ScrypathOps" remains its accessible name. The job's `database does not exist` lines were teardown noise and `fan_out` was an Elixir 1.19 compiler warning — neither was a failure. Local: mix test ops_shell_contract_test.exs -> 4 tests, 0 failures. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Scope note (read first)
This branch is based on local `main`, which is ~120 commits ahead of `origin/main` (this repo keeps granular history locally and ships milestones via squash). So this PR's diff includes the entire unshipped v1.4 (dark-mode/Phase 133) + v1.5 (brand) backlog, not just the path-motion automation. It is fully green (16/16 checks). Treat it as the squash-ship of that backlog — there is no cleaner minimal slice to extract (the path-motion work depends on Phase 133 CSS; the brand-test fix depends on the v1.5 header — neither exists on `origin/main`).
The actual change in this branch (5 commits on top of the backlog)
Goal: make Phase 133 path-motion verification fully automated — 0 human UAT — and enforced in CI.
Proof
🤖 Generated with Claude Code