Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 9s
Block integration-tester contamination artifacts / Block staging-trigger / invalid manifest contamination (pull_request) Successful in 9s
Concierge Creates Workspace Hermetic / Concierge Creates Workspace Hermetic (pull_request) Successful in 15s
CI / Python Lint & Test (pull_request) Successful in 16s
Harness Replays / detect-changes (pull_request) Successful in 17s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 11s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 7s
ci-required-drift / drift (pull_request) Successful in 28s
lint-bp-context-emit-match / lint-bp-context-emit-match (pull_request) Successful in 20s
E2E External Connect-Snippet + Leak-Lint / E2E External Connect-Snippet + Leak-Lint (pull_request) Successful in 24s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 24s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 14s
E2E Workspace Lifecycle (staginge2e) / E2E Workspace Lifecycle (compile+skip) (pull_request) Successful in 26s
Lint forbidden hand-written mcp__ tool-id literals / Scan for hand-written mcp__ tool-id literals (pull_request) Successful in 12s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 29s
lint-phantom-gate / lint-phantom-gate (pull_request) Successful in 18s
E2E API Smoke Test / detect-changes (pull_request) Successful in 39s
CI / Detect changes (pull_request) Successful in 41s
Lint publish-runner timeout-minutes / Lint publish-runner timeout-minutes (pull_request) Successful in 17s
lint-no-coe-on-required / lint-no-coe-on-required (pull_request) Successful in 22s
lint-required-workflows-docker-host-pinned / Lint docker-host pin on docker-touching workflows (pull_request) Successful in 16s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 10s
nodup-lint / no-duplication (pull_request) Successful in 15s
sdk-route-milestone-contract-drift / SDK route + milestone derive-gates (pull_request) Successful in 16s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Successful in 30s
sdk-pin-drift / SDK consumer pin vs molecule-ai-sdk main (pull_request) Successful in 17s
Lint workflow YAML (repository compatibility policy) / Lint workflow YAML for repository compatibility policy (pull_request) Successful in 19s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 30s
lint-setup-go-cache / lint-setup-go-cache (pull_request) Successful in 26s
Lint schedule budget / Zero-cron ratchet (pull_request) Successful in 30s
lint-staging-tenant-cd-gate-chain / lint-staging-tenant-cd-gate-chain (pull_request) Successful in 24s
E2E Ephemeral CP Happy Path / detect (pull_request) Successful in 1m8s
template-delivery-e2e / detect-changes (pull_request) Successful in 25s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 45s
lint-env-coupling-dismissal / lint-env-coupling-dismissal (pull_request) Successful in 1m17s
Self-Host Concierge Schedules E2E / Self-Host Concierge Schedules E2E (stub) (pull_request) Successful in 46s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 1m11s
lint-mask-pr-atomicity / lint-mask-pr-atomicity (pull_request) Successful in 1m19s
Ops Scripts Tests / Ops scripts (unittest) (pull_request) Successful in 55s
template-delivery-e2e / Template-asset delivery (fresh seo-agent — config+prompts via asset channel, seo-all via plugin reconcile) (pull_request) Successful in 5s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Failing after 1m15s
E2E Chat / E2E Chat (pull_request) Successful in 2m29s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Successful in 1m12s
Harness Replays / Harness Replays (pull_request) Successful in 2m33s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m29s
CI / Canvas (Next.js) (pull_request) Successful in 4m57s
CI / Canvas Deploy Status (pull_request) Successful in 3s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 6m15s
CI / Platform (Go) (pull_request) Successful in 6m24s
CI / all-required (pull_request) Successful in 5s
E2E Ephemeral CP Happy Path / E2E Ephemeral CP Happy Path (pull_request) Successful in 27m54s
Reverts d01cac1ce. Demonstrated red for review finding A, recorded:
run 632619 / job 933847, "Milestone derive-gate (HappyPathMilestones
vs E2E runner)":
--- SKIP: TestHappyPathMilestonesMatchRunner (0.00s)
milestone derive-gate: 0 executed, 1 skipped
::error::Milestone derive-gate SKIPPED 1 test(s). ... a skip here
means the package now executes NOWHERE while every gate reports green.
exitcode '1': failure
-> sdk-route-milestone-contract-drift (a REQUIRED context) = FAILURE
Three readings on that one run, and it is the combination that matters:
* the ROUTE derive-gate, left armed, reported "1 executed, 0 skipped"
and stayed green — the sibling step is a negative control, so the red
is the mutation and not the new plumbing;
* `CI / Platform (Go)` on the SAME commit was GREEN, reporting
"all-skip 1 (1 allowlisted)" — correctly, because the allowlist says
this package is covered elsewhere;
* so before this commit's parent, BOTH gates would have been green with
TestHappyPathMilestonesMatchRunner executing nowhere at all.
That is the whole of finding A: an exemption is only as good as the
assertion in the lane it points at, and that lane had none.
153 lines
7.8 KiB
YAML
153 lines
7.8 KiB
YAML
name: sdk-route-milestone-contract-drift
|
|
|
|
# SDK route + happy-path milestone derive-gates (RFC molecule-core#4428, Phase 3
|
|
# — PROMOTED to a hard, always-fire, merge-blocking gate). Closes #87 and #88.
|
|
#
|
|
# molecule-ai-sdk is the SSOT for the registry+A2A route lane
|
|
# (molcontracts.SDKRoutes) and the happy-path milestone set
|
|
# (molcontracts.HappyPathMilestones), both vendored into workspace-server via
|
|
# the gen/go binding. These two Go tests DERIVE the contract from the vendored
|
|
# SSOT and assert core still honours it:
|
|
#
|
|
# * TestSDKRoutesContract (internal/router) — every SDKRoutes entry is
|
|
# registered in router.go under the same method (#87).
|
|
# * TestHappyPathMilestonesMatchRunner (internal/e2emilestones) — the
|
|
# staging-full-saas runner's `required=` set equals the SSOT milestone
|
|
# ids (#88), locking them together so Phase 2's extension can't drift.
|
|
#
|
|
# ── Phase 3: HARD GATE (always-fire on every PR; NO paths filter) ──
|
|
# core `main` runs branch-protection required_contexts=['*'], which counts EVERY
|
|
# posted commit status. The two derive tests are DETERMINISTIC and were verified
|
|
# green on main before this promotion, so — unlike an advisory gate — a
|
|
# pull_request trigger is exactly what we want: the status posts on every PR and
|
|
# ['*'] makes it merge-blocking. This context is also listed explicitly in
|
|
# .gitea/required-contexts.txt for the presence guarantee (it must post; it can
|
|
# never be silently absent).
|
|
#
|
|
# NO `paths:` filter — .gitea/scripts/lint-required-no-paths.py (ARM A) forbids a
|
|
# paths filter on any required context: a path-skipped required check would go
|
|
# absent on unrelated PRs and wedge the merge queue under ['*']. The gate is
|
|
# cheap (two ~0.3s Go tests) so it runs on every PR unconditionally.
|
|
#
|
|
# NO schedule/cron: molecule-core enforces a zero-cron ratchet
|
|
# (.gitea/scripts/lint_schedule_budget.py). push:main remains as the post-merge
|
|
# drift signal; workflow_dispatch allows on-demand runs.
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: sdk-route-milestone-contract-drift-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# Phase 3 (RFC #4428 §7): required merge gate. Its context is
|
|
# `sdk-route-milestone-contract-drift / SDK route + milestone derive-gates` and
|
|
# is listed in .gitea/required-contexts.txt. Renaming this job changes the
|
|
# context string — keep it in lockstep with that file.
|
|
# bp-required: yes — merge-blocking under main's branch-protection
|
|
# status_check_contexts=['*'] (every posted context is required, #4363).
|
|
derive-gates:
|
|
name: SDK route + milestone derive-gates
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 8
|
|
defaults:
|
|
run:
|
|
working-directory: workspace-server
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
fetch-depth: 1
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
|
|
with:
|
|
go-version: stable
|
|
# cache:false — self-hosted runners bind-mount a persistent
|
|
# GOCACHE/GOMODCACHE; actions/cache corrupts it (see weekly-platform-go.yml).
|
|
cache: false
|
|
|
|
- name: Go mod download
|
|
run: go mod download
|
|
|
|
# MOLECULE_RUN_CONTRACT_DRIFT_GATES=1 un-skips the two gate tests, which
|
|
# SKIP by default (so ci.yml's `go test ./...` on PRs stays non-blocking).
|
|
# This workflow is the ONLY place they execute for real.
|
|
#
|
|
# ── WHY BOTH STEPS ASSERT EXECUTION ───────────────────────────────────
|
|
# Both were a bare `go test ... -run <regex> -v`, whose only verdict was
|
|
# go's exit code. Every way these tests can stop running exits 0:
|
|
#
|
|
# * MOLECULE_RUN_CONTRACT_DRIFT_GATES dropped, renamed or typo'd
|
|
# -> both tests hit their `if os.Getenv(...) == ""` guard and SKIP;
|
|
# * the -run regex stops matching -> "no tests to run", also exit 0.
|
|
#
|
|
# That mattered more than usual once ci.yml's no-tests-executed gate
|
|
# started allowing internal/e2emilestones to skip wholesale ON THE
|
|
# STRENGTH OF THIS LANE. The allowlist entry in
|
|
# .gitea/tests-may-skip-wholesale.txt says the package "EXECUTES in
|
|
# sdk-route-milestone-contract-drift.yml" -- so if this lane could not
|
|
# tell whether it ran anything, a dropped env var would have left
|
|
# TestHappyPathMilestonesMatchRunner executing NOWHERE with both gates
|
|
# green. The exemption would have re-created the exact hole the gate
|
|
# was written to close, inside the escape hatch meant to be safe.
|
|
#
|
|
# An exemption is only as good as the lane it points at, so the lane is
|
|
# now the thing that proves it. Same idiom as
|
|
# concierge-creates-workspace-hermetic.yml (require >0 "--- PASS:") plus
|
|
# the stronger skip hard-fail from handlers-postgres-integration.yml:
|
|
# in THIS lane a skip is not a neutral outcome, it is the failure.
|
|
- name: Route derive-gate (SDKRoutes vs router.go) — #87
|
|
env:
|
|
MOLECULE_RUN_CONTRACT_DRIFT_GATES: "1"
|
|
run: |
|
|
set -uo pipefail
|
|
LOG=$(mktemp)
|
|
rc=0
|
|
go test ./internal/router/ -run 'SDKRoutes' -count=1 -v 2>&1 | tee "$LOG" || rc=$?
|
|
npass=$(grep -c -E '^[[:space:]]*--- PASS: ' "$LOG" || true)
|
|
nskip=$(grep -c -E '^[[:space:]]*--- SKIP: ' "$LOG" || true)
|
|
echo "route derive-gate: ${npass:-0} executed, ${nskip:-0} skipped"
|
|
if [ "$rc" -ne 0 ]; then
|
|
exit "$rc"
|
|
fi
|
|
if [ "${nskip:-0}" -ne 0 ]; then
|
|
echo "::error::Route derive-gate SKIPPED ${nskip} test(s). MOLECULE_RUN_CONTRACT_DRIFT_GATES did not reach the test, so the SDKRoutes contract was NOT checked — and a skip is indistinguishable from a pass in the job summary. Confirm the env var above still matches contractDriftGateEnv in internal/router."
|
|
exit 1
|
|
fi
|
|
if [ "${npass:-0}" -eq 0 ]; then
|
|
echo "::error::Route derive-gate executed ZERO tests (0 '--- PASS:' lines) but go test still exited 0 — 'go test -run' exits 0 when the regex matches nothing. Check that 'SDKRoutes' still selects TestSDKRoutesContract in workspace-server/internal/router/."
|
|
exit 1
|
|
fi
|
|
|
|
- name: Milestone derive-gate (HappyPathMilestones vs E2E runner) — #88
|
|
env:
|
|
MOLECULE_RUN_CONTRACT_DRIFT_GATES: "1"
|
|
run: |
|
|
set -uo pipefail
|
|
LOG=$(mktemp)
|
|
rc=0
|
|
go test ./internal/e2emilestones/ -run 'HappyPathMilestones' -count=1 -v 2>&1 | tee "$LOG" || rc=$?
|
|
npass=$(grep -c -E '^[[:space:]]*--- PASS: ' "$LOG" || true)
|
|
nskip=$(grep -c -E '^[[:space:]]*--- SKIP: ' "$LOG" || true)
|
|
echo "milestone derive-gate: ${npass:-0} executed, ${nskip:-0} skipped"
|
|
if [ "$rc" -ne 0 ]; then
|
|
exit "$rc"
|
|
fi
|
|
if [ "${nskip:-0}" -ne 0 ]; then
|
|
echo "::error::Milestone derive-gate SKIPPED ${nskip} test(s). MOLECULE_RUN_CONTRACT_DRIFT_GATES did not reach the test, so TestHappyPathMilestonesMatchRunner did NOT run. This lane is the sole justification for internal/e2emilestones' entry in .gitea/tests-may-skip-wholesale.txt — a skip here means the package now executes NOWHERE while every gate reports green. Confirm the env var above still matches contractDriftGateEnv in internal/e2emilestones."
|
|
exit 1
|
|
fi
|
|
if [ "${npass:-0}" -eq 0 ]; then
|
|
echo "::error::Milestone derive-gate executed ZERO tests (0 '--- PASS:' lines) but go test still exited 0. Check that 'HappyPathMilestones' still selects TestHappyPathMilestonesMatchRunner in workspace-server/internal/e2emilestones/."
|
|
exit 1
|
|
fi
|