@hegemonart/get-design-done 1.42.0 → 1.44.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +1126 -1038
- package/README.md +159 -155
- package/SKILL.md +42 -42
- package/agents/README.md +53 -53
- package/agents/a11y-mapper.md +3 -3
- package/agents/component-benchmark-harvester.md +8 -8
- package/agents/component-benchmark-synthesizer.md +11 -11
- package/agents/component-taxonomy-mapper.md +5 -5
- package/agents/compose-executor.md +25 -25
- package/agents/conflict-resolver.md +8 -8
- package/agents/cost-forecaster.md +12 -12
- package/agents/decision-journal-exporter.md +5 -5
- package/agents/design-advisor.md +19 -19
- package/agents/design-assumptions-analyzer.md +16 -16
- package/agents/design-auditor.md +39 -39
- package/agents/design-authority-watcher.md +28 -28
- package/agents/design-component-generator.md +27 -27
- package/agents/design-context-builder.md +66 -66
- package/agents/design-context-checker-gate.md +5 -5
- package/agents/design-context-checker.md +20 -20
- package/agents/design-discussant.md +23 -23
- package/agents/design-doc-writer.md +12 -12
- package/agents/design-executor.md +38 -38
- package/agents/design-figma-writer.md +31 -31
- package/agents/design-fixer.md +27 -27
- package/agents/design-integration-checker-gate.md +5 -5
- package/agents/design-integration-checker.md +29 -29
- package/agents/design-paper-writer.md +14 -14
- package/agents/design-pattern-mapper.md +9 -9
- package/agents/design-pencil-writer.md +12 -12
- package/agents/design-phase-researcher.md +14 -14
- package/agents/design-plan-checker.md +13 -13
- package/agents/design-planner.md +24 -24
- package/agents/design-reflector.md +48 -48
- package/agents/design-research-synthesizer.md +21 -21
- package/agents/design-start-writer.md +7 -7
- package/agents/design-update-checker.md +8 -8
- package/agents/design-verifier-gate.md +5 -5
- package/agents/design-verifier.md +80 -80
- package/agents/ds-generator.md +14 -14
- package/agents/ds-migration-planner.md +12 -12
- package/agents/email-executor.md +26 -26
- package/agents/experiment-result-ingester.md +10 -10
- package/agents/flutter-executor.md +28 -28
- package/agents/gdd-graph-refresh.md +10 -10
- package/agents/gdd-intel-updater.md +11 -11
- package/agents/gdd-learnings-extractor.md +2 -2
- package/agents/motion-mapper.md +8 -8
- package/agents/motion-verifier.md +16 -16
- package/agents/pdf-executor.md +27 -27
- package/agents/perf-analyzer.md +20 -20
- package/agents/pr-commenter.md +24 -24
- package/agents/prototype-gate.md +29 -29
- package/agents/quality-gate-runner.md +21 -21
- package/agents/rollout-coordinator.md +8 -8
- package/agents/swift-executor.md +41 -41
- package/agents/ticket-sync-agent.md +19 -19
- package/agents/token-mapper.md +6 -6
- package/agents/user-research-synthesizer.md +13 -13
- package/agents/visual-hierarchy-mapper.md +2 -2
- package/dist/claude-code/.claude/skills/add-backlog/SKILL.md +3 -3
- package/dist/claude-code/.claude/skills/analyze-dependencies/SKILL.md +10 -10
- package/dist/claude-code/.claude/skills/apply-reflections/SKILL.md +13 -13
- package/dist/claude-code/.claude/skills/apply-reflections/apply-reflections-procedure.md +20 -20
- package/dist/claude-code/.claude/skills/audit/SKILL.md +7 -7
- package/dist/claude-code/.claude/skills/bandit-status/SKILL.md +7 -7
- package/dist/claude-code/.claude/skills/benchmark/SKILL.md +7 -7
- package/dist/claude-code/.claude/skills/bootstrap-ds/SKILL.md +10 -10
- package/dist/claude-code/.claude/skills/brief/SKILL.md +20 -20
- package/dist/claude-code/.claude/skills/budget/SKILL.md +4 -4
- package/dist/claude-code/.claude/skills/cache-manager/SKILL.md +6 -6
- package/dist/claude-code/.claude/skills/cache-manager/cache-policy.md +5 -5
- package/dist/claude-code/.claude/skills/check-update/SKILL.md +5 -5
- package/dist/claude-code/.claude/skills/compare/SKILL.md +15 -15
- package/dist/claude-code/.claude/skills/compare/compare-rubric.md +17 -17
- package/dist/claude-code/.claude/skills/complete-cycle/SKILL.md +5 -5
- package/dist/claude-code/.claude/skills/connections/SKILL.md +11 -11
- package/dist/claude-code/.claude/skills/connections/connections-onboarding.md +76 -76
- package/dist/claude-code/.claude/skills/continue/SKILL.md +2 -2
- package/dist/claude-code/.claude/skills/darkmode/SKILL.md +17 -17
- package/dist/claude-code/.claude/skills/darkmode/darkmode-audit-procedure.md +7 -7
- package/dist/claude-code/.claude/skills/debug/SKILL.md +3 -3
- package/dist/claude-code/.claude/skills/debug/debug-feedback-loops.md +12 -12
- package/dist/claude-code/.claude/skills/design/SKILL.md +12 -12
- package/dist/claude-code/.claude/skills/design/design-procedure.md +23 -23
- package/dist/claude-code/.claude/skills/discover/SKILL.md +7 -7
- package/dist/claude-code/.claude/skills/discover/discover-procedure.md +18 -18
- package/dist/claude-code/.claude/skills/discuss/SKILL.md +12 -12
- package/dist/claude-code/.claude/skills/do/SKILL.md +1 -1
- package/dist/claude-code/.claude/skills/explore/SKILL.md +21 -21
- package/dist/claude-code/.claude/skills/explore/explore-procedure.md +48 -48
- package/dist/claude-code/.claude/skills/export/SKILL.md +9 -9
- package/dist/claude-code/.claude/skills/extract-learnings/SKILL.md +5 -5
- package/dist/claude-code/.claude/skills/fast/SKILL.md +7 -7
- package/dist/claude-code/.claude/skills/figma-extract/SKILL.md +11 -11
- package/dist/claude-code/.claude/skills/figma-write/SKILL.md +6 -6
- package/dist/claude-code/.claude/skills/graphify/SKILL.md +4 -4
- package/dist/claude-code/.claude/skills/health/SKILL.md +16 -16
- package/dist/claude-code/.claude/skills/health/health-mcp-detection.md +3 -3
- package/dist/claude-code/.claude/skills/health/health-skill-length-report.md +6 -6
- package/dist/claude-code/.claude/skills/help/SKILL.md +1 -1
- package/dist/claude-code/.claude/skills/list-assumptions/SKILL.md +4 -4
- package/dist/claude-code/.claude/skills/map/SKILL.md +12 -12
- package/dist/claude-code/.claude/skills/migrate/SKILL.md +5 -5
- package/dist/claude-code/.claude/skills/new-cycle/SKILL.md +2 -2
- package/dist/claude-code/.claude/skills/new-cycle/milestone-completeness-rubric.md +16 -16
- package/dist/claude-code/.claude/skills/new-project/SKILL.md +1 -1
- package/dist/claude-code/.claude/skills/next/SKILL.md +5 -5
- package/dist/claude-code/.claude/skills/note/SKILL.md +1 -1
- package/dist/claude-code/.claude/skills/openrouter-status/SKILL.md +4 -4
- package/dist/claude-code/.claude/skills/optimize/SKILL.md +15 -15
- package/dist/claude-code/.claude/skills/pause/SKILL.md +5 -5
- package/dist/claude-code/.claude/skills/peer-cli-add/SKILL.md +11 -11
- package/dist/claude-code/.claude/skills/peer-cli-add/peer-cli-protocol.md +39 -39
- package/dist/claude-code/.claude/skills/peer-cli-customize/SKILL.md +14 -14
- package/dist/claude-code/.claude/skills/peers/SKILL.md +4 -4
- package/dist/claude-code/.claude/skills/plan/SKILL.md +13 -13
- package/dist/claude-code/.claude/skills/plan/plan-procedure.md +24 -24
- package/dist/claude-code/.claude/skills/plant-seed/SKILL.md +4 -4
- package/dist/claude-code/.claude/skills/pr-branch/SKILL.md +2 -2
- package/dist/claude-code/.claude/skills/progress/SKILL.md +15 -15
- package/dist/claude-code/.claude/skills/quality-gate/SKILL.md +22 -22
- package/dist/claude-code/.claude/skills/quality-gate/threat-modeling.md +19 -19
- package/dist/claude-code/.claude/skills/quick/SKILL.md +5 -5
- package/dist/claude-code/.claude/skills/reapply-patches/SKILL.md +7 -7
- package/dist/claude-code/.claude/skills/reflect/SKILL.md +3 -3
- package/dist/claude-code/.claude/skills/reflect/procedures/capability-gap-scan.md +11 -11
- package/dist/claude-code/.claude/skills/report-issue/SKILL.md +5 -5
- package/dist/claude-code/.claude/skills/report-issue/report-issue-procedure.md +27 -27
- package/dist/claude-code/.claude/skills/resume/SKILL.md +9 -9
- package/dist/claude-code/.claude/skills/review-backlog/SKILL.md +3 -3
- package/dist/claude-code/.claude/skills/review-decisions/SKILL.md +3 -3
- package/dist/claude-code/.claude/skills/roi/SKILL.md +5 -5
- package/dist/claude-code/.claude/skills/rollout-status/SKILL.md +4 -4
- package/dist/claude-code/.claude/skills/router/SKILL.md +11 -11
- package/dist/claude-code/.claude/skills/router/capability-gap-emitter.md +6 -6
- package/dist/claude-code/.claude/skills/router/router-pick-emitter.md +9 -9
- package/dist/claude-code/.claude/skills/router/router-rules.md +7 -7
- package/dist/claude-code/.claude/skills/scan/SKILL.md +16 -16
- package/dist/claude-code/.claude/skills/scan/scan-procedure.md +42 -42
- package/dist/claude-code/.claude/skills/settings/SKILL.md +2 -2
- package/dist/claude-code/.claude/skills/ship/SKILL.md +7 -7
- package/dist/claude-code/.claude/skills/sketch/SKILL.md +10 -10
- package/dist/claude-code/.claude/skills/sketch-wrap-up/SKILL.md +12 -12
- package/dist/claude-code/.claude/skills/skill-manifest/SKILL.md +5 -5
- package/dist/claude-code/.claude/skills/spike/SKILL.md +7 -7
- package/dist/claude-code/.claude/skills/spike-wrap-up/SKILL.md +13 -13
- package/dist/claude-code/.claude/skills/start/SKILL.md +8 -8
- package/dist/claude-code/.claude/skills/start/start-procedure.md +9 -9
- package/dist/claude-code/.claude/skills/stats/SKILL.md +5 -5
- package/dist/claude-code/.claude/skills/style/SKILL.md +12 -12
- package/dist/claude-code/.claude/skills/style/style-doc-procedure.md +12 -12
- package/dist/claude-code/.claude/skills/synthesize/SKILL.md +10 -10
- package/dist/claude-code/.claude/skills/timeline/SKILL.md +4 -4
- package/dist/claude-code/.claude/skills/todo/SKILL.md +3 -3
- package/dist/claude-code/.claude/skills/turn-closeout/SKILL.md +10 -10
- package/dist/claude-code/.claude/skills/unlock-decision/SKILL.md +3 -3
- package/dist/claude-code/.claude/skills/update/SKILL.md +9 -9
- package/dist/claude-code/.claude/skills/using-gdd/SKILL.md +17 -17
- package/dist/claude-code/.claude/skills/verify/SKILL.md +13 -13
- package/dist/claude-code/.claude/skills/verify/verify-procedure.md +34 -34
- package/dist/claude-code/.claude/skills/warm-cache/SKILL.md +8 -8
- package/dist/claude-code/.claude/skills/watch-authorities/SKILL.md +9 -9
- package/dist/claude-code/.claude/skills/zoom-out/SKILL.md +4 -4
- package/package.json +9 -2
- package/reference/DEPRECATIONS.md +10 -10
- package/reference/STATE-TEMPLATE.md +26 -26
- package/reference/accessibility.md +13 -13
- package/reference/adr-format.md +13 -13
- package/reference/ai-native-tool-interface.md +5 -5
- package/reference/anti-patterns.md +9 -9
- package/reference/architecture-vocabulary.md +31 -31
- package/reference/audit-scoring.md +13 -13
- package/reference/authority-feeds.md +36 -36
- package/reference/bandit-integration.md +25 -25
- package/reference/brand-voice.md +36 -36
- package/reference/capability-gap-stage-gate.md +20 -20
- package/reference/checklists.md +26 -26
- package/reference/cli-localization.md +13 -13
- package/reference/codex-tools.md +2 -2
- package/reference/color-theory.md +28 -28
- package/reference/component-authoring.md +4 -4
- package/reference/components/README.md +13 -13
- package/reference/components/TEMPLATE.md +13 -13
- package/reference/components/accordion.md +15 -15
- package/reference/components/alert.md +25 -25
- package/reference/components/badge.md +18 -18
- package/reference/components/breadcrumbs.md +24 -24
- package/reference/components/button.md +21 -21
- package/reference/components/card.md +13 -13
- package/reference/components/checkbox.md +20 -20
- package/reference/components/chip.md +20 -20
- package/reference/components/command-palette.md +15 -15
- package/reference/components/date-picker.md +22 -22
- package/reference/components/drawer.md +13 -13
- package/reference/components/file-upload.md +22 -22
- package/reference/components/input.md +18 -18
- package/reference/components/label.md +25 -25
- package/reference/components/link.md +19 -19
- package/reference/components/list.md +17 -17
- package/reference/components/menu.md +19 -19
- package/reference/components/modal-dialog.md +16 -16
- package/reference/components/navbar.md +19 -19
- package/reference/components/pagination.md +18 -18
- package/reference/components/popover.md +12 -12
- package/reference/components/progress.md +18 -18
- package/reference/components/radio.md +17 -17
- package/reference/components/rich-text-editor.md +24 -24
- package/reference/components/select-combobox.md +16 -16
- package/reference/components/sidebar.md +15 -15
- package/reference/components/skeleton.md +20 -20
- package/reference/components/slider.md +20 -20
- package/reference/components/stepper.md +24 -24
- package/reference/components/switch.md +19 -19
- package/reference/components/table.md +21 -21
- package/reference/components/tabs.md +11 -11
- package/reference/components/toast.md +19 -19
- package/reference/components/tooltip.md +19 -19
- package/reference/components/tree.md +17 -17
- package/reference/composition.md +38 -38
- package/reference/config-schema.md +37 -37
- package/reference/context-md-format.md +9 -9
- package/reference/contrast-advanced.md +29 -29
- package/reference/conversational-ui.md +17 -17
- package/reference/cost-governance.md +14 -14
- package/reference/css-grid-layout.md +8 -8
- package/reference/cycle-handoff-preamble.md +3 -3
- package/reference/data-visualization.md +67 -67
- package/reference/debugger-philosophy.md +5 -5
- package/reference/design-system-guidance.md +21 -21
- package/reference/design-systems-catalog.md +20 -20
- package/reference/design-variants.md +11 -11
- package/reference/domains/civic-patterns.md +10 -10
- package/reference/domains/finance-patterns.md +9 -9
- package/reference/domains/gaming-patterns.md +9 -9
- package/reference/domains/healthcare-patterns.md +11 -11
- package/reference/ds-bootstrap-rubric.md +13 -13
- package/reference/email-design.md +22 -22
- package/reference/emotional-design.md +10 -10
- package/reference/error-recovery.md +11 -11
- package/reference/export-formats.md +7 -7
- package/reference/figma-sandbox.md +6 -6
- package/reference/first-principles.md +10 -10
- package/reference/form-patterns.md +26 -26
- package/reference/framer-motion-patterns.md +49 -49
- package/reference/gdd-runtime-audit.md +17 -17
- package/reference/gdd-threat-model.md +44 -44
- package/reference/gemini-tools.md +3 -3
- package/reference/gestalt.md +24 -24
- package/reference/heuristics.md +32 -32
- package/reference/i18n.md +44 -44
- package/reference/iconography.md +24 -24
- package/reference/image-optimization.md +14 -14
- package/reference/information-architecture.md +47 -47
- package/reference/intel-schema.md +1 -1
- package/reference/known-failure-modes.md +37 -37
- package/reference/meta-rules.md +5 -5
- package/reference/migrations/material-3-to-4.md +17 -17
- package/reference/migrations/mui-v6.md +16 -16
- package/reference/migrations/shadcn-v2.md +25 -25
- package/reference/migrations/tailwind-v4.md +21 -21
- package/reference/model-prices.md +3 -3
- package/reference/model-tiers.md +40 -40
- package/reference/motion-advanced.md +21 -21
- package/reference/motion-easings.md +29 -29
- package/reference/motion-interpolate.md +1 -1
- package/reference/motion-spring.md +13 -13
- package/reference/motion-transition-taxonomy.md +34 -34
- package/reference/motion.md +31 -31
- package/reference/multi-author-model.md +13 -13
- package/reference/native-platforms.md +28 -28
- package/reference/notification-routing.md +6 -6
- package/reference/onboarding-progressive-disclosure.md +32 -32
- package/reference/openrouter-tier-mapping.md +8 -8
- package/reference/palette-catalog.md +37 -37
- package/reference/parallelism-rules.md +20 -20
- package/reference/peer-cli-capabilities.md +14 -14
- package/reference/peer-protocols.md +21 -21
- package/reference/perf-budget.md +21 -21
- package/reference/performance.md +22 -22
- package/reference/platforms.md +51 -51
- package/reference/pr-review-integration.md +7 -7
- package/reference/prices/antigravity.md +3 -3
- package/reference/prices/augment.md +3 -3
- package/reference/prices/claude.md +2 -2
- package/reference/prices/cline.md +4 -4
- package/reference/prices/codebuddy.md +3 -3
- package/reference/prices/codex.md +2 -2
- package/reference/prices/copilot.md +3 -3
- package/reference/prices/cursor.md +3 -3
- package/reference/prices/gemini.md +2 -2
- package/reference/prices/kilo.md +3 -3
- package/reference/prices/opencode.md +4 -4
- package/reference/prices/qwen.md +2 -2
- package/reference/prices/trae.md +3 -3
- package/reference/prices/windsurf.md +3 -3
- package/reference/prices.openrouter.md +5 -5
- package/reference/print-design.md +36 -36
- package/reference/priority-matrix.md +2 -2
- package/reference/project-skills-guide.md +3 -3
- package/reference/proportion-systems.md +23 -23
- package/reference/pseudonymization-rules.md +30 -30
- package/reference/retrieval-contract.md +14 -14
- package/reference/review-format.md +7 -7
- package/reference/rollout-coordination.md +10 -10
- package/reference/rtl-cjk-cultural.md +39 -39
- package/reference/runtime-models.md +28 -28
- package/reference/shared-preamble.md +26 -26
- package/reference/skill-authoring-contract.md +16 -16
- package/reference/skill-placeholders.md +3 -3
- package/reference/start-interview.md +10 -10
- package/reference/style-vocabulary.md +25 -25
- package/reference/surfaces.md +4 -4
- package/reference/ticket-sync.md +9 -9
- package/reference/typography.md +64 -64
- package/reference/user-research.md +54 -54
- package/reference/variable-fonts-loading.md +15 -15
- package/reference/visual-hierarchy-layout.md +41 -41
- package/scripts/lib/harness-freshness.cjs +59 -0
- package/scripts/lib/health-mirror/index.cjs +27 -0
- package/scripts/lib/manifest/harnesses.json +280 -14
- package/scripts/lib/manifest/prose-denylist.json +1 -1
- package/scripts/lib/manifest/schemas/harnesses.schema.json +32 -0
- package/sdk/mcp/gdd-mcp/server.js +125 -0
- package/skills/add-backlog/SKILL.md +3 -3
- package/skills/analyze-dependencies/SKILL.md +10 -10
- package/skills/apply-reflections/SKILL.md +13 -13
- package/skills/apply-reflections/apply-reflections-procedure.md +20 -20
- package/skills/audit/SKILL.md +7 -7
- package/skills/bandit-status/SKILL.md +7 -7
- package/skills/benchmark/SKILL.md +7 -7
- package/skills/bootstrap-ds/SKILL.md +10 -10
- package/skills/brief/SKILL.md +20 -20
- package/skills/budget/SKILL.md +4 -4
- package/skills/cache-manager/SKILL.md +6 -6
- package/skills/cache-manager/cache-policy.md +5 -5
- package/skills/check-update/SKILL.md +5 -5
- package/skills/compare/SKILL.md +15 -15
- package/skills/compare/compare-rubric.md +17 -17
- package/skills/complete-cycle/SKILL.md +5 -5
- package/skills/connections/SKILL.md +11 -11
- package/skills/connections/connections-onboarding.md +76 -76
- package/skills/continue/SKILL.md +2 -2
- package/skills/darkmode/SKILL.md +17 -17
- package/skills/darkmode/darkmode-audit-procedure.md +7 -7
- package/skills/debug/SKILL.md +3 -3
- package/skills/debug/debug-feedback-loops.md +12 -12
- package/skills/design/SKILL.md +12 -12
- package/skills/design/design-procedure.md +23 -23
- package/skills/discover/SKILL.md +7 -7
- package/skills/discover/discover-procedure.md +18 -18
- package/skills/discuss/SKILL.md +12 -12
- package/skills/do/SKILL.md +1 -1
- package/skills/explore/SKILL.md +21 -21
- package/skills/explore/explore-procedure.md +48 -48
- package/skills/export/SKILL.md +9 -9
- package/skills/extract-learnings/SKILL.md +5 -5
- package/skills/fast/SKILL.md +7 -7
- package/skills/figma-extract/SKILL.md +11 -11
- package/skills/figma-write/SKILL.md +6 -6
- package/skills/graphify/SKILL.md +4 -4
- package/skills/health/SKILL.md +16 -16
- package/skills/health/health-mcp-detection.md +3 -3
- package/skills/health/health-skill-length-report.md +6 -6
- package/skills/help/SKILL.md +1 -1
- package/skills/list-assumptions/SKILL.md +4 -4
- package/skills/map/SKILL.md +12 -12
- package/skills/migrate/SKILL.md +5 -5
- package/skills/new-cycle/SKILL.md +2 -2
- package/skills/new-cycle/milestone-completeness-rubric.md +16 -16
- package/skills/new-project/SKILL.md +1 -1
- package/skills/next/SKILL.md +5 -5
- package/skills/note/SKILL.md +1 -1
- package/skills/openrouter-status/SKILL.md +4 -4
- package/skills/optimize/SKILL.md +15 -15
- package/skills/pause/SKILL.md +5 -5
- package/skills/peer-cli-add/SKILL.md +11 -11
- package/skills/peer-cli-add/peer-cli-protocol.md +39 -39
- package/skills/peer-cli-customize/SKILL.md +14 -14
- package/skills/peers/SKILL.md +4 -4
- package/skills/plan/SKILL.md +13 -13
- package/skills/plan/plan-procedure.md +24 -24
- package/skills/plant-seed/SKILL.md +4 -4
- package/skills/pr-branch/SKILL.md +2 -2
- package/skills/progress/SKILL.md +15 -15
- package/skills/quality-gate/SKILL.md +22 -22
- package/skills/quality-gate/threat-modeling.md +19 -19
- package/skills/quick/SKILL.md +5 -5
- package/skills/reapply-patches/SKILL.md +7 -7
- package/skills/reflect/SKILL.md +3 -3
- package/skills/reflect/procedures/capability-gap-scan.md +11 -11
- package/skills/report-issue/SKILL.md +5 -5
- package/skills/report-issue/report-issue-procedure.md +27 -27
- package/skills/resume/SKILL.md +9 -9
- package/skills/review-backlog/SKILL.md +3 -3
- package/skills/review-decisions/SKILL.md +3 -3
- package/skills/roi/SKILL.md +5 -5
- package/skills/rollout-status/SKILL.md +4 -4
- package/skills/router/SKILL.md +11 -11
- package/skills/router/capability-gap-emitter.md +6 -6
- package/skills/router/router-pick-emitter.md +9 -9
- package/skills/router/router-rules.md +7 -7
- package/skills/scan/SKILL.md +16 -16
- package/skills/scan/scan-procedure.md +42 -42
- package/skills/settings/SKILL.md +2 -2
- package/skills/ship/SKILL.md +7 -7
- package/skills/sketch/SKILL.md +10 -10
- package/skills/sketch-wrap-up/SKILL.md +12 -12
- package/skills/skill-manifest/SKILL.md +5 -5
- package/skills/spike/SKILL.md +7 -7
- package/skills/spike-wrap-up/SKILL.md +13 -13
- package/skills/start/SKILL.md +8 -8
- package/skills/start/start-procedure.md +9 -9
- package/skills/stats/SKILL.md +5 -5
- package/skills/style/SKILL.md +12 -12
- package/skills/style/style-doc-procedure.md +12 -12
- package/skills/synthesize/SKILL.md +10 -10
- package/skills/timeline/SKILL.md +4 -4
- package/skills/todo/SKILL.md +3 -3
- package/skills/turn-closeout/SKILL.md +10 -10
- package/skills/unlock-decision/SKILL.md +3 -3
- package/skills/update/SKILL.md +9 -9
- package/skills/using-gdd/SKILL.md +17 -17
- package/skills/verify/SKILL.md +13 -13
- package/skills/verify/verify-procedure.md +34 -34
- package/skills/warm-cache/SKILL.md +8 -8
- package/skills/watch-authorities/SKILL.md +9 -9
- package/skills/zoom-out/SKILL.md +4 -4
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
# Form Patterns
|
|
2
2
|
|
|
3
|
-
Forms are one of the highest-friction surfaces in any product. Every unnecessary click, ambiguous label, or poorly-timed error message is a conversion lost or a user abandoned mid-task. The patterns below are grounded in empirical research
|
|
3
|
+
Forms are one of the highest-friction surfaces in any product. Every unnecessary click, ambiguous label, or poorly-timed error message is a conversion lost or a user abandoned mid-task. The patterns below are grounded in empirical research - primarily Luke Wroblewski's eye-tracking studies, WCAG 2.1 requirements, and NCSC/NIST guidance on authentication UX.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## 1. Label Position
|
|
8
8
|
|
|
9
|
-
Wroblewski's eye-tracking research measured the number of eye fixations required to move from label to field across the three common label placements. The findings are decisive: top-aligned labels are fastest because the eye travels in one direction
|
|
9
|
+
Wroblewski's eye-tracking research measured the number of eye fixations required to move from label to field across the three common label placements. The findings are decisive: top-aligned labels are fastest because the eye travels in one direction - down - without a horizontal context switch.
|
|
10
10
|
|
|
11
11
|
**Top-aligned labels** are the default for most forms. Completion time is fastest because the label and input form a single vertical unit. They also accommodate longer translated strings without breaking layout, making them the correct default for internationalised products. Use top-aligned labels whenever form complexity is moderate to high or field types are mixed.
|
|
12
12
|
|
|
13
|
-
**See:** [`./i18n.md`](./i18n.md) §Locale Formatting for the full `Intl.*` family (`NumberFormat`, `DateTimeFormat`, `PluralRules`, `RelativeTimeFormat`, `ListFormat`) over hand-rolled string concatenation
|
|
13
|
+
**See:** [`./i18n.md`](./i18n.md) §Locale Formatting for the full `Intl.*` family (`NumberFormat`, `DateTimeFormat`, `PluralRules`, `RelativeTimeFormat`, `ListFormat`) over hand-rolled string concatenation - never assemble locale-aware strings via template literals. §Text Expansion explains the +30–40% LTR budget that top-aligned labels accommodate.
|
|
14
14
|
|
|
15
|
-
**Left-aligned labels** create the slowest completion time because the eye must saccade horizontally from label to field on every row. They earn their place only when scannability of values matters more than speed
|
|
15
|
+
**Left-aligned labels** create the slowest completion time because the eye must saccade horizontally from label to field on every row. They earn their place only when scannability of values matters more than speed - settings pages, data-entry tables, or read-heavy forms where users compare label and value together. In these contexts the horizontal alignment aids review, not input.
|
|
16
16
|
|
|
17
|
-
**Floating labels (Material 3 style)** are an acceptable middle ground when screen real estate is severely constrained. The label begins as visible hint text above the field and remains visible (shrunk, repositioned) after the user starts typing. This pattern is distinct from
|
|
17
|
+
**Floating labels (Material 3 style)** are an acceptable middle ground when screen real estate is severely constrained. The label begins as visible hint text above the field and remains visible (shrunk, repositioned) after the user starts typing. This pattern is distinct from - and incompatible with - placeholder-as-label. Never use placeholder text as the sole label: when the field receives focus the placeholder disappears, leaving the user to recall what was asked. This is a WCAG 2.1 failure under criterion 1.3.5 (Identify Input Purpose) and a general usability failure on any form longer than three fields.
|
|
18
18
|
|
|
19
19
|
| Label position | Completion speed | Best use case |
|
|
20
20
|
|---|---|---|
|
|
21
21
|
| Top-aligned | Fastest | Most forms; mixed field types; long forms |
|
|
22
22
|
| Left-aligned | Slowest | Settings pages; data comparison; values matter |
|
|
23
23
|
| Floating (Material 3) | Medium | Space-constrained mobile forms |
|
|
24
|
-
| Placeholder-only |
|
|
24
|
+
| Placeholder-only | - | **Never** - WCAG 1.3.5 failure |
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
@@ -31,23 +31,23 @@ The timing of inline validation determines whether it helps or hinders. Validate
|
|
|
31
31
|
|
|
32
32
|
**On-submit only** carries the lowest cognitive load during form completion and is appropriate for short forms (three fields or fewer). The cost is UX debt on long forms: when multiple fields fail at once, the user must scroll up to find errors, fix one, scroll back, and repeat. Provide an error summary at the top if using submit-only validation on long forms (see Section 3).
|
|
33
33
|
|
|
34
|
-
**On-blur (field exit)** is the recommended default for most forms. Validation fires after the user leaves the field, not while they are in it. This prevents the jarring experience of an error appearing mid-keystroke. The critical rule: never validate on first focus
|
|
34
|
+
**On-blur (field exit)** is the recommended default for most forms. Validation fires after the user leaves the field, not while they are in it. This prevents the jarring experience of an error appearing mid-keystroke. The critical rule: never validate on first focus - only after the field has been blurred at least once. A pristine, untouched field should never show an error state.
|
|
35
35
|
|
|
36
|
-
**On-change (real-time)** is appropriate for two specific cases: password strength meters and character counters. It is not appropriate for format validation such as email addresses. Validating email format on every keystroke flags `hello@` as invalid before the user has finished typing the domain
|
|
36
|
+
**On-change (real-time)** is appropriate for two specific cases: password strength meters and character counters. It is not appropriate for format validation such as email addresses. Validating email format on every keystroke flags `hello@` as invalid before the user has finished typing the domain - a false negative that erodes trust in the form.
|
|
37
37
|
|
|
38
38
|
Never show a red error state before the user has had a realistic opportunity to complete the field. The error state is a signal that something is wrong; triggering it prematurely teaches users to ignore it.
|
|
39
39
|
|
|
40
40
|
| Timing | When to use | When to avoid |
|
|
41
41
|
|---|---|---|
|
|
42
42
|
| On-submit | Short forms (≤ 3 fields) | Long multi-section forms |
|
|
43
|
-
| On-blur | Default for most fields |
|
|
43
|
+
| On-blur | Default for most fields | - |
|
|
44
44
|
| On-change | Password strength, character count | Format validation (email, phone) |
|
|
45
45
|
|
|
46
46
|
---
|
|
47
47
|
|
|
48
48
|
## 3. Error Placement and Recovery Copy
|
|
49
49
|
|
|
50
|
-
Errors placed far from their source
|
|
50
|
+
Errors placed far from their source - in a top banner, a modal, or a footer - force the user to map the error message back to the problematic field. Errors belong immediately below the field they describe, appearing between the field and the next element in the flow.
|
|
51
51
|
|
|
52
52
|
**Visual treatment**: error messages must never rely on color alone. A red border communicates nothing to users with red-green color deficiency. Use an error icon alongside the message text. The field border color change is acceptable as a supplemental indicator, not the primary one.
|
|
53
53
|
|
|
@@ -83,7 +83,7 @@ Marking required fields unambiguously prevents submission errors and sets accura
|
|
|
83
83
|
|
|
84
84
|
The **asterisk (*) convention** is the web default. Mark every required field with `*` and place the legend "* Required" near the top of the form, before the first field. Users arriving mid-page may not see a legend placed at the bottom.
|
|
85
85
|
|
|
86
|
-
The **"(optional)" alternative** is better when most fields are required and only a few are not. Marking three optional fields "(optional)" is less visual noise than marking fifteen fields with `*`. Choose one convention per form
|
|
86
|
+
The **"(optional)" alternative** is better when most fields are required and only a few are not. Marking three optional fields "(optional)" is less visual noise than marking fifteen fields with `*`. Choose one convention per form - never mix both. A form that marks some fields `*` and labels others "(optional)" implies that unlabelled fields are neither required nor optional, which is simply confusing.
|
|
87
87
|
|
|
88
88
|
Regardless of which visual convention is used, every required field must carry `aria-required="true"` or the native `required` attribute. Screen readers announce the required state from these attributes, not from the visible asterisk.
|
|
89
89
|
|
|
@@ -103,17 +103,17 @@ Multi-step forms break long processes into manageable stages, but they introduce
|
|
|
103
103
|
|
|
104
104
|
**Show total step count upfront** and maintain it throughout. "Step 2 of 4" gives the user a completion model. Never add steps mid-flow; if a conditional step becomes necessary, it must have been accounted for in the total from the beginning. Surprise steps feel like bait-and-switch.
|
|
105
105
|
|
|
106
|
-
**Preserve state between steps** unconditionally. Browser back must restore the previous step with all previously-entered values intact. Losing data on back-navigation is one of the most damaging patterns in multi-step forms
|
|
106
|
+
**Preserve state between steps** unconditionally. Browser back must restore the previous step with all previously-entered values intact. Losing data on back-navigation is one of the most damaging patterns in multi-step forms - it destroys user trust and forces re-entry of work already done. Use session storage, URL state, or in-memory state managed at the form's root level.
|
|
107
107
|
|
|
108
108
|
**Allow non-linear completion where the domain permits.** If a user wants to skip to step 3 and complete step 2 later, let them. Stepped progress indicators (like a horizontal stepper component) should be interactive links, not decorative labels, unless there is a hard dependency that makes out-of-order completion genuinely impossible.
|
|
109
109
|
|
|
110
|
-
**Summary step before final submission.** Any multi-step form that results in a consequential action
|
|
110
|
+
**Summary step before final submission.** Any multi-step form that results in a consequential action - purchase, account creation, booking - should include a review step that shows all entered values before the commit action. The CTA on the summary step should name the action ("Place order", "Create account") not just "Submit".
|
|
111
111
|
|
|
112
112
|
---
|
|
113
113
|
|
|
114
|
-
## 6. Autofill Hints
|
|
114
|
+
## 6. Autofill Hints - `autocomplete` Taxonomy
|
|
115
115
|
|
|
116
|
-
The `autocomplete` attribute tells the browser
|
|
116
|
+
The `autocomplete` attribute tells the browser - and password managers, autofill services, and mobile OS keyboards - exactly what category of data a field expects. Proper use of `autocomplete` tokens reduces form completion time by 15–30% in controlled studies, primarily because browsers and password managers can populate multiple fields in a single tap. Always pair each token with the correct `input type`.
|
|
117
117
|
|
|
118
118
|
| Token | Meaning | Recommended `input type` |
|
|
119
119
|
|---|---|---|
|
|
@@ -140,7 +140,7 @@ For address fields that are split across multiple inputs, use the sub-tokens (`a
|
|
|
140
140
|
|
|
141
141
|
## 7. Input Mode and Enter Key Hints
|
|
142
142
|
|
|
143
|
-
On mobile devices, `inputmode` controls which virtual keyboard the OS presents, independently of the input's semantic type. `enterkeyhint` controls the label shown on the Enter/Go key. Both attributes are pure UX signals
|
|
143
|
+
On mobile devices, `inputmode` controls which virtual keyboard the OS presents, independently of the input's semantic type. `enterkeyhint` controls the label shown on the Enter/Go key. Both attributes are pure UX signals - they carry no validation meaning - and they cost nothing to add.
|
|
144
144
|
|
|
145
145
|
### `inputmode` values
|
|
146
146
|
|
|
@@ -185,7 +185,7 @@ Password fields carry more UX debt than almost any other input type, largely due
|
|
|
185
185
|
|
|
186
186
|
**Show/hide toggle** is mandatory on any password field. The eye icon is the standard affordance. Both states must have accessible labels: `aria-label="Show password"` and `aria-label="Hide password"`. The toggle should be positioned as a button inside the trailing edge of the input, using `type="button"` to prevent accidental form submission.
|
|
187
187
|
|
|
188
|
-
**Never block paste** on password fields. The UK National Cyber Security Centre (NCSC) explicitly recommends against paste-blocking. The rationale is simple: blocking paste discourages the use of password managers, which in turn pushes users toward shorter, simpler, more memorable
|
|
188
|
+
**Never block paste** on password fields. The UK National Cyber Security Centre (NCSC) explicitly recommends against paste-blocking. The rationale is simple: blocking paste discourages the use of password managers, which in turn pushes users toward shorter, simpler, more memorable - and therefore weaker - passwords. Paste-blocking does not prevent any known attack vector on the server side. Remove any `onpaste="return false"` or equivalent.
|
|
189
189
|
|
|
190
190
|
**Password strength meters** belong on new-password creation flows only. Displaying a strength meter on a login form reveals information about the stored password and adds noise to a flow where speed matters. Use `zxcvbn` or an equivalent entropy-based library rather than simple length/character-class rules, which users learn to game (e.g., "Password1!" satisfies most naive rules but is trivially guessable).
|
|
191
191
|
|
|
@@ -211,18 +211,18 @@ Password fields carry more UX debt than almost any other input type, largely due
|
|
|
211
211
|
|
|
212
212
|
These three patterns all involve the user acknowledging an outcome, but they operate at different risk levels and require different interaction costs to match.
|
|
213
213
|
|
|
214
|
-
**Consent checkboxes** govern communication preferences and data use. For marketing communications, the checkbox must be unchecked by default
|
|
214
|
+
**Consent checkboxes** govern communication preferences and data use. For marketing communications, the checkbox must be unchecked by default - opt-in is required under GDPR and comparable legislation in most jurisdictions. Pre-checked opt-out is only permissible for certain transactional communications or in jurisdictions that explicitly permit it; assume opt-in required unless legal has confirmed otherwise. The checkbox label must be specific about what the user is consenting to - "I agree to receive marketing emails from Acme Ltd." rather than "I accept the terms."
|
|
215
215
|
|
|
216
|
-
**Confirmation dialogs** are appropriate for irreversible actions that affect data the user owns
|
|
216
|
+
**Confirmation dialogs** are appropriate for irreversible actions that affect data the user owns - deleting a file, cancelling a subscription, archiving a project. The confirmation dialog must state exactly what will happen. "Are you sure?" is not a confirmation. "Delete the project 'Website Redesign'? This cannot be undone." is a confirmation. The destructive action button should be visually distinct (typically red or a warning tone) and should not be the default focused element.
|
|
217
217
|
|
|
218
|
-
**Destructive-confirmation with typed phrase** applies to catastrophic, irreversible operations: deleting an account, purging all data, permanently revoking access. Require the user to type a specific phrase
|
|
218
|
+
**Destructive-confirmation with typed phrase** applies to catastrophic, irreversible operations: deleting an account, purging all data, permanently revoking access. Require the user to type a specific phrase - the account name, the word "DELETE", the number of items being erased - before the action becomes available. This pattern exists not to prevent accidents but to ensure the user is making a conscious, deliberate decision. GitHub, Heroku, and most infrastructure tools use this pattern for account deletion. The required phrase should be shown plainly; do not require the user to guess it.
|
|
219
219
|
|
|
220
220
|
| Risk level | Pattern | Example |
|
|
221
221
|
|---|---|---|
|
|
222
|
-
| Low
|
|
223
|
-
| Medium
|
|
224
|
-
| High
|
|
225
|
-
| Critical
|
|
222
|
+
| Low - reversible | None needed | Moving a file to trash |
|
|
223
|
+
| Medium - data loss possible | Confirmation dialog with specific text | Deleting a saved draft |
|
|
224
|
+
| High - irreversible | Dialog + destructive button style | Cancelling a paid subscription |
|
|
225
|
+
| Critical - catastrophic | Dialog + type-to-confirm phrase | Deleting an account and all its data |
|
|
226
226
|
|
|
227
227
|
---
|
|
228
228
|
|
|
@@ -232,9 +232,9 @@ CAPTCHA presents a genuine tension: friction added to stop bots is friction expe
|
|
|
232
232
|
|
|
233
233
|
**Invisible CAPTCHA v3** (Google reCAPTCHA v3, Cloudflare Turnstile, hCaptcha Invisible) is the preferred approach. These services score user behaviour in the background and surface a challenge only when the risk score exceeds a threshold. Typical deployments have challenge rates under 1% for real users. There is no visible puzzle, no audio fallback needed for the happy path.
|
|
234
234
|
|
|
235
|
-
**When a visible challenge is unavoidable**, WCAG 1.1.1 requires a non-visual alternative. Every visual CAPTCHA must have an audio alternative that is solvable with a screen reader and no visual ability. Text-based CAPTCHAs distorted beyond normal variance effectively exclude users with dyslexia and cognitive disabilities
|
|
235
|
+
**When a visible challenge is unavoidable**, WCAG 1.1.1 requires a non-visual alternative. Every visual CAPTCHA must have an audio alternative that is solvable with a screen reader and no visual ability. Text-based CAPTCHAs distorted beyond normal variance effectively exclude users with dyslexia and cognitive disabilities - treat this as a legal and ethical risk, not just a UX inconvenience.
|
|
236
236
|
|
|
237
|
-
**Time-limited CAPTCHAs**
|
|
237
|
+
**Time-limited CAPTCHAs** - those that expire while a slow or motor-impaired user is completing them - are inaccessible by design and should never be used. If a CAPTCHA session can time out, the user must be able to request a refresh without losing their form data.
|
|
238
238
|
|
|
239
239
|
**Progressive trust** is the most human approach for authenticated flows. A logged-in user with a verified email and account history presents a negligible bot risk. Do not require CAPTCHA from authenticated users unless there is a specific elevated-risk action (e.g., a bulk API call or mass send). Reserve CAPTCHA friction for unauthenticated, rate-limited, or anomalous-behaviour scenarios.
|
|
240
240
|
|
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
# Framer Motion Patterns
|
|
4
4
|
|
|
5
|
-
Framer Motion is the standard animation library for React. It abstracts the browser's animation primitives into a declarative API that stays out of your way for common cases while exposing full physics-based control when you need it. This reference covers implementation patterns
|
|
5
|
+
Framer Motion is the standard animation library for React. It abstracts the browser's animation primitives into a declarative API that stays out of your way for common cases while exposing full physics-based control when you need it. This reference covers implementation patterns - not just the API, but _why_ each pattern exists and when to apply it.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
## 1. Basics
|
|
9
|
+
## 1. Basics - motion components
|
|
10
10
|
|
|
11
11
|
Any HTML (or SVG) element can become a motion component by prefixing it with `motion.`. The most common variants are `motion.div`, `motion.span`, `motion.button`, and `motion.li`, but `motion.section`, `motion.a`, `motion.img`, and any other HTML element follow the same pattern.
|
|
12
12
|
|
|
13
13
|
The four primary animation props are:
|
|
14
14
|
|
|
15
|
-
- **`initial`**
|
|
16
|
-
- **`animate`**
|
|
17
|
-
- **`exit`**
|
|
18
|
-
- **`transition`**
|
|
15
|
+
- **`initial`** - the state the element starts in before it mounts (or before a transition begins). Without `initial`, the element won't animate _from_ anything - it'll just be in the `animate` state on mount. Always provide `initial` when you want an entrance animation.
|
|
16
|
+
- **`animate`** - the state the element should animate _to_. Framer drives the element toward this state whenever it changes.
|
|
17
|
+
- **`exit`** - the state the element animates _to_ when it unmounts. Requires `<AnimatePresence>` as a parent - see Section 3.
|
|
18
|
+
- **`transition`** - controls how the animation happens (spring, tween, duration, ease). If omitted, Framer applies a spring by default for layout/positional changes and a tween for opacity.
|
|
19
19
|
|
|
20
20
|
A basic fade-in example:
|
|
21
21
|
|
|
@@ -35,17 +35,17 @@ function FadeIn({ children }: { children: React.ReactNode }) {
|
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
The slight `y: 8` offset on entry gives the element a sense of emerging from below
|
|
38
|
+
The slight `y: 8` offset on entry gives the element a sense of emerging from below - a common, subtle entrance pattern. The `easeOut` ease starts fast and slows at the end, which feels responsive.
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
42
|
## 2. Spring vs. Tween Configuration
|
|
43
43
|
|
|
44
|
-
Framer Motion supports two fundamentally different animation models. Choosing between them is not arbitrary
|
|
44
|
+
Framer Motion supports two fundamentally different animation models. Choosing between them is not arbitrary - each has a domain where it clearly wins.
|
|
45
45
|
|
|
46
46
|
### Spring physics (preferred for UI motion)
|
|
47
47
|
|
|
48
|
-
Springs model the physics of a real spring: the element overshoots slightly and settles. This is why spring motion _feels natural_
|
|
48
|
+
Springs model the physics of a real spring: the element overshoots slightly and settles. This is why spring motion _feels natural_ - real objects in the world have inertia and settle under physical forces. For UI, spring motion communicates responsiveness and quality.
|
|
49
49
|
|
|
50
50
|
`type: "spring"` is Framer's default for layout animations and positional changes. Key parameters:
|
|
51
51
|
|
|
@@ -54,10 +54,10 @@ Springs model the physics of a real spring: the element overshoots slightly and
|
|
|
54
54
|
- **`mass`** (0.5–2): adds inertia. Higher mass makes the element feel heavier and slower to respond.
|
|
55
55
|
|
|
56
56
|
The relationship that matters in practice:
|
|
57
|
-
- **High stiffness + high damping = snappy**
|
|
58
|
-
- **Low stiffness + low damping = bouncy**
|
|
57
|
+
- **High stiffness + high damping = snappy** - fast arrival, no bounce. This is the production UI default.
|
|
58
|
+
- **Low stiffness + low damping = bouncy** - never use in production UI. Bounce feels playful and toy-like, which is wrong for most product contexts.
|
|
59
59
|
|
|
60
|
-
**Hard constraint: `bounce: 0` always for icon cross-fades and micro-interactions.** The `bounce` shorthand parameter is a convenience alias
|
|
60
|
+
**Hard constraint: `bounce: 0` always for icon cross-fades and micro-interactions.** The `bounce` shorthand parameter is a convenience alias - setting it to 0 ensures no oscillation. "Bounce must be zero" is a non-negotiable rule for any interaction that fires frequently or in information-dense UI.
|
|
61
61
|
|
|
62
62
|
Recommended production preset:
|
|
63
63
|
|
|
@@ -69,16 +69,16 @@ This is snappy and clean. It arrives fast and settles without any visible oscill
|
|
|
69
69
|
|
|
70
70
|
### Tween (for duration-controlled, eased animations)
|
|
71
71
|
|
|
72
|
-
Tween animations run over a fixed duration with a specified easing curve. Use them when exact timing matters
|
|
72
|
+
Tween animations run over a fixed duration with a specified easing curve. Use them when exact timing matters - opacity fades, color transitions, anything where you need predictable, duration-controlled behavior rather than physics.
|
|
73
73
|
|
|
74
74
|
```tsx
|
|
75
75
|
transition={{ type: 'tween', duration: 0.2, ease: 'easeOut' }}
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
Ease guidance:
|
|
79
|
-
- **`"easeOut"`** for entrances
|
|
80
|
-
- **`"easeIn"`** for exits
|
|
81
|
-
- **`"easeInOut"`** for emphasis transitions
|
|
79
|
+
- **`"easeOut"`** for entrances - starts fast (feels responsive), decelerates to rest.
|
|
80
|
+
- **`"easeIn"`** for exits - accelerates away, gets out of the way quickly.
|
|
81
|
+
- **`"easeInOut"`** for emphasis transitions - smooth acceleration and deceleration, used when the same element transitions between states (not entering/exiting).
|
|
82
82
|
|
|
83
83
|
The rule of thumb: use springs for movement and scale; use tweens for opacity, color, and blur.
|
|
84
84
|
|
|
@@ -86,7 +86,7 @@ The rule of thumb: use springs for movement and scale; use tweens for opacity, c
|
|
|
86
86
|
|
|
87
87
|
## 3. AnimatePresence
|
|
88
88
|
|
|
89
|
-
`AnimatePresence` is the component that enables exit animations. Without it, React unmounts components immediately, and `exit` props are never executed
|
|
89
|
+
`AnimatePresence` is the component that enables exit animations. Without it, React unmounts components immediately, and `exit` props are never executed - the element simply vanishes.
|
|
90
90
|
|
|
91
91
|
```tsx
|
|
92
92
|
import { AnimatePresence, motion } from 'framer-motion'
|
|
@@ -116,13 +116,13 @@ Every child of `AnimatePresence` that will be conditionally rendered **must have
|
|
|
116
116
|
|
|
117
117
|
The `mode` prop controls how entering and exiting elements interact during a transition:
|
|
118
118
|
|
|
119
|
-
- **`mode: "wait"`**
|
|
120
|
-
- **`mode: "sync"`**
|
|
121
|
-
- **`mode: "popLayout"`**
|
|
119
|
+
- **`mode: "wait"`** - the exiting element completes its exit animation fully before the entering element begins its entrance. Use this for route transitions and tab panel swaps, where showing two elements simultaneously would be confusing.
|
|
120
|
+
- **`mode: "sync"`** - enter and exit animations run simultaneously. Use this when you're swapping UI elements (like icon cross-fades) and want both transitions to happen at once.
|
|
121
|
+
- **`mode: "popLayout"`** - the exiting element immediately pops out of the document flow so surrounding elements can animate into their new positions right away, while the exiting element still plays its exit animation. Ideal for list item removal.
|
|
122
122
|
|
|
123
123
|
### Critical rule: `<AnimatePresence initial={false}>`
|
|
124
124
|
|
|
125
|
-
When `AnimatePresence` wraps persistent UI that already exists on first render
|
|
125
|
+
When `AnimatePresence` wraps persistent UI that already exists on first render - like a tab panel that's visible on page load, or a sidebar that's open by default - you must pass `initial={false}`:
|
|
126
126
|
|
|
127
127
|
```tsx
|
|
128
128
|
<AnimatePresence initial={false}>
|
|
@@ -134,13 +134,13 @@ When `AnimatePresence` wraps persistent UI that already exists on first render
|
|
|
134
134
|
</AnimatePresence>
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
-
Without `initial={false}`, every component inside `AnimatePresence` will play its entrance animation on first mount, even when the user didn't trigger it. This is jarring and wrong
|
|
137
|
+
Without `initial={false}`, every component inside `AnimatePresence` will play its entrance animation on first mount, even when the user didn't trigger it. This is jarring and wrong - **never animate on initial load for existing UI**.
|
|
138
138
|
|
|
139
139
|
---
|
|
140
140
|
|
|
141
141
|
## 4. Layout Animations
|
|
142
142
|
|
|
143
|
-
The `layout` prop is one of Framer Motion's most powerful features. Add `layout` to a `motion` component, and Framer automatically detects when the component's position or size changes in the DOM and animates it from the old layout to the new one
|
|
143
|
+
The `layout` prop is one of Framer Motion's most powerful features. Add `layout` to a `motion` component, and Framer automatically detects when the component's position or size changes in the DOM and animates it from the old layout to the new one - even if the change was caused by other elements shifting around it.
|
|
144
144
|
|
|
145
145
|
```tsx
|
|
146
146
|
<motion.div layout className="card">
|
|
@@ -150,7 +150,7 @@ The `layout` prop is one of Framer Motion's most powerful features. Add `layout`
|
|
|
150
150
|
|
|
151
151
|
When `isExpanded` changes, Framer measures the old and new layouts and smoothly animates the transition. This works for position changes caused by sibling elements reordering, parent resizing, or content toggling.
|
|
152
152
|
|
|
153
|
-
**`layoutId`
|
|
153
|
+
**`layoutId` - shared element transitions:** Assign the same `layoutId` to two different components, and Framer will morph between them when one unmounts and the other mounts. This is the canonical implementation for expanding card → detail transitions and hero → full-view animations.
|
|
154
154
|
|
|
155
155
|
```tsx
|
|
156
156
|
// Card in list view
|
|
@@ -160,7 +160,7 @@ When `isExpanded` changes, Framer measures the old and new layouts and smoothly
|
|
|
160
160
|
<motion.img layoutId={`product-image-${id}`} src={fullImage} />
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
-
When the list-view card unmounts and the modal mounts, Framer animates the image from its list-view position to its modal position. The two components don't need to coexist
|
|
163
|
+
When the list-view card unmounts and the modal mounts, Framer animates the image from its list-view position to its modal position. The two components don't need to coexist - the transition bridges the gap.
|
|
164
164
|
|
|
165
165
|
**Important:** ensure only one component with a given `layoutId` is mounted at a time. Two simultaneously mounted components with the same `layoutId` will fight each other and produce broken animations.
|
|
166
166
|
|
|
@@ -206,9 +206,9 @@ function AnimatedList({ items }) {
|
|
|
206
206
|
}
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
-
**Propagation:** When a parent has `variants` and `animate="visible"`, child `motion` components that also have `variants` will automatically receive the same `animate` value
|
|
209
|
+
**Propagation:** When a parent has `variants` and `animate="visible"`, child `motion` components that also have `variants` will automatically receive the same `animate` value - they don't need their own `animate` prop. Framer propagates the state name down the tree.
|
|
210
210
|
|
|
211
|
-
**`staggerChildren`** delays each child's animation start by the specified seconds after the previous child. A value of `0.05` means each item enters 50ms after the previous
|
|
211
|
+
**`staggerChildren`** delays each child's animation start by the specified seconds after the previous child. A value of `0.05` means each item enters 50ms after the previous - the standard for list entrance animations. More than `0.08` seconds starts to feel slow; more than 6–8 items should stagger in parallel (`staggerChildren` + `staggerDirection` with a cap).
|
|
212
212
|
|
|
213
213
|
**`delayChildren`** adds an initial delay before the first child begins, which gives the parent time to render visibly before its children start entering.
|
|
214
214
|
|
|
@@ -218,7 +218,7 @@ function AnimatedList({ items }) {
|
|
|
218
218
|
|
|
219
219
|
Framer Motion provides props that animate elements in response to user gestures without requiring event handlers or state.
|
|
220
220
|
|
|
221
|
-
**`whileHover`:** The animation state while the pointer is hovering. Prefer subtle transforms
|
|
221
|
+
**`whileHover`:** The animation state while the pointer is hovering. Prefer subtle transforms - `scale: 1.02` for a gentle grow or `y: -2` for a subtle lift. Avoid large scale values (> 1.05) on UI elements; they feel unstable.
|
|
222
222
|
|
|
223
223
|
```tsx
|
|
224
224
|
<motion.button whileHover={{ scale: 1.02 }} transition={{ type: 'spring', stiffness: 400, damping: 30 }}>
|
|
@@ -238,7 +238,7 @@ Framer Motion provides props that animate elements in response to user gestures
|
|
|
238
238
|
</motion.button>
|
|
239
239
|
```
|
|
240
240
|
|
|
241
|
-
**`drag` + `dragConstraints`:** Enable drag with `drag` (either `true`, `"x"`, or `"y"`), and bound the drag region with `dragConstraints`. Use `dragElastic: 0.1` for a subtle resistance when dragging beyond the constraint boundaries
|
|
241
|
+
**`drag` + `dragConstraints`:** Enable drag with `drag` (either `true`, `"x"`, or `"y"`), and bound the drag region with `dragConstraints`. Use `dragElastic: 0.1` for a subtle resistance when dragging beyond the constraint boundaries - this gives physical feedback that the user has reached an edge.
|
|
242
242
|
|
|
243
243
|
```tsx
|
|
244
244
|
<motion.div
|
|
@@ -289,13 +289,13 @@ Use `style={{ y }}` (not `animate`) for scroll-linked values because `animate` c
|
|
|
289
289
|
</motion.section>
|
|
290
290
|
```
|
|
291
291
|
|
|
292
|
-
**`viewport={{ once: true }}`** is the preferred option for entrance animations
|
|
292
|
+
**`viewport={{ once: true }}`** is the preferred option for entrance animations - the element animates in once and stays visible. Without `once: true`, the element will animate every time it enters the viewport, which is usually wrong for entrance effects (and can feel janky during fast scrolling).
|
|
293
293
|
|
|
294
294
|
---
|
|
295
295
|
|
|
296
296
|
## 8. prefers-reduced-motion Compliance
|
|
297
297
|
|
|
298
|
-
Respecting `prefers-reduced-motion` is **mandatory** for accessibility compliance. Some users have vestibular disorders or motion sensitivity
|
|
298
|
+
Respecting `prefers-reduced-motion` is **mandatory** for accessibility compliance. Some users have vestibular disorders or motion sensitivity - for them, unnecessary motion causes real physical discomfort. This is not optional polish; it is a WCAG 2.1 requirement.
|
|
299
299
|
|
|
300
300
|
### Per-component approach with `useReducedMotion`
|
|
301
301
|
|
|
@@ -321,11 +321,11 @@ function AnimatedCard() {
|
|
|
321
321
|
}
|
|
322
322
|
```
|
|
323
323
|
|
|
324
|
-
When `prefersReducedMotion` is true, set `duration: 0` and remove any positional animation values
|
|
324
|
+
When `prefersReducedMotion` is true, set `duration: 0` and remove any positional animation values - the element should appear instantly without motion.
|
|
325
325
|
|
|
326
326
|
### App-wide approach with `MotionConfig` (preferred)
|
|
327
327
|
|
|
328
|
-
The cleanest solution for most applications is to wrap the app root with `MotionConfig` using `reducedMotion: "user"`. This instructs Framer to automatically apply reduced-motion behavior for all motion components in the subtree when the OS preference is set
|
|
328
|
+
The cleanest solution for most applications is to wrap the app root with `MotionConfig` using `reducedMotion: "user"`. This instructs Framer to automatically apply reduced-motion behavior for all motion components in the subtree when the OS preference is set - no per-component logic needed.
|
|
329
329
|
|
|
330
330
|
```tsx
|
|
331
331
|
import { MotionConfig } from 'framer-motion'
|
|
@@ -339,18 +339,18 @@ function App() {
|
|
|
339
339
|
}
|
|
340
340
|
```
|
|
341
341
|
|
|
342
|
-
`reducedMotion: "user"` reads the OS setting via `prefers-reduced-motion` media query and disables animations globally when it's set. This is the preferred approach because it's zero-maintenance
|
|
342
|
+
`reducedMotion: "user"` reads the OS setting via `prefers-reduced-motion` media query and disables animations globally when it's set. This is the preferred approach because it's zero-maintenance - adding new animated components automatically inherits the behavior.
|
|
343
343
|
|
|
344
344
|
---
|
|
345
345
|
|
|
346
346
|
## 9. 60fps Performance Rules
|
|
347
347
|
|
|
348
|
-
Animation performance on the web comes down to a single principle: **only animate properties that the browser can handle on the GPU compositor thread**. Everything else requires the browser to recalculate layout or repaint pixels
|
|
348
|
+
Animation performance on the web comes down to a single principle: **only animate properties that the browser can handle on the GPU compositor thread**. Everything else requires the browser to recalculate layout or repaint pixels - work that happens on the main thread and causes dropped frames.
|
|
349
349
|
|
|
350
350
|
### Properties that are GPU-safe (always use these)
|
|
351
351
|
|
|
352
|
-
- `x`, `y`
|
|
353
|
-
- `scale`, `scaleX`, `scaleY`
|
|
352
|
+
- `x`, `y` - map to `translateX()` and `translateY()` in `transform`
|
|
353
|
+
- `scale`, `scaleX`, `scaleY` - map to `scale()` in `transform`
|
|
354
354
|
- `rotate`, `rotateX`, `rotateY`, `rotateZ`
|
|
355
355
|
- `skewX`, `skewY`
|
|
356
356
|
- `opacity`
|
|
@@ -359,17 +359,17 @@ These properties run on the compositor thread and never block the main thread, r
|
|
|
359
359
|
|
|
360
360
|
### Properties that cause jank (never animate these)
|
|
361
361
|
|
|
362
|
-
- `width`, `height`
|
|
363
|
-
- `margin`, `padding`
|
|
362
|
+
- `width`, `height` - triggers layout recalculation on every frame
|
|
363
|
+
- `margin`, `padding` - shifts surrounding elements, triggers full reflow
|
|
364
364
|
- `border-width`
|
|
365
|
-
- `left`, `top`, `right`, `bottom` (on positioned elements)
|
|
366
|
-
- `font-size`
|
|
365
|
+
- `left`, `top`, `right`, `bottom` (on positioned elements) - triggers layout
|
|
366
|
+
- `font-size` - triggers layout and repaint
|
|
367
367
|
|
|
368
|
-
If you need to animate a size change, use `scale` on a wrapper. If you need to animate position, use `x`/`y` rather than `left`/`top`. This distinction is why Framer's layout animation system (Section 4) works
|
|
368
|
+
If you need to animate a size change, use `scale` on a wrapper. If you need to animate position, use `x`/`y` rather than `left`/`top`. This distinction is why Framer's layout animation system (Section 4) works - it uses `transform` internally even when responding to layout-driven position changes.
|
|
369
369
|
|
|
370
370
|
### `will-change: transform`
|
|
371
371
|
|
|
372
|
-
Only add `will-change: transform` when you observe a first-frame stutter on a specific element. Do not add it preemptively
|
|
372
|
+
Only add `will-change: transform` when you observe a first-frame stutter on a specific element. Do not add it preemptively - it forces the browser to allocate a separate GPU layer for the element immediately, consuming GPU memory whether or not the animation is actually playing. Reserve it for elements with known performance issues after profiling.
|
|
373
373
|
|
|
374
374
|
---
|
|
375
375
|
|
|
@@ -377,9 +377,9 @@ Only add `will-change: transform` when you observe a first-frame stutter on a sp
|
|
|
377
377
|
|
|
378
378
|
`MotionConfig` is a context provider that configures all `motion` components within its subtree. Use it for:
|
|
379
379
|
|
|
380
|
-
- **Reduced motion compliance**
|
|
381
|
-
- **Global transition defaults**
|
|
382
|
-
- **Custom ease functions**
|
|
380
|
+
- **Reduced motion compliance** - as shown in Section 8, `reducedMotion: "user"` is the cleanest global solution.
|
|
381
|
+
- **Global transition defaults** - set a default transition for all motion components so individual components don't need to repeat the same `transition` prop.
|
|
382
|
+
- **Custom ease functions** - define a custom cubic-bezier ease once and reference it by name throughout the tree.
|
|
383
383
|
|
|
384
384
|
```tsx
|
|
385
385
|
<MotionConfig
|
|
@@ -398,14 +398,14 @@ With this configuration, every `motion` component that doesn't specify its own `
|
|
|
398
398
|
|
|
399
399
|
These are the mistakes that appear most frequently in codebases using Framer Motion:
|
|
400
400
|
|
|
401
|
-
**Missing `initial` with `animate`:** If you add `animate={{ opacity: 1 }}` without `initial={{ opacity: 0 }}`, the element is already at opacity 1 on mount
|
|
401
|
+
**Missing `initial` with `animate`:** If you add `animate={{ opacity: 1 }}` without `initial={{ opacity: 0 }}`, the element is already at opacity 1 on mount - there's nothing to animate from. Always pair `animate` with `initial` when you want an entrance effect.
|
|
402
402
|
|
|
403
403
|
**Missing `key` props in `AnimatePresence`:** Exit animations will silently not fire if children of `AnimatePresence` don't have `key` props. Framer can't identify which element is leaving without a stable key.
|
|
404
404
|
|
|
405
405
|
**Variant propagation only reaches `motion` children:** Parent variants propagate to child `motion` components, but not to plain HTML children or non-motion React components. If a list item is a plain `<li>` instead of `<motion.li>`, it won't receive the parent's variant orchestration.
|
|
406
406
|
|
|
407
|
-
**`layoutId` conflicts:** If two components with the same `layoutId` are both mounted simultaneously
|
|
407
|
+
**`layoutId` conflicts:** If two components with the same `layoutId` are both mounted simultaneously - even briefly during a transition - Framer doesn't know which is the source and which is the target. The result is erratic, broken animation. Ensure mutual exclusivity: when one mounts, the other must have already unmounted.
|
|
408
408
|
|
|
409
|
-
**Unnecessary `AnimatePresence` nesting:** Nesting `AnimatePresence` inside another `AnimatePresence` complicates exit orchestration
|
|
409
|
+
**Unnecessary `AnimatePresence` nesting:** Nesting `AnimatePresence` inside another `AnimatePresence` complicates exit orchestration - inner exits may not complete before outer exits begin. Keep the tree flat; use a single `AnimatePresence` at the appropriate level.
|
|
410
410
|
|
|
411
411
|
**Wrapping everything in `motion.div`:** `motion.div` carries a slightly larger bundle footprint than a plain `div` because it registers the element with Framer's animation engine. Don't wrap static, unanimated elements. Only wrap elements that actually need animation. Reserve `motion.*` for elements where animation provides genuine value.
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# GDD Runtime Static Security Audit
|
|
2
2
|
|
|
3
|
-
**Phase:** 33.5
|
|
3
|
+
**Phase:** 33.5 - GDD Runtime Security Hardening · **Plan:** 33.5-02 (Wave A) · **Date:** 2026-05-31
|
|
4
4
|
**Reference:** branch `phase/33-5-runtime-security`
|
|
5
|
-
**Scope:** the SHIPPED runtime surface
|
|
5
|
+
**Scope:** the SHIPPED runtime surface - `hooks/`, `scripts/`, `sdk/`, `bin/`.
|
|
6
6
|
**Method:** static enumeration (read-only source inspection, no execution, no network).
|
|
7
7
|
**Companions:**
|
|
8
8
|
|
|
9
|
-
- `scripts/security/outbound-allowlist.json`
|
|
9
|
+
- `scripts/security/outbound-allowlist.json` - the machine-readable, CANONICAL active-egress
|
|
10
10
|
allowlist that Phase 33.5-04's `scripts/scan-outbound-network.cjs` gate `JSON.parse`s (this
|
|
11
11
|
report is its human-readable rationale).
|
|
12
|
-
- `reference/gdd-threat-model.md`
|
|
12
|
+
- `reference/gdd-threat-model.md` - the STRIDE threat model (Phase 33.5-01).
|
|
13
13
|
|
|
14
14
|
This audit freezes the egress / secret / external-input picture so any *future* surface that is
|
|
15
15
|
not pre-approved trips the 33.5-04 gate at CI time. It is grounded in the CONTEXT real-tree sweep
|
|
@@ -21,10 +21,10 @@ not pre-approved trips the 33.5-04 gate at CI time. It is grounded in the CONTEX
|
|
|
21
21
|
|
|
22
22
|
GDD's runtime makes outbound calls (or local IPC/server binds) from exactly the sites below. Each
|
|
23
23
|
maps to an `outbound-allowlist.json` directory glob (see the **Allowlisted** column). The 33.5-04
|
|
24
|
-
gate is **ACTIVE-egress-only**
|
|
24
|
+
gate is **ACTIVE-egress-only** - it matches `require`/`import` of `node:http(s)`/`http(s)`/`ws`/
|
|
25
25
|
`node-fetch`/`axios`/`undici`, `fetch(`, `http(s).get/.request(`, `new WebSocket`/`new
|
|
26
26
|
WebSocketServer`/`WebSocketServer(`, and child-process `spawn`/`spawnSync`/`exec*` of
|
|
27
|
-
`gh|curl|wget|nc|ssh|scp`
|
|
27
|
+
`gh|curl|wget|nc|ssh|scp` - so a bare URL string alone never trips it.
|
|
28
28
|
|
|
29
29
|
| File | What it calls | Transport | Legitimacy | Allowlisted by |
|
|
30
30
|
| --- | --- | --- | --- | --- |
|
|
@@ -40,7 +40,7 @@ WebSocketServer`/`WebSocketServer(`, and child-process `spawn`/`spawnSync`/`exec
|
|
|
40
40
|
| `scripts/e2e/run-headless.ts` | live Anthropic API run | REST (key-gated) | Test infrastructure only; gated on `ANTHROPIC_API_KEY` + main-branch; never in default `npm test` | `scripts/e2e/**` |
|
|
41
41
|
| `scripts/lib/authority-watcher/index.cjs` | authority-feed classification of already-fetched records | (delegated) | The live article fetch is delegated to `agents/design-authority-watcher.md`; `index.cjs` is the pure-CommonJS classifier. Allowlisted so any direct feed fetch added here stays pre-approved | `scripts/lib/authority-watcher/**` |
|
|
42
42
|
|
|
43
|
-
**Documented but NOT hard-gated** (the scanner scope is `.js`-family + active-egress only
|
|
43
|
+
**Documented but NOT hard-gated** (the scanner scope is `.js`-family + active-egress only -
|
|
44
44
|
these cannot exfiltrate on their own and are covered by gitleaks + the threat model):
|
|
45
45
|
|
|
46
46
|
| File / surface | What it is | Why not gated |
|
|
@@ -50,8 +50,8 @@ these cannot exfiltrate on their own and are covered by gitleaks + the threat mo
|
|
|
50
50
|
| `scripts/lib/install/merge.cjs` (line 85) | "Plugin repository: https://github.com/hegemonart/get-design-done" printed string | bare URL string, no call |
|
|
51
51
|
| `scripts/lib/issue-reporter/destination.cjs` (lines 29–30) | frozen `DESTINATION_URL` / `ISSUE_TEMPLATE_URL` constants | constants consumed only via the `gh` spawn above |
|
|
52
52
|
| `scripts/lint-agentskills-spec.cjs` (line 76) | spec-example URL in a comment | bare URL string, no call |
|
|
53
|
-
| `hooks/update-check.sh` | shell update-check egress | shell script
|
|
54
|
-
| `scripts/bootstrap.sh` | shell bootstrap egress | shell script
|
|
53
|
+
| `hooks/update-check.sh` | shell update-check egress | shell script - outside the `.{js,cjs,mjs,ts}` scanner scope |
|
|
54
|
+
| `scripts/bootstrap.sh` | shell bootstrap egress | shell script - outside the `.{js,cjs,mjs,ts}` scanner scope |
|
|
55
55
|
|
|
56
56
|
---
|
|
57
57
|
|
|
@@ -64,7 +64,7 @@ Where a token/key/credential is read, forwarded, or scrubbed across the shipped
|
|
|
64
64
|
| `scripts/lib/redact.cjs` | PEM, JWT, Anthropic `sk-ant-`, Stripe `sk_live_`, Slack `xox*`, GitHub `ghp_`, AWS `AKIA`, generic `sk-` (8 patterns) | **scrub** | Deep-walks event-stream payloads at serialize time so everything that hits disk / a bus subscriber is `[REDACTED:<type>]`. **D-07 extends** with Gemini `AIza…`, GitHub fine-grained `github_pat_`, and GitHub `ghs_/gho_/ghu_/ghr_` (currently uncovered). |
|
|
65
65
|
| `scripts/lib/peer-cli/acp-client.cjs` (line 102) | full `process.env` | **forward** (default) | `const env = opts.env … : process.env` → GDD's `ANTHROPIC_API_KEY`/`GH_TOKEN`/`GDD_*` leak to a spawned peer when `opts.env` is absent. **Fixed by 33.5-04** (allowlist-forward, default-deny, shared `sanitize-env`). |
|
|
66
66
|
| `scripts/lib/peer-cli/asp-client.cjs` (line 122) | full `process.env` | **forward** (default) | Same default-inherit gap; same 33.5-04 fix. |
|
|
67
|
-
| `scripts/lib/figma-extract/pull.cjs` | `FIGMA_TOKEN` / `FIGMA_PERSONAL_ACCESS_TOKEN` | **read** | Lives only inside caller-provided `headers`; NEVER written to disk, NEVER logged (diagnostics read a short body prefix, never the token
|
|
67
|
+
| `scripts/lib/figma-extract/pull.cjs` | `FIGMA_TOKEN` / `FIGMA_PERSONAL_ACCESS_TOKEN` | **read** | Lives only inside caller-provided `headers`; NEVER written to disk, NEVER logged (diagnostics read a short body prefix, never the token - D-10 of the figma sub-plan). |
|
|
68
68
|
| `scripts/lib/issue-reporter/gh-submit.cjs` / `dedup.cjs` | none held by GDD | **delegate** | Authentication is `gh`'s own logged-in credential store; GDD never reads or forwards a GitHub token for these spawns. |
|
|
69
69
|
|
|
70
70
|
---
|
|
@@ -77,32 +77,32 @@ Where untrusted data crosses a trust boundary into GDD's runtime.
|
|
|
77
77
|
| --- | --- | --- | --- |
|
|
78
78
|
| WebSocket upgrade request | a remote client connecting to the event-stream WS | `scripts/lib/transports/ws.cjs` HTTP `upgrade` handler | Bearer-token gate (≥8 chars) already ships; **33.5-03** adds 127.0.0.1-default bind + timing-safe compare so the default config does not expose `0.0.0.0`. |
|
|
79
79
|
| gdd-state MCP tool inputs | the MCP client / model | `sdk/mcp/gdd-state/tools/*.ts` (11 tools) + `tools/shared.ts` | Each tool has a JSON schema under `sdk/mcp/gdd-state/schemas/`; **33.5-08** tightens them (`additionalProperties:false` + `maxLength`) and adds a payload-size cap (JSON-bomb guard). |
|
|
80
|
-
| `GDD_STATE_PATH` env override | the launching environment | `sdk/mcp/gdd-state/tools/shared.ts:resolveStatePath()` (line 60–61) | `process.env['GDD_STATE_PATH'] ?? .design/STATE.md` with **no path-traversal guard** today
|
|
80
|
+
| `GDD_STATE_PATH` env override | the launching environment | `sdk/mcp/gdd-state/tools/shared.ts:resolveStatePath()` (line 60–61) | `process.env['GDD_STATE_PATH'] ?? .design/STATE.md` with **no path-traversal guard** today - `..`/absolute-outside escape is unchecked. **Closed by 33.5-08** (resolve + assert within project root / `.design/`). |
|
|
81
81
|
| `.design/config.json` | a repo-local config file (potentially attacker-influenced in a malicious clone) | 14 modules incl. `scripts/lib/peer-cli/registry.cjs` (line 154) | Drives `peer_cli.enabled_peers` / (33.5) `peer_cli.env_allowlist`, the WS `event_stream.bind_host`, and the issue-reporter kill-switch. Parsed defensively; opt-in by design (a peer must be explicitly enabled). |
|
|
82
|
-
| peer child `stdout` | a spawned LOCAL peer CLI | `scripts/lib/peer-cli/acp-client.cjs` (JSON-RPC frame parser) | A malicious/buggy peer could flood stdout
|
|
82
|
+
| peer child `stdout` | a spawned LOCAL peer CLI | `scripts/lib/peer-cli/acp-client.cjs` (JSON-RPC frame parser) | A malicious/buggy peer could flood stdout - **already capped at 16 MiB un-newlined** (DoS guard). JSON-RPC frames are parsed, not `eval`'d. |
|
|
83
83
|
|
|
84
84
|
---
|
|
85
85
|
|
|
86
86
|
## Finding
|
|
87
87
|
|
|
88
88
|
The legitimate active-egress set is fully enumerated above and **no raw unexpected egress was
|
|
89
|
-
found**
|
|
89
|
+
found** - every outbound call, server bind, or child spawn maps to one of six trusted modules,
|
|
90
90
|
each frozen into `scripts/security/outbound-allowlist.json` with a justification. The allowlist
|
|
91
91
|
uses **directory globs** so a new helper in an already-trusted module (e.g. another
|
|
92
92
|
`figma-extract` file) does not silently trip the gate, and every glob is asserted by
|
|
93
|
-
`test/suite/phase-33-5-audit.test.cjs` to resolve to ≥1 real file
|
|
93
|
+
`test/suite/phase-33-5-audit.test.cjs` to resolve to ≥1 real file - so the 33.5-04 gate cannot be
|
|
94
94
|
defeated by a stale entry that matches nothing.
|
|
95
95
|
|
|
96
96
|
The residual gaps surfaced here are **closed by the remaining Phase 33.5 plans**:
|
|
97
97
|
|
|
98
|
-
- **33.5-03**
|
|
98
|
+
- **33.5-03** - WebSocket bind hardening: 127.0.0.1 default bind (no more `0.0.0.0`), opt-in remote
|
|
99
99
|
via `event_stream.bind_host`, and a timing-safe (`crypto.timingSafeEqual`) token compare. Closes
|
|
100
100
|
the WS-upgrade external-input row.
|
|
101
|
-
- **33.5-04**
|
|
101
|
+
- **33.5-04** - Outbound-network static CI gate: `scripts/scan-outbound-network.cjs` +
|
|
102
102
|
`npm run scan:outbound`, consuming THIS plan's `outbound-allowlist.json`. Also lands the
|
|
103
103
|
peer-CLI env-allowlist sandbox (shared `sanitize-env`) that closes the `acp-client.cjs` /
|
|
104
104
|
`asp-client.cjs` full-`process.env` forward rows.
|
|
105
|
-
- **33.5-05** (and 33.5-07/08)
|
|
105
|
+
- **33.5-05** (and 33.5-07/08) - secret-scan extension (Gemini + GitHub fine-grained/server
|
|
106
106
|
tokens) with a synthetic-secret fuzz, the gdd-state path-traversal guard + payload cap +
|
|
107
107
|
tightened schemas, `SECURITY.md`, and the regression baseline.
|
|
108
108
|
|