@omega.js/desktop 0.53.0 → 0.54.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -38
- package/dist/cli-run.js +4 -1
- package/dist/cli.js +2 -2
- package/dist/commands/cdp/client.js +1 -1
- package/dist/commands/cdp.js +1 -1
- package/dist/commands/clean.js +2 -3
- package/dist/commands/dev.js +25 -0
- package/dist/commands/lib/ensure-target.js +12 -17
- package/dist/commands/lib/migrate.js +17 -0
- package/dist/commands/logs.js +1 -1
- package/dist/commands/release.js +1 -1
- package/dist/commands/test.js +4 -4
- package/dist/commands/update.js +5 -4
- package/dist/defaults/.github/workflows/build.yml +18 -18
- package/dist/defaults/_.gitignore +0 -2
- package/dist/defaults/_mas/README.md +3 -3
- package/dist/defaults/config/certs/README.md +1 -1
- package/dist/defaults/config/omega.json5 +36 -36
- package/dist/defaults/docs/README.md +3 -3
- package/dist/defaults/gulpfile.js +1 -1
- package/dist/defaults/hooks/build/post.js +1 -1
- package/dist/defaults/hooks/build/pre.js +1 -1
- package/dist/defaults/hooks/notarize/post.js +2 -2
- package/dist/defaults/hooks/release/post.js +1 -1
- package/dist/defaults/hooks/release/pre.js +1 -1
- package/dist/defaults/src/assets/scss/pages/about.scss +1 -1
- package/dist/defaults/src/assets/scss/pages/main.scss +1 -1
- package/dist/defaults/src/assets/scss/pages/settings.scss +1 -1
- package/dist/defaults/src/integrations/context-menu/index.js +11 -11
- package/dist/defaults/src/integrations/menu/index.js +5 -5
- package/dist/defaults/src/integrations/tray/index.js +9 -9
- package/dist/defaults/src/main.js +2 -2
- package/dist/defaults/src/preload.js +1 -1
- package/dist/defaults/test/README.md +3 -3
- package/dist/defaults/test/_init.js +1 -1
- package/dist/gulp/tasks/audit.js +5 -8
- package/dist/lib/restart-manager/index.js +1 -1
- package/dist/lib/restart-manager/install.js +1 -1
- package/dist/lib/restart-manager/protocol.js +1 -1
- package/dist/main.js +4 -3
- package/dist/preload.js +1 -1
- package/dist/test/suites/build/audit.test.js +20 -7
- package/dist/test/suites/build/build-workflow-jobs.test.js +2 -2
- package/dist/test/suites/build/cli.test.js +28 -0
- package/dist/test/suites/build/defaults-em-dash.test.js +22 -0
- package/dist/test/suites/build/defaults-scaffold.test.js +19 -5
- package/dist/test/suites/build/deploy-direct.test.js +7 -5
- package/dist/test/suites/build/deploy-dispatch.test.js +2 -1
- package/dist/test/suites/build/deploy-hook.test.js +4 -2
- package/dist/test/suites/build/dev-verb.test.js +67 -0
- package/dist/test/suites/build/ensure-target.test.js +11 -3
- package/dist/test/suites/build/merge-line-files.test.js +6 -6
- package/dist/test/suites/build/migrate.test.js +29 -0
- package/dist/test/suites/build/project-scripts-deps.test.js +6 -10
- package/dist/test/suites/build/runner-env-write.test.js +73 -0
- package/dist/test/suites/build/runner.test.js +9 -8
- package/dist/test/suites/build/setup-scripts.test.js +27 -0
- package/dist/test/suites/build/validate-config.test.js +13 -2
- package/dist/test/suites/build/verb-logs.test.js +20 -0
- package/dist/test/suites/renderer/window-desktop-surface.test.js +1 -1
- package/dist/utils/build-pipeline.js +4 -4
- package/dist/utils/runner-env.js +13 -28
- package/dist/vendor/config/company.js +46 -14
- package/dist/vendor/config/defaults.js +30 -7
- package/dist/vendor/config/edit.js +25 -3
- package/dist/vendor/config/env-delivery.js +1 -1
- package/dist/vendor/config/env-schema.js +3 -6
- package/dist/vendor/config/env.js +34 -22
- package/dist/vendor/config/index.js +13 -17
- package/dist/vendor/config/load.js +15 -7
- package/dist/vendor/config/repo.js +10 -27
- package/dist/vendor/config/schema-client.js +64 -0
- package/dist/vendor/config/schema-cloud.js +38 -0
- package/dist/vendor/config/schema-manager.js +118 -0
- package/dist/vendor/config/schema-overrides.js +68 -0
- package/dist/vendor/config/schema.js +99 -152
- package/dist/vendor/config/validate.js +97 -77
- package/dist/vendor/devkit/agents-md.js +233 -0
- package/dist/vendor/devkit/attach-log-file.js +15 -1
- package/dist/vendor/devkit/ci-workflows.js +30 -30
- package/dist/vendor/devkit/cli-router.js +13 -7
- package/dist/vendor/devkit/defaults-engine.js +9 -43
- package/dist/vendor/devkit/deploy-snapshot.js +44 -9
- package/dist/vendor/devkit/env-lines.js +183 -0
- package/dist/vendor/devkit/local.js +62 -10
- package/dist/vendor/devkit/lockfile.js +32 -13
- package/dist/vendor/devkit/logger.js +7 -2
- package/dist/vendor/devkit/merge-line-files.js +219 -176
- package/dist/vendor/devkit/omega-bin.js +208 -111
- package/dist/vendor/devkit/preludes/docs-sync.js +52 -0
- package/dist/vendor/devkit/preludes/index.js +1 -0
- package/dist/vendor/devkit/target-picker.js +45 -0
- package/dist/vendor/devkit/test/dashed-files.js +37 -0
- package/dist/vendor/devkit/test/run-verb-under-tee.js +71 -0
- package/dist/vendor/devkit/update.js +15 -15
- package/dist/vendor/devkit/verb-scripts.js +40 -0
- package/dist/vendor/devkit/verbs.js +170 -0
- package/package.json +18 -24
- package/dist/commands/install.js +0 -37
- package/dist/defaults/AGENTS.md +0 -119
- package/dist/defaults/CLAUDE.md +0 -1
- package/dist/vendor/config/env-retired.js +0 -137
- package/dist/vendor/config/retired-keys.js +0 -635
- package/docs/analytics.md +0 -140
- package/docs/app-state.md +0 -92
- package/docs/audit.md +0 -69
- package/docs/auth.md +0 -284
- package/docs/auto-updater.md +0 -243
- package/docs/boot-sequence.md +0 -44
- package/docs/build-system.md +0 -169
- package/docs/cdp-debugging.md +0 -169
- package/docs/common-mistakes.md +0 -21
- package/docs/config-schema.md +0 -120
- package/docs/context-menu.md +0 -112
- package/docs/context.md +0 -81
- package/docs/css.md +0 -84
- package/docs/deep-link.md +0 -186
- package/docs/environment-detection.md +0 -112
- package/docs/fontawesome.md +0 -109
- package/docs/hooks.md +0 -89
- package/docs/icons.md +0 -79
- package/docs/index.md +0 -328
- package/docs/installer-options.md +0 -165
- package/docs/ipc.md +0 -61
- package/docs/lib-modules.md +0 -53
- package/docs/logging.md +0 -227
- package/docs/menu.md +0 -160
- package/docs/releasing.md +0 -239
- package/docs/remote-config.md +0 -118
- package/docs/remote-scripts.md +0 -144
- package/docs/restart-manager.md +0 -144
- package/docs/runner.md +0 -290
- package/docs/sentry.md +0 -97
- package/docs/shared/agent-docs.md +0 -89
- package/docs/shared/analytics.md +0 -612
- package/docs/shared/brands.md +0 -57
- package/docs/shared/breaking-changes.md +0 -917
- package/docs/shared/config.md +0 -1948
- package/docs/shared/deploys.md +0 -341
- package/docs/shared/icons.md +0 -219
- package/docs/shared/local-dev.md +0 -167
- package/docs/shared/logging.md +0 -205
- package/docs/shared/monitoring.md +0 -167
- package/docs/shared/publishing.md +0 -187
- package/docs/shared/rulings.md +0 -34
- package/docs/shared/testing.md +0 -147
- package/docs/shared/theming.md +0 -629
- package/docs/shared/translation.md +0 -342
- package/docs/shared/updates.md +0 -61
- package/docs/signing.md +0 -293
- package/docs/startup.md +0 -142
- package/docs/storage.md +0 -59
- package/docs/templating.md +0 -101
- package/docs/test-boot-layer.md +0 -157
- package/docs/test-framework.md +0 -362
- package/docs/themes.md +0 -149
- package/docs/tooltips.md +0 -99
- package/docs/tray.md +0 -164
- package/docs/usage.md +0 -58
- package/docs/verts.md +0 -62
- package/docs/windows.md +0 -149
package/docs/shared/testing.md
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
# Testing
|
|
2
|
-
|
|
3
|
-
## The layered mantra (ratified 2026-07-28, #46)
|
|
4
|
-
|
|
5
|
-
**Every behavior is proven at the LOWEST layer that can prove it, and the shape is MIRRORED across all frameworks.** This is standing doctrine — it governs every future test, and existing tests that violate it get healed, not grandfathered.
|
|
6
|
-
|
|
7
|
-
1. **Unit** — a single function or module, in isolation, inside its own package. If a unit test can prove it, nothing above may be the only proof.
|
|
8
|
-
2. **Integration** — a whole system inside ONE package, real wiring, no mocks of the package's own code (the js_patterns rule: never mock what you can test real).
|
|
9
|
-
3. **End-to-end** — reserved for contracts that CROSS framework boundaries (client↔backend, desktop↔backend, brand↔brand, consumer↔monorepo). An e2e lane that only exercises one package's internals is mis-layered: push the assertion down.
|
|
10
|
-
|
|
11
|
-
Mirrored means: the same lanes, the same runner semantics, the same `test/` layout and naming, the same scope grammar (below) in every package. A test that exists in one framework's suite exists in its siblings' suites wherever the surface exists (the mirrored-implementation rule applied to test shape).
|
|
12
|
-
|
|
13
|
-
### Mirrored suite shape
|
|
14
|
-
|
|
15
|
-
Every package's suite wears the same clothes:
|
|
16
|
-
|
|
17
|
-
- **Top-level `test/`** — one directory at the package root, mirroring the source tree beneath it. No `test/tests/`, no per-source-dir `__tests__`. Deliberate exception (#46 disposition): desktop and extension keep their suites at `src/test/` — the layer-tagged harness (runners, fixture apps, per-layer suites) is framework source their own `omega test` runner resolves from, and relocating it buys no mirror benefit for real churn risk. The five plain-node email test files that used to sit colocated under backend's `src/manager/libraries/email/` — run by hand, outside every lane — were healed under `packages/backend/test/email/` in their mirrored spots ([#105](https://github.com/Omega-JS-Stack/omega/issues/105)); the runner discovers them like everything else.
|
|
18
|
-
- **`*.test.js` filenames** — the suffix IS the discovery signal, everywhere. Backend's suite now matches (its runner discovers `.test.js` only — no bare-`.js` fallback); `_`-prefixed files and directories stay excluded at any depth for shared helpers and fixtures.
|
|
19
|
-
- **`node --test`** is the default runner (`node --test test/*.test.js` in the npm script), unless the package's own runner is the motivated path: backend, desktop, and extension self-test through `omega test` because their suites need a booted emulator/app and the C5 scope grammar, and devkit wraps the same runner in `scripts/run-tests.js` for its two-pass shape: a `node --test` main pass over every other file, then `e2e-harness.test.js` on its own, executed DIRECTLY (no `--test`, so no runner child and no result-stream IPC to corrupt, the mechanism behind that suite's long-running flake, [#36](https://github.com/Omega-JS-Stack/omega/issues/36)), with one retry that banks the failing output to `.temp/`.
|
|
20
|
-
- **Every case file exports through `defineCases()`** — `module.exports = defineCases({ tests: [...] })`, from `@omega.js/devkit/test/define-cases` ([#630](https://github.com/Omega-JS-Stack/omega/issues/630)). `node --test` on a case file only LOADS it, so the cases never run and the report reads `pass 1`: a hollow green. The wrapper turns that load into a loud throw naming `npx omega test <path>`, and stays a pass-through for the real lane (the runners call `markRunnerActive()` first). A devkit guard fails any unwrapped case file, so a new one cannot regress.
|
|
21
|
-
|
|
22
|
-
The always-wrong shapes — a `__tests__/` directory, a `*.spec.*` filename, `test/tests/` nesting — are bounced at write time by the omega plugin's shape hook (`agent-plugins/claude/hooks/shape/`), in this repo and in every consumer session the plugin loads in. The layer CHOICE stays judgment; the hook guards only the mechanical shape.
|
|
23
|
-
|
|
24
|
-
Layer names inside a suite are platform-native on purpose: desktop's `main`/`renderer` and extension's `background`/`view` name the runtimes those platforms actually have. That is vocabulary, not drift — the mirroring rule is about layout, naming, and runner semantics, not about pretending every platform has the same layers.
|
|
25
|
-
|
|
26
|
-
## The three verification tiers (what runs when)
|
|
27
|
-
|
|
28
|
-
| Tier | What | Command | When |
|
|
29
|
-
|------|------|---------|------|
|
|
30
|
-
| 1 — Package suites | Each package's own `node --test` (config, devkit, manager, web, client, backend boot, …) plus the root `scripts/*.test.js` guards (secrets-copier etc.) | `npm test` in the package, or `npm run test:packages` at the root — **`test:packages` is also the QUICK lane** (no corpus/e2e/journey) | Every checkpoint |
|
|
31
|
-
| 2 — Corpus (automated consumers) | The **brand-shape corpus** (7+ generated brand shapes: real onboard + real Eleventy builds with per-shape invariants — themes, target combos, content, font preloads; offline, no installs) then the sandbox **backend corpus** (framework routes/events/rules through a REAL consumer + real emulator) | `npm run test:corpus` at the root | Every checkpoint that touches runtime behavior |
|
|
32
|
-
| 2a — Sandbox e2e | The **cross-stack e2e** (headless Chromium → website → backend: signup/signin/subscribe/cancel/refund/data-request/delete lifecycle, plus the four chart types drawn as real SVG marks). It runs the consumer-brand lane file below directly (the root lane calls `node test/e2e/run.js`, not the brand-root `omega test` walk): `brands/sandbox-brand/test/e2e/run.js` on the devkit harness, driving a REAL `@omega.js/web` page served by the REAL `omega dev` against the emulator with its seeded personas, every port allocator-bumped ([#775](https://github.com/Omega-JS-Stack/omega/issues/775)) | `npm run test:e2e` at the root | Every checkpoint; `OMEGA_SKIP_E2E=1` to skip |
|
|
33
|
-
| 2b — Verts company-mode e2e | The **cross-brand verts (house-ads) proof** (OMEGA Playground's backend emulator serves seeded `verts` inventory — HTML unit / scoring / 204 / fail-closed redirect / whitelist scoping — and The Daily Build consumes as `source: 'company'`: real `@omega.js/config` compose → real `@omega.js/client` resolution → the consumer-built URL fetched against the parent stack) | `npm run test:verts` at the root | Every checkpoint touching the verts chain; `OMEGA_SKIP_E2E=1` to skip |
|
|
34
|
-
| 2c — Extension auth e2e | The **extension ↔ backend auth boundary in a REAL Chrome** ([scripts/e2e-extension-auth.js](../../scripts/e2e-extension-auth.js)): the playground's extension app builds as a TESTING build, headless Chrome loads it unpacked, the brand-site `?authToken=` tab drives the background SW's real sign-in, and the popup context's `omega:syncAuth` message makes the SW fetch a fresh custom token from the emulator — the uid round trip asserted INSIDE the extension. Offline by construction (host-resolver rules NXDOMAIN everything but the local stack). It FOLLOWS a bumped port like every other lane ([#744](https://github.com/Omega-JS-Stack/omega/issues/744)): the emulator boots FIRST and the extension builds second, so the bundle task bakes the live ports into `OMEGA_BUILD_JSON`'s `config.dev.ports` — the only channel a browser context has — and the lane reads the bake back out of the packaged service worker's own bundle and asserts the baked hosting/auth pair equals the resolved map before Chrome ever starts | `npm run test:e2e-extension` at the root | Every checkpoint touching extension auth, the SW build config, or the `/user/token` wire; `OMEGA_SKIP_E2E=1` to skip, and no puppeteer Chrome → SKIPPED (exit 0, reason printed) |
|
|
35
|
-
| 2d — Desktop auth e2e | The **desktop ↔ backend auth boundary in a REAL Electron app** ([scripts/e2e-desktop-auth.js](../../scripts/e2e-desktop-auth.js)): a consumer app staged from @omega.js/desktop's bundled fixture is built and booted by the real boot runner, then a SECOND Electron instance launches carrying `<brand.id>://auth/token?authToken=…` — the OS single-instance lock forwards that argv to the running app, whose real `second-instance` → deep-link → desktop auth lib chain signs it in. Both sides of the process boundary are asserted: main's client-bridge on the emulator user, and the renderer's own @omega.js/client Firebase on the same uid (it learned only through the real `desktop:auth:sign-in-with-token` broadcast). Offline by construction — a testing run connects main's auth to the emulator and the staged config points the renderer's client at the same ports | `npm run test:e2e-desktop` at the root | Every checkpoint touching desktop auth, the deep-link routes, or the `/user/token` wire; `OMEGA_SKIP_E2E=1` to skip, and no electron binary (or no built @omega.js/desktop `dist/`) → SKIPPED (exit 0, reason printed) |
|
|
36
|
-
| 2e — User flows e2e (real browser) | The **CRUCIAL user flows through the actual UI** ([scripts/e2e-flows.js](../../scripts/e2e-flows.js)): headless Chromium against the playground's full emulator suite (seeded personas) plus the REAL `omega dev` — the auth-emulator proxy the provider redirect leg needs lives only there. Five areas — four per spec item of [#155](https://github.com/Omega-JS-Stack/omega/issues/155), plus the billing journeys of [#209](https://github.com/Omega-JS-Stack/omega/issues/209): **auth** (the Google picker's real redirect leg on /signup and again with `authReturnUrl`, `?authSignout=true` + the account page's kick-out, the empty-return loud failure, and the password FORMS — /signin, /signup, /reset), **checkout** (bound state, armed payment buttons, a `_dev_cardProvider=test` payment landing on /payment/confirmation with its order), **verts** (an unfilled slot laddering no-fill → promo, the card content-sized under its slot ceiling, the UTM set on the promo link and the click forwarded out of the frame), **account** (a signed-in policy page rendering the persona's account state from the emulator), **billing journeys** (one paid lifecycle per DEDICATED seeded persona — upgrade, cancel, a declined renewal, a trial converting — each verdict read off the RENDERED account page; the two end states no UI can reach post a hand-built test webhook at the backend exactly as a provider would). **Hard precondition**: `OMEGA_WEBHOOK_KEY` must be in the playground backend's `.env` cascade — the webhook route authenticates on it, and the lane throws `OMEGA_WEBHOOK_KEY is missing from the playground backend's .env cascade` at startup rather than running a crippled pass. Owns its stack: it HOLDS every classic port so both children bump onto fresh ones, so it never disturbs a live dev boot | `npm run test:flows` at the root | Every checkpoint touching auth pages, checkout, the vert ladder, the account page, or the billing lifecycle; `OMEGA_SKIP_E2E=1` to skip, and no puppeteer Chrome → SKIPPED (exit 0, reason printed) |
|
|
37
|
-
| 2.5 — Wizard journey (outside-monorepo consumer) | The FULL consumer story in a temp brand born OUTSIDE the monorepo: real onboard wizard (flags) → `i local` tree link → `omega dev` boot + branded-homepage probe → headless creds-scrubbed manage (update must build every target) | `npm run test:journey` at the root (also the tail of root `npm test`) | The full sequence, and any change to onboard/linking/boot plumbing |
|
|
38
|
-
| 3 — Playground (live rehearsal) | The 24-service manage pipeline against REAL cloud (Firebase, Cloudflare, SendGrid, …) | `npm run pipeline` in `brands/playground-omega` | SPARINGLY — Ian-authorized (real infra, real cost) |
|
|
39
|
-
| C — Consumer brand e2e | Every OMEGA brand's OWN browser lane, `<brandRoot>/test/e2e/run.js` on [@omega.js/devkit/test/e2e-harness](../../packages/devkit/src/test/e2e-harness.js): the brand's real pages against its real local stack (the backend emulator with its seeded personas plus the real `omega dev`), on allocator ports beside a live dev boot. The brand-root walk, the `--target` picker and the fanned-out flags: [../manager/brand.md](../manager/brand.md) ([#775](https://github.com/Omega-JS-Stack/omega/issues/775)) | `npx omega test` at the brand root, after every target | Every brand checkpoint. Tier 2a IS this lane, run by the sandbox brand |
|
|
40
|
-
|
|
41
|
-
**Opt-in lanes are not a tier and never join a run by accident.** A lane is a set of suites that exist only for the real external thing — credentials, the network, a vendor CLI — plus a GATE deciding whether they may run at all. It is reached exactly one way, `npx omega test --lane=<name>`, and a gate that cannot be satisfied prints ONE skip line and exits clean; the lane's own `test/<lane>/` directory is excluded from discovery unless its gate opened it, so no default run, no CI tier and no path filter can pull it in. Today there is one: backend's `--lane=stripe-live`, which forwards REAL Stripe test-mode webhooks into the local emulator and refuses to open on anything that is not an `sk_test_` secret ([packages/backend/docs/test-framework.md](../../packages/backend/docs/test-framework.md#opt-in-lanes---lane)). Distinct from `--extended`, which is a *mode* letting the same suites make real calls.
|
|
42
|
-
|
|
43
|
-
**Root `npm test` runs tiers 1 + 2 + 2a + 2b + 2c + 2d + 2e + 2.5 in one shot** (package suites → corpus → sandbox e2e → extension auth e2e → desktop auth e2e → verts company-mode e2e → auth-token e2e → user flows e2e → wizard journey, sequentially — emulator runs must never overlap). Tier 1 expands `packages/*` only — the brands/* workspaces are covered by their own dedicated stages (the sandbox e2e used to run TWICE per root test through both lanes). The sandbox is offline-only (fake `demo-*` project); the journey brand is `demo-*`/`.invalid`-scoped and creds-scrubbed; the playground is the only tier that touches real cloud.
|
|
44
|
-
|
|
45
|
-
**One seeded persona, one testable purpose** (Ian 2026-08-19, [#369](https://github.com/Omega-JS-Stack/omega/issues/369)). Every persona the backend seeds ([packages/backend/src/test/test-accounts.js](../../packages/backend/src/test/test-accounts.js)) is a FULL realistic account — a real profile, a real signup context, the devices an established user is signed in on ([#327](https://github.com/Omega-JS-Stack/omega/issues/327)) — carrying exactly ONE distinguishing state on top, and a lane reads that state off the persona built to demonstrate it. New state gets a NEW persona, never a rider on an existing one: referrals live on the `referrer`/`referred` pair alone and were stripped back off the steady-state subscriber QA signs in as to check billing ([#363](https://github.com/Omega-JS-Stack/omega/issues/363)). A persona telling two stories makes a failure ambiguous about which one broke, and it quietly answers the assertion the other lane came to make.
|
|
46
|
-
|
|
47
|
-
**And every account a test uses is DECLARED there, never minted mid-test** (Ian 2026-08-20, [#406](https://github.com/Omega-JS-Stack/omega/issues/406)). The isolation doctrine covers all suites, not just payments: an account is exclusive to the one suite that drives it, so suites run in any order with no state pollution, and the healthy ones live in the roster as machinery personas (no `palette` label — machinery is never offered to a human in the dev switcher) named for the suite that drives them. A suite still writes the STATE its scenario starts from; what it no longer does is stand the account up. The one exception is a deliberately BROKEN state — an auth user with no doc, a doc with no auth user — which the seed cannot make by construction, so the guard suites that need half a pair build it themselves, in the suite, with `admin.auth().createUser` ([packages/backend/test/routes/payments/intent-purchaser-guard.test.js](../../packages/backend/test/routes/payments/intent-purchaser-guard.test.js)).
|
|
48
|
-
|
|
49
|
-
**Every tier-1 suite preloads the stdout guard** ([#356](https://github.com/Omega-JS-Stack/omega/issues/356)). `node --test` reads a child file's results as v8 frames on that child's STDOUT, and console bytes on the same fd desync node 24's parser — a multibyte character behind a frame makes its SIGNED length negative and the whole FILE dies with `Unable to deserialize cloned data` ([#321](https://github.com/Omega-JS-Stack/omega/issues/321)). So every `node --test` command in this repo carries `--require @omega.js/devkit/test/stdout-guard`, which the runner forwards to each child: string writes go to stderr (still in the report), the protocol pipe carries frames alone, and a spawned child's inherited stdout is remapped to fd 2. Wiring a new suite means adding that flag — `scripts/stdout-guard-wiring.test.js` fails the lane otherwise. The frameworks whose `omega test` runs the in-process runner (backend, desktop, extension) have no frame pipe and stay unwired. Retires when the repo's pinned node reaches >=26.7.0, which reads the length unsigned.
|
|
50
|
-
|
|
51
|
-
**Port isolation cuts two ways.** Every lane FOLLOWS a bumped port — including the extension lane, which builds after the boot so the resolved map is baked into the artifact the SW reads ([#744](https://github.com/Omega-JS-Stack/omega/issues/744)); the user-flows lane REFUSES the classics outright — it binds every classic port on all three addresses (127.0.0.1, ::1, wildcard: Node's SO_REUSEADDR means one bind is not enough) for the whole run, so the emulator CLI and `omega dev` both bump onto fresh ports and a developer's live stack is untouched. A classic port that is already busy is somebody else's: the hold simply fails and the allocator bumps around it as always. The website port is allocated by the LANE and handed to both children as `OMEGA_WEBSITE_PORT`, because the backend builds checkout confirmation URLs from it and boots first.
|
|
52
|
-
|
|
53
|
-
**The auth lanes divide by SURFACE.** `npm run test:auth` ([scripts/e2e-auth-token.js](../../scripts/e2e-auth-token.js)) is the fast WIRE-CONTRACT lane: the desktop auth lib required in-process from node against the emulator, proving the custom-token round trip in seconds. Tiers 2c and 2d are the REAL-SURFACE proofs of the same chain — the extension's background SW inside actual Chrome, and the desktop app inside actual Electron with a real OS-delivered deep link. A change to the token wire runs the fast lane; a change to how either app RECEIVES it runs its real-surface lane.
|
|
54
|
-
|
|
55
|
-
**Every lane leaves a log.** A lane tees its full output — its own lines plus every child command's — to `.temp/logs/<lane>.log` (`test:packages` → `.temp/logs/test-packages.log`), and each e2e runner writes per-step verdicts to `.temp/<lane>/steps.log` beside its environment logs, so `grep '^FAIL' .temp/*/steps.log` names the failing step even after a lane was killed. Truncated per run; read them instead of re-running a lane to see what it said ([logging.md](logging.md)).
|
|
56
|
-
|
|
57
|
-
**A lane is the LOADED context, so the watch deadlines scale with it** ([#211](https://github.com/Omega-JS-Stack/omega/issues/211)). Web's rebuild-watching suites (`dev-watch`, `live-decisions`, `dev-server-restart`) poll a built page until the watcher rebuilds it; the 30s deadline that makes a solo run fail FAST is the one that blows under the full parallel suite — load, not breakage. They read their deadline from `test/lib/deadlines.js`, which multiplies the 30s base by `OMEGA_TEST_DEADLINE_SCALE`; `scripts/lane.js` exports `3` to every child (an outer lane's or the caller's value passes through untouched), so a lane gets 90s and a solo `node --test <file>` — which never goes through a lane — keeps the tight base. A junk multiplier throws rather than silently running unscaled.
|
|
58
|
-
|
|
59
|
-
**The deadline was never the whole answer, so those suites got their own serial lane** ([#344](https://github.com/Omega-JS-Stack/omega/issues/344)). Nine lane-only sightings later, instrumented captures showed the lane failures are two distinct things, neither of them a slow rebuild:
|
|
60
|
-
|
|
61
|
-
- **A doubled config reset** (`3 !== 2`, with Eleventy's "You called Benchmark after() without a before()" alongside it). One save reaches chokidar once per registered watch-target path FORM, and web registers two for a cwd-contained dir (deliberately — see `registerTemplateWatchTargets`). Normally the two events land in one throttle window; under load they straddle the build, and the second walks past Eleventy's `watchManager.isBuildRunning()` guard — a config reset REPLACES `watchManager` part way through the build it is serving — into a build concurrent with the one in flight. Unfixable from our side without dropping a form, which was tried and measured worse (0/8 red vs 5/8), so the suite is event-keyed instead: it asserts the edit rode the reset lane, never how many resets it took.
|
|
62
|
-
- **A starved watcher.** The packaged-layer watcher received zero chokidar events — not one raw event — for 91 seconds, while `getWatched()` still listed the edited file. It correlates with load, so the suites now run away from it.
|
|
63
|
-
|
|
64
|
-
**The knob only reaches a run the LANE started, so the deadline reads the MACHINE too** ([#615](https://github.com/Omega-JS-Stack/omega/issues/615)). Three more sightings on 2026-08-25 — two workers and a backstop run — were bare `npm test -w packages/web` runs beside other work, which never come through `scripts/lane.js` and so ran at the flat 30s. `rebuildDeadlineMs()` derives it now: **the 30s floor × measured contention (the 1-minute load average per CPU, floored at 1 and capped at 6) × the lane knob**. An idle machine is unchanged at 30s; a 5×-oversubscribed one gets 150s; a wedged one still reaches a verdict. Not a timed build, deliberately — these fixtures build in ~20ms and rebuild in ~1.7s whether the machine is idle or at load 50 (measured), so the wait is on chokidar's event, never on work. And every timeout now names **the elapsed time, the derivation, and the last rebuild event seen** (`last rebuild event: build #1 finished, 180059ms ago (1 started, 1 finished)`), which is what tells the starved watcher above apart from an honestly slow one — the starvation reproduces at load ~60 as *zero* rebuild events, so a bigger deadline is provably not its answer.
|
|
65
|
-
|
|
66
|
-
So the three suites live in `packages/web/test/watch/` and run in their own phase — `node --test --test-concurrency=1 test/watch/*.test.js`, after the parallel glob, one file at a time. That is the serialization rejected in #211's triage for costing wall time; the measured cost is ~20s, against a flake that twice blocked a ship gate. The directory IS the list (`watch-deadlines.test.js` reads it), so a new watcher suite cannot join one lane and be forgotten by the other. The deadline knob stays: it still covers an honestly slow rebuild.
|
|
67
|
-
|
|
68
|
-
**Skip knobs** (all documented in their script headers too): `OMEGA_SKIP_E2E=1` (sandbox e2e, verts, auth-token, extension auth, desktop auth, user flows), `OMEGA_SKIP_JOURNEY=1` (journey), `OMEGA_JOURNEY_STRICT=1` (unmet journey preconditions fail instead of skip), `OMEGA_JOURNEY_KEEP=1` (keep the temp brand after a green run).
|
|
69
|
-
|
|
70
|
-
**The emulator ready deadline scales the same way** ([#332](https://github.com/Omega-JS-Stack/omega/issues/332)): a boot's port sweep could stall on a network mount (lsof against a Time Machine volume), so every self-booting backend lane waits `OMEGA_EMULATOR_READY_TIMEOUT` ms (default 180000) for "All emulators ready" before declaring the boot dead. A junk value throws.
|
|
71
|
-
|
|
72
|
-
**The boot's port handling stopped paying for that stall, and says up front when it cannot work** ([#332](https://github.com/Omega-JS-Stack/omega/issues/332)). Both emulator sweeps (the pre-boot reaper and the post-shutdown orphan sweep) now bind-probe a port before asking lsof who holds it: a free port has no holder to name, so a normal boot shells out zero times instead of once per port. `omega test`'s "is an emulator already up" check is the same in-process probe. And every boot runs a PREFLIGHT before spawning firebase: after the allocator has bumped around whatever it can, any port the child still has to bind and somebody else holds fails the run immediately with a report naming each port and the dev stack to stop, rather than a stack that never comes up and a burned ready deadline. Every self-booting lane inherits it, because they all boot through the same `omega emulator` path.
|
|
73
|
-
|
|
74
|
-
**Publish rehearsal — `npm run release:check`** (scripts/release-check.js): packs every publishable (real prepare + vendoring), scratch-installs each tarball with local-tarball overrides for the published @omega.js runtime deps, `require.resolve`s it, and scans the installed tree for raw private @omega.js references. The laptop mirror of CI's pack-smoke; the mechanical gate for the publish-proving checkpoint. `--only=web,manager` iterates a subset; `--keep` preserves the scratch dir.
|
|
75
|
-
|
|
76
|
-
## The brand-shape corpus (cp197)
|
|
77
|
-
|
|
78
|
-
Tier 2's opening act ([scripts/corpus-shapes.js](../../scripts/corpus-shapes.js)): a matrix of brand SHAPES — target combos (web-only, default web+backend derivation, all-four, backend-only, desktop+extension), themes (classy, newsflash), content (a real `_posts` entry the blog must list) — each born through the REAL onboard in a temp dir (config validates, git initializes) and, for web cells, built the way `omega build` builds — `ensureTarget`, then `buildSite` with the whole asset lane, from the target root, writing a real `dist/` the invariants then READ (no installs; every dependency resolves from the monorepo) — with per-cell invariants: branded homepage, `data-theme-id`, `/blog`, sitemap + robots, font preloads. Rendering against a hand-built asset manifest instead proved the fixture rather than the product, and the font-preload list (a product of the compiled sheet) failed on every web cell until the stub went ([#776](https://github.com/Omega-JS-Stack/omega/issues/776)). Fully offline; failing cells keep their temp brand for autopsy. A new shape = a new `CELLS` row, never a new harness. One cell brings its own FIXTURE: `shape-theme-override` drops a consumer-local tier-2 theme over the seeded one before that same shared build runs, then reads every file kind the cascade resolves back out of `dist/` — layout, include, the theme scss entry, a section's html, a section's own js, the base js an `inherit: ['js']` folder left to the chain, and the packaged skin's `_theme.js` a shadowing theme inherits by shipping none ([#773](https://github.com/Omega-JS-Stack/omega/issues/773); the fixture is `scripts/corpus-fixtures/theme-override/`). The journey lane (below) covers the one axis this can't: the outside-monorepo install/boot/manage story.
|
|
79
|
-
|
|
80
|
-
## The wizard journey lane (cp195)
|
|
81
|
-
|
|
82
|
-
The scripted form of the cp194 hand rehearsal — proof that a consumer OUTSIDE the monorepo (where hoist-luck can't save anything) can live the whole story. Mechanics: `@omega.js/devkit/test/journey-harness` (spec-driven: `{ id, url, targets, expect }` — a corpus of brand shapes can reuse it); runner: [scripts/e2e-journey.js](../../scripts/e2e-journey.js).
|
|
83
|
-
|
|
84
|
-
- **Preconditions skip, never lie**: no network or no java → the lane prints SKIPPED and exits 0 (`OMEGA_JOURNEY_STRICT=1` turns that into a failure). `OMEGA_SKIP_JOURNEY=1` skips outright.
|
|
85
|
-
- **Runtime legs run creds-scrubbed**: `omega dev` and manage children get credential-shaped env vars stripped — the journey must never reach a real cloud. Install legs (onboard/link) keep the machine env.
|
|
86
|
-
- **The manage scorecard** comes from the brand's `.omega/runs/*.json`: `update` must succeed and no service may error except the allowed set (default `{testing}` — the live-URL probe of a never-deployed `.invalid` brand fails by design).
|
|
87
|
-
- **Artifacts**: numbered stage logs in `.temp/journey/`, per-step verdicts in `.temp/journey/steps.log` (`grep '^FAIL'` names the leg that broke, including a `preflight` abort that never reached a step); on failure the temp brand is KEPT and its path printed (`OMEGA_JOURNEY_KEEP=1` keeps it on success too).
|
|
88
|
-
- Heavy by design (registry installs, all-four app builds) — that's the point; it caught brand-root manager resolution (#8), the ambient-Node engines stamp (#9), and the stale-manifest clobber + linked-prepare destruction (#10) on its first runs.
|
|
89
|
-
|
|
90
|
-
# Test scoping (`omega test`) — the C5 grammar
|
|
91
|
-
|
|
92
|
-
One grammar, every framework (parser: `@omega.js/devkit/test/scope`, adopted by the devkit runner-core → desktop + extension, the backend runner, and web's test command). Decided by Ian 2026-07-11 (core-changes inbox C5): **a bare test run from a brand/app never drags the framework's suite in** — the framework suite is always an explicit choice.
|
|
93
|
-
|
|
94
|
-
## Grammar
|
|
95
|
-
|
|
96
|
-
| Target | Runs | Notes |
|
|
97
|
-
|--------|------|-------|
|
|
98
|
-
| *(bare)* | **project tests only** | consumer default; `pages/x` = project tests under that path |
|
|
99
|
-
| `project:` / `brand:` | project tests only | explicit spelling; optional path: `project:auth/` |
|
|
100
|
-
| `framework:` / `omega:` / `mgr:` | the framework's own suite | universal aliases |
|
|
101
|
-
| `backend:` `web:` `desktop:` `extension:` (+ legacy `em:` `bxm:` `ujm:`) | the framework's own suite | per-framework ids |
|
|
102
|
-
| `full:` | both sources | optional path applies to both: `full:auth` |
|
|
103
|
-
|
|
104
|
-
- Paths after a prefix scope within that source: `framework:routes/general`, `project:checkout`.
|
|
105
|
-
- Unknown prefixes (`framwork:x`) warn and are ignored — never silently match nothing.
|
|
106
|
-
- Multiple targets union sources; each path binds to its own source.
|
|
107
|
-
|
|
108
|
-
## The self-test exception
|
|
109
|
-
|
|
110
|
-
Inside a framework package itself (cwd package name === the framework), a bare run means the framework's own suite — each package's `npm test` keeps meaning "run my suite". Consumer context is what flips to project-only.
|
|
111
|
-
|
|
112
|
-
## Corpus spelling
|
|
113
|
-
|
|
114
|
-
The sandbox brand's backend corpus is the framework suite run in consumer context — its scripts say so explicitly since cp94: `npx omega test framework:` ([brands/sandbox-brand/targets/backend/package.json](../../brands/sandbox-brand/targets/backend/package.json)).
|
|
115
|
-
|
|
116
|
-
## Per-framework notes
|
|
117
|
-
|
|
118
|
-
- **web** — project scope = production build + smoke checks (pages rendered, themed 404, the main bundle on disk, every internal link in `dist/**/*.html` resolving — [#430](https://github.com/Omega-JS-Stack/omega/issues/430) — and the four fast audit scans of the same dist: page meta, anchor fragments, image `alt`, sitemap orphans, each opt-out-able per page in the shared `config/link-exceptions.json5` — [#468](https://github.com/Omega-JS-Stack/omega/issues/468)) + consumer `test/`; `framework:` runs @omega.js/web's own `node --test` suite. That suite is local-era only: it requires the package's unbuilt `src/` and its dev dependencies, neither of which a published install receives, so `framework:` from an installed brand finds nothing ([#115](https://github.com/Omega-JS-Stack/omega/issues/115) ruling: the suite's home is the monorepo, and shipping it would cost every consumer ~1 MB for a scope only framework developers use).
|
|
119
|
-
- **backend** — framework source = the routes/events/rules corpus (boot/ stays self-test-only); project source = `<project>/test`. Filters are prefix-stripped centrally, so `framework:routes/general` and a bare `general/` (project) match within their own trees only. Unlike web's, this suite RUNS from a consumer in both install modes — local link and a plain npm install ([#720](https://github.com/Omega-JS-Stack/omega/issues/720)): the package ships `test/` as raw source beside `dist/`, and every case file reaches the framework through `dist/` (the 1:1 mirror of `src/`, which does not ship) and the vendored devkit/config/account under `dist/vendor/` (those packages never publish). Deliberate divergence from desktop/extension, whose suites ride INSIDE `src/test/` and therefore inside `dist/`; backend's live at the package root, so `test/` earns its own `files` entry. `test/boot/suite-portability.test.js` fails the build the day a case file reaches for `../../src/…` or a devDependency again.
|
|
120
|
-
- **desktop / extension** — runner-core handles sources + layers (`--layer build|main|renderer|boot` etc. unchanged, orthogonal to scoping).
|
|
121
|
-
- **Every runner-core framework** — framework `boot/` suites are self-test only: they assert on the framework's own fixture consumer, so consumer discovery excludes them and consumers write their own under `<app>/test/boot/`. A consumer run that asks for `--layer boot` with no suites of its own says so instead of running silently empty.
|
|
122
|
-
|
|
123
|
-
## Brand root (cp94b)
|
|
124
|
-
|
|
125
|
-
At a **brand root** (a directory carrying `config/omega.json5` with no framework declared nearer), every framework's `omega` bin hands over to `@omega.js/manager` — the omega-bin dispatcher detects the brand the same way it detects targets (nearest context wins, walking up; the rule twins `@omega.js/config`'s `resolveBrandRoot`). The manager's `test` command then fans out over the brand's target-mapped dirs, spawning each target's own framework bin with `cwd` = the target dir:
|
|
126
|
-
|
|
127
|
-
| At the brand root | Runs |
|
|
128
|
-
|-------------------|------|
|
|
129
|
-
| `npx omega test` | every target's **project tests** (bare per target) |
|
|
130
|
-
| `npx omega test framework:` | every target's framework suite |
|
|
131
|
-
| `npx omega test full:` | both sources, every target |
|
|
132
|
-
| `npx omega test routes/x` | project filter forwarded to every target (a target that does not carry it is a no-op; a path NO target carries fails the run, [#814](https://github.com/Omega-JS-Stack/omega/issues/814)) |
|
|
133
|
-
| `npx omega test web:pages/` | ONLY the web target — its framework suite, scoped |
|
|
134
|
-
| `npx omega test em:` | ONLY the desktop target's framework suite |
|
|
135
|
-
|
|
136
|
-
- Universal targets (bare paths, `framework:`/`omega:`/`mgr:`, `full:`, `project:`/`brand:`) forward to every target dir verbatim; per-framework ids route to the target owning that framework. The id → framework map is `FRAMEWORK_IDS` in `@omega.js/devkit/test/scope` — the same SSOT each framework's runner reads its own aliases from.
|
|
137
|
-
- An id with no matching target warns and runs nothing (exit 0 — same semantics as a target-level filter matching no tests). Only-invalid targets fall back to bare-everywhere, mirroring the target-level parser.
|
|
138
|
-
- That exit 0 holds for a bare prefix only. An id carrying a PATH whose framework this brand has no target for (`omega test desktop:renderer/typo` in a website-only brand) keeps the warning AND fails: zero runs plus a named path is the same typo the target-level rule catches ([#814](https://github.com/Omega-JS-Stack/omega/issues/814)).
|
|
139
|
-
- A forwarded path a target does not carry is a **no-op there, not a failure**: the manager sets `OMEGA_TEST_FANOUT=1` on every forwarded run, and a target CLI whose target selected nothing answers with the distinct `NO_MATCH_EXIT_CODE` (3) instead of 1 (both live in `@omega.js/devkit/test/scope`). The fan-out counts those as misses, and fails the brand run only when EVERY target missed, printing `No test file matches "<target>"` in the manager's own log tag. A standalone run in a target dir, with no signal, still exits 1 ([#814](https://github.com/Omega-JS-Stack/omega/issues/814)).
|
|
140
|
-
- Targets run **sequentially** with streamed output; any failing target makes the whole run exit 1 (per-target summary at the end).
|
|
141
|
-
- **Flags are not fanned out** (`--layer`, `--extended`, …) — flagged runs are target-level invocations; run them from the target dir.
|
|
142
|
-
- Other manager commands ride the same handoff: bare `omega` at a brand root means the manager's manage cycle, `omega onboard` reaches the wizard, and `omega deploy` is the brand-root deliberate-deploy fan-out (docs/shared/deploys.md).
|
|
143
|
-
|
|
144
|
-
## CI runner notes
|
|
145
|
-
|
|
146
|
-
- **npm 11 script-approval gating skips dependency postinstalls on CI runners.** Puppeteer's Chrome download is handled explicitly in ci.yml (`npx puppeteer browsers install chrome`); if a native-postinstall dep (electron, canvas, sharp, …) ever misbehaves in CI, this gating is the first suspect — add an explicit install step like puppeteer's rather than disabling the gate.
|
|
147
|
-
- **Post-mortem steps use `if: always()`**, not `if: failure()` — runner-level cancellation (OOM, watchdog) is NOT `failure()`, and cancelled runs are exactly the ones that need the diagnostics.
|