voidmachine 0.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +123 -4
- package/bin/void-harness.mjs +12 -0
- package/bin/void-machine.mjs +12 -0
- package/core-assets/.claude-plugin/plugin.json +253 -0
- package/core-assets/.gitkeep +0 -0
- package/core-assets/PHILOSOPHY.md +218 -0
- package/core-assets/PROJECT-DOCTRINE.template.md +14 -0
- package/core-assets/adapters/security/manifest.yaml +96 -0
- package/core-assets/agents/.gitkeep +0 -0
- package/core-assets/agents/accessibility-specialist.md +59 -0
- package/core-assets/agents/api-integration-engineer.md +59 -0
- package/core-assets/agents/code-explorer.md +94 -0
- package/core-assets/agents/code-explorer.source +7 -0
- package/core-assets/agents/data-migration-engineer.md +59 -0
- package/core-assets/agents/devex-docs-engineer.md +57 -0
- package/core-assets/agents/doctrine-critic.md +91 -0
- package/core-assets/agents/doctrine-critic.source +11 -0
- package/core-assets/agents/domain-architect.md +56 -0
- package/core-assets/agents/experience-designer.md +59 -0
- package/core-assets/agents/frontend-engineer.md +60 -0
- package/core-assets/agents/independent-code-reviewer.md +61 -0
- package/core-assets/agents/migration-planner.md +97 -0
- package/core-assets/agents/migration-planner.source +7 -0
- package/core-assets/agents/observability-sre-engineer.md +59 -0
- package/core-assets/agents/pdf-specialist.md +58 -0
- package/core-assets/agents/performance-engineer.md +58 -0
- package/core-assets/agents/product-challenger.md +55 -0
- package/core-assets/agents/security-engineer.md +66 -0
- package/core-assets/agents/silent-failure-hunter.md +94 -0
- package/core-assets/agents/silent-failure-hunter.source +8 -0
- package/core-assets/agents/solution-architect.md +57 -0
- package/core-assets/agents/test-qa-engineer.md +60 -0
- package/core-assets/agents/type-design-analyzer.md +93 -0
- package/core-assets/agents/type-design-analyzer.source +8 -0
- package/core-assets/agents/visual-craft-director.md +62 -0
- package/core-assets/codex/hooks.json +157 -0
- package/core-assets/contract-history/visual-craft-director/v2.yaml +43 -0
- package/core-assets/data/catalog.v3.json +10479 -0
- package/core-assets/data/certification.json +1800 -0
- package/core-assets/data/identity.json +8 -0
- package/core-assets/data/model.json +4226 -0
- package/core-assets/enforce/ci-enforce.sh +183 -0
- package/core-assets/hooks/_checks.sh +145 -0
- package/core-assets/hooks/_hooklib.sh +163 -0
- package/core-assets/hooks/_syntax-worker.cjs +503 -0
- package/core-assets/hooks/_void-hook.mjs +5459 -0
- package/core-assets/hooks/activation-meter.sh +13 -0
- package/core-assets/hooks/anti-ai-slop-grep.sh +38 -0
- package/core-assets/hooks/auto-format.sh +9 -0
- package/core-assets/hooks/axe-precommit.sh +35 -0
- package/core-assets/hooks/block-dangerous-bash.sh +10 -0
- package/core-assets/hooks/block-protected-push.sh +81 -0
- package/core-assets/hooks/boundary-direction-check.sh +10 -0
- package/core-assets/hooks/control-character-grep.sh +10 -0
- package/core-assets/hooks/fowler-refactors.txt +71 -0
- package/core-assets/hooks/large-cl-grep.sh +9 -0
- package/core-assets/hooks/llm-cost-precommit.sh +55 -0
- package/core-assets/hooks/migration-lint.sh +58 -0
- package/core-assets/hooks/migration-pr-template.sh +55 -0
- package/core-assets/hooks/no-ai-design-slop.sh +10 -0
- package/core-assets/hooks/no-any-grep.sh +10 -0
- package/core-assets/hooks/no-as-cast-grep.sh +10 -0
- package/core-assets/hooks/no-console-log-grep.sh +10 -0
- package/core-assets/hooks/no-null-grep.sh +10 -0
- package/core-assets/hooks/no-only-no-skip.sh +10 -0
- package/core-assets/hooks/outcome-meter.sh +13 -0
- package/core-assets/hooks/pre-PR-review-evidence.sh +47 -0
- package/core-assets/hooks/protect-sensitive-files.sh +10 -0
- package/core-assets/hooks/refactor-named-grep.sh +49 -0
- package/core-assets/hooks/secret-in-content.sh +10 -0
- package/core-assets/hooks/sessionstart-context.sh +9 -0
- package/core-assets/hooks/stop-typecheck.sh +9 -0
- package/core-assets/hooks/tdd-guard.md +90 -0
- package/core-assets/hooks/tdd-guard.sh +10 -0
- package/core-assets/hooks/test-name-lint.sh +10 -0
- package/core-assets/hooks/tidying-commit-prefix.sh +37 -0
- package/core-assets/hooks/trim-large-output.sh +9 -0
- package/core-assets/hooks/tsc-noemit-precommit.sh +22 -0
- package/core-assets/hooks/ubiquitous-language-lint.sh +58 -0
- package/core-assets/hooks/viewport-screenshot-gate.sh +48 -0
- package/core-assets/modules/.gitkeep +0 -0
- package/core-assets/packs/pack-mobile/skills/void-eas-build-profile/.source +7 -0
- package/core-assets/packs/pack-mobile/skills/void-eas-build-profile/SKILL.md +164 -0
- package/core-assets/packs/pack-mobile/skills/void-eas-build-profile/harness.yaml +14 -0
- package/core-assets/packs/pack-mobile/skills/void-expo-config-plugins/.source +7 -0
- package/core-assets/packs/pack-mobile/skills/void-expo-config-plugins/SKILL.md +178 -0
- package/core-assets/packs/pack-mobile/skills/void-expo-config-plugins/harness.yaml +14 -0
- package/core-assets/packs/pack-mobile/skills/void-expo-router-pattern/.source +8 -0
- package/core-assets/packs/pack-mobile/skills/void-expo-router-pattern/SKILL.md +209 -0
- package/core-assets/packs/pack-mobile/skills/void-expo-router-pattern/harness.yaml +14 -0
- package/core-assets/packs/pack-mobile/skills/void-ota-update-strategy/.source +7 -0
- package/core-assets/packs/pack-mobile/skills/void-ota-update-strategy/SKILL.md +194 -0
- package/core-assets/packs/pack-mobile/skills/void-ota-update-strategy/harness.yaml +14 -0
- package/core-assets/packs/pack-monorepo/skills/void-dependency-direction/.source +5 -0
- package/core-assets/packs/pack-monorepo/skills/void-dependency-direction/SKILL.md +160 -0
- package/core-assets/packs/pack-monorepo/skills/void-dependency-direction/harness.yaml +14 -0
- package/core-assets/packs/pack-monorepo/skills/void-package-extraction/.source +5 -0
- package/core-assets/packs/pack-monorepo/skills/void-package-extraction/SKILL.md +70 -0
- package/core-assets/packs/pack-monorepo/skills/void-package-extraction/harness.yaml +14 -0
- package/core-assets/packs/pack-monorepo/skills/void-service-package/.source +5 -0
- package/core-assets/packs/pack-monorepo/skills/void-service-package/SKILL.md +122 -0
- package/core-assets/packs/pack-monorepo/skills/void-service-package/harness.yaml +14 -0
- package/core-assets/packs/pack-monorepo/skills/void-turbo-pipeline-tuning/.source +7 -0
- package/core-assets/packs/pack-monorepo/skills/void-turbo-pipeline-tuning/SKILL.md +116 -0
- package/core-assets/packs/pack-monorepo/skills/void-turbo-pipeline-tuning/harness.yaml +14 -0
- package/core-assets/packs/pack-nextjs/skills/void-cache-component-pattern/.source +5 -0
- package/core-assets/packs/pack-nextjs/skills/void-cache-component-pattern/SKILL.md +162 -0
- package/core-assets/packs/pack-nextjs/skills/void-cache-component-pattern/harness.yaml +14 -0
- package/core-assets/packs/pack-nextjs/skills/void-instrumentation-setup/.source +5 -0
- package/core-assets/packs/pack-nextjs/skills/void-instrumentation-setup/SKILL.md +161 -0
- package/core-assets/packs/pack-nextjs/skills/void-instrumentation-setup/harness.yaml +14 -0
- package/core-assets/packs/pack-nextjs/skills/void-loading-error-boundaries/.source +5 -0
- package/core-assets/packs/pack-nextjs/skills/void-loading-error-boundaries/SKILL.md +176 -0
- package/core-assets/packs/pack-nextjs/skills/void-loading-error-boundaries/harness.yaml +14 -0
- package/core-assets/packs/pack-nextjs/skills/void-parallel-routes-slots/.source +5 -0
- package/core-assets/packs/pack-nextjs/skills/void-parallel-routes-slots/SKILL.md +129 -0
- package/core-assets/packs/pack-nextjs/skills/void-parallel-routes-slots/harness.yaml +14 -0
- package/core-assets/packs/pack-nextjs/skills/void-route-group-decision/.source +5 -0
- package/core-assets/packs/pack-nextjs/skills/void-route-group-decision/SKILL.md +107 -0
- package/core-assets/packs/pack-nextjs/skills/void-route-group-decision/harness.yaml +14 -0
- package/core-assets/packs/pack-pwa/skills/void-install-prompt-ux/.source +5 -0
- package/core-assets/packs/pack-pwa/skills/void-install-prompt-ux/SKILL.md +153 -0
- package/core-assets/packs/pack-pwa/skills/void-install-prompt-ux/harness.yaml +14 -0
- package/core-assets/packs/pack-pwa/skills/void-manifest-checklist/.source +5 -0
- package/core-assets/packs/pack-pwa/skills/void-manifest-checklist/SKILL.md +204 -0
- package/core-assets/packs/pack-pwa/skills/void-manifest-checklist/harness.yaml +14 -0
- package/core-assets/packs/pack-pwa/skills/void-offline-first-mutation/.source +8 -0
- package/core-assets/packs/pack-pwa/skills/void-offline-first-mutation/SKILL.md +266 -0
- package/core-assets/packs/pack-pwa/skills/void-offline-first-mutation/harness.yaml +14 -0
- package/core-assets/packs/pack-pwa/skills/void-service-worker-strategy/.source +5 -0
- package/core-assets/packs/pack-pwa/skills/void-service-worker-strategy/SKILL.md +178 -0
- package/core-assets/packs/pack-pwa/skills/void-service-worker-strategy/harness.yaml +14 -0
- package/core-assets/packs/pack-react/skills/void-accessibility-check/.source +8 -0
- package/core-assets/packs/pack-react/skills/void-accessibility-check/SKILL.md +136 -0
- package/core-assets/packs/pack-react/skills/void-accessibility-check/harness.yaml +14 -0
- package/core-assets/packs/pack-react/skills/void-client-vs-server-component/.source +5 -0
- package/core-assets/packs/pack-react/skills/void-client-vs-server-component/SKILL.md +116 -0
- package/core-assets/packs/pack-react/skills/void-client-vs-server-component/harness.yaml +14 -0
- package/core-assets/packs/pack-react/skills/void-form-pattern/.source +7 -0
- package/core-assets/packs/pack-react/skills/void-form-pattern/SKILL.md +157 -0
- package/core-assets/packs/pack-react/skills/void-form-pattern/harness.yaml +14 -0
- package/core-assets/packs/pack-react/skills/void-state-architecture/.source +5 -0
- package/core-assets/packs/pack-react/skills/void-state-architecture/SKILL.md +160 -0
- package/core-assets/packs/pack-react/skills/void-state-architecture/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-background-job-pattern/.source +6 -0
- package/core-assets/packs/pack-server/skills/void-background-job-pattern/SKILL.md +214 -0
- package/core-assets/packs/pack-server/skills/void-background-job-pattern/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-drizzle-migration-safe/.source +6 -0
- package/core-assets/packs/pack-server/skills/void-drizzle-migration-safe/SKILL.md +168 -0
- package/core-assets/packs/pack-server/skills/void-drizzle-migration-safe/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-env-validation/.source +6 -0
- package/core-assets/packs/pack-server/skills/void-env-validation/SKILL.md +131 -0
- package/core-assets/packs/pack-server/skills/void-env-validation/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-rate-limit-strategy/.source +6 -0
- package/core-assets/packs/pack-server/skills/void-rate-limit-strategy/SKILL.md +149 -0
- package/core-assets/packs/pack-server/skills/void-rate-limit-strategy/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-server-action/.source +7 -0
- package/core-assets/packs/pack-server/skills/void-server-action/SKILL.md +181 -0
- package/core-assets/packs/pack-server/skills/void-server-action/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-testing-server-modules/.source +7 -0
- package/core-assets/packs/pack-server/skills/void-testing-server-modules/SKILL.md +73 -0
- package/core-assets/packs/pack-server/skills/void-testing-server-modules/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-webhook-handler-pattern/.source +7 -0
- package/core-assets/packs/pack-server/skills/void-webhook-handler-pattern/SKILL.md +205 -0
- package/core-assets/packs/pack-server/skills/void-webhook-handler-pattern/harness.yaml +14 -0
- package/core-assets/policies/core.yaml +95 -0
- package/core-assets/policies/ui.yaml +23 -0
- package/core-assets/profiles/base.yaml +29 -0
- package/core-assets/profiles/expo-config.yaml +36 -0
- package/core-assets/profiles/expo.yaml +35 -0
- package/core-assets/profiles/monorepo.yaml +33 -0
- package/core-assets/profiles/nextjs-config.yaml +31 -0
- package/core-assets/profiles/nextjs.yaml +31 -0
- package/core-assets/profiles/node-server.yaml +31 -0
- package/core-assets/profiles/pwa.yaml +33 -0
- package/core-assets/profiles/react.yaml +39 -0
- package/core-assets/profiles/sql.yaml +39 -0
- package/core-assets/profiles/typescript.yaml +39 -0
- package/core-assets/skills/.gitkeep +0 -0
- package/core-assets/skills/void-accessibility/.source +15 -0
- package/core-assets/skills/void-accessibility/SKILL.md +275 -0
- package/core-assets/skills/void-accessibility/harness.yaml +17 -0
- package/core-assets/skills/void-api-and-interface-design/.source +13 -0
- package/core-assets/skills/void-api-and-interface-design/SKILL.md +231 -0
- package/core-assets/skills/void-api-and-interface-design/harness.yaml +16 -0
- package/core-assets/skills/void-async-safety/.source +13 -0
- package/core-assets/skills/void-async-safety/SKILL.md +287 -0
- package/core-assets/skills/void-async-safety/harness.yaml +16 -0
- package/core-assets/skills/void-audit/.source +15 -0
- package/core-assets/skills/void-audit/SKILL.md +83 -0
- package/core-assets/skills/void-audit/harness.yaml +14 -0
- package/core-assets/skills/void-autopilot/.source +21 -0
- package/core-assets/skills/void-autopilot/SKILL.md +358 -0
- package/core-assets/skills/void-autopilot/harness.yaml +14 -0
- package/core-assets/skills/void-brainstorm/.source +14 -0
- package/core-assets/skills/void-brainstorm/SKILL.md +271 -0
- package/core-assets/skills/void-brainstorm/harness.yaml +14 -0
- package/core-assets/skills/void-checkpoint/.source +39 -0
- package/core-assets/skills/void-checkpoint/SKILL.md +227 -0
- package/core-assets/skills/void-checkpoint/harness.yaml +14 -0
- package/core-assets/skills/void-claude-md/.source +15 -0
- package/core-assets/skills/void-claude-md/SKILL.md +180 -0
- package/core-assets/skills/void-claude-md/harness.yaml +14 -0
- package/core-assets/skills/void-code-review/.source +14 -0
- package/core-assets/skills/void-code-review/SKILL.md +232 -0
- package/core-assets/skills/void-code-review/harness.yaml +15 -0
- package/core-assets/skills/void-commit-discipline/.source +9 -0
- package/core-assets/skills/void-commit-discipline/SKILL.md +211 -0
- package/core-assets/skills/void-commit-discipline/harness.yaml +15 -0
- package/core-assets/skills/void-context/.source +15 -0
- package/core-assets/skills/void-context/SKILL.md +167 -0
- package/core-assets/skills/void-context/harness.yaml +14 -0
- package/core-assets/skills/void-debug/.source +11 -0
- package/core-assets/skills/void-debug/SKILL.md +211 -0
- package/core-assets/skills/void-debug/harness.yaml +14 -0
- package/core-assets/skills/void-decide/.source +9 -0
- package/core-assets/skills/void-decide/SKILL.md +147 -0
- package/core-assets/skills/void-decide/harness.yaml +14 -0
- package/core-assets/skills/void-devex-audit/.source +8 -0
- package/core-assets/skills/void-devex-audit/SKILL.md +113 -0
- package/core-assets/skills/void-devex-audit/harness.yaml +15 -0
- package/core-assets/skills/void-doctor/.source +15 -0
- package/core-assets/skills/void-doctor/SKILL.md +83 -0
- package/core-assets/skills/void-doctor/harness.yaml +14 -0
- package/core-assets/skills/void-domain-driven-design/.source +13 -0
- package/core-assets/skills/void-domain-driven-design/SKILL.md +292 -0
- package/core-assets/skills/void-domain-driven-design/harness.yaml +15 -0
- package/core-assets/skills/void-frontend-design/.source +19 -0
- package/core-assets/skills/void-frontend-design/SKILL.md +274 -0
- package/core-assets/skills/void-frontend-design/harness.yaml +17 -0
- package/core-assets/skills/void-functional/.source +14 -0
- package/core-assets/skills/void-functional/SKILL.md +312 -0
- package/core-assets/skills/void-functional/harness.yaml +15 -0
- package/core-assets/skills/void-graph/.source +15 -0
- package/core-assets/skills/void-graph/SKILL.md +80 -0
- package/core-assets/skills/void-graph/harness.yaml +14 -0
- package/core-assets/skills/void-hexagonal-architecture/.source +12 -0
- package/core-assets/skills/void-hexagonal-architecture/SKILL.md +276 -0
- package/core-assets/skills/void-hexagonal-architecture/harness.yaml +15 -0
- package/core-assets/skills/void-implement/.source +39 -0
- package/core-assets/skills/void-implement/SKILL.md +183 -0
- package/core-assets/skills/void-implement/harness.yaml +14 -0
- package/core-assets/skills/void-learn/.source +13 -0
- package/core-assets/skills/void-learn/SKILL.md +226 -0
- package/core-assets/skills/void-learn/harness.yaml +14 -0
- package/core-assets/skills/void-llm-cost-discipline/.source +13 -0
- package/core-assets/skills/void-llm-cost-discipline/SKILL.md +299 -0
- package/core-assets/skills/void-llm-cost-discipline/harness.yaml +15 -0
- package/core-assets/skills/void-make-pdf/.source +9 -0
- package/core-assets/skills/void-make-pdf/SKILL.md +52 -0
- package/core-assets/skills/void-make-pdf/harness.yaml +15 -0
- package/core-assets/skills/void-merge/.source +8 -0
- package/core-assets/skills/void-merge/SKILL.md +111 -0
- package/core-assets/skills/void-merge/harness.yaml +14 -0
- package/core-assets/skills/void-migrations/.source +13 -0
- package/core-assets/skills/void-migrations/SKILL.md +264 -0
- package/core-assets/skills/void-migrations/harness.yaml +17 -0
- package/core-assets/skills/void-observability/.source +12 -0
- package/core-assets/skills/void-observability/SKILL.md +245 -0
- package/core-assets/skills/void-observability/harness.yaml +15 -0
- package/core-assets/skills/void-plan/.source +15 -0
- package/core-assets/skills/void-plan/SKILL.md +239 -0
- package/core-assets/skills/void-plan/harness.yaml +14 -0
- package/core-assets/skills/void-plan-review/.source +12 -0
- package/core-assets/skills/void-plan-review/SKILL.md +128 -0
- package/core-assets/skills/void-plan-review/harness.yaml +15 -0
- package/core-assets/skills/void-qa/.source +15 -0
- package/core-assets/skills/void-qa/SKILL.md +103 -0
- package/core-assets/skills/void-qa/harness.yaml +15 -0
- package/core-assets/skills/void-refactor/.source +9 -0
- package/core-assets/skills/void-refactor/SKILL.md +220 -0
- package/core-assets/skills/void-refactor/harness.yaml +15 -0
- package/core-assets/skills/void-retrospective/.source +9 -0
- package/core-assets/skills/void-retrospective/SKILL.md +71 -0
- package/core-assets/skills/void-retrospective/harness.yaml +15 -0
- package/core-assets/skills/void-security-audit/.source +12 -0
- package/core-assets/skills/void-security-audit/SKILL.md +138 -0
- package/core-assets/skills/void-security-audit/harness.yaml +15 -0
- package/core-assets/skills/void-security-guidance/.source +12 -0
- package/core-assets/skills/void-security-guidance/SKILL.md +255 -0
- package/core-assets/skills/void-security-guidance/harness.yaml +15 -0
- package/core-assets/skills/void-source-driven-development/.source +12 -0
- package/core-assets/skills/void-source-driven-development/SKILL.md +158 -0
- package/core-assets/skills/void-source-driven-development/harness.yaml +15 -0
- package/core-assets/skills/void-tdd/.source +15 -0
- package/core-assets/skills/void-tdd/SKILL.md +394 -0
- package/core-assets/skills/void-tdd/harness.yaml +15 -0
- package/core-assets/skills/void-testing/.source +19 -0
- package/core-assets/skills/void-testing/SKILL.md +301 -0
- package/core-assets/skills/void-testing/harness.yaml +17 -0
- package/core-assets/skills/void-ticket/.source +18 -0
- package/core-assets/skills/void-ticket/SKILL.md +239 -0
- package/core-assets/skills/void-ticket/harness.yaml +14 -0
- package/core-assets/skills/void-typescript-strict/.source +10 -0
- package/core-assets/skills/void-typescript-strict/SKILL.md +294 -0
- package/core-assets/skills/void-typescript-strict/harness.yaml +17 -0
- package/core-assets/skills/void-ui-review/.source +12 -0
- package/core-assets/skills/void-ui-review/SKILL.md +97 -0
- package/core-assets/skills/void-ui-review/harness.yaml +15 -0
- package/core-assets/skills/void-verify/.source +12 -0
- package/core-assets/skills/void-verify/SKILL.md +184 -0
- package/core-assets/skills/void-verify/harness.yaml +15 -0
- package/core-assets/specialists/accessibility-specialist.source +11 -0
- package/core-assets/specialists/accessibility-specialist.yaml +30 -0
- package/core-assets/specialists/api-integration-engineer.source +11 -0
- package/core-assets/specialists/api-integration-engineer.yaml +30 -0
- package/core-assets/specialists/data-migration-engineer.source +11 -0
- package/core-assets/specialists/data-migration-engineer.yaml +30 -0
- package/core-assets/specialists/devex-docs-engineer.source +11 -0
- package/core-assets/specialists/devex-docs-engineer.yaml +29 -0
- package/core-assets/specialists/domain-architect.source +11 -0
- package/core-assets/specialists/domain-architect.yaml +28 -0
- package/core-assets/specialists/experience-designer.source +15 -0
- package/core-assets/specialists/experience-designer.yaml +38 -0
- package/core-assets/specialists/frontend-engineer.source +11 -0
- package/core-assets/specialists/frontend-engineer.yaml +32 -0
- package/core-assets/specialists/independent-code-reviewer.source +10 -0
- package/core-assets/specialists/independent-code-reviewer.yaml +37 -0
- package/core-assets/specialists/migrations.json +15 -0
- package/core-assets/specialists/observability-sre-engineer.source +11 -0
- package/core-assets/specialists/observability-sre-engineer.yaml +30 -0
- package/core-assets/specialists/pdf-specialist.source +11 -0
- package/core-assets/specialists/pdf-specialist.yaml +29 -0
- package/core-assets/specialists/performance-engineer.source +11 -0
- package/core-assets/specialists/performance-engineer.yaml +29 -0
- package/core-assets/specialists/product-challenger.source +10 -0
- package/core-assets/specialists/product-challenger.yaml +27 -0
- package/core-assets/specialists/security-engineer.source +13 -0
- package/core-assets/specialists/security-engineer.yaml +41 -0
- package/core-assets/specialists/solution-architect.source +13 -0
- package/core-assets/specialists/solution-architect.yaml +32 -0
- package/core-assets/specialists/test-qa-engineer.source +14 -0
- package/core-assets/specialists/test-qa-engineer.yaml +35 -0
- package/core-assets/specialists/visual-craft-director.source +17 -0
- package/core-assets/specialists/visual-craft-director.yaml +60 -0
- package/core-assets/templates/github/dependency-audit.mjs +54 -0
- package/core-assets/templates/github/void-dependency-audit.yml +35 -0
- package/core-assets/templates/github/void-security.yml +85 -0
- package/dist/main.js +57001 -0
- package/package.json +66 -4
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: void-expo-config-plugins
|
|
3
|
+
description: Add native functionality to an Expo app via config plugins, never by ejecting. Pick existing Expo modules first, write a custom plugin only when none fits. Document everything in app.config.ts.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# expo-config-plugins
|
|
7
|
+
|
|
8
|
+
Use when an Expo app needs functionality not available in JS — push notifications, camera, haptics, secure storage, or any native iOS/Android API. The Expo way: config plugins + Expo Modules. Ejecting to bare React Native is a one-way door — almost never the right answer.
|
|
9
|
+
|
|
10
|
+
## The decision tree
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
1. Is there an `expo-X` package on npm? → use it (e.g., expo-camera, expo-haptics)
|
|
14
|
+
2. Is there a community config plugin? → use it (search "expo-plugin" + your need)
|
|
15
|
+
3. Does it just need a Podfile / Manifest tweak? → write a custom config plugin (~30 lines)
|
|
16
|
+
4. Does it need new Swift/Kotlin code? → write an Expo Module
|
|
17
|
+
5. Truly nothing fits? → reconsider the requirement; ejecting last resort
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Most teams hit (1) for 80% of needs and never go past (3).
|
|
21
|
+
|
|
22
|
+
## What a config plugin is
|
|
23
|
+
|
|
24
|
+
A function that mutates the native iOS/Android project files at build time. It does not run in the app — it runs in EAS Build / `expo prebuild`.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// app.config.ts
|
|
28
|
+
import { ConfigContext, ExpoConfig } from 'expo/config';
|
|
29
|
+
|
|
30
|
+
export default ({ config }: ConfigContext): ExpoConfig => ({
|
|
31
|
+
...config,
|
|
32
|
+
name: 'Solaar',
|
|
33
|
+
slug: 'solaar',
|
|
34
|
+
plugins: [
|
|
35
|
+
'expo-router',
|
|
36
|
+
'expo-secure-store',
|
|
37
|
+
[
|
|
38
|
+
'expo-build-properties',
|
|
39
|
+
{
|
|
40
|
+
ios: { deploymentTarget: '15.1', useFrameworks: 'static' },
|
|
41
|
+
android: { compileSdkVersion: 34, targetSdkVersion: 34 },
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
'./plugins/my-custom-plugin', // local plugin
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Each entry is either:
|
|
50
|
+
|
|
51
|
+
- A string: package name; uses the package's default plugin
|
|
52
|
+
- A tuple `[name, props]`: passes config to the plugin
|
|
53
|
+
|
|
54
|
+
## Common Expo modules
|
|
55
|
+
|
|
56
|
+
Pin the ones below mentally — they handle 80% of native needs:
|
|
57
|
+
|
|
58
|
+
| Need | Package |
|
|
59
|
+
|---|---|
|
|
60
|
+
| Camera | `expo-camera` |
|
|
61
|
+
| Push notifications | `expo-notifications` |
|
|
62
|
+
| Haptics (vibrate, tap feedback) | `expo-haptics` |
|
|
63
|
+
| Secure storage (keychain / keystore) | `expo-secure-store` |
|
|
64
|
+
| File system | `expo-file-system` |
|
|
65
|
+
| Local notifications | `expo-notifications` |
|
|
66
|
+
| Authentication (OAuth, biometrics) | `expo-auth-session`, `expo-local-authentication` |
|
|
67
|
+
| Location | `expo-location` |
|
|
68
|
+
| Linking (deep links) | `expo-linking` |
|
|
69
|
+
| Updates | `expo-updates` |
|
|
70
|
+
| Sharing | `expo-sharing` |
|
|
71
|
+
| WebView | `expo-webview` (or `react-native-webview`) |
|
|
72
|
+
| Storage (key-value) | `expo-sqlite` or `react-native-mmkv` |
|
|
73
|
+
|
|
74
|
+
For each: install, add to `plugins` array if it needs prebuild config, use the JS API in your code.
|
|
75
|
+
|
|
76
|
+
## When to write a custom plugin
|
|
77
|
+
|
|
78
|
+
Two cases:
|
|
79
|
+
|
|
80
|
+
### A. Config-only (Info.plist, AndroidManifest tweaks)
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
// plugins/with-app-tracking.ts
|
|
84
|
+
import { ConfigPlugin, withInfoPlist } from 'expo/config-plugins';
|
|
85
|
+
|
|
86
|
+
const withAppTracking: ConfigPlugin<{ reason: string }> = (config, { reason }) => {
|
|
87
|
+
return withInfoPlist(config, (cfg) => {
|
|
88
|
+
cfg.modResults.NSUserTrackingUsageDescription = reason;
|
|
89
|
+
return cfg;
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default withAppTracking;
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Usage:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
// app.config.ts
|
|
100
|
+
plugins: [
|
|
101
|
+
['./plugins/with-app-tracking', { reason: 'Personalize your experience' }],
|
|
102
|
+
];
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
This pattern handles: permission strings, URL schemes, custom Info.plist keys, AndroidManifest entries, build settings.
|
|
106
|
+
|
|
107
|
+
### B. Native code (Swift / Kotlin)
|
|
108
|
+
|
|
109
|
+
If you need actual native code, write an **Expo Module**, not a config plugin. Expo Modules are first-class native modules with auto-linking + TS bindings.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx create-expo-module@latest --local my-native-module
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The generated package has `ios/`, `android/`, and `src/` (TS bindings). Build runs `expo prebuild` automatically.
|
|
116
|
+
|
|
117
|
+
## When NOT to do native
|
|
118
|
+
|
|
119
|
+
Most "I need a native feature" turns out to be solvable in JS:
|
|
120
|
+
|
|
121
|
+
- **"I need to read the file system"** → `expo-file-system`, no native code
|
|
122
|
+
- **"I need biometric auth"** → `expo-local-authentication`
|
|
123
|
+
- **"I need to detect device type"** → `expo-device` provides everything
|
|
124
|
+
|
|
125
|
+
Try the JS path first. Native is overhead: build complexity, EAS Build minutes, debugging hardness.
|
|
126
|
+
|
|
127
|
+
## `app.config.ts` vs `app.json`
|
|
128
|
+
|
|
129
|
+
Use `app.config.ts`. Reasons:
|
|
130
|
+
|
|
131
|
+
- TypeScript = typo-safe
|
|
132
|
+
- Dynamic values (env vars, computed flags) supported
|
|
133
|
+
- One source of truth for dev/staging/prod variants
|
|
134
|
+
|
|
135
|
+
Example:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
// app.config.ts
|
|
139
|
+
import { ConfigContext, ExpoConfig } from 'expo/config';
|
|
140
|
+
|
|
141
|
+
const variant = process.env.APP_VARIANT ?? 'dev';
|
|
142
|
+
|
|
143
|
+
export default ({ config }: ConfigContext): ExpoConfig => ({
|
|
144
|
+
...config,
|
|
145
|
+
name: variant === 'prod' ? 'Solaar' : `Solaar (${variant})`,
|
|
146
|
+
slug: 'solaar',
|
|
147
|
+
ios: {
|
|
148
|
+
bundleIdentifier: `io.voidcorp.solaar.${variant}`,
|
|
149
|
+
},
|
|
150
|
+
android: {
|
|
151
|
+
package: `io.voidcorp.solaar.${variant}`,
|
|
152
|
+
},
|
|
153
|
+
extra: {
|
|
154
|
+
apiUrl: process.env.EXPO_PUBLIC_API_URL,
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Dev + staging + prod variants installed side-by-side on a single device thanks to bundle ID variation.
|
|
160
|
+
|
|
161
|
+
## Anti-patterns
|
|
162
|
+
|
|
163
|
+
- ✗ **Ejecting on first sign of native need** — almost always reversible to "use an Expo module"
|
|
164
|
+
- ✗ **Hand-editing `ios/` or `android/`** after prebuild — gets nuked on next `expo prebuild`; bad merges between repo and prebuild
|
|
165
|
+
- ✗ **Mixing `app.json` and `app.config.ts`** — pick one, delete the other; precedence is unclear
|
|
166
|
+
- ✗ **`extra` field used for secrets** — `extra` ships in the JS bundle, visible to anyone
|
|
167
|
+
- ✗ **Plugin order matters and you ignored it** — some plugins must come before others (e.g., expo-router before any router consumer); follow plugin docs
|
|
168
|
+
|
|
169
|
+
## Verification
|
|
170
|
+
|
|
171
|
+
`npx expo prebuild --no-install --clean` — runs the plugin pipeline locally, generates `ios/` and `android/` you can inspect. If something's broken at native level, you'll see it before EAS.
|
|
172
|
+
|
|
173
|
+
## Composition
|
|
174
|
+
|
|
175
|
+
- `void-eas-build-profile` — config plugins + build profiles together define what binary ships
|
|
176
|
+
- `void-ota-update-strategy` — JS-only changes don't need a rebuild; native config changes do
|
|
177
|
+
- `void-state-architecture` — share state architecture with web siblings; only native deps differ
|
|
178
|
+
- `void-env-validation` — env reaches the app via `extra` or EAS Build env; validate with same schema discipline
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Harness metadata for this skill. Not part of the Agent Skills
|
|
2
|
+
# specification, and never copied into a consumer project: the SKILL.md
|
|
3
|
+
# beside it stays portable and validates against the official six fields.
|
|
4
|
+
# Read by this repository's graph, and by the installer for `runtimes`.
|
|
5
|
+
kind: standard
|
|
6
|
+
owner: folpe
|
|
7
|
+
runtimes: [claude, codex]
|
|
8
|
+
enforcement:
|
|
9
|
+
floor: ci
|
|
10
|
+
inline:
|
|
11
|
+
claude: active
|
|
12
|
+
codex: active
|
|
13
|
+
hermes: ci-only
|
|
14
|
+
eval_targets: [claude/anthropic/opus]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Sources for `void-expo-router-pattern` skill (see `docs/plans/skill-audits/void-expo-router-pattern.md` for the full audit):
|
|
2
|
+
|
|
3
|
+
- expo-router official documentation — no URL recorded in the audit — distilled the file-based routing conventions (`(tabs)/`, `(modal)/`, `(auth)/`, per-group `_layout.tsx`, typed routes via `experiments.typedRoutes`, deep-link auto-config).
|
|
4
|
+
- Next.js App Router conventions — no URL recorded in the audit — used as the shared mental model expo-router mirrors by design, so a monorepo keeps one routing model across web and mobile.
|
|
5
|
+
|
|
6
|
+
No external skill ships this verbatim; authored by distilling the expo-router docs and aligning them to the Next.js App Router convention.
|
|
7
|
+
|
|
8
|
+
Strategy: distill. Lean on the deliberate Next.js parity so devs do not reinvent React Navigation patterns.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: void-expo-router-pattern
|
|
3
|
+
description: File-based routing in Expo via expo-router — layouts, tabs, modals, deep links. Mirrors Next.js App Router so monorepos can share mental model. Stack-aware navigation patterns.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# expo-router-pattern
|
|
7
|
+
|
|
8
|
+
Use when adding screens / navigation to an Expo app using `expo-router`. The convention mirrors Next.js App Router by design — same `(group)/`, `_layout.tsx`, `[param]`, dynamic segments — so a monorepo sharing web + mobile keeps one routing mental model.
|
|
9
|
+
|
|
10
|
+
If your app uses React Navigation directly (not expo-router), this skill does not apply (different API entirely).
|
|
11
|
+
|
|
12
|
+
## File layout (Solaar archetype)
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
apps/mobile/app/
|
|
16
|
+
├── _layout.tsx # root layout: providers, Stack navigator
|
|
17
|
+
├── (tabs)/ # tab navigation root
|
|
18
|
+
│ ├── _layout.tsx # tab bar config
|
|
19
|
+
│ ├── index.tsx # / → Home tab
|
|
20
|
+
│ ├── contacts/
|
|
21
|
+
│ │ ├── index.tsx # /contacts
|
|
22
|
+
│ │ └── [id].tsx # /contacts/123
|
|
23
|
+
│ ├── notes.tsx
|
|
24
|
+
│ └── settings.tsx
|
|
25
|
+
├── (modal)/ # modal-presented screens
|
|
26
|
+
│ ├── _layout.tsx # presentation: 'modal'
|
|
27
|
+
│ ├── new-contact.tsx # /new-contact (slides up)
|
|
28
|
+
│ └── share.tsx
|
|
29
|
+
├── (auth)/ # auth flow (no tabs)
|
|
30
|
+
│ ├── _layout.tsx # presentation: stack, no tab bar
|
|
31
|
+
│ ├── sign-in.tsx
|
|
32
|
+
│ └── sign-up.tsx
|
|
33
|
+
└── +not-found.tsx # 404
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Layouts: what each `_layout.tsx` does
|
|
37
|
+
|
|
38
|
+
### Root `app/_layout.tsx`
|
|
39
|
+
|
|
40
|
+
- Wraps `<Stack />` (or `<Slot />` for layoutless)
|
|
41
|
+
- Provides global Providers (Auth, Query Client, Theme, i18n)
|
|
42
|
+
- Initializes Sentry, fonts, splash hide
|
|
43
|
+
- Owns navigation theme (`DarkTheme` / `DefaultTheme`)
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
// app/_layout.tsx
|
|
47
|
+
import { Stack } from 'expo-router';
|
|
48
|
+
import { Providers } from '@/providers';
|
|
49
|
+
import { useFonts } from 'expo-font';
|
|
50
|
+
import * as SplashScreen from 'expo-splash-screen';
|
|
51
|
+
|
|
52
|
+
SplashScreen.preventAutoHideAsync();
|
|
53
|
+
|
|
54
|
+
export default function RootLayout() {
|
|
55
|
+
const [loaded] = useFonts({ /* ... */ });
|
|
56
|
+
if (!loaded) return null;
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Providers>
|
|
60
|
+
<Stack screenOptions={{ headerShown: false }}>
|
|
61
|
+
<Stack.Screen name="(tabs)" />
|
|
62
|
+
<Stack.Screen name="(modal)" options={{ presentation: 'modal' }} />
|
|
63
|
+
<Stack.Screen name="(auth)" />
|
|
64
|
+
</Stack>
|
|
65
|
+
</Providers>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### `(tabs)/_layout.tsx`
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import { Tabs } from 'expo-router';
|
|
74
|
+
|
|
75
|
+
export default function TabsLayout() {
|
|
76
|
+
return (
|
|
77
|
+
<Tabs screenOptions={{ tabBarActiveTintColor: 'gold' }}>
|
|
78
|
+
<Tabs.Screen name="index" options={{ title: 'Accueil' }} />
|
|
79
|
+
<Tabs.Screen name="contacts" options={{ title: 'Contacts' }} />
|
|
80
|
+
<Tabs.Screen name="notes" options={{ title: 'Notes' }} />
|
|
81
|
+
<Tabs.Screen name="settings" options={{ title: 'Réglages' }} />
|
|
82
|
+
</Tabs>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### `(modal)/_layout.tsx`
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
import { Stack } from 'expo-router';
|
|
91
|
+
|
|
92
|
+
export default function ModalLayout() {
|
|
93
|
+
return <Stack screenOptions={{ presentation: 'modal' }} />;
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Navigation primitives
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
import { useRouter, Link } from 'expo-router';
|
|
101
|
+
|
|
102
|
+
function ContactCard({ id }: { id: string }) {
|
|
103
|
+
const router = useRouter();
|
|
104
|
+
return (
|
|
105
|
+
<>
|
|
106
|
+
{/* Imperative */}
|
|
107
|
+
<Pressable onPress={() => router.push(`/contacts/${id}`)} />
|
|
108
|
+
|
|
109
|
+
{/* Declarative — composes with Tappable from @repo/ui */}
|
|
110
|
+
<Link href={{ pathname: '/contacts/[id]', params: { id } }} asChild>
|
|
111
|
+
<Tappable>{children}</Tappable>
|
|
112
|
+
</Link>
|
|
113
|
+
</>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Prefer `<Link>` over `router.push` — typed params, better a11y (renders an actual touchable surface), works with deep links.
|
|
119
|
+
|
|
120
|
+
## Dynamic segments + typed routes
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
app/(tabs)/contacts/[id].tsx → /contacts/:id
|
|
124
|
+
app/(tabs)/contacts/[...rest].tsx → /contacts/* (catch-all)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Access params:
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
import { useLocalSearchParams } from 'expo-router';
|
|
131
|
+
|
|
132
|
+
export default function Contact() {
|
|
133
|
+
const { id } = useLocalSearchParams<{ id: string }>();
|
|
134
|
+
return <ContactView id={id} />;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Enable **typed routes** in `app.config.ts`:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
experiments: { typedRoutes: true }
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Then `<Link href="/contacts/xyz" />` is type-checked at build time.
|
|
145
|
+
|
|
146
|
+
## Deep links
|
|
147
|
+
|
|
148
|
+
`expo-router` automatically configures deep links from the file structure. To open the app at `/contacts/123` from outside:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
iOS: yourscheme://contacts/123
|
|
152
|
+
Android: same
|
|
153
|
+
Universal Link / App Link: https://app.solaar.com/contacts/123
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`scheme` in `app.config.ts`:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
scheme: 'solaar',
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Universal Links need additional setup (`apple-app-site-association`, `assetlinks.json` on the web).
|
|
163
|
+
|
|
164
|
+
## Modals
|
|
165
|
+
|
|
166
|
+
Two patterns:
|
|
167
|
+
|
|
168
|
+
### A. Modal as a route (`(modal)/` group)
|
|
169
|
+
|
|
170
|
+
Stack-based modal presentation, full route URL, supports deep linking. Use for screens that ARE work — new-contact form, share sheet, settings detail.
|
|
171
|
+
|
|
172
|
+
### B. Modal as local state
|
|
173
|
+
|
|
174
|
+
`useState` + a `<Modal>` component from React Native or Radix-ish mobile library. Use for transient UI — confirmation dialog, toast.
|
|
175
|
+
|
|
176
|
+
Same rule as web: if the modal should be shareable / deep-linkable / browser-backable, it's a route. Otherwise local state.
|
|
177
|
+
|
|
178
|
+
## Auth flow
|
|
179
|
+
|
|
180
|
+
`(auth)/` group with its own layout, no tab bar. Root layout redirects unauthenticated users:
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
// app/_layout.tsx
|
|
184
|
+
import { Redirect } from 'expo-router';
|
|
185
|
+
import { useAuth } from '@/auth';
|
|
186
|
+
|
|
187
|
+
export default function RootLayout() {
|
|
188
|
+
const { user, loading } = useAuth();
|
|
189
|
+
if (loading) return null;
|
|
190
|
+
if (!user) return <Redirect href="/sign-in" />;
|
|
191
|
+
return <Stack>...</Stack>;
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Anti-patterns
|
|
196
|
+
|
|
197
|
+
- ✗ **Mixing `react-navigation` directly with `expo-router`** — they conflict; pick one
|
|
198
|
+
- ✗ **One mega `_layout.tsx` at root with all logic** — push down to per-group layouts
|
|
199
|
+
- ✗ **Dynamic `useEffect` to redirect** — use `<Redirect>` or middleware-like guards in `_layout.tsx`
|
|
200
|
+
- ✗ **`router.replace` in render** — only call navigation actions in effects/handlers
|
|
201
|
+
- ✗ **Hardcoded path strings everywhere** — enable typedRoutes; centralize destinations in a constants file otherwise
|
|
202
|
+
|
|
203
|
+
## Composition
|
|
204
|
+
|
|
205
|
+
- `void-expo-config-plugins` — `expo-router` is itself a config plugin (`plugins: ['expo-router']`)
|
|
206
|
+
- `void-eas-build-profile` — dev profile must have `developmentClient: true` for expo-router to work in dev
|
|
207
|
+
- `void-route-group-decision` — same `(group)/` convention; same WHY (group by trust posture)
|
|
208
|
+
- `void-state-architecture` — modal-as-route vs modal-as-state decision
|
|
209
|
+
- `void-accessibility-check` — touch targets, focus management apply identically
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Harness metadata for this skill. Not part of the Agent Skills
|
|
2
|
+
# specification, and never copied into a consumer project: the SKILL.md
|
|
3
|
+
# beside it stays portable and validates against the official six fields.
|
|
4
|
+
# Read by this repository's graph, and by the installer for `runtimes`.
|
|
5
|
+
kind: standard
|
|
6
|
+
owner: folpe
|
|
7
|
+
runtimes: [claude, codex]
|
|
8
|
+
enforcement:
|
|
9
|
+
floor: ci
|
|
10
|
+
inline:
|
|
11
|
+
claude: active
|
|
12
|
+
codex: active
|
|
13
|
+
hermes: ci-only
|
|
14
|
+
eval_targets: [claude/anthropic/opus]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Sources for `void-ota-update-strategy` skill (see `docs/plans/skill-audits/void-ota-update-strategy.md` for the full audit):
|
|
2
|
+
|
|
3
|
+
- EAS Update official documentation — no URL recorded in the audit — distilled the runtime-version model (fingerprint vs appVersion policy), the JS-only-vs-rebuild boundary, channels, and rollback into a change-type table plus an explicit "what CAN'T go in OTA" list, with a strong recommendation toward the fingerprint policy.
|
|
4
|
+
|
|
5
|
+
No external skill ships this verbatim; authored by distilling the official EAS Update docs into the void-machine "when can I OTA vs when do I rebuild" decision.
|
|
6
|
+
|
|
7
|
+
Strategy: distill. Surface the quiet runtime-version footgun explicitly rather than leave it scattered across the docs.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: void-ota-update-strategy
|
|
3
|
+
description: Ship JS-only fixes via EAS Update; full rebuild via EAS Build. Runtime versions, channels, rollback. The "when can I OTA vs when do I rebuild" decision.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ota-update-strategy
|
|
7
|
+
|
|
8
|
+
Use when planning to ship a change to an Expo app already in users' hands. The fundamental choice: **JS-only change** → EAS Update (minutes); **native change** → EAS Build + store submit (hours/days). Getting this wrong means either users running broken apps or unnecessary store rejections.
|
|
9
|
+
|
|
10
|
+
## The fundamental rule
|
|
11
|
+
|
|
12
|
+
| Change type | Path | Speed |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| JS bug fix (logic, UI text, layout) | EAS Update | Minutes |
|
|
15
|
+
| Adding a new screen (no new native dep) | EAS Update | Minutes |
|
|
16
|
+
| Changing a string in `app.config.ts` env | EAS Update if same plugins | Minutes |
|
|
17
|
+
| New `expo-X` package added to plugins | EAS Build + submit | Hours + store review days |
|
|
18
|
+
| Native config changes (Info.plist, permissions) | EAS Build + submit | Hours + store review |
|
|
19
|
+
| New native module | EAS Build + submit | Hours + store review |
|
|
20
|
+
| Bump SDK version | EAS Build + submit | Hours + store review |
|
|
21
|
+
|
|
22
|
+
When in doubt: if the change touches `plugins:` or `ios:`/`android:` config in `app.config.ts`, it needs a rebuild.
|
|
23
|
+
|
|
24
|
+
## Branches and channels (mental model)
|
|
25
|
+
|
|
26
|
+
EAS Update splits the publishing target in two layers:
|
|
27
|
+
|
|
28
|
+
- **Branch** — where you PUBLISH an update (`eas update --branch production`)
|
|
29
|
+
- **Channel** — what the BUILD reads from (declared in `eas.json` per profile)
|
|
30
|
+
|
|
31
|
+
Builds read from a channel; channels point at branches; you publish to branches.
|
|
32
|
+
|
|
33
|
+
```jsonc
|
|
34
|
+
// eas.json
|
|
35
|
+
"production": {
|
|
36
|
+
"channel": "production",
|
|
37
|
+
// ...
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This build is on channel `production`. By default, channel `production` points at branch `production`. To re-point: `eas channel:edit production --branch <other>`.
|
|
42
|
+
|
|
43
|
+
Publish to the production branch (so the production channel picks it up):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
eas update --branch production --message "Fix avatar crash on iOS"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
A user with a production-channel build pulls this update on next app launch.
|
|
50
|
+
|
|
51
|
+
Pre-launch / staging:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
eas update --branch preview --message "..."
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Users with a preview-channel build get this update; production users don't see it (different channel → different branch).
|
|
58
|
+
|
|
59
|
+
## Runtime versions — the critical detail
|
|
60
|
+
|
|
61
|
+
`expo-updates` checks **runtime version** before applying an update. If the update's runtime version doesn't match the build's, the update is silently skipped.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
// app.config.ts
|
|
65
|
+
runtimeVersion: { policy: 'fingerprint' },
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`'fingerprint'` is the modern policy: SDK + every native plugin + every native dep contribute to a hash. Add a native plugin → fingerprint changes → new builds have new runtime version → old builds won't accept new updates (which is correct: old build can't run the new code anyway).
|
|
69
|
+
|
|
70
|
+
Other policies:
|
|
71
|
+
|
|
72
|
+
- `'appVersion'` — uses `version` field (manual; you remember to bump it when adding native)
|
|
73
|
+
- `'sdkVersion'` — every SDK bump = new runtime (coarse; native changes within an SDK rev don't trigger)
|
|
74
|
+
|
|
75
|
+
`fingerprint` is the right default; the others are legacy. Don't use them on new apps.
|
|
76
|
+
|
|
77
|
+
## Update strategy: when does the user actually see the update
|
|
78
|
+
|
|
79
|
+
`expo-updates` config:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// app.config.ts (extra section or via expo-updates config block)
|
|
83
|
+
updates: {
|
|
84
|
+
enabled: true,
|
|
85
|
+
checkAutomatically: 'ON_LOAD', // check on every cold start
|
|
86
|
+
fallbackToCacheTimeout: 0, // 0 = use cache instantly, check in background
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
| `checkAutomatically` | When checks happen |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `ON_LOAD` | Every cold start |
|
|
93
|
+
| `ON_ERROR_RECOVERY` | Only after a crash |
|
|
94
|
+
| `NEVER` | Manual only (call `Updates.checkForUpdateAsync()` yourself) |
|
|
95
|
+
|
|
96
|
+
For most apps, `ON_LOAD` + `fallbackToCacheTimeout: 0` is right: instant launch with cached code, update applied silently on next launch.
|
|
97
|
+
|
|
98
|
+
If you need immediate apply (critical security fix), code-call:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import * as Updates from 'expo-updates';
|
|
102
|
+
|
|
103
|
+
async function applyUpdateIfAvailable() {
|
|
104
|
+
const update = await Updates.checkForUpdateAsync();
|
|
105
|
+
if (update.isAvailable) {
|
|
106
|
+
await Updates.fetchUpdateAsync();
|
|
107
|
+
await Updates.reloadAsync(); // forces immediate reload
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Show a confirmation UI before forced reload — don't yank the user out of their current task.
|
|
113
|
+
|
|
114
|
+
## Rollback
|
|
115
|
+
|
|
116
|
+
If an update is broken, deploy a fixed one — there's no "delete" for OTA updates, but newer wins.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
eas update --branch production --message "Revert: avatar crash fix had its own bug"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The new update reverts the change in code. Users get it on next check.
|
|
123
|
+
|
|
124
|
+
For a hard halt of bad updates, republish a known-good update at the head of the branch:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
eas update:republish --branch production --group <previous-good-update-id>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Or re-point the production channel at a known-good branch:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
eas channel:edit production --branch production-stable
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
This shifts every production-channel build to read the alternate branch.
|
|
137
|
+
|
|
138
|
+
## Testing updates before production
|
|
139
|
+
|
|
140
|
+
`eas channel:edit` lets you point a channel at any branch:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Build was on production, but you want to test an update from "experimental" branch
|
|
144
|
+
eas channel:edit production --branch experimental
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Test on real production-channel builds → switch back to production branch when validated:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
eas channel:edit production --branch production
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## What CAN'T go in an OTA update
|
|
154
|
+
|
|
155
|
+
- Anything touching native code (SDK bump, new module, config plugin change)
|
|
156
|
+
- Adding new permissions
|
|
157
|
+
- Changing the bundle ID, name, icon
|
|
158
|
+
- Splash screen image change (it's pre-bundled in the native shell)
|
|
159
|
+
|
|
160
|
+
These all require EAS Build + store submission. Plan ahead.
|
|
161
|
+
|
|
162
|
+
## Anti-patterns
|
|
163
|
+
|
|
164
|
+
- ✗ **`'appVersion'` runtime policy** — silently skips updates when devs forget to bump version. `fingerprint` removes this trap.
|
|
165
|
+
- ✗ **OTA-ing a native dep change** — update silently doesn't apply (different runtime version). Confusing for hours of debugging.
|
|
166
|
+
- ✗ **`Updates.reloadAsync()` without warning** — yanks user mid-task; bad UX
|
|
167
|
+
- ✗ **Same channel for staging + prod** — staging users get prod updates; prod users get unfinished work
|
|
168
|
+
- ✗ **No `extra` field for update metadata** — can't tell "which build is this user on" in Sentry / analytics
|
|
169
|
+
|
|
170
|
+
## Verification
|
|
171
|
+
|
|
172
|
+
After deploying an update:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
eas update:list --branch production --limit 5
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Shows recent updates with IDs, messages, runtime versions, dates. Spot the new one.
|
|
179
|
+
|
|
180
|
+
In the app, check:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import * as Updates from 'expo-updates';
|
|
184
|
+
console.log(Updates.updateId, Updates.createdAt);
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
This logs which update the user is currently on. Useful in a debug screen for QA.
|
|
188
|
+
|
|
189
|
+
## Composition
|
|
190
|
+
|
|
191
|
+
- `void-eas-build-profile` — channel names map to build profile names
|
|
192
|
+
- `void-expo-config-plugins` — plugin changes invalidate runtime versions
|
|
193
|
+
- `void-observability` — log `Updates.updateId` in every Sentry event so you can correlate "which JS is running"
|
|
194
|
+
- `void-async-safety` — `Updates.checkForUpdateAsync()` failures should not block app launch; timeout + fallback to cached
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Harness metadata for this skill. Not part of the Agent Skills
|
|
2
|
+
# specification, and never copied into a consumer project: the SKILL.md
|
|
3
|
+
# beside it stays portable and validates against the official six fields.
|
|
4
|
+
# Read by this repository's graph, and by the installer for `runtimes`.
|
|
5
|
+
kind: standard
|
|
6
|
+
owner: folpe
|
|
7
|
+
runtimes: [claude, codex]
|
|
8
|
+
enforcement:
|
|
9
|
+
floor: ci
|
|
10
|
+
inline:
|
|
11
|
+
claude: active
|
|
12
|
+
codex: active
|
|
13
|
+
hermes: ci-only
|
|
14
|
+
eval_targets: [claude/anthropic/opus]
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Sources for `void-dependency-direction` skill (see `docs/plans/skill-audits/void-dependency-direction.md` for the full audit):
|
|
2
|
+
|
|
3
|
+
- No external source. Native concretization of the `01-monorepo-layout.md` module's abstract `@repo/*` import rules into worked examples.
|
|
4
|
+
|
|
5
|
+
Strategy: native. Distills the module topology into concrete violation-plus-fix examples; core `void-hexagonal-architecture` is the generic doctrine, this is the monorepo-specific operational guide.
|