diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index 5a07cea..e5a1e3f 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -83,13 +83,13 @@ jobs: include-hidden-files: true retention-days: 1 - backend: + backend-foundation: runs-on: ubuntu-latest timeout-minutes: 12 outputs: scope: ${{ steps.scope.outputs.scope }} steps: - - name: Checkout backend candidate and history + - name: Checkout foundation candidate and history uses: actions/checkout@v7 with: fetch-depth: 0 @@ -117,13 +117,97 @@ jobs: if: ${{ steps.scope.outputs.scope == 'full' }} run: npm ci - - name: Verify D1, Worker, reference client and security behavior + - name: Verify D1 foundation, authorization and reference clients if: ${{ steps.scope.outputs.scope == 'full' }} - run: npm run test:d1:core && npm run orbit:test:clients + run: npm run test:d1:core:foundation && npm run orbit:test:clients - - name: Record frontend-only backend decision + - name: Record frontend-only decision if: ${{ steps.scope.outputs.scope != 'full' }} - run: echo 'No backend/security path changed; the fail-safe classifier selected frontend validation.' + run: echo 'No backend/security path changed; this validation slice is not required.' + + backend-site: + runs-on: ubuntu-latest + timeout-minutes: 12 + outputs: + scope: ${{ steps.scope.outputs.scope }} + steps: + - name: Checkout site-auth candidate and history + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Classify changed paths independently + id: scope + env: + BEFORE_SHA: ${{ github.event.before }} + EVENT_NAME: ${{ github.event_name }} + run: | + if [[ "$EVENT_NAME" == 'workflow_dispatch' || -z "$BEFORE_SHA" || "$BEFORE_SHA" =~ ^0+$ ]]; then + printf 'scope=full\n' >> "$GITHUB_OUTPUT" + exit 0 + fi + git diff --name-only "$BEFORE_SHA" "$GITHUB_SHA" | node scripts/orbit-actions-scope.mjs + + - name: Set up Node + if: ${{ steps.scope.outputs.scope == 'full' }} + uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + + - name: Install dependencies + if: ${{ steps.scope.outputs.scope == 'full' }} + run: npm ci + + - name: Verify site authorization and sign-in behavior + if: ${{ steps.scope.outputs.scope == 'full' }} + run: npm run test:d1:core:site + + - name: Record frontend-only decision + if: ${{ steps.scope.outputs.scope != 'full' }} + run: echo 'No backend/security path changed; this validation slice is not required.' + + backend-public: + runs-on: ubuntu-latest + timeout-minutes: 12 + outputs: + scope: ${{ steps.scope.outputs.scope }} + steps: + - name: Checkout public-core candidate and history + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Classify changed paths independently + id: scope + env: + BEFORE_SHA: ${{ github.event.before }} + EVENT_NAME: ${{ github.event_name }} + run: | + if [[ "$EVENT_NAME" == 'workflow_dispatch' || -z "$BEFORE_SHA" || "$BEFORE_SHA" =~ ^0+$ ]]; then + printf 'scope=full\n' >> "$GITHUB_OUTPUT" + exit 0 + fi + git diff --name-only "$BEFORE_SHA" "$GITHUB_SHA" | node scripts/orbit-actions-scope.mjs + + - name: Set up Node + if: ${{ steps.scope.outputs.scope == 'full' }} + uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + + - name: Install dependencies + if: ${{ steps.scope.outputs.scope == 'full' }} + run: npm ci + + - name: Verify public repository and identity behavior + if: ${{ steps.scope.outputs.scope == 'full' }} + run: npm run test:d1:core:public + + - name: Record frontend-only decision + if: ${{ steps.scope.outputs.scope != 'full' }} + run: echo 'No backend/security path changed; this validation slice is not required.' backend-publication: runs-on: ubuntu-latest @@ -212,10 +296,12 @@ jobs: deploy: needs: - frontend - - backend + - backend-foundation + - backend-site + - backend-public - backend-publication - backend-platform - if: ${{ needs.frontend.result == 'success' && needs.backend.result == 'success' && needs.backend-publication.result == 'success' && needs.backend-platform.result == 'success' && needs.frontend.outputs.scope != 'docs' && needs.frontend.outputs.scope == needs.backend.outputs.scope && needs.frontend.outputs.scope == needs.backend-publication.outputs.scope && needs.frontend.outputs.scope == needs.backend-platform.outputs.scope }} + if: ${{ needs.frontend.result == 'success' && needs.backend-foundation.result == 'success' && needs.backend-site.result == 'success' && needs.backend-public.result == 'success' && needs.backend-publication.result == 'success' && needs.backend-platform.result == 'success' && needs.frontend.outputs.scope != 'docs' && needs.frontend.outputs.scope == needs.backend-foundation.outputs.scope && needs.frontend.outputs.scope == needs.backend-site.outputs.scope && needs.frontend.outputs.scope == needs.backend-public.outputs.scope && needs.frontend.outputs.scope == needs.backend-publication.outputs.scope && needs.frontend.outputs.scope == needs.backend-platform.outputs.scope }} runs-on: ubuntu-latest timeout-minutes: 8 environment: diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 91b9102..a5b56cd 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -13,59 +13,107 @@ concurrency: cancel-in-progress: false jobs: - full-regression: + frontend: runs-on: ubuntu-latest - timeout-minutes: 20 + timeout-minutes: 12 steps: - - name: Checkout - uses: actions/checkout@v7 + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + - run: npm ci + - name: Verify complete production frontend + run: | + npm run actions:scope:test + npm run verify:frontend:production - - name: Set up Node - uses: actions/setup-node@v7 + backend-foundation: + runs-on: ubuntu-latest + timeout-minutes: 12 + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 with: node-version: 24 cache: npm + - run: npm ci + - run: npm run test:d1:core:foundation && npm run orbit:test:clients - - name: Install dependencies - run: npm ci + backend-site: + runs-on: ubuntu-latest + timeout-minutes: 12 + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + - run: npm ci + - run: npm run test:d1:core:site - - name: Run the complete application regression - run: | - npm run actions:scope:test - npm run check - npm run build - npm run production:config:check - npm run worker:build:production:live - npm run contract:live + backend-public: + runs-on: ubuntu-latest + timeout-minutes: 12 + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + - run: npm ci + - run: npm run test:d1:core:public + + backend-publication: + runs-on: ubuntu-latest + timeout-minutes: 12 + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + - run: npm ci + - run: npm run test:d1:publication + + backend-platform: + runs-on: ubuntu-latest + timeout-minutes: 12 + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + - run: npm ci + - run: npm run test:d1:platform + + live-contract: + runs-on: ubuntu-latest + timeout-minutes: 8 + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + - run: npm ci + - name: Verify the live agent contract + run: npm run contract:live - # Staging provaları hiçbir yerde otomatik koşmuyordu ve bir tanesi üç - # hafta boyunca bozuk kaldı: sunucuya yeni bir yayın kısıtı girdi, betik - # ondan habersiz kaldı, kimse çalıştırmadığı için kimse görmedi. - # - # Bu iş staging'e YENİ KOD DAĞITMIYOR. Staging Samet'in elle kullandığı - # kapı; gece yarısı üzerine yazmak, sabah bakmak için bıraktığı şeyi - # silmek olurdu. Burada aranan şey provaların ürüne ayak uydurup - # uydurmadığı — bunun için o an staging'de ne varsa yeter. staging-rehearsal: - needs: full-regression runs-on: ubuntu-latest timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@v7 - - name: Set up Node uses: actions/setup-node@v7 with: node-version: 24 cache: npm - - name: Install dependencies run: npm ci - - # Eksik bir secret'ın hatası, provanın kendi hatasına benzemesin. - # Kimlik bilgisi olmadan düşen bir iş, gerçekten kırılmış bir - # sözleşmeyle karıştırılırsa görmezden gelinmeye başlar. - name: Verify the staging credentials are present env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} @@ -77,32 +125,17 @@ jobs: [ -n "$CLOUDFLARE_API_TOKEN" ] || missing="$missing CLOUDFLARE_API_TOKEN" [ -n "$ORBIT_SESSION_PEPPER_V1" ] || missing="$missing ORBIT_STAGING_SESSION_PEPPER_V1" [ -n "$ORBIT_CSRF_PEPPER_V1" ] || missing="$missing ORBIT_STAGING_CSRF_PEPPER_V1" - [ -n "$ORBIT_AGENT_CREDENTIAL_PEPPER_V1" ] \ - || missing="$missing ORBIT_STAGING_AGENT_CREDENTIAL_PEPPER_V1" + [ -n "$ORBIT_AGENT_CREDENTIAL_PEPPER_V1" ] || missing="$missing ORBIT_STAGING_AGENT_CREDENTIAL_PEPPER_V1" if [ -n "$missing" ]; then echo "::error::Staging rehearsal cannot run. Missing repository secrets:$missing" exit 1 fi - - name: Rehearse the staging contract env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} ORBIT_SESSION_PEPPER_V1: ${{ secrets.ORBIT_STAGING_SESSION_PEPPER_V1 }} ORBIT_CSRF_PEPPER_V1: ${{ secrets.ORBIT_STAGING_CSRF_PEPPER_V1 }} ORBIT_AGENT_CREDENTIAL_PEPPER_V1: ${{ secrets.ORBIT_STAGING_AGENT_CREDENTIAL_PEPPER_V1 }} - # Buraya yalnız yeşil olduğu ölçülmüş provalar giriyor. Bir provayı - # kırmızı hâlde bağlamak, gecelik işi görmezden gelinen bir işe - # çevirir — düzeltmeye çalıştığımız sorunun ta kendisi. - # - # Bu yüzden slice2 ve slice3 provaları buraya eklenmedi, silindi: - # biri kaldırılmış bir API'ye bakıyordu, diğeri staging büyüdükçe - # kendi kendine bozulan sayılara. Kapsamın nereye düştüğü - # orbit-slice1-tests.ts ve orbit-slice3-tests.ts içinde, - # devraldıkları testin başında yazılı. - # - # Yeni bir staging betiği eklenirse ya buraya girer ya da elle - # çalışacağı orbit-production-config-tests.mjs'de açıkça beyan - # edilir; kilit ikisini de yapmadan geçmiyor. run: | npm run staging:verify npm run staging:slice4:e2e diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 7309e6d..00de7bb 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -13,9 +13,6 @@ concurrency: group: pull-request-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true -# Reuse the production deploy's exhaustive D1 partition instead of running the -# full D1 suite serially before every other check. The final verify job keeps -# the stable branch-protection check name and fails unless every slice passes. jobs: frontend: runs-on: ubuntu-latest @@ -23,39 +20,65 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 - - name: Set up Node uses: actions/setup-node@v7 with: node-version: 24 cache: npm - - name: Install locked dependencies run: npm ci - - name: Verify scope classifier and production frontend run: | npm run actions:scope:test npm run verify:frontend:production - backend: + backend-foundation: runs-on: ubuntu-latest timeout-minutes: 12 steps: - name: Checkout uses: actions/checkout@v7 - - name: Set up Node uses: actions/setup-node@v7 with: node-version: 24 cache: npm + - name: Install locked dependencies + run: npm ci + - name: Verify D1 foundation and authorization behavior + run: npm run test:d1:core:foundation && npm run orbit:test:clients + backend-site: + runs-on: ubuntu-latest + timeout-minutes: 12 + steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Set up Node + uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm - name: Install locked dependencies run: npm ci + - name: Verify site authorization and sign-in behavior + run: npm run test:d1:core:site - - name: Verify D1 core and reference clients - run: npm run test:d1:core && npm run orbit:test:clients + backend-public: + runs-on: ubuntu-latest + timeout-minutes: 12 + steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Set up Node + uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + - name: Install locked dependencies + run: npm ci + - name: Verify public repository and identity behavior + run: npm run test:d1:core:public backend-publication: runs-on: ubuntu-latest @@ -63,16 +86,13 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 - - name: Set up Node uses: actions/setup-node@v7 with: node-version: 24 cache: npm - - name: Install locked dependencies run: npm ci - - name: Verify publication, renderer and style behavior run: npm run test:d1:publication @@ -82,37 +102,12 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 - - name: Set up Node uses: actions/setup-node@v7 with: node-version: 24 cache: npm - - name: Install locked dependencies run: npm ci - - name: Verify dashboard, media and platform behavior run: npm run test:d1:platform - - verify: - needs: - - frontend - - backend - - backend-publication - - backend-platform - if: ${{ always() }} - runs-on: ubuntu-latest - timeout-minutes: 3 - steps: - - name: Require every parallel verification slice - env: - FRONTEND_RESULT: ${{ needs.frontend.result }} - BACKEND_RESULT: ${{ needs.backend.result }} - PUBLICATION_RESULT: ${{ needs.backend-publication.result }} - PLATFORM_RESULT: ${{ needs.backend-platform.result }} - run: | - test "$FRONTEND_RESULT" = success - test "$BACKEND_RESULT" = success - test "$PUBLICATION_RESULT" = success - test "$PLATFORM_RESULT" = success diff --git a/docs/V6_PROJECT_LEDGER.md b/docs/V6_PROJECT_LEDGER.md index 2424096..c9b8957 100644 --- a/docs/V6_PROJECT_LEDGER.md +++ b/docs/V6_PROJECT_LEDGER.md @@ -2327,3 +2327,21 @@ Anything under `docs/archive/` describes an earlier state and is frozen. Read it dark-launch bundle checks. Manual foundation runs still execute the full historical diagnostics. This removes duplicate CI work without deleting any validation surface. +### 2026-09-18 — Actions wall-time optimization follow-up + +- The first PR parallelization was not accepted as a practical speedup after + measuring end-to-end workflow time: the PR workflow still took 2m44s versus + roughly 2m53s before. A three-second aggregator job waited about 52 seconds + for a runner, so job-level timings overstated the user-visible gain. +- The aggregator was removed because the repository ruleset does not require a + named status check; it only blocks deletion and non-fast-forward updates. +- The measured 1m43s core backend bottleneck was split into three balanced + groups around the 40–46 second D1 suites: foundation/authorization, site + auth/sign-in, and public/identity. PR validation and production deployment + now run all three groups on separate runners. +- The nightly full regression no longer runs `npm run build` as one serial + duplicate chain and no longer waits for it before starting staging rehearsal. + It runs the same exhaustive D1 splits, production frontend verifier, live + contract, and staging rehearsal concurrently. +- Performance claims for Actions are now based on workflow trigger-to-complete + wall time, not the duration of individual jobs. diff --git a/package.json b/package.json index 473b71d..00bd181 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "orbit:import:verify": "tsx scripts/orbit-slice3-manifest.ts", "orbit:import:local": "tsx scripts/orbit-slice3-import.ts --local --database=orbit-v6-local --config=wrangler.test.jsonc", "test:d1": "tsx --test --test-concurrency=5 scripts/orbit-test-ports-tests.ts scripts/orbit-agent-identity-tests.ts scripts/orbit-api-contract-tests.ts scripts/orbit-handle-policy-tests.ts scripts/orbit-d1-tests.ts scripts/orbit-email-tests.ts scripts/orbit-mcp-authorization-tests.ts scripts/orbit-site-authorization-tests.ts scripts/orbit-site-action-tests.ts scripts/orbit-site-client-register-tests.ts scripts/orbit-site-client-registration-tests.ts scripts/orbit-site-signin-e2e-tests.ts scripts/orbit-public-page-tests.ts scripts/orbit-public-repository-tests.ts scripts/orbit-single-renderer-tests.ts scripts/orbit-style-scale-tests.ts scripts/orbit-slice1-tests.ts scripts/orbit-slice1-worker-tests.ts scripts/orbit-slice3-tests.ts scripts/orbit-slice4-tests.ts scripts/orbit-slice5-tests.ts", - "test:d1:core": "tsx --test --test-concurrency=5 scripts/orbit-test-ports-tests.ts scripts/orbit-site-authorization-tests.ts scripts/orbit-site-action-tests.ts scripts/orbit-site-client-register-tests.ts scripts/orbit-site-client-registration-tests.ts scripts/orbit-site-signin-e2e-tests.ts scripts/orbit-mcp-authorization-tests.ts scripts/orbit-agent-identity-tests.ts scripts/orbit-api-contract-tests.ts scripts/orbit-handle-policy-tests.ts scripts/orbit-d1-tests.ts scripts/orbit-email-tests.ts scripts/orbit-public-page-tests.ts scripts/orbit-public-repository-tests.ts scripts/orbit-slice1-tests.ts scripts/orbit-slice1-worker-tests.ts scripts/orbit-slice3-tests.ts", + "test:d1:core": "npm run test:d1:core:foundation && npm run test:d1:core:site && npm run test:d1:core:public", "test:d1:publication": "tsx --test scripts/orbit-slice4-tests.ts scripts/orbit-single-renderer-tests.ts scripts/orbit-style-scale-tests.ts", "test:d1:platform": "tsx --test scripts/orbit-slice5-tests.ts", "v6:check": "npm run test:d1 && npm run check && npm run worker:build", @@ -58,7 +58,10 @@ "browser:test": "tsx scripts/orbit-browser-tests.mjs", "og:generate": "node scripts/orbit-og-images.mjs", "orbit:post": "node scripts/orbit-post.mjs", - "orbit:publish": "node scripts/orbit-publish.mjs" + "orbit:publish": "node scripts/orbit-publish.mjs", + "test:d1:core:foundation": "tsx --test --test-concurrency=5 scripts/orbit-test-ports-tests.ts scripts/orbit-agent-identity-tests.ts scripts/orbit-api-contract-tests.ts scripts/orbit-handle-policy-tests.ts scripts/orbit-d1-tests.ts scripts/orbit-email-tests.ts scripts/orbit-mcp-authorization-tests.ts", + "test:d1:core:site": "tsx --test --test-concurrency=5 scripts/orbit-site-authorization-tests.ts scripts/orbit-site-action-tests.ts scripts/orbit-site-client-register-tests.ts scripts/orbit-site-client-registration-tests.ts scripts/orbit-site-signin-e2e-tests.ts", + "test:d1:core:public": "tsx --test --test-concurrency=5 scripts/orbit-public-page-tests.ts scripts/orbit-public-repository-tests.ts scripts/orbit-slice1-tests.ts scripts/orbit-slice1-worker-tests.ts scripts/orbit-slice3-tests.ts" }, "dependencies": { "@astrojs/cloudflare": "14.3.2", diff --git a/scripts/orbit-actions-scope-tests.mjs b/scripts/orbit-actions-scope-tests.mjs index d75e25c..d004bfe 100644 --- a/scripts/orbit-actions-scope-tests.mjs +++ b/scripts/orbit-actions-scope-tests.mjs @@ -48,7 +48,13 @@ test('every D1 test file the full suite runs is also in exactly one split', asyn const testFiles = (script) => script.match(/scripts\/[\w.-]+/gu) ?? []; const full = testFiles(packageJson.scripts['test:d1']); - const splitNames = ['test:d1:core', 'test:d1:publication', 'test:d1:platform']; + const splitNames = [ + 'test:d1:core:foundation', + 'test:d1:core:site', + 'test:d1:core:public', + 'test:d1:publication', + 'test:d1:platform', + ]; const split = splitNames.flatMap((name) => testFiles(packageJson.scripts[name])); const missing = full.filter((file) => !split.includes(file)); @@ -91,4 +97,19 @@ test('every D1 test file the full suite runs is also in exactly one split', asyn pullRequestWorkflow.includes('npm run verify:frontend:production'), 'production frontend verifier is not run by the pull-request workflow', ); + + const fullRegressionWorkflow = await readFile( + new URL('../.github/workflows/full-regression.yml', import.meta.url), + 'utf8', + ); + for (const name of splitNames) { + assert.ok( + fullRegressionWorkflow.includes(`npm run ${name}`), + `${name} is not run by the full-regression workflow`, + ); + } + assert.ok( + fullRegressionWorkflow.includes('npm run verify:frontend:production'), + 'production frontend verifier is not run by full regression', + ); }); diff --git a/scripts/orbit-production-config-tests.mjs b/scripts/orbit-production-config-tests.mjs index a294d8c..5d4b49d 100644 --- a/scripts/orbit-production-config-tests.mjs +++ b/scripts/orbit-production-config-tests.mjs @@ -348,7 +348,9 @@ assert( assert( !deployWorkflow.includes('\n classify:') && !deployWorkflow.includes('needs: classify') - && deployWorkflow.includes('needs.frontend.outputs.scope == needs.backend.outputs.scope') + && deployWorkflow.includes('needs.frontend.outputs.scope == needs.backend-foundation.outputs.scope') + && deployWorkflow.includes('needs.frontend.outputs.scope == needs.backend-site.outputs.scope') + && deployWorkflow.includes('needs.frontend.outputs.scope == needs.backend-public.outputs.scope') && deployWorkflow.includes('needs.frontend.outputs.scope == needs.backend-publication.outputs.scope') && deployWorkflow.includes('needs.frontend.outputs.scope == needs.backend-platform.outputs.scope'), 'path classification still serializes validation jobs or can disagree without failing closed', @@ -358,7 +360,9 @@ assert( 'production frontend is not built and verified through the single-build path', ); assert( - deployWorkflow.includes('npm run test:d1:core && npm run orbit:test:clients') + deployWorkflow.includes('npm run test:d1:core:foundation && npm run orbit:test:clients') + && deployWorkflow.includes('npm run test:d1:core:site') + && deployWorkflow.includes('npm run test:d1:core:public') && deployWorkflow.includes('npm run test:d1:publication') && deployWorkflow.includes('npm run test:d1:platform'), 'full backend scope can skip D1, Worker or reference client verification', @@ -396,8 +400,8 @@ assert( * çıkarılırlarsa aynı üç haftalık körlük geri gelir. */ assert( fullRegressionWorkflow.includes('staging-rehearsal:') - && fullRegressionWorkflow.includes('needs: full-regression'), - 'nightly regression no longer rehearses staging after the application checks', + && !fullRegressionWorkflow.includes('needs: full-regression'), + 'nightly staging rehearsal is missing or unnecessarily serialized behind another regression job', ); for (const script of ['staging:verify', 'staging:slice4:e2e']) { assert( @@ -435,8 +439,8 @@ assert( assert( fullRegressionWorkflow.includes("cron: '30 1 * * *'") && fullRegressionWorkflow.includes('workflow_dispatch:') - && fullRegressionWorkflow.includes('npm run build') - && fullRegressionWorkflow.includes('npm run worker:build:production:live'), + && fullRegressionWorkflow.includes('npm run verify:frontend:production') + && fullRegressionWorkflow.includes('npm run contract:live'), 'nightly/manual full regression surface is incomplete', ); process.stdout.write(`Orbit production config tests: ${assertions} assertions passed\n`);