Skip to content

Wave 22 — editor confiável: leituras coordenadas, falhas de revisão visíveis, jornada de aceite em navegador #789

Wave 22 — editor confiável: leituras coordenadas, falhas de revisão visíveis, jornada de aceite em navegador

Wave 22 — editor confiável: leituras coordenadas, falhas de revisão visíveis, jornada de aceite em navegador #789

Workflow file for this run

name: CI
on:
push:
branches:
- main
pull_request:
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
quality:
name: Quality and security
runs-on: ubuntu-latest
# Raised from 20 to 35 by two Wave 20 changes that landed in the same
# phase, so the reasons are recorded together.
#
# First, the two capture product journeys encode their own fixtures and
# render a 60 s 960x540 h264+aac file rather than reading a committed one.
# Measured on a sixteen-core developer machine against a local PostgreSQL
# 16: the react playback journey ran 21.8-42.8 s over an uninterrupted
# N=10 (median 22.2 s) and 90.4 s once on a loaded box; the insufficient
# evidence journey 20.3-22.0 s over N=3 and 88.2 s under the same load. So
# about twenty seconds each when the machine is idle, four times that when
# it is not.
#
# Second, the long-form synthesis journey started RENDERING the brief's
# two-hour master instead of stopping at its plan. On an eight-core machine
# that step cost 122.8, 125.6, 153.6, 159.3 and 194.5 s of FFmpeg over five
# runs, and the whole suite went from about 213 s to 361.4, 400.1, 412.8
# and 559.1 s over four runs.
#
# The 35 minutes are a BUDGET, not a measurement. A hosted runner has fewer
# cores than either machine those seconds came from, this repository has no
# CI history to calibrate against, and how much of the old cap the job was
# already using is not observable from a developer machine. If a real run
# reports the job finishing well inside this, bring it back down.
timeout-minutes: 35
env:
CI: true
APOLLO_API_ENVIRONMENT: production
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public
services:
postgres:
image: pgvector/pgvector:0.8.5-pg16-trixie
env:
POSTGRES_DB: apollo_v2
POSTGRES_USER: apollo
POSTGRES_PASSWORD: apollo-ci
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U apollo -d apollo_v2"
--health-interval 10s
--health-timeout 5s
--health-retries 10
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: 22
cache: npm
cache-dependency-path: |
package-lock.json
remotion/package-lock.json
# `git diff --check` is required by TODO.md, which lists it among the
# commands that must pass before a slice is called finished — "typecheck,
# `git diff --check`, audits sem vulnerabilidades, migration validation,
# bundle Remotion e build Next.js passam" — and repeats it in two more
# acceptance lists. Four reports under `docs/quality` then record it as
# approved: proof-mode-v1.md, batch-edit-v1.md, batch-partial-retry-v1.md
# and source-deconstruction-v1.md.
#
# It is NOT one of the nine gates AGENTS.md enumerates. An earlier
# version of this comment attributed the requirement there, in the one
# sentence written to show the gate was mandated rather than invented;
# `grep -n "diff --check" AGENTS.md` returns nothing.
#
# Every one of those documents reports the check as having passed on
# somebody's machine, and until this step existed nothing made the next
# machine repeat it: `grep -rn "diff --check" .github/ scripts/
# package.json` came back empty while the checked-out tree carried seven
# blank lines at end of file. A gate nobody runs is a sentence in a
# document.
#
# `lint:whitespace` diffs against `4b825dc…`, git's empty tree, so every
# line in the repository is read as added and the whole tree is checked —
# not just a branch's own diff, which has no base to compare against on a
# push to main. Which rules apply per path is declared in `.gitattributes`.
- name: Reject trailing whitespace and blank lines at end of file
run: npm run lint:whitespace
- name: Prepare integration environment
run: touch .env
- name: Install locked dependencies
run: npm ci
- name: Install locked renderer dependencies
run: npm ci --prefix remotion
- name: Audit dependencies
run: npm run security:audit
- name: Audit renderer dependencies
run: npm run security:audit:remotion
- name: Typecheck
run: npm run typecheck
- name: Verify architecture boundaries
run: npm run lint
- name: Lint application and tests
run: npm run lint:code
- name: Verify canonical domain language
run: npm run domain-language:validate
- name: Verify isolated local infrastructure contracts
run: npm run infra:validate
- name: Verify pinned platform versions
run: npm run platform:validate
- name: Run unit and contract tests
run: npm test
- name: Validate public API contracts
run: npm run api:v1:validate
- name: Verify UI capability parity report
run: npm run api:parity:validate
- name: Validate database schema and migrations
run: npm run db:v2:validate
- name: Apply database migrations
run: npm run db:v2:migrate:deploy
- name: Prepare synthetic phase gate PostgreSQL proof database
run: |
postgres_container="$(docker ps --filter 'ancestor=pgvector/pgvector:0.8.5-pg16-trixie' --format '{{.ID}}')"
test -n "$postgres_container"
docker exec -e PGPASSWORD=apollo-ci "$postgres_container" createdb -U apollo apollo_synthetic_gate_e2e
npm run db:v2:migrate:deploy
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_synthetic_gate_e2e?schema=public&application_name=apollo-video-e2e-synthetic-phase-gate-migrate-${{ github.run_id }}&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Run synthetic phase gate PostgreSQL proof
run: npm run test:integration:synthetic-phase-gate-pg
env:
APOLLO_SYNTHETIC_PHASE_GATE_PG_E2E: "1"
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_synthetic_gate_e2e?schema=public&application_name=apollo-video-e2e-synthetic-phase-gate-${{ github.run_id }}&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Verify synthetic phase gate PostgreSQL cleanup
if: always()
run: |
postgres_container="$(docker ps --filter 'ancestor=pgvector/pgvector:0.8.5-pg16-trixie' --format '{{.ID}}')"
test -n "$postgres_container"
orphans="$(docker exec -e PGPASSWORD=apollo-ci "$postgres_container" psql -U apollo -d postgres -Atc "select count(*) from pg_stat_activity where application_name = '$APOLLO_SYNTHETIC_PHASE_GATE_APP_NAME'")"
test "$orphans" = 0
env:
APOLLO_SYNTHETIC_PHASE_GATE_APP_NAME: apollo-video-e2e-synthetic-phase-gate-${{ github.run_id }}
- name: Run source cleanup and separation FFmpeg goldens
run: npm run test:integration:source-cleanup-goldens
- name: Run synthetic production persistence tests
run: npm run test:integration:synthetic-production-db
- name: Run durable TTS-to-avatar provider journey
run: npm run test:integration:provider-tts-avatar
- name: Run bounded localization provider integration tests
run: npm run test:integration:localization-provider
- name: Run music-led montage analysis integration tests
run: npm run test:integration:music-led-montage
- name: Run vertical upload-to-proxy smoke
run: npm run test:integration:vertical-smoke
env:
APOLLO_V2_VERTICAL_SMOKE: "1"
- name: Run FFmpeg integration tests
run: npm run test:integration:media
- name: Run editorial renderer goldens
run: npm run test:integration:editorial-renderer
- name: Run transcript retiming worker goldens
run: npm run test:integration:transcript-rate
- name: Run long-form extraction golden
run: npm run test:integration:contiguous-extraction-golden
- name: Bundle Remotion renderer
run: npm run remotion:build
- name: Run synthetic presenter provider-to-MP4 golden
run: npm run test:integration:synthetic-presenter
- name: Run hybrid StoryPlan provider-to-MP4 golden
run: npm run test:integration:hybrid-story
- name: Run authorized Remotion smoke render
run: npm run test:integration:render
- name: Run proof mode goldens and worker-produced proof MP4s
run: npm run test:integration:proof-mode-goldens
env:
APOLLO_PROOF_MODE_VISUAL_E2E: "1"
- name: Build production application
run: npm run build
- name: Provision and migrate isolated contamination E2E database
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_contamination_e2e?schema=public&application_name=apollo-video-e2e-contamination-migrate-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
run: |
postgres_container="$(docker ps --filter 'ancestor=pgvector/pgvector:0.8.5-pg16-trixie' --format '{{.ID}}')"
test -n "$postgres_container"
docker exec -e PGPASSWORD=apollo-ci "$postgres_container" createdb -U apollo apollo_contamination_e2e
npm run db:v2:migrate:deploy
- name: Run contamination-to-source-separation journey
run: npm run test:integration:contamination-reports
env:
APOLLO_CONTAMINATION_E2E: "1"
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_contamination_e2e?schema=public&application_name=apollo-video-e2e-contamination-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Verify zero orphan contamination E2E backends
if: always()
run: |
postgres_container="$(docker ps --filter 'ancestor=pgvector/pgvector:0.8.5-pg16-trixie' --format '{{.ID}}')"
test -n "$postgres_container"
orphans="$(docker exec -e PGPASSWORD=apollo-ci "$postgres_container" psql -U apollo -d postgres -Atc "select count(*) from pg_stat_activity where application_name = 'apollo-video-e2e-contamination-ci'")"
test "$orphans" = 0
- name: Publish public API documentation artifact
uses: actions/upload-artifact@v4
with:
name: apollo-public-api-${{ github.sha }}
path: generated/public-api
if-no-files-found: error
retention-days: 30
- name: Run Prisma integration tests
run: npm run test:integration:prisma
- name: Run media artifact persistence tests
run: npm run test:integration:artifacts
- name: Run media transfer audit persistence tests
run: npm run test:integration:media-transfer-audit
- name: Run asset rights audit persistence tests
run: npm run test:integration:asset-rights-audit
- name: Run synthetic script plan persistence tests
run: npm run test:integration:synthetic-script-plan
- name: Run per-block generation cache and retry tests
run: npm run test:integration:synthetic-block-generation
- name: Run block audio concatenation and consolidated master tests
run: npm run test:integration:synthetic-block-compilation
- name: Run synthetic cache decision ledger tests
run: npm run test:integration:synthetic-cache-decision
- name: Run synthetic master asset persistence tests
run: npm run test:integration:synthetic-master-asset
- name: Run transformation brief and provider registry persistence tests
run: npm run test:integration:transformation-registry
- name: Run review-derived cleanup mask persistence tests
run: npm run test:integration:review-cleanup-mask
- name: Run Wave 18 editorial synthesis journey
run: npm run test:e2e:editorial-synthesis
- name: Run Wave 18 heterogeneous capture session journey
run: npm run test:e2e:capture-heterogeneous
- name: Run Wave 18 insufficient sync evidence journey
run: npm run test:e2e:capture-insufficient-evidence
- name: Run Wave 19 capture protocol, marker and diagnostic journeys
run: npm run test:e2e:sync-diagnostic-journey
- name: Run Wave 19 marker rendering against ffprobe
run: npm run test:integration:sync-marker-render
- name: Run Wave 19 clean marker detection fixtures
run: npm run test:integration:marker-detection
- name: Run Wave 19 marker robustness fixtures
run: npm run test:integration:marker-robustness
- name: Run Wave 20 falsification suite
run: npm run test:unit:wave20-falsification
- name: Run Wave 20 ffmpeg binary resolution guard
run: npm run test:unit:ffmpeg-binary
- name: Run Wave 20 multicam direction render against ffprobe
run: npm run test:integration:multicam-direction-render
# The calibration source for `SCREEN_ACTIVITY_SATURATION_BPS = 400`. Spec
# 05 §29.1 and the FR-150 traceability row both quote this suite's
# measured 0/4/103/137/3151 bps by name, and the script existed in
# package.json without a CI step while `.integration.mjs` keeps it out of
# `npm test` — so the two documents cited a number nothing re-measured.
- name: Run Wave 20 multicam visual evidence against real pixels
run: npm run test:integration:multicam-visual-evidence
- name: Run Wave 20 multicam silence evidence against real samples
run: npm run test:integration:multicam-silence-evidence
# The composition root itself, executed. It needs `tsx` rather than the
# default gate's strip-only loader because `repository-factory.ts` pulls
# modules with TypeScript parameter properties. No database: PrismaClient
# connects lazily, so the URL points at a closed port on purpose.
- name: Run Wave 20 multicam composition root
run: npm run test:integration:multicam-composition
- name: Run Wave 20 react playback fingerprint fixture
run: npm run test:integration:playback-map-fingerprint
- name: Run Wave 20 colour measurement against decoded frames
run: npm run test:integration:color-measurement
- name: Run Wave 20 multicam colour match against real pixels
run: npm run test:integration:color-match
- name: Run Wave 20 colour visual evaluations and keep the artifacts
id: color-visual-evaluations
run: npm run test:integration:color-visual-evaluations
env:
APOLLO_COLOR_VISUAL_EVAL_OUTPUT: ${{ runner.temp }}/color-visual-evaluations
# `always()` alone made this step demand files from a job that had failed
# before the evaluations ran, so an unrelated failure earlier in the job
# was reported twice and the second report named the wrong step. It now
# runs whenever the producing step ran — including when that step itself
# failed, which is exactly when its partial output is worth keeping — and
# is skipped when the evaluations never started. `if-no-files-found`
# stays `error`: if the suite ran, the files must exist.
- name: Publish Wave 20 colour visual evaluation artifacts
if: always() && steps.color-visual-evaluations.conclusion != 'skipped'
uses: actions/upload-artifact@v4
with:
name: apollo-color-visual-evaluations-${{ github.sha }}
path: ${{ runner.temp }}/color-visual-evaluations
if-no-files-found: error
retention-days: 30
- name: Run Wave 20 multi-range synthesis render against ffprobe
run: npm run test:integration:synthesis-render
- name: Run Wave 20 repository round trip against the schema
run: npm run test:e2e:wave20-journey
- name: Run Wave 20 capture sync worker against generated audio
run: npm run test:integration:capture-sync-worker
- name: Run Wave 19 sync diagnostic persistence against PostgreSQL
run: npm run test:e2e:sync-diagnostic-persistence
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-sync-diagnostic-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_SYNC_DIAGNOSTIC_E2E: "1"
- name: Run Wave 20 multicam, colour and playback persistence against PostgreSQL
run: npm run test:e2e:wave20-persistence
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-wave20-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_WAVE20_PERSISTENCE_E2E: "1"
- name: Run Wave 20 multicam direction round trip against PostgreSQL
run: npm run test:e2e:multicam-direction
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-multicam-direction-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_MULTICAM_DIRECTION_E2E: "1"
- name: Run Wave 20 react playback journey against PostgreSQL
run: npm run test:e2e:playback-map
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-playback-map-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_PLAYBACK_MAP_E2E: "1"
- name: Run Wave 20 react playback product journey against PostgreSQL
run: npm run test:e2e:react-playback-journey
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-react-playback-journey-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_REACT_PLAYBACK_JOURNEY_E2E: "1"
APOLLO_REACT_PLAYBACK_JOURNEY_OUTPUT: ${{ runner.temp }}/react-playback-journey
# The suite reads the MP4 back with ffprobe before this runs, so the
# upload is for a human, not for the assertion. `warn` rather than
# `error`: a journey that failed before the render must show its own
# failure, not a second one about a missing file.
- name: Publish the react playback journey MP4
if: always()
uses: actions/upload-artifact@v4
with:
name: apollo-react-playback-journey-${{ github.sha }}
path: ${{ runner.temp }}/react-playback-journey
if-no-files-found: warn
retention-days: 14
- name: Run Wave 20 insufficient evidence product journey against PostgreSQL
run: npm run test:e2e:insufficient-evidence-journey
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-insufficient-evidence-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_INSUFFICIENT_EVIDENCE_E2E: "1"
- name: Run Wave 20 capture sync worker persistence against PostgreSQL
run: npm run test:e2e:capture-sync-persistence
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-capture-sync-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_CAPTURE_SYNC_E2E: "1"
- name: Run Wave 20 colour critic gate against PostgreSQL
run: npm run test:e2e:color-critic
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-color-critic-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_COLOR_CRITIC_E2E: "1"
- name: Run Wave 20 multicam long-form phase gate against PostgreSQL
run: npm run test:e2e:multicam-longform-gate
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-multicam-gate-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_MULTICAM_GATE_E2E: "1"
- name: Run Wave 20 phase gate journey through the published /v1 routes
run: npm run test:e2e:phase-gate-journey
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-phase-gate-journey-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_PHASE_GATE_E2E: "1"
- name: Run Wave 20 two-hour and ten-minute masters to rendered two-minute cuts against ffprobe and PostgreSQL
run: npm run test:e2e:longform-synthesis
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-longform-synthesis-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_LONGFORM_SYNTHESIS_E2E: "1"
# Both capture journeys refuse a database whose name does not say `e2e`
# (`assertIsolatedDatabase`, tests/v2/helpers/capture-journey.mjs): each
# deletes every row of its own workspace before and after the run, and
# that guard is what keeps a misconfigured environment from doing the same
# to a shared server. The service container is disposable, so they get
# their own database inside it instead of the guard being relaxed — the
# shape the long-form workflow step already uses on the Compose cluster.
#
# The database is created THROUGH the service container, the way the
# orphan check above already does it (`docker exec ... psql`), and not
# with a `psql` on the runner: a bare `psql` would be the only invocation
# in this workflow that depends on the runner image shipping a PostgreSQL
# client, and the day it stops shipping one this step fails before the
# journeys have a chance to say anything.
- name: Create the disposable capture journey database
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2_e2e_capture?schema=public
run: |
set -euo pipefail
postgres_container="$(docker ps --filter 'ancestor=pgvector/pgvector:0.8.5-pg16-trixie' --format '{{.ID}}')"
test -n "$postgres_container"
docker exec -e PGPASSWORD=apollo-ci "$postgres_container" \
psql -U apollo -d postgres -c 'create database apollo_v2_e2e_capture'
npm run db:v2:migrate:deploy
- name: Run Wave 20 podcast multicam product journey against PostgreSQL
run: npm run test:e2e:podcast-multicam-journey
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2_e2e_capture?schema=public&application_name=apollo-video-e2e-podcast-multicam-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_PODCAST_MULTICAM_E2E: "1"
APOLLO_PODCAST_MULTICAM_OUTPUT: ${{ runner.temp }}/podcast-multicam-journey
- name: Run Wave 20 teacher and screen product journey against PostgreSQL
run: npm run test:e2e:teacher-screen-journey
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2_e2e_capture?schema=public&application_name=apollo-video-e2e-teacher-screen-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_TEACHER_SCREEN_E2E: "1"
APOLLO_TEACHER_SCREEN_OUTPUT: ${{ runner.temp }}/teacher-screen-journey
# Each suite reads its own MP4 back with ffprobe and samples its pixels
# before this runs, so the upload is for a person, not for the assertion.
# `warn` rather than `error`: a journey that failed before the render must
# show its own failure, not a second one about a missing file.
- name: Publish the capture journey MP4s
if: always()
uses: actions/upload-artifact@v4
with:
name: apollo-capture-journeys-${{ github.sha }}
path: |
${{ runner.temp }}/podcast-multicam-journey
${{ runner.temp }}/teacher-screen-journey
if-no-files-found: warn
retention-days: 14
- name: Run Wave 19 capture protocol and diagnostic browser journey
run: npm run test:e2e:sync-diagnostic-browser
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-sync-browser-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_SYNC_DIAGNOSTIC_BROWSER_E2E: "1"
- name: Run Wave 20 multicam long-form phase gate browser journey
run: npm run test:e2e:multicam-longform-gate-browser
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-multicam-gate-browser-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_MULTICAM_GATE_BROWSER_E2E: "1"
- name: Run Wave 20 multicam, colour and playback operator browser journey
run: npm run test:e2e:wave20-browser
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-wave20-browser-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_WAVE20_BROWSER_E2E: "1"
# Wave 22: the journey a person actually meets — login, list, card,
# play/pause/seek, one annotation and its replay, the reads each open
# costs under production governance defaults, and the 409 / 429 / 401
# boundaries. Evidence JSON and screenshots are uploaded below; nothing
# in them is a cookie, a token or a signed URL.
- name: Run Wave 22 editor reliability browser journey
id: editor-reliability
run: npm run test:e2e:editor-reliability
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2_e2e_capture?schema=public&application_name=apollo-video-e2e-editor-reliability-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_EDITOR_RELIABILITY_E2E: "1"
APOLLO_EDITOR_RELIABILITY_EVIDENCE_DIR: ${{ runner.temp }}/editor-reliability
APOLLO_EDITOR_RELIABILITY_KEEP_EVIDENCE: "1"
- name: Publish Wave 22 editor reliability evidence
if: always() && steps.editor-reliability.conclusion != 'skipped'
uses: actions/upload-artifact@v4
with:
name: apollo-editor-reliability-${{ github.sha }}
path: ${{ runner.temp }}/editor-reliability
if-no-files-found: warn
retention-days: 14
- name: Run Wave 18 capture sessions browser journey
run: npm run test:e2e:capture-sessions-browser
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-capture-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_CAPTURE_SESSIONS_E2E: "1"
- name: Run transformation production journey
run: npm run test:e2e:transformation-production
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci@127.0.0.1:5432/apollo_v2?schema=public&application_name=apollo-video-e2e-transformation-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_TRANSFORMATION_E2E_SCREENSHOT: ${{ runner.temp }}/transformation-reviewed-and-accepted.png
APOLLO_TRANSFORMATION_E2E_DEBUG: "1"
- name: Publish transformation review visual evidence
uses: actions/upload-artifact@v4
with:
name: transformation-review-${{ github.sha }}
path: ${{ runner.temp }}/transformation-reviewed-and-accepted.png
if-no-files-found: error
retention-days: 30
- name: Run novelty budget decision persistence tests
run: npm run test:integration:novelty-budget
- name: Run presenter profile lifecycle tests
run: npm run test:integration:presenter-lifecycle
- name: Run durable operation persistence tests
run: npm run test:integration:operations
- name: Run webhook persistence tests
run: npm run test:integration:webhooks
- name: Run public API integration tests
run: npm run test:integration:api
local-infrastructure:
name: Isolated Compose infrastructure
runs-on: ubuntu-latest
timeout-minutes: 50
env:
COMPOSE_PROJECT_NAME: apollo-f0-${{ github.run_id }}
POSTGRES_DB: apollo_v2
POSTGRES_USER: apollo
POSTGRES_PASSWORD: apollo-ci-compose-password
POSTGRES_TUNNEL_PORT: 55432
MINIO_ROOT_USER: apollo-ci-storage
MINIO_ROOT_PASSWORD: apollo-ci-storage-password
MINIO_API_PORT: 59000
MINIO_CONSOLE_PORT: 59001
APOLLO_V2_S3_BUCKET: apollo-video-ci
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2?schema=public&application_name=apollo-video-compose-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
V2_DOCKER_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@postgres:5432/apollo_v2?schema=public&application_name=apollo-video-compose-runtime-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
APOLLO_BUILD_REVISION: compose-ci
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: 22
cache: npm
# The Wave 20 journeys spawn `worker:v2:capture-sync`, and every
# `worker:v2:*` script in this repository runs `tsx --env-file=.env`,
# which exits 9 when the file is absent. The quality job has prepared it
# since Wave 18; this job did not, and the podcast journey it gained in
# Wave 20 reported `driver exited 9: node: .env: not found` on a runner
# where nothing had created one.
- name: Prepare integration environment
run: touch .env
- name: Install locked dependencies
run: npm ci
- name: Start isolated PostgreSQL and MinIO
run: |
docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml up -d --wait postgres minio
docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml up --no-deps minio-init
- name: Migrate clean Compose PostgreSQL
run: npm run db:v2:migrate:deploy
- name: Create the isolated proof integrity E2E database
run: |
exists="$(docker compose -f infra/postgres/compose.yml exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select 1 from pg_database where datname = 'apollo_video_v2_e2e'")"
test "$exists" = 1 || docker compose -f infra/postgres/compose.yml exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres createdb -U "$POSTGRES_USER" apollo_video_v2_e2e
- name: Migrate the isolated proof integrity E2E database
run: npm run db:v2:migrate:deploy
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_video_v2_e2e?schema=public&application_name=apollo-video-e2e-proof-migrate-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Run localization translation preflight persistence proof
run: npm run test:integration:localization-preflight
env:
APOLLO_RUN_LOCALIZATION_PREFLIGHT_PG_E2E: "1"
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_video_v2_e2e?schema=public&application_name=apollo-video-e2e-localization-preflight-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Run music analysis authenticated worker persistence proof
run: npm run test:integration:music-analysis
env:
APOLLO_RUN_MUSIC_ANALYSIS_PG_E2E: "1"
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_video_v2_e2e?schema=public&application_name=apollo-video-e2e-music-analysis-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Run music montage atomic publication persistence proof
run: npm run test:integration:music-montage
env:
APOLLO_RUN_MUSIC_MONTAGE_PG_E2E: "1"
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_video_v2_e2e?schema=public&application_name=apollo-video-e2e-music-montage-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Run proof integrity gate E2E against /v1 and PostgreSQL
run: npm run test:e2e:proof-integrity
env:
APOLLO_PROOF_INTEGRITY_E2E: "1"
APOLLO_E2E_SERVER_MODE: dev
APOLLO_API_ENVIRONMENT: production
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_video_v2_e2e?schema=public&application_name=apollo-video-e2e-proof-integrity-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Provision the isolated ProofMode E2E database
run: |
docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres createdb -U "$POSTGRES_USER" apollo_v2_e2e
npm run db:v2:migrate:deploy
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2_e2e?schema=public&application_name=apollo-video-e2e-ci-proof-modes&connection_limit=5&pool_timeout=10&connect_timeout=10
# The harness already backs off and retries a legitimate 429
# (GOVERNANCE_LIMIT_EXCEEDED). APOLLO_GOVERNANCE_ANOMALY_REQUEST_MINIMUM
# additionally declares the size of this one supervised burst — 66
# authenticated /v1 calls — so the request-rate anomaly does not read a
# scripted E2E as an attack and cost the job minutes of backoff. It is an
# env knob that already exists (governanceAnomalyPolicyFromEnvironment);
# no product default changes, and every other governance control
# (rate limit, concurrency, quota, spend and error-rate anomaly) stays at
# its production value for this run.
- name: Run Phase 2 three-file reuse, validated hook, testimonial proof and ProofMode E2E
run: npm run test:e2e:proof-modes
env:
APOLLO_COMPATIBILITY_GRAPH_E2E: "1"
APOLLO_E2E_SERVER_MODE: dev
APOLLO_API_ENVIRONMENT: production
APOLLO_GOVERNANCE_ANOMALY_REQUEST_MINIMUM: "200"
APOLLO_PROTECTED_PAYLOAD_KEY_ID: ci-protected-payload
APOLLO_PROTECTED_PAYLOAD_KEY: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2_e2e?schema=public&application_name=apollo-video-e2e-ci-proof-modes&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Verify no orphan ProofMode E2E backends remain
if: always()
run: |
orphans="$(docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select count(*) from pg_stat_activity where application_name = 'apollo-video-e2e-ci-proof-modes'")"
test "$orphans" = 0
- name: Run export matrix persistence and five-format worker E2E
env:
APOLLO_EXPORT_MATRIX_DB: "1"
APOLLO_EXPORT_MATRIX_E2E: "1"
APOLLO_E2E_SERVER_MODE: dev
APOLLO_API_ENVIRONMENT: production
APOLLO_V2_ARTIFACT_ROOT: ${{ runner.temp }}/apollo-v2-export-matrix
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2_e2e_export_matrix?schema=public&application_name=apollo-video-e2e-export-matrix-${{ github.run_id }}&connection_limit=5&pool_timeout=10&connect_timeout=10
run: |
set -euo pipefail
compose="docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml"
preflight="$($compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select count(*) from pg_stat_activity where application_name like 'apollo-video-e2e-%'")"
test "$preflight" = 0
$compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres createdb -U "$POSTGRES_USER" apollo_v2_e2e_export_matrix
npm run db:v2:migrate:deploy
npm run test:integration:export-matrix
npm run test:e2e:export-matrix
- name: Verify zero orphan export matrix E2E backends
if: always()
env:
APOLLO_EXPORT_MATRIX_APP_NAME: apollo-video-e2e-export-matrix-${{ github.run_id }}
run: |
set -euo pipefail
compose="docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml"
for attempt in 1 2 3 4 5 6 7 8 9 10; do
backends="$($compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select count(*) from pg_stat_activity where application_name = '$APOLLO_EXPORT_MATRIX_APP_NAME'")"
if test "$backends" = 0; then exit 0; fi
sleep 1
done
$compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select pid, application_name, state from pg_stat_activity where application_name = '$APOLLO_EXPORT_MATRIX_APP_NAME'"
exit 1
- name: Resume long-form indexing on a disposable Compose database
env:
APOLLO_LONG_FORM_WORKFLOW_E2E: "1"
APOLLO_API_ENVIRONMENT: production
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2_e2e_long_form?schema=public&application_name=apollo-video-e2e-long-form-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
GROQ_API_KEY: ci-not-a-real-groq-credential-000
GROQ_TRANSCRIBE_MODEL: whisper-large-v3
GROQ_TRANSCRIBE_COST_MINOR_UNITS_PER_HOUR: "1"
OPENAI_API_KEY: ci-not-a-real-openai-credential-0
OPENAI_DIARIZATION_MODEL: gpt-4o-transcribe-diarize
OPENAI_DIARIZATION_COST_MINOR_UNITS_PER_HOUR: "1"
APOLLO_PROTECTED_PAYLOAD_KEY_ID: ci-protected-payload
APOLLO_PROTECTED_PAYLOAD_KEY: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
run: |
set -euo pipefail
administer() {
docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "$1"
}
preflight="$(administer "select count(*) from pg_stat_activity where application_name like 'apollo-video-e2e-%'")"
printf 'preflight e2e backends: %s\n' "$preflight"
test "$preflight" = 0
administer "create database apollo_v2_e2e_long_form"
npm run db:v2:migrate:deploy
npm run test:e2e:long-form-index-workflow
orphans=1
for _ in 1 2 3 4 5 6 7 8 9 10; do
orphans="$(administer "select count(*) from pg_stat_activity where application_name like 'apollo-video-e2e-long-form-ci%'")"
if test "$orphans" = 0; then break; fi
sleep 1
done
printf 'postflight e2e backends: %s\n' "$orphans"
test "$orphans" = 0
- name: Reconstruct proxy from PostgreSQL and versioned MinIO only
run: node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON --test tests/v2/prisma-vertical-smoke.integration.mjs
env:
APOLLO_V2_VERTICAL_SMOKE: "1"
APOLLO_V2_S3_RECONSTRUCTION_SMOKE: "1"
APOLLO_V2_ARTIFACT_STORAGE_DRIVER: s3
APOLLO_V2_ARTIFACT_ROOT: ${{ runner.temp }}/apollo-v2-s3-staging
APOLLO_V2_RENDER_WORK_ROOT: ${{ runner.temp }}/apollo-v2-s3-render
APOLLO_V2_S3_ENDPOINT: http://127.0.0.1:59000
APOLLO_V2_S3_REGION: us-east-1
APOLLO_V2_S3_ACCESS_KEY_ID: ${{ env.MINIO_ROOT_USER }}
APOLLO_V2_S3_SECRET_ACCESS_KEY: ${{ env.MINIO_ROOT_PASSWORD }}
APOLLO_V2_S3_FORCE_PATH_STYLE: "true"
APOLLO_V2_S3_ALLOW_INSECURE_HTTP: "true"
- name: Run durable TTS-to-avatar journey against PostgreSQL and versioned MinIO
run: npm run test:e2e:provider-journey
env:
APOLLO_V2_ARTIFACT_STORAGE_DRIVER: s3
APOLLO_V2_S3_ENDPOINT: http://127.0.0.1:59000
APOLLO_V2_S3_REGION: us-east-1
APOLLO_V2_S3_ACCESS_KEY_ID: ${{ env.MINIO_ROOT_USER }}
APOLLO_V2_S3_SECRET_ACCESS_KEY: ${{ env.MINIO_ROOT_PASSWORD }}
APOLLO_V2_S3_FORCE_PATH_STYLE: "true"
APOLLO_V2_S3_ALLOW_INSECURE_HTTP: "true"
# Exclusive per-run bucket: the test itself creates it, enables
# versioning, proves zero orphan objects and deletes it afterwards.
APOLLO_V2_S3_BUCKET: apollo-journey-${{ github.run_id }}-${{ github.run_attempt }}
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2?schema=public&application_name=apollo-video-e2e-provider-journey-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Run block plan journey through /v1, durable workers and versioned MinIO
run: npm run test:e2e:block-plan-journey
env:
APOLLO_V2_ARTIFACT_STORAGE_DRIVER: s3
APOLLO_V2_S3_ENDPOINT: http://127.0.0.1:59000
APOLLO_V2_S3_REGION: us-east-1
APOLLO_V2_S3_ACCESS_KEY_ID: ${{ env.MINIO_ROOT_USER }}
APOLLO_V2_S3_SECRET_ACCESS_KEY: ${{ env.MINIO_ROOT_PASSWORD }}
APOLLO_V2_S3_FORCE_PATH_STYLE: "true"
APOLLO_V2_S3_ALLOW_INSECURE_HTTP: "true"
# Exclusive per-run bucket: the test creates it, enables versioning,
# proves zero orphan objects and deletes it afterwards.
APOLLO_V2_S3_BUCKET: apollo-blockplan-${{ github.run_id }}-${{ github.run_attempt }}
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2?schema=public&application_name=apollo-video-e2e-block-plan-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
# The Wave 20 product journeys run in the `quality` job, which has
# PostgreSQL and no MinIO, and every one of them used to pin
# APOLLO_V2_ARTIFACT_STORAGE_DRIVER=local inside the test file — so the
# briefing's "PostgreSQL 16 AND versioned object storage" was true of
# neither half. The driver now comes from the environment, and these two
# steps are the half that was missing: the journey with the heaviest
# media path and the journey that decides whether the phase ships, both
# against the versioned MinIO this job already runs.
#
# They run HERE and stay in `quality` too, on purpose: the `quality` runs
# keep proving the local driver, and a difference between the two answers
# is the signal worth having.
- name: Provision the isolated capture journey database
run: |
docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres createdb -U "$POSTGRES_USER" apollo_v2_e2e_capture
npm run db:v2:migrate:deploy
env:
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2_e2e_capture?schema=public&application_name=apollo-video-e2e-capture-migrate-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Run Wave 20 podcast multicam journey against PostgreSQL and versioned MinIO
run: npm run test:e2e:podcast-multicam-journey
env:
APOLLO_PODCAST_MULTICAM_E2E: "1"
APOLLO_API_ENVIRONMENT: production
APOLLO_PODCAST_MULTICAM_OUTPUT: ${{ runner.temp }}/podcast-multicam-journey-s3
APOLLO_V2_ARTIFACT_STORAGE_DRIVER: s3
APOLLO_V2_S3_ENDPOINT: http://127.0.0.1:59000
APOLLO_V2_S3_REGION: us-east-1
APOLLO_V2_S3_ACCESS_KEY_ID: ${{ env.MINIO_ROOT_USER }}
APOLLO_V2_S3_SECRET_ACCESS_KEY: ${{ env.MINIO_ROOT_PASSWORD }}
APOLLO_V2_S3_FORCE_PATH_STYLE: "true"
APOLLO_V2_S3_ALLOW_INSECURE_HTTP: "true"
# Exclusive per-run bucket: the journey creates it, enables
# versioning, proves zero orphan objects and deletes it afterwards.
APOLLO_V2_S3_BUCKET: apollo-podcast-${{ github.run_id }}-${{ github.run_attempt }}
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2_e2e_capture?schema=public&application_name=apollo-video-e2e-podcast-multicam-s3-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Run Wave 20 phase gate journey against PostgreSQL and versioned MinIO
run: npm run test:e2e:phase-gate-journey
env:
APOLLO_PHASE_GATE_E2E: "1"
APOLLO_API_ENVIRONMENT: production
APOLLO_V2_ARTIFACT_STORAGE_DRIVER: s3
APOLLO_V2_S3_ENDPOINT: http://127.0.0.1:59000
APOLLO_V2_S3_REGION: us-east-1
APOLLO_V2_S3_ACCESS_KEY_ID: ${{ env.MINIO_ROOT_USER }}
APOLLO_V2_S3_SECRET_ACCESS_KEY: ${{ env.MINIO_ROOT_PASSWORD }}
APOLLO_V2_S3_FORCE_PATH_STYLE: "true"
APOLLO_V2_S3_ALLOW_INSECURE_HTTP: "true"
# The gate reads rows and manifests, never bytes — no route it
# serves constructs artifact storage at all — so this bucket exists
# to be asserted EMPTY at the end, which is what makes that sentence
# falsifiable rather than a comment. No APOLLO_V2_RENDER_WORK_ROOT on
# purpose: a criterion that starts reading bytes fails here with
# PERSISTENCE_NOT_CONFIGURED instead of quietly succeeding.
APOLLO_V2_S3_BUCKET: apollo-phase-gate-${{ github.run_id }}-${{ github.run_attempt }}
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2?schema=public&application_name=apollo-video-e2e-phase-gate-s3-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Verify zero orphan capture journey backends and buckets
if: always()
run: |
set -euo pipefail
compose="docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml"
for name in apollo-video-e2e-podcast-multicam-s3-ci apollo-video-e2e-phase-gate-s3-ci; do
for attempt in 1 2 3 4 5 6 7 8 9 10; do
backends="$($compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select count(*) from pg_stat_activity where application_name = '$name'")"
if test "$backends" = 0; then break; fi
sleep 1
done
echo "$name backends: $backends"
test "$backends" = 0
done
for bucket in "apollo-podcast-${{ github.run_id }}-${{ github.run_attempt }}" "apollo-phase-gate-${{ github.run_id }}-${{ github.run_attempt }}"; do
if $compose exec -T minio mc ls "local/$bucket" >/dev/null 2>&1; then
echo "run-exclusive bucket $bucket still exists after cleanup" >&2
exit 1
fi
done
# Read back with ffprobe and sampled for pixels by the journey itself
# before this runs, so the upload is for a person. Separate from the
# `quality` job's copy on purpose: this is the file the object-storage
# run delivered, and comparing the two is the point.
- name: Publish the versioned-storage podcast journey MP4
if: always()
uses: actions/upload-artifact@v4
with:
name: apollo-podcast-journey-s3-${{ github.sha }}
path: ${{ runner.temp }}/podcast-multicam-journey-s3
if-no-files-found: warn
retention-days: 14
- name: Run synthetic master cross-project reuse E2E against /v1 and PostgreSQL
run: npm run test:e2e:synthetic-master-reuse
env:
# Local artifact storage on purpose: this journey proves reuse costs
# nothing, so it must not depend on an object store round trip.
APOLLO_V2_ARTIFACT_STORAGE_DRIVER: local
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2?schema=public&application_name=apollo-video-e2e-master-reuse-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Run synthetic production, criticism and cache reuse journey against /v1, durable workers and PostgreSQL
run: npm run test:e2e:synthetic-production-journey
env:
# Local artifact storage on purpose: this journey rewrites stored
# bytes to prove a tampered blob is never reused, and reads them back
# with ffprobe through the real critic adapters.
APOLLO_V2_ARTIFACT_STORAGE_DRIVER: local
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2?schema=public&application_name=apollo-video-e2e-production-journey-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Verify zero orphan synthetic production journey backends
if: always()
run: |
set -euo pipefail
compose="docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml"
for attempt in 1 2 3 4 5 6 7 8 9 10; do
backends="$($compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select count(*) from pg_stat_activity where application_name = 'apollo-video-e2e-production-journey-ci'")"
if test "$backends" = 0; then break; fi
sleep 1
done
test "$backends" = 0
- name: Run synthetic cache eligibility and invalidation against PostgreSQL
run: npm run test:integration:synthetic-cache-invalidation
env:
# Local artifact storage on purpose: this suite measures provider
# traffic and cache decisions, not object store round trips.
APOLLO_V2_ARTIFACT_STORAGE_DRIVER: local
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2?schema=public&application_name=apollo-video-cache-invalidation-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Run synthetic critic report persistence against PostgreSQL
run: npm run test:integration:synthetic-critic-report
env:
# Local artifact storage on purpose: this suite proves the verdict
# projection and its fail-closed rehydration, not object store round
# trips.
APOLLO_V2_ARTIFACT_STORAGE_DRIVER: local
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2?schema=public&application_name=apollo-video-critic-report-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Verify zero orphan synthetic master reuse backends
if: always()
run: |
set -euo pipefail
compose="docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml"
for attempt in 1 2 3 4 5 6 7 8 9 10; do
backends="$($compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select count(*) from pg_stat_activity where application_name = 'apollo-video-e2e-master-reuse-ci'")"
if test "$backends" = 0; then break; fi
sleep 1
done
test "$backends" = 0
- name: Install locked renderer dependencies for the production build
run: npm ci --prefix remotion
- name: Build production application for the browser journey
run: npm run build
- name: Run localization media fail-closed persistence proof
run: npm run test:integration:localization-media
env:
APOLLO_RUN_LOCALIZATION_MEDIA_PG_E2E: "1"
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_video_v2_e2e?schema=public&application_name=apollo-video-e2e-localization-media-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Run localization media proxy-backed positive persistence proof
run: npm run test:integration:localization-media-proxy
env:
APOLLO_RUN_LOCALIZATION_MEDIA_PROXY_PG_E2E: "1"
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_video_v2_e2e?schema=public&application_name=apollo-video-e2e-localization-media-proxy-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Run localization browser journey against the production build
run: npm run test:e2e:localization-browser
env:
APOLLO_LOCALIZATION_BROWSER_E2E: "1"
PLAYWRIGHT_CHROME_EXECUTABLE: /usr/bin/google-chrome
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_video_v2_e2e?schema=public&application_name=apollo-video-e2e-localization-browser-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Run presenter lifecycle browser journey against PostgreSQL and versioned MinIO
run: npm run test:e2e:presenter-lifecycle
env:
APOLLO_V2_ARTIFACT_STORAGE_DRIVER: s3
APOLLO_V2_S3_ENDPOINT: http://127.0.0.1:59000
APOLLO_V2_S3_REGION: us-east-1
APOLLO_V2_S3_ACCESS_KEY_ID: ${{ env.MINIO_ROOT_USER }}
APOLLO_V2_S3_SECRET_ACCESS_KEY: ${{ env.MINIO_ROOT_PASSWORD }}
APOLLO_V2_S3_FORCE_PATH_STYLE: "true"
APOLLO_V2_S3_ALLOW_INSECURE_HTTP: "true"
# Exclusive per-run bucket: the test creates it, enables versioning,
# proves zero orphan objects and deletes it afterwards.
APOLLO_V2_S3_BUCKET: apollo-presenter-${{ github.run_id }}-${{ github.run_attempt }}
PLAYWRIGHT_CHROME_EXECUTABLE: /usr/bin/google-chrome
V2_DATABASE_URL: postgresql://apollo:apollo-ci-compose-password@127.0.0.1:55432/apollo_v2?schema=public&application_name=apollo-video-e2e-presenter-ci&connection_limit=5&pool_timeout=10&connect_timeout=10
- name: Verify zero orphan presenter journey backends and objects
if: always()
run: |
set -euo pipefail
compose="docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml"
for attempt in 1 2 3 4 5 6 7 8 9 10; do
backends="$($compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select count(*) from pg_stat_activity where application_name = 'apollo-video-e2e-presenter-ci'")"
if test "$backends" = 0; then break; fi
sleep 1
done
test "$backends" = 0
if $compose exec -T minio mc ls "local/apollo-presenter-${{ github.run_id }}-${{ github.run_attempt }}" >/dev/null 2>&1; then
echo "run-exclusive presenter bucket still exists after cleanup" >&2
exit 1
fi
- name: Verify zero orphan block plan backends and objects
if: always()
run: |
set -euo pipefail
compose="docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml"
for attempt in 1 2 3 4 5 6 7 8 9 10; do
backends="$($compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select count(*) from pg_stat_activity where application_name = 'apollo-video-e2e-block-plan-ci'")"
if test "$backends" = 0; then break; fi
sleep 1
done
test "$backends" = 0
if $compose exec -T minio mc ls "local/apollo-blockplan-${{ github.run_id }}-${{ github.run_attempt }}" >/dev/null 2>&1; then
echo "run-exclusive block plan bucket still exists after cleanup" >&2
exit 1
fi
- name: Verify zero orphan provider journey backends and objects
if: always()
run: |
set -euo pipefail
compose="docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml"
for attempt in 1 2 3 4 5 6 7 8 9 10; do
backends="$($compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select count(*) from pg_stat_activity where application_name = 'apollo-video-e2e-provider-journey-ci'")"
if test "$backends" = 0; then break; fi
sleep 1
done
test "$backends" = 0
if $compose exec -T minio mc ls "local/apollo-journey-${{ github.run_id }}-${{ github.run_attempt }}" >/dev/null 2>&1; then
echo "run-exclusive journey bucket still exists after cleanup" >&2
exit 1
fi
- name: Verify PostgreSQL and versioned object round-trip
run: |
table_count="$(docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select count(*) from information_schema.tables where table_schema = 'public'")"
test "$table_count" -ge 128
version_info="$(docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml exec -T minio mc version info "local/$APOLLO_V2_S3_BUCKET")"
printf '%s' "$version_info" | grep -qi enabled
printf apollo-v2-compose-smoke | docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml exec -T minio mc pipe "local/$APOLLO_V2_S3_BUCKET/gates/smoke.txt"
object_value="$(docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml exec -T minio mc cat "local/$APOLLO_V2_S3_BUCKET/gates/smoke.txt")"
test "$object_value" = apollo-v2-compose-smoke
- name: Start supervised app and workers
run: |
printf '%s\n' \
'GROQ_API_KEY=ci-not-a-real-groq-credential-000' \
'GROQ_TRANSCRIBE_MODEL=whisper-large-v3' \
'GROQ_TRANSCRIBE_COST_MINOR_UNITS_PER_HOUR=1' \
'OPENAI_API_KEY=ci-not-a-real-openai-credential-0' \
'OPENAI_DIARIZATION_MODEL=gpt-4o-transcribe-diarize' \
'OPENAI_DIARIZATION_COST_MINOR_UNITS_PER_HOUR=1' \
'APOLLO_PROTECTED_PAYLOAD_KEY_ID=ci-protected-payload' \
'APOLLO_PROTECTED_PAYLOAD_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' \
> .env.local
docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml -f infra/workflow/compose.yml up -d --build --wait app ingest-worker render-worker webhook-worker long-form-worker
curl --fail --silent --show-error http://127.0.0.1:3333/v1/health > /dev/null
for service in app ingest-worker render-worker webhook-worker long-form-worker; do
container_id="$(docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml -f infra/workflow/compose.yml ps -q "$service")"
status="$(docker inspect --format '{{.State.Status}}' "$container_id")"
restart_policy="$(docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' "$container_id")"
printf '%s id=%s status=%s restart=%s\n' "$service" "$container_id" "$status" "$restart_policy"
test -n "$container_id"
if test "$status" != running; then
docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml -f infra/workflow/compose.yml logs --no-color --tail 100 "$service"
exit 1
fi
test "$restart_policy" = unless-stopped
done
sleep 5
for service in app ingest-worker render-worker webhook-worker long-form-worker; do
container_id="$(docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml -f infra/workflow/compose.yml ps -q "$service")"
test "$(docker inspect --format '{{.State.Status}}' "$container_id")" = running
test "$(docker inspect --format '{{.RestartCount}}' "$container_id")" = 0
done
# Same knob and same reasoning as the ProofMode step above: the harness
# already backs off and waits out a legitimate 429
# (GOVERNANCE_LIMIT_EXCEEDED). APOLLO_GOVERNANCE_ANOMALY_REQUEST_MINIMUM
# declares the size of this one supervised burst — seeding a 1.000
# document corpus plus its queries and evaluations is ~1.045 authenticated
# /v1 calls at ~630 requests per minute — so the request-rate anomaly does
# not read a scripted E2E as an attack and spend the job in backoff. It is
# an env knob that already exists (governanceAnomalyPolicyFromEnvironment);
# no product default changes, and every other governance control (rate
# limit, concurrency, quota, spend and error-rate anomaly) stays at its
# production value for this run.
- name: Run cross-asset semantic search E2E on a dedicated database
env:
APOLLO_HYBRID_SEARCH_E2E: "1"
APOLLO_GOVERNANCE_ANOMALY_REQUEST_MINIMUM: "5000"
APOLLO_E2E_SERVER_MODE: dev
APOLLO_API_ENVIRONMENT: sandbox
APOLLO_SEMANTIC_EMBEDDING_PROVIDER: openai
OPENAI_API_KEY: ci-not-a-real-openai-credential-0
APOLLO_E2E_HYBRID_SEARCH_DATABASE: apollo_v2_e2e_hybrid_search
APOLLO_E2E_HYBRID_SEARCH_APP_NAME: apollo-video-e2e-hybrid-search-${{ github.run_id }}
run: |
set -euo pipefail
compose="docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml"
orphans="$($compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select count(*) from pg_stat_activity where application_name like 'apollo-video-e2e-%'")"
printf 'preflight e2e backends=%s\n' "$orphans"
test "$orphans" = 0
$compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -v ON_ERROR_STOP=1 -Atc "create database \"$APOLLO_E2E_HYBRID_SEARCH_DATABASE\" owner \"$POSTGRES_USER\""
export V2_DATABASE_URL="postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@127.0.0.1:$POSTGRES_TUNNEL_PORT/$APOLLO_E2E_HYBRID_SEARCH_DATABASE?schema=public&application_name=$APOLLO_E2E_HYBRID_SEARCH_APP_NAME&connection_limit=5&pool_timeout=10&connect_timeout=10"
npm run db:v2:migrate:deploy
npm run test:e2e:hybrid-search
- name: Verify zero orphan E2E backends after semantic search E2E
if: always()
env:
APOLLO_E2E_HYBRID_SEARCH_APP_NAME: apollo-video-e2e-hybrid-search-${{ github.run_id }}
run: |
set -euo pipefail
compose="docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml"
for attempt in 1 2 3 4 5 6 7 8 9 10; do
backends="$($compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select count(*) from pg_stat_activity where application_name = '$APOLLO_E2E_HYBRID_SEARCH_APP_NAME'")"
printf 'postflight attempt=%s backends=%s\n' "$attempt" "$backends"
if test "$backends" = 0; then
exit 0
fi
sleep 3
done
$compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "select pid, application_name, state, query_start from pg_stat_activity where application_name like 'apollo-video-e2e-%'"
exit 1
- name: Stop isolated infrastructure
if: always()
run: docker compose -f infra/postgres/compose.yml -f infra/object-storage/compose.yml -f infra/workflow/compose.yml down --volumes --remove-orphans --timeout 30