npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@omega.js/manager

v0.56.0

Published

The OMEGA orchestration engine — walks every service in dependency order and reconciles each to the brand's omega.json5, idempotently, against a brand monorepo. omega-manager's brains, ported service by service. Private until first publish.

Downloads

1,100

Readme

@omega.js/manager

The OMEGA orchestration engine — omega-manager's brains, ported into the monorepo for the brand-monorepo world. npx omega manage at a brand root (the context-aware dispatcher hands brand roots here; the package's own direct bin is omega-manager, per the omega-<framework> convention) walks every service in dependency order and reconciles each one to the brand's config/omega.json5, idempotently: run it twice, get the same result. A bare npx omega prints help and touches nothing (#229).

npx omega manage               # the whole walk: all services against the brand containing cwd
npx omega manage --service=update     # one service
npx omega manage --dry-run            # preview update's install/build work without running it
npx omega manage --continue-on-error  # don't stop at the first failing service
npx omega manage --strict             # preflight failures (missing env/scopes) fail hard instead of skipping
npx omega manage --reset-assets       # force-refresh the derived assets cache (=logos or =templates for one kind)
npx omega manage --force              # rebuild every target, ignoring the update service's incremental cache
npx omega manage --migration          # audit the Firestore data migrations (=<name> for one); --execute writes
npx omega onboard              # create (or converge) a brand — wizard in a TTY, derivation otherwise
                               #   (incl. the managed-accounts step: keep the inherited list —
                               #    company config or the support@{domain} default — or write your own)
npx omega onboard --targets=web,backend   # in a brand: add the targets it lacks (removing one is by hand)
npx omega status               # what this folder is (empty, unrelated, template, half-done, brand, legacy),
                               #   what it lacks, and the next command; --json for scripts. Writes nothing, runs anywhere
npx omega deploy               # DELIBERATE publish fan-out: each target's own deploy verb, backend first
                               #   (--target= picks targets; every other flag forwards; docs/shared/deploys.md)
npx omega update               # dependency-freshness fan-out: each target's own update verb (targets
                               #   independent — one failure never blocks the rest; docs/shared/updates.md)
npx omega bump                 # print the brand's version; `patch|minor|major` writes the next one into
                               #   the root, EVERY target and the brand lock (#869). Never commits or tags
npx omega i local              # link every @omega.js dep from the local monorepo, brand-wide, in one call
npx omega i live               # restore the registry versions, brand-wide (--dry-run plans either flip)
npx omega translate --target=web   # any framework verb the manager has no command for passes through to
                               #   the targets that own it, args forwarded as typed (docs/manager/brand.md § Verbs)
npx omega help                 # command listing (also bare `npx omega`, -h, --help)

# The COMPANY layer: one command, run in the brand whose config says company: { id: 'self' }:
npx omega company init         # create the shared company/ tree in this brand (idempotent)

Runs at the brand root: inside targets/<name>/ (a backend's functions/ included) a verb the target's framework owns runs in place, and a brand-wide verb (one only the manager owns) refuses and prints the root form to run instead. status and onboard need no brand, so they run the manager from anywhere, inside a target included. One walk is always one brand: a company is a LAYER a brand names (see The company layer).

Two homes, one log

| Location | Contains | Lifetime | |----------|----------|----------| | config/omega.json5 | Every provisioned fact — user choices AND everything the run resolves (API-returned ids, zone ids, the confirmations no API can re-check). The same file every framework reads; no .brands/ mirror to disperse | Committed, human-edited, machine-written (comment-preserving) | | .env (brand root) | Every secret — loaded before any service runs; @omega.js/config hard-fails on secret-shaped keys in omega.json5 | Gitignored, per-machine | | .omega/runs/{ts}.json | Transient per-run output (counts, status flags, errors) | One file per process |

The old third bucket — the durable CACHE of derived data .omega/state.json carried — is retired (#434): it mostly duplicated what each idempotent ensure re-reads from the platform anyway, and it kept ids and secrets out of the homes the frameworks actually read. Brands that still carry that content convert with npx omega manage --migration=state-retirement --execute. The FILE lives on as the ONE per-machine RECORD file, sectioned per fact kind (#479): its deploy section is @omega.js/devkit's deploy record (written by every deploy verb, read by the testing service; the interim .omega/deploys.json of #449 folds into it on the first read or write), and future record kinds join as sibling sections. The migration removes only the sections it retires — every record section survives it untouched.

Service runner contract (ported verbatim from omega-manager)

Services declare operations in src/operations.js OPERATIONS; handlers live in src/services/{service}/{ensure,read,transform,write}/{operation}.js. Handler returns are strictly validated — allowed top-level keys are state, output, status, error only; anything else throws. state is the WITHIN-RUN carry: it accumulates into each later operation's serviceData and is never written anywhere. output lands in the run file. A fact that must outlive the run goes to config/omega.json5 via src/lib/config-write.js, or to the brand .env via src/lib/env-secret.js when it is secret-shaped. Statuses: success (✓), warned (⚠), error (✗), skipped.

Run-mode gates are standardized in src/lib/run-gates.js — never hand-compose them: canPrompt(options) (TTY and not dry-run — the only prompt gate), dryRunPlan(message, result?) (the canonical ⊘ Dry run — would <message> line, optionally passing a handler return through for one-statement gates), and needsInteractiveSkip(key, action, note?) (the warned step-aside carrying the needsInteractive marker the run summary aggregates into its ⚑ section with per-service rerun hints).

Env secrets gate through requestServiceInput (src/lib/service-input.js) — a service setup declares the vars its APIs need ([{ name, label, url }]): present → proceed; missing + interactive → masked ask, persisted to the brand .env, exported, run continues; missing + non-interactive → skip carrying missingEnv, which the run summary aggregates into its 🔑 section (exact keys + the interactive rerun command). Never hand-roll process.env.X skip checks in setups.

Which keys a service asks for, and when, is the env schema's (@omega.js/config: each key's askedWhen, one rule per asking service, and the format table for the ship keys), and ONE function answers it: missingEnvKeys(config, env, { verb: 'manage' }), with serviceAskedKeys(config, service) as one service's half. The REQUIRES registry (src/config.js) keeps only the manager's own fields per service: why (what the requirement buys), label, disablePath, prompted, gates and scopes (the Google OAuth scopes the service's API calls actually hit, each a member of google-auth's union, since every consent grants the union). No row holds a key name or a "when"; serviceInputs(service) lists what a service can ask for, from the schema.

Preflight runs the registry before any service (src/lib/preflight.js, modeled on cp236's google-auth 403 diagnostics): env presence (names only, values never print) and Google scopes against the token store's granted-scopes record: what IS knowable before a call; the live grant is only proven at call time, with the 403 diagnostics as the backstop. Failures print ONE consolidated walkthrough (what's missing, which service, why, the exact fix + mint URL, then the rerun verb) instead of N mid-run skips. Verdicts per failing service: run (interactive and the run itself collects the fix: the paste/consent flows), skip (the fix needs the operator; the walkthrough prints, the cycle continues, and the skip's missingEnv feeds the 🔑 aggregate), or error with --strict. It checks only the gating keys missingEnvKeys says each service asks of this brand: optional inputs (per-provider payment keys, operator-only service accounts, the ship keys) never gate a service, and the operation that needs one asks in place.

Services (ported so far)

| Service | Operations | What it ensures | |---------|-----------|-----------------| | workspace | structure, config, defaults, company, gitignore, scripts, agents, claude-settings, workflows, env-keys, env-order, env-rules, translation-sdk | Root targets/* workspaces; a dir per enabled target (dir→target via declared targets in the dir's omega.json5, else website*/backend*/… dir conventions); brand + target configs load and validate; the brand (and company) .gitignore is the marked file, its Default section healed every manage and its Custom section kept (a pre-marker file converges once); the root scripts speak omega (legacy omega-manager script VALUES healed in place with args preserved, and a deploy: 'omega deploy' script guaranteed); the agent-docs chain (the brand AGENTS.md import of node_modules/@omega.js/manager/AGENTS.md in its Default section, the brand's notes under Custom, the retired scope link removed; a linked manager's docs are synced by devkit's docs-sync boot prelude; no CLAUDE.md is written); the brand's committed .claude/settings.json names the published omega plugin (omega from GitHub, omega@omega on, omega@omega-local off) for every brand, and while the brand is linked to a monorepo its private .claude/settings.local.json names that checkout's local copy (omega@omega-local on, omega@omega off), those keys leaving again when it goes live; no other setting is ever touched, and the step then prints the install line when the plugin is missing from the machine and runs the two update commands when it is older than this OMEGA (agent-docs.md); the keys OMEGA generates for itself (the OMEGA_* trio plus UNSUBSCRIBE_HMAC_KEY) are minted into the brand .env when the cascade serves none (#569): one SSOT (@omega.js/config's env schema: the entries carrying a generated function; config.md) shared with the onboard stub, so a brand onboarded before a key existed gains it on the next manage instead of discovering the hole at the first send; a value the company .env already serves is never shadowed, a minted value is published into the run's own env so the SAME walk's disperse composes it into targets/backend/.env, and the run names the key it minted, never the value; the brand .env (and the company .env when the brand names a company) converges through devkit's marker engine onto the template lib/env-order.js renders from the env schema's groups (the ONE key inventory, config.md), shared with both scaffold stubs and every writeEnvValue writeback: a Default Values section rewritten on every run with every set value kept on its key's line, then a Custom Values section kept verbatim where unknown keys live, and a file with a line dotenv reads another way (e.g. KEY: value), which converging would change the value of, left untouched; a web target whose resolved config translates with the claude provider declares @anthropic-ai/claude-agent-sdk (at the range @omega.js/web declares as its optional peer) and one npm install at the brand root puts it in place (already declared is a zero-mutation no-op, translation off or provider chatgpt is untouched, and disabling translation never removes the dep) (translation.md) | | repo | repo, website, runners, secrets | Every repo the brand owns, from ONE block (repo: { provider, owner }, presence IS the switch, #883): the SOURCE monorepo <brand.id>-omega at the visibility the brand root's package.json private field states (absent = private), created empty when missing (you push, no clone) and reported (warned, never deleted) while it still serves the retired Pages; one website repo <brand.id>-<target name> per web target whose hosting.provider is github, carrying the target url as its homepage, with Pages pointed at gh-pages and the target host as the custom domain (a missing branch = "Pages configures after the first deploy", not an error); and the org RUNNER GROUP a public desktop brand's self-hosted Windows signer needs, which fails the walk with the checkbox link when no group allows public repositories (#872). The website repo is private only when the brand is private AND the owner is on a paid plan (Pages on a private repo is a paid feature), and every run says which visibility it chose and why. No repo NAME is configurable anywhere: @omega.js/config's sourceRepo/websiteRepo/releasesRepo derive them all. Auth via the gh CLI (gh auth login, or GH_TOKEN in the brand .env) through @omega.js/devkit/github-repo, the one GitHub-repo boundary. No repo block configured → clean skip | | edge | zone, dns-records, email-routing, zone-settings, cache-rules, rules-managed-transforms, rules-redirect, rules-configuration, rules-response-headers, rules-security, speed-scheduled-tests, web-analytics, workers | The zone matches edge.providers.cloudflare: zone created when missing (pending zones report their nameservers; interactive runs with a manual registrar open its nameserver page and poll until activation — API registrars defer to the domain service, non-interactive runs move on), platform DNS record set diff-synced (GitHub Pages A/AAAA, www, email-provider MX/SPF, DMARC; company extras are config-gated, not hardcoded: dns.dmarcReports, dns.bimiLogo, verification TXTs via dns.records; the SendGrid domain-auth set is read LIVE off SendGrid per run, never config — custom TXT records are additive and never suppress the SPF default), Email Routing forwarding rules when domain.email.providers.cloudflare (unverified destinations get a verification email; interactive runs open the dashboard and retry the rule once verified, others warn — rerun after verifying), ~40 zone settings diffed in one pass, the five ruleset families + managed transforms reconciled by rule name (stale rules removed), speed-test schedule, Cloudflare Web Analytics on or off per webAnalytics.enabled, worker scripts + routes. Auth via CLOUDFLARE_TOKEN in the brand .env; no token → clean skip. Subdomain brands (brand.url below the apex) use the parent zone and only run zone + dns-records. Reads cache to .omega/cache/cloudflare/{op}.json; zoneId lands in state | | domain | nameservers | The registrar's nameservers point at the Cloudflare zone. domain.providers.namecheap reconciles via the Namecheap API (NAMECHEAP_USERNAME + NAMECHEAP_API_KEY in the brand .env; already-set nameservers are a no-op; a domain not in the account yet warns and moves on); manual registrars ('squarespace', anything else) print the values to set only while the zone is pending — an active zone proves they're already set. SLD/TLD split uses the public suffix list (omega-manager popped one label, breaking .co.uk-style TLDs). The email half of domain config is consumed by cloudflare's dns-records + email-routing ops | | cloud | billing, services, project-settings, oauth-consent, service-account, hosting, firestore, database, authentication, storage, functions, cloud-messaging, sdk-config | The Firebase/GCP project matches cloud: {}: Blaze billing (cloud.billingAccount is config — omega-manager silently linked a hardcoded company account; unconfigured Spark projects warn with guidance), the 14 required Google Cloud APIs + compute deploy roles (diff-first — omega-manager batch-enabled every run), project display name + the 'Web App' web app (diffed, not blind-PATCHed), OAuth consent screen (cloud.supportEmail, default support@{domain}; its branding page — logo, home/privacy/terms links, authorized domains — has NO API either, so the run NAMES it as a manual step with the console link: links and authorized domains are safe anytime, a LOGO upload starts Google's verification review for External apps), the Admin SDK service account with diff-aware role grants and its once-only key (.omega/secrets/service-account.json, copied into the backend target's functions/), Hosting api.{domain} custom domains with ownership/ACME DNS written via Cloudflare in ONE pass (interactive runs then poll until Firebase verifies — writing any records it demands mid-poll; the ACME challenge appears once ownership passes — and finish by proxying the CNAME; non-interactive runs stay warned-pending and reruns converge), Firestore (nam5 + PITR), Realtime Database, Authentication (Identity Platform, email sign-in + privacy + anon auto-delete + password policy + authorized domains diffed; Google sign-in has NO Google API — instructions + warned until enabled; the OAuth redirect URIs (no API either) confirm interactively and stamp state, non-interactive runs warn), Storage, Functions readiness, FCM + the VAPID pair (missing keys are pasted in interactively with length validation and land in gitignored state; non-interactive runs warn), and the web SDK config (fetched to state with authDomain set to the BRAND host — first-party sign-in redirects under browser storage partitioning; the web build self-hosts Firebase's /__/auth/* handler files so the path resolves on the site domain; drift is written back into omega.json5 key-by-key via the comment-preserving editor — dry-run prints the paste-able block + warns instead). Auth: GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET in the brand .env (OAuth2 tokens cache to .omega/auth/; first run prints the auth URL); sign every consent as the COMPANY account — the consenting identity becomes the owner of every project the manager creates (src/lib/google-auth.js is the identity seam), so company ownership requires company consents; personal accounts are IAM members only, and access-heal.js repairs a wrong-identity project; no credentials → clean skip. cloud.shared: true runs only service-account + sdk-config | | captcha | site-key | The brand's OWN classic reCAPTCHA keys (the public site key in config at captcha.providers.recaptcha.siteKey, the secret in the brand .env as RECAPTCHA_SECRET_KEY, #893) actually work: the secret is proven valid via the documented siteverify endpoint (omega-manager only printed a checkmark); an invalid secret fails the service with .env guidance. A half-keyed brand fails too (#507): RECAPTCHA_SECRET_KEY set without captcha.providers.recaptcha.siteKey in config (the public half the client renders), or that site key set without the secret, is a silent 403 on every protected POST, so the one place that sees both halves fails loudly with the console link instead of proving the secret and calling the brand green; NEITHER half is the sanctioned unkeyed brand and stays green (#17). De-ITW: no default/shared key exists anywhere: missing keys → interactive runs ASK, each half through its own home's flow (the site key lands in omega.json5, the secret in .env), whose walkthrough mints the key at the GCP reCAPTCHA console (console.cloud.google.com/security/recaptcha) in the brand's own project, never a company-shared key (non-interactive → clean skip). Classic reCAPTCHA has no key-management API, so the key's domain list stays printed guidance: {domain} + www.{domain} + the console link (captcha.providers.recaptcha.project deep-links the Cloud Console key page; omega-manager hardcoded the company project). Interactive runs open the console, confirm the domain list once, and stamp it in config at captcha.providers.recaptcha.domainsConfirmed: the state file is retired (#434); a changed brand domain re-prompts | | analytics | google-streams, google-firebase-link, meta-pixel, tiktok-pixel | The brand's analytics match analytics.providers: one GA4 web data stream per enabled target (web → the root domain, backend → api., desktop/extension/mobile → virtual subdomains; found by URI, renamed on drift, enhanced measurement diffed before patching: omega-manager blind-PATCHed every run, and a clean Measurement Protocol secret each, regenerated until dash/underscore-free; GA's data-collection acknowledgement has no API → warned with the settings URL, rerun converges), the GA property ↔ Firebase project link (wrong-property links are moved to the configured property; a property linked to someone else's project warns; the project NUMBER comes from the cloud service's state: no second client; "link still propagating" → warned, rerun converges), and the Meta/TikTok pixels, created when missing and then token-checked (#417). Meta asks for nothing up front (Ian 2026-08-21, the GA4 half's near-zero-input standard applied to the pixel): an interactive pass leads with the uniform Yes / Skip / Disable gate, then walks to the page that mints the system-user token: the URL is printed and Enter opens it, asked every time, never auto-opened, takes the paste-in that saves META_ACCESS_TOKEN into the brand .env, and asks that token which ad accounts it can see (GET /me/adaccounts): exactly one is auto-selected with no prompt, several is a pick (brand matches first, with a "No Meta Pixel" opt-out in the list), none warns with the assign-an-asset fix. The resolved account lands in analytics.providers.meta.accountId, the pixel is created on it, and its id lands too, all in ONE run, so a brand configures Meta by saying Yes. With analytics.providers.{meta,tiktok}.accountId already in config (the Meta AD ACCOUNT / the TikTok ADVERTISER: non-secret platform ids live in config beside google.accountId, never in .env) and no id yet, the pixel is found BY NAME (the brand name) on the account before anything is created, then created via Meta's Marketing API (POST /act_<id>/adspixels) or TikTok's Business API (POST /open_api/v1.3/pixel/create/), and the resulting id is written back into omega.json5 (analytics.providers.{provider}.id, comment-preserving) so the same pass reports the pixel it just made; a configured id is converged proof and the platform API is never called. The access token is the gate for both halves (META_ACCESS_TOKEN / TIKTOK_ACCESS_TOKEN in the brand .env: the names @omega.js/backend reads; ONE token per platform serves both the create and the conversions sender, and Meta's is a Business Manager system-user token with ads_management): no token → warned with the where-to-get guidance and nothing is called, never a failure, and a run that cannot ask (no TTY, dry run) with nothing configured stays silent instead of nagging. analytics.providers.meta: false is the off switch: the tri-state opt-out (the gate's Disable and the picker's opt-out both write it) stops every call, prompt and warning for that provider: delete the line to be asked again. TikTok is the SCAFFOLD half (Ian 2026-08-21): the client, the config slot, and the wiring are complete and the token flips it on, but TikTok's app is not provisioned yet, so no brand carries the key and the create path has never run against the live API: it gets no token-page walk and no advertiser discovery until then, so its create still waits for a configured accountId. Pixel-token checks are unchanged for a configured id: missing → interactive runs offer the same guided paste-in that saves the token to the brand .env (the disperse service carries it into the backend's functions/.env), while leaving it empty, non-interactive, and dry runs keep the warned where-to-get guidance. Google auth: GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET (tokens cache to .omega/auth/google-analytics-tokens.json, separate scope from firebase). analytics.providers.google.propertyId is required config: interactive runs offer the account + property selection/creation flow (create-new provisions the GA4 property via the Admin API with the config time zone/currency) and land both ids in omega.json5; a brand of a company inherits the company's analytics.providers.google.accountId through the merge chain, so setup notes "defaulting to company account" instead of prompting the account picker (an explicit brand-level accountId always wins; the picker stays the fallback when no company default exists); without it (or without credentials) the google operations are filtered out and the pixel half still runs. Per-target measurementId + apiSecret land in state as streams.{target}, and each stream's measurement id also lands in config at targets.{target}.analytics.providers.google.id: the per-surface override every framework reads through the merge chain (the same shape the monitoring service uses for its DSNs; the resolved value wins, so a stale hand-set id is patched and a converged rerun leaves the file byte-identical). Until #417 the ids only ever reached state, so a brand's analytics.providers.google.id stayed null and each target's Measurement Protocol secret had no measurement id of its own stream to pair with | | search | property, ga-link, sitemaps | Search Console matches the brand: the domain property (sc-domain:{domain} — covers every subdomain) exists, verified in ONE pass when missing (DNS_TXT token → TXT record via Cloudflare with stale google-site-verification records replaced → verify — interactive runs poll until DNS propagates, non-interactive runs verify once with still-propagating DNS → warned, rerun converges; no Cloudflare token → the record to add manually; subdomain brands verify at their label in the apex zone); the GA association has NO API on either side — warned with the associations URL until confirmed — in a TTY the run asks and stamps gaLinked in state; non-interactive runs keep warning; sitemaps (search.providers.searchConsole.sitemapPaths, default /sitemap.xml) submitted only when MISSING — omega-manager resubmitted existing ones every run; brands without a web target skip submission. Auth: the same GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET (own token cache — webmasters + siteverification scopes). omega-manager's dead searchConsole.subdomains config key was dropped (the section is search.providers.searchConsole now) | | advertising | sites | The brand's domain is present in the configured AdSense account, with its approval state reported: READY = converged success; GETTING_READY/REQUIRES_REVIEW/NEEDS_ATTENTION warn with what Google is waiting on. The Management API v2 is read-only — no site add/config writes exist — so interactive runs open the add-site console page and poll until the domain appears; non-interactive runs warn with the exact deep-link and the rerun converges once it appears. advertising.providers.adsense.client (ca-pub-…) is required config — the provider-neutral advertising section, never a brand-named top-level key; the entry's PRESENCE is the opt-in (omega-manager defaulted the account to the company's shared one). Auth: the same GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET (adsense.readonly scope, own token cache); missing → clean skip | | monitoring | projects, dsn | One error-monitoring project per enabled target (web/backend/desktop/extension), reconciled against the Sentry API (new capability: omega-manager had no monitoring service; DSNs were hand-set). Org resolution: monitoring.providers.sentry.org from config wins, else the token's single org is used and written back (self-heal); multi-org tokens without config warn with guidance, and every org-scoped call follows the org's links.regionUrl (multi-region SaaS). Projects are {brand.id}-{target} under a {brand.id} team (ensured on first need) with per-target platforms (web/extension javascript, backend node, desktop electron); existing slugs are converged proof: nothing renamed or deleted. The dsn operation fetches each project's active client key and lands it in targets.<name>.monitoring.providers.sentry.dsn via the comment-preserving writeback: the exact key every framework's runtime reads through the config merge chain (DSNs are public by design; a hand-set stale DSN is drift and gets patched). Gates: a monitoring.providers.sentry entry (presence IS the pick, #425) + SENTRY_AUTH_TOKEN in the brand .env: a PERSONAL auth token with org:read, project:read, project:write, team:read, team:write (Sentry org tokens are CI-scoped and cannot create teams/projects); missing → clean skip with the 🔑 aggregate naming it | | campaigns | domain-auth, link-branding, sender-identity, list, unsubscribe-groups, custom-fields, segments, event-webhook, contact-person | The brand's email-marketing stack matches marketing.campaigns (provider key sendgrid): domain authentication created in one pass when missing (SendGrid's 3 DKIM/bounce CNAMEs diff-synced into the Cloudflare apex zone — exact match untouched, wrong content patched — then validated: interactive runs poll until DNS propagates, non-interactive runs validate ONCE with still-propagating DNS → warned, rerun converges; no Cloudflare token → the records to add manually, and validation still runs so a manual fix converges); a verified sender for Single Sends (offers@{contact domain}, unverified leftovers recreated) — CAN-SPAM's physical address comes from brand.address, required config: omega-manager silently stamped the company's mailing address on every brand; the brand's marketing list resolved config → state → name → create (the resolved id is written back into omega.json5 — marketing.campaigns.providers.sendgrid.listId — and mirrored in state); custom fields and segments from @omega.js/backend's marketing SSOT (required through the workspace — no filesystem climb into a sibling repo; type mismatches recreated, stale query_dsl patched in place with delete+recreate fallback, leaked __temp_ segments swept, [email protected]/backend fields/segments never touched); and the account-global Event Webhook pointed at the parent @omega.js/backend's forwarder (parent in omega.json5, 'self' for the parent brand — omega-manager defaulted it to the company URL; consent toggles enforced, tracking toggles untouched, drift patched with the minimum diff; needs OMEGA_WEBHOOK_KEY); and brand.contact.person.name, the human @omega.js/backend's welcome, discount-nudge and checkup emails sign off as — missing → the walk FAILS naming the key, instead of leaving three sends to throw on the first live signup. Auth via SENDGRID_API_KEY in the brand .env; missing → clean skip | | newsletter | publication, custom-fields, segments, webhook | The brand's newsletter matches marketing.newsletter (provider key beehiiv): the publication resolved config id → state id → auto-match by brand name (publications have no create API — nothing matching prints the exact name/description/subdomain values to copy into the dashboard + warned; interactive runs then open the create page and poll until the new publication auto-matches (non-interactive/dry runs stay warned); the resolved id is written back into omega.json5 — marketing.newsletter.providers.beehiiv.publicationId — with a state mirror); custom fields from @omega.js/backend's marketing SSOT, diffed by display name (Beehiiv matches subscriber values by display; each provider's view of the catalog comes from the SSOT's own fieldsForProvider(), skip lists included), kind mismatches recreated; segments — Beehiiv has no segment-create API: the API side only reads, so missing ones warn with human-readable conditions, and interactive runs offer to create them by driving the dashboard UI through the OMEGA Companion extension (trusted-CDP automation over its WebSocket protocol; conditions needing Beehiiv's built-in Signup-date attribute fail with the exact manual steps) or to open the dashboard for manual creation — either way the operation re-lists afterwards, so success means Beehiiv reports every segment (non-interactive/dry runs keep the warn and never mutate); the publication webhook points at the parent @omega.js/backend's forwarder (publications can be shared across sibling brands, so the parent fans events out), matched by its managed description first so it survives parent moves, drift patched with the minimum diff (needs OMEGA_WEBHOOK_KEY). Auth via BEEHIIV_API_KEY in the brand .env; missing → clean skip | | payment | paypal-account, paypal-webhook, paypal-products, stripe-account, stripe-radar, stripe-disputes, stripe-webhook, stripe-products, chargebee-account, chargebee-webhook, chargebee-products | The brand's payment providers match payment.products (priced, non-archived products only). Stripe: the account's business profile diffed to the brand (name/url/support email — unsupported account types warn instead of erroring; updateAccountInfo: false opts out), products + prices resolved config id → state → metadata match ({ brandId, productId } stamped on every product we create — lost state self-heals without duplicates) → create, with wrong-amount actives archived (Stripe can't delete prices) and the correct price created; the webhook matched by URL, re-enabled when Stripe auto-disabled it, enabled_events diffed; Radar rules and Enhanced Dispute Protection have NO API — printed as guidance + Dashboard deep-links, then confirmed interactively in a TTY (state-stamped radarConfirmed/disputesConfirmed); non-interactive runs stay warned. PayPal: live-vs-sandbox auto-detected by the auth probe; catalog products resolved config → state → exact-name match → create; billing plans matched by interval + amount + trial with duplicates and stale plans deactivated; legacy products' plans deactivated (paypal.legacyProductIds); webhook event_types diffed via JSON Patch. Chargebee: fully deterministic IDs (family = brandId, item = {brandId}-{productId}, price = …-{interval}) so resolution needs no stored state; item fields diffed, price amounts updated in place, legacy plans reported read-only; webhook URL carries &brand= (shared sites serve multiple brands). All webhooks point at api.{domain}/omega/payments/webhook (need OMEGA_WEBHOOK_KEY; cloud.shared brands skip them — no brand API exists), and every provider keeps exactly one endpoint on that host: any other endpoint whose URL carries the same host + webhook path (omega-manager's ?processor= twin, an endpoint minted with a rotated key) is stale — deleted with one loud line naming the redacted URL, since it received every event and answered 400 Missing provider parameter forever (#570). Endpoints on OTHER hosts are never touched: the account may run whatever else it likes. Product images come from brand.images.brandmark — omega-manager hardcoded the company CDN; the company Stripe org ID default is gone too. Auth per provider: STRIPE_SECRET_KEY / paypal.clientId + PAYPAL_CLIENT_SECRET / chargebee.site + CHARGEBEE_API_KEY (a provider set to false in config is off — the setup flow's Disable answer writes that; interactive runs offer the key-entry flow when an enabled provider is missing credentials: public keys land in omega.json5, secrets in the brand .env + process.env so the same run proceeds); no provider configured → clean skip. --provider=stripe\|paypal\|chargebee narrows the run. Product IDs are written back into omega.json5 (payment.products[id=…].{stripe,paypal}.productId) and mirrored in state | | forms | form, user | The brand's Slapform contact form (slapform.com) matches the brand — a Slapform-operator integration that writes into Slapform's own Firestore, via SLAPFORM_SERVICE_ACCOUNT in the brand .env (a path to the Slapform Firebase project's service-account JSON, absolute or brand-root-relative — omega-manager read the company-mode .output/slapform/secrets/; the client is plain Firestore REST + a service-account JWT, no firebase-admin dependency). The form document (forms.providers.slapform.formId — with the operator SA + forms.providers.slapform.templateFormId (the company layer's shape donor), a missing id MINTS the brand's OWN form: product user (email = brand contact email, password via the account service's owner channels) + doc shape-templated from the donor with the embedded id re-pointed, id written back into omega.json5, converged by the ensures in the same run (2b); interactive runs still offer the paste-back setup flow — Disable writes forms.providers.slapform: false; SLAPFORM_API_KEY is recognized as the future product-API tier) is diff-synced on name + enabled — omega-manager merge-wrote it on every run and silently created a name-only orphan document when the id was wrong; a missing form is now a visible error. The form-owner account (the form's owner UID) is set to forms.providers.slapform.plan (default: Slapform's Grandmaster top tier — omega-manager resolved it from the slapform brand's .brands/ config, a company-mode read) as an internal comp, patched with a leaf-field mask so Slapform-written subscription fields (trial, cancellation flags) survive. Missing formId or service account → clean skip; brands without a web target skip too (the form lives on the website) | | chat | chat, user | The brand's support chat agent on Chatsy (chatsy.ai) matches the brand — the forms pattern on Chatsy's Firestore (CHATSY_SERVICE_ACCOUNT in the brand .env, same shared REST client). The agent document (inbound.chat.providers.chatsy.agentId — with the operator SA + inbound.chat.providers.chatsy.templateAgentId, a missing id MINTS the brand's OWN agent the same 2b way as forms, id written back; interactive paste-back still offered — Disable writes inbound.chat.providers.chatsy: false; CHATSY_API_KEY recognized as the future product-API tier) is diff-synced on name, welcome message, language, auto-translate, brand details, and knowledge: the packaged baseline (support policies with the brand's URL/description filled in + pricing generated from payment.products — free/limits/trials formatted, archived skipped) plus the brand repo's config/chatsy.md appended when present (omega-manager read .brands/{id}/chatsy.md). Patches are leaf-masked so Chatsy-owned fields (owner, id, metadata) survive; a missing agent is a visible error. De-ITW'd: the agent image comes from brand.images.brandmark (omega-manager hardcoded the company CDN; no brandmark → the image isn't managed), the baseline's sponsorship line is inbound.chat.providers.chatsy.sponsorshipsUrl config (default {website}/contact — omega-manager hardcoded the company page), and a real omega-manager bug died in the port: it replaced {website} before inserting the generated pricing, so every agent's knowledge shipped with a literal {website}/pricing. The agent-owner account is set to inbound.chat.providers.chatsy.plan (default: Chatsy's Max top tier — the company-mode .brands/ read became config) via the shared owner-plan reconciliation. inbound.chat.providers.chatsy.updateAgentInfo: false skips the service (a shared agent managed by another brand); missing agentId or service account → clean skip; no web target skips too | | email | agent, user | The brand's customer-service email agent on Replyify (replyify.app) matches the brand — the forms/chat pattern on Replyify's Firestore (REPLYIFY_SERVICE_ACCOUNT in the brand .env, same shared REST client), gated on the backend target (the agent answers the brand's support email). The agent document (inbound.email.providers.replyify.agentId — with the operator SA + inbound.email.providers.replyify.templateAgentId, a missing id MINTS the brand's OWN agent the same 2b way as forms, id written back; interactive paste-back still offered; REPLYIFY_API_KEY recognized as the future product-API tier) is diff-synced on name, Gmail filter query (the brand filter from config/replyify.md's ---filter--- section, or auto-generated to:(@domain), ANDed with the baseline exclusions for transactional addresses), brand details, and knowledge: the packaged baseline support policies plus the file's ---knowledge--- section appended (omega-manager read .brands/{id}/replyify.md; all four file formats supported). Patches are leaf-masked so Replyify-owned fields survive; a missing agent is a visible error. De-ITW'd hard: omega-manager's baseline shipped the company's entire sponsorship business block (guest-post rules, the company sponsorship URL, its WELCOME10 code) and a hardcoded GIFT15 discount to every brand's agent — the sponsorship block is gone from the package (company business prose belongs in config/replyify.md), and the discount section renders only when inbound.email.providers.replyify.discount { code, label } is configured, so agents can never invent a code. The agent-owner account is set to inbound.email.providers.replyify.plan (default: Replyify's Max top tier — the company-mode .brands/ read became config) via the shared owner-plan reconciliation. inbound.email.providers.replyify.updateAgentInfo: false skips the service (shared agent); missing agentId or service account → clean skip | | server | brands | The brand's registry entry on the company server's Firestore (brands/{brand.id}) matches config — for operators whose parent backend keeps a registry of the brands it serves (webhook fan-out, cross-brand features). Only the whitelisted top-level sections cross (brand, github, sponsorships) and the document is replace-synced: read first, rewritten only on drift — where drift includes a leftover key in the registry, since a full replace removes keys dropped from config (omega-manager's merge: false semantic, but it overwrote the document blindly on every run with no dry-run guard, hardcoded the company project itw-creative-works, and read company-instance secrets from .output/; the port takes SERVER_SERVICE_ACCOUNT in the brand .env — same shared REST client, which gained a setDoc full-replace method here). No service account → clean skip | | directory | entry | The brand's own entry in the PARENT project's brands collection (brands/{brand.id}) — a PUSH, so a brand migrated onto OMEGA keeps its listing current from its own omega.json5 instead of from a central config store the way legacy omega-manager wrote it (#246). Opt-in and generic: three clean-skip gates — directory.enabled: true (absent config never pushes; the parent declares the collection world-readable in its own rules, so participation is never implicit), a parent naming the relationship, and not a demo-* brand (the cloud service's emulator-only gate). The payload is brand identity (brand.{id,name,url}) plus repo slugs from @omega.js/config's ONE derivation ({ owner, name, repo }, the same value the backend exposes as config.resolved.github — omitted rather than pushed half-resolved) plus the opt-in BLOCKS the brand declares; sponsorships (acceptable, unacceptable, prices per placement type) is the first, and the next is one entry in lib/blocks.js plus a schema line. Read first, written only on drift, and the write is a merge with an updateMask over the framework-owned sections — unlike server's replace, because the hub keeps its own fields on the same document and the framework owes only the entry (a section dropped from config is still removed: its path stays in the mask). Auth is DIRECTORY_SERVICE_ACCOUNT in the brand .env (path to the parent project's service-account JSON) — no service account → clean skip. Config shape, entry shape and the legacy→doc field mapping: docs/manager/directory.md | | assets | logo-gen, process, templates, icons, social-icons, favicons | The brand's derived visual collateral generated locally (no external API) from its logo sources: assets/logo/*.svg in the brand repo (committed collateral; omega-manager read .brands/{id}/assets/) into the gitignored .omega/assets/ (omega-manager's .output/{id}/assets/). logo-gen: wordmark + combomark SVGs from the brandmark + brand name + brand.font via opentype.js text-to-path (missing-only: a hand-tuned file is never overwritten; de-ITW'd: omega-manager packaged the company's commercial fonts and defaulted every brand to CromaSans: the port ships no fonts, brand.font resolves from the brand's assets/fonts/ or system dirs; unset → noted and skipped, configured-but-missing → error). process: color + all-black SVG variants and PNG size ladders per source. templates: brand-editable PSDs at assets/templates/{name}.psd, seeded on first run from the company tree's assets/templates/ (de-ITW'd: omega-manager baked ITW's binary templates into its own src/defaults/): logo layers re-rastered from the brandmark (black variant for the macOS tray template) and text layers filled from config with the font auto-shrunk to fit, via ag-psd + node-canvas, then composited to PNG exports (og-image, app icons, Chrome-store promos); manual Photoshop edits persist and re-export on the next run (replaced TEXT renders after the PSD is opened + saved once: PSDs store per-layer rasters; the logo layers are re-rastered here so they're always current). icons: macOS .icns + Windows .ico (from the templates-composited icon.png when present, brandmark otherwise). social-icons: brandmark on white for profile pictures. favicons: the web favicon set + site.webmanifest (content-diffed against config). Every operation is mtime-diffed: only missing/stale outputs regenerate, which replaced omega-manager's --onboarding gate on the write ops (it regenerated blindly with no dry-run guard). --reset-assets (#214) forces the rebuild timestamps would skip: it clears the derived cache first, so the same walk regenerates it. Bare resets both kinds; --reset-assets=logos clears the logo variants + app icons + social icons + favicons, --reset-assets=templates the PSD exports, and the run names what it cleared. Only .omega/assets/ is cache: the brand's committed assets/logo/*.svg and assets/templates/*.psd sources are never touched (omega-manager's --reset-logos / --reset-templates, ported). No brandmark → the AI brandmark generation runs first when MrLogo credentials are present (ZERO config options: MrLogo is a sibling ITW product, so the endpoint is a code constant and auth rides the same ladder as the product services: MRLOGO_SERVICE_ACCOUNT ensures the brand's OWN MrLogo product user and calls with its api.privateKey · MRLOGO_API_KEY uses an existing account's key directly (BEM auth resolves a non-JWT Bearer by users.api.privateKey, verified against the live route) · LOGO_API_ID_TOKEN is the pasted-ID-token escape hatch (expires hourly); an optional creative-direction prompt rides the request and the returned SVG lands as committed assets/logo/brandmark.svg), otherwise a clean skip with guidance. Not ported, dead code: omega-manager's social-images/store-images write ops iterated template names (og-image, chrome-small, …) that never matched its TEMPLATE_CONFIG keys (social-og-image, store-chrome-promo-small, …), so they could never generate anything; their intended outputs are the templates operation's social/og-image.png + store/chrome/promo-*.png exports | | certificates | api-key, certificates, bundle-ids, profiles | Apple code-signing for brands with desktop/mobile targets, reconciled via the App Store Connect API (ES256 JWT over node:crypto, no SDK). Signing certs live in the SIGNING TREE: {companyRoot||brandRoot}/.omega/certificates/apple/: one Apple account signs everything a company ships, so a brand of a company (company: { id }, #677) shares the company TREE's material (omega-manager's shared-set model reborn, keyed by the brand's own config instead of a hardcoded ITW instance) while standalone brands keep it brand-local; certs download there or are created via CSR: the CSR private key is preserved across runs because it pairs with the issued cert; .p12 exports are mtime-diffed against their .cer and import into the macOS login keychain non-interactively (trusted-tool ACLs + partition list). Manual types (DEVELOPER_ID_*_G2: Apple requires Account Holder login) validate the local .cer via openssl; when missing, interactive runs get a full walkthrough (lib/manual-walkthrough.js): the pipeline stages its own CSR (a picker-friendly .certSigningRequest copy lands in Downloads), opens the portal's create page Enter-gated, then watches Downloads for the issued .cer, accepting it only when it modulus-pairs with the staged key AND its CN names the expected type, before installing/exporting/importing like any other cert. Non-interactive runs keep printed guidance + converge-on-rerun (omega-manager auto-opened the browser but still sent users to Keychain Access for the CSR; expired-agreements handling still prints the URL). A downloaded cert without its paired local key warns on the .p12 skip instead of silently not exporting. Bundle ID = certificates.providers.apple.bundleIdPrefix + brand.id with dashes as dots (com.itwcreativeworks + daily-build → com.itwcreativeworks.daily.build; the prefix is reverse-DNS config the onboard wizard derives from the company/brand domain, de-ITW'd: omega-manager hardcoded the company prefix) with capability reconciliation (Sign in with Apple consent block); provisioning profiles per platform × cert type download to .omega/certificates/apple/profiles/. Needs APPLE_API_ISSUER/APPLE_API_KEY_ID/APPLE_TEAM_ID in the brand .env + an AuthKey_*.p8: a missing .p8 gets its own interactive rescue (Enter-gated open of the App Store Connect keys page, then the fresh AuthKey_*.p8 is detected in Downloads and FILED into the signing tree; each .p8 downloads ONCE, so losing it to Downloads clutter is the failure mode this kills); CSC_KEY_PASSWORD auto-generates and persists to the SIGNING ROOT's .env on first run (the company tree's .env when shared: the env chain loads it under every brand of that company). omega-manager's --force-recreate flag was not ported (delete the local .cer to force a re-download; expired certs recreate automatically) | | ai | keys | The AI provider credentials the backend needs — OPENAI_API_KEY and ANTHROPIC_API_KEY (#639). It provisions nothing: there is no AI platform API to reconcile against, so the whole service is the shared setup contract's gate — a brand that wants AI pastes both keys mid-walk, a brand that does not answers Disable once (ai.enabled: false). ONE name per provider: the legacy OMEGA_* twins are gone and a company-wide key is simply the company .env layer of the cascade under that same name. Both are OPTIONAL (gates: false), so preflight never blocks a run on them and a brand using one provider is never nagged about the other. It runs BEFORE disperse, so the keys are in the brand .env when disperse composes the backend target's own | | disperse | (none) | Registered with nothing to do (#891). Config dispersal dissolved into the omega.json5 hierarchy, .env composition into the delivery step every verb runs (#678), and signing artifacts are READ IN PLACE from the signing tree, whose paths derive once at the desktop env load. Ian kept the SERVICE as the named home for the next thing that cannot ride the hierarchy: docs/manager/disperse.md | | seo | github-repos | Parasite SEO: programmatically created GitHub repos with a templated README, download script, and daily-cron maintenance workflow. Content items live in seo.github.content (config/omega.json5) or the config/seo.json5 sidecar, which the config loader reads as the seo section with the same merge and checks (the chatsy.md/replyify.md convention; omega-manager kept them in .brands/{id}/seo.json and auto-created a default entry per brand: the port never writes config, no content = clean skip). Per item: repo created if missing (public, auto-init), template files pushed content-compared (unchanged files never rewritten), stale non-template files deleted, description/homepage/topics reconciled (normalized compares: omega-manager re-PATCHed empty descriptions every run), starred as the author. Collision guardrail ported verbatim: more than 10 non-template files means the name collides with a REAL repo → the item errors without touching anything. Per-item author identity (author.tokenEnv, the name of the .env variable holding the token, + author.git commit identity) for repos owned by separate accounts; owner defaults to repo.owner. Item failures surface as a service error (omega-manager swallowed them into a success). Auth via the gh CLI | | update | targets | One resolution-gated npm install at the brand root (skipped when every target's deps already resolve through the node_modules climb — a brand nested in a bigger workspace never grows a stray install), then each target's own npm run build (targets without a build script are recorded skipped, not failed). The builds are incremental (#445): each target carries two fingerprints — a stat sweep of its own tree (node_modules, dist, logs, .temp, .omega, .cache, .firebase, .git and *.log excluded, so a build never dirties its own inputs) and the installed identity of every @omega.js/* dep it declares (the version string for an npm install; for a local link, the link target's version plus a sweep of its dist). Both unchanged since the last successful build → the target is converged and skips with one line; either fresh → the full build. The pair lives in .omega/cache/update.json, rewritten after each successful build; --force ignores it, and a missing or unreadable cache simply means a full run (a cache never fails the walk) | | account | users | The required accounts exist in the brand's own Firebase Auth with the expected passwords, carry roles.admin + the highest payment.products plan on their Firestore user doc, and (the audit) nobody else holds roles.admin (an unauthorized admin fails the service with its email + uid). Accounts come from account.admins (de-ITW'd: omega-manager hardcoded the company's personal emails as ADMIN_EMAILS in config.js: the default is just support@{domain}; a company's own list lives in its company omega.json5 and replaces the default whole, so per-company personal accounts need zero framework code); passwords are owner-resolved per account, never stored (cp91): OMEGA_ACCOUNT_PASSWORD__<EMAIL> env pin (email uppercased, non-alphanumerics → _; rides the D15 cascade) → the config/hooks/account/password.js owner hook (({ email, domain, apex, brand }) => password, brand root else the company tree: a company formula without a line of it in any framework repo; broken hooks fail the service loudly) → HMAC-SHA256 of email + apex domain keyed by ACCOUNT_PASSWORD_SEED, generated + persisted to the brand .env lazily on first need: a brand fully covered by env/hook never grows a seed (omega-manager derived every password from a personal formula compiled into the code). Non-default channels are called out per account in the run log (· via env … / · via hook …). Existing accounts are verified by an actual sign-in probe and only updated on mismatch; new accounts complete the real POST /user/signup flow against the live backend (welcome email, marketing lists), and marketing: true entries push the contact to the providers, which is why the service runs after update/deploy. Runs on the Identity Toolkit REST API + Firestore REST with the brand's own .omega/secrets/service-account.json (custom tokens signed locally via the shared JWT helper, no firebase-admin). cloud.shared brands skip (the owning brand manages accounts) | | migrations | targets-rename, notifications, users, orders, payments-intents, payment-provider, state-retirement | Firestore data migrations — only with --migration (bare = all, --migration=<name> = one), so a normal manage run never touches collection data, and only with --execute: a bare --migration is the audit pass — every fix computed, counted, logged and snapshotted, nothing written. The framework is omega-manager's: per-collection fix pipelines with batched iteration, one merged leaf-masked REST patch per doc (overlapping ancestor/descendant paths conflict-resolved first), before/after snapshots + _summary.json under .omega/migrations/{collection}/{ts}/, --limit / --ids / --verbose, and schema validation — with fix/write failures and invalid docs downgrading the service to warned (omega-manager printed the ⚠ but reported success). The ported migrations are the two canonical @omega.js/backend-schema ones: notifications (uid→owner, owner.uid flatten, legacy created/updated → metadata.*, url → context.client.url with the full context build, the legacy attribution.utm blob folded into first/last touches, attribution backfill, string trim) and users (orphaned docs with no auth record deleted; plan→subscription; flat subscription.id/name → product object; trial.activated→claimed; deprecated-field removal; referrer-UID → attribution.affiliate.code via DB lookup; metadata.created reconciled to Firebase Auth's canonical creation time; auth.uid/email backfill; implicit-signup consent backfill; the full @omega.js/backend default-user backfill; per-doc affiliate.code/api credential generation; sentinel + null normalization; the same attribution fold; usage period→monthly + zero-total cleanup). orders and payments-intents are native to OMEGA — that one fold on payments-orders and payments-intents, attribution and nothing else. payment-provider is native too (#428): the data half of the processor → provider word rename, sweeping the stored field across all five payment-touching collections (users at subscription.payment.processor, payments-orders/payments-intents/payments-webhooks at processor, payments-disputes at alert.processor) — idempotent, a doc carrying both keys keeps provider, and its stats are namespaced payment-provider:<collection> so they never overwrite another migration’s. Two are LOCAL — the brand's own files, no backend target and no service account: state-retirement (#434) moves the retired .omega/state.json CONTENT into config + .env, leaving the file's machine records (#479) untouched, and targets-rename (#443) moves a pre-#443 brand's apps/ folder to targets/ with the root workspaces glob following it. The rename is the one migration a walk can never reach on the brand it fixes — discovery fails loud on the old shape — so runManage runs --migration=targets-rename ALONE, ahead of the load that would throw; the registered entry only ever re-checks a converged brand. De-ITW'd: omega-manager's other 25 registered migrations are company one-offs (per-brand usage-key renames, damage repairs from old middleware and prior bad runs, the somiibo keep-plan carve-out) and stay in the company instance. Runs on the shared Identity Toolkit + Firestore REST clients (which gained listDocs pagination, aggregation counts, doc metadata, and delete here) over the brand's own .omega/secrets/service-account.json; cloud.shared brands skip (the owning brand migrates the shared project) | | bookmark | sync | The brand's console/dashboard bookmarks pushed to the OMEGA Companion extension (the OMEGA brand's own extension target since #927: install it from the Chrome Web Store, or load omega-omega/targets/extension/packaged/chrome/raw/ unpacked; it auto-connects to ws://localhost:9876, port override via OMEGA_EXTENSION_PORT) and filed under Ω / {Brand} / {Category}. Links derive from config + state in new-world shapes: Cloud/Firebase consoles from cloud.config.projectId (per-function log deep links via a gcloud functions list read when available), Analytics from analytics.providers.google.accountId + propertyId, Search Console from brand.url's domain, Stripe dashboard links unslugged (de-ITW'd: omega-manager deep-linked the ITW platform account id from state — per-brand keys make the key's own account the dashboard default), GitHub as the ONE brand monorepo repo + its Actions (omega-manager linked per-target repos), and Live website/API URLs (API only when a dir maps to the backend target). Interactive sessions only — the sync opens the WebSocket server the extension reconnects to (10s wait), sends one OMEGA_BOOKMARK_SYNC, and reads the ack (refusals and no-connection warn); headless runs skip cleanly, and dry runs print the planned groups without opening a server or shelling to gcloud | | testing | target-checks | Health checks after every other service ran, grouped per target + a repo section. Local (always): parseable package.json; web → dist/index.html exists; backend → firebase.json + functions/package.json; each target's installed framework version (resolved through the node_modules climb: file:/workspace refs read their real version) vs the npm latest, outdated → warned, unpublished/no-registry → dim note. Live (skipped under --dry-run with ⊘ would … lines: a dry run never touches the network): web → homepage fetch on brand.url (3 attempts, backoff, retries network errors AND non-2xx like omega-manager's original website-check: its consolidated target-checks had dropped non-2xx retries); backend → API health on https://api.{host}/omega/health (the @omega.js/backend getApiUrl + route-mount convention) with the payload's deployed version compared to the npm latest (cloud.shared brands skip: the owning brand deploys); repo-level → working tree clean (git status scoped -- . so a nested brand only sees its own files; not a repo → silent) and the latest GitHub Actions run of the brand repo via gh (success ✓ / in-progress warned / failed ✗, de-ITW'd: omega-manager defaulted the org to itw-creative-works; no repo.owner → silent, matching the repo service's gate). Any failed check → error, any warning → warned; results feed RunSummary's per-check drill-down. Stays behind: the build.json check (a UJM artifact: @omega.js/web has no build manifest) and the stash check (nothing stashes in the new update service) |

--dry-run in repo, edge, domain, cloud, analytics, search, monitoring, campaigns, newsletter, payment, forms, chat, email, server, assets, certificates, seo, account, migrations (would-fix/would-delete counts + before/after preview snapshots, zero Firestore writes), and testing (local checks still run; every remote action — fetch, npm view, gh — prints a ⊘ would … line instead, zero network) performs reads only: every drift is reported as planned, zero mutations (omega-manager's stripe operations had no dry-run guard at all — every payment mutation is guarded now). The captcha and advertising services never mutate anything — the captcha service's one siteverify probe is a read and the AdSense Management API has no writes at all — so their dry-run and normal runs are identical.

The porting queue: omega-manager's service order is **full