fix(test): planning-lane E2E is RED on main (2/7, incl. its own contr… #1640
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Full Suite (non-blocking) | |
| # The demoted test tier (docs/plans/2026-06-04-001-refactor-fast-trusted-test-gate-plan.md). | |
| # Runs the full sharded suite, the engine slow tier, and the dashboard | |
| # inventory guard on every push to main — post-merge signal only. These jobs | |
| # are NON-BLOCKING by design: they never run on PRs and must never be added | |
| # to branch-protection required checks. A red run here is information, not a | |
| # merge stopper; see docs/testing.md for the quarantine ratchet that keeps | |
| # this tier honest. | |
| on: | |
| push: | |
| branches: [main] | |
| # Key the concurrency group by SHA, not ref: on push to main the ref is | |
| # always refs/heads/main, so a ref-keyed group with cancel-in-progress would | |
| # let consecutive merges cancel each other's runs — silently skipping the | |
| # only coverage for everything the gate dropped. Per-SHA groups never collide. | |
| concurrency: | |
| group: full-suite-${{ github.sha }} | |
| cancel-in-progress: false | |
| # Least-privilege token: jobs only read the repo (checkout + cache) and upload | |
| # workflow artifacts (timings), which needs no extra permission scope. | |
| permissions: | |
| contents: read | |
| # FN-4863: Opt JavaScript actions into Node 24 ahead of GitHub's forced cutover on 2026-06-02. | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" | |
| jobs: | |
| test-shards: | |
| name: Test shard ${{ matrix.shard }}/4 | |
| runs-on: ubuntu-latest | |
| # FNXC:FixPgTestsAndCi 2026-06-26-09:10: | |
| # Provision a PostgreSQL service container so the postgres/*.pg.test.ts | |
| # suites run in the non-blocking full suite too (parity with the gate). | |
| # The pg-test-harness probe skips gracefully if unreachable. | |
| services: | |
| postgres: | |
| image: postgres:15 | |
| env: | |
| POSTGRES_USER: postgres | |
| POSTGRES_PASSWORD: postgres | |
| POSTGRES_DB: postgres | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -h localhost -p 5432 -U postgres" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 10 | |
| env: | |
| FUSION_PG_TEST_URL_BASE: "postgresql://postgres:postgres@localhost:5432" | |
| PGPASSWORD: "postgres" | |
| # Backstop for a wedged shard. The per-invocation watchdog (L2, | |
| # scripts/lib/run-vitest-watchdog.mjs) kills any single hung invocation at | |
| # its budget ceiling (<=30min), so this job budget only fires if L2 itself | |
| # wedges — and it must sit strictly above that ceiling so L2 fires first. | |
| # Without this, a hang ran to GitHub's 6h default (the silent-black-hole bug | |
| # this plan closes). | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2, 3, 4] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| # Engine tests run real git operations (merge-base against main, | |
| # case-variant ref checks) that require full history. Shallow | |
| # clones silently break tests like worktree-acquisition's resume | |
| # misbinding path. | |
| fetch-depth: 0 | |
| - name: Setup Node.js and pnpm | |
| uses: ./.github/actions/setup-node-pnpm | |
| # Dist-artifact cache (L1): ensureTestArtifacts otherwise rebuilds dist/ | |
| # for 8 packages (~71s) on every shard because CI starts with no dist. | |
| # Key on a stable, pre-build, git-based hash of ALL build packages' source | |
| # inputs. Exact-match only — NO restore-keys: a partial/stale dist hit is | |
| # the exact failure mode this repo has been bitten by (FN-4232/FN-4605), | |
| # and ensureTestArtifacts still validates/rebuilds anything missing-or-stale | |
| # after restore, so a miss is safe but a wrong-content hit would not be. | |
| # NEVER add node_modules here (breaks Windows pnpm junctions elsewhere). | |
| - name: Compute dist source hash | |
| id: dist-hash | |
| run: echo "hash=$(node scripts/ensure-test-artifacts.mjs --print-source-hash)" >> "$GITHUB_OUTPUT" | |
| - name: Cache built dist artifacts | |
| id: dist-cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: | | |
| packages/core/dist | |
| packages/dashboard/dist | |
| packages/engine/dist | |
| packages/plugin-sdk/dist | |
| plugins/fusion-plugin-dependency-graph/dist | |
| plugins/fusion-plugin-hermes-runtime/dist | |
| plugins/fusion-plugin-openclaw-runtime/dist | |
| plugins/fusion-plugin-paperclip-runtime/dist | |
| key: dist-${{ runner.os }}-${{ steps.dist-hash.outputs.hash }} | |
| # On a cache HIT, restored dist files carry their save-time mtimes while | |
| # checkout rewrites src mtimes to "now" (src newer than dist), which would | |
| # make ensureTestArtifacts' mtime fallback rebuild everything and defeat | |
| # the cache. Seed the per-package content-hash cache so its content-hash | |
| # short-circuit fires instead. ensureTestArtifacts still runs (inside | |
| # test:ci:shard) and rebuilds anything genuinely missing/changed. | |
| - name: Seed artifact hash-cache on cache hit | |
| if: steps.dist-cache.outputs.cache-hit == 'true' | |
| run: node scripts/ensure-test-artifacts.mjs --seed-artifact-cache | |
| - name: Test (deterministic shard) | |
| run: pnpm test:ci:shard --shard ${{ matrix.shard }} --total 4 | |
| # Each shard emits per-file vitest JSON timing reporter output under | |
| # .timings/. Upload as an artifact so the timing snapshot can be | |
| # refreshed locally/from the default branch via | |
| # `node scripts/ci-test-shard.mjs --write-timings`. We do NOT commit the | |
| # snapshot automatically — refresh is manual/scheduled only. | |
| - name: Upload per-shard test timings | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: test-timings-shard-${{ matrix.shard }} | |
| # Relative outputFile paths mean each package writes its own | |
| # <pkgDir>/.timings/ file — glob the whole tree, not just the root. | |
| path: | | |
| .timings/timings-*.json | |
| packages/*/.timings/timings-*.json | |
| plugins/*/.timings/timings-*.json | |
| plugins/examples/*/.timings/timings-*.json | |
| if-no-files-found: ignore | |
| # FNXC:TestInfrastructure 2026-07-24-01:05: | |
| # .timings/ is a dot-directory and upload-artifact excludes hidden | |
| # files by default, so this step silently uploaded NOTHING since it was | |
| # added ("No files were found") and the timing snapshot could never be | |
| # refreshed from CI. Hidden files must be included for the glob to match. | |
| # FNXC:CI 2026-07-28-01:35: pin is actions/upload-artifact@v7 (Dependabot #2444). | |
| include-hidden-files: true | |
| retention-days: 14 | |
| # The dashboard quality gate used to enumerate its test files by hand, so any | |
| # unenumerated app/ or src/ test file ran in NO project. This guard fails when | |
| # a dashboard test file is neither executed by a quality project (curated + | |
| # backfill lanes) nor on the reviewed skip-list. Cheap: it only runs | |
| # `vitest list`, not the tests. | |
| test-inventory-guard: | |
| name: Dashboard curated-gate guard | |
| runs-on: ubuntu-latest | |
| # Runs only `vitest list` (no tests execute), so this is generous headroom, | |
| # not a tight bound — but no CI job should be able to hang to the 6h ceiling. | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Node.js and pnpm | |
| uses: ./.github/actions/setup-node-pnpm | |
| # Same dist-artifact cache as test-shards (L1): the curated-gate guard runs | |
| # `vitest list`, whose config resolution can touch built dist, so it also | |
| # pays the cold-dist rebuild. Exact-match key on the pre-build source hash; | |
| # NO restore-keys (stale dist is the failure mode), NO node_modules. | |
| - name: Compute dist source hash | |
| id: dist-hash | |
| run: echo "hash=$(node scripts/ensure-test-artifacts.mjs --print-source-hash)" >> "$GITHUB_OUTPUT" | |
| - name: Cache built dist artifacts | |
| id: dist-cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: | | |
| packages/core/dist | |
| packages/dashboard/dist | |
| packages/engine/dist | |
| packages/plugin-sdk/dist | |
| plugins/fusion-plugin-dependency-graph/dist | |
| plugins/fusion-plugin-hermes-runtime/dist | |
| plugins/fusion-plugin-openclaw-runtime/dist | |
| plugins/fusion-plugin-paperclip-runtime/dist | |
| key: dist-${{ runner.os }}-${{ steps.dist-hash.outputs.hash }} | |
| - name: Seed artifact hash-cache on cache hit | |
| if: steps.dist-cache.outputs.cache-hit == 'true' | |
| run: node scripts/ensure-test-artifacts.mjs --seed-artifact-cache | |
| - name: Assert every dashboard test file is gated or skip-listed | |
| run: node scripts/check-test-inventory.mjs --dashboard-curated | |
| line-count-audit: | |
| name: Line-count audit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Node.js and pnpm | |
| uses: ./.github/actions/setup-node-pnpm | |
| # FNXC:TestInfrastructure 2026-06-21-10:26: | |
| # Keep line-count drift visible in automated post-merge signal without restoring it to the blocking PR gate. | |
| # The guard is intentionally absent from pretest/pr-checks so task verification and merge progress are not blocked by file-size cleanup work. | |
| - name: Run line-count audit | |
| continue-on-error: true | |
| run: pnpm check:line-count | |
| # The engine-slow tier (src/**/*.slow.test.ts) runs here with a non-empty | |
| # execution assertion, so a glob/config drift that silently empties the tier | |
| # fails this workflow instead of passing vacuously. Engine slow tests do real | |
| # git operations, so a full clone (fetch-depth: 0) is required. | |
| test-slow: | |
| name: Engine slow tier | |
| runs-on: ubuntu-latest | |
| # FNXC:FixPgTestsAndCi 2026-07-14-00:00: | |
| # Provision a PostgreSQL service container so reliability-interaction test | |
| # helpers (which now require a PG-backed TaskStore after SQLite removal | |
| # VAL-REMOVAL-005) can run in the engine-slow tier too. | |
| services: | |
| postgres: | |
| image: postgres:15 | |
| env: | |
| POSTGRES_USER: postgres | |
| POSTGRES_PASSWORD: postgres | |
| POSTGRES_DB: postgres | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -h localhost -p 5432 -U postgres" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 10 | |
| env: | |
| FUSION_PG_TEST_URL_BASE: "postgresql://postgres:postgres@localhost:5432" | |
| PGPASSWORD: "postgres" | |
| # Real-git slow suites; same backstop rationale as test-shards. Sits above | |
| # the L2 per-invocation ceiling so the watchdog fires first on a single hang. | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js and pnpm | |
| uses: ./.github/actions/setup-node-pnpm | |
| - name: Run engine-slow with non-empty-execution assertion | |
| run: node scripts/assert-engine-slow-nonempty.mjs | |
| # FNXC:CIGateSpeed 2026-07-22-23:30: | |
| # Warm the PR gate's incremental build cache from main. The Gate job in | |
| # pr-checks.yml restores `gate-dist-*` with restore-keys; caches saved on a | |
| # PR merge ref are invisible to other PRs, so without this job every PR's | |
| # FIRST gate run builds cold (~6-8 min). Saving main's built dist plus | |
| # .fusion/cache/plugin-build-cache.json here lets that first run rebuild only | |
| # the packages the PR actually changed. The path list MUST stay byte-identical | |
| # to the Gate job's cache block (actions/cache versions caches by path list — | |
| # a drifted list makes the caches mutually invisible). Fast CLI packaging | |
| # matches the Gate's consumer shape. This is a cache warmer, not a test or | |
| # verification step; the blocking Build job on PRs keeps full-build coverage. | |
| warm-gate-build-cache: | |
| name: Warm gate build cache | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Node.js and pnpm | |
| uses: ./.github/actions/setup-node-pnpm | |
| - name: Compute dist source hash | |
| id: dist-hash | |
| run: echo "hash=$(node scripts/ensure-test-artifacts.mjs --print-source-hash)" >> "$GITHUB_OUTPUT" | |
| - name: Cache built dist artifacts (incremental) | |
| id: dist-cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: | | |
| packages/core/dist | |
| packages/dashboard/dist | |
| packages/engine/dist | |
| packages/plugin-sdk/dist | |
| packages/cli/dist | |
| plugins/fusion-plugin-dependency-graph/dist | |
| plugins/fusion-plugin-hermes-runtime/dist | |
| plugins/fusion-plugin-openclaw-runtime/dist | |
| plugins/fusion-plugin-paperclip-runtime/dist | |
| .fusion/cache/plugin-build-cache.json | |
| key: gate-dist-${{ runner.os }}-${{ steps.dist-hash.outputs.hash }} | |
| restore-keys: | | |
| gate-dist-${{ runner.os }}- | |
| - name: Build (incremental against restored cache) | |
| run: pnpm build | |
| env: | |
| FUSION_CLI_FULL_PACKAGE: "0" | |
| # FNXC:CIGateSpeed 2026-07-23-00:05: | |
| # Also warm the Typecheck job's tsc incremental buildinfo cache from main | |
| # (same PR-invisibility rationale as the dist cache above). The path list | |
| # must stay byte-identical to the Typecheck job's cache block in | |
| # pr-checks.yml. Restored buildinfo is self-validating — tsc re-checks | |
| # anything that changed — so restore-keys is correctness-neutral. | |
| - name: Cache TypeScript incremental buildinfo | |
| uses: actions/cache@v6 | |
| with: | |
| path: | | |
| packages/*/dist/.tsbuildinfo | |
| packages/dashboard/dist/.tsbuildinfo-app | |
| plugins/*/dist/.tsbuildinfo | |
| key: typecheck-tsbuildinfo-${{ runner.os }}-${{ github.sha }} | |
| restore-keys: | | |
| typecheck-tsbuildinfo-${{ runner.os }}- | |
| - name: Typecheck (warms incremental buildinfo) | |
| run: pnpm typecheck |