Files
molecule-core/.gitea/workflows/e2e-workspace-lifecycle.yml
T
hongming-personal 44688ebabc
sop-checklist / all-items-acked (pull_request) acked: 0/9 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +6 — body-unfilled: comprehensive-testing, local-postgres-e2
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request_target) Successful in 4s
Block integration-tester contamination artifacts / Block staging-trigger / invalid manifest contamination (pull_request) Successful in 8s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 9s
CI / Python Lint & Test (pull_request) Successful in 7s
E2E Peer Visibility (literal MCP list_peers) / detect-changes (pull_request) Successful in 7s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 8s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 8s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 9s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 7s
Lint forbidden hand-written mcp__ tool-id literals / Scan for hand-written mcp__ tool-id literals (pull_request) Successful in 6s
E2E Workspace Lifecycle (staginge2e) / E2E Workspace Lifecycle (compile+skip) (pull_request) Successful in 20s
lint-bp-context-emit-match / lint-bp-context-emit-match (pull_request) Successful in 20s
lint-required-workflows-docker-host-pinned / Lint docker-host pin on docker-touching workflows (pull_request) Successful in 9s
Lint publish-runner timeout-minutes / Lint publish-runner timeout-minutes (pull_request) Successful in 18s
lint-no-coe-on-required / lint-no-coe-on-required (pull_request) Successful in 19s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 30s
E2E API Smoke Test / detect-changes (pull_request) Successful in 39s
CI / Detect changes (pull_request) Successful in 39s
E2E Chat / detect-changes (pull_request) Successful in 37s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 37s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 20s
Lint schedule budget / Zero-cron ratchet (pull_request) Successful in 18s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 8s
lint-setup-go-cache / lint-setup-go-cache (pull_request) Successful in 14s
lint-staging-tenant-cd-gate-chain / lint-staging-tenant-cd-gate-chain (pull_request) Successful in 16s
gate-check-v3 / gate-check (pull_request_target) Successful in 11s
sop-checklist / review-refire (pull_request_target) Has been skipped
template-delivery-e2e / detect-changes (pull_request) Successful in 23s
PR Diff Guard / PR diff guard (pull_request) Successful in 24s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Successful in 27s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 2s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
CI / Platform (Go) (pull_request) Successful in 3s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 48s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 3s
CI / Canvas (Next.js) (pull_request) Successful in 3s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 5s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 4s
CI / Canvas Deploy Status (pull_request) Successful in 2s
E2E Chat / E2E Chat (pull_request) Successful in 4s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Successful in 49s
template-delivery-e2e / Template-asset delivery (fresh seo-agent — config+prompts via asset channel, seo-all via plugin reconcile) (pull_request) Successful in 2s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 33s
CI / all-required (pull_request) Successful in 4s
Ops Scripts Tests / Ops scripts (unittest) (pull_request) Successful in 32s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Successful in 32s
qa-review / approved (pull_request_target) Approved via pull_request_review trigger
gitea-merge-queue / queue (pull_request_review) Successful in 7s
reserved-path-review / reserved-path-review (pull_request_target) Approved via pull_request_review trigger
security-review / approved (pull_request_target) Approved via pull_request_review trigger
qa-review / approved (pull_request_review) Successful in 7s
reserved-path-review / reserved-path-review (pull_request_review) Successful in 8s
security-review / approved (pull_request_review) Successful in 8s
audit-force-merge / audit (pull_request_target) Successful in 13s
ci-required-drift / drift (pull_request) Waiting to run
fix(ci): zero standing non-success statuses on main HEAD + sop-ack advisory relax behind MERGE_REQUIRE_SOP_ACK
Main-HEAD status coherence, three root fixes (no fake-green, no hacks):

1. e2e-workspace-lifecycle: the real staging job lived behind
   'if: workflow_dispatch || schedule' inside a push/PR-triggered workflow,
   so Gitea auto-published a permanent SKIPPED commit status for it on
   every main push and every PR head. SPLIT: the job moves verbatim into
   the new dispatch-only e2e-workspace-lifecycle-staging.yml (no push/PR
   trigger -> no status at all on push; a real dispatch posts the REAL
   verdict). The per-PR compile+LOUD-skip gate stays where it was,
   unchanged. Zero-cron ratchet honored (no schedule; operator-config
   cron-bot dispatch is the cadence path, same as status-reaper).

2. staging-tenant-cd rollback-pin: the 'if: failure() && advance-pin
   success' job guard likewise posted SKIPPED on every green main push.
   The job is now 'if: always()' with an explicit in-job branch on the
   real upstream results: green roll -> loud no-op success; gate red /
   pin never advanced -> loud no-op success; roll FAILED after a pin
   advance -> the same revert as before (exact parity, incl. the
   cancelled-roll no-revert and empty-old_image cases). Gate chain
   untouched: lint_staging_tenant_cd_gate_chain passes (edges + no-coe).

3. sop-checklist OPERATOR RELAX (standing directive until the agent team
   exists): ack enforcement now keys on the org Actions variable
   MERGE_REQUIRE_SOP_ACK (already set org-wide, 'false'). Anything but
   'true' => the gate still runs the FULL ack evaluation (nothing is
   short-circuited) but POSTs success with the exact description prefix
   'ack advisory until agent-team' + the real would-be verdict, so the
   context reports without blocking. 'true' restores enforcement with no
   code change. Implemented as the pure, unit-tested
   apply_ack_enforcement_policy() (new TestApplyAckEnforcementPolicy in
   test_sop_checklist.py, runs in the test-ops-scripts pytest lane); also
   clears the stale-red 'sop-checklist / all-items-acked (pull_request)'
   sibling on PR heads on the next evaluation.

Verified locally: yaml.safe_load OK on all 4 touched workflows;
lint_staging_tenant_cd_gate_chain.py OK; py_compile OK on both python
files; policy function truth-table exercised.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 13:57:05 -07:00

103 lines
5.3 KiB
YAML

name: E2E Workspace Lifecycle (staginge2e)
# core#2332 P1.10 — close the workspace-lifecycle coverage gap.
#
# soft-restart / pause / resume / hibernate were only unit-tested (httptest in
# workspace-server/internal/handlers/*_test.go) and never proven against a real
# container. This drives the Go staginge2e suite
# (workspace-server/internal/staginge2e/workspace_lifecycle_test.go) which
# provisions a REAL throwaway staging tenant, exercises each lifecycle endpoint,
# and asserts OBSERVABLE container state (status transitions + serve reachability
# + url-cleared-on-stop) — not just HTTP 200.
#
# ADVISORY-BY-INFRA. It needs a live staging tenant (a cold molecules-server / local-docker
# provision, was the ~30+ min cold EC2 path), so the real run is workflow_dispatch
# only — NOT per-PR and NOT a
# required check. Promotion to a required branch-protection context is a separate
# CTO decision (mirrors the cp internal/staginge2e suite, cp#386, and the
# peer-visibility flip-to-required pattern, molecule-core#1296).
#
# SPLIT (main-status coherence): the real staging job now lives in its own
# dispatch-only workflow, e2e-workspace-lifecycle-staging.yml. When it was a
# job HERE behind `if: workflow_dispatch || schedule`, every push/PR run of
# this workflow auto-published a `skipped` commit status for it
# (`E2E Workspace Lifecycle (staginge2e) / E2E Workspace Lifecycle (staging)
# (push)`) on every main HEAD — permanent skip-noise on the default branch's
# status surface. A dispatch-only workflow posts NO status on push/PR at all,
# and posts its REAL verdict when actually dispatched. Same trust model, same
# suite, zero behavior change for the compile gate below.
#
# HONEST GATE — NO continue-on-error mask (feedback_fix_root_not_symptom). The
# PR job validates that the suite COMPILES under -tags=staging_e2e and SKIPs LOUD
# without creds (the suite's contract) — a broken test file fails at PR time. The
# real assertion runs on dispatch/cron with staging creds.
#
# Gitea 1.22.6 / act_runner notes honored: no cross-repo uses (mirrored
# actions/checkout SHA), per-SHA concurrency, pinned GITHUB_SERVER_URL.
on:
# group A: staging added alongside main so the compile+skip PR gate runs on both protected
# branches. The real lifecycle-staging job (e2e-workspace-lifecycle-staging.yml) provisions on
# molecules-server (local-docker) via E2E_PROVIDER and stays dispatch-only — a 30-60 min cold
# provision is not a per-PR cost (the cheap per-PR coverage is the compile+skip gate here +
# local-provision-e2e).
push:
branches: [main, staging]
paths:
- 'workspace-server/internal/handlers/workspace_restart.go'
- 'workspace-server/internal/handlers/workspace_crud.go'
- 'workspace-server/internal/staginge2e/**'
- '.gitea/workflows/e2e-workspace-lifecycle.yml'
pull_request:
branches: [main, staging]
paths:
- 'workspace-server/internal/handlers/workspace_restart.go'
- 'workspace-server/internal/handlers/workspace_crud.go'
- 'workspace-server/internal/staginge2e/**'
- '.gitea/workflows/e2e-workspace-lifecycle.yml'
workflow_dispatch:
concurrency:
# Per-SHA (feedback_concurrency_group_per_sha).
group: e2e-workspace-lifecycle-${{ github.event.pull_request.head.sha || github.sha }}
cancel-in-progress: false
env:
GITHUB_SERVER_URL: https://git.moleculesai.app
jobs:
# PR / compile gate: prove the staginge2e suite compiles under the build tag
# and skips LOUD without creds. Cheap, honest, non-required. This is NOT a
# fake-green mask of the real assertion — it fails if the test file stops
# compiling. bp-required: pending CTO decision (see header).
lifecycle-compile-skip:
name: E2E Workspace Lifecycle (compile+skip)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version: 'stable'
# cache:false — the self-hosted runner bind-mounts a persistent
# GOCACHE/GOMODCACHE (/var/cache/ci-go-{build,mod}); actions/cache is
# redundant and corrupts it by untarring over the bind mount ("File
# exists" -> "Failed to restore" -> partial cache -> linker/typecheck
# errors on heavy jobs, e.g. test -race link "too many errors" and
# go-arch-lint "without types"). Fleet sweep after the cp ci.yml find.
cache: false
cache-dependency-path: workspace-server/go.sum
- name: go vet (staging_e2e tag)
working-directory: workspace-server
run: go vet -tags staging_e2e ./internal/staginge2e/...
- name: Compile + skip-run (must SKIP LOUD without STAGING_E2E)
working-directory: workspace-server
run: |
# No STAGING_E2E / creds → the suite MUST skip (not pass-with-zero-
# assertions, not fail-open). `go test` exit 0 with a SKIP line is the
# contract. -run pins to the one test so this stays fast.
out=$(go test -tags staging_e2e ./internal/staginge2e/ -run TestWorkspaceLifecycle -count=1 -v 2>&1)
echo "$out"
echo "$out" | grep -q "SKIP: TestWorkspaceLifecycle_Staging" \
|| { echo "::error::expected a LOUD skip of TestWorkspaceLifecycle_Staging without creds"; exit 1; }