@litfamily/litgrok 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.grok/agents/litgrok-executor.md +33 -0
- package/.grok/agents/litgrok-korean-prose-editor.md +32 -0
- package/.grok/agents/litgrok-korean-style-analyzer.md +30 -0
- package/.grok/agents/litgrok-librarian-researcher.md +31 -0
- package/.grok/agents/litgrok-meaning-preservation-auditor.md +30 -0
- package/.grok/agents/litgrok-native-flow-reviewer.md +30 -0
- package/.grok/agents/litgrok-planner.md +31 -0
- package/.grok/agents/litgrok-polish-orchestrator.md +30 -0
- package/.grok/agents/litgrok-qa-runner.md +33 -0
- package/.grok/agents/litgrok-quality-reviewer.md +33 -0
- package/.grok/agents/litgrok-verifier.md +32 -0
- package/.grok/hooks/deliverable-hedge-guard.json +16 -0
- package/.grok/hooks/deliverable-hedge-guard.mjs +148 -0
- package/.grok/hooks/lit-mark.mjs +142 -0
- package/.grok/hooks/plan-gate.mjs +81 -0
- package/.grok/hooks/post-compact.json +15 -0
- package/.grok/hooks/post-compact.mjs +6 -0
- package/.grok/hooks/post-tool-use-failure.json +15 -0
- package/.grok/hooks/post-tool-use-failure.mjs +6 -0
- package/.grok/hooks/post-tool-use.json +16 -0
- package/.grok/hooks/post-tool-use.mjs +7 -0
- package/.grok/hooks/pre-compact.json +15 -0
- package/.grok/hooks/pre-compact.mjs +6 -0
- package/.grok/hooks/record-passive-event.mjs +198 -0
- package/.grok/hooks/session-start.json +15 -0
- package/.grok/hooks/session-start.mjs +37 -0
- package/.grok/hooks/stop-failure.json +15 -0
- package/.grok/hooks/stop-failure.mjs +6 -0
- package/.grok/hooks/stop.json +15 -0
- package/.grok/hooks/stop.mjs +36 -0
- package/.grok/hooks/subagent-start.json +15 -0
- package/.grok/hooks/subagent-start.mjs +6 -0
- package/.grok/hooks/subagent-stop.json +15 -0
- package/.grok/hooks/subagent-stop.mjs +6 -0
- package/.grok/hooks/user-prompt-submit.json +15 -0
- package/.grok/hooks/user-prompt-submit.mjs +7 -0
- package/.grok/rules/00-litgrok.md +152 -0
- package/.grok/skills/autoconference/LICENSE +21 -0
- package/.grok/skills/autoconference/PROVENANCE.md +23 -0
- package/.grok/skills/autoconference/SKILL.md +140 -0
- package/.grok/skills/autoconference/assets/conference_template.md +76 -0
- package/.grok/skills/autoconference/assets/report_template.md +56 -0
- package/.grok/skills/autoconference/assets/synthesis_template.md +40 -0
- package/.grok/skills/autoconference/references/_canonical-corpus/manifest.json +138 -0
- package/.grok/skills/autoconference/references/agent-prompts.md +30 -0
- package/.grok/skills/autoconference/references/conference-protocol.md +21 -0
- package/.grok/skills/autoconference/references/core-principles.md +13 -0
- package/.grok/skills/autoconference/references/family-contract.md +26 -0
- package/.grok/skills/autoconference/references/modes/analyze.md +11 -0
- package/.grok/skills/autoconference/references/modes/core/convergence-guide.md +41 -0
- package/.grok/skills/autoconference/references/modes/core/crash-recovery.md +11 -0
- package/.grok/skills/autoconference/references/modes/core.md +26 -0
- package/.grok/skills/autoconference/references/modes/debate.md +11 -0
- package/.grok/skills/autoconference/references/modes/plan.md +12 -0
- package/.grok/skills/autoconference/references/modes/resume.md +11 -0
- package/.grok/skills/autoconference/references/modes/ship.md +11 -0
- package/.grok/skills/autoconference/references/modes/survey.md +11 -0
- package/.grok/skills/autoconference/references/results-logging.md +12 -0
- package/.grok/skills/autoconference/references/visualization-guide.md +10 -0
- package/.grok/skills/autoconference/scripts/init_conference.py +646 -0
- package/.grok/skills/autoconference/scripts/verify-canonical-corpus.mjs +177 -0
- package/.grok/skills/autoconference/templates/code-performance.md +59 -0
- package/.grok/skills/autoconference/templates/debate-mode.md +50 -0
- package/.grok/skills/autoconference/templates/prompt-optimization.md +58 -0
- package/.grok/skills/autoconference/templates/quick-conference.md +48 -0
- package/.grok/skills/autoconference/templates/research-synthesis.md +56 -0
- package/.grok/skills/autoconference/templates/survey-mode.md +63 -0
- package/.grok/skills/autoresearch/LICENSE +21 -0
- package/.grok/skills/autoresearch/PROVENANCE.md +22 -0
- package/.grok/skills/autoresearch/SKILL.md +149 -0
- package/.grok/skills/autoresearch/assets/report_template.md +52 -0
- package/.grok/skills/autoresearch/assets/research_template.md +38 -0
- package/.grok/skills/autoresearch/assets/results_template.tsv +2 -0
- package/.grok/skills/autoresearch/references/_canonical-corpus/manifest.json +148 -0
- package/.grok/skills/autoresearch/references/core-principles.md +16 -0
- package/.grok/skills/autoresearch/references/family-contract.md +36 -0
- package/.grok/skills/autoresearch/references/modes/core/evaluator-contract.md +12 -0
- package/.grok/skills/autoresearch/references/modes/core/stuck-detection.md +11 -0
- package/.grok/skills/autoresearch/references/modes/core.md +21 -0
- package/.grok/skills/autoresearch/references/modes/debug/investigation-techniques.md +11 -0
- package/.grok/skills/autoresearch/references/modes/debug.md +11 -0
- package/.grok/skills/autoresearch/references/modes/fix.md +11 -0
- package/.grok/skills/autoresearch/references/modes/learn.md +11 -0
- package/.grok/skills/autoresearch/references/modes/plan.md +14 -0
- package/.grok/skills/autoresearch/references/modes/predict/persona-templates.md +11 -0
- package/.grok/skills/autoresearch/references/modes/predict.md +11 -0
- package/.grok/skills/autoresearch/references/modes/reason.md +11 -0
- package/.grok/skills/autoresearch/references/modes/scenario/dimensions.md +11 -0
- package/.grok/skills/autoresearch/references/modes/scenario.md +11 -0
- package/.grok/skills/autoresearch/references/modes/security/owasp-checklist.md +11 -0
- package/.grok/skills/autoresearch/references/modes/security/stride-model.md +11 -0
- package/.grok/skills/autoresearch/references/modes/security.md +12 -0
- package/.grok/skills/autoresearch/references/modes/ship/type-checklists.md +12 -0
- package/.grok/skills/autoresearch/references/modes/ship.md +11 -0
- package/.grok/skills/autoresearch/references/results-logging.md +12 -0
- package/.grok/skills/autoresearch/references/visualization-guide.md +24 -0
- package/.grok/skills/autoresearch/scripts/init_research.py +391 -0
- package/.grok/skills/autoresearch/scripts/style_presets.py +123 -0
- package/.grok/skills/autoresearch/scripts/verify-canonical-corpus.mjs +179 -0
- package/.grok/skills/browser-drive/SKILL.md +194 -0
- package/.grok/skills/browser-drive/references/snapshot-act-loop.md +61 -0
- package/.grok/skills/comment-checker/SKILL.md +194 -0
- package/.grok/skills/debugging/SKILL.md +82 -0
- package/.grok/skills/debugging/references/methodology/00-setup.md +108 -0
- package/.grok/skills/debugging/references/methodology/02-investigate.md +126 -0
- package/.grok/skills/debugging/references/methodology/04-oracle-triple.md +106 -0
- package/.grok/skills/debugging/references/methodology/05-escalate.md +69 -0
- package/.grok/skills/debugging/references/methodology/06-fix.md +116 -0
- package/.grok/skills/debugging/references/methodology/08-qa.md +94 -0
- package/.grok/skills/debugging/references/methodology/09-cleanup.md +164 -0
- package/.grok/skills/debugging/references/methodology/partial-runtime-evidence.md +228 -0
- package/.grok/skills/debugging/references/post-tool-use-failure-taxonomy.md +55 -0
- package/.grok/skills/debugging/references/reproduction-recipes.md +182 -0
- package/.grok/skills/debugging/references/runtimes/bundled-js-binary.md +415 -0
- package/.grok/skills/debugging/references/runtimes/go.md +252 -0
- package/.grok/skills/debugging/references/runtimes/native-binary.md +484 -0
- package/.grok/skills/debugging/references/runtimes/node.md +260 -0
- package/.grok/skills/debugging/references/runtimes/python.md +248 -0
- package/.grok/skills/debugging/references/runtimes/rust.md +234 -0
- package/.grok/skills/debugging/references/tools/ghidra.md +212 -0
- package/.grok/skills/debugging/references/tools/playwright-cli.md +194 -0
- package/.grok/skills/debugging/references/tools/pwndbg.md +263 -0
- package/.grok/skills/debugging/references/tools/pwntools.md +265 -0
- package/.grok/skills/deep-interview/SKILL.md +216 -0
- package/.grok/skills/frontend-ui-ux/LICENSE +21 -0
- package/.grok/skills/frontend-ui-ux/PROVENANCE.json +38 -0
- package/.grok/skills/frontend-ui-ux/SKILL.md +58 -0
- package/.grok/skills/frontend-ui-ux/SOURCE-MANIFEST.json +1060 -0
- package/.grok/skills/frontend-ui-ux/THIRD-PARTY-NOTICE.txt +14 -0
- package/.grok/skills/frontend-ui-ux/data/design-intelligence.json +1 -0
- package/.grok/skills/frontend-ui-ux/references/_canonical-corpus/legal/frontend-ATTRIBUTION.md +217 -0
- package/.grok/skills/frontend-ui-ux/references/_canonical-corpus/legal/frontend-LICENSE-Apache-2.0.txt +201 -0
- package/.grok/skills/frontend-ui-ux/references/_canonical-corpus/legal/root-LICENSE +21 -0
- package/.grok/skills/frontend-ui-ux/references/_canonical-corpus/manifest.json +873 -0
- package/.grok/skills/frontend-ui-ux/references/adaptive-layout.md +92 -0
- package/.grok/skills/frontend-ui-ux/references/brand-and-imagery.md +93 -0
- package/.grok/skills/frontend-ui-ux/references/complete-contract.md +557 -0
- package/.grok/skills/frontend-ui-ux/references/composition.md +85 -0
- package/.grok/skills/frontend-ui-ux/references/creative-directions.md +80 -0
- package/.grok/skills/frontend-ui-ux/references/design/README.md +248 -0
- package/.grok/skills/frontend-ui-ux/references/design/_INDEX.md +191 -0
- package/.grok/skills/frontend-ui-ux/references/design/airbnb.md +393 -0
- package/.grok/skills/frontend-ui-ux/references/design/airtable.md +92 -0
- package/.grok/skills/frontend-ui-ux/references/design/apple.md +250 -0
- package/.grok/skills/frontend-ui-ux/references/design/aside.md +209 -0
- package/.grok/skills/frontend-ui-ux/references/design/binance.md +348 -0
- package/.grok/skills/frontend-ui-ux/references/design/bmw.md +183 -0
- package/.grok/skills/frontend-ui-ux/references/design/brutalist-skill.md +92 -0
- package/.grok/skills/frontend-ui-ux/references/design/bugatti.md +271 -0
- package/.grok/skills/frontend-ui-ux/references/design/cal.md +262 -0
- package/.grok/skills/frontend-ui-ux/references/design/claude.md +315 -0
- package/.grok/skills/frontend-ui-ux/references/design/clay.md +307 -0
- package/.grok/skills/frontend-ui-ux/references/design/clickhouse.md +284 -0
- package/.grok/skills/frontend-ui-ux/references/design/clone-from-url.md +65 -0
- package/.grok/skills/frontend-ui-ux/references/design/cohere.md +269 -0
- package/.grok/skills/frontend-ui-ux/references/design/coinbase.md +132 -0
- package/.grok/skills/frontend-ui-ux/references/design/composio.md +310 -0
- package/.grok/skills/frontend-ui-ux/references/design/cursor.md +312 -0
- package/.grok/skills/frontend-ui-ux/references/design/design-system-architecture.md +244 -0
- package/.grok/skills/frontend-ui-ux/references/design/elevenlabs.md +268 -0
- package/.grok/skills/frontend-ui-ux/references/design/expo.md +284 -0
- package/.grok/skills/frontend-ui-ux/references/design/ferrari.md +317 -0
- package/.grok/skills/frontend-ui-ux/references/design/figma.md +223 -0
- package/.grok/skills/frontend-ui-ux/references/design/framer.md +249 -0
- package/.grok/skills/frontend-ui-ux/references/design/gpt-tasteskill.md +74 -0
- package/.grok/skills/frontend-ui-ux/references/design/hashicorp.md +281 -0
- package/.grok/skills/frontend-ui-ux/references/design/ibm.md +335 -0
- package/.grok/skills/frontend-ui-ux/references/design/image-to-code-skill.md +1228 -0
- package/.grok/skills/frontend-ui-ux/references/design/imagegen-brandkit.md +798 -0
- package/.grok/skills/frontend-ui-ux/references/design/imagegen-frontend-mobile.md +1465 -0
- package/.grok/skills/frontend-ui-ux/references/design/imagegen-frontend-web.md +987 -0
- package/.grok/skills/frontend-ui-ux/references/design/intercom.md +149 -0
- package/.grok/skills/frontend-ui-ux/references/design/kraken.md +128 -0
- package/.grok/skills/frontend-ui-ux/references/design/lamborghini.md +291 -0
- package/.grok/skills/frontend-ui-ux/references/design/layout-skill.md +107 -0
- package/.grok/skills/frontend-ui-ux/references/design/lazyweb.md +77 -0
- package/.grok/skills/frontend-ui-ux/references/design/linear.app.md +370 -0
- package/.grok/skills/frontend-ui-ux/references/design/lovable.md +301 -0
- package/.grok/skills/frontend-ui-ux/references/design/mastercard.md +368 -0
- package/.grok/skills/frontend-ui-ux/references/design/meta.md +369 -0
- package/.grok/skills/frontend-ui-ux/references/design/minimalist-skill.md +85 -0
- package/.grok/skills/frontend-ui-ux/references/design/minimax.md +260 -0
- package/.grok/skills/frontend-ui-ux/references/design/mintlify.md +329 -0
- package/.grok/skills/frontend-ui-ux/references/design/miro.md +111 -0
- package/.grok/skills/frontend-ui-ux/references/design/mistral.ai.md +264 -0
- package/.grok/skills/frontend-ui-ux/references/design/mongodb.md +269 -0
- package/.grok/skills/frontend-ui-ux/references/design/nike.md +366 -0
- package/.grok/skills/frontend-ui-ux/references/design/notion.md +312 -0
- package/.grok/skills/frontend-ui-ux/references/design/nvidia.md +296 -0
- package/.grok/skills/frontend-ui-ux/references/design/ollama.md +270 -0
- package/.grok/skills/frontend-ui-ux/references/design/opencode.ai.md +284 -0
- package/.grok/skills/frontend-ui-ux/references/design/output-skill.md +49 -0
- package/.grok/skills/frontend-ui-ux/references/design/pinterest.md +233 -0
- package/.grok/skills/frontend-ui-ux/references/design/playstation.md +367 -0
- package/.grok/skills/frontend-ui-ux/references/design/posthog.md +259 -0
- package/.grok/skills/frontend-ui-ux/references/design/raycast.md +271 -0
- package/.grok/skills/frontend-ui-ux/references/design/react-dev-tooling-skill.md +230 -0
- package/.grok/skills/frontend-ui-ux/references/design/redesign-skill.md +178 -0
- package/.grok/skills/frontend-ui-ux/references/design/renault.md +314 -0
- package/.grok/skills/frontend-ui-ux/references/design/replicate.md +264 -0
- package/.grok/skills/frontend-ui-ux/references/design/resend.md +306 -0
- package/.grok/skills/frontend-ui-ux/references/design/revolut.md +188 -0
- package/.grok/skills/frontend-ui-ux/references/design/runwayml.md +247 -0
- package/.grok/skills/frontend-ui-ux/references/design/sanity.md +360 -0
- package/.grok/skills/frontend-ui-ux/references/design/sentry.md +265 -0
- package/.grok/skills/frontend-ui-ux/references/design/shopify.md +353 -0
- package/.grok/skills/frontend-ui-ux/references/design/soft-skill.md +98 -0
- package/.grok/skills/frontend-ui-ux/references/design/spacex.md +197 -0
- package/.grok/skills/frontend-ui-ux/references/design/spotify.md +249 -0
- package/.grok/skills/frontend-ui-ux/references/design/starbucks.md +583 -0
- package/.grok/skills/frontend-ui-ux/references/design/stitch-design-example.md +121 -0
- package/.grok/skills/frontend-ui-ux/references/design/stitch-skill.md +184 -0
- package/.grok/skills/frontend-ui-ux/references/design/stripe.md +325 -0
- package/.grok/skills/frontend-ui-ux/references/design/supabase.md +258 -0
- package/.grok/skills/frontend-ui-ux/references/design/superhuman.md +255 -0
- package/.grok/skills/frontend-ui-ux/references/design/taste-skill.md +1206 -0
- package/.grok/skills/frontend-ui-ux/references/design/tesla.md +289 -0
- package/.grok/skills/frontend-ui-ux/references/design/theverge.md +342 -0
- package/.grok/skills/frontend-ui-ux/references/design/together.ai.md +266 -0
- package/.grok/skills/frontend-ui-ux/references/design/uber.md +298 -0
- package/.grok/skills/frontend-ui-ux/references/design/vercel.md +313 -0
- package/.grok/skills/frontend-ui-ux/references/design/vodafone.md +426 -0
- package/.grok/skills/frontend-ui-ux/references/design/voltagent.md +326 -0
- package/.grok/skills/frontend-ui-ux/references/design/warp.md +256 -0
- package/.grok/skills/frontend-ui-ux/references/design/webflow.md +95 -0
- package/.grok/skills/frontend-ui-ux/references/design/wired.md +281 -0
- package/.grok/skills/frontend-ui-ux/references/design/wise.md +176 -0
- package/.grok/skills/frontend-ui-ux/references/design/x.ai.md +260 -0
- package/.grok/skills/frontend-ui-ux/references/design/zapier.md +331 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/EVIDENCE.md +97 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/README.md +48 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/UPSTREAM.md +80 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/lane-a-direction.md +64 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/lane-b-execution.md +65 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/lane-c-review.md +65 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/lane-d-memory.md +83 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/orchestration.md +80 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/routing.md +79 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/LICENSE +21 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/accessibility-reviewer.md +83 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/content-writer.md +132 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-builder.md +109 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-critic.md +89 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-lead.md +113 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-scout.md +78 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-strategist.md +121 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/heuristic-evaluator.md +268 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/inspiration-scout.md +107 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/motion-designer.md +120 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/accessible-content/reference.md +101 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/adaptive-interfaces/reference.md +109 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/cognitive-accessibility/reference.md +107 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-debate/reference.md +199 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-debt-tracker/reference.md +174 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-handoff/reference.md +125 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-md/reference.md +106 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-retrospective/reference.md +266 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-review/reference.md +123 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-system-alignment/reference.md +120 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/designpowers-critique/reference.md +164 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/heuristic-evaluation/reference.md +85 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/inclusive-personas/reference.md +98 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/inspiration-scouting/reference.md +165 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/interaction-design/reference.md +122 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/motion-choreography/reference.md +81 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/research-planning/reference.md +96 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/responsive-patterns/reference.md +77 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/synthetic-user-testing/reference.md +192 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/taste-feedback/reference.md +165 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/taste-report/reference.md +78 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/token-architecture/reference.md +75 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/ui-composition/reference.md +117 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/usability-testing/reference.md +78 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/verification-before-shipping/reference.md +125 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/voice-and-tone/reference.md +79 -0
- package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/writing-design-plans/reference.md +119 -0
- package/.grok/skills/frontend-ui-ux/references/evidence-review.md +126 -0
- package/.grok/skills/frontend-ui-ux/references/implementation-platforms.md +109 -0
- package/.grok/skills/frontend-ui-ux/references/inclusive-interface.md +92 -0
- package/.grok/skills/frontend-ui-ux/references/interaction-motion.md +101 -0
- package/.grok/skills/frontend-ui-ux/references/operating-lanes.md +92 -0
- package/.grok/skills/frontend-ui-ux/references/perfection/README.md +160 -0
- package/.grok/skills/frontend-ui-ux/references/perfection/react-perf-tooling.md +127 -0
- package/.grok/skills/frontend-ui-ux/references/performance-delivery.md +93 -0
- package/.grok/skills/frontend-ui-ux/references/product-direction.md +84 -0
- package/.grok/skills/frontend-ui-ux/references/redesign-playbook.md +97 -0
- package/.grok/skills/frontend-ui-ux/references/system-foundations.md +84 -0
- package/.grok/skills/frontend-ui-ux/references/taste-direction.md +86 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/README.md +659 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/charts.csv +26 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/colors.csv +162 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/icons.csv +106 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/landing.csv +35 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/products.csv +162 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/react-performance.csv +45 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/astro.csv +54 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/flutter.csv +53 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/html-tailwind.csv +56 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/jetpack-compose.csv +53 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/nextjs.csv +53 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/nuxt-ui.csv +51 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/nuxtjs.csv +59 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/react-native.csv +52 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/react.csv +54 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/shadcn.csv +61 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/svelte.csv +54 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/swiftui.csv +51 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/vue.csv +50 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/styles.csv +85 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/typography.csv +74 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/ui-reasoning.csv +162 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/ux-guidelines.csv +100 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/web-interface.csv +31 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/scripts/core.py +262 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/scripts/design_system.py +1148 -0
- package/.grok/skills/frontend-ui-ux/references/ui-ux-db/scripts/search.py +114 -0
- package/.grok/skills/frontend-ui-ux/references/visual-language.md +84 -0
- package/.grok/skills/frontend-ui-ux/references/visual-reconstruction.md +97 -0
- package/.grok/skills/frontend-ui-ux/schemas/design-contract-v1alpha1.schema.json +306 -0
- package/.grok/skills/frontend-ui-ux/schemas/design-contract-v1beta1.schema.json +291 -0
- package/.grok/skills/frontend-ui-ux/schemas/design-contract-v1beta2.schema.json +311 -0
- package/.grok/skills/frontend-ui-ux/scripts/design-contract-format.mjs +286 -0
- package/.grok/skills/frontend-ui-ux/scripts/design-contract-inventory-rules.mjs +354 -0
- package/.grok/skills/frontend-ui-ux/scripts/design-contract-rules.mjs +361 -0
- package/.grok/skills/frontend-ui-ux/scripts/design-contract-surface-rules.mjs +313 -0
- package/.grok/skills/frontend-ui-ux/scripts/design-data.mjs +68 -0
- package/.grok/skills/frontend-ui-ux/scripts/errors.mjs +12 -0
- package/.grok/skills/frontend-ui-ux/scripts/import-design-intelligence.mjs +124 -0
- package/.grok/skills/frontend-ui-ux/scripts/json-boundary.mjs +63 -0
- package/.grok/skills/frontend-ui-ux/scripts/query-design-intelligence.mjs +106 -0
- package/.grok/skills/frontend-ui-ux/scripts/search.mjs +43 -0
- package/.grok/skills/frontend-ui-ux/scripts/source-replay.mjs +153 -0
- package/.grok/skills/frontend-ui-ux/scripts/strict-json.mjs +143 -0
- package/.grok/skills/frontend-ui-ux/scripts/validate-design-contract.mjs +52 -0
- package/.grok/skills/frontend-ui-ux/scripts/verify-canonical-corpus.mjs +214 -0
- package/.grok/skills/lit-burnoff/SKILL.md +175 -0
- package/.grok/skills/lit-burnoff-file/SKILL.md +67 -0
- package/.grok/skills/lit-code/SKILL.md +531 -0
- package/.grok/skills/lit-code/references/go/README.md +90 -0
- package/.grok/skills/lit-code/references/go/backend-stack.md +641 -0
- package/.grok/skills/lit-code/references/go/bootstrap.md +328 -0
- package/.grok/skills/lit-code/references/go/bubbletea-v2.md +360 -0
- package/.grok/skills/lit-code/references/go/cobra-stack.md +468 -0
- package/.grok/skills/lit-code/references/go/concurrency.md +362 -0
- package/.grok/skills/lit-code/references/go/data-modeling.md +329 -0
- package/.grok/skills/lit-code/references/go/error-handling.md +359 -0
- package/.grok/skills/lit-code/references/go/golangci-strict.md +236 -0
- package/.grok/skills/lit-code/references/go/grpc-connect.md +375 -0
- package/.grok/skills/lit-code/references/go/libraries.md +337 -0
- package/.grok/skills/lit-code/references/go/one-liners.md +202 -0
- package/.grok/skills/lit-code/references/go/sqlc-pgx.md +471 -0
- package/.grok/skills/lit-code/references/go/testing.md +467 -0
- package/.grok/skills/lit-code/references/go/type-patterns.md +298 -0
- package/.grok/skills/lit-code/references/permission-sandbox-matrix.md +70 -0
- package/.grok/skills/lit-code/references/python/README.md +314 -0
- package/.grok/skills/lit-code/references/python/async-anyio.md +442 -0
- package/.grok/skills/lit-code/references/python/data-modeling.md +233 -0
- package/.grok/skills/lit-code/references/python/data-processing.md +133 -0
- package/.grok/skills/lit-code/references/python/error-handling.md +218 -0
- package/.grok/skills/lit-code/references/python/fastapi-stack.md +316 -0
- package/.grok/skills/lit-code/references/python/httpx2-optimization.md +360 -0
- package/.grok/skills/lit-code/references/python/libraries.md +307 -0
- package/.grok/skills/lit-code/references/python/one-liners.md +268 -0
- package/.grok/skills/lit-code/references/python/orjson-stack.md +378 -0
- package/.grok/skills/lit-code/references/python/pydantic-ai.md +285 -0
- package/.grok/skills/lit-code/references/python/pyproject-strict.md +232 -0
- package/.grok/skills/lit-code/references/python/textual-tui.md +201 -0
- package/.grok/skills/lit-code/references/python/type-patterns.md +176 -0
- package/.grok/skills/lit-code/references/rust/README.md +317 -0
- package/.grok/skills/lit-code/references/rust/async-tokio.md +299 -0
- package/.grok/skills/lit-code/references/rust/axum-stack.md +467 -0
- package/.grok/skills/lit-code/references/rust/cargo-strict.md +317 -0
- package/.grok/skills/lit-code/references/rust/clap-stack.md +409 -0
- package/.grok/skills/lit-code/references/rust/concurrency.md +375 -0
- package/.grok/skills/lit-code/references/rust/libraries.md +439 -0
- package/.grok/skills/lit-code/references/rust/one-liners.md +291 -0
- package/.grok/skills/lit-code/references/rust/proptest-insta.md +429 -0
- package/.grok/skills/lit-code/references/rust/type-state.md +354 -0
- package/.grok/skills/lit-code/references/rust/unsafe-discipline.md +250 -0
- package/.grok/skills/lit-code/references/rust/zero-cost-safety.md +527 -0
- package/.grok/skills/lit-code/references/rust-ub/README.md +289 -0
- package/.grok/skills/lit-code/references/rust-ub/miri-sanitizers-loom.md +411 -0
- package/.grok/skills/lit-code/references/rust-ub/ub-taxonomy.md +269 -0
- package/.grok/skills/lit-code/references/tool-boundaries.md +66 -0
- package/.grok/skills/lit-code/references/typescript/README.md +195 -0
- package/.grok/skills/lit-code/references/typescript/backend-hono.md +672 -0
- package/.grok/skills/lit-code/references/typescript/bootstrap.md +199 -0
- package/.grok/skills/lit-code/references/typescript/data-modeling.md +202 -0
- package/.grok/skills/lit-code/references/typescript/error-handling.md +169 -0
- package/.grok/skills/lit-code/references/typescript/tsconfig-strict.md +152 -0
- package/.grok/skills/lit-code/references/typescript/type-patterns.md +196 -0
- package/.grok/skills/lit-code/references/worked-cases.md +190 -0
- package/.grok/skills/lit-commit/SKILL.md +218 -0
- package/.grok/skills/lit-comprehend/SKILL.md +63 -0
- package/.grok/skills/lit-comprehend/references/artifact-format.md +58 -0
- package/.grok/skills/lit-comprehend/references/artifact-template.md +219 -0
- package/.grok/skills/lit-comprehend/references/honesty-ledger-contract.md +51 -0
- package/.grok/skills/lit-comprehend/references/micro-worlds.md +195 -0
- package/.grok/skills/lit-comprehend/references/worked-explainer.md +51 -0
- package/.grok/skills/lit-crucible/SKILL.md +231 -0
- package/.grok/skills/lit-handoff/SKILL.md +159 -0
- package/.grok/skills/lit-handoff/evals/evals.json +154 -0
- package/.grok/skills/lit-handoff/examples/HANDOFF-example-generic-auth-refactor.md +97 -0
- package/.grok/skills/lit-handoff/references/_canonical-corpus/manifest.json +17 -0
- package/.grok/skills/lit-handoff/references/source-pointer.md +31 -0
- package/.grok/skills/lit-handoff/scripts/verify-canonical-corpus.mjs +137 -0
- package/.grok/skills/lit-handoff/templates/HANDOFF.md +121 -0
- package/.grok/skills/lit-init/SKILL.md +244 -0
- package/.grok/skills/lit-korean/SKILL.md +176 -0
- package/.grok/skills/lit-plan/SKILL.md +71 -0
- package/.grok/skills/lit-plan/references/plan-schema.md +87 -0
- package/.grok/skills/lit-plan/references/start-work-handoff-contract.md +59 -0
- package/.grok/skills/lit-plan/scripts/scaffold-plan.mjs +259 -0
- package/.grok/skills/lit-plan/scripts/validate-plan.mjs +89 -0
- package/.grok/skills/lit-recap/SKILL.md +57 -0
- package/.grok/skills/lit-scientific-visualization/SKILL.md +213 -0
- package/.grok/skills/lit-scientific-visualization/scripts/verify-canonical-corpus.mjs +181 -0
- package/.grok/skills/lit-team/SKILL.md +73 -0
- package/.grok/skills/lit-team/references/explore-packet.md +70 -0
- package/.grok/skills/lit-team/references/general-purpose-packet.md +75 -0
- package/.grok/skills/lit-team/references/plan-packet.md +73 -0
- package/.grok/skills/litgoal/SKILL.md +98 -0
- package/.grok/skills/litgrok/SKILL.md +77 -0
- package/.grok/skills/litresearch/SKILL.md +60 -0
- package/.grok/skills/litresearch/references/mcp-tool-use-patterns.md +83 -0
- package/.grok/skills/litresearch/references/source-verdict-taxonomy.md +40 -0
- package/.grok/skills/litwork/SKILL.md +109 -0
- package/.grok/skills/lsp/SKILL.md +56 -0
- package/.grok/skills/lsp/references/built-in-lsp-contract.md +71 -0
- package/.grok/skills/lsp-setup/SKILL.md +82 -0
- package/.grok/skills/lsp-setup/references/bash/README.md +54 -0
- package/.grok/skills/lsp-setup/references/c-cpp/README.md +58 -0
- package/.grok/skills/lsp-setup/references/csharp/README.md +64 -0
- package/.grok/skills/lsp-setup/references/dart/README.md +48 -0
- package/.grok/skills/lsp-setup/references/elixir/README.md +51 -0
- package/.grok/skills/lsp-setup/references/go/README.md +53 -0
- package/.grok/skills/lsp-setup/references/haskell/README.md +57 -0
- package/.grok/skills/lsp-setup/references/java/README.md +55 -0
- package/.grok/skills/lsp-setup/references/julia/README.md +56 -0
- package/.grok/skills/lsp-setup/references/kotlin/README.md +58 -0
- package/.grok/skills/lsp-setup/references/lua/README.md +48 -0
- package/.grok/skills/lsp-setup/references/php/README.md +49 -0
- package/.grok/skills/lsp-setup/references/python/README.md +60 -0
- package/.grok/skills/lsp-setup/references/ruby/README.md +53 -0
- package/.grok/skills/lsp-setup/references/rust/README.md +55 -0
- package/.grok/skills/lsp-setup/references/swift/README.md +52 -0
- package/.grok/skills/lsp-setup/references/terraform/README.md +50 -0
- package/.grok/skills/lsp-setup/references/typescript/README.md +63 -0
- package/.grok/skills/lsp-setup/references/yaml/README.md +47 -0
- package/.grok/skills/lsp-setup/references/zig/README.md +49 -0
- package/.grok/skills/lsp-setup/scripts/detect-lsp.mjs +40 -0
- package/.grok/skills/lsp-setup/scripts/lsp-server-table.mjs +309 -0
- package/.grok/skills/lsp-setup/scripts/verify-lsp.mjs +54 -0
- package/.grok/skills/refactor/SKILL.md +210 -0
- package/.grok/skills/review-work/SKILL.md +515 -0
- package/.grok/skills/review-work/references/behavior-lane-contract.md +48 -0
- package/.grok/skills/review-work/references/documentation-lane-contract.md +49 -0
- package/.grok/skills/review-work/references/integration-lane-contract.md +51 -0
- package/.grok/skills/review-work/references/regression-lane-contract.md +48 -0
- package/.grok/skills/review-work/references/safety-lane-contract.md +48 -0
- package/.grok/skills/review-work/references/test-lane-contract.md +48 -0
- package/.grok/skills/review-work/scripts/check-lanes.mjs +63 -0
- package/.grok/skills/rules/SKILL.md +51 -0
- package/.grok/skills/rules/references/loading-order-contract.md +66 -0
- package/.grok/skills/rules/scripts/resolve-guidance.mjs +180 -0
- package/.grok/skills/skill-observer/SKILL.md +78 -0
- package/.grok/skills/skill-observer/references/review-contract.md +77 -0
- package/.grok/skills/skill-observer/scripts/curator.mjs +121 -0
- package/.grok/skills/skill-observer/scripts/review.mjs +347 -0
- package/.grok/skills/skill-observer/scripts/skill-loop.mjs +2087 -0
- package/.grok/skills/skill-observer/scripts/validate-skills.mjs +83 -0
- package/.grok/skills/start-work/SKILL.md +396 -0
- package/.grok/skills/structural-search/SKILL.md +195 -0
- package/.grok/skills/visual-qa/SKILL.md +50 -0
- package/.grok/skills/visual-qa/references/capture-playbook.md +47 -0
- package/.grok/skills/visual-qa/references/complete-contract.md +721 -0
- package/.grok/skills/visual-qa/references/verdict-taxonomy.md +34 -0
- package/.grok/skills/visual-qa/scripts/verify-evidence-manifest.mjs +91 -0
- package/.grok/skills/wikify/SKILL.md +65 -0
- package/.grok/skills/wikify/references/page-format.md +81 -0
- package/.grok/skills/wikify/references/provenance-contract.md +52 -0
- package/.grok/vendor/NOTICE.md +16 -0
- package/.grok/vendor/licenses/045_scientific-visualization-MIT.txt +21 -0
- package/.grok/vendor/provenance/045_scientific-visualization.md +37 -0
- package/.grok/vendor/scientific-visualization/assets/color_palettes.py +197 -0
- package/.grok/vendor/scientific-visualization/assets/nature.mplstyle +75 -0
- package/.grok/vendor/scientific-visualization/assets/presentation.mplstyle +74 -0
- package/.grok/vendor/scientific-visualization/assets/publication.mplstyle +78 -0
- package/.grok/vendor/scientific-visualization/evals/evals.json +158 -0
- package/.grok/vendor/scientific-visualization/references/_canonical-corpus/manifest.json +38 -0
- package/.grok/vendor/scientific-visualization/references/color_palettes.md +380 -0
- package/.grok/vendor/scientific-visualization/references/journal_requirements.md +359 -0
- package/.grok/vendor/scientific-visualization/references/matplotlib_examples.md +608 -0
- package/.grok/vendor/scientific-visualization/references/mdanalysis_martini_visualization.md +85 -0
- package/.grok/vendor/scientific-visualization/references/publication_guidelines.md +217 -0
- package/.grok/vendor/scientific-visualization/references/seaborn_for_publications.md +293 -0
- package/.grok/vendor/scientific-visualization/scripts/figure_export.py +238 -0
- package/.grok/vendor/scientific-visualization/scripts/style_presets.py +467 -0
- package/.grok/vendor/scientific-visualization/tests/test_figure_export.py +51 -0
- package/.grok/vendor/scientific-visualization/tests/test_style_presets.py +114 -0
- package/CHANGELOG.md +131 -0
- package/CODE_OF_CONDUCT.md +9 -0
- package/CONTRIBUTING.md +22 -0
- package/LICENSE +21 -0
- package/README.md +228 -0
- package/README_ko-KR.md +228 -0
- package/SECURITY.md +11 -0
- package/SUPPORT.md +9 -0
- package/bin/litgrok.mjs +860 -0
- package/docs/assets/cover.webp +0 -0
- package/docs/assets/litgrok-clay-icon.png +0 -0
- package/docs/assets/litgrok-continuity-1600.webp +0 -0
- package/docs/assets/litgrok-ignition-1600.webp +0 -0
- package/docs/assets/litgrok-wordmark.svg +5 -0
- package/docs/assets/readme/README.md +31 -0
- package/docs/assets/readme/badge-license.svg +1 -0
- package/docs/assets/readme/badge-version.svg +1 -0
- package/docs/privacy.md +13 -0
- package/docs/reference.md +271 -0
- package/docs/reference_ko-KR.md +267 -0
- package/package.json +50 -0
- package/plugin.json +27 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
// Front door for litfamily.design-contract/v1alpha1.
|
|
2
|
+
//
|
|
3
|
+
// A contract binds exactly two kinds of immutable identity: one `source_hash` for the
|
|
4
|
+
// revision under design, and one `sha256` for each reference it declares. It owes nothing to
|
|
5
|
+
// any other packaged artifact and requires no retrieval step before it can be validated.
|
|
6
|
+
//
|
|
7
|
+
// Rules are collected rather than thrown so a single run reports every defect. Callers that
|
|
8
|
+
// only need a yes/no answer use validateDesignContractValue.
|
|
9
|
+
import { UiuxContractError } from "./errors.mjs";
|
|
10
|
+
import {
|
|
11
|
+
canonicalDesignContract,
|
|
12
|
+
ID_PREFIXES,
|
|
13
|
+
isLowercaseSha256,
|
|
14
|
+
isPrefixedId,
|
|
15
|
+
readDesignContract,
|
|
16
|
+
} from "./design-contract-format.mjs";
|
|
17
|
+
import {
|
|
18
|
+
collectInventoryIssues,
|
|
19
|
+
designContractInventoryEntries,
|
|
20
|
+
} from "./design-contract-inventory-rules.mjs";
|
|
21
|
+
import { collectSurfaceIssues, isObject } from "./design-contract-surface-rules.mjs";
|
|
22
|
+
|
|
23
|
+
export const DESIGN_SCHEMA = "litfamily.design-contract/v1alpha1";
|
|
24
|
+
export const DESIGN_SCHEMA_BETA = "litfamily.design-contract/v1beta1";
|
|
25
|
+
export const DESIGN_SCHEMA_BETA2 = "litfamily.design-contract/v1beta2";
|
|
26
|
+
|
|
27
|
+
// Exactly twelve root keys, in the order a reviewer reads them: identity, intent, direction,
|
|
28
|
+
// the finite surface, the four quality budgets, the evidence policy, and the two places where
|
|
29
|
+
// something is knowingly left undone.
|
|
30
|
+
const DESIGN_CONTRACT_ROOT_KEYS = Object.freeze([
|
|
31
|
+
"schema_id", "contract_id", "source_hash", "intent", "direction", "inventory",
|
|
32
|
+
"accessibility", "localization", "performance", "evidence_policy", "omissions",
|
|
33
|
+
"accepted_exceptions",
|
|
34
|
+
]);
|
|
35
|
+
const DESIGN_CONTRACT_BETA_ROOT_KEYS = Object.freeze([
|
|
36
|
+
...DESIGN_CONTRACT_ROOT_KEYS,
|
|
37
|
+
"lane",
|
|
38
|
+
"tokens",
|
|
39
|
+
"component_behaviors",
|
|
40
|
+
"responsive_transformations",
|
|
41
|
+
"motion",
|
|
42
|
+
"acceptance_criteria",
|
|
43
|
+
]);
|
|
44
|
+
// v1beta2 adds one optional root key. Optional is the whole point: a contract that says nothing
|
|
45
|
+
// about taste is complete, and a v1beta1 document keeps validating byte-for-byte.
|
|
46
|
+
const DESIGN_CONTRACT_BETA2_OPTIONAL_KEYS = Object.freeze(["taste"]);
|
|
47
|
+
|
|
48
|
+
// Direction expressed as numbers a reviewer can argue with, rather than adjectives they cannot.
|
|
49
|
+
const TASTE_DIALS = Object.freeze(["variance", "motion", "density"]);
|
|
50
|
+
|
|
51
|
+
const LANES = new Set(["new-build", "brownfield", "redesign", "reference-fidelity", "design-system"]);
|
|
52
|
+
const TOKEN_CATEGORIES = new Set(["color", "typography", "spacing", "radius", "shadow", "motion", "other"]);
|
|
53
|
+
const MOTION_POLICIES = new Set(["none", "functional", "expressive"]);
|
|
54
|
+
const VERIFICATION_METHODS = new Set([
|
|
55
|
+
"tests", "browser", "keyboard", "accessibility-tree", "screen-reader", "performance",
|
|
56
|
+
"localization", "manual",
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
export {
|
|
60
|
+
canonicalDesignContract,
|
|
61
|
+
designContractInventoryEntries,
|
|
62
|
+
readDesignContract,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function collectIdentityIssues(contract, issues) {
|
|
66
|
+
if (![DESIGN_SCHEMA, DESIGN_SCHEMA_BETA, DESIGN_SCHEMA_BETA2].includes(contract.schema_id)) {
|
|
67
|
+
issues.push({
|
|
68
|
+
code: "DESIGN_CONTRACT_SCHEMA_ID",
|
|
69
|
+
path: "schema_id",
|
|
70
|
+
message: `schema_id must equal one of ${[DESIGN_SCHEMA, DESIGN_SCHEMA_BETA, DESIGN_SCHEMA_BETA2].join(", ")}`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (!isPrefixedId(contract.contract_id, ID_PREFIXES.contract)) {
|
|
74
|
+
issues.push({
|
|
75
|
+
code: "DESIGN_CONTRACT_ID_PREFIX",
|
|
76
|
+
path: "contract_id",
|
|
77
|
+
message: `contract_id must be a typed identifier starting with ${ID_PREFIXES.contract}`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (!isLowercaseSha256(contract.source_hash)) {
|
|
81
|
+
issues.push({
|
|
82
|
+
code: "DESIGN_CONTRACT_HASH_GRAMMAR",
|
|
83
|
+
path: "source_hash",
|
|
84
|
+
message: "source_hash must be 64 lowercase hexadecimal digits",
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function addIssue(issues, code, path, message) {
|
|
90
|
+
issues.push({ code, path, message });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function exactObject(value, allowed, path, issues) {
|
|
94
|
+
if (!isObject(value)) {
|
|
95
|
+
addIssue(issues, "DESIGN_CONTRACT_BETA_SHAPE", path, `${path} must be an object`);
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const missing = allowed.filter((key) => !Object.hasOwn(value, key));
|
|
99
|
+
const unknown = Object.keys(value).filter((key) => !allowed.includes(key));
|
|
100
|
+
if (missing.length > 0) addIssue(issues, "DESIGN_CONTRACT_BETA_SHAPE", path, `${path} is missing ${missing.join(", ")}`);
|
|
101
|
+
if (unknown.length > 0) addIssue(issues, "DESIGN_CONTRACT_BETA_SHAPE", path, `${path} declares unknown keys ${unknown.join(", ")}`);
|
|
102
|
+
return missing.length === 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function records(value, path, issues, { minimum = 0, maximum = Number.POSITIVE_INFINITY } = {}) {
|
|
106
|
+
if (!Array.isArray(value)) {
|
|
107
|
+
addIssue(issues, "DESIGN_CONTRACT_BETA_SHAPE", path, `${path} must be an array`);
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
if (value.length < minimum) addIssue(issues, "DESIGN_CONTRACT_BETA_SHAPE", path, `${path} must list at least ${minimum} entries`);
|
|
111
|
+
if (value.length > maximum) addIssue(issues, "DESIGN_CONTRACT_BETA_SHAPE", path, `${path} must list at most ${maximum} entries`);
|
|
112
|
+
return value.filter((item, index) => {
|
|
113
|
+
if (isObject(item)) return true;
|
|
114
|
+
addIssue(issues, "DESIGN_CONTRACT_BETA_SHAPE", `${path}[${index}]`, `${path}[${index}] must be an object`);
|
|
115
|
+
return false;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function requiredText(value, path, issues) {
|
|
120
|
+
if (typeof value !== "string" || Array.from(value).length > 512
|
|
121
|
+
|| !/^\S(?:[^\n\r\t]*\S)?$/u.test(value)) {
|
|
122
|
+
addIssue(issues, "DESIGN_CONTRACT_BETA_TEXT", path, `${path} must be bounded one-line text`);
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function declaredIds(contract, category) {
|
|
129
|
+
const values = contract.inventory?.[category];
|
|
130
|
+
if (!Array.isArray(values)) return new Set();
|
|
131
|
+
return new Set(values.filter(isObject).map((item) => item.id).filter((id) => typeof id === "string"));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function references(value, path, prefix, declared, issues) {
|
|
135
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
136
|
+
addIssue(issues, "DESIGN_CONTRACT_BETA_REFERENCE", path, `${path} must list at least one ${prefix}`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (value.length > 64) {
|
|
140
|
+
addIssue(issues, "DESIGN_CONTRACT_BETA_REFERENCE", path, `${path} must list at most 64 ${prefix} references`);
|
|
141
|
+
}
|
|
142
|
+
if (new Set(value).size !== value.length) addIssue(issues, "DESIGN_CONTRACT_BETA_REFERENCE", path, `${path} must be unique`);
|
|
143
|
+
for (const id of value) {
|
|
144
|
+
if (!isPrefixedId(id, `${prefix}:`)) addIssue(issues, "DESIGN_CONTRACT_BETA_REFERENCE", path, `${String(id)} must use the ${prefix}: prefix`);
|
|
145
|
+
else if (!declared.has(id)) addIssue(issues, "DESIGN_CONTRACT_BETA_REFERENCE", path, `${id} is not a declared ${prefix}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function collectBetaIssues(contract, issues) {
|
|
150
|
+
const ids = [];
|
|
151
|
+
if (!LANES.has(contract.lane)) addIssue(issues, "DESIGN_CONTRACT_BETA_LANE", "lane", "lane must name a supported operating lane");
|
|
152
|
+
const routes = declaredIds(contract, "routes");
|
|
153
|
+
const components = declaredIds(contract, "components");
|
|
154
|
+
const interactions = declaredIds(contract, "interactions");
|
|
155
|
+
const states = declaredIds(contract, "states");
|
|
156
|
+
const viewports = declaredIds(contract, "viewports");
|
|
157
|
+
const inventoryIds = new Set([
|
|
158
|
+
...routes, ...declaredIds(contract, "regions"), ...components, ...interactions, ...states,
|
|
159
|
+
...viewports, ...declaredIds(contract, "references"),
|
|
160
|
+
]);
|
|
161
|
+
if (!Array.isArray(contract.inventory?.viewports) || contract.inventory.viewports.length < 2) {
|
|
162
|
+
addIssue(issues, "DESIGN_CONTRACT_BETA_VIEWPORTS", "inventory.viewports", "beta contracts must declare at least two viewport bounds");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
for (const [index, token] of records(contract.tokens, "tokens", issues, { minimum: 1, maximum: 256 }).entries()) {
|
|
166
|
+
const path = `tokens[${index}]`;
|
|
167
|
+
exactObject(token, ["id", "category", "value", "usage"], path, issues);
|
|
168
|
+
if (!isPrefixedId(token.id, "token:")) addIssue(issues, "DESIGN_CONTRACT_BETA_TOKEN", `${path}.id`, "token id must use the token: prefix");
|
|
169
|
+
else ids.push(token.id);
|
|
170
|
+
if (!TOKEN_CATEGORIES.has(token.category)) addIssue(issues, "DESIGN_CONTRACT_BETA_TOKEN", `${path}.category`, "token category is unsupported");
|
|
171
|
+
requiredText(token.value, `${path}.value`, issues);
|
|
172
|
+
requiredText(token.usage, `${path}.usage`, issues);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
for (const [index, behavior] of records(contract.component_behaviors, "component_behaviors", issues, { minimum: 1, maximum: 512 }).entries()) {
|
|
176
|
+
const path = `component_behaviors[${index}]`;
|
|
177
|
+
exactObject(behavior, ["component_id", "state_ids", "interaction_ids", "keyboard_behavior"], path, issues);
|
|
178
|
+
if (!isPrefixedId(behavior.component_id, "component:")) addIssue(issues, "DESIGN_CONTRACT_BETA_COMPONENT", `${path}.component_id`, "component_id must use the component: prefix");
|
|
179
|
+
else if (!components.has(behavior.component_id)) addIssue(issues, "DESIGN_CONTRACT_BETA_COMPONENT", `${path}.component_id`, `${behavior.component_id} is not a declared component`);
|
|
180
|
+
references(behavior.state_ids, `${path}.state_ids`, "state", states, issues);
|
|
181
|
+
references(behavior.interaction_ids, `${path}.interaction_ids`, "interaction", interactions, issues);
|
|
182
|
+
requiredText(behavior.keyboard_behavior, `${path}.keyboard_behavior`, issues);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
for (const [index, transformation] of records(contract.responsive_transformations, "responsive_transformations", issues, { minimum: 1, maximum: 256 }).entries()) {
|
|
186
|
+
const path = `responsive_transformations[${index}]`;
|
|
187
|
+
exactObject(transformation, ["route_id", "viewport_id", "behavior"], path, issues);
|
|
188
|
+
if (!isPrefixedId(transformation.route_id, "route:") || !routes.has(transformation.route_id)) addIssue(issues, "DESIGN_CONTRACT_BETA_RESPONSIVE", `${path}.route_id`, `${String(transformation.route_id)} is not a declared route`);
|
|
189
|
+
if (!isPrefixedId(transformation.viewport_id, "viewport:") || !viewports.has(transformation.viewport_id)) addIssue(issues, "DESIGN_CONTRACT_BETA_RESPONSIVE", `${path}.viewport_id`, `${String(transformation.viewport_id)} is not a declared viewport`);
|
|
190
|
+
requiredText(transformation.behavior, `${path}.behavior`, issues);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (exactObject(contract.motion, ["policy", "reduced_motion_behavior", "transitions"], "motion", issues)) {
|
|
194
|
+
if (!MOTION_POLICIES.has(contract.motion.policy)) addIssue(issues, "DESIGN_CONTRACT_BETA_MOTION", "motion.policy", "motion.policy is unsupported");
|
|
195
|
+
requiredText(contract.motion.reduced_motion_behavior, "motion.reduced_motion_behavior", issues);
|
|
196
|
+
const transitions = records(contract.motion.transitions, "motion.transitions", issues, { maximum: 256 });
|
|
197
|
+
if (contract.motion.policy !== "none" && transitions.length === 0) addIssue(issues, "DESIGN_CONTRACT_BETA_MOTION", "motion.transitions", "active motion policy requires a transition");
|
|
198
|
+
if (contract.motion.policy === "none" && transitions.length > 0) addIssue(issues, "DESIGN_CONTRACT_BETA_MOTION", "motion.transitions", "motion policy none forbids transitions");
|
|
199
|
+
for (const [index, transition] of transitions.entries()) {
|
|
200
|
+
const path = `motion.transitions[${index}]`;
|
|
201
|
+
exactObject(transition, ["id", "interaction_id", "duration_ms", "easing"], path, issues);
|
|
202
|
+
if (!isPrefixedId(transition.id, "transition:")) addIssue(issues, "DESIGN_CONTRACT_BETA_MOTION", `${path}.id`, "transition id must use the transition: prefix");
|
|
203
|
+
else ids.push(transition.id);
|
|
204
|
+
if (!isPrefixedId(transition.interaction_id, "interaction:") || !interactions.has(transition.interaction_id)) addIssue(issues, "DESIGN_CONTRACT_BETA_MOTION", `${path}.interaction_id`, `${String(transition.interaction_id)} is not a declared interaction`);
|
|
205
|
+
if (!Number.isInteger(transition.duration_ms) || transition.duration_ms < 0 || transition.duration_ms > 10_000) addIssue(issues, "DESIGN_CONTRACT_BETA_MOTION", `${path}.duration_ms`, "duration_ms must be an integer from 0 through 10000");
|
|
206
|
+
requiredText(transition.easing, `${path}.easing`, issues);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
for (const [index, criterion] of records(contract.acceptance_criteria, "acceptance_criteria", issues, { minimum: 1, maximum: 256 }).entries()) {
|
|
211
|
+
const path = `acceptance_criteria[${index}]`;
|
|
212
|
+
exactObject(criterion, ["id", "observable", "verification", "required", "inventory_ids"], path, issues);
|
|
213
|
+
if (!isPrefixedId(criterion.id, "criterion:")) addIssue(issues, "DESIGN_CONTRACT_BETA_CRITERION", `${path}.id`, "criterion id must use the criterion: prefix");
|
|
214
|
+
else ids.push(criterion.id);
|
|
215
|
+
requiredText(criterion.observable, `${path}.observable`, issues);
|
|
216
|
+
if (!VERIFICATION_METHODS.has(criterion.verification)) addIssue(issues, "DESIGN_CONTRACT_BETA_CRITERION", `${path}.verification`, "criterion verification is unsupported");
|
|
217
|
+
if (criterion.required !== true) addIssue(issues, "DESIGN_CONTRACT_BETA_CRITERION", `${path}.required`, "criterion required must be true");
|
|
218
|
+
if (!Array.isArray(criterion.inventory_ids) || criterion.inventory_ids.length === 0
|
|
219
|
+
|| criterion.inventory_ids.length > 64
|
|
220
|
+
|| criterion.inventory_ids.length !== new Set(criterion.inventory_ids).size) {
|
|
221
|
+
addIssue(issues, "DESIGN_CONTRACT_BETA_CRITERION", `${path}.inventory_ids`, "criterion must name 1 through 64 unique covered inventory ids");
|
|
222
|
+
} else for (const id of criterion.inventory_ids) if (!inventoryIds.has(id)) addIssue(issues, "DESIGN_CONTRACT_BETA_CRITERION", `${path}.inventory_ids`, `${String(id)} is not declared in contract inventory`);
|
|
223
|
+
}
|
|
224
|
+
return ids;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Taste is reported one defect at a time. A reviewer fixing a dial wants the dial named, not a
|
|
228
|
+
// list of every consequence of the same mistake.
|
|
229
|
+
function collectTasteIssues(contract, issues) {
|
|
230
|
+
if (!Object.hasOwn(contract, "taste")) return;
|
|
231
|
+
const taste = contract.taste;
|
|
232
|
+
if (!isObject(taste)) {
|
|
233
|
+
addIssue(issues, "DESIGN_CONTRACT_TASTE", "taste", "taste must be an object");
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const unknown = Object.keys(taste).filter((key) => !TASTE_DIALS.includes(key));
|
|
237
|
+
if (unknown.length > 0) {
|
|
238
|
+
addIssue(issues, "DESIGN_CONTRACT_TASTE", "taste", `taste declares unknown dials ${unknown.join(", ")}`);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
for (const dial of TASTE_DIALS) {
|
|
242
|
+
const path = `taste.${dial}`;
|
|
243
|
+
if (!Object.hasOwn(taste, dial)) {
|
|
244
|
+
addIssue(issues, "DESIGN_CONTRACT_TASTE", path, `${path} is required whenever taste is declared`);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (!Number.isInteger(taste[dial]) || taste[dial] < 1 || taste[dial] > 10) {
|
|
248
|
+
addIssue(issues, "DESIGN_CONTRACT_TASTE", path, `${path} must be an integer from 1 through 10`);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Identifiers are unique across the whole document, not merely within a group. Two surfaces
|
|
255
|
+
// that share an identifier cannot be told apart in an evidence record or a review receipt.
|
|
256
|
+
function collectIdCollisionIssues(contract, issues) {
|
|
257
|
+
const declared = [
|
|
258
|
+
{ path: "contract_id", id: contract.contract_id },
|
|
259
|
+
...designContractInventoryEntries(contract).map(({ id, kind }) => ({ path: `inventory.${kind}`, id })),
|
|
260
|
+
];
|
|
261
|
+
for (const field of ["omissions", "accepted_exceptions"]) {
|
|
262
|
+
if (!Array.isArray(contract[field])) continue;
|
|
263
|
+
contract[field].forEach((record, index) => {
|
|
264
|
+
if (isObject(record)) declared.push({ path: `${field}[${index}].id`, id: record.id });
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
const seen = new Set();
|
|
268
|
+
for (const { path, id } of declared) {
|
|
269
|
+
if (typeof id !== "string") continue;
|
|
270
|
+
if (seen.has(id)) {
|
|
271
|
+
issues.push({
|
|
272
|
+
code: "DESIGN_CONTRACT_ID_COLLISION",
|
|
273
|
+
path,
|
|
274
|
+
message: `${id} is declared more than once in this contract`,
|
|
275
|
+
});
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
seen.add(id);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function collectDesignContractIssues(contract) {
|
|
283
|
+
const issues = [];
|
|
284
|
+
if (!isObject(contract)) {
|
|
285
|
+
return [{
|
|
286
|
+
code: "DESIGN_CONTRACT_NOT_OBJECT",
|
|
287
|
+
path: "",
|
|
288
|
+
message: "a Design Contract must be a JSON object",
|
|
289
|
+
}];
|
|
290
|
+
}
|
|
291
|
+
const beta2 = contract.schema_id === DESIGN_SCHEMA_BETA2;
|
|
292
|
+
const beta = beta2 || contract.schema_id === DESIGN_SCHEMA_BETA;
|
|
293
|
+
const requiredKeys = beta ? DESIGN_CONTRACT_BETA_ROOT_KEYS : DESIGN_CONTRACT_ROOT_KEYS;
|
|
294
|
+
const allowedKeys = beta2 ? [...requiredKeys, ...DESIGN_CONTRACT_BETA2_OPTIONAL_KEYS] : requiredKeys;
|
|
295
|
+
const missing = requiredKeys.filter((key) => !Object.hasOwn(contract, key));
|
|
296
|
+
const unknown = Object.keys(contract).filter((key) => !allowedKeys.includes(key));
|
|
297
|
+
if (missing.length > 0) {
|
|
298
|
+
issues.push({
|
|
299
|
+
code: "DESIGN_CONTRACT_ROOT_KEYS",
|
|
300
|
+
path: "",
|
|
301
|
+
message: `the contract is missing ${missing.join(", ")}`,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (unknown.length > 0) {
|
|
305
|
+
issues.push({
|
|
306
|
+
code: "DESIGN_CONTRACT_ROOT_KEYS",
|
|
307
|
+
path: "",
|
|
308
|
+
message: `the contract declares unknown root keys ${unknown.join(", ")}`,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
// Root keys must all be present before group rules can say anything meaningful.
|
|
312
|
+
if (missing.length > 0) return issues;
|
|
313
|
+
collectIdentityIssues(contract, issues);
|
|
314
|
+
collectSurfaceIssues(contract, issues);
|
|
315
|
+
collectInventoryIssues(contract, issues);
|
|
316
|
+
if (beta) {
|
|
317
|
+
collectTasteIssues(contract, issues);
|
|
318
|
+
const betaIds = collectBetaIssues(contract, issues);
|
|
319
|
+
const existingIds = new Set([
|
|
320
|
+
contract.contract_id,
|
|
321
|
+
...designContractInventoryEntries(contract).map(({ id }) => id),
|
|
322
|
+
...(Array.isArray(contract.omissions) ? contract.omissions.map(({ id }) => id) : []),
|
|
323
|
+
...(Array.isArray(contract.accepted_exceptions)
|
|
324
|
+
? contract.accepted_exceptions.map(({ id }) => id)
|
|
325
|
+
: []),
|
|
326
|
+
]);
|
|
327
|
+
for (const id of betaIds) {
|
|
328
|
+
if (existingIds.has(id)) addIssue(issues, "DESIGN_CONTRACT_ID_COLLISION", "", `${id} is declared more than once in this contract`);
|
|
329
|
+
existingIds.add(id);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
collectIdCollisionIssues(contract, issues);
|
|
333
|
+
return issues;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function designContractReport(contract) {
|
|
337
|
+
const issues = collectDesignContractIssues(contract);
|
|
338
|
+
const beta2 = isObject(contract) && contract.schema_id === DESIGN_SCHEMA_BETA2;
|
|
339
|
+
const beta = beta2 || (isObject(contract) && contract.schema_id === DESIGN_SCHEMA_BETA);
|
|
340
|
+
const alpha = isObject(contract) && contract.schema_id === DESIGN_SCHEMA;
|
|
341
|
+
const valid = issues.length === 0;
|
|
342
|
+
return {
|
|
343
|
+
valid,
|
|
344
|
+
schema: beta2 ? DESIGN_SCHEMA_BETA2 : (beta ? DESIGN_SCHEMA_BETA : DESIGN_SCHEMA),
|
|
345
|
+
issues,
|
|
346
|
+
diagnostics: alpha ? ["LEGACY_SCHEMA_V1ALPHA1"] : [],
|
|
347
|
+
evidence_eligible: beta && valid,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Throwing wrapper for callers that treat an invalid contract as an exception, such as the
|
|
352
|
+
// evidence lane binding a contract to a manifest hash.
|
|
353
|
+
export function validateDesignContractValue(contract) {
|
|
354
|
+
const issues = collectDesignContractIssues(contract);
|
|
355
|
+
if (issues.length === 0) return;
|
|
356
|
+
const [first] = issues;
|
|
357
|
+
throw new UiuxContractError(first.code, first.message, {
|
|
358
|
+
issue_count: issues.length,
|
|
359
|
+
issues,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// Non-inventory rule groups of litfamily.design-contract/v1alpha1.
|
|
2
|
+
//
|
|
3
|
+
// The JSON Schema document mirrors these rules for editors and reviewers, but a schema
|
|
4
|
+
// document is inert: nothing in the runtime reads it. Every rule that must actually hold is
|
|
5
|
+
// executed here and reported as a structured issue.
|
|
6
|
+
import { isStableId, isUtcTimestamp } from "./design-contract-format.mjs";
|
|
7
|
+
|
|
8
|
+
// A single literal target. Not a pattern and not an enum: the contract binds one conformance
|
|
9
|
+
// target so that two contracts are comparable without interpreting a range.
|
|
10
|
+
export const ACCESSIBILITY_TARGET = "WCAG 2.2 AA";
|
|
11
|
+
|
|
12
|
+
export const TOKEN_STRATEGIES = Object.freeze(["reuse", "extend", "create"]);
|
|
13
|
+
|
|
14
|
+
export const EVIDENCE_CHANNELS = Object.freeze([
|
|
15
|
+
"tests", "browser", "keyboard", "accessibility-tree", "screen-reader", "performance",
|
|
16
|
+
"localization",
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
const ACCESSIBILITY_FLAGS = Object.freeze([
|
|
20
|
+
"keyboard", "screen_reader", "reduced_motion", "forced_colors",
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
const LOCALIZATION_FLAGS = Object.freeze([
|
|
24
|
+
"cjk_line_break_review", "font_fallback_review", "ime_review", "rtl_review",
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
const LOCALE_TAG = /^[a-z]{2,3}(?:-[A-Z][a-z]{3})?(?:-(?:[A-Z]{2}|\d{3}))?$/u;
|
|
28
|
+
|
|
29
|
+
const TEXT_MAX_LENGTH = 512;
|
|
30
|
+
|
|
31
|
+
const PHRASE_MAX_ITEMS = 32;
|
|
32
|
+
|
|
33
|
+
const SCOPE_MAX_ITEMS = 64;
|
|
34
|
+
|
|
35
|
+
export function isObject(value) {
|
|
36
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Trimmed, single-line, bounded. A field that needs a paragraph belongs in a reference
|
|
40
|
+
// document, not in a machine-compared contract value.
|
|
41
|
+
export function isText(value) {
|
|
42
|
+
if (typeof value !== "string") return false;
|
|
43
|
+
const length = Array.from(value).length;
|
|
44
|
+
return length > 0 && length <= TEXT_MAX_LENGTH && value.trim() === value
|
|
45
|
+
&& !/[\n\r\t]/u.test(value);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function isStrictBoolean(value) {
|
|
49
|
+
return typeof value === "boolean";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function isBoundedInteger(value, minimum, maximum) {
|
|
53
|
+
return Number.isInteger(value) && value >= minimum && value <= maximum;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function isBoundedNumber(value, minimum, maximum) {
|
|
57
|
+
return typeof value === "number" && Number.isFinite(value) && value >= minimum && value <= maximum;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isPhraseList(value, { minimum, maximum = PHRASE_MAX_ITEMS }) {
|
|
61
|
+
return Array.isArray(value) && value.length >= minimum && value.length <= maximum
|
|
62
|
+
&& value.length === new Set(value).size && value.every(isText);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function keySet(value, required, optional = []) {
|
|
66
|
+
if (!isObject(value)) return { missing: required, unknown: [] };
|
|
67
|
+
const allowed = new Set([...required, ...optional]);
|
|
68
|
+
return {
|
|
69
|
+
missing: required.filter((field) => !Object.hasOwn(value, field)),
|
|
70
|
+
unknown: Object.keys(value).filter((field) => !allowed.has(field)),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Closed objects everywhere. An unknown key is a silent design decision nobody reviewed, so
|
|
75
|
+
// it fails rather than being ignored.
|
|
76
|
+
export function collectKeyIssues(issues, value, path, code, required, optional = []) {
|
|
77
|
+
if (!isObject(value)) {
|
|
78
|
+
issues.push({ code, path, message: `${path} must be an object` });
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const { missing, unknown } = keySet(value, required, optional);
|
|
82
|
+
if (missing.length > 0) {
|
|
83
|
+
issues.push({ code, path, message: `${path} is missing ${missing.join(", ")}` });
|
|
84
|
+
}
|
|
85
|
+
if (unknown.length > 0) {
|
|
86
|
+
issues.push({ code, path, message: `${path} declares unknown keys ${unknown.join(", ")}` });
|
|
87
|
+
}
|
|
88
|
+
return missing.length === 0 && unknown.length === 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function collectIntentIssues(issues, intent) {
|
|
92
|
+
const code = "DESIGN_CONTRACT_INTENT";
|
|
93
|
+
if (!collectKeyIssues(issues, intent, "intent", code, [
|
|
94
|
+
"audiences", "tasks", "qualities", "constraints", "non_goals",
|
|
95
|
+
])) return;
|
|
96
|
+
for (const field of ["audiences", "tasks", "qualities"]) {
|
|
97
|
+
if (!isPhraseList(intent[field], { minimum: 1 })) {
|
|
98
|
+
issues.push({
|
|
99
|
+
code,
|
|
100
|
+
path: `intent.${field}`,
|
|
101
|
+
message: `intent.${field} needs 1..${PHRASE_MAX_ITEMS} unique non-empty entries`,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
for (const field of ["constraints", "non_goals"]) {
|
|
106
|
+
if (!isPhraseList(intent[field], { minimum: 0 })) {
|
|
107
|
+
issues.push({
|
|
108
|
+
code,
|
|
109
|
+
path: `intent.${field}`,
|
|
110
|
+
message: `intent.${field} must be an array of at most ${PHRASE_MAX_ITEMS} unique non-empty entries`,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function collectDirectionIssues(issues, direction) {
|
|
117
|
+
const code = "DESIGN_CONTRACT_DIRECTION";
|
|
118
|
+
if (!collectKeyIssues(issues, direction, "direction", code, [
|
|
119
|
+
"name", "principles", "token_strategy", "voice",
|
|
120
|
+
])) return;
|
|
121
|
+
for (const field of ["name", "voice"]) {
|
|
122
|
+
if (!isText(direction[field])) {
|
|
123
|
+
issues.push({ code, path: `direction.${field}`, message: `direction.${field} must be one bounded line` });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Three principles is the minimum that expresses a direction; more than seven is a wish
|
|
127
|
+
// list nobody can apply as a tie-breaker.
|
|
128
|
+
if (!isPhraseList(direction.principles, { minimum: 3, maximum: 7 })) {
|
|
129
|
+
issues.push({
|
|
130
|
+
code,
|
|
131
|
+
path: "direction.principles",
|
|
132
|
+
message: "direction.principles needs 3..7 unique non-empty entries",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
if (!TOKEN_STRATEGIES.includes(direction.token_strategy)) {
|
|
136
|
+
issues.push({
|
|
137
|
+
code,
|
|
138
|
+
path: "direction.token_strategy",
|
|
139
|
+
message: `direction.token_strategy must be one of ${TOKEN_STRATEGIES.join(", ")}`,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function collectAccessibilityIssues(issues, accessibility) {
|
|
145
|
+
const code = "DESIGN_CONTRACT_ACCESSIBILITY";
|
|
146
|
+
if (!collectKeyIssues(issues, accessibility, "accessibility", code, [
|
|
147
|
+
"target", ...ACCESSIBILITY_FLAGS, "zoom_percent",
|
|
148
|
+
])) return;
|
|
149
|
+
if (accessibility.target !== ACCESSIBILITY_TARGET) {
|
|
150
|
+
issues.push({
|
|
151
|
+
code,
|
|
152
|
+
path: "accessibility.target",
|
|
153
|
+
message: `accessibility.target must equal ${JSON.stringify(ACCESSIBILITY_TARGET)}`,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
for (const flag of ACCESSIBILITY_FLAGS) {
|
|
157
|
+
if (!isStrictBoolean(accessibility[flag])) {
|
|
158
|
+
issues.push({
|
|
159
|
+
code,
|
|
160
|
+
path: `accessibility.${flag}`,
|
|
161
|
+
message: `accessibility.${flag} must be true or false, not a truthy value`,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (!isBoundedInteger(accessibility.zoom_percent, 200, 400)) {
|
|
166
|
+
issues.push({
|
|
167
|
+
code,
|
|
168
|
+
path: "accessibility.zoom_percent",
|
|
169
|
+
message: "accessibility.zoom_percent must be an integer from 200 through 400",
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function collectLocalizationIssues(issues, localization) {
|
|
175
|
+
const code = "DESIGN_CONTRACT_LOCALIZATION";
|
|
176
|
+
if (!collectKeyIssues(issues, localization, "localization", code, [
|
|
177
|
+
"locales", "text_expansion_percent", ...LOCALIZATION_FLAGS,
|
|
178
|
+
])) return;
|
|
179
|
+
if (!isPhraseList(localization.locales, { minimum: 1 })
|
|
180
|
+
|| !localization.locales.every((locale) => LOCALE_TAG.test(locale))) {
|
|
181
|
+
issues.push({
|
|
182
|
+
code,
|
|
183
|
+
path: "localization.locales",
|
|
184
|
+
message: "localization.locales needs at least one unique language tag such as ko-KR",
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (!isBoundedInteger(localization.text_expansion_percent, 0, 300)) {
|
|
188
|
+
issues.push({
|
|
189
|
+
code,
|
|
190
|
+
path: "localization.text_expansion_percent",
|
|
191
|
+
message: "localization.text_expansion_percent must be an integer from 0 through 300",
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
for (const flag of LOCALIZATION_FLAGS) {
|
|
195
|
+
if (!isStrictBoolean(localization[flag])) {
|
|
196
|
+
issues.push({
|
|
197
|
+
code,
|
|
198
|
+
path: `localization.${flag}`,
|
|
199
|
+
message: `localization.${flag} must be true or false, not a truthy value`,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Every dimension is bounded on both sides. An open-ended budget is not a budget, and a
|
|
206
|
+
// missing upper bound is how a regression gets accepted as "within contract".
|
|
207
|
+
function collectPerformanceIssues(issues, performance) {
|
|
208
|
+
const code = "DESIGN_CONTRACT_PERFORMANCE";
|
|
209
|
+
if (!collectKeyIssues(issues, performance, "performance", code, [
|
|
210
|
+
"lcp_ms", "cls", "inp_ms", "initial_js_kb", "initial_css_kb",
|
|
211
|
+
])) return;
|
|
212
|
+
for (const field of ["lcp_ms", "inp_ms"]) {
|
|
213
|
+
if (!isBoundedInteger(performance[field], 1, 60000)) {
|
|
214
|
+
issues.push({
|
|
215
|
+
code,
|
|
216
|
+
path: `performance.${field}`,
|
|
217
|
+
message: `performance.${field} must be an integer from 1 through 60000`,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (!isBoundedNumber(performance.cls, 0, 1)) {
|
|
222
|
+
issues.push({
|
|
223
|
+
code,
|
|
224
|
+
path: "performance.cls",
|
|
225
|
+
message: "performance.cls must be a finite number from 0 through 1",
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
for (const field of ["initial_js_kb", "initial_css_kb"]) {
|
|
229
|
+
if (!isBoundedInteger(performance[field], 0, 1048576)) {
|
|
230
|
+
issues.push({
|
|
231
|
+
code,
|
|
232
|
+
path: `performance.${field}`,
|
|
233
|
+
message: `performance.${field} must be an integer from 0 through 1048576`,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function collectEvidencePolicyIssues(issues, policy) {
|
|
240
|
+
const code = "DESIGN_CONTRACT_EVIDENCE_POLICY";
|
|
241
|
+
if (!collectKeyIssues(issues, policy, "evidence_policy", code, [
|
|
242
|
+
"independent_review_required", "required_channels", "cleanup_required",
|
|
243
|
+
])) return;
|
|
244
|
+
for (const field of ["independent_review_required", "cleanup_required"]) {
|
|
245
|
+
if (!isStrictBoolean(policy[field])) {
|
|
246
|
+
issues.push({
|
|
247
|
+
code,
|
|
248
|
+
path: `evidence_policy.${field}`,
|
|
249
|
+
message: `evidence_policy.${field} must be true or false, not a truthy value`,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const channels = policy.required_channels;
|
|
254
|
+
if (!Array.isArray(channels) || channels.length < 1
|
|
255
|
+
|| channels.length > EVIDENCE_CHANNELS.length
|
|
256
|
+
|| channels.length !== new Set(channels).size
|
|
257
|
+
|| !channels.every((channel) => EVIDENCE_CHANNELS.includes(channel))) {
|
|
258
|
+
issues.push({
|
|
259
|
+
code,
|
|
260
|
+
path: "evidence_policy.required_channels",
|
|
261
|
+
message: `evidence_policy.required_channels must be a non-empty unique subset of ${EVIDENCE_CHANNELS.join(", ")}`,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Omissions and accepted exceptions are the only places where a contract may leave something
|
|
267
|
+
// undone, so both carry a named owner. `expires_at` is optional: an undated record is a
|
|
268
|
+
// standing decision, and a dated one must be a strict UTC instant.
|
|
269
|
+
function collectScopeRecordIssues(issues, records, field) {
|
|
270
|
+
const code = "DESIGN_CONTRACT_SCOPE_RECORD";
|
|
271
|
+
if (!Array.isArray(records) || records.length > SCOPE_MAX_ITEMS) {
|
|
272
|
+
issues.push({
|
|
273
|
+
code,
|
|
274
|
+
path: field,
|
|
275
|
+
message: `${field} must be an array of at most ${SCOPE_MAX_ITEMS} records`,
|
|
276
|
+
});
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
records.forEach((record, index) => {
|
|
280
|
+
const path = `${field}[${index}]`;
|
|
281
|
+
if (!collectKeyIssues(issues, record, path, code, ["id", "reason", "owner"], ["expires_at"])) return;
|
|
282
|
+
if (!isStableId(record.id)) {
|
|
283
|
+
issues.push({
|
|
284
|
+
code: "DESIGN_CONTRACT_ID_GRAMMAR",
|
|
285
|
+
path: `${path}.id`,
|
|
286
|
+
message: `${path}.id must be a typed lowercase identifier`,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
for (const key of ["reason", "owner"]) {
|
|
290
|
+
if (!isText(record[key])) {
|
|
291
|
+
issues.push({ code, path: `${path}.${key}`, message: `${path}.${key} must be one bounded line` });
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (Object.hasOwn(record, "expires_at") && !isUtcTimestamp(record.expires_at)) {
|
|
295
|
+
issues.push({
|
|
296
|
+
code: "DESIGN_CONTRACT_TIMESTAMP",
|
|
297
|
+
path: `${path}.expires_at`,
|
|
298
|
+
message: `${path}.expires_at must be a strict UTC instant such as 2026-08-01T00:00:00.000Z`,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function collectSurfaceIssues(contract, issues) {
|
|
305
|
+
collectIntentIssues(issues, contract.intent);
|
|
306
|
+
collectDirectionIssues(issues, contract.direction);
|
|
307
|
+
collectAccessibilityIssues(issues, contract.accessibility);
|
|
308
|
+
collectLocalizationIssues(issues, contract.localization);
|
|
309
|
+
collectPerformanceIssues(issues, contract.performance);
|
|
310
|
+
collectEvidencePolicyIssues(issues, contract.evidence_policy);
|
|
311
|
+
collectScopeRecordIssues(issues, contract.omissions, "omissions");
|
|
312
|
+
collectScopeRecordIssues(issues, contract.accepted_exceptions, "accepted_exceptions");
|
|
313
|
+
}
|