From a49ce839457bbc7b8e62a92861da0db4af9d55d7 Mon Sep 17 00:00:00 2001 From: song Date: Thu, 15 Oct 2026 23:15:45 +0800 Subject: [PATCH] ci: shard the packaged chat-bundle browser lane The chat-bundle-browser lane ran the whole packaged scenario catalog plus the team, craft, upgrade and ZCode smokes on one runner under a 15-minute cap. The catalog has grown to about 58 scenarios (about 770 s of scenario time before install and browser setup), so the lane now hits the cap routinely: recent PRs and the last five main pushes were cancelled while still in the final scenario, and the checks aggregate went red for every PR regardless of content. Partition the lane like dashboard-browser: a 3-shard matrix with a 20-minute cap, each shard running the unit smokes and its third of the packaged catalog through the existing LOOPX_PERSONAL_WORKSPACE_SHARD selector. The companion smokes are spread over the shards (turn acceptance retry and upgrade on 1, team evidence and feedback on 2, craft and ZCode recovery on 3). The full local script is unchanged; three new scripts expose its catalog, team and craft parts, and the decomposition was asserted programmatically. The review gate already requires the job to succeed, which with a matrix means every shard. Locally, shard 3 of the packaged catalog and the craft smoke pass after a fresh bundle build; the shard selector test passes. Co-Authored-By: Claude Fable 5.1 Signed-off-by: song --- .github/workflows/python-tests.yml | 27 ++++++++++++++++++++++-- apps/presentation/dashboard/package.json | 3 +++ docs/development/frontend-delivery.md | 8 ++++--- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 4b832123dc..0092f6bbd1 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -134,7 +134,14 @@ jobs: needs: [changes, chat-bundle] if: needs.changes.outputs.core_tests == 'true' runs-on: ubuntu-latest - timeout-minutes: 15 + # The packaged scenario catalog outgrew one 15-minute runner, so the + # catalog is partitioned like dashboard-browser and the companion smokes + # are spread across the shards. The aggregate needs every shard to pass. + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3] steps: - uses: actions/checkout@v7 - uses: actions/download-artifact@v7 @@ -155,13 +162,29 @@ jobs: cache-dependency-path: apps/presentation/dashboard/package-lock.json - name: Qualify the actual compiled UI working-directory: apps/presentation/dashboard + env: + LOOPX_PERSONAL_WORKSPACE_SHARD: "${{ matrix.shard }}/3" run: | npm ci --ignore-scripts ./node_modules/.bin/playwright install --with-deps chromium - npm run smoke:personal-workspace-packaged + node ../../../examples/personal-workspace-browser/shard.test.mjs + npm run smoke:personal-workspace-packaged:catalog + - name: Qualify turn acceptance retry and the upgrade window + if: matrix.shard == 1 + working-directory: apps/presentation/dashboard + run: | npm run smoke:chat-turn-acceptance-retry npm run smoke:chat-upgrade + - name: Qualify packaged team evidence and feedback recovery + if: matrix.shard == 2 + working-directory: apps/presentation/dashboard + run: npm run smoke:personal-workspace-packaged:team + - name: Qualify the Goal workspace craft composition + if: matrix.shard == 3 + working-directory: apps/presentation/dashboard + run: npm run smoke:personal-workspace-packaged:craft - name: Qualify packaged ZCode recovery against its persistent owner + if: matrix.shard == 3 env: LOOPX_USAGE_PING: "0" DO_NOT_TRACK: "1" diff --git a/apps/presentation/dashboard/package.json b/apps/presentation/dashboard/package.json index 42bfbf6840..7412a42580 100644 --- a/apps/presentation/dashboard/package.json +++ b/apps/presentation/dashboard/package.json @@ -41,6 +41,9 @@ "smoke:personal-workspace": "npm run smoke:conversation-history-order && npm run smoke:goal-order && npm run smoke:goal-activity && npm run smoke:proposal-recency && node src/features/personal-workspace/conversation-order.test.mjs && node --no-warnings --experimental-strip-types --test src/features/personal-workspace/steering-recovery.test.mjs && npm run smoke:delegation-preflight && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/font-token.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && node ../../../examples/personal-workspace-browser-smoke.mjs", "smoke:workspace-locale": "LOOPX_PERSONAL_WORKSPACE_SCENARIO=workspace-locale node ../../../examples/personal-workspace-browser-smoke.mjs", "smoke:personal-workspace-packaged": "npm run smoke:conversation-history-order && npm run smoke:goal-order && npm run smoke:goal-activity && npm run smoke:proposal-recency && node src/features/personal-workspace/conversation-order.test.mjs && node --no-warnings --experimental-strip-types --test src/features/personal-workspace/steering-recovery.test.mjs && npm run smoke:delegation-preflight && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/font-token.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && node ../../../examples/personal-workspace-browser/server-startup.test.mjs && LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node ../../../examples/personal-workspace-browser-smoke.mjs && LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/team-evidence-return-smoke.mjs && LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/team-feedback-recovery-smoke.mjs && LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/goal-workspace-craft-browser-smoke.mjs", + "smoke:personal-workspace-packaged:catalog": "npm run smoke:conversation-history-order && npm run smoke:goal-order && npm run smoke:goal-activity && npm run smoke:proposal-recency && node src/features/personal-workspace/conversation-order.test.mjs && node --no-warnings --experimental-strip-types --test src/features/personal-workspace/steering-recovery.test.mjs && npm run smoke:delegation-preflight && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/font-token.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && node ../../../examples/personal-workspace-browser/server-startup.test.mjs && LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node ../../../examples/personal-workspace-browser-smoke.mjs", + "smoke:personal-workspace-packaged:team": "LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/team-evidence-return-smoke.mjs && LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/team-feedback-recovery-smoke.mjs", + "smoke:personal-workspace-packaged:craft": "LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/goal-workspace-craft-browser-smoke.mjs", "smoke:todo-resume-condition": "rm -rf /tmp/loopx-todo-resume-condition-smoke && tsc --ignoreConfig --target ES2022 --module commonjs --moduleResolution node --ignoreDeprecations 6.0 --skipLibCheck --strict --outDir /tmp/loopx-todo-resume-condition-smoke smoke/todo-resume-condition-smoke.ts src/features/personal-workspace/todo-resume-condition.ts && node /tmp/loopx-todo-resume-condition-smoke/smoke/todo-resume-condition-smoke.js", "smoke:presentation-surface-schema": "rm -rf /tmp/loopx-presentation-surface-schema-smoke && tsc --ignoreConfig --target ES2022 --module CommonJS --moduleResolution Node --ignoreDeprecations 6.0 --skipLibCheck --strict --resolveJsonModule --esModuleInterop --outDir /tmp/loopx-presentation-surface-schema-smoke smoke/presentation-surface-schema-smoke.ts src/data/status.ts src/data/decision-research.ts src/data/goal-channel-frontstage.ts && NODE_PATH=\"$PWD/node_modules\" node /tmp/loopx-presentation-surface-schema-smoke/apps/presentation/dashboard/smoke/presentation-surface-schema-smoke.js", "smoke:projection-localization": "rm -rf /tmp/loopx-projection-localization-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck --strict --outDir /tmp/loopx-projection-localization-smoke smoke/projection-localization-smoke.ts src/features/personal-workspace/projection-localization.ts && node /tmp/loopx-projection-localization-smoke/smoke/projection-localization-smoke.js", diff --git a/docs/development/frontend-delivery.md b/docs/development/frontend-delivery.md index 1ec466480a..8d4792db8e 100644 --- a/docs/development/frontend-delivery.md +++ b/docs/development/frontend-delivery.md @@ -177,9 +177,11 @@ crash recovery or an already-installed App's adoption of a proposed PR. PR CI builds and verifies a clean bundle once, then uploads `chat-bundle-`. Consumers download that one artifact, and -`chat-bundle-browser` exercises its actual pages in a browser in parallel. The -`checks` aggregate requires that browser lane, so `merge-gate` cannot pass on -an artifact that failed browser qualification. Both frontend-only and +`chat-bundle-browser` exercises its actual pages in a browser across three +parallel shards of the scenario catalog (`LOOPX_PERSONAL_WORKSPACE_SHARD`), with +the companion team, craft, upgrade and ZCode smokes spread over the shards. The +`checks` aggregate requires every shard, so `merge-gate` cannot pass on an +artifact that failed browser qualification. Both frontend-only and mixed/backend PRs pass through this producer. On pull requests the checkout SHA is GitHub's tested merge commit, which may differ from the branch head. Generated-file Git cleanliness is no longer a qualification gate. Browser, integrity and workflow gates remain required.