Files
molecule-ai-sdk/.gitea/workflows/contracts-codegen-drift.yml
T
hongming eab25a5d47
Test / skill-framework-cli (pull_request) Successful in 14s
Test / package-smoke (pull_request) Successful in 20s
contracts-codegen-drift / codegen-drift (pull_request) Successful in 9s
contracts-codegen-drift / contract-conformance (pull_request) Successful in 14s
contracts-codegen-drift / adapter-registry-conformance (pull_request) Successful in 15s
contracts-codegen-drift / validate (pull_request) Successful in 19s
contracts-codegen-drift / native-plugins-registry-conformance (pull_request) Successful in 13s
contracts-codegen-drift / plugin-state-conformance (pull_request) Successful in 14s
contracts-codegen-drift / workspace-routes-manifest-conformance (pull_request) Successful in 17s
contracts-codegen-drift / prompt-canonical-text-conformance (pull_request) Successful in 16s
Test / test (3.11) (pull_request) Successful in 48s
contracts-codegen-drift / env-contract-drift (pull_request) Successful in 11s
contracts-codegen-drift / platform-identity-gate-conformance (pull_request) Successful in 15s
contracts-codegen-drift / branding-conformance (pull_request) Successful in 14s
Test / test (3.12) (pull_request) Successful in 51s
contracts-codegen-drift / env-contract-conformance (pull_request) Successful in 16s
meta-ci-advisory / meta (advisory) (pull_request) Successful in 8s
llm-registry-ssot-drift / llm-registry-ssot-drift (pull_request) Successful in 12s
contracts-codegen-drift / org-template declares something (pull_request) Successful in 23s
contracts-codegen-drift / schedule accepts both cron spellings (pull_request) Successful in 20s
registry-ssot-drift / registry-ssot-drift (pull_request) Successful in 11s
contracts-codegen-drift / plugin-install-report-conformance (pull_request) Successful in 17s
Test / test (3.13) (pull_request) Successful in 1m4s
contracts-codegen-drift / go-parity (pull_request) Successful in 20s
llm-registry-ssot-drift / llm-registry-ssot-conformance (pull_request) Successful in 17s
Test / all-required (pull_request) Successful in 2s
contracts-codegen-drift / bindings-parity (pull_request) Successful in 26s
registry-ssot-drift / registry-ssot-conformance (pull_request) Successful in 15s
contracts-codegen-drift / all-required (pull_request) Successful in 3s
fix(contracts/env): re-derive read sites from origin/main; the first sweep was wrong
REPORTED DEFECT. The registry claimed molecule-controlplane never reads
MOLECULE_DECLARE_DEFAULT_NATIVE_PLUGINS ("zero occurrences") and filed it dark.
Both were false. CP reads it at internal/handlers/tenant_config.go:140 and
RELAYS it into the /tenants/config response every tenant consumes; production
has it enabled in Infisical /shared/controlplane. Corrected to two readers
(cp + tenant), cross_plane with defaults_agree RECOMPUTED, status live.

ROOT CAUSE, and it was systematic. The sweep grepped LOCAL WORKING TREES:
controlplane's was on a feature branch 222 commits behind main, the runtime's
was on a feature branch and being edited concurrently, and molecule-core's own
origin/main ref was stale. Re-deriving against authoritative origin/main found
the reported row was not alone:

  40  citations pointing at the wrong line
   4  readers missed entirely (MOLECULE_MODEL on cp + workspace,
      MOLECULE_AGENT_NAME and MOLECULE_WORKSPACE_TOKEN on workspace)
   1  declared flag that no longer exists on main at all
      (FLEET_IMAGE_RECONCILE_DRY_RUN — fleet_reconcile_wire.go now hardcodes
      Converger:nil + DryRun:true) — removed
   1  false "this plane never reads it" claim

THREE FALSE-NEGATIVE CLASSES IN THE GATE ITSELF, which is why re-deriving was
not enough. The extractor only saw string literals at the call site, so it was
blind to:

  * the name constant — `const declareDefaultNativePluginsEnv = "..."` then
    `os.Getenv(declareDefaultNativePluginsEnv)`; also the runtime's
    `KERNEL_FLAG_ENV`. Resolution is now repo-wide (binding and accessor
    routinely live in different files); an identifier bound to two names is
    reported as `ambiguous-const`, never guessed.
  * the package-qualified helper — `envx.Bool("NAME", false)`, which is
    molecule-core's STANDARD boolean gate helper. The env-ness is in the
    qualifier; matching only the callee ("Bool") missed it.
  * shell and Dockerfile — `if [ "$MEMORY_V2_CUTOVER" = "true" ]`. Restricted
    to shell-ish files so `${...}` in TS/Go does not flood the gate.

The first two are the idioms the BEST-engineered flags use, so a literal-only
extractor reported exactly the wrong repos clean — a guard covering nothing
while looking like it covered everything.

One false POSITIVE fixed too: `env["X"] = v` is a WRITE. CP assembles each
workspace's desired env that way, and counting it as a read would declare CP a
consumer of vars it only emits — the mirror image of missing a reader.

STRUCTURAL FIX, so this cannot recur silently:
  * tools/env-contract/extract.mjs — extraction factored out, shared by the
    gate and the verifier so the two can never disagree about what a read is.
    They did disagree: the verifier's literal-substring test called correct
    const-mediated citations dead.
  * tools/env-contract/verify-sites.mjs — re-verifies every consumer citation
    against origin/main through the API, follows Python imports for cross-file
    bindings, reports when a consumer's main has MOVED since verification, and
    exits 2 (never 0) without credentials. Operator tool, not a CI job: CI has
    no Gitea credential and a step that skipped without one would go green over
    an unverified registry, since a skipped context passes a wildcard branch
    protection.
  * provenance{} pins the origin/main commit per consumer; every reader carries
    site_verified_at; CI fails any consumer citation older than 180 days, which
    is only refreshable by running the verifier.
  * default_off_reason `enabled_by_config` + `enabled_in` — the state the
    registry could not previously express and therefore recorded backwards: the
    CODE default is off but production enables it, so the capability is LIVE.
    The schema now forbids such an entry from also being dark.

83/83 consumer citations verify against origin/main. 34 node self-tests (7 new,
each red first), 41 conformance tests. Re-proved the gate fails on a deliberate
violation using the const idiom it was previously blind to: exit 0 -> exit 1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 17:54:24 -07:00

520 lines
27 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Contracts codegen-drift + validate + parity gate (ONE merged gate over the
# whole contracts/ SSOT — supersedes cloudproviders-codegen-drift.yml).
#
# molecule-ai-sdk is the consolidated home of TWO contract IDL kinds that now
# share ONE contracts/ + gen/ + tools/:
# 1. the flat cloud-provider YAML SSOT (contracts/cloudproviders.yaml, #40)
# 2. the folded molecule-contracts JSON-Schema contracts (contracts/<domain>/
# *.contract.json + *.schema.json — mcp, plugin-manifest, workspace-template,
# org-template, catalog, provision-request, promote-request, workspace-comms)
#
# The invariant: the committed generated bindings under gen/{go,ts,python} are
# EXACTLY what tools/gen-*.mjs produce from contracts/ — never hand-edited, never
# stale — AND every JSON-Schema contract instance validates against its schema.
# Independent guards, all feeding a strict all-required aggregator:
#
# validate (python) check-jsonschema every contracts/<domain>/
# *.contract.json against its sibling *.schema.json (the
# JSON-Schema IDL kind; makes const/enum/required pins
# load-bearing — e.g. required_tool==provision_workspace).
# codegen-drift (node-only) re-run ALL generators (both IDL kinds) and
# fail on any diff in the GENERATED paths (scoped so the
# verbatim-moved llmwire/tools/shim files are never flagged).
# contract-conformance (python) jsonschema-validate the cloudproviders YAML +
# assert the semantic invariants (unique ids/aliases, one
# is_local, default resolves, {persist}{""}==migration-056).
# go-parity (go) build + vet + test gen/go incl. the folded
# molcontracts package, the moved-verbatim cloudprovider
# behavior oracle, llmwire pins, nodup-lint, dependency guard.
name: contracts-codegen-drift
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: contracts-codegen-drift-${{ github.ref }}
cancel-in-progress: true
jobs:
validate:
name: validate
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install check-jsonschema
run: pip install --no-cache-dir check-jsonschema
- name: Validate every contracts/<domain>/*.contract.json against its sibling schema
shell: bash
run: |
set -euo pipefail
shopt -s nullglob
contracts=(contracts/*/*.contract.json)
if [ ${#contracts[@]} -eq 0 ]; then
echo "::error::no contracts/*/*.contract.json instances found — nothing to validate (fail-closed)"
exit 1
fi
rc=0
for instance in "${contracts[@]}"; do
schema="${instance%.contract.json}.schema.json"
if [ ! -f "$schema" ]; then
echo "::error file=$instance::missing sibling schema $schema (fail-closed)"
rc=1
continue
fi
echo "validating $instance against $schema"
if ! check-jsonschema --schemafile "$schema" "$instance"; then
echo "::error file=$instance::failed schema validation against $schema"
rc=1
fi
done
if [ "$rc" -ne 0 ]; then
echo "::error::one or more contract instances failed schema validation"
exit 1
fi
echo "all contract instances validate against their schemas"
codegen-drift:
name: codegen-drift
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "20"
- name: Regenerate open RuntimeId schemas and bindings
run: node tools/gen-runtimes.mjs
- name: Regenerate NativePlugins bindings from the native-plugins registry
run: node tools/gen-native-plugins.mjs
- name: Regenerate Go bindings (cloudprovider + molcontracts) from the SSOT
run: node tools/gen-go.mjs
- name: Regenerate TypeScript bindings (cloudprovider + contract/comms/catalog)
run: node tools/gen-ts.mjs
- name: Regenerate Python bindings (cloudprovider + contract/comms/catalog)
run: node tools/gen-python.mjs
- name: Regenerate skill-framework bindings (EnterOS Skill Framework contracts)
run: node tools/gen-skill-framework.mjs
- name: Fail if committed gen/ differs from a fresh regeneration
shell: bash
run: |
set -euo pipefail
# Scope to the GENERATED paths only. The hand-authored, NON-generated
# files that also live under gen/ are intentionally out of scope here
# (the go-parity/build jobs compile/type-check them instead):
# gen/go/{llmwire,tools,doc.go,deps_test.go,go.mod,go.sum} + cloudprovider_test.go
# gen/ts/{index.ts,package.json,tsconfig.json}
# gen/python/{__init__.py,pyproject.toml}
if ! git diff --exit-code -- \
contracts/plugin-manifest/plugin-manifest.schema.json \
contracts/workspace-template/workspace-template.schema.json \
contracts/org-template/org-template.schema.json \
contracts/catalog/catalog-entry.schema.json \
contracts/catalog/catalog.schema.json \
contracts/catalog/publish-request.schema.json \
molecule_plugin/_runtime_ids.py \
gen/go/cloudprovider/cloudprovider.go \
gen/go/molcontracts \
gen/ts/cloudproviders.generated.ts \
gen/ts/contract_gen.ts gen/ts/workspace_comms_gen.ts gen/ts/catalog_gen.ts gen/ts/idle_prompt_gen.ts gen/ts/runtime_ids.generated.ts \
gen/ts/native_plugins.generated.ts \
gen/ts/skill_framework_gen.ts \
gen/ts/branding.generated.ts \
gen/python/cloudproviders.py \
gen/python/contract_gen.py gen/python/workspace_comms_gen.py gen/python/catalog_gen.py gen/python/idle_prompt_gen.py gen/python/runtime_ids_gen.py \
gen/python/native_plugins_gen.py \
gen/python/skill_framework_gen.py \
gen/python/branding_gen.py \
gen/python/plugin_install_report_gen.py; then
echo "::error::gen/ is out of date — committed generated bindings differ from a fresh"
echo "::error::run of the generators. gen/ is NEVER hand-edited. Regenerate locally with"
echo "::error::'node tools/gen-runtimes.mjs && node tools/gen-native-plugins.mjs && node tools/gen-go.mjs && node tools/gen-ts.mjs && node tools/gen-python.mjs && node tools/gen-skill-framework.mjs'"
echo "::error::and commit the result."
exit 1
fi
echo "gen/ is in sync with contracts/ (no drift, both IDL kinds)"
contract-conformance:
name: contract-conformance
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install validators
run: pip install pytest "jsonschema>=4.18" "pyyaml>=6.0"
- name: Validate cloudproviders SSOT schema + semantic invariants
# --noconftest keeps this job hermetic: it needs only the contract test,
# not the package's runtime deps (the repo's main `test` job exercises
# the package + conftest fixtures separately).
run: python -m pytest tests/test_cloudproviders_contract.py -q --noconftest
adapter-registry-conformance:
# ADR-004 §2: official-runtimes.registry.json is the SSOT for WHICH runtimes
# are natively supported; mcp-plugin-delivery.contract.json is SSOT for the
# MCP descriptor + gate tool. Their per-runtime native surfaces MUST stay
# RECONCILED (same runtime set, same native path/format/server-map key,
# same server-name/required-tool literals). This gate turns that prose
# invariant into a cross-file assertion — a one-sided edit reds here.
name: adapter-registry-conformance
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install validators
run: pip install --no-cache-dir pytest "jsonschema>=4.18"
- name: Reconcile the official runtime registry with the MCP delivery contract
run: python -m pytest tests/test_adapter_registry_contract.py -q --noconftest
native-plugins-registry-conformance:
# native-plugins.registry.json is the SSOT for the set of platform-delivered
# first-party plugins and their install policy. This gate validates the
# registry against its schema + the load-bearing invariants (key==name,
# pinned source, install in {default,concierge}, the scheduler/mgmt-MCP
# entries carry their expected policy) with negative controls.
name: native-plugins-registry-conformance
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install validators
run: pip install --no-cache-dir pytest "jsonschema>=4.18"
- name: Validate the native plugins registry against its schema + invariants
run: python -m pytest tests/test_native_plugins_registry_contract.py -q --noconftest
plugin-state-conformance:
# contracts/plugin-state/plugin-state.contract.json is the delivery SSOT for
# what a plugin-manifest `contributes.state` declaration produces: the
# reserved daemon env vars (MOLECULE_PLUGIN_STATE_DIR /
# MOLECULE_PLUGIN_STATE_DURABLE), the per-plugin directory template, the
# provisioner-declared durability signal, and the degradation policy.
# THREE consumers must agree on those literals (controlplane provisioner Go,
# molecule_runtime injector Python, plugin authors) — molecule-ai-workspace-
# runtime#360 produced three "fixed in one place, not the other" regressions
# in a single day, so each literal is const-pinned and asserted here. The
# generic `validate` glob proves the instance validates; this job proves the
# load-bearing invariants (degradation never fail-closed, identity never
# plugin-supplied, migration never deleting, root outside the volumes the
# teardown path destroys) with negative controls.
name: plugin-state-conformance
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install validators
run: pip install --no-cache-dir pytest "jsonschema>=4.18"
- name: Validate the plugin-state contract + its declaration surface
run: python -m pytest tests/test_plugin_state_contract.py -q --noconftest
workspace-routes-manifest-conformance:
# contracts/workspace-comms/routes.manifest.json is the SDK-owned SSOT for the
# method+path of the registry + A2A endpoint lane (RFC molecule-core#4428,
# Phase 0). It is NOT a *.contract.json instance, so the generic validate glob
# does not cover it — this dedicated gate validates it against its schema and
# asserts the load-bearing invariants (unique ids, the exact registry+a2a lane,
# both contract-lane headers) with negative controls. Mirrors the
# native-plugins-registry-conformance job.
name: workspace-routes-manifest-conformance
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install validators
run: pip install --no-cache-dir pytest "jsonschema>=4.18"
- name: Validate the workspace routes manifest against its schema + invariants
run: python -m pytest tests/test_workspace_routes_manifest_contract.py -q --noconftest
prompt-canonical-text-conformance:
# TestSSOT: the prompt contract's embedded canonical_text (BASE_PLATFORM_PROMPT
# + ORCHESTRATOR_ONLY_GUARDRAIL) MUST stay byte-identical to the runtime
# producer (molecule_runtime.prompt). Offline leg validates the contract vs
# the vendored SSOT mirror; a runtime checkout (MOLECULE_RUNTIME_SRC) adds a
# live cross-repo leg. A silent drift un-gags a concierge / changes the
# platform identity frame.
name: prompt-canonical-text-conformance
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install validators
run: pip install --no-cache-dir pytest
- name: Prompt contract canonical text == runtime SSOT (byte-identical)
run: python -m pytest tests/test_prompt_canonical_text_contract.py -q --noconftest
platform-identity-gate-conformance:
# The wire-contract fail-closed enforcement schema (platform-identity-gate)
# run against its golden decision-table cases: every `pass` case validates,
# every `fail` case is rejected (anti-vacuous proof of the divergence-#5
# bypass class — online-claimed-without-loaded-proof).
name: platform-identity-gate-conformance
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install validators
run: pip install --no-cache-dir pytest "jsonschema>=4.18"
- name: Run the platform-identity fail-closed gate against golden cases
run: python -m pytest tests/test_platform_identity_gate_conformance.py -q --noconftest
branding-conformance:
# contracts/branding/branding.contract.json is the machine-readable mirror
# source for the branding identity SSOT (the hand-written Go authority is
# gen/go/branding/branding.go; its branding_contract_parity_test.go pins
# Go == the instance in the go-parity job). This gate schema-validates the
# instance, asserts the B1 exact-value pins (Tier-1 flipped to Enter OS,
# Tier-2 held at legacy), the cross-SSOT agreement with cloudproviders.yaml
# + registry.yaml, and parity between the generated Python mirror and the
# instance. The TS leg runs in bindings-parity (branding.test.cjs).
name: branding-conformance
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install validators
run: pip install --no-cache-dir pytest "jsonschema>=4.18" "pyyaml>=6.0"
- name: Validate the branding SSOT schema + pins + cross-SSOT + mirror parity
run: python -m pytest tests/test_branding_contract.py -q --noconftest
env-contract-conformance:
# contracts/env/env.registry.json is the SSOT for every governed environment
# variable on the platform: owning plane, kind, what happens when nobody sets
# it, and — for a gate that legitimately defaults off — a MACHINE-CHECKABLE
# precondition instead of a prose comment. It generalises the per-module
# x-enteros.env seam this repo already ships (packages/core/src/ctx.ts
# createCtx + packages/mcp-server/src/env-check.ts checkModuleEnv + env_doctor)
# from one module's process to the whole platform.
#
# env.registry.json is NOT a *.contract.json instance, so the generic
# `validate` glob does not cover it — this dedicated gate validates it against
# its schema and asserts the invariants a schema cannot express, each with a
# NEGATIVE CONTROL:
# * cross_plane.defaults_agree is RECOMPUTED from readers[].default_on, so an
# entry cannot assert an agreement it does not have (MOLECULE_MAILBOX_KERNEL
# genuinely disagrees across cp and workspace).
# * a `gate_on` precondition must resolve to a DECLARED gate — the defect
# class that let a rollout note name DELEGATION_RESULT_INBOX_PUSH, a flag
# appearing nowhere in the repo that named it.
# * a `contract_pin` precondition is resolved for real through its JSON
# Pointer, so MOLECULE_MAILBOX_KERNEL's declaration reds the day
# contracts/idle-prompt stops pinning kernel_gated_writes const true.
# Mirrors the native-plugins-registry-conformance job.
name: env-contract-conformance
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install validators
run: pip install --no-cache-dir pytest "jsonschema>=4.18"
- name: Validate the env registry against its schema + invariants
run: python -m pytest tests/test_env_registry_contract.py -q --noconftest
env-contract-drift:
# THE DRIFT GATE. Fails when this repo reads a governed environment variable
# that contracts/env/env.registry.json does not declare, or when the registry
# cites a read site that no longer exists. molecule-ai-sdk is at
# drift_gate=enforcing so the lint is proven to fail on a real violation —
# this codebase has several guards that report success while covering nothing,
# and a lint that has never failed is not a lint. The self-tests build a
# throwaway repo per violation class and assert the exit code AND the message;
# the lint's own integration leg then runs it against this repo for real.
#
# Node stdlib only, no npm install — the same posture as skill-framework-cli,
# and what makes a consumer able to adopt this with one vendored file.
#
# node-version 22, NOT 20: `node --test <glob>` only accepts a glob pattern
# from Node 21 onward. On 20 the quoted pattern is taken as a literal path
# and the step dies with "Could not find '.../tools/env-contract/*.test.mjs'".
# skill-framework-cli already pins 22 for the same invocation; the other
# node jobs here pin 20 because they only run generators, which take paths.
name: env-contract-drift
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "22"
- name: Self-test the drift gate (stdlib node --test)
run: node --test "tools/env-contract/*.test.mjs"
- name: Fail on any undeclared governed env read in this repo
run: node tools/env-contract/lint.mjs --repo molecule-ai-sdk
- name: env_doctor (static) — declared surface, darkness and preconditions
run: node tools/env-contract/lint.mjs --report
# Cross-repo citations are NOT verified here: this job has no Gitea
# credential, and a step that skipped without one would go green over an
# unverified registry (a `skipped` context passes a wildcard branch
# protection). tools/env-contract/verify-sites.mjs is the operator tool;
# what CI enforces is the site_verified_at freshness ceiling asserted in
# tests/test_env_registry_contract.py.
org-template-declaration:
name: org-template declares something
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install validators
run: pip install --no-cache-dir pytest "jsonschema>=4.18"
# The NEGATIVE half of the org-template contract. `anyOf` tests key
# PRESENCE, so `workspaces: []` satisfied it and an org template declaring
# nothing validated. A stale vendored validator used to catch that by
# testing truthiness; deleting it (org-template-molecule-dev#21) removed the
# only check that saw it. molecule-ci#106.
- name: An org template must declare something (workspaces:[] is not a declaration)
run: python -m pytest tests/test_org_template_empty_declaration.py -q --noconftest
schedule-cron-spellings:
name: schedule accepts both cron spellings
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install validators
run: pip install --no-cache-dir pytest "jsonschema>=4.18"
# The schema modelled only core's `cron_expr` and so REJECTED the shipped
# platform-agent template, which authors the runtime-native `cron` for the
# verbatim concierge-graft path. sdk#178.
- name: A schedule may spell cron either way — but not both
run: python -m pytest tests/test_schedule_cron_spellings.py -q --noconftest
go-parity:
name: go-parity
runs-on: ubuntu-latest
timeout-minutes: 10
env:
GOTOOLCHAIN: local
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version-file: gen/go/go.mod
cache: false
- name: go mod verify
working-directory: gen/go
run: go mod verify
- name: build (cloudprovider + llmwire + molcontracts + nodup-lint)
working-directory: gen/go
run: go build ./...
- name: vet
working-directory: gen/go
run: go vet ./...
- name: test (parity oracle + llmwire + nodup-lint + molcontracts)
working-directory: gen/go
run: go test ./...
- name: dependency guard (SDK depends on neither consumer)
working-directory: gen/go
run: go test -run TestNoConsumerDependency -v .
bindings-parity:
name: bindings-parity
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "20"
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Build and test TypeScript bindings
working-directory: gen/ts
run: npm ci && npm test
- name: Build Python bindings
run: pip install --no-cache-dir build && python -m build gen/python
plugin-install-report-conformance:
# contracts/plugin-install-report/ is the SSOT for how the runtime tells core
# what happened when it installed a workspace's declared plugins. Three of its
# values are the whole reason it exists, and each is ONE boolean flip away from
# re-creating the outage it addresses:
# concierge_gated:false — boot-step telemetry IS gated on kind=platform, which
# is why a fleet-wide boot-install failure was invisible for every
# kind=workspace workspace in prod on 2026-07-30: the only boxes core could
# see were the only boxes that were fine.
# durable:true — POST /boot-event is BroadcastOnly ("no structure_events row"),
# so it answers nothing for an operator asking later.
# outcome_rule — `installed` lists sources STAGED, not live; installed=[6] with
# swapped=false means nothing is live, and that state was indistinguishable
# from success in every signal core had.
# The generic `validate` glob proves the instance validates; this job proves
# those invariants, that the wire fields still mirror InstallReport 1:1, and that
# the Go + Python bindings were generated AT ALL — with negative controls.
name: plugin-install-report-conformance
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install validators
run: pip install --no-cache-dir pytest "jsonschema>=4.18"
- name: Validate the plugin-install-report contract + its generated bindings
run: python -m pytest tests/test_plugin_install_report_contract.py -q --noconftest
all-required:
name: all-required
needs: [validate, codegen-drift, contract-conformance, adapter-registry-conformance, native-plugins-registry-conformance, plugin-state-conformance, plugin-install-report-conformance, workspace-routes-manifest-conformance, prompt-canonical-text-conformance, platform-identity-gate-conformance, branding-conformance, env-contract-conformance, env-contract-drift, org-template-declaration, schedule-cron-spellings, go-parity, bindings-parity]
if: ${{ always() }}
runs-on: ubuntu-latest
steps:
- name: Assert every required dependency succeeded
run: |
set -euo pipefail
results='${{ toJSON(needs) }}'
echo "$results"
echo "$results" | python3 -c '
import json, sys
ns = json.load(sys.stdin)
# Strict: every required dependency MUST be "success". A skipped or
# cancelled required gate must RED this aggregator, not pass it (an
# all-required gate can never treat non-success as acceptable, or the
# SSOT invariant it guards is unenforced).
bad = [(k, v.get("result")) for k, v in ns.items()
if v.get("result") != "success"]
if bad:
print("FAIL: jobs not green:", file=sys.stderr)
for k, r in bad:
print(f" - {k}: {r}", file=sys.stderr)
sys.exit(1)
print(f"OK: all {len(ns)} required jobs succeeded")
'