Skip to content

feat(rules): add 4 design-system PreToolUse rules (DOJ-3924)#14

Merged
lapc506 merged 3 commits intomainfrom
doj-3924-ds-pretooluse-hook
May 10, 2026
Merged

feat(rules): add 4 design-system PreToolUse rules (DOJ-3924)#14
lapc506 merged 3 commits intomainfrom
doj-3924-ds-pretooluse-hook

Conversation

@lapc506
Copy link
Copy Markdown
Collaborator

@lapc506 lapc506 commented May 7, 2026

Summary

Adds the 4th enforcement layer for the Dojo Design System: AI write-time PreToolUse rules that fire on Edit | Write | MultiEdit and block (or warn) before the file is touched if the LLM proposes a DS violation. Complements dojo-os's existing useDsRules runtime hook, ds-lint pre-commit hook, and validate-storage-upload Edge Function.

4 new rules added to hooks/rules/rules.yaml:

  • ds-arbitrary-breakpoint — block min-[Xpx] arbitrary breakpoints in TSX/JSX/TS, force sm:/md:/lg:/xl:/2xl: presets
  • ds-deep-ui-import — block deep imports from @/components/ui/<file>, force barrel imports
  • ds-arbitrary-fixed-width-in-ds-component — block w-[Xpx] inside src/components/ui/ (DS components must be flexible — w-full + max-w-[Xpx]); app-level usage exempt
  • ds-raw-hex-color-in-source — warn on raw hex #RRGGBB in TSX/TS source; exempt tailwind.config.*, theme/, design-system/, design-tokens, .test.*, .stories.*

Each rule has 5 tests (20 new total). All rules ship with bypass markers (// hook-bypass: <id>) for the rare cases where a violation is the right call.

Why

The existing 3 layers all fire post-write. This 4th layer fires pre-write — tightest feedback loop, educates the LLM in real time, prevents the violation from ever landing on disk.

Implementation

  • Manifest-only change. No new bash scripts. No schema changes.
  • Uses the existing v1.5.0 framework: pre-edit.sh dispatches Edit/Write/MultiEdit; parse-input.sh extracts file_path + content; eval-rule.sh evaluates the rule.
  • One regex carefully anchored: (^|[^a-zA-Z-])w-\\[[0-9]+px\\] so legitimate composites like max-w-[200px] and min-w-[120px] don't false-positive.
  • Tested both via npm run test-hooks (56/56 pass — 36 existing + 20 new) and via manual smoke tests piping fixtures into eval-rule.sh directly.

Out of scope (deferred)

  • Touch-target rules (h-7/h-8 on interactives) — needs context-aware AST parsing, not regex
  • Per-pillar overrides — needs per-module YAML wiring (DOJ-3439 Phase B/C)
  • Auto-fix mode — out of scope per DOJ-3924 ACs

Test plan

  • npm run build-rules passes (14 rules generated, no schema errors)
  • npm run test-hooks passes (56/56)
  • Manual smoke test: each of the 4 rules fires correctly on a violating fixture and is silent on a clean one
  • git diff main..HEAD touches only hooks/rules/rules.yaml, hooks/rules/rules.json, hooks/rules/README.md

Closes DOJ-3924 (Phase G of DOJ-3439 design-system-linter).

@greptile review

Created by Claude Code on behalf of @andres-dojocoding

🤖 Generated with Claude Code

Adds AI-write-time enforcement for Design System rules — the 4th
enforcement layer complementing dojo-os's:
- useDsRules React hook (passive, runtime-only)
- ds-lint pre-commit hook (post-author, post-write)
- validate-storage-upload Edge Function (Storage bucket uploads)

This 4th layer fires PreToolUse on Edit/Write/MultiEdit, blocking the
write BEFORE the file is touched if the LLM proposes a violation.
Tightest feedback loop + educates the LLM in real time.

4 new rules:
- ds-arbitrary-breakpoint (block min-[Xpx] in TSX, suggest sm:/md:/lg:)
- ds-deep-ui-import (block @/components/ui/<file> deep imports, force barrel)
- ds-arbitrary-fixed-width-in-ds-component (block w-[Xpx] inside src/components/ui/)
- ds-raw-hex-color-in-source (warn raw hex in TSX/TS, exempt theme/test/storybook)

Each rule ships with 5 tests (20 total): blocks/warns the violation,
allows preset alternatives, allows exempt paths, allows bypass marker.

Bumps the rules manifest only — no new bash scripts, no schema changes.
The existing v1.5.0 manifest-driven framework dispatches via pre-edit.sh
on Edit/Write/MultiEdit and parse-input.sh extracts file_path + content.

Closes DOJ-3924 (Phase G of DOJ-3439 design-system-linter).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 7, 2026

Greptile Summary

This PR adds four PreToolUse rules that enforce the Dojo Design System at AI write-time — before a file is touched — completing the 4th enforcement layer alongside the existing runtime hook, pre-commit linter, and Storage upload validator. All three previously flagged issues have been resolved: the (^|/) path anchor now handles absolute paths, the ds-raw-hex-color-in-source description correctly reads "Warn", and lock-in tests for the design-system/ and design-tokens exemptions have been added.

  • ds-arbitrary-breakpoint blocks min-[Xpx] breakpoints in TSX/JSX/TS, directing authors to sm:/md:/lg:/xl:/2xl: presets.
  • ds-deep-ui-import blocks imports from @/components/ui/<file>, enforcing barrel-only access to the DS public API.
  • ds-arbitrary-fixed-width-in-ds-component blocks w-[Xpx] inside src/components/ui/, requiring w-full + max-w-[Xpx] for flexible DS components; absolute-path regression test included.
  • ds-raw-hex-color-in-source warns on raw #RRGGBB literals outside token/config/test/story paths, with 8 test cases locking in all five exemptions except .stories.*.

Confidence Score: 5/5

Manifest-only change adding 4 new enforcement rules; no runtime code paths are modified and no existing rules are altered.

All three previously flagged blocking issues have been addressed: the absolute-path anchor is fixed with (^|/), the action/description mismatch on the hex rule is corrected, and the two missing exemption tests have been added. The remaining observations are minor coverage gaps that do not affect correctness of the existing rules.

No files require special attention; the two open gaps (missing .stories.* test and uncovered max-[Xpx] breakpoints) are both in hooks/rules/rules.yaml.

Important Files Changed

Filename Overview
hooks/rules/rules.yaml Adds 4 DS PreToolUse rules; all previously-flagged P1 issues fixed (`(^
hooks/rules/rules.json Generated JSON mirror of rules.yaml; all 4 new rules faithfully represented including the 6-test suite for ds-arbitrary-fixed-width-in-ds-component and the 8-test suite for ds-raw-hex-color-in-source.
hooks/rules/README.md Adds a Rule families section documenting the new DS family; accurate, well-linked to the Linear issue.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Claude proposes Edit / Write / MultiEdit"] --> B["pre-edit.sh dispatches"]
    B --> C["parse-input.sh: extract file_path + content"]
    C --> D["eval-rule.sh: evaluate DS rules"]
    D --> E{"ds-arbitrary-breakpoint"}
    D --> F{"ds-deep-ui-import"}
    D --> G{"ds-arbitrary-fixed-width"}
    D --> H{"ds-raw-hex-color"}
    E -->|"match + no bypass"| I["EXIT 2 — BLOCKED"]
    F -->|"match + no bypass"| I
    G -->|"match + no bypass"| I
    H -->|"match + no bypass"| J["EXIT 0 + stderr WARNING"]
    E -->|"no match / bypass"| K["EXIT 0 — allowed"]
    F -->|"no match / bypass"| K
    G -->|"no match / bypass"| K
    H -->|"no match / bypass"| K
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
hooks/rules/rules.yaml:607-608
**`.stories.*` exemption lacks a lock-in test**

The `not_pattern` lists five exemptions; this PR added lock-in tests for `design-system/` and `design-tokens` (addressing the previous review comment) but `.stories.*` still has no corresponding test case. A future regex change that accidentally narrows `not_pattern` could silently start warning on story files without any test failure — the same scenario the two new tests were explicitly added to prevent.

### Issue 2 of 2
hooks/rules/rules.yaml:428-432
**`max-[Xpx]` arbitrary breakpoints not covered**

Tailwind supports both `min-[Xpx]:` and `max-[Xpx]:` arbitrary breakpoints. The content pattern `min-\[[0-9]+px\]` only catches the `min-` variant; a write like `className="max-[1000px]:hidden"` will silently pass through. If `max-[Xpx]` breakpoints are intentionally out of scope, a brief comment noting that would help future maintainers avoid surprise gaps.

Reviews (2): Last reviewed commit: "fix(rules): make ds-fixed-width rule fir..." | Re-trigger Greptile

Comment thread hooks/rules/rules.yaml Outdated
Comment thread hooks/rules/rules.yaml Outdated
Comment thread hooks/rules/rules.yaml
Comment thread hooks/rules/rules.yaml
Comment on lines +489 to +491
pattern: 'from[[:space:]]+["'']@/components/ui/[a-zA-Z][a-zA-Z0-9_-]+["'']'
action: block
bypass_marker: ds-deep-ui-import
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Deep imports with sub-paths are not caught

The character class [a-zA-Z][a-zA-Z0-9_-]+ contains no /, so an import like from "@/components/ui/button/index" or from "@/components/ui/date-picker/utils" won't fire the rule. In practice these multi-segment paths are rare, but a comment noting the limitation would help future maintainers.

Prompt To Fix With AI
This is a comment left during a code review.
Path: hooks/rules/rules.yaml
Line: 489-491

Comment:
**Deep imports with sub-paths are not caught**

The character class `[a-zA-Z][a-zA-Z0-9_-]+` contains no `/`, so an import like `from "@/components/ui/button/index"` or `from "@/components/ui/date-picker/utils"` won't fire the rule. In practice these multi-segment paths are rare, but a comment noting the limitation would help future maintainers.

How can I resolve this? If you propose a fix, please make it concise.

… hex warn

Greptile review on PR #14 caught three real issues:

1. ds-arbitrary-fixed-width-in-ds-component used a ^-anchored file_path
   pattern that never matched at runtime — Claude Code passes absolute
   paths like /home/user/repo/src/components/ui/foo.tsx, but ^src/ only
   anchors to start-of-string. All 56 tests passed because fixtures used
   relative paths; the rule was silently inert in production. Switch to
   (^|/)src/components/ui/ so both relative and absolute paths trigger
   it. Add a regression test covering an absolute fixture path.

2. ds-raw-hex-color-in-source description started with "Block" while
   action is warn — confused both maintainers reading the manifest and
   end-users seeing the runtime stderr. Description now says "Warn".

3. The design-system/ and design-tokens not_pattern exemptions had no
   test coverage. Added two fixtures so a future regex narrow-down that
   broke them would fail CI.

Tests: 59/59 passing (was 56/56; added 3 regression cases).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lapc506
Copy link
Copy Markdown
Collaborator Author

lapc506 commented May 10, 2026

Addressed all 3 substantive Greptile findings in 26a92e2:

1. ^src/ anchor → (^|/)src/components/ui/
Reproduced the bug: with the old pattern, /home/user/repo/src/components/ui/button.tsx (the absolute-path form Claude Code passes at runtime) returned exit 0 instead of 2 — rule was silently inert. Fixed and added a regression test (blocks-w-pixel-in-ui-button-absolute-path).

2. Description/action mismatch
ds-raw-hex-color-in-source description now reads "Warn on raw hex…" to match action: warn.

3. Test coverage for not_pattern exemptions
Added allows-design-system-folder (src/design-system/tokens.ts) and allows-design-tokens-file (src/lib/design-tokens.ts) so a future regex narrow-down that breaks those exemptions would fail CI.

Tests: 56/56 → 59/59 passing.

Issue 4 (deep-import multi-segment limitation) was a comment-only suggestion; given the rule only blocks the most common single-segment case and we have the bypass marker for genuine exceptions, leaving it as-is per its current scope.

@greptile review

GitHub Actions account is locked due to a billing issue, blocking every
ubuntu-latest job from starting. Same pattern dojo-os already adopted:
swap to blacksmith-2vcpu-ubuntu-2204 (Blacksmith managed runners) so the
test workflow can actually run.

Drop-in replacement — Blacksmith runners are API-compatible with the
GitHub-hosted Ubuntu image, and the workflow steps (npm ci, build-rules,
test-hooks.sh) don't depend on anything GitHub-specific.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lapc506 lapc506 merged commit 8785187 into main May 10, 2026
1 check passed
@lapc506 lapc506 deleted the doj-3924-ds-pretooluse-hook branch May 10, 2026 00:46
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