## Summary
Fixes all deterministic full-suite (non-blocking) CI failures on `main`
caused by the SQLite-to-PostgreSQL cutover (VAL-REMOVAL-005).
## Changes
### i18n Key Parity (5 locale files)
- Added missing `taskPopupsBoardListOnly` +
`taskPopupsBoardListOnlyHelp` keys (empty strings per convention) to
zh-CN, zh-TW, fr, es, ko `app.json`
### Dashboard Curated-Gate Guard (`scripts/lib/test-quarantine.json`)
- Repaired "mirror drift": 16 dashboard test files were quarantined in
`vitest.config.ts` but never added to the quarantine ledger. Added all
16 with failing run URLs and `quarantinedAt` dates.
### Line-Count Audit CI Cache (`.github/workflows/full-suite.yml`)
- Removed `skip-install: "true"` from `line-count-audit` job —
`setup-node@v5` with `cache: pnpm` failed post-step because no
`node_modules` existed to cache.
### Engine Slow Tier — Full PG Migration
- **CI**: Added PostgreSQL service container to `test-slow` job (same
config as `test-shards`)
- **`_helpers.ts`**: Migrated `makeReliabilityFixture()` from removed
SQLite `Database.init()` to PG-backed `TaskStore`:
- Added `probeTcpReachable()` (TCP probe, copied from shared harness)
- Added `hasPg` export (uses TCP probe, not env-var guess)
- Added `adminExecAsync()` (`Promise.withResolvers`, psql via
`PG_TEST_URL_BASE`)
- Added `createPgLayer()` (fresh PG database + schema baseline +
`AsyncDataLayer`)
- Updated cleanup: `await store.close()`, close layer, drop database
- **Slow test**: Migrated 24 sync SQLite API calls to async PG APIs:
- `store.getRunAuditEvents()` → `await auditEvents(store, ...)` via
exported `queryRunAuditEvents`
- `store.getDatabase().prepare(...)` → Drizzle queries via
`store.getAsyncLayer()!.db`
- **Core exports**: Added `queryRunAuditEvents` from `async-audit.ts`
and `eq as drizzleEq` from `drizzle-orm`
- **22 reliability test files**: Added `hasPg` guards so tests skip
locally when PG is unavailable
### Shard 3 — PG Test Auth Bug (18 postgres test files)
- Replaced `psql -U ${process.env.USER ?? "postgres"}` with `psql
"${PG_TEST_URL_BASE}/postgres"` connection string. On GitHub Actions,
`process.env.USER` is `'runner'`, not `'postgres'`, causing auth
failure.
### Shard 3 — Removed Function Tests (`mesh-task-replication.test.ts`)
- Deleted 3 tests for functions intentionally removed in PostgresCutover
(`buildMeshReplicatedTaskCreatePayload`, `toReplicatedCreateInput`,
`taskMatchesReplicatedCreate`). Kept `buildBootstrapPrompt` test.
### Shard 3 — Store Thinking Levels (`store-thinking-levels.test.ts`)
- Migrated from removed SQLite path to PG-backed
`createTaskStoreForTest` + `pgDescribe`.
## Verification
| Check | Result |
|---|---|
| Merge gate (`pnpm test:gate`) | ✅ 294 + 99 + 63 = 456 passed |
| Engine slow tier (22 tests) | ✅ 22/22 passed |
| i18n parity tests | ✅ 7 passed |
| mesh-task-replication | ✅ 1 passed |
| PG data-layer | ✅ 14 passed |
| PG taskstore-lifecycle | ✅ 16 passed |
| store-thinking-levels | ✅ 1 passed |
| Dashboard curated-gate | ✅ passes |
| Typecheck (engine + core) | ✅ clean |
| Lint | ✅ exit 0 |
## Parked (not in scope)
- **Shards 1/2 timeout**: Engine test suite exceeds CI time budget.
Pre-existing, unrelated to these fixes.
- **2 latent PG files** (`chat-store-content-search-edit`,
`satellite-db-injected-stores`): Surface a separate pre-existing schema
baseline gap. Out of scope.
250 lines
10 KiB
YAML
250 lines
10 KiB
YAML
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@v4
|
|
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@v4
|
|
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@v4
|
|
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
|
|
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@v4
|
|
|
|
- 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@v4
|
|
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@v4
|
|
|
|
- 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@v4
|
|
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
|