Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Work items

MOTIR-4001Done

`motir-marketing` has no design-asset guard lane — and a PORT of motir-core's mock scanner would be green on all 54 sites it was asked to catch

Found while running MOTIR-3985, which ADOPTED motir-core's answer that a design board's chrome owes AA (MOTIR-3054) and swept the 54 sites. That card's third obligation was "give the repository a lane, or say why it has none", and the reason it has none is not cost — it is that the obvious lane does not work, which is a finding rather than a deferral.

The decision is now enforced by nothing

design/marketing/design-notes.md § A design board's CHROME owes AA states the rule and .github/workflows/ci.yml runs lint · typecheck · build · test. vitest.config.mts's include is tests/**/*.test.ts(x) and no spec in tests/ reads design/** for contrast at all — the closest, tests/aaMatrix.test.ts, rules on --el-* token pairs read off rendered React components via tests/support/paintedInks.ts, which resolves Tailwind CLASSES and cannot see a .mock.html stylesheet. So the next asset authored here re-introduces the defect and CI is green.

⚠️ AND THE PORT IS THE TRAP — motir-core's scanner is STRUCTURALLY BLIND to this repository's defect

The natural fix is to copy motir-core's tests/theme/inkContrastMockScan.ts + vitest.design.config.ts. Measured against the 54 sites MOTIR-3985 swept, that port reports ZERO.

scanMock's muted arm resolves a surface with ownSurface, which reads a bg-(--el-*) utility, an inline background: var(--el-*), or a stylesheet background: var(--el-*); declaredToken returns null for anything that is not a var(--el-…). It then rules the resolved surface against TINTED_SURFACE_TOKENS, a list of --el-* NAMES.

These two mocks paint their annotation board on body { background: #f4f3f1 } — a raw scaffold colour the assets own outright, by the same convention motir-core's own mocks use for a sheet. The walk therefore resolves null at every one of the 54 elements, finds no tinted token, and abstains. Two of the sites were not even a --el-* INK (#1aae39 at 2.65:1, #e03131 at 4.07:1), which no arm of that scanner rules on either.

A guard whose blind spot is exactly the population it was installed for reads as a verdict. That is the thing this card exists to avoid shipping, and it is why the lane is a design question rather than a copy.

The scan that DOES see it, and what it costs

The measurement that found all 54 — and that MOTIR-3985 re-ran to verify 54 → 0 — loads each mock in headless chromium, walks every element carrying its own text node, reads its resolved color against its effective background (compositing ancestor fills), and rules at that element's OWN size and weight. It is total by construction: it starts from the elements, not from a list of pairs, so a raw hex is measured exactly like a token. Reproduced on that card's branch:

scanned 502 text-bearing elements; 1 below 1.4.3
  1   3.85  design-showcase.mock.html  span.i  13px/500  #6c5cdd on #0f0f0f  — "☾"

(That one entry is the already-dispositioned decorative glyph — an accessible name in its own sibling label, clearing 1.4.11's 3:1 — recorded in design-notes.md and not a 1.4.3 failure.)

The cost is a browser this repository does not depend on. package.json has no playwright / @playwright/test; MOTIR-3985's scan and both .png re-exports borrowed motir-core's installed chromium. So the lane means: a devDependency, a playwright install chromium step, a vitest.design.config.ts, and a design-guards job — added to deploy's needs in the same change, per ci.yml's own header ("do not declare a gate before its job exists").

⚠️ jsdom is NOT a cheaper substitute, and it was measured rather than assumed. It is already a devDependency and the suite already runs on it, so it is the first thing to reach for. Probed against design-showcase.mock.html, jsdom's getComputedStyle applies the class cascade for color — and then drops two things this scan cannot do without: it does not expand a font: SHORTHAND (.rule's font: 600 10px … reads back as 16px / normal, so every 1.4.3 large-text call is wrong), and it does not apply background: var(--el-page-bg) (.frame reads back rgba(0,0,0,0), so the surface walk resolves the sheet where the asset paints white). Both failures are silent and both fall on the side that under-reports. That is this card's own trap one layer down — do not re-derive it.

Acceptance criteria

  • A lane exists that FAILS on the pre-MOTIR-3985 state of both mocks. Assert it against a FIXTURE carrying the real failing markup (a #f4f3f1 sheet with a --el-text-muted caption on it, and a raw-hex ink), not against the swept assets — a guard whose only evidence is a green tree on a tree that already passes is the shape this card is about.
  • The lane rules on ::before / ::after generated content as well as elements, or its header states that it does not and why. design-showcase.mock.html's .fold::after ("FOLD — 900px", 10px/700, #e03131) is the live instance: document.querySelectorAll('*') cannot reach it, and a getComputedStyle(el, '::after') probe measures it at 4.51:1 on #ffffff — clearing 1.4.3 by 0.01, on a raw hex nothing measures.
  • ci.yml gains the job AND deploy's needs gains its name, in the same change.
  • The lane runs on EVERY branch prefix (a design/* diff is the only kind that can break it, and ci.yml here has no path filtering today — confirm that before assuming a skip exists to fix).
  • pnpm lint, format:check, typecheck, build, test.

Boundary

Not the sweepMOTIR-3985 swept the 54 sites and recorded the decision; this card only makes the decision enforceable. Not motir-core — nothing here changes inkContrastMockScan.ts; it is cited as the guard that does not transfer, and the reason is a property of THIS repository's assets (a raw sheet), not a defect in it. Not the component layertests/aaMatrix.test.ts already rules on what the site paints and is untouched. Not the .png re-export tool — that is the other thing the same devDependency pays for, and it is MOTIR-4003, which consumes this card's playwright pin. The two were one card until likely-over-gate-sizing fired on the pair at 90 minutes.

Advisory dispositions

validate_work_item after the split returns ONE blocker and ONE advisory, and both are correct.

  • blockerMOTIR-3985 at in_progress — INTENDED, not a defect. This card's first acceptance criterion asserts the lane FAILS on the pre-sweep state of both mocks, which is a fixture derived from that card's diff; and landing the lane before the sweep merges would turn main red. The edge is the one log-bug.md's EDGE TEST asked for, and it is why this card sits in MOTIR-656 rather than under the epic.
  • reference · advisory — names MOTIR-4003 at todo — NO EDGE IS OWED, and the direction is the whole answer. This card does not consume the render script; the render script consumes the playwright pin this card adds. The edge exists and runs the other way (MOTIR-4003 blocked_by MOTIR-4001). The reference here is a Boundary pointer naming what this card deliberately does NOT do, which is exactly the shape gate 4's "until proven otherwise" clause exists for.