@nurix/etna 0.18.20 → 0.18.22
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/README.md +1 -1
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/CLAUDE.md +3 -3
- package/data/harnesses/code/agents/brainstorm.md +1 -1
- package/data/harnesses/code/agents/maintenance.md +8 -7
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/rules/database.md +38 -0
- package/data/harnesses/code/rules/express.md +28 -0
- package/data/harnesses/code/rules/frontend.md +14 -0
- package/data/harnesses/code/rules/general.md +2 -40
- package/data/harnesses/code/rules/nomenclature.md +1 -8
- package/data/harnesses/code/rules/nustack.md +150 -0
- package/data/harnesses/code/skills/brainstorm/README.md +4 -4
- package/data/harnesses/code/skills/brainstorm/SKILL.md +5 -3
- package/data/harnesses/code/skills/brainstorm/exit-artifacts.md +15 -11
- package/data/harnesses/code/skills/brainstorm/templates/source-of-truth.md +5 -5
- package/data/harnesses/code/skills/design/SKILL.md +14 -11
- package/data/harnesses/code/skills/design/data/catalog/rules.csv +4 -4
- package/data/harnesses/code/skills/design/data/combinations/Readme.md +29 -0
- package/data/harnesses/code/skills/design/data/combinations/charts.csv +33 -0
- package/data/harnesses/code/skills/design/data/combinations/fonts-known.json +437 -0
- package/data/harnesses/code/skills/design/data/combinations/icons.csv +157 -0
- package/data/harnesses/code/skills/design/data/combinations/landing.csv +38 -0
- package/data/harnesses/code/skills/design/data/combinations/lucide-names.json +2001 -0
- package/data/harnesses/code/skills/design/data/combinations/manifest.json +283 -0
- package/data/harnesses/code/skills/design/data/combinations/palettes.csv +49 -0
- package/data/harnesses/code/skills/design/data/combinations/product-router.csv +80 -0
- package/data/harnesses/code/skills/design/data/combinations/styles.csv +54 -0
- package/data/harnesses/code/skills/design/data/combinations/typography.csv +61 -0
- package/data/harnesses/code/skills/design/data/design-md/SCHEMA.md +27 -238
- package/data/harnesses/code/skills/design/data/design-md/library/airbnb.md +7 -88
- package/data/harnesses/code/skills/design/data/design-md/library/airtable.md +1 -82
- package/data/harnesses/code/skills/design/data/design-md/library/anthropic-claude.md +4 -85
- package/data/harnesses/code/skills/design/data/design-md/library/apple.md +5 -86
- package/data/harnesses/code/skills/design/data/design-md/library/binance.md +4 -87
- package/data/harnesses/code/skills/design/data/design-md/library/bmw.md +7 -86
- package/data/harnesses/code/skills/design/data/design-md/library/cal.md +4 -84
- package/data/harnesses/code/skills/design/data/design-md/library/clay.md +5 -86
- package/data/harnesses/code/skills/design/data/design-md/library/clickhouse.md +3 -84
- package/data/harnesses/code/skills/design/data/design-md/library/cohere.md +2 -82
- package/data/harnesses/code/skills/design/data/design-md/library/coinbase.md +10 -91
- package/data/harnesses/code/skills/design/data/design-md/library/composio.md +2 -84
- package/data/harnesses/code/skills/design/data/design-md/library/cursor.md +3 -85
- package/data/harnesses/code/skills/design/data/design-md/library/elevenlabs.md +7 -90
- package/data/harnesses/code/skills/design/data/design-md/library/expo.md +0 -80
- package/data/harnesses/code/skills/design/data/design-md/library/figma.md +0 -80
- package/data/harnesses/code/skills/design/data/design-md/library/framer.md +5 -85
- package/data/harnesses/code/skills/design/data/design-md/library/hashicorp.md +2 -82
- package/data/harnesses/code/skills/design/data/design-md/library/ibm.md +5 -86
- package/data/harnesses/code/skills/design/data/design-md/library/intercom.md +9 -91
- package/data/harnesses/code/skills/design/data/design-md/library/kraken.md +9 -89
- package/data/harnesses/code/skills/design/data/design-md/library/linear-app.md +0 -115
- package/data/harnesses/code/skills/design/data/design-md/library/lovable.md +5 -85
- package/data/harnesses/code/skills/design/data/design-md/library/meta.md +7 -88
- package/data/harnesses/code/skills/design/data/design-md/library/minimax.md +3 -84
- package/data/harnesses/code/skills/design/data/design-md/library/mintlify.md +4 -85
- package/data/harnesses/code/skills/design/data/design-md/library/miro.md +8 -88
- package/data/harnesses/code/skills/design/data/design-md/library/mistral-ai.md +8 -88
- package/data/harnesses/code/skills/design/data/design-md/library/mongodb.md +4 -85
- package/data/harnesses/code/skills/design/data/design-md/library/notion.md +4 -84
- package/data/harnesses/code/skills/design/data/design-md/library/nvidia.md +7 -88
- package/data/harnesses/code/skills/design/data/design-md/library/ollama.md +5 -86
- package/data/harnesses/code/skills/design/data/design-md/library/opencode-ai.md +6 -87
- package/data/harnesses/code/skills/design/data/design-md/library/posthog.md +9 -91
- package/data/harnesses/code/skills/design/data/design-md/library/raycast.md +5 -88
- package/data/harnesses/code/skills/design/data/design-md/library/replicate.md +9 -89
- package/data/harnesses/code/skills/design/data/design-md/library/resend.md +4 -119
- package/data/harnesses/code/skills/design/data/design-md/library/revolut.md +4 -84
- package/data/harnesses/code/skills/design/data/design-md/library/runwayml.md +0 -82
- package/data/harnesses/code/skills/design/data/design-md/library/sanity.md +3 -86
- package/data/harnesses/code/skills/design/data/design-md/library/sentry.md +13 -94
- package/data/harnesses/code/skills/design/data/design-md/library/shopify.md +2 -82
- package/data/harnesses/code/skills/design/data/design-md/library/slack.md +6 -88
- package/data/harnesses/code/skills/design/data/design-md/library/spacex.md +13 -94
- package/data/harnesses/code/skills/design/data/design-md/library/spotify.md +4 -86
- package/data/harnesses/code/skills/design/data/design-md/library/stripe.md +7 -88
- package/data/harnesses/code/skills/design/data/design-md/library/supabase.md +6 -88
- package/data/harnesses/code/skills/design/data/design-md/library/superhuman.md +5 -86
- package/data/harnesses/code/skills/design/data/design-md/library/tesla.md +6 -86
- package/data/harnesses/code/skills/design/data/design-md/library/theverge.md +12 -93
- package/data/harnesses/code/skills/design/data/design-md/library/together-ai.md +11 -91
- package/data/harnesses/code/skills/design/data/design-md/library/uber.md +8 -88
- package/data/harnesses/code/skills/design/data/design-md/library/vercel.md +2 -82
- package/data/harnesses/code/skills/design/data/design-md/library/voltagent.md +11 -126
- package/data/harnesses/code/skills/design/data/design-md/library/warp.md +8 -88
- package/data/harnesses/code/skills/design/data/design-md/library/webflow.md +3 -83
- package/data/harnesses/code/skills/design/data/design-md/library/wired.md +10 -91
- package/data/harnesses/code/skills/design/data/design-md/library/wise.md +7 -90
- package/data/harnesses/code/skills/design/data/design-md/library/x-ai.md +6 -88
- package/data/harnesses/code/skills/design/data/design-md/library/zapier.md +9 -89
- package/data/harnesses/code/skills/design/data/design-md/nurix.md +4 -87
- package/data/harnesses/code/skills/design/data/design-tokens/SCHEMA.md +580 -0
- package/data/harnesses/code/skills/design/data/design-tokens/airbnb/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/airbnb/design.tokens.json +964 -0
- package/data/harnesses/code/skills/design/data/design-tokens/airtable/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/airtable/design.tokens.json +960 -0
- package/data/harnesses/code/skills/design/data/design-tokens/anthropic-claude/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/anthropic-claude/design.tokens.json +965 -0
- package/data/harnesses/code/skills/design/data/design-tokens/apple/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/apple/design.tokens.json +954 -0
- package/data/harnesses/code/skills/design/data/design-tokens/binance/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/binance/design.tokens.json +949 -0
- package/data/harnesses/code/skills/design/data/design-tokens/bmw/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/bmw/design.tokens.json +962 -0
- package/data/harnesses/code/skills/design/data/design-tokens/cal/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/cal/design.tokens.json +958 -0
- package/data/harnesses/code/skills/design/data/design-tokens/clay/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/clay/design.tokens.json +953 -0
- package/data/harnesses/code/skills/design/data/design-tokens/clickhouse/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/clickhouse/design.tokens.json +960 -0
- package/data/harnesses/code/skills/design/data/design-tokens/cohere/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/cohere/design.tokens.json +973 -0
- package/data/harnesses/code/skills/design/data/design-tokens/coinbase/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/coinbase/design.tokens.json +954 -0
- package/data/harnesses/code/skills/design/data/design-tokens/composio/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/composio/design.tokens.json +963 -0
- package/data/harnesses/code/skills/design/data/design-tokens/cursor/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/cursor/design.tokens.json +968 -0
- package/data/harnesses/code/skills/design/data/design-tokens/elevenlabs/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/elevenlabs/design.tokens.json +981 -0
- package/data/harnesses/code/skills/design/data/design-tokens/expo/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/expo/design.tokens.json +962 -0
- package/data/harnesses/code/skills/design/data/design-tokens/figma/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/figma/design.tokens.json +964 -0
- package/data/harnesses/code/skills/design/data/design-tokens/framer/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/framer/design.tokens.json +959 -0
- package/data/harnesses/code/skills/design/data/design-tokens/goldens.json +251 -0
- package/data/harnesses/code/skills/design/data/design-tokens/hashicorp/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/hashicorp/design.tokens.json +965 -0
- package/data/harnesses/code/skills/design/data/design-tokens/ibm/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/ibm/design.tokens.json +978 -0
- package/data/harnesses/code/skills/design/data/design-tokens/intercom/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/intercom/design.tokens.json +982 -0
- package/data/harnesses/code/skills/design/data/design-tokens/kraken/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/kraken/design.tokens.json +958 -0
- package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.resolver.json +23 -0
- package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.tokens.dark.json +589 -0
- package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.tokens.json +964 -0
- package/data/harnesses/code/skills/design/data/design-tokens/lovable/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/lovable/design.tokens.json +955 -0
- package/data/harnesses/code/skills/design/data/design-tokens/meta/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/meta/design.tokens.json +977 -0
- package/data/harnesses/code/skills/design/data/design-tokens/minimax/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/minimax/design.tokens.json +950 -0
- package/data/harnesses/code/skills/design/data/design-tokens/mintlify/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/mintlify/design.tokens.json +976 -0
- package/data/harnesses/code/skills/design/data/design-tokens/miro/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/miro/design.tokens.json +979 -0
- package/data/harnesses/code/skills/design/data/design-tokens/mistral-ai/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/mistral-ai/design.tokens.json +955 -0
- package/data/harnesses/code/skills/design/data/design-tokens/mongodb/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/mongodb/design.tokens.json +958 -0
- package/data/harnesses/code/skills/design/data/design-tokens/notion/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/notion/design.tokens.json +953 -0
- package/data/harnesses/code/skills/design/data/design-tokens/nurix/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/nurix/design.tokens.json +904 -0
- package/data/harnesses/code/skills/design/data/design-tokens/nvidia/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/nvidia/design.tokens.json +957 -0
- package/data/harnesses/code/skills/design/data/design-tokens/ollama/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/ollama/design.tokens.json +945 -0
- package/data/harnesses/code/skills/design/data/design-tokens/opencode-ai/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/opencode-ai/design.tokens.json +960 -0
- package/data/harnesses/code/skills/design/data/design-tokens/posthog/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/posthog/design.tokens.json +953 -0
- package/data/harnesses/code/skills/design/data/design-tokens/raycast/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/raycast/design.tokens.json +955 -0
- package/data/harnesses/code/skills/design/data/design-tokens/replicate/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/replicate/design.tokens.json +958 -0
- package/data/harnesses/code/skills/design/data/design-tokens/resend/design.resolver.json +23 -0
- package/data/harnesses/code/skills/design/data/design-tokens/resend/design.tokens.dark.json +613 -0
- package/data/harnesses/code/skills/design/data/design-tokens/resend/design.tokens.json +968 -0
- package/data/harnesses/code/skills/design/data/design-tokens/revolut/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/revolut/design.tokens.json +956 -0
- package/data/harnesses/code/skills/design/data/design-tokens/runwayml/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/runwayml/design.tokens.json +960 -0
- package/data/harnesses/code/skills/design/data/design-tokens/sanity/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/sanity/design.tokens.json +958 -0
- package/data/harnesses/code/skills/design/data/design-tokens/sentry/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/sentry/design.tokens.json +980 -0
- package/data/harnesses/code/skills/design/data/design-tokens/shopify/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/shopify/design.tokens.json +964 -0
- package/data/harnesses/code/skills/design/data/design-tokens/slack/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/slack/design.tokens.json +957 -0
- package/data/harnesses/code/skills/design/data/design-tokens/spacex/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/spacex/design.tokens.json +979 -0
- package/data/harnesses/code/skills/design/data/design-tokens/spotify/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/spotify/design.tokens.json +974 -0
- package/data/harnesses/code/skills/design/data/design-tokens/stripe/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/stripe/design.tokens.json +973 -0
- package/data/harnesses/code/skills/design/data/design-tokens/supabase/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/supabase/design.tokens.json +979 -0
- package/data/harnesses/code/skills/design/data/design-tokens/superhuman/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/superhuman/design.tokens.json +966 -0
- package/data/harnesses/code/skills/design/data/design-tokens/tesla/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/tesla/design.tokens.json +961 -0
- package/data/harnesses/code/skills/design/data/design-tokens/theverge/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/theverge/design.tokens.json +977 -0
- package/data/harnesses/code/skills/design/data/design-tokens/together-ai/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/together-ai/design.tokens.json +991 -0
- package/data/harnesses/code/skills/design/data/design-tokens/uber/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/uber/design.tokens.json +966 -0
- package/data/harnesses/code/skills/design/data/design-tokens/vercel/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/vercel/design.tokens.json +988 -0
- package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.resolver.json +23 -0
- package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.tokens.dark.json +602 -0
- package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.tokens.json +974 -0
- package/data/harnesses/code/skills/design/data/design-tokens/warp/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/warp/design.tokens.json +979 -0
- package/data/harnesses/code/skills/design/data/design-tokens/webflow/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/webflow/design.tokens.json +960 -0
- package/data/harnesses/code/skills/design/data/design-tokens/wired/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/wired/design.tokens.json +968 -0
- package/data/harnesses/code/skills/design/data/design-tokens/wise/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/wise/design.tokens.json +965 -0
- package/data/harnesses/code/skills/design/data/design-tokens/x-ai/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/x-ai/design.tokens.json +988 -0
- package/data/harnesses/code/skills/design/data/design-tokens/zapier/design.resolver.json +20 -0
- package/data/harnesses/code/skills/design/data/design-tokens/zapier/design.tokens.json +978 -0
- package/data/harnesses/code/skills/design/data/operator-pack/surface-catalog.md +1 -1
- package/data/harnesses/code/skills/design/references/create.md +14 -9
- package/data/harnesses/code/skills/design/references/diagnosis.md +1 -1
- package/data/harnesses/code/skills/design/references/hard-rules.md +4 -4
- package/data/harnesses/code/skills/design/references/improve.md +11 -9
- package/data/harnesses/code/skills/design/references/introspect.md +1 -1
- package/data/harnesses/code/skills/design/references/personality.md +337 -413
- package/data/harnesses/code/skills/design/scripts/convert-design-md.mjs +1032 -0
- package/data/harnesses/code/skills/design/scripts/derive-personality.mjs +596 -296
- package/data/harnesses/code/skills/design/scripts/{detector.mjs → lib/checks.mjs} +145 -140
- package/data/harnesses/code/skills/design/scripts/lib/golden.mjs +26 -0
- package/data/harnesses/code/skills/design/scripts/phase-gate.mjs +105 -37
- package/data/harnesses/code/skills/design/scripts/preship-sweep.mjs +60 -73
- package/data/harnesses/code/skills/design/scripts/render-rung.mjs +1 -1
- package/data/harnesses/code/skills/design/scripts/scaffold.mjs +1 -1
- package/data/harnesses/code/skills/design/scripts/self-integrity.mjs +205 -37
- package/data/harnesses/code/skills/design/scripts/snapshot-goldens.mjs +68 -0
- package/data/harnesses/code/skills/design/scripts/validate-combinations.mjs +314 -0
- package/data/harnesses/code/skills/documentation/SKILL.md +61 -17
- package/data/harnesses/code/skills/documentation/references/examples.md +41 -0
- package/data/harnesses/code/skills/documentation/references/style-guide.md +14 -9
- package/data/harnesses/code/skills/documentation/references/templates.md +306 -52
- package/data/harnesses/etna/CLAUDE.md +3 -3
- package/data/harnesses/etna/agents/brainstorm.md +1 -1
- package/data/harnesses/etna/agents/maintenance.md +8 -7
- package/data/harnesses/etna/harness.json +2 -2
- package/data/harnesses/etna/rules/general.md +2 -40
- package/data/harnesses/etna/rules/nomenclature.md +1 -8
- package/data/harnesses/etna/skills/brainstorm/README.md +4 -4
- package/data/harnesses/etna/skills/brainstorm/SKILL.md +5 -3
- package/data/harnesses/etna/skills/brainstorm/exit-artifacts.md +15 -11
- package/data/harnesses/etna/skills/brainstorm/templates/source-of-truth.md +5 -5
- package/data/harnesses/etna/skills/documentation/SKILL.md +61 -17
- package/data/harnesses/etna/skills/documentation/references/examples.md +41 -0
- package/data/harnesses/etna/skills/documentation/references/style-guide.md +14 -9
- package/data/harnesses/etna/skills/documentation/references/templates.md +306 -52
- package/data/harnesses/mobile/CLAUDE.md +3 -3
- package/data/harnesses/mobile/agents/brainstorm.md +1 -1
- package/data/harnesses/mobile/agents/maintenance.md +8 -7
- package/data/harnesses/mobile/harness.json +2 -2
- package/data/harnesses/mobile/rules/database.md +38 -0
- package/data/harnesses/mobile/rules/general.md +2 -40
- package/data/harnesses/mobile/rules/nomenclature.md +1 -8
- package/data/harnesses/mobile/rules/nustack.md +150 -0
- package/data/harnesses/mobile/skills/brainstorm/README.md +4 -4
- package/data/harnesses/mobile/skills/brainstorm/SKILL.md +5 -3
- package/data/harnesses/mobile/skills/brainstorm/exit-artifacts.md +15 -11
- package/data/harnesses/mobile/skills/brainstorm/templates/source-of-truth.md +5 -5
- package/data/harnesses/mobile/skills/documentation/SKILL.md +61 -17
- package/data/harnesses/mobile/skills/documentation/references/examples.md +41 -0
- package/data/harnesses/mobile/skills/documentation/references/style-guide.md +14 -9
- package/data/harnesses/mobile/skills/documentation/references/templates.md +306 -52
- package/package.json +1 -1
- package/data/harnesses/code/skills/design/scripts/hook-config.json +0 -11
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// skills/design/scripts/preship-sweep.mjs — verification-ladder rung 1 (D21/D23):
|
|
1
|
+
// skills/design/scripts/preship-sweep.mjs — verification-ladder rung 1 (D21/D23): static sweep — per-file catalog + manifest drift via lib/checks.mjs + cross-file machine checks + mandatory-procedures checklist + scope cross-check. --files <a> <b> … scopes the sweep (improve's end-of-run verify); no args = whole-app. --manifest <design.tokens.json | artifact dir> overrides the drift vocabulary source (default: <project>/design.tokens.json).
|
|
2
2
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
3
3
|
import { join, dirname, resolve, relative } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import {
|
|
5
|
+
import { runChecks } from './lib/checks.mjs';
|
|
6
6
|
|
|
7
7
|
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
8
8
|
const CATALOG = join(SCRIPT_DIR, '..', 'data', 'catalog', 'rules.csv');
|
|
@@ -122,56 +122,27 @@ function countNav(project) {
|
|
|
122
122
|
return (text.match(/\bpath\s*=/g) || []).length;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
//
|
|
126
|
-
function parseDetectorJson(stdout) {
|
|
127
|
-
const s = (stdout || '').trim();
|
|
128
|
-
if (!s) return [];
|
|
129
|
-
try {
|
|
130
|
-
const parsed = JSON.parse(s);
|
|
131
|
-
if (Array.isArray(parsed)) return parsed;
|
|
132
|
-
if (parsed && Array.isArray(parsed.findings)) return parsed.findings;
|
|
133
|
-
if (parsed && Array.isArray(parsed.violations)) return parsed.violations;
|
|
134
|
-
return null;
|
|
135
|
-
} catch { return null; }
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Render one relayed detector finding as a single fix-or-classify line.
|
|
125
|
+
// Render one catalog finding as a single fix-or-classify line.
|
|
139
126
|
function formatFinding(f, project) {
|
|
140
|
-
const anchor = f.
|
|
127
|
+
const anchor = f.rule || '?';
|
|
141
128
|
const file = f.file ? relative(project, f.file) || f.file : '';
|
|
142
129
|
const loc = f.line != null ? `${file}:${f.line}` : file;
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
const ev = evidence ? ` [${evidence}]` : '';
|
|
146
|
-
return ` ${red(anchor)} ${loc}${ev} ${msg}`.trimEnd();
|
|
130
|
+
const ev = f.excerpt ? ` [${f.excerpt}]` : '';
|
|
131
|
+
return ` ${red(anchor)} ${loc}${ev} ${f.fix || ''}`.trimEnd();
|
|
147
132
|
}
|
|
148
133
|
|
|
149
|
-
//
|
|
150
|
-
function
|
|
151
|
-
const detector = join(SCRIPT_DIR, 'detector.mjs');
|
|
152
|
-
if (!existsSync(detector)) {
|
|
153
|
-
section.push(yellow(` SWEEP INCOMPLETE: detector.mjs not found at ${detector} — the per-file catalog was not run. Build it before shipping.`));
|
|
154
|
-
return -1;
|
|
155
|
-
}
|
|
134
|
+
// Run the per-file catalog + manifest drift (lib/checks.mjs) over the file set; returns the violation count.
|
|
135
|
+
function runCatalog(project, files, manifestPath, section) {
|
|
156
136
|
if (!files.length) { section.push(' (no src/**/*.{tsx,ts,css,html} files to scan)'); return 0; }
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (findings) {
|
|
163
|
-
if (!findings.length) { section.push(green(' detector: no per-file violations.')); return 0; }
|
|
164
|
-
section.push(` detector reported ${findings.length} violation(s) — fix each now, or classify why it does not apply in docs/memory/design-decay.md; never ignore silently:`);
|
|
165
|
-
for (const f of findings) section.push(formatFinding(f, project));
|
|
166
|
-
return findings.length;
|
|
167
|
-
}
|
|
168
|
-
if (res.stdout) section.push(res.stdout.replace(/\n$/, '').split('\n').map((l) => ` ${l}`).join('\n'));
|
|
169
|
-
if (res.stderr) section.push(yellow(res.stderr.replace(/\n$/, '').split('\n').map((l) => ` ${l}`).join('\n')));
|
|
170
|
-
return res.status ? 1 : 0;
|
|
137
|
+
const findings = runChecks(files, { manifestPath });
|
|
138
|
+
if (!findings.length) { section.push(green(' catalog checks: no per-file violations.')); return 0; }
|
|
139
|
+
section.push(` catalog checks reported ${findings.length} violation(s) — fix each now, or classify why it does not apply in docs/memory/design-decay.md; never ignore silently:`);
|
|
140
|
+
for (const f of findings) section.push(formatFinding(f, project));
|
|
141
|
+
return findings.length;
|
|
171
142
|
}
|
|
172
143
|
|
|
173
|
-
// Run the cross-file machine checks (dep-02 one-h1 + heading skips) and the advisory nav-count.
|
|
174
|
-
function crossFileChecks(project, files, catalog, section) {
|
|
144
|
+
// Run the cross-file machine checks (dep-02 one-h1 + heading skips) and, on whole-app runs, the route-shell aggregate and advisory nav-count.
|
|
145
|
+
function crossFileChecks(project, files, catalog, section, scoped) {
|
|
175
146
|
const violations = [];
|
|
176
147
|
for (const file of files) {
|
|
177
148
|
if (!/\.(tsx|html)$/.test(file)) continue;
|
|
@@ -183,7 +154,7 @@ function crossFileChecks(project, files, catalog, section) {
|
|
|
183
154
|
if (skips.length) violations.push(` ${red('dep-02')} ${rel} heading level skips (${skips.join(', ')}); heading levels never skip.`);
|
|
184
155
|
}
|
|
185
156
|
const routesDir = join(project, 'src/routes');
|
|
186
|
-
if (existsSync(routesDir)) {
|
|
157
|
+
if (!scoped && existsSync(routesDir)) {
|
|
187
158
|
for (const ent of readdirSync(routesDir, { withFileTypes: true })) {
|
|
188
159
|
if (!ent.isDirectory()) continue;
|
|
189
160
|
const dir = join(routesDir, ent.name);
|
|
@@ -194,12 +165,14 @@ function crossFileChecks(project, files, catalog, section) {
|
|
|
194
165
|
if (violations.length) { section.push(...violations); }
|
|
195
166
|
else section.push(green(' cross-file machine checks (dep-02 one-h1, heading-skip): clean.'));
|
|
196
167
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
168
|
+
if (!scoped) {
|
|
169
|
+
const bounds = navBounds(catalog);
|
|
170
|
+
const nav = countNav(project);
|
|
171
|
+
if (bounds) {
|
|
172
|
+
const within = nav >= bounds.min && nav <= bounds.max;
|
|
173
|
+
const note = ` ${within ? '' : yellow('[advisory] ')}nav items: ${nav} (spc-14 advisory band ${bounds.min}-${bounds.max}${within ? ', within' : ', outside — judged, not gated'}).`;
|
|
174
|
+
section.push(note);
|
|
175
|
+
}
|
|
203
176
|
}
|
|
204
177
|
return violations.length;
|
|
205
178
|
}
|
|
@@ -221,59 +194,73 @@ function procChecklist(catalog) {
|
|
|
221
194
|
return lines;
|
|
222
195
|
}
|
|
223
196
|
|
|
224
|
-
// Assemble and print the whole sweep, then exit non-zero when
|
|
197
|
+
// Assemble and print the whole sweep, then exit non-zero when catalog or cross-file checks found violations.
|
|
225
198
|
function main() {
|
|
226
199
|
const args = process.argv.slice(2);
|
|
227
200
|
let project = process.cwd();
|
|
201
|
+
let scopedFiles = null;
|
|
202
|
+
let manifestArg = null;
|
|
228
203
|
for (let i = 0; i < args.length; i++) {
|
|
229
204
|
if (args[i] === '--project') project = resolve(args[++i] || '.');
|
|
230
205
|
else if (args[i].startsWith('--project=')) project = resolve(args[i].slice('--project='.length));
|
|
206
|
+
else if (args[i] === '--manifest') manifestArg = resolve(args[++i] || '.');
|
|
207
|
+
else if (args[i].startsWith('--manifest=')) manifestArg = resolve(args[i].slice('--manifest='.length));
|
|
208
|
+
else if (args[i] === '--files') {
|
|
209
|
+
scopedFiles = [];
|
|
210
|
+
while (i + 1 < args.length && !args[i + 1].startsWith('--')) scopedFiles.push(resolve(args[++i]));
|
|
211
|
+
}
|
|
231
212
|
}
|
|
232
213
|
|
|
233
214
|
const catalog = loadCatalog();
|
|
234
|
-
const
|
|
215
|
+
const scoped = scopedFiles !== null;
|
|
216
|
+
const files = scoped ? scopedFiles : walk(join(project, 'src'), ['.tsx', '.ts', '.css', '.html']);
|
|
217
|
+
const defaultManifest = join(project, 'design.tokens.json');
|
|
218
|
+
const manifestPath = manifestArg || (existsSync(defaultManifest) ? defaultManifest : null);
|
|
235
219
|
|
|
236
|
-
console.log(bold(`preship-sweep — verification ladder rung 1 (project: ${project})`));
|
|
220
|
+
console.log(bold(`preship-sweep — verification ladder rung 1 (project: ${project}${scoped ? `, scoped to ${files.length} file(s) via --files` : ''})`));
|
|
237
221
|
console.log('');
|
|
238
222
|
|
|
239
|
-
console.log(bold('(a) Per-file catalog —
|
|
223
|
+
console.log(bold('(a) Per-file catalog + manifest drift — lib/checks.mjs'));
|
|
240
224
|
const secA = [];
|
|
241
|
-
const
|
|
225
|
+
const catalogCount = runCatalog(project, files, manifestPath, secA);
|
|
242
226
|
console.log(secA.join('\n'));
|
|
243
227
|
console.log('');
|
|
244
228
|
|
|
245
229
|
console.log(bold('(b) Cross-file machine checks'));
|
|
246
230
|
const secB = [];
|
|
247
|
-
const crossCount = crossFileChecks(project, files, catalog, secB);
|
|
231
|
+
const crossCount = crossFileChecks(project, files, catalog, secB, scoped);
|
|
248
232
|
console.log(secB.join('\n'));
|
|
249
233
|
console.log('');
|
|
250
234
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
235
|
+
if (!scoped) {
|
|
236
|
+
console.log(bold('(c) Mandatory procedures — self-audit checklist (proc-*)'));
|
|
237
|
+
console.log(procChecklist(catalog).join('\n'));
|
|
238
|
+
console.log('');
|
|
254
239
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
240
|
+
console.log(bold('(c) Scope cross-check (proc-14)'));
|
|
241
|
+
const surfaces = countSurfaces(project);
|
|
242
|
+
const deliverables = countDeliverables(project);
|
|
243
|
+
if (surfaces == null) {
|
|
244
|
+
console.log(` ${yellow('no docs/design/structure.md map found — cannot cross-check scope.')}`);
|
|
245
|
+
} else {
|
|
246
|
+
console.log(` structure map declares ${surfaces} surface(s); found ${deliverables} route deliverable(s) under src/routes/.`);
|
|
247
|
+
if (surfaces !== deliverables) console.log(` ${yellow(`[advisory] scope mismatch (${surfaces} vs ${deliverables}) — add or reconcile before responding (proc-14).`)}`);
|
|
248
|
+
}
|
|
249
|
+
console.log('');
|
|
260
250
|
} else {
|
|
261
|
-
console.log(
|
|
262
|
-
|
|
251
|
+
console.log('(scoped run — the mandatory-procedures checklist and scope cross-check run on the whole-app sweep)');
|
|
252
|
+
console.log('');
|
|
263
253
|
}
|
|
264
|
-
console.log('');
|
|
265
254
|
|
|
266
|
-
const
|
|
267
|
-
const failed = detectorFailed || crossCount > 0;
|
|
255
|
+
const failed = catalogCount > 0 || crossCount > 0;
|
|
268
256
|
if (failed) {
|
|
269
257
|
const reasons = [];
|
|
270
|
-
if (
|
|
271
|
-
if (detectorCount > 0) reasons.push(`${detectorCount} per-file violation(s)`);
|
|
258
|
+
if (catalogCount > 0) reasons.push(`${catalogCount} per-file violation(s)`);
|
|
272
259
|
if (crossCount > 0) reasons.push(`${crossCount} cross-file violation(s)`);
|
|
273
260
|
console.log(red(`FAIL preship-sweep — ${reasons.join('; ')}. Resolve, then re-run.`));
|
|
274
261
|
process.exit(1);
|
|
275
262
|
}
|
|
276
|
-
console.log(green('PASS preship-sweep — no static violations; work the checklist above before ship.'));
|
|
263
|
+
console.log(green(scoped ? 'PASS preship-sweep (scoped) — no static violations in the given files.' : 'PASS preship-sweep — no static violations; work the checklist above before ship.'));
|
|
277
264
|
process.exit(0);
|
|
278
265
|
}
|
|
279
266
|
|
|
@@ -440,7 +440,7 @@ function addFinding(findings, viewport, finding) {
|
|
|
440
440
|
else findings.set(key, { ...finding, viewports: new Set([viewport]) });
|
|
441
441
|
}
|
|
442
442
|
|
|
443
|
-
// Print one finding in the
|
|
443
|
+
// Print one finding in the check engine's fix-or-classify format.
|
|
444
444
|
function printFinding(f) {
|
|
445
445
|
const vp = [...f.viewports].sort((a, b) => a - b).join(', ');
|
|
446
446
|
console.log(`[${f.severity}] ${f.anchor} ${f.dimension} — ${f.statement}`);
|
|
@@ -284,7 +284,7 @@ function emit(root, rel, content, written) {
|
|
|
284
284
|
function indexHtml() {
|
|
285
285
|
return `<!doctype html>
|
|
286
286
|
<!-- Generated by scaffold.mjs. Personality state lives in these <html data-*> attributes (code is the cache).
|
|
287
|
-
The values below are placeholders; derive-personality.mjs --emit resolves them from the project's design.
|
|
287
|
+
The values below are placeholders; derive-personality.mjs --emit resolves them from the project's design-tokens artifact (design.tokens.json). -->
|
|
288
288
|
<html
|
|
289
289
|
lang="en"
|
|
290
290
|
data-style="app"
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
// skills/design/scripts/self-integrity.mjs — the design skill's own CI check: anchor parity, catalog invariants, stated-count and fixture-pair validation
|
|
2
|
-
import { readFileSync, existsSync, readdirSync } from "node:fs";
|
|
3
|
-
import { join, dirname,
|
|
1
|
+
// skills/design/scripts/self-integrity.mjs — the design skill's own CI check: anchor parity, catalog invariants, stated-count and fixture-pair validation (pairs exercised through lib/checks.mjs), the token-era net — token file-tree + goldens parity, per-artifact --check validity, and (behind --goldens) golden emission hash match — plus combination-data validation (validate-combinations.mjs); --fixture <rule-id> runs a single pair
|
|
2
|
+
import { readFileSync, existsSync, readdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
3
|
+
import { join, dirname, resolve } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
4
6
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import {
|
|
7
|
+
import { runChecks } from "./lib/checks.mjs";
|
|
8
|
+
import { goldenHash } from "./lib/golden.mjs";
|
|
9
|
+
import { validateCombinations } from "./validate-combinations.mjs";
|
|
6
10
|
|
|
7
11
|
const SCRIPTS_DIR = dirname(fileURLToPath(import.meta.url));
|
|
8
12
|
const SKILL_ROOT = join(SCRIPTS_DIR, "..");
|
|
@@ -10,7 +14,11 @@ const SKILL_ROOT = join(SCRIPTS_DIR, "..");
|
|
|
10
14
|
const CATALOG_CSV = join(SKILL_ROOT, "data/catalog/rules.csv");
|
|
11
15
|
const FIXTURES_DIR = join(SKILL_ROOT, "data/catalog/fixtures");
|
|
12
16
|
const HARD_RULES_MD = join(SKILL_ROOT, "references/hard-rules.md");
|
|
13
|
-
const
|
|
17
|
+
const TOKENS_DIR = join(SKILL_ROOT, "data/design-tokens");
|
|
18
|
+
const COMBINATIONS_DIR = join(SKILL_ROOT, "data/combinations");
|
|
19
|
+
const GOLDENS_JSON = join(TOKENS_DIR, "goldens.json");
|
|
20
|
+
const DERIVE_PERSONALITY = join(SCRIPTS_DIR, "derive-personality.mjs");
|
|
21
|
+
const SPAWN_LIMIT = 8;
|
|
14
22
|
|
|
15
23
|
const CSV_HEADER = ["id", "dimension", "statement", "numbers", "grade", "fire_points", "detector", "scope_note", "provenance", "fixture_pair"];
|
|
16
24
|
const VALID_DIMENSIONS = new Set(["P1", "P2", "P3", "P4", "P5", "P6", "P7", "S1", "S2", "S3", "S4", "B1", "B2", "B3", "B4", "B5"]);
|
|
@@ -227,8 +235,8 @@ function checkFixturePresence(catalog) {
|
|
|
227
235
|
return { name: "fixture-pair presence", pass: errors.length === 0, detail: `${ok}/${total} pairs present`, errors };
|
|
228
236
|
}
|
|
229
237
|
|
|
230
|
-
// Run
|
|
231
|
-
function
|
|
238
|
+
// Run the check engine (lib/checks.mjs) on one fixture file and return the set of anchor ids it reports.
|
|
239
|
+
function runEngine(file) {
|
|
232
240
|
// Scan the leading comment block for a `// detector-args:` directive (the mandatory file-path
|
|
233
241
|
// comment occupies line 1, so the directive cannot be line 0). Relative paths in it (e.g. a
|
|
234
242
|
// --manifest sibling) resolve against FIXTURES_DIR, matching each fixture's documented verify command.
|
|
@@ -239,46 +247,41 @@ function runDetector(file) {
|
|
|
239
247
|
const argMatch = /^\/\/\s*detector-args:\s*(.+)$/.exec(t);
|
|
240
248
|
if (argMatch) { extra = argMatch[1].trim().split(/\s+/); break; }
|
|
241
249
|
}
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
250
|
+
let manifestPath = null;
|
|
251
|
+
const extraFiles = [];
|
|
252
|
+
for (let i = 0; i < extra.length; i++) {
|
|
253
|
+
const a = extra[i];
|
|
254
|
+
if (a === "--manifest") manifestPath = resolve(FIXTURES_DIR, extra[++i] || "");
|
|
255
|
+
else if (a.startsWith("--manifest=")) manifestPath = resolve(FIXTURES_DIR, a.slice("--manifest=".length));
|
|
256
|
+
else if (!a.startsWith("--")) extraFiles.push(resolve(FIXTURES_DIR, a));
|
|
257
|
+
}
|
|
245
258
|
try {
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (typeof v === "object") Object.values(v).forEach(collect);
|
|
252
|
-
};
|
|
253
|
-
collect(parsed);
|
|
254
|
-
} catch { /* fall back to scanning raw output below */ }
|
|
255
|
-
let m;
|
|
256
|
-
ANCHOR_TOKEN.lastIndex = 0;
|
|
257
|
-
const bare = new RegExp("\\b(" + ANCHOR_PREFIXES + ")-(\\d{2})\\b", "g");
|
|
258
|
-
while ((m = bare.exec(out)) !== null) ids.add(`${m[1]}-${m[2]}`);
|
|
259
|
-
return { ids, spawnError: res.error ? String(res.error) : null };
|
|
259
|
+
const findings = runChecks([file, ...extraFiles], { manifestPath });
|
|
260
|
+
return { ids: new Set(findings.map((f) => f.rule)) };
|
|
261
|
+
} catch (e) {
|
|
262
|
+
return { ids: new Set(), error: String(e) };
|
|
263
|
+
}
|
|
260
264
|
}
|
|
261
265
|
|
|
262
266
|
// ── Check 4: contrastive validity — flag fires the rule, pass does not ──────────
|
|
263
|
-
function checkContrastiveValidity(catalog) {
|
|
267
|
+
function checkContrastiveValidity(catalog, onlyId = null) {
|
|
264
268
|
const errors = [];
|
|
265
|
-
if (!existsSync(DETECTOR_MJS)) return { name: "contrastive fixture validity", pass: false, detail: "detector.mjs missing", errors: ["scripts/detector.mjs not found — cannot exercise fixtures"] };
|
|
266
269
|
if (!existsSync(FIXTURES_DIR)) return { name: "contrastive fixture validity", pass: false, detail: "fixtures dir missing", errors: ["data/catalog/fixtures/ does not exist"] };
|
|
267
270
|
const files = readdirSync(FIXTURES_DIR);
|
|
268
|
-
const present = new Set(files);
|
|
269
271
|
const findFile = (id, kind) => files.find((f) => f.startsWith(`${id}.${kind}.`));
|
|
270
272
|
let checked = 0;
|
|
271
273
|
for (const row of catalog.rows) {
|
|
272
274
|
if (row.fixture_pair !== "yes") continue;
|
|
275
|
+
if (onlyId && row.id !== onlyId) continue;
|
|
273
276
|
const flag = findFile(row.id, "flag");
|
|
274
277
|
const pass = findFile(row.id, "pass");
|
|
275
278
|
if (!flag || !pass) continue; // absence is check 3's job
|
|
276
279
|
checked++;
|
|
277
|
-
const flagRes =
|
|
278
|
-
if (flagRes.
|
|
280
|
+
const flagRes = runEngine(join(FIXTURES_DIR, flag));
|
|
281
|
+
if (flagRes.error) { errors.push(`${row.id}: check engine threw on flag (${flagRes.error})`); continue; }
|
|
279
282
|
if (!flagRes.ids.has(row.id)) errors.push(`${row.id}: flag fixture did not report ${row.id} (reported: ${[...flagRes.ids].join(",") || "none"})`);
|
|
280
|
-
const passRes =
|
|
281
|
-
if (passRes.
|
|
283
|
+
const passRes = runEngine(join(FIXTURES_DIR, pass));
|
|
284
|
+
if (passRes.error) { errors.push(`${row.id}: check engine threw on pass (${passRes.error})`); continue; }
|
|
282
285
|
if (passRes.ids.has(row.id)) errors.push(`${row.id}: pass fixture wrongly reported ${row.id}`);
|
|
283
286
|
}
|
|
284
287
|
return { name: "contrastive fixture validity", pass: errors.length === 0, detail: `${checked} pair(s) exercised`, errors };
|
|
@@ -356,14 +359,15 @@ function checkFileTree() {
|
|
|
356
359
|
"references/hard-rules.md", "references/create.md", "references/improve.md", "references/introspect.md",
|
|
357
360
|
"references/diagnosis.md", "references/structure.md", "references/personality.md", "references/interaction.md", "references/lifecycle.md",
|
|
358
361
|
"data/catalog/rules.csv",
|
|
362
|
+
"data/catalog/fixtures/cod-02.manifest.tokens.json",
|
|
359
363
|
"data/design-md/SCHEMA.md", "data/design-md/nurix.md",
|
|
360
364
|
"data/operator-pack/surface-catalog.md", "data/operator-pack/fixtures.md", "data/operator-pack/trust-rubric.md",
|
|
361
365
|
"data/operator-pack/exemplars/list-detail.tsx", "data/operator-pack/exemplars/dashboard.tsx",
|
|
362
366
|
"data/operator-pack/exemplars/entity-detail.tsx", "data/operator-pack/exemplars/create-edit.tsx",
|
|
363
367
|
"data/operator-pack/exemplars/log-stream.tsx", "data/operator-pack/exemplars/command-center.tsx",
|
|
364
|
-
"scripts/
|
|
368
|
+
"scripts/lib/checks.mjs", "scripts/phase-gate.mjs", "scripts/preship-sweep.mjs", "scripts/render-rung.mjs",
|
|
365
369
|
"scripts/scaffold.mjs", "scripts/derive-personality.mjs", "scripts/generate-fixtures.mjs",
|
|
366
|
-
"scripts/self-integrity.mjs",
|
|
370
|
+
"scripts/self-integrity.mjs",
|
|
367
371
|
];
|
|
368
372
|
for (const rel of files) if (!existsSync(join(SKILL_ROOT, rel))) errors.push(`missing ${rel}`);
|
|
369
373
|
|
|
@@ -377,6 +381,138 @@ function checkFileTree() {
|
|
|
377
381
|
return { name: "file-tree presence", pass: errors.length === 0, detail: `${found}/${files.length} files present`, errors };
|
|
378
382
|
}
|
|
379
383
|
|
|
384
|
+
// Every artifact directory under data/design-tokens/ (a directory holding design.tokens.json), sorted by slug.
|
|
385
|
+
function listArtifactSlugs() {
|
|
386
|
+
if (!existsSync(TOKENS_DIR)) return [];
|
|
387
|
+
return readdirSync(TOKENS_DIR, { withFileTypes: true })
|
|
388
|
+
.filter((d) => d.isDirectory() && existsSync(join(TOKENS_DIR, d.name, "design.tokens.json")))
|
|
389
|
+
.map((d) => d.name)
|
|
390
|
+
.sort();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Parse goldens.json into its entry slugs (every key except "_meta"), or null when absent/unparseable.
|
|
394
|
+
function readGoldenSlugs() {
|
|
395
|
+
const text = readSafe(GOLDENS_JSON);
|
|
396
|
+
if (text === null) return null;
|
|
397
|
+
try { return Object.keys(JSON.parse(text)).filter((k) => k !== "_meta").sort(); }
|
|
398
|
+
catch { return null; }
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Run an async worker over items with at most `limit` in flight; results keep item order.
|
|
402
|
+
async function runPool(items, limit, worker) {
|
|
403
|
+
const results = new Array(items.length);
|
|
404
|
+
let next = 0;
|
|
405
|
+
const lane = async () => {
|
|
406
|
+
while (next < items.length) {
|
|
407
|
+
const i = next++;
|
|
408
|
+
results[i] = await worker(items[i], i);
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) || 1 }, lane));
|
|
412
|
+
return results;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Spawn `node <args>` and resolve with { status, stdout, stderr } (never rejects; a spawn error surfaces as status -1).
|
|
416
|
+
function execNode(args) {
|
|
417
|
+
return new Promise((resolveExec) => {
|
|
418
|
+
const child = spawn(process.execPath, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
419
|
+
let stdout = "";
|
|
420
|
+
let stderr = "";
|
|
421
|
+
child.stdout.on("data", (d) => { stdout += d; });
|
|
422
|
+
child.stderr.on("data", (d) => { stderr += d; });
|
|
423
|
+
child.on("error", (e) => resolveExec({ status: -1, stdout, stderr: stderr + String(e) }));
|
|
424
|
+
child.on("close", (status) => resolveExec({ status, stdout, stderr }));
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// First non-empty line of a child process's output, for one-line error reporting.
|
|
429
|
+
function firstLine(text) {
|
|
430
|
+
return (text || "").split("\n").find((l) => l.trim() !== "") || "(no output)";
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ── Check 7: token-era file tree + goldens<->artifact parity ───────────────────
|
|
434
|
+
function checkTokenFileTree() {
|
|
435
|
+
const errors = [];
|
|
436
|
+
const pins = [
|
|
437
|
+
"data/design-tokens/SCHEMA.md",
|
|
438
|
+
"data/design-tokens/goldens.json",
|
|
439
|
+
"scripts/convert-design-md.mjs",
|
|
440
|
+
"scripts/snapshot-goldens.mjs",
|
|
441
|
+
"scripts/lib/golden.mjs",
|
|
442
|
+
];
|
|
443
|
+
for (const rel of pins) if (!existsSync(join(SKILL_ROOT, rel))) errors.push(`missing ${rel}`);
|
|
444
|
+
const pinsFound = pins.length - errors.length;
|
|
445
|
+
|
|
446
|
+
const diskSlugs = listArtifactSlugs();
|
|
447
|
+
const goldenSlugs = readGoldenSlugs();
|
|
448
|
+
if (goldenSlugs === null) {
|
|
449
|
+
errors.push("goldens.json missing or does not parse as JSON");
|
|
450
|
+
} else {
|
|
451
|
+
const goldenSet = new Set(goldenSlugs);
|
|
452
|
+
for (const slug of goldenSlugs) {
|
|
453
|
+
if (!existsSync(join(TOKENS_DIR, slug, "design.tokens.json")))
|
|
454
|
+
errors.push(`goldens.json entry "${slug}" has no data/design-tokens/${slug}/design.tokens.json on disk`);
|
|
455
|
+
else if (!existsSync(join(TOKENS_DIR, slug, "design.resolver.json")))
|
|
456
|
+
errors.push(`goldens.json entry "${slug}": data/design-tokens/${slug}/ is missing design.resolver.json`);
|
|
457
|
+
}
|
|
458
|
+
for (const slug of diskSlugs) {
|
|
459
|
+
if (!goldenSet.has(slug)) errors.push(`artifact dir data/design-tokens/${slug}/ has no goldens.json entry`);
|
|
460
|
+
else if (!existsSync(join(TOKENS_DIR, slug, "design.resolver.json")) && !errors.some((e) => e.includes(`"${slug}"`)))
|
|
461
|
+
errors.push(`artifact dir data/design-tokens/${slug}/ is missing design.resolver.json`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
const goldenCount = goldenSlugs === null ? 0 : goldenSlugs.length;
|
|
465
|
+
return { name: "token file-tree + goldens parity", pass: errors.length === 0, detail: errors.length ? `${errors.length} issue(s)` : `${pinsFound}/${pins.length} pins, goldens<->artifacts ${goldenCount}<->${diskSlugs.length}`, errors };
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// ── Check 8: every design-tokens artifact passes derive-personality --check ────
|
|
469
|
+
async function checkArtifactValidity() {
|
|
470
|
+
const slugs = listArtifactSlugs();
|
|
471
|
+
if (slugs.length === 0) return { name: "artifact validity (--check)", pass: false, detail: "no artifact dirs found", errors: [`no design.tokens.json dirs under ${TOKENS_DIR}`] };
|
|
472
|
+
const runs = await runPool(slugs, SPAWN_LIMIT, (slug) => execNode([DERIVE_PERSONALITY, join(TOKENS_DIR, slug), "--check"]));
|
|
473
|
+
const errors = [];
|
|
474
|
+
runs.forEach((res, i) => {
|
|
475
|
+
if (res.status !== 0) errors.push(`${slugs[i]}: --check exited ${res.status}: ${firstLine(res.stderr || res.stdout)}`);
|
|
476
|
+
});
|
|
477
|
+
return { name: "artifact validity (--check)", pass: errors.length === 0, detail: `${slugs.length - errors.length}/${slugs.length} artifacts pass --check`, errors };
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// ── Check 9: emitted CSS hashes match goldens.json (slow; gated behind --goldens) ──
|
|
481
|
+
async function checkGoldenMatch(enabled) {
|
|
482
|
+
if (!enabled) return { name: "golden emission match", pass: true, skip: true, detail: "skipped (slow) — run with --goldens to verify emitted hashes against goldens.json", errors: [] };
|
|
483
|
+
const goldenSlugs = readGoldenSlugs();
|
|
484
|
+
if (goldenSlugs === null) return { name: "golden emission match", pass: false, detail: "goldens.json missing/unparseable", errors: ["goldens.json missing or does not parse as JSON"] };
|
|
485
|
+
const goldens = JSON.parse(readSafe(GOLDENS_JSON));
|
|
486
|
+
const emitRoot = mkdtempSync(join(tmpdir(), "design-self-integrity-"));
|
|
487
|
+
let matched = 0;
|
|
488
|
+
const errors = [];
|
|
489
|
+
try {
|
|
490
|
+
// Each worker returns its slug's error list; flattening after the pool keeps report order deterministic.
|
|
491
|
+
const perSlug = await runPool(goldenSlugs, SPAWN_LIMIT, async (slug) => {
|
|
492
|
+
const slugErrors = [];
|
|
493
|
+
const outdir = join(emitRoot, slug);
|
|
494
|
+
const res = await execNode([DERIVE_PERSONALITY, join(TOKENS_DIR, slug), "--emit", outdir]);
|
|
495
|
+
if (res.status !== 0) {
|
|
496
|
+
slugErrors.push(`${slug}: --emit exited ${res.status}: ${firstLine(res.stderr || res.stdout)}`);
|
|
497
|
+
return slugErrors;
|
|
498
|
+
}
|
|
499
|
+
for (const [key, fileName] of [["personality", "personality.css"], ["globals", "globals.css"]]) {
|
|
500
|
+
const css = readSafe(join(outdir, "src/styles", fileName));
|
|
501
|
+
if (css === null) { slugErrors.push(`${slug}: emit produced no src/styles/${fileName}`); continue; }
|
|
502
|
+
const actual = goldenHash(css);
|
|
503
|
+
const expected = goldens[slug] && goldens[slug][key];
|
|
504
|
+
if (actual === expected) matched++;
|
|
505
|
+
else slugErrors.push(`${slug}: ${fileName} hash ${actual} != golden ${expected ?? "(missing key)"}`);
|
|
506
|
+
}
|
|
507
|
+
return slugErrors;
|
|
508
|
+
});
|
|
509
|
+
for (const slugErrors of perSlug) errors.push(...slugErrors);
|
|
510
|
+
} finally {
|
|
511
|
+
rmSync(emitRoot, { recursive: true, force: true });
|
|
512
|
+
}
|
|
513
|
+
return { name: "golden emission match", pass: errors.length === 0, detail: `${matched}/${goldenSlugs.length * 2} hashes match goldens.json`, errors };
|
|
514
|
+
}
|
|
515
|
+
|
|
380
516
|
// Derive the ground-truth counts from the catalog itself.
|
|
381
517
|
function deriveGround(catalog) {
|
|
382
518
|
return {
|
|
@@ -386,14 +522,42 @@ function deriveGround(catalog) {
|
|
|
386
522
|
};
|
|
387
523
|
}
|
|
388
524
|
|
|
525
|
+
// Run one rule's contrastive fixture pair through the check engine and exit 0/1 on its result.
|
|
526
|
+
function runSingleFixture(catalog, ruleId) {
|
|
527
|
+
const row = catalog.rows.find((r) => r.id === ruleId);
|
|
528
|
+
if (!row) { console.error(`--fixture: no rules.csv row with id "${ruleId}"`); process.exit(2); }
|
|
529
|
+
if (row.fixture_pair !== "yes") { console.error(`--fixture: ${ruleId} has fixture_pair=${row.fixture_pair}; no contrastive pair to run`); process.exit(2); }
|
|
530
|
+
const r = checkContrastiveValidity(catalog, ruleId);
|
|
531
|
+
if (r.errors.length === 0 && r.detail.startsWith("0 ")) {
|
|
532
|
+
console.error(`--fixture: ${ruleId}.flag.* / ${ruleId}.pass.* not found in data/catalog/fixtures/`);
|
|
533
|
+
process.exit(2);
|
|
534
|
+
}
|
|
535
|
+
if (r.pass) { console.log(`PASS fixture pair ${ruleId} — flag reports ${ruleId}, pass reports zero.`); process.exit(0); }
|
|
536
|
+
console.error(`FAIL fixture pair ${ruleId}`);
|
|
537
|
+
for (const e of r.errors) console.error(` - ${e}`);
|
|
538
|
+
process.exit(1);
|
|
539
|
+
}
|
|
540
|
+
|
|
389
541
|
// Run every check, print the summary table, and exit non-zero on any failure.
|
|
390
|
-
function main() {
|
|
542
|
+
async function main() {
|
|
543
|
+
const args = process.argv.slice(2);
|
|
544
|
+
let fixtureId = null;
|
|
545
|
+
let runGoldens = false;
|
|
546
|
+
for (let i = 0; i < args.length; i++) {
|
|
547
|
+
if (args[i] === "--fixture") fixtureId = args[++i] || null;
|
|
548
|
+
else if (args[i].startsWith("--fixture=")) fixtureId = args[i].slice("--fixture=".length);
|
|
549
|
+
else if (args[i] === "--goldens") runGoldens = true;
|
|
550
|
+
}
|
|
551
|
+
|
|
391
552
|
const results = [];
|
|
392
553
|
let catalog = null;
|
|
393
554
|
try { catalog = loadCatalog(); } catch (e) {
|
|
394
555
|
console.error("FATAL: " + e.message);
|
|
395
556
|
process.exit(2);
|
|
396
557
|
}
|
|
558
|
+
|
|
559
|
+
if (fixtureId) runSingleFixture(catalog, fixtureId);
|
|
560
|
+
|
|
397
561
|
const ground = deriveGround(catalog);
|
|
398
562
|
|
|
399
563
|
const runners = [
|
|
@@ -403,9 +567,13 @@ function main() {
|
|
|
403
567
|
() => checkContrastiveValidity(catalog),
|
|
404
568
|
() => checkStatedCounts(catalog, ground),
|
|
405
569
|
() => checkFileTree(),
|
|
570
|
+
() => checkTokenFileTree(),
|
|
571
|
+
() => checkArtifactValidity(),
|
|
572
|
+
() => checkGoldenMatch(runGoldens),
|
|
573
|
+
() => validateCombinations(COMBINATIONS_DIR),
|
|
406
574
|
];
|
|
407
575
|
for (const run of runners) {
|
|
408
|
-
try { results.push(run()); }
|
|
576
|
+
try { results.push(await run()); }
|
|
409
577
|
catch (e) { results.push({ name: run.name || "check", pass: false, detail: "threw", errors: [e.stack || String(e)] }); }
|
|
410
578
|
}
|
|
411
579
|
|
|
@@ -420,7 +588,7 @@ function main() {
|
|
|
420
588
|
console.log("self-integrity — design skill");
|
|
421
589
|
console.log("=".repeat(60));
|
|
422
590
|
const pad = (s, n) => (s + " ".repeat(n)).slice(0, n);
|
|
423
|
-
for (const r of results) console.log(`${r.pass ? "PASS" : "FAIL"} ${pad(r.name, 34)} ${r.detail}`);
|
|
591
|
+
for (const r of results) console.log(`${r.skip ? "SKIP" : r.pass ? "PASS" : "FAIL"} ${pad(r.name, 34)} ${r.detail}`);
|
|
424
592
|
console.log("=".repeat(60));
|
|
425
593
|
|
|
426
594
|
const failed = results.filter((r) => !r.pass);
|
|
@@ -429,4 +597,4 @@ function main() {
|
|
|
429
597
|
process.exit(0);
|
|
430
598
|
}
|
|
431
599
|
|
|
432
|
-
main();
|
|
600
|
+
main().catch((e) => { console.error("FATAL: " + (e.stack || String(e))); process.exit(2); });
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// skills/design/scripts/snapshot-goldens.mjs — snapshot golden hashes of the CSS emitted for every design-tokens artifact (data/design-tokens/<slug>/) into data/design-tokens/goldens.json, the byte-match regression oracle for the emission pipeline.
|
|
3
|
+
|
|
4
|
+
import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { goldenHash } from "./lib/golden.mjs";
|
|
10
|
+
|
|
11
|
+
const scriptsDir = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const skillDir = dirname(scriptsDir);
|
|
13
|
+
const derivePersonalityScript = join(scriptsDir, "derive-personality.mjs");
|
|
14
|
+
const tokensDir = join(skillDir, "data", "design-tokens");
|
|
15
|
+
const goldensJsonPath = join(tokensDir, "goldens.json");
|
|
16
|
+
|
|
17
|
+
// Where full emitted outputs are kept (one subdir per slug) — GOLDENS_EMIT_ROOT to pin it, else a fresh temp dir.
|
|
18
|
+
const emitRoot = process.env.GOLDENS_EMIT_ROOT || mkdtempSync(join(tmpdir(), "design-goldens-"));
|
|
19
|
+
mkdirSync(emitRoot, { recursive: true });
|
|
20
|
+
|
|
21
|
+
// Every artifact directory under data/design-tokens/ (a directory holding design.tokens.json).
|
|
22
|
+
const entries = readdirSync(tokensDir, { withFileTypes: true })
|
|
23
|
+
.filter((d) => d.isDirectory() && existsSync(join(tokensDir, d.name, "design.tokens.json")))
|
|
24
|
+
.map((d) => ({ slug: d.name, path: join(tokensDir, d.name) }))
|
|
25
|
+
.sort((a, b) => (a.slug < b.slug ? -1 : 1));
|
|
26
|
+
|
|
27
|
+
const goldens = {};
|
|
28
|
+
const failures = [];
|
|
29
|
+
|
|
30
|
+
for (const { slug, path } of entries) {
|
|
31
|
+
const outdir = join(emitRoot, slug);
|
|
32
|
+
const run = spawnSync(process.execPath, [derivePersonalityScript, path, "--emit", outdir], { encoding: "utf8" });
|
|
33
|
+
if (run.status !== 0) {
|
|
34
|
+
failures.push({ slug, error: (run.stderr || run.stdout || `exit status ${run.status}`).trim() });
|
|
35
|
+
console.log(`FAIL ${slug}`);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const personalityCss = readFileSync(join(outdir, "src", "styles", "personality.css"), "utf8");
|
|
40
|
+
const globalsCss = readFileSync(join(outdir, "src", "styles", "globals.css"), "utf8");
|
|
41
|
+
goldens[slug] = { personality: goldenHash(personalityCss), globals: goldenHash(globalsCss) };
|
|
42
|
+
console.log(`OK ${slug}`);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
failures.push({ slug, error: String((err && err.message) || err) });
|
|
45
|
+
console.log(`FAIL ${slug}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Assemble the sorted-key document; "_meta" sorts first (ASCII "_" < "a").
|
|
50
|
+
const document = {
|
|
51
|
+
_meta: {
|
|
52
|
+
description: "Post-carrier golden hashes of the CSS emitted by derive-personality.mjs --emit for every design-tokens artifact (data/design-tokens/<slug>/), dark-inclusive: an artifact shipping design.tokens.dark.json hashes its emitted [data-theme=\"dark\"] block too. Each entry records the sha256 of the comment-stripped personality.css and globals.css (see scripts/lib/golden.mjs). The byte-match regression oracle for the emission pipeline.",
|
|
53
|
+
generator: "scripts/snapshot-goldens.mjs",
|
|
54
|
+
strip: "css-comments",
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
for (const slug of Object.keys(goldens).sort()) document[slug] = goldens[slug];
|
|
58
|
+
|
|
59
|
+
mkdirSync(dirname(goldensJsonPath), { recursive: true });
|
|
60
|
+
writeFileSync(goldensJsonPath, JSON.stringify(document, null, 2) + "\n");
|
|
61
|
+
|
|
62
|
+
console.log(`\n${entries.length} entries: ${Object.keys(goldens).length} OK, ${failures.length} FAIL`);
|
|
63
|
+
console.log(`goldens: ${goldensJsonPath}`);
|
|
64
|
+
console.log(`emitted outputs: ${emitRoot}`);
|
|
65
|
+
if (failures.length > 0) {
|
|
66
|
+
for (const f of failures) console.error(`\nFAIL ${f.slug}:\n${f.error}`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|