@omega.js/manager 0.1.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 +14 -0
- package/LICENSE +98 -0
- package/README.md +165 -0
- package/bin/omega +2 -0
- package/bin/omg +2 -0
- package/claude-plugin/.claude-plugin/plugin.json +11 -0
- package/claude-plugin/.mcp.json +9 -0
- package/claude-plugin/README.md +139 -0
- package/claude-plugin/hooks/gate/mark.sh +58 -0
- package/claude-plugin/hooks/gate/run.sh +97 -0
- package/claude-plugin/hooks/guard/run.sh +249 -0
- package/claude-plugin/hooks/hooks.json +63 -0
- package/claude-plugin/hooks/inject/run.sh +127 -0
- package/claude-plugin/hooks/lib/omega-gate.sh +20 -0
- package/claude-plugin/hooks/lib/omega-scope.sh +41 -0
- package/claude-plugin/hooks/lib/omega-skills.sh +209 -0
- package/claude-plugin/hooks/quality/run.sh +132 -0
- package/claude-plugin/hooks/shape/run.sh +45 -0
- package/claude-plugin/mcp-router-launch.js +29 -0
- package/claude-plugin/skills/README.md +48 -0
- package/claude-plugin/skills/accessibility/SKILL.md +31 -0
- package/claude-plugin/skills/analytics/SKILL.md +30 -0
- package/claude-plugin/skills/backend/SKILL.md +25 -0
- package/claude-plugin/skills/brandcheck/SKILL.md +29 -0
- package/claude-plugin/skills/browser/SKILL.md +64 -0
- package/claude-plugin/skills/client/SKILL.md +24 -0
- package/claude-plugin/skills/desktop/SKILL.md +25 -0
- package/claude-plugin/skills/extension/SKILL.md +25 -0
- package/claude-plugin/skills/main/SKILL.md +54 -0
- package/claude-plugin/skills/manager/SKILL.md +25 -0
- package/claude-plugin/skills/seo/SKILL.md +36 -0
- package/claude-plugin/skills/theme/SKILL.md +33 -0
- package/claude-plugin/skills/web/SKILL.md +29 -0
- package/dist/cli-run.js +34 -0
- package/dist/cli.js +29 -0
- package/dist/commands/build.js +17 -0
- package/dist/commands/clean.js +16 -0
- package/dist/commands/company.js +46 -0
- package/dist/commands/deploy.js +143 -0
- package/dist/commands/dev.js +437 -0
- package/dist/commands/devlog.js +25 -0
- package/dist/commands/help.js +48 -0
- package/dist/commands/manage.js +53 -0
- package/dist/commands/migrate.js +76 -0
- package/dist/commands/onboard.js +31 -0
- package/dist/commands/pipeline.js +319 -0
- package/dist/commands/test.js +380 -0
- package/dist/commands/update.js +128 -0
- package/dist/commands/version.js +8 -0
- package/dist/company-init.js +167 -0
- package/dist/company.js +283 -0
- package/dist/config.js +1359 -0
- package/dist/devlog/index.js +226 -0
- package/dist/devlog/lib/collect-commits.js +136 -0
- package/dist/devlog/lib/generate-post.js +201 -0
- package/dist/devlog/lib/ghostii.js +122 -0
- package/dist/devlog/lib/project-map.js +98 -0
- package/dist/devlog/lib/publish-website.js +88 -0
- package/dist/index.js +45 -0
- package/dist/lib/agents-md.js +201 -0
- package/dist/lib/analytics-secret.js +22 -0
- package/dist/lib/argv.js +21 -0
- package/dist/lib/auth-admin.js +120 -0
- package/dist/lib/automation-client.js +210 -0
- package/dist/lib/backend-marketing.js +49 -0
- package/dist/lib/brand.js +240 -0
- package/dist/lib/bundle-id.js +53 -0
- package/dist/lib/claude-settings.js +125 -0
- package/dist/lib/company-scaffold.js +213 -0
- package/dist/lib/company.js +227 -0
- package/dist/lib/config-flow.js +350 -0
- package/dist/lib/config-write.js +41 -0
- package/dist/lib/custom-target.js +75 -0
- package/dist/lib/domain-utils.js +19 -0
- package/dist/lib/duration.js +39 -0
- package/dist/lib/env-order.js +236 -0
- package/dist/lib/env-secret.js +78 -0
- package/dist/lib/firestore-rest.js +326 -0
- package/dist/lib/framework-bin.js +156 -0
- package/dist/lib/gitignore.js +63 -0
- package/dist/lib/google-auth.js +495 -0
- package/dist/lib/google-token.js +66 -0
- package/dist/lib/jwt.js +31 -0
- package/dist/lib/legacy-oauth.js +59 -0
- package/dist/lib/node-version.js +127 -0
- package/dist/lib/owner-plan.js +78 -0
- package/dist/lib/package-scripts.js +152 -0
- package/dist/lib/preflight.js +470 -0
- package/dist/lib/product-create.js +144 -0
- package/dist/lib/run-command.js +74 -0
- package/dist/lib/run-gates.js +70 -0
- package/dist/lib/run-output.js +42 -0
- package/dist/lib/run-summary.js +384 -0
- package/dist/lib/scaffold.js +427 -0
- package/dist/lib/service-input.js +207 -0
- package/dist/lib/service-runner.js +466 -0
- package/dist/lib/stale.js +24 -0
- package/dist/lib/target-selection.js +155 -0
- package/dist/lib/verb-fanout.js +128 -0
- package/dist/lib/verify-live.js +188 -0
- package/dist/manage.js +311 -0
- package/dist/omega-bin.js +7 -0
- package/dist/onboard.js +532 -0
- package/dist/services/account/ensure/users.js +259 -0
- package/dist/services/account/index.js +97 -0
- package/dist/services/account/lib/backend-client.js +107 -0
- package/dist/services/account/lib/password.js +36 -0
- package/dist/services/account/lib/resolve-password.js +100 -0
- package/dist/services/advertising/ensure/sites.js +101 -0
- package/dist/services/advertising/index.js +101 -0
- package/dist/services/advertising/lib/adsense-api.js +41 -0
- package/dist/services/ai/ensure/keys.js +21 -0
- package/dist/services/ai/index.js +36 -0
- package/dist/services/analytics/ensure/google-firebase-link.js +230 -0
- package/dist/services/analytics/ensure/google-streams.js +256 -0
- package/dist/services/analytics/ensure/meta-pixel.js +22 -0
- package/dist/services/analytics/ensure/tiktok-pixel.js +22 -0
- package/dist/services/analytics/index.js +134 -0
- package/dist/services/analytics/lib/analytics-api.js +156 -0
- package/dist/services/analytics/lib/meta-api.js +83 -0
- package/dist/services/analytics/lib/pixel-account.js +78 -0
- package/dist/services/analytics/lib/pixel-provision.js +152 -0
- package/dist/services/analytics/lib/pixel-specs.js +57 -0
- package/dist/services/analytics/lib/pixel-token.js +88 -0
- package/dist/services/analytics/lib/property-flow.js +77 -0
- package/dist/services/analytics/lib/tiktok-api.js +157 -0
- package/dist/services/analytics/lib/tiktok-auth.js +153 -0
- package/dist/services/assets/ensure/logo-gen.js +68 -0
- package/dist/services/assets/index.js +109 -0
- package/dist/services/assets/lib/assets-config.js +168 -0
- package/dist/services/assets/lib/brandmark-api.js +160 -0
- package/dist/services/assets/lib/derived.js +113 -0
- package/dist/services/assets/lib/font-loader.js +67 -0
- package/dist/services/assets/lib/reconcile.js +122 -0
- package/dist/services/assets/lib/reset.js +118 -0
- package/dist/services/assets/lib/svg-logo-generator.js +154 -0
- package/dist/services/assets/lib/svg-to-black.js +32 -0
- package/dist/services/assets/write/favicons.js +82 -0
- package/dist/services/assets/write/icons.js +64 -0
- package/dist/services/assets/write/process.js +96 -0
- package/dist/services/assets/write/reconcile.js +30 -0
- package/dist/services/assets/write/social-icons.js +108 -0
- package/dist/services/assets/write/templates.js +345 -0
- package/dist/services/bookmark/ensure/sync.js +255 -0
- package/dist/services/bookmark/index.js +17 -0
- package/dist/services/campaigns/ensure/contact-person.js +40 -0
- package/dist/services/campaigns/ensure/custom-fields.js +84 -0
- package/dist/services/campaigns/ensure/domain-auth.js +92 -0
- package/dist/services/campaigns/ensure/event-webhook.js +89 -0
- package/dist/services/campaigns/ensure/link-branding.js +187 -0
- package/dist/services/campaigns/ensure/list.js +50 -0
- package/dist/services/campaigns/ensure/segments.js +109 -0
- package/dist/services/campaigns/ensure/sender-identity.js +153 -0
- package/dist/services/campaigns/ensure/unsubscribe-groups.js +116 -0
- package/dist/services/campaigns/index.js +79 -0
- package/dist/services/campaigns/lib/dns-sync.js +81 -0
- package/dist/services/campaigns/lib/segment-query.js +138 -0
- package/dist/services/campaigns/lib/sendgrid-api.js +287 -0
- package/dist/services/captcha/ensure/site-key.js +83 -0
- package/dist/services/captcha/index.js +78 -0
- package/dist/services/captcha/lib/console-url.js +45 -0
- package/dist/services/captcha/lib/recaptcha-api.js +36 -0
- package/dist/services/certificates/ensure/api-key.js +22 -0
- package/dist/services/certificates/ensure/bundle-ids.js +115 -0
- package/dist/services/certificates/ensure/certificates.js +187 -0
- package/dist/services/certificates/ensure/profiles.js +143 -0
- package/dist/services/certificates/index.js +225 -0
- package/dist/services/certificates/lib/apple-api.js +183 -0
- package/dist/services/certificates/lib/certificate-manager.js +252 -0
- package/dist/services/certificates/lib/identifier-manager.js +107 -0
- package/dist/services/certificates/lib/keychain.js +135 -0
- package/dist/services/certificates/lib/manual-walkthrough.js +165 -0
- package/dist/services/certificates/lib/profile-manager.js +115 -0
- package/dist/services/chat/data/baseline-knowledge.md +96 -0
- package/dist/services/chat/ensure/chat.js +106 -0
- package/dist/services/chat/ensure/user.js +45 -0
- package/dist/services/chat/index.js +139 -0
- package/dist/services/chat/lib/baseline-knowledge.js +137 -0
- package/dist/services/cloud/ensure/authentication.js +267 -0
- package/dist/services/cloud/ensure/billing.js +120 -0
- package/dist/services/cloud/ensure/cloud-messaging.js +105 -0
- package/dist/services/cloud/ensure/database.js +60 -0
- package/dist/services/cloud/ensure/firestore.js +79 -0
- package/dist/services/cloud/ensure/functions.js +49 -0
- package/dist/services/cloud/ensure/hosting.js +398 -0
- package/dist/services/cloud/ensure/oauth-consent.js +248 -0
- package/dist/services/cloud/ensure/project-settings.js +72 -0
- package/dist/services/cloud/ensure/sdk-config.js +95 -0
- package/dist/services/cloud/ensure/service-account.js +152 -0
- package/dist/services/cloud/ensure/services.js +103 -0
- package/dist/services/cloud/ensure/storage.js +47 -0
- package/dist/services/cloud/index.js +130 -0
- package/dist/services/cloud/lib/access-heal.js +130 -0
- package/dist/services/cloud/lib/firebase-api.js +662 -0
- package/dist/services/cloud/lib/project-flow.js +102 -0
- package/dist/services/directory/ensure/entry.js +48 -0
- package/dist/services/directory/index.js +64 -0
- package/dist/services/directory/lib/blocks.js +104 -0
- package/dist/services/disperse/index.js +45 -0
- package/dist/services/disperse/write/certs.js +117 -0
- package/dist/services/domain/ensure/nameservers.js +179 -0
- package/dist/services/domain/index.js +69 -0
- package/dist/services/domain/lib/namecheap-api.js +167 -0
- package/dist/services/domain/lib/registrars.js +46 -0
- package/dist/services/domain/lib/whitelist-walkthrough.js +76 -0
- package/dist/services/edge/ensure/cache-rules.js +105 -0
- package/dist/services/edge/ensure/dns-records.js +304 -0
- package/dist/services/edge/ensure/email-routing.js +292 -0
- package/dist/services/edge/ensure/rules-configuration.js +98 -0
- package/dist/services/edge/ensure/rules-managed-transforms.js +101 -0
- package/dist/services/edge/ensure/rules-redirect.js +107 -0
- package/dist/services/edge/ensure/rules-response-headers.js +90 -0
- package/dist/services/edge/ensure/rules-security.js +142 -0
- package/dist/services/edge/ensure/speed-scheduled-tests.js +98 -0
- package/dist/services/edge/ensure/workers.js +219 -0
- package/dist/services/edge/ensure/zone-settings.js +108 -0
- package/dist/services/edge/ensure/zone.js +183 -0
- package/dist/services/edge/index.js +73 -0
- package/dist/services/edge/lib/cloudflare-api.js +84 -0
- package/dist/services/edge/lib/dns-records-helpers.js +518 -0
- package/dist/services/edge/lib/read-cache.js +16 -0
- package/dist/services/edge/lib/ruleset-helper.js +108 -0
- package/dist/services/edge/workers/omega-api-proxy.js +43 -0
- package/dist/services/email/data/baseline-filter.md +4 -0
- package/dist/services/email/data/baseline-knowledge.md +86 -0
- package/dist/services/email/ensure/agent.js +107 -0
- package/dist/services/email/ensure/user.js +45 -0
- package/dist/services/email/index.js +138 -0
- package/dist/services/email/lib/baseline.js +82 -0
- package/dist/services/email/lib/knowledge-file.js +53 -0
- package/dist/services/forms/ensure/form.js +46 -0
- package/dist/services/forms/ensure/user.js +45 -0
- package/dist/services/forms/index.js +138 -0
- package/dist/services/migrations/ensure/notifications.js +189 -0
- package/dist/services/migrations/ensure/orders.js +34 -0
- package/dist/services/migrations/ensure/payment-provider.js +132 -0
- package/dist/services/migrations/ensure/payments-intents.js +34 -0
- package/dist/services/migrations/ensure/state-retirement.js +266 -0
- package/dist/services/migrations/ensure/targets-rename.js +124 -0
- package/dist/services/migrations/ensure/users.js +1176 -0
- package/dist/services/migrations/index.js +101 -0
- package/dist/services/migrations/lib/attribution-touch.js +64 -0
- package/dist/services/migrations/lib/ensure-metadata.js +144 -0
- package/dist/services/migrations/lib/migration-runner.js +473 -0
- package/dist/services/migrations/lib/sanitize-strings.js +112 -0
- package/dist/services/migrations/lib/schema-validator.js +154 -0
- package/dist/services/monitoring/ensure/dsn.js +54 -0
- package/dist/services/monitoring/ensure/projects.js +135 -0
- package/dist/services/monitoring/index.js +54 -0
- package/dist/services/monitoring/lib/sentry-api.js +94 -0
- package/dist/services/newsletter/ensure/custom-fields.js +90 -0
- package/dist/services/newsletter/ensure/publication.js +94 -0
- package/dist/services/newsletter/ensure/segments.js +140 -0
- package/dist/services/newsletter/ensure/webhook.js +108 -0
- package/dist/services/newsletter/index.js +64 -0
- package/dist/services/newsletter/lib/beehiiv-api.js +124 -0
- package/dist/services/newsletter/lib/segment-automation.js +577 -0
- package/dist/services/payment/ensure/chargebee-account.js +30 -0
- package/dist/services/payment/ensure/chargebee-products.js +359 -0
- package/dist/services/payment/ensure/chargebee-webhook.js +113 -0
- package/dist/services/payment/ensure/paypal-account.js +36 -0
- package/dist/services/payment/ensure/paypal-products.js +374 -0
- package/dist/services/payment/ensure/paypal-webhook.js +128 -0
- package/dist/services/payment/ensure/stripe-account.js +93 -0
- package/dist/services/payment/ensure/stripe-disputes.js +56 -0
- package/dist/services/payment/ensure/stripe-products.js +245 -0
- package/dist/services/payment/ensure/stripe-radar.js +74 -0
- package/dist/services/payment/ensure/stripe-webhook.js +130 -0
- package/dist/services/payment/index.js +163 -0
- package/dist/services/payment/lib/chargebee-api.js +338 -0
- package/dist/services/payment/lib/payment-utils.js +116 -0
- package/dist/services/payment/lib/paypal-api.js +354 -0
- package/dist/services/payment/lib/provider-setup.js +134 -0
- package/dist/services/payment/lib/stripe-api.js +152 -0
- package/dist/services/repo/ensure/org.js +70 -0
- package/dist/services/repo/ensure/pages.js +72 -0
- package/dist/services/repo/ensure/repo.js +90 -0
- package/dist/services/repo/index.js +52 -0
- package/dist/services/repo/lib/github-api.js +196 -0
- package/dist/services/search/ensure/ga-link.js +57 -0
- package/dist/services/search/ensure/property.js +137 -0
- package/dist/services/search/ensure/sitemaps.js +70 -0
- package/dist/services/search/index.js +61 -0
- package/dist/services/search/lib/search-console-api.js +92 -0
- package/dist/services/seo/ensure/github-repos.js +271 -0
- package/dist/services/seo/index.js +55 -0
- package/dist/services/seo/lib/gh-api.js +161 -0
- package/dist/services/seo/templates/developer-tool/.github/workflows/maintenance.yml +25 -0
- package/dist/services/seo/templates/developer-tool/.nvmrc +1 -0
- package/dist/services/seo/templates/developer-tool/_README.md.js +121 -0
- package/dist/services/seo/templates/developer-tool/_package.json.js +33 -0
- package/dist/services/seo/templates/developer-tool/src/index.js +120 -0
- package/dist/services/seo/templates/index.js +57 -0
- package/dist/services/server/ensure/brands.js +63 -0
- package/dist/services/server/index.js +42 -0
- package/dist/services/testing/ensure/target-checks.js +97 -0
- package/dist/services/testing/index.js +11 -0
- package/dist/services/testing/lib/checks.js +467 -0
- package/dist/services/update/index.js +20 -0
- package/dist/services/update/lib/cache.js +59 -0
- package/dist/services/update/lib/fingerprint.js +157 -0
- package/dist/services/update/write/targets.js +206 -0
- package/dist/services/workspace/ensure/agents.js +42 -0
- package/dist/services/workspace/ensure/claude-settings.js +34 -0
- package/dist/services/workspace/ensure/config.js +54 -0
- package/dist/services/workspace/ensure/defaults.js +60 -0
- package/dist/services/workspace/ensure/env-keys.js +55 -0
- package/dist/services/workspace/ensure/env-order.js +66 -0
- package/dist/services/workspace/ensure/env-rules.js +97 -0
- package/dist/services/workspace/ensure/gitignore.js +18 -0
- package/dist/services/workspace/ensure/scripts.js +126 -0
- package/dist/services/workspace/ensure/structure.js +97 -0
- package/dist/services/workspace/ensure/translation-sdk.js +148 -0
- package/dist/services/workspace/ensure/workflows.js +54 -0
- package/dist/services/workspace/index.js +8 -0
- package/dist/vendor/account/engine.js +182 -0
- package/dist/vendor/account/features.js +220 -0
- package/dist/vendor/account/index.js +53 -0
- package/dist/vendor/account/schema.js +272 -0
- package/dist/vendor/account/subscription.js +38 -0
- package/dist/vendor/config/company.js +31 -0
- package/dist/vendor/config/defaults.js +173 -0
- package/dist/vendor/config/demo.js +18 -0
- package/dist/vendor/config/desktop-artifacts.js +110 -0
- package/dist/vendor/config/edit.js +769 -0
- package/dist/vendor/config/env-delivery.js +145 -0
- package/dist/vendor/config/env-rules.js +93 -0
- package/dist/vendor/config/env-schema.js +1078 -0
- package/dist/vendor/config/env.js +445 -0
- package/dist/vendor/config/hooks.js +97 -0
- package/dist/vendor/config/index.js +237 -0
- package/dist/vendor/config/instances.js +208 -0
- package/dist/vendor/config/load.js +490 -0
- package/dist/vendor/config/merge.js +68 -0
- package/dist/vendor/config/order.js +139 -0
- package/dist/vendor/config/ports.js +374 -0
- package/dist/vendor/config/providers.js +32 -0
- package/dist/vendor/config/repo.js +142 -0
- package/dist/vendor/config/retired-keys.js +430 -0
- package/dist/vendor/config/schema.js +1610 -0
- package/dist/vendor/config/secrets.js +50 -0
- package/dist/vendor/config/seed.js +34 -0
- package/dist/vendor/config/site-global.js +205 -0
- package/dist/vendor/config/validate.js +554 -0
- package/dist/vendor/config/winback.js +61 -0
- package/dist/vendor/devkit/attach-log-file.js +262 -0
- package/dist/vendor/devkit/certs.js +199 -0
- package/dist/vendor/devkit/ci-workflows.js +520 -0
- package/dist/vendor/devkit/cli-router.js +156 -0
- package/dist/vendor/devkit/command-path.js +46 -0
- package/dist/vendor/devkit/deploy-record.js +180 -0
- package/dist/vendor/devkit/flows.js +317 -0
- package/dist/vendor/devkit/local-https.js +360 -0
- package/dist/vendor/devkit/local.js +1905 -0
- package/dist/vendor/devkit/logger.js +128 -0
- package/dist/vendor/devkit/omega-bin.js +345 -0
- package/dist/vendor/devkit/prompt.js +187 -0
- package/dist/vendor/devkit/safe-install.js +18 -0
- package/dist/vendor/devkit/stop-signals.js +28 -0
- package/dist/vendor/devkit/test/scope.js +162 -0
- package/dist/vendor/devkit/translate/cache.js +84 -0
- package/dist/vendor/devkit/translate/engine.js +243 -0
- package/dist/vendor/devkit/translate/index.js +49 -0
- package/dist/vendor/devkit/translate/languages.js +134 -0
- package/dist/vendor/devkit/translate/providers.js +188 -0
- package/dist/vendor/devkit/update.js +569 -0
- package/docs/AGENTS.md +200 -0
- package/docs/account.md +45 -0
- package/docs/advertising.md +46 -0
- package/docs/ai.md +34 -0
- package/docs/analytics.md +69 -0
- package/docs/assets.md +57 -0
- package/docs/bookmark.md +26 -0
- package/docs/brand.md +151 -0
- package/docs/campaigns.md +96 -0
- package/docs/captcha.md +46 -0
- package/docs/certificates.md +68 -0
- package/docs/chat.md +47 -0
- package/docs/cloud.md +110 -0
- package/docs/company.md +64 -0
- package/docs/directory.md +140 -0
- package/docs/disperse.md +46 -0
- package/docs/domain.md +56 -0
- package/docs/edge.md +234 -0
- package/docs/email.md +42 -0
- package/docs/forms.md +48 -0
- package/docs/index.md +109 -0
- package/docs/migration.md +203 -0
- package/docs/migrations.md +56 -0
- package/docs/monitoring.md +41 -0
- package/docs/newsletter.md +48 -0
- package/docs/payment.md +83 -0
- package/docs/repo.md +48 -0
- package/docs/search.md +44 -0
- package/docs/seo.md +34 -0
- package/docs/server.md +37 -0
- package/docs/shared/agent-docs.md +89 -0
- package/docs/shared/analytics.md +612 -0
- package/docs/shared/brands.md +51 -0
- package/docs/shared/breaking-changes.md +497 -0
- package/docs/shared/config.md +1387 -0
- package/docs/shared/deploys.md +215 -0
- package/docs/shared/icons.md +201 -0
- package/docs/shared/local-dev.md +147 -0
- package/docs/shared/logging.md +202 -0
- package/docs/shared/monitoring.md +153 -0
- package/docs/shared/publishing.md +183 -0
- package/docs/shared/rulings.md +34 -0
- package/docs/shared/testing.md +147 -0
- package/docs/shared/theming.md +604 -0
- package/docs/shared/translation.md +291 -0
- package/docs/shared/updates.md +61 -0
- package/docs/testing.md +34 -0
- package/docs/update.md +36 -0
- package/docs/workspace.md +64 -0
- package/package.json +88 -0
package/docs/index.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# OMEGA Manager (@omega.js/manager)
|
|
2
|
+
|
|
3
|
+
The package's own long-form reference is [packages/manager/README.md](../README.md) — the wizard, the manage cycle, the services, and the CLI surface all live there.
|
|
4
|
+
|
|
5
|
+
Every verb is NAMED ([#229](https://github.com/Omega-JS-Stack/omega/issues/229)): `omega manage` is the whole service walk (the ONE name — no alias; `npm run manage` is the packaged form), `omega dev` boots the local stack (`npm start`), `omega deploy` publishes. A bare `omega` at a brand root prints help and runs nothing — it used to default into the walk, which meant an arg-less command quietly rewrote a brand.
|
|
6
|
+
|
|
7
|
+
The `omega:manager` router skill from the omega Claude plugin loads automatically in any project with `@omega.js/manager` (and inside `packages/manager` here); it points back at this map and the brand guide.
|
|
8
|
+
|
|
9
|
+
`omega pipeline` (the brand's live full-cycle test, detailed in that README) closes with the **verify sweep** ([#48](https://github.com/Omega-JS-Stack/omega/issues/48)): after the `--deploy=<targets>` legs run, `verify:site` / `verify:domain` / `verify:cloudflare` prove the launch surface answers from the outside, as scorecard rows judged exactly like deploy legs; `--verify` alone runs the sweep without deploying, and a dry run, a demo-* brand, or a brand with no cloud project records gated skips instead of touching the network. That is the one-command repo-to-live path (manage → deploy → verify) — the story lives in [deploys.md](shared/deploys.md), the code in `src/lib/verify-live.js`.
|
|
10
|
+
|
|
11
|
+
`omega dev` opens with a manage cycle before it spawns any app leg ([#44](https://github.com/Omega-JS-Stack/omega/issues/44)), so brand-level edits (the brandmark and its derived set, certs) are redistributed on every boot instead of going stale — what refreshes when is the redistribution contract in [local-dev.md](shared/local-dev.md). That cycle is the BOOT LANE ([#228](https://github.com/Omega-JS-Stack/omega/issues/228)): `config.js`'s `BOOT_SERVICES` — workspace, assets, disperse — the local file work the legs consume, about a second, while cloud reconciliation and rebuilds wait for `npm run manage` (or `omega dev --full`, the whole walk). It is the DELIVERY lane, and a boot is one of its two triggers ([#678](https://github.com/Omega-JS-Stack/omega/issues/678)): a brand-root `omega deploy` runs the same list before it fans out, so a publish never ships inputs a manage run happened to be current on. Secrets ride no lane at all — the brand-root `.env` is the ONE file humans and the manager edit, and every verb composes its target's runtime env from the cascade by schema (a running backend restages when that file changes). It also runs non-interactive: steps needing a human skip into the run summary's ⚑ pending list with their `npm run manage -- --service=<name>` hint rather than holding the terminal, the full preflight walkthrough still names what setup owes, and the legs boot regardless — only real errors stop the boot.
|
|
12
|
+
|
|
13
|
+
**Every way the boot is stopped unwinds the legs** ([#629](https://github.com/Omega-JS-Stack/omega/issues/629)): Ctrl+C (SIGINT), a supervisor's stop (SIGTERM) and a CLOSED TERMINAL (SIGHUP) all reach the same `shutdown`, registered off the ONE list every OMEGA supervisor imports, `STOP_SIGNALS` in [@omega.js/devkit's src/stop-signals.js](../../packages/devkit/src/stop-signals.js). It has to be the whole list, not a subset: the legs are spawned DETACHED so the orchestrator's stop can reach each leg's own grandchildren, which also means a signal the orchestrator does not listen for kills it outright and leaves every leg (the backend's emulator tree among them) orphaned with its ports. A second stop while one is running is a force kill, and a leg that has not gone in 20s is escalated, so no way of stopping the loop can wedge it.
|
|
14
|
+
|
|
15
|
+
**A write DELETES what the config no longer derives** ([#636](https://github.com/Omega-JS-Stack/omega/issues/636), Ian 2026-08-29): every generated output is derived from schema + config, so each write/disperse op that can enumerate its FULL expected set derives it, diffs against disk inside its OWN output boundary, and removes the strays after a successful write. Deleting a config key is a real removal, not a key that stops being read — drop `targets.extension` and the composed `.github/workflows/extension-*.yml` files a previous manage wrote are gone on the next walk. Human-authored files are NEVER touched: the reconcile is per-op and never a runner-wide sweep, it stays inside machine-owned output (the gitignored `.omega/assets/`), and in a dir that mixes the two — the brand root's `.github/workflows/` — a file goes only past a double lock, the GENERATED header AND the composed naming. What is deliberately NOT reconciled: a whole `targets/<dir>` on a key drop (it holds human app code), `.omega/certificates/**` (enumerable only with Apple API state, not from schema + config), a target's certs dir (its dests are documented HUMAN drop points an operator `cp`s into, so [disperse](disperse.md) stays warn-only), and values inside human-mixed files — root/target `package.json`, `AGENTS.md`, `CLAUDE.md`, `.claude/settings.json` stay heal-only, which is why disabling translation still leaves the SDK dep ([#168](https://github.com/Omega-JS-Stack/omega/issues/168)). The two surfaces are the [assets](assets.md) and [workspace](workspace.md) service docs.
|
|
16
|
+
|
|
17
|
+
**One walk FINISHES the job** ([#662](https://github.com/Omega-JS-Stack/omega/issues/662)): a service writes what it can, then WAITS on the external condition its own step still needs — a provider validating, DNS propagating, an async provisioning releasing — and completes that step in the SAME walk. The ONE mechanism is `pollWithSpinner` (`@omega.js/devkit/flows`): ENTER checks now, `s` skips, and a run with no TTY polls quietly or steps aside; `openBrowserAndPoll` is the same wait with a browser step in front of it, for a condition a human has to go and create. Every wait is gated on `canPrompt` (a dry run and a headless run never hold the terminal), bounded where the condition can go stale, and a wait that is skipped or runs out returns `status: 'warned'` with its `reason`, so the summary names what is still owed. "Set it and rerun" is not an outcome a service picks for itself — later runs are for CHANGES, not for finishing. The waits in the tree today: the edge service holds `emailurl.<domain>` grey until SendGrid validates the branded link and then proxies it in the same pass ([#646](https://github.com/Omega-JS-Stack/omega/issues/646)), the domain service waits for Cloudflare to assign a brand-new zone's nameservers before writing them at the registrar, the analytics service waits out GA's hold on a link it just deleted, and campaigns/cloud/search/edge wait on DNS propagation and zone activation the same way. Only a condition no wait can reach stays a rerun: a multi-day human review (AdSense approval), a value only a person can supply, a portal step with no API behind it.
|
|
18
|
+
|
|
19
|
+
A brand still carrying `apps/` instead of `targets/` ([#443](https://github.com/Omega-JS-Stack/omega/issues/443)) is not healed inside a run — nothing here carries standing backwards-compat machinery (Ian, 2026-08-21). Discovery FAILS LOUD on the old shape (`src/lib/brand.js`: the old shape is not a brand with zero targets, and walking it as empty would quietly do the wrong thing in every service) and names the one-time fix: `npx omega manage --migration=targets-rename --execute`, then `npm install`. That is a registered migration like any other (`src/services/migrations/ensure/targets-rename.js`) — an audit until `--execute`, moving the folder and flipping the root `workspaces` glob together, idempotent, and refusing a brand that carries BOTH folders rather than guessing which copy is real. It is the one migration a walk can never reach on the brand it fixes, so `runManage` runs it ALONE, ahead of the load that would throw. The consumer-facing row is in [breaking-changes.md](shared/breaking-changes.md#one-vocabulary--a-brands-surfaces-live-in-targets-443).
|
|
20
|
+
|
|
21
|
+
`omega migrate` at a brand root is the OTHER direction of the same idea ([#612](https://github.com/Omega-JS-Stack/omega/issues/612)): it deletes every retired key from `config/omega.json5` in place. `@omega.js/web`'s `omega migrate` converts a UJM brand and drops the retired keys on the way through; a brand ALREADY on omega.json5 used to get only the validator's error and an edit by hand — and editing an AUTHORED omega.json5 is the manager's lane. The list is `@omega.js/config`'s `findRetiredKeys` (both halves: renamed NAMES and retired PATHS, [config.md](shared/config.md)), matched against the file as WRITTEN rather than the merged view, so the path it prints is the line you would have deleted yourself. It prints one line per key naming the block that replaced it and why, writes through the comment-preserving editor (`removeConfigValues` — the key, its subtree and the comment documenting it go; every other byte stays), and is idempotent: a key that isn't there is skipped, so a converged brand's rerun leaves the file byte-identical. `--dry-run` prints the same plan and writes nothing. Moving each setting into its new home is still yours — the migrate removes the dead key, it does not guess the new value.
|
|
22
|
+
|
|
23
|
+
Both verbs are STEPS inside a bigger process, and that process has its own doc: [migration.md](migration.md), the per-brand playbook for taking a legacy brand (UJM/BEM/EM/BXM/WM) onto OMEGA ([#149](https://github.com/Omega-JS-Stack/omega/issues/149)). Six phases with checkable exit criteria — assessment against the register, config conversion, repo restructure, per-target migration, deploy and cutover, retirement — plus the trap register of silent-failure classes the first migration wave found. A run on a real brand is HARD GATED on Ian's explicit go, per brand.
|
|
24
|
+
|
|
25
|
+
## The setup contract — ask when needed, skip, or disable permanently
|
|
26
|
+
|
|
27
|
+
Every service that needs a credential or a one-time authorization behaves the SAME way ([#608](https://github.com/Omega-JS-Stack/omega/issues/608)). When a service runs and an input it needs is missing, it asks right then, through one helper (`src/lib/service-input.js`) and one gate (`confirmSetup`, the ONE home of that wording), with three outcomes:
|
|
28
|
+
|
|
29
|
+
- **Provide** — the Enter-gated open of the exact page that mints the value (ask permission, never auto-open), a masked paste, persisted to the brand `.env` and exported for THIS run, so a fresh brand configures itself mid-walk.
|
|
30
|
+
- **Skip for now** — this run steps aside; the next one asks again.
|
|
31
|
+
- **Disable permanently** — `<service>.enabled: false` lands in `config/omega.json5` (the tri-state opt-out) and nothing asks again until the line is deleted.
|
|
32
|
+
|
|
33
|
+
**The same rule reaches back to ONBOARDING** ([#770](https://github.com/Omega-JS-Stack/omega/issues/770)): a value the wizard cannot derive is a question it asks, not a hole the first manage walk discovers. `omega onboard` prompts for the contact PERSON right after the tagline — the human who signs the personal sends (welcome, discount nudge, checkup) — with the headshot URL and the link URL as skippable follow-ups, and writes `brand.contact.person` beside the derived `contact.email`. Non-interactive runs answer it with `--contactName` (plus the optional `--contactImage`/`--contactUrl`); without the flag NOTHING is written, because a human name can never be derived and a placeholder identity would sign a real brand's email — the campaigns gate ([#694](https://github.com/Omega-JS-Stack/omega/issues/694)) is what catches the gap at manage time. A resume run keeps the brand's existing value untouched.
|
|
34
|
+
|
|
35
|
+
**A needed value with no default is ASKED for, never warned about** ([#635](https://github.com/Omega-JS-Stack/omega/issues/635)). A service that finds a value missing opens the gate right then — env keys through `lib/service-input.js`, `config/omega.json5` values through `lib/config-flow.js`'s `resolveConfigValue` — and continues into the rest of its pass on what it just landed. "Set it and rerun" is not an outcome: a warn is what a run prints when nobody CAN be asked (headless), when the user stepped aside, or when there is no question to put (a Sentry token that sees zero orgs offers no choice).
|
|
36
|
+
|
|
37
|
+
**Non-interactive runs never prompt.** A CI run, a piped `omega dev` boot, or `--dry-run` prints a loud skip line naming the exact keys and where to mint them, and returns the machine-readable `missingEnv` list the run summary's 🔑 section aggregates. Values are never printed — names, labels, and mint URLs only.
|
|
38
|
+
|
|
39
|
+
**The run summary NAMES what warned** ([#643](https://github.com/Omega-JS-Stack/omega/issues/643)): the service runner records every warned or failed operation as `{ operation, reason }` on the service result, so the per-brand breakdown prints one `<operation>: <reason>` line per operation instead of a service-wide "some operations had issues". The reason is the operation's own one-liner — names and counts only, never values — and an operation that returns none is still named.
|
|
40
|
+
|
|
41
|
+
WHAT each service needs lives in ONE place, `src/config.js`'s `REQUIRES` registry, read by both halves: `lib/preflight.js` checks the whole enabled set up front and prints one consolidated fix walkthrough, and the helper asks for whatever is still missing at the moment of need. An entry marked `gates: false` is an OPTIONAL input — a second payment provider, one of two pixel platforms, an operator-tier service account — so its absence never gates the service and the operation that needs it asks in place, disabling only its own provider. Which keys OMEGA mints for itself and which a human acquires is the env schema's to say (`@omega.js/config`, [#581](https://github.com/Omega-JS-Stack/omega/issues/581)); `test/service-input.test.js` sweeps the registry against it, so a credential the manager reads but never declares fails the suite. A declared key the schema marks `generated:` is never PASTED — nobody can produce it but OMEGA, so the helper mints it in place (`lib/env-secret.js`'s `mintGeneratedKey`, the same mint the workspace `env-keys` op uses) and it never appears in `missingEnv`, because the 🔑 section names what a human must go and get. When such a key is the only thing missing no gate opens at all, headless runs included — which is how a payment or marketing service running before the workspace service still has `OMEGA_WEBHOOK_KEY` to build its forwarder URL from. When a pasted key is missing beside it, the gate runs FIRST and the mint happens only past it, so "Disable permanently" never leaves a freshly minted secret behind. A dry run names what it would mint and writes nothing.
|
|
42
|
+
|
|
43
|
+
The ONE instance where OMEGA MINTS a credential instead of taking a paste is the analytics service's TikTok token ([#448](https://github.com/Omega-JS-Stack/omega/issues/448)) — the portal authorization the setup walks, and what it does and does not persist: [analytics.md](analytics.md).
|
|
44
|
+
|
|
45
|
+
## Custom targets
|
|
46
|
+
|
|
47
|
+
Besides the framework targets, a brand runs targets the framework does not own — a Render API, a worker, a script ([#603](https://github.com/Omega-JS-Stack/omega/issues/603)). `targets.<name>: { type: 'custom' }` (or the array form, one entry per instance) declares one, and everything it can DO comes from its own `package.json` scripts: **`start`, `build`, `test`, `deploy`, `clean`**. The manager runs each verb through `npm run <verb>` when the script is present and skips it LOUDLY when it is absent — a verb that quietly did nothing is indistinguishable from one that worked. Nothing is inferred, defaulted, or synthesized: no script, no verb.
|
|
48
|
+
|
|
49
|
+
Discovery leaves `entry.target` NULL and sets `custom: true` (`src/lib/brand.js`), so the `filter((entry) => entry.target)` every framework service already applies skips custom targets by construction. Exactly ONE manage op reads the flag instead: the **workspace service**, which checks the declared dir exists and never counts it among the unmapped. No machine composes an `.env` file for it ([#678](https://github.com/Omega-JS-Stack/omega/issues/678)); it INHERITS the brand keys instead — `manage.js` loads the env chain into `process.env` before it spawns anything, so a custom target started by `omega dev`/`omega deploy` has them, while a standalone run inside the target dir does not (there is no `@omega.js/config` in there to walk the cascade). At the brand root, `omega dev` boots a custom target's `start` in the same terminal fan-out (only when that script exists), and `omega deploy` / `omega test` / `omega build` / `omega clean` run its `deploy` / `test` / `build` / `clean` — no framework flags forwarded, since a package script has no contract for them (which is why `omega deploy --dry-run` stops at the plan for a custom target instead of running its script). A scoped `omega test <framework:…>` never addresses one; a bare run does. The verb→run decision has one home, `src/lib/framework-bin.js`'s `resolveTargetRun`, so the framework lane and the custom lane cannot drift. Its `SCRIPT_LANE_VERBS` escape hatch — a framework whose CLI has no command for a verb, which the fan-out must run as the target's own script — is EMPTY today: every framework serves every fan-out verb, extension's `omega build` included since [#81](https://github.com/Omega-JS-Stack/omega/issues/81).
|
|
50
|
+
|
|
51
|
+
`omega build` and `omega clean` are themselves brand-root fan-outs over EVERY target type ([#603](https://github.com/Omega-JS-Stack/omega/issues/603) addendum): one shared walk (`src/lib/verb-fanout.js`, the two command files are its callers) with the deploy fan-out's target discovery, `--target=` picker and dependency order (backend first, custom targets last) — the selection and flag-forwarding helpers every fan-out shares live in `src/lib/target-selection.js`, which `commands/deploy.js` re-exports for its own readers. Targets are independent — neither verb publishes anything, so a failure never stops the walk and any failure exits 1. `--dry-run` is CONSUMED by the fan-out rather than forwarded (no framework's `build`/`clean` reads it, so a forwarded flag would really clean): every target prints the command it would have run and the walk executes nothing, on the framework lane and the custom lane alike. Config rules and the validator's errors: [config.md](shared/config.md) § Custom targets.
|
|
52
|
+
|
|
53
|
+
## A backend in custom-server mode
|
|
54
|
+
|
|
55
|
+
A brand's backend can run as its own server instead of Cloud Functions — `targets.backend.projectType: 'custom'` ([#584](https://github.com/Omega-JS-Stack/omega/issues/584)), the same `@omega.js/backend` app listening on `PORT` for a container host (Render & co). It is still a FRAMEWORK target, not a custom target: discovery maps it to `backend` as always and hangs the mode on the entry as `projectType`. What changes is only which verbs its framework can serve, and there are exactly two.
|
|
56
|
+
|
|
57
|
+
- **`omega deploy` and `omega test` take the SCRIPT lane** — `npm run <verb>` in the target dir, no flags forwarded, a loud skip when the script is absent, and `--dry-run` stopping at the plan. Those two are Firebase-shaped in the backend CLI (a Functions deploy, the emulator test run) and REFUSE in custom mode, so dispatching them would only produce a red target. `resolveTargetRun` is the one place that decides it (`src/lib/framework-bin.js`), which is why the custom-target lane and this one cannot drift.
|
|
58
|
+
- **`omega dev` boots the server, not the emulator** — the leg is the target's own `start` (`devLegFor`), and the backend keeps its place at the front of the default set: its API is what the website leg points at either way.
|
|
59
|
+
- **The workspace `scripts` op turns PER KEY instead of skipping the target** ([#689](https://github.com/Omega-JS-Stack/omega/issues/689)) — it still syncs the standard scripts whose verbs work in this mode (`test`, `test:static`) and leaves the brand its own `start`, `deploy`, `emulator` and `test:emulator`: never written, never scaffolded. The framework's `projectScriptsCustomOwned` declaration names them, so the list lives in one place.
|
|
60
|
+
- **The testing service stops demanding a `firebase.json`** a custom backend has no reason to carry — the ONE Firebase-only check in that service, noted with the reason instead of failed. The staged `dist/` check still runs: a custom backend builds like any other.
|
|
61
|
+
- **Everything else is untouched**: the rest of the workspace service, the update service's install + build, the assets and cloud services. The mode changes the ARTIFACT, not whether the brand is on Firebase — `firebase-admin` still loads, so a custom server that reads Firestore or verifies an ID token keeps working, and the services keyed on `cloud` gate on the cloud config exactly as before.
|
|
62
|
+
|
|
63
|
+
The framework side of the mode — what each refused verb prints, and what stays identical — is [backend/index.md](../backend/index.md); the deploy story is [deploys.md](shared/deploys.md).
|
|
64
|
+
|
|
65
|
+
Each brand-level verb tees its whole run to its OWN file, `<brandRoot>/logs/<verb>.log` — `manage`, `dev`, and since [#623](https://github.com/Omega-JS-Stack/omega/issues/623) `build`, `clean`, `deploy`, `update`, `test` and `pipeline` — truncated on each launch, ANSI-stripped, gitignored. They shared one file until [#231](https://github.com/Omega-JS-Stack/omega/issues/231), where booting the stack truncated the record of the last walk; the fan-outs wrote nothing at all until #623, so the walk's own verdict had to be stitched from the per-target logs. A fan-out log carries exactly that verdict — header, loud skips, summary — because each target spawns with stdio inherit and writes its own log. Grep them instead of re-running a manage or restarting the dev stack. The mechanism and the full per-surface path table: [logging.md](shared/logging.md); where every brand surface is authored versus generated: [brand.md](brand.md#the-placement-contract--where-a-thing-lives-and-what-refreshes-it).
|
|
66
|
+
|
|
67
|
+
## The services — one doc each
|
|
68
|
+
|
|
69
|
+
Every folder under `packages/manager/src/services/` carries its own doc here, named after the
|
|
70
|
+
service: what it reconciles, the config keys it reads and writes, and its gotchas. The order
|
|
71
|
+
below is `config.js`'s `SERVICE_ORDER` — the dependency order every walk runs in
|
|
72
|
+
(`test/service-docs.test.js` holds the two sets in parity, both directions).
|
|
73
|
+
|
|
74
|
+
| Service | What it reconciles |
|
|
75
|
+
|---|---|
|
|
76
|
+
| [workspace](workspace.md) | The brand monorepo itself: structure, config health, the agent-docs chain, the `.env` files |
|
|
77
|
+
| [repo](repo.md) | The brand's GitHub presence: org profile, the monorepo repo, Pages |
|
|
78
|
+
| [edge](edge.md) | The Cloudflare zone: DNS, email routing, settings, rulesets, speed tests, workers |
|
|
79
|
+
| [domain](domain.md) | The registrar's nameservers, pointed at that zone |
|
|
80
|
+
| [cloud](cloud.md) | The Firebase/GCP project: billing, APIs, hosting, auth, the data stores, the SDK config |
|
|
81
|
+
| [captcha](captcha.md) | The brand's own reCAPTCHA keys, proven valid |
|
|
82
|
+
| [analytics](analytics.md) | GA4 streams per target, the Firebase link, the Meta/TikTok pixels |
|
|
83
|
+
| [search](search.md) | The Search Console domain property, its verification and sitemaps |
|
|
84
|
+
| [advertising](advertising.md) | The domain's presence and approval state in AdSense (read-only) |
|
|
85
|
+
| [monitoring](monitoring.md) | One Sentry project per target, DSNs written back per target |
|
|
86
|
+
| [campaigns](campaigns.md) | SendGrid: domain auth, link branding, sender, list, unsubscribe groups, fields, segments, webhook |
|
|
87
|
+
| [newsletter](newsletter.md) | Beehiiv: publication access, fields, segments, webhook |
|
|
88
|
+
| [payment](payment.md) | Stripe/PayPal/Chargebee products, prices and webhooks; the Coinbase Commerce key ask (crypto reconciles nothing) |
|
|
89
|
+
| [forms](forms.md) | The brand's Slapform contact form and its owner account (operator only) |
|
|
90
|
+
| [chat](chat.md) | The brand's Chatsy agent, knowledge and owner account (operator only) |
|
|
91
|
+
| [email](email.md) | The brand's Replyify agent, filter and owner account (operator only) |
|
|
92
|
+
| [server](server.md) | The brand's registry entry on the company server's Firestore |
|
|
93
|
+
| [directory](directory.md) | The brand's own entry pushed into the PARENT project's `brands` collection (opt-in) |
|
|
94
|
+
| [assets](assets.md) | Derived logo variants, PSD exports, app icons, social icons, favicons |
|
|
95
|
+
| [certificates](certificates.md) | Apple signing certs, bundle IDs, provisioning profiles |
|
|
96
|
+
| [ai](ai.md) | The AI provider keys the backend calls with |
|
|
97
|
+
| [disperse](disperse.md) | Signing artifacts copied into the desktop/mobile targets |
|
|
98
|
+
| [seo](seo.md) | Parasite SEO GitHub repos |
|
|
99
|
+
| [update](update.md) | Installs deps and builds every target (incremental) |
|
|
100
|
+
| [account](account.md) | The required Firebase Auth accounts, their plans, and the admin audit |
|
|
101
|
+
| [migrations](migrations.md) | Firestore + on-disk migrations, audit until `--execute` |
|
|
102
|
+
| [bookmark](bookmark.md) | Brand bookmarks pushed to the companion Chrome extension |
|
|
103
|
+
| [testing](testing.md) | Health checks after everything else ran |
|
|
104
|
+
|
|
105
|
+
The [directory](directory.md) doc goes deepest, because that service's payload is a contract with the project ABOVE the brand: it holds the config a brand writes, the entry shape, and the legacy omega-manager `.brands/*/config.json` → entry field mapping.
|
|
106
|
+
|
|
107
|
+
The rung ABOVE a brand is the company workspace: [company.md](company.md) — what it owns (the inherited config layer, the `.env` that loads under every brand's, the shared Apple signing tree), the `.omega/company.json` stamp that is the whole link, and the two commands that make it one step each — `omega company init` scaffolds the workspace (idempotent; fills gaps only), `omega company adopt <brand-path>` stamps a brand into it.
|
|
108
|
+
|
|
109
|
+
The brand-root guide is [brand.md](brand.md) — what a session inside any consumer brand reads first (anatomy, verbs, brand hard rules, upstream-first). Every consumer brand's root `AGENTS.md` imports `node_modules/@omega.js/AGENTS.md`, a symlink the workspace service maintains at the monorepo's top-level `AGENTS.md` (the map) — contract: [agent-docs.md](shared/agent-docs.md). No package carries agent docs. The other half of that setup — the committed `.claude/settings.json` that enables the omega plugin in every session inside the brand — is the same service's `claude-settings` op: [workspace.md](workspace.md).
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Migrating a legacy brand onto OMEGA
|
|
2
|
+
|
|
3
|
+
> The per-brand playbook: the repeatable process that takes ONE legacy brand (a consumer of UJM / BEM / EM / BXM / WM, described by an `omega-manager` `.brands/<id>/config.json`) all the way to a live OMEGA brand monorepo. Six phases, each with exit criteria you can check by running a command or looking at a file.
|
|
4
|
+
|
|
5
|
+
**Who runs this.** A brand-migration session, working inside the BRAND. Read [brand.md](brand.md) first (brand-root anatomy, the verbs, the brand hard rules), then work down this file. The monorepo's own sessions do not run migrations; they close the framework issues a migration files.
|
|
6
|
+
|
|
7
|
+
**Boundaries, one job apiece.** [#148](https://github.com/Omega-JS-Stack/omega/issues/148) owns the breaking-changes register and the legacy-accommodation purge; this playbook POINTS at [shared/breaking-changes.md](shared/breaking-changes.md) per phase and never restates a row. [#40](https://github.com/Omega-JS-Stack/omega/issues/40) owns converter TOOLING (the `omega migrate` family); this playbook says when to run it, never how it is built. [#42](https://github.com/Omega-JS-Stack/omega/issues/42) (merged into [#149](https://github.com/Omega-JS-Stack/omega/issues/149)) owns the hard gate below.
|
|
8
|
+
|
|
9
|
+
## Before you start: the two standing rules
|
|
10
|
+
|
|
11
|
+
**1. The hard gate. Every run on a REAL brand starts with Ian's explicit go, per brand.** No exceptions, no "I was already in there". The gate is carried from [#42](https://github.com/Omega-JS-Stack/omega/issues/42): `somiibo` and `sweet-saucy` are named in it, and it binds every other real brand the same way. The first launch batch is `omega-brand`, ITW Creative Works, `somiibo`, `studymonkey`, `soundgrail`, `optiic`, `proxifly`; ITW carries one extra acceptance condition, its guest-post platform must work on the new stack. Two more standing facts: brands migrate SERIALLY (one at a time, so each new ruling sweeps the finished ones the same day), and the legacy password formula becomes a company owner hook (`config/hooks/account/password.js`, see [brand.md](brand.md#the-placement-contract--where-a-thing-lives-and-what-refreshes-it)) at migration time, never a re-implementation in the brand.
|
|
12
|
+
|
|
13
|
+
**2. Upstream-first, and file and skip.** A defect every consumer would hit belongs in the FRAMEWORK, not in this brand. File the omega issue and leave the consumer alone: the upstream fix repairs every brand at once, and a broken surface in an unlaunched brand costs nothing, because no brand goes live until the batch cuts over together. Retired anti-patterns from the first wave: hand-authored social shortlink redirect pages ([#429](https://github.com/Omega-JS-Stack/omega/issues/429) generates them from the socials config) and a brand-side built-output link test ([#430](https://github.com/Omega-JS-Stack/omega/issues/430) made it native to `omega test`). Never author consumer security rules that exist or belong at framework level: consumer rules are for brand-custom collections and docs only. The full rule, and its "within reason" line: [brand.md § Working locally against the framework](brand.md#working-locally-against-the-framework-upstream-first) and [shared/local-dev.md](shared/local-dev.md).
|
|
14
|
+
|
|
15
|
+
## Phase 1 - Assessment
|
|
16
|
+
|
|
17
|
+
Produce a per-brand worksheet ISSUE on the omega repo naming every contract this brand touches and the manual migration step for each. The worksheet is the migration's record: decisions, deferrals and carried legacy defects all land on it.
|
|
18
|
+
|
|
19
|
+
1. **Walk the register.** Work down [shared/breaking-changes.md](shared/breaking-changes.md), section by section, for every legacy framework this brand consumes. Copy nothing: cite the row. The cross-cutting section applies to every brand.
|
|
20
|
+
2. **Inventory the WHOLE legacy `.brands/<id>/` directory, not just `config.json`.** `ls` the directory and account for EVERY file on the worksheet. The non-obvious carriers that were dropped twice in a row: `chatsy.md` / `replyify.md` (live-agent knowledge sidecars, they belong at `config/` byte-identical, or the first `omega manage` OVERWRITES the live agents' brand knowledge), `seo.json` (becomes `config/seo.json5`, the parasite-SEO entries), and `_config.yml`'s `brand.type` (feeds the Organization JSON-LD `@type`).
|
|
21
|
+
3. **Read the state file too.** The Firebase SDK config is NOT in the legacy brand config: `.brands/<id>/config.json` carries only `firebase: { shared, projectId }`, and the full block lives in `.output/<id>/state.json` under `firebase.sdkConfig`. Every omega-manager-era conversion reads BOTH files. What each legacy state fact converts to is the register's [Two homes, not three](shared/breaking-changes.md#two-homes-not-three--the-omegastatejson-cache-retired-434) section.
|
|
22
|
+
4. **Diff the legacy `.env` key list against the composed one, and justify every key that did not carry.** Renaming a key is HALF the job: ported code still reading the old `process.env` name fails silently at runtime (one brand shipped `backendManagerKey: undefined` in an outbound body, swallowed). Grep the ported tree for every legacy env NAME as well as its value.
|
|
23
|
+
5. **Extension-bearing brands have a fifth source file.** The extension's own GA Measurement Protocol secret lives in the legacy `<brand>-browser-extension/config/browser-extension-manager.json` at `analytics.providers.google.secret`; the brand `.env`'s `GOOGLE_ANALYTICS_SECRET` is the BACKEND stream's. Account for that analytics block explicitly (a blank id/secret pair means no action).
|
|
24
|
+
6. **Inventory what the port can lose silently.** Each of these is a worksheet row before any code moves: every legacy COLLECTION (a collection nothing on the marketing pages reads drops silently and takes every byline with it), every legacy page's frontmatter KEYS (framework bands read them, and a band switched off drops the brand's own copy riding it), every hand-edit to the legacy `firestore.rules` (per-field protection added by editing the old managed block is not carried), every data-generated page FAMILY (build hook plus Firestore), and every EXTERNAL caller of the backend (crawlers, cron, sibling products, shipped apps).
|
|
25
|
+
7. **A brand that declares a connection owes two by-hand steps** ([#788](https://github.com/Omega-JS-Stack/omega/issues/788)). A worksheet row each, for every provider under the legacy `oauth2` block: rename its `OAUTH2_<PROVIDER>_CLIENT_ID`/`_CLIENT_SECRET` pair to `CONNECTIONS_<PROVIDER>_*` (brand `.env`, every `.env.<environment>` overlay, and the CI secrets), and register `<site>/connections/callback` as a redirect URI in that provider's own console BESIDE the old `/oauth2` one — a redirect URI is matched exactly, so the console edit lands before the deploy and the old URI is removed after it. The user DOCUMENTS are not by hand: `npx omega manage --migration=users --execute` moves them ([shared/breaking-changes.md](shared/breaking-changes.md#the-user-connection-feature-is-connections-788)).
|
|
26
|
+
8. **Scope the content re-architecture separately.** A UJM site whose layouts read page frontmatter (one brand: 145 pages) needs sections or `_data/` designed as its OWN work item before the port starts. Meta-only frontmatter is not residue; it is the new contract ([../web/frontmatter.md](../web/frontmatter.md)).
|
|
27
|
+
|
|
28
|
+
**Exit criteria**
|
|
29
|
+
|
|
30
|
+
- A worksheet issue exists on the omega repo, labelled for this brand, listing every register row that applies and its manual step.
|
|
31
|
+
- Every file in `.brands/<id>/` appears on the worksheet with its destination or an explicit "carries nothing" note.
|
|
32
|
+
- The legacy `.env` key list appears on the worksheet, each key marked carried (with its new name) or justified as dropped.
|
|
33
|
+
- The collections, frontmatter-key, rules-hand-edit, page-family and external-caller inventories are on the worksheet.
|
|
34
|
+
|
|
35
|
+
## Phase 2 - Config conversion
|
|
36
|
+
|
|
37
|
+
One target: a `config/omega.json5` at the brand root that the real loader accepts for every enabled target. Convert ONCE. Nothing dual-reads a legacy form (standing ruling, Ian 2026-07-06), so a key you leave behind is a validation error, never a silent fallback.
|
|
38
|
+
|
|
39
|
+
1. **Use the mapping tables.** [shared/config.md § Migration](shared/config.md#migration--legacy-configs--omegajson5) is the SSOT, one table per legacy framework plus the retired-name and retired-path lists. `npx omega migrate` in a website target converts the UJM half for you ([../web/index.md](../web/index.md)); everything else is by hand. On a brand ALREADY on omega.json5, brand-root `npx omega migrate` deletes the retired keys in place ([index.md](index.md)); it removes the dead key and never guesses the new value.
|
|
40
|
+
2. **Provisioned facts go to their REAL home.** A provisioned fact lands in `config/omega.json5` and a secret lands in the brand `.env`. Do not seed a `.omega/state.json`: its service-keyed content is retired and the file is now machine records only ([#434](https://github.com/Omega-JS-Stack/omega/issues/434)).
|
|
41
|
+
3. **Keep legacy secret VALUES under the renamed keys.** `BACKEND_MANAGER_KEY`'s value becomes `OMEGA_ADMIN_KEY`'s value: webhooks and parent/child callers still authenticate with the old value. AI keys carry to the bare vendor names (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, [#639](https://github.com/Omega-JS-Stack/omega/issues/639)); dropping them silently disables contact inference.
|
|
42
|
+
4. **GA streams are per target.** Legacy state carries one stream per surface. A website measurement id set at the SHARED level makes backend and extension inherit it and land their events in the website's stream. Override per stream at `targets.<type>.analytics.providers.google.id`, and give the extension its own `targets/extension/.env` with the extension stream's `GOOGLE_ANALYTICS_SECRET` (the target layer overrides the brand layer; verify it is gitignored).
|
|
43
|
+
5. **A comment is not an opt-out.** Every legacy `<service>: false` must land as an explicit `enabled: false` under its new home, or the framework default re-enables the service and the next interactive manage run offers to create an agent the brand opted out of. Example: `inbound: { email: { providers: { replyify: { enabled: false } } } }`. Grep the legacy config for every `: false` and prove each one landed.
|
|
44
|
+
6. **Add the FontAwesome root during conversion, not after the first warning-triangle build.** Pro-only icon names render the FA-free triangle with no error. Set `OMEGA_FONTAWESOME_ROOT` in the brand-root `.env` to the machine's staged Pro pack (fleet convention: `~/.omega/fontawesome`, holding `svgs/{solid,brands}`). It is a machine-local key and never enrolls as a CI secret; CI resolves Pro icons through the npm token lane instead ([shared/icons.md](shared/icons.md)).
|
|
45
|
+
7. **Check the legacy consent switch.** `consent.enabled: false` beside loaded analytics providers is a legal-posture contradiction the schema itself calls out ([shared/config.md § Consent](shared/config.md#consent-clientconsent--383)). Preserve legacy parity in the config and file the decision as its own brand issue.
|
|
46
|
+
8. **On ANY resumed migration, re-validate before trusting prior verification.** A paused migration's config rots: one brand validated clean on Aug 15 and no longer built on Aug 21, because four key shapes had retired in between. Re-run `loadConfig` for every target and reconcile against the register before continuing.
|
|
47
|
+
9. **Review with two lenses, then score.** Lens one is value fidelity (does every carried value match the source byte for byte); lens two is an exhaustive source-key WALK (does every key in every source file have an accounted-for destination). The key-walk lens is the one that found a dropped App Check `siteKey` and the GA stream regression; fidelity alone found nothing. Run both on every brand's conversion.
|
|
48
|
+
|
|
49
|
+
**Exit criteria**
|
|
50
|
+
|
|
51
|
+
- `loadConfig` returns zero errors for every enabled target, run fresh (not a remembered result). One legacy shape to expect here: `cloud.apiSubdomain` is a BOOLEAN (`false` skips the `api.{domain}` hosting domain), never the subdomain string, and the loader rejects the string form loudly.
|
|
52
|
+
- No retired key remains: brand-root `npx omega migrate --dry-run` prints nothing to remove.
|
|
53
|
+
- Every legacy service opt-out appears as an explicit `enabled: false`; every legacy secret value appears under its new key name in `.env`; no secret-shaped key appears in `config/omega.json5`.
|
|
54
|
+
- The two-lens review is recorded on the worksheet with its findings resolved.
|
|
55
|
+
|
|
56
|
+
## Phase 3 - Repo restructure
|
|
57
|
+
|
|
58
|
+
The shape is [brand.md § Brand root anatomy](brand.md#brand-root-anatomy): one repo, npm workspaces, `config/omega.json5` plus `.env` at the root, one dir per enabled target under `targets/`.
|
|
59
|
+
|
|
60
|
+
1. **Repo birth order matters, twice.** Let `npx omega onboard` write `.gitignore` FIRST, then enable any session tooling and merge its lines in by hand: onboard fills gaps only and never touches an existing file, so enabling tooling first means the brand ignore set silently never lands. A later manage run heals only `.omega/`, `logs/` and `.env.*`; **`.env`, `node_modules/` and `dist/` never land at all**, which is how a commit ends up staging `.env`. And `gh repo create --clone` on an empty repo inits a local `master` while the org default is `main`, so the first push fails on `src refspec main does not match any`: run `git branch -m master main` before anything else. **The new repo's NAME is `<brand.id>-omega`**, the `<brand.id>-<role>` rule ([#809](https://github.com/Omega-JS-Stack/omega/issues/809)): `omega` is the source monorepo's role, beside the `<brand.id>-releases` repo a desktop target publishes to. The framework derives both, so the brand config types no repo name at all; a brand that keeps a pre-rule name (a legacy repo nobody is renaming) declares it once with `repo.providers.github.repo`. Every legacy brand repo is PRIVATE, websites included; the new one is too.
|
|
61
|
+
2. **Merge the per-surface legacy repos into `targets/`.** `targets/website`, `targets/backend`, `targets/desktop`, `targets/extension` (register row: [omega-manager to @omega.js/manager](shared/breaking-changes.md#omega-manager--omegajsmanager)). The word `apps/` is retired everywhere ([#443](https://github.com/Omega-JS-Stack/omega/issues/443)).
|
|
62
|
+
3. **A brand that landed on `apps/` is fixed ONCE, by the registered migration.** `npx omega manage --migration=targets-rename --execute` (bare is the audit and moves nothing), then `npm install` at the brand root so npm re-links the workspaces. Nothing heals it inside a normal run and both folders at once fails loudly rather than guessing. Home: `packages/manager/src/services/migrations/ensure/targets-rename.js` ([migrations.md](migrations.md)). After it: grep the brand-AUTHORED files for `apps/` paths (the migration moves the folder, never your text), and re-validate the loader for both targets.
|
|
63
|
+
4. **Composed CI workflows come from the composer, never from a hand.** A hand-written file carrying the "GENERATED" header is both wrong and silently overwritten on the next real run: one brand's hand-written extension publish workflow used a workflow-level `defaults.run.working-directory`, which also applies to the steps BEFORE `actions/checkout`, so the job died on step 1. The composer emits per-step working directories instead ([shared/deploys.md § Scaffolded workflows](shared/deploys.md#scaffolded-workflows--no-push-triggers)). On every pass that touches a brand, recompose each composed workflow at framework HEAD and diff it: a diff that is not a path change is framework drift and lands as its own `fix(ci)` commit. Nested `targets/<target>/.github/` copies are framework-generated strays; delete them in the migration commit.
|
|
64
|
+
|
|
65
|
+
**Exit criteria**
|
|
66
|
+
|
|
67
|
+
- The repo root holds `config/omega.json5`, `.env`, `package.json` with a `targets/*` workspaces glob, and `AGENTS.md` + `CLAUDE.md` per the doc chain.
|
|
68
|
+
- `ls` shows `targets/` and no `apps/`, and `grep -r "apps/" ` over brand-authored files is empty.
|
|
69
|
+
- `npm install` at the brand root links every target; `npx omega manage --service=workspace` reports no unmapped target.
|
|
70
|
+
- Every `.github/workflows/*.yml` is composer output at framework HEAD, and no `targets/*/.github/` directory survives.
|
|
71
|
+
|
|
72
|
+
## Phase 4 - Per-target migration
|
|
73
|
+
|
|
74
|
+
One target at a time. A target is DONE when it builds, its suites are green, the linked local stack serves it, and its parity evidence is on the worksheet. Only then does the next target start. Lanes and scoping for every command below: [shared/testing.md](shared/testing.md#the-three-verification-tiers-what-runs-when).
|
|
75
|
+
|
|
76
|
+
**Three rules that hold for every target.**
|
|
77
|
+
|
|
78
|
+
- **A green build proves nothing about runtime.** Renamed identifiers, stripped frontmatter and dead bindings all fail silently. After every port, grep the consumer for every legacy identifier the framework renamed, then diff the rendered output against the legacy build.
|
|
79
|
+
- **Finish with ZERO unknown-arg warnings.** Each one is a renamed arg to map or a dead key to delete. One brand rendered the framework's own CTA subline on 13 pages, with 160 tests green, because UJM's `description` is OMEGA's `subheadline` and dead args elsewhere had muted the channel.
|
|
80
|
+
- **Legacy code is not a spec.** Port latent legacy bugs AS-IS for parity and flag each for a deliberate post-parity fix (fixing one changes behavior and cost). Leave an in-tree `carried from legacy` comment naming the worksheet issue, so nobody "fixes" one unannounced.
|
|
81
|
+
|
|
82
|
+
### Website (`@omega.js/web`)
|
|
83
|
+
|
|
84
|
+
1. Run `npx omega migrate --check` in `targets/website`, read every finding, then run `npx omega migrate`. It converts config, runs the codemods, and deletes the Ruby toolchain files ([../web/index.md](../web/index.md)). Its dependency-resolution scan is devkit's, shared with the backend's own `npx omega migrate` (backend phase, rule 10), so BOTH targets get scanned and neither can answer differently.
|
|
85
|
+
2. Move the consumer's own modules directory out of `src/assets/js/modules/` before the first build (the fleet used `js/libs/`). That lane is DELETED, not reserved: nothing builds it in any layer, so its files ship nowhere and the build warns naming the directory. Put a consumer theme at `src/themes/<id>`, not the target root.
|
|
86
|
+
3. A partial theme inherits classy's Bootstrap CONFIG, not just its CSS. Audit every knob classy re-values that the legacy theme left stock (type scale first) and restore through the `@forward 'omega:theme' with (…)` hatch. PREFIX the theme knobs before adding the hatch, or the un-prefixed names collide with the re-exported Bootstrap members.
|
|
87
|
+
4. Re-derive any overridden base layout from the CURRENT base. Never copy the legacy file: the includes it calls have moved.
|
|
88
|
+
5. Walk the band gates in both directions. Bands legacy showed must be ENABLED (several default `enabled: false`), demo surfaces legacy lacked must be switched OFF (the About photo band and the hero demo frame default ON with framework demo copy), and a page that authored copy for a band ENABLES the band and empties its demo args, never `false` (a `false` gate kills the brand's own copy riding the band). A legacy top-level `features:` frontmatter block is renamed (the index layout reads `resolved.bento`, and `features` is a config section now: [Plan limits become the features catalog](shared/breaking-changes.md#plan-limits-become-the-features-catalog-647)).
|
|
89
|
+
6. Pin the mastheads. A page that never set its own masthead renders the NEW theme's default, which differs from the old theme's. Pin legacy values through the sidecar heading args (`hero.headline` / `hero.headline_accent` / `hero.subheadline`), and pin the h1 for EVERY page in the parity suite with no exemptions: an exempted page is exactly where this class hides. The blog HUB is the usual miss (the framework ships "Thoughts, news and stories"): materialize it with `npx omega customize /blog` and set its hero copy in `blog.11tydata.json`.
|
|
90
|
+
7. Pin CONTENT COUNTS, not just title/description/h1. Counts computed from source data are what catch an empty grid: LiquidJS does not resolve outer-scope variables inside a `where_exp` expression string the way Jekyll's did, so a ported filter renders empty grids with a green build, a green smoke run and 153 green parity tests. Capture the term into the expression as a literal instead.
|
|
91
|
+
8. Spell page modules per page DIR (`js/pages/edit/index.js`), or reach for a `[name]` wildcard file for a page family. A 4-segment module is the historic silent drop ([#469](https://github.com/Omega-JS-Stack/omega/issues/469)).
|
|
92
|
+
9. Data-generated page families port as: a frozen data snapshot vendored at the TARGET root (never `src/`), the build hook's selection / filter / slugify logic replayed as `_data` plus pagination templates, and parity defined as exact URL-set equality per family against the deployed legacy tree. Reproduce the hook's bugs and file the fixes.
|
|
93
|
+
10. Diff every shortlink destination. Handle patterns can change destination ENTITY (`/company/` to `/in/`, `/artist/` to `/user/`); the `{ handle, redirect }` escape hatch restores the legacy target. Social shortlink redirect pages are generated from the socials config now, never hand-authored ([#429](https://github.com/Omega-JS-Stack/omega/issues/429)).
|
|
94
|
+
11. There is no successor lane for UJM `pricing.plans` frontmatter: pricing presentation belongs in config `payment.products`. A sidecar `plans` array REPLACES the composed plans and blanks every amount.
|
|
95
|
+
12. Sweep the built output for engine-dialect drift: LiquidJS `divided_by` is float where Jekyll's was integer, and markdown punctuation changed with the renderer ([#547](https://github.com/Omega-JS-Stack/omega/issues/547)).
|
|
96
|
+
13. Never author a web `redirects` config key. Templated redirect rules live at the edge under `edge.providers.cloudflare.rules.redirect`; an enumerable redirect is a redirect PAGE ([edge.md](edge.md), and the register's UJM Redirects row).
|
|
97
|
+
|
|
98
|
+
### Backend (`@omega.js/backend`)
|
|
99
|
+
|
|
100
|
+
1. Rename `assistant` to `ctx` at every call site, and DELETE `respond()`'s `{ sentry: true }` option: codes at or above 500 auto-capture now.
|
|
101
|
+
2. Consumer backends are src-first: legacy `functions/{routes,schemas,lib}` becomes `src/{routes,schemas,lib}`, and `dist/` is staged output.
|
|
102
|
+
3. Rename every ported test file to `<concern>.test.js`. The runner matches `*.test.js` only, and a plain `.js` suite reports zero tests with no error ([#481](https://github.com/Omega-JS-Stack/omega/issues/481)).
|
|
103
|
+
4. Convert declarative schemas whose fields are named `types` / `default` / `min` and friends to the zod `fields` form: the leaf detection collided with those names and resolved the whole schema to `{}`, so every request 400'd ([#256](https://github.com/Omega-JS-Stack/omega/issues/256)).
|
|
104
|
+
5. Trace the middleware order before ruling that a port changed a route's surface. A consumer route with no settings schema 500s BEFORE its handler in BEM and in OMEGA alike (an empty `schemas/` throws, it does not pass through). Entry-shape drift is the sibling class: a stale cron doc shape means the job silently never runs ([#495](https://github.com/Omega-JS-Stack/omega/issues/495)).
|
|
105
|
+
6. Prove rename-only parity by a NORMALIZED re-diff: the residue must be only the contract edits. The normalizer is what catches that OpenAI `assistant` ROLE STRINGS have to survive an `assistant` to `ctx` identifier rename.
|
|
106
|
+
7. Rules: the brand's `firestore.rules` is a pure SOURCE seed now and the framework half compiles in ([#255](https://github.com/Omega-JS-Stack/omega/issues/255), [#353](https://github.com/Omega-JS-Stack/omega/issues/353)). Convert with `npx omega migrate:rules`, which is a RUN-ALONE verb and never a side effect of another one; pre-family markers convert with `npx omega migrate:markers` ([#40](https://github.com/Omega-JS-Stack/omega/issues/40)). Decide the POSTURE first (phase 5, rule 1). Until then, pin the posture normalized-byte-equal against the real legacy file and re-declare any hand-edited per-field protection in your own `match /users/{uid}` block, which merges into the framework's.
|
|
107
|
+
8. Run the target checks READ-ONLY while the brand is not deploying: `npx omega test --offline` blocks every live mutation the checks would make (index deploys, bucket lifecycle, seeding) and downgrades them to reported warnings ([#284](https://github.com/Omega-JS-Stack/omega/issues/284)). Let the index sync run before hand-writing indexes: the live project drifts both ahead of and behind the legacy indexes file.
|
|
108
|
+
9. Prove the static lane is socket-free with a connect-trap preload: a static suite that opens a socket is a suite that will fail in CI for a reason unrelated to the port.
|
|
109
|
+
10. Run `npx omega migrate` in the backend target and declare every bare require it names ([#600](https://github.com/Omega-JS-Stack/omega/issues/600)). BEM's FLAT install answered a route's bare `require('fs-jetpack')`; OMEGA's does only by hoisting, and the requires that carry this are LAZY, inside the handler, so the module loads fine and the route 500s on the first real request. The verb REPORTS (file, line, fix) and installs nothing: which version a brand wants is the brand's call. Run it in `targets/website` too, where the same scan rides `npx omega migrate --check`.
|
|
110
|
+
11. Move every EXTERNAL caller before launch. The BEM `command:` wire format is gone: a legacy client posting `{ command: … }` to the bare `/backend-manager` prefix gets a silent 302, not an error. Each caller takes the one-line move to `POST /omega/admin/firestore` with a Bearer `OMEGA_ADMIN_KEY`. The URL alias itself stays on purpose ([Deliberate compatibility that REMAINS](shared/breaking-changes.md#deliberate-compatibility-that-remains)); the wire format does not.
|
|
111
|
+
|
|
112
|
+
### Desktop (`@omega.js/desktop`)
|
|
113
|
+
|
|
114
|
+
No fleet brand has migrated a desktop target yet, so this lane has no field-proven traps of its own: work the register's [electron-manager section](shared/breaking-changes.md#electron-manager--omegajsdesktop) row by row and add what you find to the trap register below.
|
|
115
|
+
|
|
116
|
+
1. Swap the dependency and every `require('electron-manager/…')` path in `src/main.js`, `src/preload.js`, the renderer components, `gulpfile.js` and the tests.
|
|
117
|
+
2. Rename the preload global (`window.em` to `window.desktop`), the theme attribute (`data-em-theme-set` to `data-omega-theme-set`), and the client bridge property (`manager.webManager` to `manager.omega`).
|
|
118
|
+
3. Move the config per the mapping table, including the per-OS move (`targets.mac` / `.win` / `.linux` to `targets.desktop.platforms.*`) and the signing strategy key.
|
|
119
|
+
4. On the signing box, by hand: the retired Windows runner logon keys are deleted and `WIN_CSC_LINK` is renamed to `WIN_EV_TOKEN_PATH`. Nothing reads the old names.
|
|
120
|
+
5. Expect the shared config schema to report findings the legacy config carried silently, and fix them rather than muting them.
|
|
121
|
+
|
|
122
|
+
### Extension (`@omega.js/extension`)
|
|
123
|
+
|
|
124
|
+
1. Swap the dependency and the require paths, rename `data-wm-bind` to `data-omega-bind`, the `bxm:` cross-context commands to `omega:`, and the runtime singleton to `manager.omega`. A consumer on a pre-2.0.0 hook layout runs `npx omega migrate` once.
|
|
125
|
+
2. Copy `config/icon.png` from the legacy repo. The packager silently PRUNES manifest icon entries whose files are missing, so a portless icon config ships an iconless extension. Copy `config/messages.json` too: it carries the store-visible name and description, and a missing one ships scaffold placeholders.
|
|
126
|
+
3. Verify the packaged build carries the extension's static IMAGES (the missing lane was [#259](https://github.com/Omega-JS-Stack/omega/issues/259)) and that the production manifest carries no dev origin: the manifest is a UNION merge with framework defaults, so a consumer can add but historically could not remove ([#260](https://github.com/Omega-JS-Stack/omega/issues/260)).
|
|
127
|
+
4. `theme.id` is shared config but theme sets are per framework: a custom website theme needs `targets.extension.theme.id` set, or the extension sass build dies unactionably ([#261](https://github.com/Omega-JS-Stack/omega/issues/261)).
|
|
128
|
+
5. Keep MV3 top-level constants synchronous with `%%% brand.url %%%` build tokens rather than a hardcoded host. Extension bundles get tokens only, never runtime dev ports.
|
|
129
|
+
6. Boot tests that evaluate inside a Firebase-initializing service worker need a timeout well above the 20s default on the first run after a build.
|
|
130
|
+
7. MV3 discipline survives the port: every listener registers above `new Manager()`, and only `setPanelBehavior` goes in the init promise.
|
|
131
|
+
|
|
132
|
+
**Exit criteria (per target, before the next target starts)**
|
|
133
|
+
|
|
134
|
+
- The target's production build exits 0 with zero unknown-arg warnings (`npx omega build` on every target type — the extension has the verb too since [#81](https://github.com/Omega-JS-Stack/omega/issues/81), and its `npm run build` is the thin alias of it).
|
|
135
|
+
- `npx omega test` is green in the target (project scope), including the parity suite this port added.
|
|
136
|
+
- The target is linked to the framework monorepo (`npx omega i local` in the target, ONE time, the link is durable) and the brand-root `omega dev` serves it, with the surface checked by hand against the legacy one ([shared/local-dev.md](shared/local-dev.md#brand-root-one-command-omega-dev-web--backend-together)).
|
|
137
|
+
- The parity evidence is on the worksheet: the identifier grep, the rendered-output diff, the per-band or per-route counts, and every carried legacy defect with its in-tree comment.
|
|
138
|
+
|
|
139
|
+
## Phase 5 - Deploy and cutover
|
|
140
|
+
|
|
141
|
+
Cutover is per BRAND, not per target. Nothing here runs before every target has passed phase 4.
|
|
142
|
+
|
|
143
|
+
1. **Decide the Firestore rules posture BEFORE the first deploy.** This is a launch decision, not a build detail: adopting the compiled artifact changes what the LIVE project enforces ([#522](https://github.com/Omega-JS-Stack/omega/issues/522)). Record the decision on the worksheet, run `npx omega migrate:rules` deliberately and alone, and re-pin the posture test to the decided state.
|
|
144
|
+
2. **Deploy with the verb.** `omega deploy` at the brand root fans out in dependency order (backend, then web, then extension and desktop), and each target's own deploy verb runs ([shared/deploys.md § The verb](shared/deploys.md#the-verb--omega-deploy-on-every-target)). Commits and pushes never publish anything.
|
|
145
|
+
3. **The real-Stripe step.** Put the REAL key in place, run `npx omega manage`, and CONFIRM the two Dashboard-only latches when the payment service prints their guidance: Radar rules and Enhanced Dispute Protection have no API, so `payment.providers.stripe.radarConfirmed` and `.disputesConfirmed` are the record that a human did it ([payment.md](payment.md)). A brand that skips this launches with the fraud posture unset and nothing to say so.
|
|
146
|
+
4. **Recompose the CI workflows LAST.** The composer's secrets block derives from the env schema, so its list changes whenever the brand's `.env` keys change: one brand's workflow went stale by exactly one secret between compose and commit. Recompose after the final `.env` is settled, diff, commit.
|
|
147
|
+
5. **Run the verify sweep.** `omega pipeline --verify` at the brand root runs `verify:site`, `verify:domain` and `verify:cloudflare` against the live surface and scores them like deploy legs ([shared/deploys.md § One command, repo to live](shared/deploys.md#one-command-repo-to-live--manage--deploy--verify-48)). A deployed brand that does not answer from the outside is not cut over.
|
|
148
|
+
|
|
149
|
+
**Exit criteria**
|
|
150
|
+
|
|
151
|
+
- The rules-posture decision is recorded on the worksheet and the live project matches it.
|
|
152
|
+
- `omega deploy` at the brand root exits 0 for every target in one run.
|
|
153
|
+
- The Stripe Radar and dispute latches are `true` in `config/omega.json5`, set by a real confirmation.
|
|
154
|
+
- Every composed workflow is regenerated after the final `.env` and committed.
|
|
155
|
+
- The verify sweep is green for the brand's canonical URL, its DNS, and its edge.
|
|
156
|
+
|
|
157
|
+
## Phase 6 - Retirement
|
|
158
|
+
|
|
159
|
+
1. **The legacy repo flips to read-only reference.** It keeps serving history and nothing more (AGENTS.md HARD RULE 1). Nothing in this playbook ever asks for a commit in a legacy repo.
|
|
160
|
+
2. **Delete the orphaned functions.** The `bm_*` Cloud Functions keep RUNNING after the `omega_*` deploy renames them: repoint every trigger, scheduler and webhook first, then delete the old ones from the Firebase project.
|
|
161
|
+
3. **Retire the central entry.** The brand's `.brands/<id>/` entry in omega-manager is dead once the brand carries its own `config/omega.json5`. Its DIRECTORY listing inverts rather than disappearing: the brand now PUSHES its own entry into the parent project's `brands` collection on every manage walk ([directory.md](directory.md)), so set `parent` and the opt-in blocks and let the walk write it.
|
|
162
|
+
4. **Legacy infrastructure entries migrate per the standing rulings.** The brand's registry entry on the company server is the `server` service's ([server.md](server.md)); anything else legacy-side is a worksheet row with a named owner, never an orphan.
|
|
163
|
+
5. **Redirect rules live at the edge.** There is no web-config redirect shape (the `targets.web.redirects` key shipped in 0.45.0 and was withdrawn). Legacy dashboard-authored rules move into `edge.providers.cloudflare.rules.redirect` so the zone has a declared source, and `omega manage` then owns the ruleset and REMOVES what config does not name.
|
|
164
|
+
6. **The deliberate aliases stay.** `/backend-manager/*` and the other rows in [Deliberate compatibility that REMAINS](shared/breaking-changes.md#deliberate-compatibility-that-remains) retire on their own named conditions, never because a migration finished.
|
|
165
|
+
|
|
166
|
+
**Exit criteria**
|
|
167
|
+
|
|
168
|
+
- The legacy repo is archived or otherwise marked read-only, and its README points at the brand monorepo.
|
|
169
|
+
- No `bm_*` function remains in the Firebase project, and every trigger/scheduler/webhook names an `omega_*` function.
|
|
170
|
+
- The brand's directory entry is present in the parent project and written by the brand's own walk.
|
|
171
|
+
- The `.brands/<id>/` entry is retired, and every legacy infrastructure row on the worksheet is closed.
|
|
172
|
+
- The worksheet issue is closed, pointing at the CHANGELOG entry or the cutover commit.
|
|
173
|
+
|
|
174
|
+
## Trap register
|
|
175
|
+
|
|
176
|
+
Classes that have no single phase step: framework regressions to note rather than work around, workarounds that a later ruling superseded, and lane gaps a port still hits by hand. Every issue below is the framework's own tracker, and each row is a VERIFICATION of what the framework now does, not a workaround. **A row's state is judged against the TREE this doc ships with, never against the issue's label**: a fix lands in the working tree before its issue closes, so read the code the brand is linked to and trust that.
|
|
177
|
+
|
|
178
|
+
| Class | What it looks like | Tracked as |
|
|
179
|
+
|---|---|---|
|
|
180
|
+
| Non-image static assets | Audio and video under `src/assets` never reached `dist/`, so a ported alarm sound or video 404s live with a green build | [#295](https://github.com/Omega-JS-Stack/omega/issues/295) |
|
|
181
|
+
| Taxonomy page meta | Every blog tag and category page shared the site title and description (one brand: 303 pages) | [#294](https://github.com/Omega-JS-Stack/omega/issues/294) |
|
|
182
|
+
| A green build that is not green | `omega build` exited 0 on fatal config errors, so a scripted build proved nothing | [#426](https://github.com/Omega-JS-Stack/omega/issues/426) |
|
|
183
|
+
| Opposite contracts on sibling args | The hero's two buttons obeyed opposite `enabled` rules, so a faithful port could invert them | [#438](https://github.com/Omega-JS-Stack/omega/issues/438) |
|
|
184
|
+
| Bands with no opt-out | A section rendering unconditionally with hardcoded demo copy: keep its phrases OUT of the placeholder tripwire until the gate exists, or the suite is red by design | [#456](https://github.com/Omega-JS-Stack/omega/issues/456) |
|
|
185
|
+
| Silently dropped page modules | A 4-segment page module shipped a page with no JS and no CSS, build green | [#469](https://github.com/Omega-JS-Stack/omega/issues/469) |
|
|
186
|
+
| Invisible test suites | BEM-era plain `.js` test files are invisible to the OMEGA runner and report a false pass 0 | [#481](https://github.com/Omega-JS-Stack/omega/issues/481) |
|
|
187
|
+
| Entry shapes that never run | A doc shape the runner never calls (a stale cron entry) means the job silently never fires | [#495](https://github.com/Omega-JS-Stack/omega/issues/495) |
|
|
188
|
+
| Section defaults that are live demo copy | An absent key ships the framework's demo sentence onto a brand page | [#512](https://github.com/Omega-JS-Stack/omega/issues/512) |
|
|
189
|
+
| A deploy chain that rewrites posture | `setup` plus `deploy` used to auto-fix a deferred `firestore.rules` in place, changing LIVE posture on the first deploy with no prompt | [#522](https://github.com/Omega-JS-Stack/omega/issues/522) |
|
|
190
|
+
| Punctuation and number dialects | Kramdown smart punctuation lost on every post; LiquidJS `divided_by` is float where Jekyll's was integer | [#547](https://github.com/Omega-JS-Stack/omega/issues/547) |
|
|
191
|
+
| Offline builds | `--cached-only` builds died offline on an auth-helper fetch | [#548](https://github.com/Omega-JS-Stack/omega/issues/548) |
|
|
192
|
+
| Legacy Jekyll collections | The converter DROPS the legacy `collections:` block and emits one manual-step note per collection, because a Jekyll collection carries no grouping `field` and `targets.web.collections` requires one. Verify: one note per legacy collection in the migrate report, and no `collections` key in the converted config. Then, BY HAND, declare `targets.web.collections.<name>` with its `field` (the dotted frontmatter path its category pages group on) and move the documents to `src/_<name>/` | [#589](https://github.com/Omega-JS-Stack/omega/issues/589) |
|
|
193
|
+
| `authorizedFetch` rewrite | The codemod carries the two dead shapes with the rename: it drops `response: 'json'` (the parsed body IS the return value) and rewrites `err.status` to `err.code`. Verify: no surviving `response: 'json'` or `err.status` on a rewritten call, and read the finding's own leftovers list (the `output` option, and the `/backend-manager/` route segment that is now `/omega/`) | [#594](https://github.com/Omega-JS-Stack/omega/issues/594) |
|
|
194
|
+
| UJM per-render globals | The codemod hoists `{% assign random_id = 100 \| omega_random %}` above the first bare `random_id` read (under the frontmatter fence where there is one) and is idempotent. Verify: every file that reads `random_id` carries the assign, since the un-assigned read renders EMPTY and hands every repeated block on the page one shared id | [#595](https://github.com/Omega-JS-Stack/omega/issues/595) |
|
|
195
|
+
| Bare requires in ported routes | A route's bare `require()` of a framework dependency resolved under the flat BEM install and does not under the linked OMEGA one. The scan runs on BOTH ported targets: `npx omega migrate` in the backend target and `npx omega migrate --check` in the website, one devkit module behind both. Verify: `npx omega migrate` names nothing in either target, and every package it named is in that target's own package.json (never installed for you) | [#600](https://github.com/Omega-JS-Stack/omega/issues/600) |
|
|
196
|
+
| Fleet fan-out | There is no cross-brand runner: serial, one brand at a time, is the process | [#431](https://github.com/Omega-JS-Stack/omega/issues/431) |
|
|
197
|
+
|
|
198
|
+
**Superseded, so do not carry them forward.**
|
|
199
|
+
|
|
200
|
+
- **Array concat workarounds are retired.** Sidecar arrays REPLACE layout defaults at HEAD, JSON and `.11tydata.js` alike ([#269](https://github.com/Omega-JS-Stack/omega/issues/269), [#543](https://github.com/Omega-JS-Stack/omega/issues/543)), so the renamed-key and `eleventyComputed` workarounds are unnecessary for a new port. Earlier ports' renamed keys still work and need no revert.
|
|
201
|
+
- **`omega setup` no longer exists**, so its live-mutation problem is not solved with PATH shims: the audit half runs inside `omega test` (`--offline` blocks every mutation) and the network half is a precheck inside `omega deploy`, opted out with `--no-secrets` on web, desktop and extension. The BACKEND has no separate precheck (its network steps are inside the verb itself), so it ACCEPTS `--no-secrets` and ignores it: parity of spelling, not of behavior ([#675](https://github.com/Omega-JS-Stack/omega/issues/675), [#284](https://github.com/Omega-JS-Stack/omega/issues/284)).
|
|
202
|
+
- **Rules are not deferred to a supervised `setup` pass** any more: `npx omega migrate:rules` is the run-alone verb, taken deliberately with the posture decision made first.
|
|
203
|
+
- **The `targets-rename` lib lives at `packages/manager/src/services/migrations/ensure/targets-rename.js`**, not at any `lib/` path, and the word `apps/` is retired throughout.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# The migrations service — Firestore and on-disk migrations
|
|
2
|
+
|
|
3
|
+
The `migrations` service runs migrations against the brand's Firestore collections and, for
|
|
4
|
+
`local: true` operations, against the brand's own files. It only runs when `--migration` is
|
|
5
|
+
set, so a normal manage walk never touches collection data — and even then a run is an AUDIT:
|
|
6
|
+
it prints what every fix WOULD do and writes nothing until `--execute`.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
npx omega manage --migration # every migration, audit only
|
|
10
|
+
npx omega manage --migration=users # one migration, audit only
|
|
11
|
+
npx omega manage --migration=users --execute # perform it
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
An unknown `--migration=<name>` lists the available ones and skips.
|
|
15
|
+
|
|
16
|
+
## The registered migrations
|
|
17
|
+
|
|
18
|
+
| Migration | What it converges |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `targets-rename` (local) | A pre-[#443](https://github.com/Omega-JS-Stack/omega/issues/443) brand's `apps/` folder → `targets/`, moving the folder and flipping the root `workspaces` glob together. |
|
|
21
|
+
| `notifications` | Push-subscription docs to the canonical shape: `uid` → `owner`, flattened `owner.uid`, legacy timestamps → `metadata.*`, `url` → `context.client.url`, the legacy `attribution.utm` blob folded into `first`/`last`, string trimming — then schema validation. |
|
|
22
|
+
| `users` | The canonical user schema: orphan docs deleted, `plan` → `subscription`, flat `subscription.id` → `subscription.product`, `oauth2.<provider>` → `connections.<provider>` with a `type: 'oauth2'` stamp and the original deleted ([#788](https://github.com/Omega-JS-Stack/omega/issues/788)), each moved record gaining the `identity.id` the connections route matches on — from Google's `sub` or Kick's `user_id`, as a string, deleting nothing ([#793](https://github.com/Omega-JS-Stack/omega/issues/793)), deprecated fields removed, timestamps reconciled against Firebase Auth's canonical creation time, auth/consent/attribution backfills, dynamic values generated, sentinels (`''`, `127.0.0.1`, `ZZ`, `Unknown`) normalized to null, `usage.*.period` → `usage.*.monthly`. |
|
|
23
|
+
| `orders` | `payments-orders`: the legacy `attribution.utm` blob → first/last touches. |
|
|
24
|
+
| `payments-intents` | The same fold on `payments-intents`, the upstream copy of the same degradation. |
|
|
25
|
+
| `payment-provider` | The [#428](https://github.com/Omega-JS-Stack/omega/issues/428) word rename's DATA half: the stored `processor` field → `provider`, across all five payment-touching collections (`users.subscription.payment.processor`, `payments-orders.processor`, `payments-intents.processor`, `payments-webhooks.processor`, `payments-disputes.alert.processor`). |
|
|
26
|
+
| `state-retirement` (local) | The retired `.omega/state.json` CONTENT into `config/omega.json5` + the brand `.env` ([#434](https://github.com/Omega-JS-Stack/omega/issues/434)), leaving the file's machine records alone ([#479](https://github.com/Omega-JS-Stack/omega/issues/479)). |
|
|
27
|
+
|
|
28
|
+
Register a new one by adding a handler in `src/services/migrations/ensure/` and an entry in
|
|
29
|
+
`config.js`'s `OPERATIONS.migrations`.
|
|
30
|
+
|
|
31
|
+
## Config and credentials
|
|
32
|
+
|
|
33
|
+
No config of its own. The Firestore migrations need a `backend` target and the brand's own
|
|
34
|
+
service account at `.omega/secrets/service-account.json` (Identity Toolkit + `FirestoreREST`,
|
|
35
|
+
not `firebase-admin`). The two `local: true` migrations need NEITHER — they only touch the
|
|
36
|
+
brand's own files.
|
|
37
|
+
|
|
38
|
+
## Gotchas
|
|
39
|
+
|
|
40
|
+
- **`targets-rename` is the one migration a walk can never reach** on the brand it fixes:
|
|
41
|
+
discovery FAILS LOUD on the old shape, so `runManage` runs it ALONE, ahead of the load that
|
|
42
|
+
would throw. A brand carrying BOTH folders is FATAL, not merged — which copy is real is a
|
|
43
|
+
guess. Run `npm install` afterwards so npm re-links `node_modules/<target>` at the new path.
|
|
44
|
+
- **Migrations without schema validation are deliberate.** The payment collections' shapes are
|
|
45
|
+
owned by the webhook pipeline, so `orders`, `payments-intents` and `payment-provider` touch
|
|
46
|
+
their one field and declare nothing else.
|
|
47
|
+
- **`payment-provider` is idempotent by construction**: no legacy key is a strict no-op, both
|
|
48
|
+
keys present keeps `provider` (written by current code, so newer) and drops the leftover, and
|
|
49
|
+
a `null` value still moves — `null` is what the schema stores for an account that never paid.
|
|
50
|
+
- **The `users` connections move follows the same rule** ([#788](https://github.com/Omega-JS-Stack/omega/issues/788)):
|
|
51
|
+
no `oauth2` is a strict no-op, both keys present keeps `connections` and deletes only the
|
|
52
|
+
leftover, and the original is deleted in the same write — the standing ruling on a migration
|
|
53
|
+
that MOVES a field. It runs ahead of the defaults backfill, or the backfill would write an
|
|
54
|
+
empty `connections` object the move then collides with.
|
|
55
|
+
- **Legacy one-offs stay in omega-manager.** Its other 25 registered migrations repair one
|
|
56
|
+
company's historical data, not the schema.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# The monitoring service — one Sentry project per target
|
|
2
|
+
|
|
3
|
+
The `monitoring` service (Sentry provider) creates one error-monitoring project per enabled
|
|
4
|
+
target — web, backend, desktop, extension — and lands each project's DSN in the brand's
|
|
5
|
+
config. It has no cross-service dependencies: its own API, its own token.
|
|
6
|
+
|
|
7
|
+
The cross-framework error-reporting contract — the doctrine, the switches, the release tags,
|
|
8
|
+
the capture seams — is [monitoring.md](shared/monitoring.md); this file is what the manage
|
|
9
|
+
walk provisions.
|
|
10
|
+
|
|
11
|
+
## What it reconciles
|
|
12
|
+
|
|
13
|
+
- **`projects`** — resolves the org (config wins, else the token's one visible org, written
|
|
14
|
+
back to `monitoring.providers.sentry.org`) and the team, then ensures one project per
|
|
15
|
+
enabled target.
|
|
16
|
+
- **`dsn`** — each project's client-key DSN written to
|
|
17
|
+
`targets.<type>.monitoring.providers.sentry.dsn`
|
|
18
|
+
([#425](https://github.com/Omega-JS-Stack/omega/issues/425)) through the comment-preserving
|
|
19
|
+
writeback. DSNs are PUBLIC by design and schema-pinned, so config is their home; only the
|
|
20
|
+
auth token is a secret. The resolved value always wins — a hand-set stale DSN is drift and
|
|
21
|
+
gets patched — and a converged rerun leaves the file byte-identical.
|
|
22
|
+
|
|
23
|
+
## Config
|
|
24
|
+
|
|
25
|
+
| Key | Meaning |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `monitoring.enabled: false` (or `monitoring: false`) | Skip. |
|
|
28
|
+
| `monitoring.providers.sentry` | The monitor is a KEY under `providers`: no entry means none chosen and the service skips. Any other key is "not a known monitor". |
|
|
29
|
+
| `monitoring.providers.sentry.org` | The Sentry org — resolved and written back when absent. |
|
|
30
|
+
| `targets.<type>.monitoring.providers.sentry.dsn` | Where each DSN lands. |
|
|
31
|
+
|
|
32
|
+
**Credential**: `SENTRY_AUTH_TOKEN` in the brand `.env` — a PERSONAL auth token with
|
|
33
|
+
`org:read`, `project:read`, `project:write`, `team:read`, `team:write`.
|
|
34
|
+
|
|
35
|
+
## Gotchas
|
|
36
|
+
|
|
37
|
+
- **Sentry's "organization tokens" cannot create teams or projects.** They are CI-scoped; only
|
|
38
|
+
a personal token works here, which is why the setup hint spells the scope list out.
|
|
39
|
+
- **A project with no active client key** cannot yield a DSN. The operation says to create one
|
|
40
|
+
and rerun — closing that with an API call (`createProjectKey`) is a separate enhancement, not
|
|
41
|
+
a wait: there is nothing to poll for.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# The newsletter service — the brand's publication (Beehiiv)
|
|
2
|
+
|
|
3
|
+
The `newsletter` service reconciles the brand's newsletter publication on Beehiiv: access to
|
|
4
|
+
the publication, `@omega.js/backend`'s custom fields, its segments (verify-only), and the
|
|
5
|
+
publication webhook pointed at the parent backend's forwarder.
|
|
6
|
+
|
|
7
|
+
## What it reconciles
|
|
8
|
+
|
|
9
|
+
- **`publication`** — resolved from `marketing.newsletter.providers.beehiiv.publicationId`,
|
|
10
|
+
else auto-matched by brand name/id across the account's publications. Publications CANNOT be
|
|
11
|
+
created via API: when nothing matches, the exact values to copy are printed, an interactive
|
|
12
|
+
run opens the create page and polls until the new publication auto-matches, and the resolved
|
|
13
|
+
id is written back into omega.json5.
|
|
14
|
+
- **`custom-fields`** — `@omega.js/backend`'s fields, honoring the provider skip list (Beehiiv
|
|
15
|
+
needs first/last name as custom fields but tracks country and UTM source natively). Beehiiv
|
|
16
|
+
matches subscriber values by DISPLAY name, so fields are diffed by `display`; a kind
|
|
17
|
+
mismatch is a delete + recreate.
|
|
18
|
+
- **`segments`** — Beehiiv has NO segment-create API. The read side lists what exists;
|
|
19
|
+
interactive runs offer to drive the dashboard UI through the companion Chrome extension
|
|
20
|
+
(trusted-event browser automation) or to open the dashboard for manual creation, then
|
|
21
|
+
RE-verify against the API so "created" means Beehiiv says so. Non-interactive and dry runs
|
|
22
|
+
never mutate: missing segments warn with human-readable conditions.
|
|
23
|
+
- **`webhook`** — the publication webhook carrying `subscription.unsubscribed`,
|
|
24
|
+
`subscription.deleted` and `subscription.paused` to the parent backend's forwarder. Matched
|
|
25
|
+
by its managed description first (stable across parent moves), then by URL; drift in
|
|
26
|
+
url/event_types/enabled is patched with the minimum diff.
|
|
27
|
+
|
|
28
|
+
Fields and segments `@omega.js/backend` does not own are never touched.
|
|
29
|
+
|
|
30
|
+
## Config
|
|
31
|
+
|
|
32
|
+
| Key | Meaning |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `marketing.newsletter.enabled: false` | Skip. |
|
|
35
|
+
| `marketing.newsletter.providers.beehiiv` | The vendor is a KEY under `providers` — no entry, no service. |
|
|
36
|
+
| `marketing.newsletter.providers.beehiiv.publicationId` | The publication; written back when resolved. |
|
|
37
|
+
| `parent` | Whose backend the webhook points at (`'self'` for the parent brand). |
|
|
38
|
+
|
|
39
|
+
**Credentials**: `BEEHIIV_API_KEY` in the brand `.env`; `OMEGA_WEBHOOK_KEY` is minted by the
|
|
40
|
+
setup contract, never asked for.
|
|
41
|
+
|
|
42
|
+
## Gotchas
|
|
43
|
+
|
|
44
|
+
- **A publication can be shared across sibling brands**, which is exactly why the webhook
|
|
45
|
+
points at the parent and the parent fans each event out per brand.
|
|
46
|
+
- **The segment automation targets the LAST matching element** on each dashboard row: Beehiiv
|
|
47
|
+
duplicates element IDs across condition rows, and its React inputs need the native value
|
|
48
|
+
setter plus synthetic input/change events.
|