Skip to content

feat(lint): add needle_pivot_offset hub-referenced layout check#2744

Draft
xuanruli wants to merge 1 commit into
mainfrom
xuanru/needle-pivot-offset-check
Draft

feat(lint): add needle_pivot_offset hub-referenced layout check#2744
xuanruli wants to merge 1 commit into
mainfrom
xuanru/needle-pivot-offset-check

Conversation

@xuanruli

Copy link
Copy Markdown
Contributor

What it catches

A gauge needle / clock hand / dial pointer / radar sweep that rotates about the wrong pivot — the recovered center-of-rotation sits far from the dial hub (e.g. transform-origin at the needle base or SVG element edge instead of the dial center). Visually the needle "wobbles" or orbits off-axis instead of sweeping cleanly about the hub.

This is a genuine gap in the current checks: rotation_pivot_drift (#2741) provably cannot catch it — a correct sweeping needle's bbox-center orbits identically to a broken one, so only a dial-hub reference distinguishes them. This is the separate hub-referenced check that analysis called for.

How it works

  • Sampler maps 2 material endpoints per frame via getScreenCTM (honors the actual rendered transform, independent of svgOrigin).
  • Resolves the dial hub = shared center of the modal set of static concentric circles, or the arc-center of the largest static near-circular path (Kasa circle fit).
  • Fits a circle to the endpoint trajectory to recover the true center-of-rotation; flags drift > 0.35 * pointer_length. One warning per hub.
  • Never fires without a resolvable hub. Walks the rotation reference to the composition root (not the <svg>) so a pointer rotated by a div ancestor is measured correctly.
  • Multi-body guard: >= 2 bodies at distinct angular positions on one hub = orbit/atom system, not a dial → suppressed.

Corpus evidence (autonomous geometry-fuzz run, 81 fuzzed diagrams)

  • 7 / 7 true positives, 0 false positives across all 81 samples.
  • Assigned TPs: fuzz005, fuzz017, fuzz032. Bonus TPs: fuzz044, fuzz056, fuzz068, fuzz080.
  • The Gemini-3.6 video-judge itself MISSED all 4 bonus TPs (vlm_has_defects: false) — the deterministic hub-reference check beats the VLM on this defect class.
  • FPs driven to 0 by the two principled guards above: fuzz016 (planet arc rotated by a div ancestor) cleared by root-walk; fuzz055 (atom) cleared by the multi-body guard.
  • fuzz080 reads as a false positive to the connector check but is a true positive here — confirms the architectural boundary between the two checks is drawn correctly.

Validation

  • Autonomous Gemini-3.6 video-judge fuzz run to surface candidate defects, then a deterministic FP sweep across all 81 rendered compositions (not VLM-gated — code inspection is the arbiter, since the VLM both over- and under-calls this class).
  • 9 unit tests (checkPipeline.needlePivotOffset.test.ts) + full check suite pass; bun run build green.

🤖 Generated with Claude Code

Catches a gauge/clock/radar/dial pointer that rotates about the wrong
pivot (center-of-rotation far from the dial hub). Deterministic: samples
two material endpoints per frame via getScreenCTM, fits a circle to
recover the true center-of-rotation, and compares to the dial hub (modal
static-circle center, or arc-path center fallback). Suppresses multi-body
orbit/atom systems (>=2 angular clusters on one hub) and never fires
without a resolvable hub.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Fallow audit report

Found 14 findings.

Duplication (12)
Severity Rule Location Description
minor fallow/code-duplication packages/cli/src/commands/check.test.ts:323 Code clone group 1 (13 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/commands/check.test.ts:358 Code clone group 1 (13 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/commands/layout-audit.browser.js:1586 Code clone group 2 (13 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/commands/layout-audit.browser.js:1601 Code clone group 3 (8 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/utils/checkBrowser.ts:477 Code clone group 4 (6 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/utils/checkBrowser.ts:501 Code clone group 4 (6 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/utils/checkPipeline.needlePivotOffset.test.ts:43 Code clone group 5 (8 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/utils/checkPipeline.needlePivotOffset.test.ts:89 Code clone group 5 (8 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/utils/checkPipeline.ts:558 Code clone group 6 (24 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/utils/checkPipeline.ts:683 Code clone group 2 (13 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/utils/checkPipeline.ts:700 Code clone group 3 (8 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/utils/checkPipeline.ts:714 Code clone group 6 (24 lines, 2 instances)
Health (2)
Severity Rule Location Description
minor fallow/high-crap-score packages/cli/src/utils/checkBrowser.ts:509 'parseNeedlePivotSample' has CRAP score 31.6 (threshold: 30.0, cyclomatic 10)
minor fallow/high-cognitive-complexity packages/cli/src/utils/checkPipeline.ts:804 'detectNeedlePivotOffset' has cognitive complexity 21 (threshold: 15)

Generated by fallow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant