Repository navigation
309 lines (302 loc) · 20.3 KB
/
Copy pathci.yml
File metadata and controls
309 lines (302 loc) · 20.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
name: CI
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Engine tests
# Phase-1 (DETERMINISTIC-3): coverage is report-only (no --cov-fail-under yet) so it
# surfaces untested paths without flaking CI. pytest-cov is in the dev group.
run: uv run --directory servers/engine --group dev pytest -q --cov=. --cov-report=term-missing
- name: Rules tests
env:
WORLDOS_RULES_OFFLINE: "1"
run: uv run --directory servers/rules --group dev pytest -q --cov=. --cov-report=term-missing
- name: Voice tests (torch-free; null backend + static metadata)
run: uv run --directory servers/voice --group dev pytest -q --cov=. --cov-report=term-missing
- name: Static walkability gate (manifest lint + ortho triple-check + geometry; stdlib-only)
# Epic #1581: the STATIC half of the walkability gate runs on EVERY PR — a manifest entry
# without ortho/plate/sidecar, a drifted ortho (manifest vs sidecar vs fit-math), a blocked
# door landing, or an orphan walkable pocket goes RED pre-merge, with no player and no deps.
# Red-first units + the same repo check also run under pytest: qa/test_walk_static.py.
run: python3 qa/walk_static.py
- name: Static walkability units (red-first proofs for the gate itself)
run: uv run --directory servers/engine --group dev pytest -q -p no:xdist ../../qa/test_walk_static.py
- name: Occluder sidecar vs grid gate (every manifest sidecar projected onto its geometry; stdlib-only)
# "The room is the scene" Day 1 (#1793): a box over walkable floor is the cyan-ghost class, an impassable cell
# with no box draws the walker through the painted mass. Red-first units: qa/test_sidecar_grid_check.py.
run: python3 qa/sidecar_grid_check.py
- name: Occluder sidecar gate units
run: uv run --directory servers/engine --group dev pytest -q -p no:xdist ../../qa/test_sidecar_grid_check.py
- name: Per-object alignment gate units (red-first proofs for qa/object_align_check.py)
# The instrument's own failure modes, pinned: a FEATURELESS crop pair correlates to a perfect
# dx=dy=0 with a dead peak (a deleted object certified as aligned — the exact class the gate
# exists to catch), and objects the gate cannot measure must be counted + named in the verdict
# rather than silently exempted. Runs in this lane because the engine dev group already carries
# numpy + Pillow; a review on #1690 flagged the instrument shipping with no CI reference at all.
run: uv run --directory servers/engine --group dev pytest -q -p no:xdist ../../qa/test_object_align_check.py
- name: Spawn-placement unit test (#1584/#1651 CI half — choose_spawns places the party on the open-floor
centroid, never a barrel/corner/door; was ZERO CI references before this)
run: uv run --directory servers/engine --group dev pytest -q -p no:xdist ../../qa/test_seed_spawns.py
# #1651 live-half slice — the tri-state classifier, the 3D occlusion / head-density silhouette
# detector, and the spawn/coherence-open assert. Pure cores only; the live sandbox drive is
# box-hosted, not run in CI.
- name: player_cert primitives unit test (#1651 live-half slice)
run: uv run --directory servers/engine --group dev pytest -q -p no:xdist ../../qa/test_player_cert.py
- name: Single-flight launch-guard test (scripts/launch_common.sh lock)
# Pure bash + POSIX mkdir/kill -0/rm — no uv/claude/viewer. Proves a second concurrent
# play_party.sh cold-open is cleanly rejected and the lock self-heals (release / dead holder).
run: bash qa/test_play_party_single_flight.sh
- name: Velocity-gates test (fast_gate green-cache + release_gate RAM preflight; pure bash)
# Phase-1 (VELOCITY-1/4): proves the opt-in fast_gate cache short-circuits a same-SHA
# rerun and the release_gate RAM preflight refuses a sweep under a forced-low-RAM stub.
run: bash qa/test_velocity_gates.sh
- name: Dry-run-gate-fix test (median-of-N + verdict logic via stubbed runner; pure bash)
# Phase-2 (AUTONOMY-6): proves the agent's pre-PR fix self-check computes the median + GREEN/
# below-bar verdict + exit codes correctly, with the live runner stubbed (no model key).
run: bash qa/test_dry_run_gate_fix.sh
- name: run_duo retry robustness test (transient-vs-real failure classification; pure bash)
# Proves turn_retry now retries a TRANSIENT 500/overload/429/timeout cluster up to 4x with
# backoff (the gs-ember-18b overnight-run killer) but FAILS FAST on a 401/403 auth error,
# with the claude turn fully stubbed (no model key).
run: bash qa/test_run_duo_retry_robustness.sh
- name: Companion-actor timeout test (play_party.sh per-beat deadline; stubbed claude; pure bash)
run: bash qa/test_play_party_actor_timeout.sh
- name: Provider-status sidecar test (claude lanes write provider_status on stop/crash; pure bash)
run: bash qa/test_provider_status_claude_lanes.sh
- name: run_duo DM-timeout test (bounded DM beat + failure surfacing + retry remint; pure bash)
run: bash qa/test_run_duo_dm_timeout.sh
- name: Stream-tailer lifecycle test (start/stop/reap pidfile; default-dark; pure bash)
run: bash qa/test_stream_tailer_lifecycle.sh
- name: ui_playtest heartbeat test (model-independent progress heartbeat on both DM lanes; pure bash)
run: bash qa/test_ui_playtest_heartbeat.sh
- name: Combat-seed smoke (zero-LLM; pre_seed_combat subclass/resource/save enrichment assertions)
run: uv run --directory servers/engine --group dev python ../../qa/smoke_pre_seed_combat.py
# #1651 CI half — qa/certifications pins plate/boxes/geometry shas; ANY drift since certification
# must read STALE, never silently green. Now BLOCKING (#1668): shop's manifest_entry drift
# (door_hotspots added by #1649) was re-certified in the sandbox lane, so the gate is green and
# continue-on-error is removed — a red-but-non-blocking gate is the audit's decay pattern (case 6,
# only red CI enforces the property).
- name: Room-certification freshness test (#1651 CI half; now BLOCKING per #1668)
run: uv run --directory servers/engine --group dev pytest -q -p no:xdist ../../qa/test_room_certification.py
viewer-tests:
# The viewer is where most recent regressions landed, but its suite was not in
# CI (three viewer PRs each had to caveat "verified locally"). This job runs the
# whole viewer/tests/ suite, single-process, on push + PR so viewer regressions
# are caught in CI like the engine/rules/voice suites.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install Node (for the JSX behaviour harness)
# A subset of viewer tests transpile the real .jsx through the VENDORED
# babel-standalone (viewer/openworlds/vendor/) and run it under node's `vm`.
# Babel is vendored and the harness only requires node built-ins (fs, vm), so
# NO `npm install` is needed — just a node binary on PATH. Without node these
# tests `skipIf` themselves; we install it so they actually RUN in CI.
uses: actions/setup-node@v7
with:
node-version: "20"
- name: Create + activate a venv with the viewer test deps
# Most viewer tests import viewer/server.py (stdlib-only at import time) and need
# no third-party deps. The exception is test_portrait_gen's real-subprocess test,
# which shells the engine via `uv run --directory servers/engine --no-project`
# (a bare interpreter) to prove the null image provider opens no socket. That
# child resolves the ACTIVE venv, so installing pydantic (the engine's runtime
# dep, declared in servers/engine/pyproject.toml) here lets the engine subprocess
# import `store`/`imagegen` and the test runs instead of erroring on import.
run: |
uv venv .venv-viewer --python 3.12
echo "VIRTUAL_ENV=$PWD/.venv-viewer" >> "$GITHUB_ENV"
echo "$PWD/.venv-viewer/bin" >> "$GITHUB_PATH"
uv pip install --python .venv-viewer pydantic pytest
- name: Viewer tests (single-process; JSX harness via node + vendored babel)
# -p no:xdist keeps this single-process (no parallel workers) — lean by design.
run: python -m pytest viewer/tests -q -p no:xdist
qa-release-gate-tests:
# #729: the qa/ release-gate + scoring + wrapper-contract tests were NEVER in CI — the `test`
# job only collects servers/*/tests/. A *tested* crash (#723 `build_shas`, caught by
# test_release_readiness.py's 4 build_sha tests) shipped because CI never ran them. Gate the
# release-authority surface (release_readiness.py, scores_db.py, the play-wrapper contracts) so
# a regression there can no longer merge green.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: QA release-gate + scoring + wrapper-contract tests
# Run from the engine venv (pydantic etc. on path) with cwd=servers/engine, so the qa tests'
# `import server/store` resolves and release_readiness.py subprocesses use the same interpreter.
run: |
uv run --directory servers/engine --group dev python -m pytest -q -p no:xdist \
../../qa/test_release_readiness.py \
../../qa/test_release_readiness_scope.py \
../../qa/test_scores_db_comparability.py \
../../qa/test_scores_db.py \
../../qa/test_artifact_evals.py \
../../qa/test_closeout.py \
../../qa/test_duo_resume.py \
../../qa/test_release_gate_static.py \
../../qa/test_ui_playtest_score_image_outcomes.py \
../../qa/test_claude_auth_isolation.py \
../../qa/test_snapshot_world_state.py \
../../qa/test_score_determinism.py \
../../qa/test_lens_variance.py \
../../qa/test_detect_regression.py \
../../qa/test_root_cause_analyzer.py \
../../qa/test_evidence_audit.py \
../../qa/test_triage_failure.py \
../../qa/test_behavioral_gate_corpus.py \
../../qa/test_deterministic_rri_gate.py \
../../qa/test_orchestrate_split_rri.py \
../../qa/test_visual_regression_check.py \
../../qa/test_visual_critic.py \
../../qa/test_structural_coverage.py \
../../qa/test_story_readout_approval.py \
../../qa/test_feature_engagement.py \
../../qa/test_fact_fidelity.py \
../../qa/test_recap_fidelity.py \
../../qa/test_assert_behavioral.py \
../../qa/test_arc_gate.py \
../../qa/test_distill.py \
../../qa/test_story_readout_coverage.py \
../../qa/test_story_readout_outcome.py \
../../qa/test_story_readout_timing.py \
../../qa/test_latency_rollup.py \
../../qa/test_stream_tailer.py \
../../qa/test_collect_findings.py \
../../qa/test_dm_beat_mark.py \
../../qa/test_export_app_evidence.py \
../../qa/test_macos_app_static.py \
../../qa/test_ui_playtest_app_buckets.py \
../../qa/test_app_failure_buckets.py \
../../qa/test_app_handoff_gate.py \
../../qa/test_app_smoke_scripted.py \
../../qa/test_support_vm_preflight.py \
../../qa/test_version_consistency.py \
../../qa/test_generate_release_notes.py \
../../qa/test_combat_smoke.py \
../../qa/test_render_recipe.py
- name: Arc-mode runbook proof (shell; default-path byte identity)
# Not a pytest file, so it needs its own step: this is the ONLY proof that WORLDOS_ARC_MODE
# leaves the duo/play runbook strings byte-identical (the run_duo no-drift guarantee).
run: bash qa/tests/test_arc_mode_runbook.sh
server-contracts:
# Phase-1 (DETERMINISTIC-2): deterministic cross-service MCP contract tests
# (engine<->rules<->voice; null/torch-free voice backend, offline rules). The rules
# boundary runs through the rules venv (rapidfuzz), so sync BOTH servers before running.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Sync rules venv (rapidfuzz/httpx for the offline rules worker)
env:
WORLDOS_RULES_OFFLINE: "1"
run: uv sync --directory servers/rules
- name: Cross-service MCP contract tests
env:
WORLDOS_RULES_OFFLINE: "1"
run: uv run --directory servers/engine --group dev python -m pytest -q -p no:xdist ../../servers/integration_tests
paint-drift-gate:
# W6.3 (#1462): the DETERMINISTIC paint-drift eval — a regenerated canonical_plate must keep its
# painted set pieces on the authored logical cells (qa/check_plate_drift.py). Its own lane because
# the check needs the qa image stack (Pillow + numpy), which the engine venv is intentionally free
# of (so those heavy deps never touch the engine/rules/voice suites).
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
# #1651 CI half: check_coherence_freshness.py below walks git history (last commit touching
# each paint-coherence report vs the plate's blob sha at HEAD) — needs full history, not the
# default shallow depth-1 clone, or every room reads as "not tracked by git" instead of a
# real verdict.
fetch-depth: 0
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Create a venv with the qa image lane (Pillow + numpy + pytest)
run: |
uv venv .venv-qa-image --python 3.12
echo "$PWD/.venv-qa-image/bin" >> "$GITHUB_PATH"
uv pip install --python .venv-qa-image pillow numpy pytest
- name: Manifests match their seeds (regeneratable, not stale)
run: python qa/build_room_manifest.py --check
- name: Paint-drift gate tests (known-good PASS + synthetic 2-cell shift CAUGHT; single-process)
run: python -m pytest qa/test_plate_drift_gate.py -q -p no:xdist
- name: room_recipes.json canonical_plate drift gate (every committed manifest+plate)
run: python qa/check_plate_drift.py gate-recipes
- name: Grid<->paint coherence gate tests (aligned PASS + synthetic shift + current-crypt INCOHERENT)
# The ABSOLUTE grid<->paint gate (qa/check_grid_paint_coherence.py): a painted prop must sit on
# its OWN authored footprint, not just avoid drifting from a baseline (the drift gate above).
# This is the eval that would have caught the sarcophagus the owner walked onto (paint ~3/4 cell
# off the grid the engine keys collision to). Deterministic + self-contained (regenerates the
# greybox from the manifest geometry); single-process. Reliable for hard-silhouette architectural
# props — the crypt reality anchor here is the validated red-first-against-reality case.
run: python -m pytest qa/test_grid_paint_coherence.py -q -p no:xdist
- name: Paint-coverage coherence instrument (#1647 — per-cell open-vs-covered gate; deterministic core + VQA stub)
# The COMPLEMENT of the grid<->paint gate above: check_grid_paint_coherence asks "does a prop's paint
# sit on its own footprint?"; paint_coherence asks "is a WALKABLE cell visually open floor, or under a
# tall prop's painted silhouette?" (the crypt spawn-in-sarcophagus / shop-shelf defects). Deterministic
# per-cell classifier + tri-state gate + the VQA adjudicator exercised with an injected stub (no LLM /
# box / player). Shares this lane's Pillow+numpy stack; single-process.
run: python -m pytest qa/test_paint_coherence.py -q -p no:xdist
- name: Registration instrument cores (#1680 — per-room paint<->collision agreement; deterministic)
# The SHIP RULER (qa/registration_score.py): for each room, the screen-space agreement between the
# PAINTED-object mask (paint_coherence's per-cell coverage machinery) and the COLLISION set
# (walkmask + boxes-sidecar footprints, projected through the contract camera). Names the two
# navigation-truth defects per cell — invisible walls (painted open, collides) and walk-through
# furniture (painted blocked, walkable). Deterministic synthetic-plate + pure-math cores; no LLM /
# box / player. Shares this lane's Pillow+numpy stack; single-process.
run: python -m pytest qa/test_registration_score.py -q -p no:xdist
- name: Registration BASELINE over every manifest room (#1680 — the honest paint<->collision score)
# The instrument run over ALL plates_manifest rooms. NON-BLOCKING (continue-on-error) like the
# coherence-freshness lint below: the baseline is EXPECTED RED today (per #1680 the owner's '98%
# mismatch' — every existing room is below the 0.99 ship bar; that is the finding, not a
# regression). The uploaded summary + per-room heatmaps are the acceptance ruler for the #83
# 3D-first rebuild chain; a room only clears this once rebuilt.
continue-on-error: true
run: python qa/registration_score.py score-rooms --evidence-dir "$RUNNER_TEMP/registration" -o "$RUNNER_TEMP/registration/summary.json"
- name: Upload registration baseline (summary JSON + per-room heatmaps)
if: always()
uses: actions/upload-artifact@v7
with:
name: registration-baseline
path: ${{ runner.temp }}/registration
if-no-files-found: warn
- name: Room-manifest DERIVATION from greybox geometry (regeneratable + loop-closed on forest_road)
# Owner playtest #5: manifests are DERIVED from greybox geometry (single source of truth for
# footprint + occlusion + walkable) via tools/derive_room_manifest.py. Pins the derivation math,
# that forest_road.cells.json is regeneratable, and that the derived manifest is COHERENT against
# the greybox its geometry describes (the loop closed at the source).
run: python -m pytest qa/test_derive_room_manifest.py -q -p no:xdist
- name: Journey factual-VQA cores + panel defect-checklist regression (instruments B + C)
# Instrument B's path-gen/aggregation/verdict (stub scorer, no LLM/box) and amendment C's panel
# defect-checklist prompt regression — pinned so an edit to either can't silently drift.
run: python -m pytest qa/test_journey_eval.py qa/test_plate_loop.py -q -p no:xdist
- name: Journey click-sweep cores (#1523 — adversarial prop-click sweep target/traversal logic)
# manifest_from_surface / build_adversarial_targets / dfs_sweep / build_findings — pure, no
# engine/HTTP/box involved (mirrors the split above); the live HTTP driving is exercised
# on-box (see qa/journey_click_sweep.py's module docstring), deliberately not unit-tested here.
run: python -m pytest qa/test_journey_click_sweep.py -q -p no:xdist
- name: Coherence-report freshness lint (#1651 CI half — every plates_manifest-shipped room must
have a paint-coherence report whose plate/geometry/ortho still match the shipped state; a
missing report is a FAILURE, never a silent skip). Placed LAST in this job, and
continue-on-error, because it is EXPECTED RED today (see the PR body — 3 rooms have no report
yet, a real pre-existing coverage gap this lint newly surfaces). `paint-drift-gate` is not a
required branch-protection check, but continue-on-error still applies here — a permanently-red
non-required job for a documented pre-existing gap risks "crying wolf" and masking a genuinely
NEW regression among this job's other steps. The assertion itself stays exactly as strict.
continue-on-error: true
run: python qa/check_coherence_freshness.py
license-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: License / content gate
run: python3 scripts/license_check.py