Skip to content

ci: make check workflows thin wrappers over scripts/ci-local.sh - #396

Merged
dmealing merged 4 commits into
mainfrom
fm/mo-workflow-thin-wrappers
Oct 3, 2026
Merged

dmealing merged 4 commits into
mainfrom
fm/mo-workflow-thin-wrappers

Conversation

@dmealing

@dmealing dmealing commented Oct 3, 2026

Copy link
Copy Markdown
Member

Intent

Rewire now: turn metaobjects' three native check workflows (.github/workflows/hygiene.yml, conformance.yml, integration-tests.yml) into thin wrappers that call scripts/ci-local.sh, so the script is the single definition of those checks.
Background: the workflows still carry their own native step bodies, duplicating what scripts/ci-local.sh runs, so the two definitions can drift. local-ci.yml already uses the thin-wrapper inversion and is the model to follow. GitHub Actions is currently disabled on this repository, so the workflows do not run today.

What Changed

  • Workflows inverted to thin wrappers. hygiene.yml, conformance.yml and integration-tests.yml no longer carry native step bodies: each job checks out, installs the toolchain its lane needs, and calls scripts/ci-local.sh with a lane selector (--only leak-scan; --only gates|ts-fast|csharp|java-fast|python --no-integration plus --only java-slow with MO_CI_JACOCO=1; --only <lane> --integration-only), so the script is the single definition of the checks and a workflow run cannot drift from a local one. Conformance's five separate jobs (fixture-lint, typecheck, completeness-gate, doc-template-drift, embedded-library-drift) collapse into one gates job plus a lane matrix; Kotlin folds into the java-fast lane; integration-tests drops its standalone migrate-ts-pg job in favor of the ts-slow lane on the shared Postgres sidecar (which now also runs the runtime-ts real-PG matrix, with MIGRATE_TS_PG_EXPECT/RUNTIME_TS_PG_EXPECT sentinels armed).
  • scripts/ci-local.sh gains the selection primitives the wrappers need: --no-integration and --integration-only flags (mutually exclusive with each other and --quick), a standalone leak-scan --only section, MO_CI_LEAK_BASE to point the leak scan at the PR's base branch, MO_CI_JACOCO=1 to keep JaCoCo on for the hosted nightly reactor, and a repeat ApiDocsCrossPort pass in the C# lane after Cli.Tests builds the CLI (the gate is order-dependent, not redundant). Workflows also add permissions: contents: read and persist-credentials: false.
  • Docs refreshed to the thin-wrapper model, replacing the stale "GitHub Actions is disabled" prose in AGENTS.md, CONTRIBUTING.md, .no-mistakes.yaml, .githooks/pre-push, both fixture READMEs, and two conformance-test comments (C# ApiDocsCrossPortConformanceTests, Java RegistryManifestConformanceTest) that pointed at conformance.yml for where gates are wired.

Risk Assessment

✅ Low: Every lane selection was traced and empirically dry-run against the old native workflow bodies — coverage is preserved or a superset, all pre-existing selections are byte-identical to base, the fix-round prose matches verified reality, and the only residual is a harmless comment imprecision.

Testing

Drove the change's real surface end-to-end: parsed all three rewritten workflows and asserted 48 semantic properties (every executed step invokes ci-local.sh, no native check bodies remain, lanes/env reach real consumers, the round-1-declined python sidecar was NOT added), then ran every one of the 11 wrapper command invocations against the script itself — list-only selection proved --no-integration drops the docker half per lane and --integration-only keeps only it — and executed the cheap lanes for real (hygiene's leak-scan command and conformance's 32-gate gates job, both PASSED). Adversarially confirmed wrapper/script drift fails loudly (all malformed invocations exit 2 with named messages) and that a staged leak makes the scan exit 1 with an actionable message. Verified the corrected prose matches reality (Actions enabled=true via gh api) and that the review-fix commit touched only CONTRIBUTING.md and .no-mistakes.yaml. The full flagless regression run and a --quick --no-integration run were still executing (26 consecutive steps green, zero failures, inside the Stryker mutation gate) when this phase reported, so the csharp/java/reactor/python/docker-integration suites are recorded untested rather than passed. No LLM API spend: all work was local compute.

  • Live validation: ✅ go - 8 of 10 scenarios driven live against the product
Scenario Result Live Evidence
hygiene.yml is a thin wrapper: its only run step calls scripts/ci-local.sh --only leak-scan --strict-toolchains with MO_CI_LEAK_BASE at the PR base, and the job id 'leak-scan' (branch-protection statu… ✅ pass live semantic-wrapper-assertions.txt + real run 'MO_CI_LEAK_BASE=origin/main scripts/ci-local.sh --only leak-scan --strict-toolchains' → LOCAL CI PASSED (exit 0)
conformance.yml is a thin wrapper: jobs gates/conformance/java-reactor each carry one ci-local.sh run step with lane selector and --strict-toolchains, no native bun/mvn/dotnet bodies remain, JVM lanes… ✅ pass live semantic-wrapper-assertions.txt (48/48) + wrapper-list-only-drives.txt (all conformance invocations exit 0)
conformance.yml's gates job runs for real: all 32 offline gates pass under --strict-toolchains, including fixture-lint, doc-template drift and embedded-library drift (the three jobs the old workflow c… ✅ pass live gates-lane-real-run.log → LOCAL CI PASSED
integration-tests.yml is a thin wrapper: 4-lane matrix (ts-slow/csharp/java-slow/python) calls ci-local.sh --integration-only, job-level postgres sidecar with METAOBJECTS_TEST_PG_URL, both sentinel en… ✅ pass live semantic-wrapper-assertions.txt + list-only: ts-slow --integration-only lists exactly ts build (for integration), sidecar, migrate-ts real-PG suite, runtime-ts PG matrix, integration-tests (ts); all f…
Wrapper/script drift fails loudly, never silently: unknown lanes, missing --only value, unknown flags, and the --integration-only combos with --no-integration or --quick all exit 2 with named messages ✅ pass live flag-contract-adversarial.txt (7/7 cases as expected)
The leak gate actually fails on a leak: an added line with an absolute home path makes the scanner exit 1 naming file and line (driven via the scanner's staged-diff mode; committing the leak was block… ✅ pass live leak-scan-failure-mode.txt → exit 1 with 'CONTRIBUTING.md Debug notes: my build cache lives at ~/secret-build-cache.'; worktree restored clean
Round-1 fix: CONTRIBUTING.md and .no-mistakes.yaml say the workflows run on GitHub as thin wrappers over scripts/ci-local.sh and the script stays the pre-PR gate; no stale 'Actions is disabled' prose… ✅ pass live grep of touched prose (only the accurate 'When GitHub Actions is off' conditional remains) + gh api actions/permissions → enabled=true + git show --stat 1cd896a (CONTRIBUTING.md, .no-mistakes.yaml o…
Round-1 declined item NOT implemented: the conformance matrix carries no job-level postgres sidecar and the python lane stays as reviewed (no services block on any conformance.yml job) ✅ pass live semantic-wrapper-assertions.txt → 'conformance: NO services/sidecar on any job' PASS
Full regression: flagless scripts/ci-local.sh run-everything (all-port conformance, java reactor, mutation gate, 5-port docker integration) ⏸️ untested no The prior payload did not establish a live result: the flagless run was launched and was green through 26 steps, but it was still executing the Stryker completeness-gate when the phase had to report a…
conformance.yml's java-reactor job verbatim (MO_CI_JACOCO=1 reactor with JaCoCo ON) driven live ⏸️ untested no Queued behind the full regression run to avoid two concurrent maven reactors; the flagless run in progress exercises the default jacoco-skipped path, so the MO_CI_JACOCO toggle was verified only by li…
Evidence: Semantic thin-wrapper assertions (48/48 pass)

ALL SEMANTIC ASSERTIONS PASS — workflows parsed and asserted: single ci-local.sh run step per job, no native bodies, lane/matrix/env/permissions correct, no services block on conformance.yml (declined python-sidecar change not implemented)

PASS  hygiene: job id is exactly 'leak-scan' (branch-protection status name unchanged): ['leak-scan']
PASS  hygiene: exactly one run step (no native body)
PASS  hygiene: run step calls the script with --only leak-scan --strict-toolchains
PASS  hygiene: MO_CI_LEAK_BASE pointed at the PR base branch
PASS  hygiene: checkout fetch-depth 0 (scan diffs against base)
PASS  conformance: jobs are gates/conformance/java-reactor: ['conformance', 'gates', 'java-reactor']
PASS  conformance/gates: single run step invoking ci-local.sh (thin wrapper)
PASS  conformance/gates: no native check body remains (no bun test/mvn/dotnet/leak-scan invocations)
PASS  conformance/conformance: single run step invoking ci-local.sh (thin wrapper)
PASS  conformance/conformance: no native check body remains (no bun test/mvn/dotnet/leak-scan invocations)
PASS  conformance/java-reactor: single run step invoking ci-local.sh (thin wrapper)
PASS  conformance/java-reactor: no native check body remains (no bun test/mvn/dotnet/leak-scan invocations)
PASS  conformance: matrix lanes = ts-fast/csharp/java-fast/python: ['ts-fast', 'csharp', 'java-fast', 'python']
PASS  conformance/gates: runs --strict-toolchains
PASS  conformance/conformance: passes --no-integration (integration-tests.yml owns docker suites)
PASS  conformance/conformance: runs --strict-toolchains
PASS  conformance/java-reactor: passes --no-integration (integration-tests.yml owns docker suites)
PASS  conformance/java-reactor: runs --strict-toolchains
PASS  conformance: lane passed from matrix
PASS  conformance: JVM lanes point METAOBJECTS_CI_M2_REPO at setup-java's maven cache
PASS  java-reactor: MO_CI_JACOCO=1 (JaCoCo ON in the second environment)
PASS  java-reactor: runs the java-slow lane
PASS  conformance: setup step if-condition references real lanes: "matrix.lane == 'ts-fast'" -> ['ts-fast']
PASS  conformance: setup step if-condition references real lanes: "matrix.lane == 'ts-fast'" -> ['ts-fast']
PASS  conformance: setup step if-condition references real lanes: "matrix.lane == 'csharp'" -> ['csharp']
PASS  conformance: setup step if-condition references real lanes: "matrix.lane == 'csharp'" -> ['csharp']
PASS  conformance: setup step if-condition references real lanes: "matrix.lane == 'java-fast'" -> ['java-fast']
PASS  conformance: setup step if-condition references real lanes: "matrix.lane == 'python'" -> ['python']
PASS  conformance: NO services/sidecar on any job (round-1 declined python-sidecar change NOT implemented)
PASS  conformance: least-privilege permissions contents:read
PASS  integration-tests: single release-gate job (migrate-ts-pg folded in): ['release-gate']
PASS  integration-tests: matrix lanes = ts-slow/csharp/java-slow/python: ['ts-slow', 'csharp', 'java-slow', 'python']
PASS  integration-tests: job-level postgres sidecar present
PASS  integration-tests: single run step invoking ci-local.sh
PASS  integration-tests: passes --integration-only (docker/Postgres half only)
PASS  integration-tests: runs --strict-toolchains
PASS  integration-tests: METAOBJECTS_TEST_PG_URL targets the sidecar (script's own sidecar stands aside)
PASS  integration-tests: migrate-ts sentinel ARMED (MIGRATE_TS_PG_EXPECT=1, folded job keeps its guard)
PASS  integration-tests: runtime-ts sentinel ARMED (RUNTIME_TS_PG_EXPECT=1)
PASS  integration-tests: JVM lanes point METAOBJECTS_CI_M2_REPO at setup-java's cache
PASS  integration-tests: least-privilege permissions contents:read
PASS  every workflow lane is an accepted --only value: used=['csharp', 'gates', 'java-fast', 'java-slow', 'leak-scan', 'python', 'ts-fast'] accepted=['csharp', 'gates', 'java', 'java-fast', 'java-slow', 'leak-scan', 'python', 'ts', 'ts-fast', 'ts-slow', 'ts-unit']
PASS  METAOBJECTS_CI_M2_REPO is consumed by scripts/ci-local.sh
PASS  MO_CI_JACOCO is consumed by scripts/ci-local.sh
PASS  MO_CI_LEAK_BASE is consumed by scripts/ci-local.sh
PASS  MIGRATE_TS_PG_EXPECT has a real consumer in migrate-ts
PASS  RUNTIME_TS_PG_EXPECT has a real consumer in runtime-ts
PASS  METAOBJECTS_TEST_PG_URL has real consumers in server/

ALL SEMANTIC ASSERTIONS PASS
Evidence: List-only drives of all 11 wrapper invocations

Every hygiene.yml / conformance.yml / integration-tests.yml command exits 0; --no-integration lanes print the '⊘ not running the docker/Postgres integration half' banner with no SKIP entries; --integration-only lanes list only the docker-half steps

\### CMD: env MO_CI_LEAK_BASE=origin/main scripts/ci-local.sh --only leak-scan --strict-toolchains
metaobjects local CI  (mode: only:leak-scan)
Steps that would run:
  + leak-scan (security)
exit=0

\### CMD: env scripts/ci-local.sh --only gates --strict-toolchains
metaobjects local CI  (mode: only:gates)
Steps that would run:
  + leak-scan (security)
  + pom-version parity
  + bun-version parity
  + uv.lock version parity
  + publish-intent parity
  + publish-set parity
  + no committed pre-release version
  + script-name hook collisions
  + ci lane selection
  + extract field kinds
  + no-magic gate wired (5 ports)
  + test-file references resolve
  + metamodel-version bump
  + positioning claims
  + migration guides vs registry
  + dependencies installed
  + peer-range bounds
  + owned template copies current
  + reference templates lint
  + shipped doc examples load
  + no retired $apiPrefix
  + site payload is true
  + site reference is fresh
  + release tag gate
  + scripts/ typecheck
  + requirements ledger verifies
  + requirements cover vocabulary
  + metamodel scaffolder
  + requirement harness
  + fixture-lint
  + doc-template drift
  + embedded-library drift
exit=0

\### CMD: env METAOBJECTS_CI_M2_REPO=~/.m2/repository scripts/ci-local.sh --only ts-fast --no-integration --strict-toolchains
metaobjects local CI  (mode: only:ts-fast, no integration)
Steps that would run:
  + ts build + typecheck
  + conformance: typescript
  + completeness-gate (mutation)

── ⊘ --no-integration: not running the docker/Postgres integration half ──
exit=0

\### CMD: env METAOBJECTS_CI_M2_REPO=~/.m2/repository scripts/ci-local.sh --only csharp --no-integration --strict-toolchains
metaobjects local CI  (mode: only:csharp, no integration)
Steps that would run:
  + conformance: csharp

── ⊘ --no-integration: not running the docker/Postgres integration half ──
exit=0

\### CMD: env METAOBJECTS_CI_M2_REPO=~/.m2/repository scripts/ci-local.sh --only java-fast --no-integration --strict-toolchains
metaobjects local CI  (mode: only:java-fast, no integration)
Steps that would run:
  + conformance: java
  + conformance: kotlin

── ⊘ --no-integration: not running the docker/Postgres integration half ──
exit=0

\### CMD: env METAOBJECTS_CI_M2_REPO=~/.m2/repository scripts/ci-local.sh --only python --no-integration --strict-toolchains
metaobjects local CI  (mode: only:python, no integration)
Steps that would run:
  + conformance: python

── ⊘ --no-integration: not running the docker/Postgres integration half ──
exit=0

\### CMD: env MO_CI_JACOCO=1 METAOBJECTS_CI_M2_REPO=~/.m2/repository scripts/ci-local.sh --only java-slow --no-integration --strict-toolchains
metaobjects local CI  (mode: only:java-slow, no integration)
Steps that would run:
  + java-reactor (install)

── ⊘ --no-integration: not running the docker/Postgres integration half ──
exit=0

\### CMD: env scripts/ci-local.sh --only ts-slow --integration-only --strict-toolchains
metaobjects local CI  (mode: only:ts-slow, integration only)
Steps that would run:
  + ts build (for integration)
  ▸ reusing Postgres sidecar 'metaobjects-ci-sidecar'
    METAOBJECTS_TEST_PG_URL -> localhost:33600 (stop it: docker rm -f metaobjects-ci-sidecar)
  + migrate-ts real-PG suite
  + runtime-ts real-PG dialect matrix
  + integration-tests (ts)
exit=0

\### CMD: env scripts/ci-local.sh --only csharp --integration-only --strict-toolchains
metaobjects local CI  (mode: only:csharp, integration only)
Steps that would run:
  + integration-tests (csharp)
exit=0

\### CMD: env scripts/ci-local.sh --only java-slow --integration-only --strict-toolchains
metaobjects local CI  (mode: only:java-slow, integration only)
Steps that would run:
  + integration-tests (java)
  + integration-tests (kotlin)
exit=0

\### CMD: env scripts/ci-local.sh --only python --integration-only --strict-toolchains
metaobjects local CI  (mode: only:python, integration only)
Steps that would run:
  + integration-tests (python)
exit=0
Evidence: Per-lane step selection detail

ts-slow --integration-only → ts build (for integration), sidecar reuse, migrate-ts real-PG suite, runtime-ts PG dialect matrix, integration-tests (ts). --only leak-scan → exactly one step. gates → 32 offline gates incl. fixture-lint, doc-template drift, embedded-library drift (the three former conformance.yml jobs)

\### CMD: env MO_CI_LEAK_BASE=origin/main scripts/ci-local.sh --only leak-scan --strict-toolchains
metaobjects local CI  (mode: only:leak-scan)
Steps that would run:
  + leak-scan (security)
exit=0

\### CMD: env scripts/ci-local.sh --only gates --strict-toolchains
metaobjects local CI  (mode: only:gates)
Steps that would run:
  + leak-scan (security)
  + pom-version parity
  + bun-version parity
  + uv.lock version parity
  + publish-intent parity
  + publish-set parity
  + no committed pre-release version
  + script-name hook collisions
  + ci lane selection
  + extract field kinds
  + no-magic gate wired (5 ports)
  + test-file references resolve
  + metamodel-version bump
  + positioning claims
  + migration guides vs registry
  + dependencies installed
  + peer-range bounds
  + owned template copies current
  + reference templates lint
  + shipped doc examples load
  + no retired $apiPrefix
  + site payload is true
  + site reference is fresh
  + release tag gate
  + scripts/ typecheck
  + requirements ledger verifies
  + requirements cover vocabulary
  + metamodel scaffolder
  + requirement harness
  + fixture-lint
  + doc-template drift
  + embedded-library drift
exit=0

\### CMD: env METAOBJECTS_CI_M2_REPO=~/.m2/repository scripts/ci-local.sh --only ts-fast --no-integration --strict-toolchains
metaobjects local CI  (mode: only:ts-fast, no integration)
Steps that would run:
  + ts build + typecheck
  + conformance: typescript
  + completeness-gate (mutation)

── ⊘ --no-integration: not running the docker/Postgres integration half ──
exit=0

\### CMD: env METAOBJECTS_CI_M2_REPO=~/.m2/repository scripts/ci-local.sh --only csharp --no-integration --strict-toolchains
metaobjects local CI  (mode: only:csharp, no integration)
Steps that would run:
  + conformance: csharp

── ⊘ --no-integration: not running the docker/Postgres integration half ──
exit=0

\### CMD: env METAOBJECTS_CI_M2_REPO=~/.m2/repository scripts/ci-local.sh --only java-fast --no-integration --strict-toolchains
metaobjects local CI  (mode: only:java-fast, no integration)
Steps that would run:
  + conformance: java
  + conformance: kotlin

── ⊘ --no-integration: not running the docker/Postgres integration half ──
exit=0

\### CMD: env METAOBJECTS_CI_M2_REPO=~/.m2/repository scripts/ci-local.sh --only python --no-integration --strict-toolchains
metaobjects local CI  (mode: only:python, no integration)
Steps that would run:
  + conformance: python

── ⊘ --no-integration: not running the docker/Postgres integration half ──
exit=0

\### CMD: env MO_CI_JACOCO=1 METAOBJECTS_CI_M2_REPO=~/.m2/repository scripts/ci-local.sh --only java-slow --no-integration --strict-toolchains
metaobjects local CI  (mode: only:java-slow, no integration)
Steps that would run:
  + java-reactor (install)

── ⊘ --no-integration: not running the docker/Postgres integration half ──
exit=0

\### CMD: env scripts/ci-local.sh --only ts-slow --integration-only --strict-toolchains
metaobjects local CI  (mode: only:ts-slow, integration only)
Steps that would run:
  + ts build (for integration)
  ▸ reusing Postgres sidecar 'metaobjects-ci-sidecar'
    METAOBJECTS_TEST_PG_URL -> localhost:33600 (stop it: docker rm -f metaobjects-ci-sidecar)
  + migrate-ts real-PG suite
  + runtime-ts real-PG dialect matrix
  + integration-tests (ts)
exit=0

\### CMD: env scripts/ci-local.sh --only csharp --integration-only --strict-toolchains
metaobjects local CI  (mode: only:csharp, integration only)
Steps that would run:
  + integration-tests (csharp)
exit=0

\### CMD: env scripts/ci-local.sh --only java-slow --integration-only --strict-toolchains
metaobjects local CI  (mode: only:java-slow, integration only)
Steps that would run:
  + integration-tests (java)
  + integration-tests (kotlin)
exit=0

\### CMD: env scripts/ci-local.sh --only python --integration-only --strict-toolchains
metaobjects local CI  (mode: only:python, integration only)
Steps that would run:
  + integration-tests (python)
exit=0
Evidence: Adversarial flag-contract results

All malformed invocations exit 2 with named messages; a drifted lane name ('typescript') is rejected, so wrapper/script drift fails loudly

[2] --only bogus -> --only expects gates|leak-scan|ts|ts-fast|ts-unit|ts-slow|java|java-fast|java-slow|python|csharp, got 'bogus'
[2] --only (no value) -> same message, got ''
[2] --integration-only --no-integration -> mutually exclusive
[2] --integration-only --quick -> mutually exclusive
[2] --bogus-flag -> unknown arg: --bogus-flag (see --help)
[2] --only typescript (drifted lane) -> rejected, got 'typescript'
[0] --help -> exits 0
Evidence: Leak-scan failure mode (staged leak)

leak-scan: possible private/other-project or local-path leak in added lines (metaobjects is PUBLIC): CONTRIBUTING.md Debug notes: my build cache lives at ~/secret-build-cache. — exit 1; worktree restored clean after the drive


leak-scan: possible private/other-project or local-path leak in added lines (metaobjects is PUBLIC):
    CONTRIBUTING.md	Debug notes: my build cache lives at ~/secret-build-cache.

Genericize the references (e.g. 'a downstream consumer', '<repo-root>') — see CLAUDE.md -> Public repository hygiene.
Evidence: Full flagless regression run (in progress at report time)

Green through 26 steps (all gates, TS conformance, TS unit suites); executing Stryker completeness-gate when reported; no failures

metaobjects local CI  (mode: full — all ports + reactor + docker)

── ▶ leak-scan (security) ──────────────────────────────────────────────
leak-scan: clean

── ▶ pom-version parity ──────────────────────────────────────────────
check-pom-versions: ✓ 3 parent + 1 property pom(s) match reactor 8.0.13

── ▶ bun-version parity ──────────────────────────────────────────────
check-bun-version: bun 1.3.14 matches the workflow pin.

── ▶ uv.lock version parity ──────────────────────────────────────────────
check-uv-lock-version: uv.lock agrees with pyproject.toml (1.0.13)

── ▶ publish-intent parity ──────────────────────────────────────────────
publish-intent parity: OK (lockstep 1.0.13; 2 declared source-only)

── ▶ publish-set parity ──────────────────────────────────────────────
   1. @metaobjectsdev/metadata  (server/typescript/packages/metadata)
   2. @metaobjectsdev/render  (server/typescript/packages/render)
   3. @metaobjectsdev/codegen-ts  (server/typescript/packages/codegen-ts)
   4. @metaobjectsdev/runtime-ts  (server/typescript/packages/runtime-ts)
   5. @metaobjectsdev/migrate-ts  (server/typescript/packages/migrate-ts)
   6. @metaobjectsdev/sdk  (server/typescript/packages/sdk)
   7. @metaobjectsdev/docs-site  (server/typescript/packages/docs-site)
   8. @metaobjectsdev/runtime-web  (client/web/packages/runtime-web)
   9. @metaobjectsdev/codegen-ts-react  (server/typescript/packages/codegen-ts-react)
  10. @metaobjectsdev/codegen-ts-tanstack  (server/typescript/packages/codegen-ts-tanstack)
  11. @metaobjectsdev/react  (client/web/packages/react)
  12. @metaobjectsdev/tanstack  (client/web/packages/tanstack)
  13. @metaobjectsdev/cli  (server/typescript/packages/cli)
  14. @metaobjectsdev/ai-runtime  (server/typescript/packages/ai-runtime)
publish set: OK (lockstep 1.0.13; 14 packages, tier-ordered, closed over sibling deps)
ok:   happy path: private + off-lockstep excluded, order [metadata, render, docs-site, cli]
ok:   untiered member: threw on "publish order undeclared"
ok:   unclosed set: threw on "not closed over its own runtime dependencies"
ok:   inverted tier order: threw on "publishes a dependency AFTER its dependent"
ok:   regression: docs-site (#6) publishes before cli (#12) in the real tree
ok:   regression: docs-site has a declared tier

publish-set tests: all passed

── ▶ no committed pre-release version ──────────────────────────────────────────────
check-no-prerelease-versions: ✓ no pre-release version in any version declaration

── ▶ script-name hook collisions ──────────────────────────────────────────────
script-name-hooks: ✓ 10 root scripts, no lifecycle-hook collisions

── ▶ ci lane selection ──────────────────────────────────────────────
ok:   [server/typescript/packages/cli/src/x.ts] -> 'ts'
ok:   [client/web/packages/react/src/y.tsx] -> 'ts'
ok:   [server/java/metadata/src/main/java/A.java] -> 'java'
ok:   [server/java/codegen-kotlin/src/main/kotlin/B.kt] -> 'java'
ok:   [server/python/src/metaobjects/loader.py] -> 'python'
ok:   [server/csharp/MetaObjects/Loader.cs] -> 'csharp'
ok:   [fixtures/conformance/foo/meta.json] -> 'ts java python csharp'
ok:   [spec/metamodel.md] -> 'ts java python csharp'
ok:   [scripts/ci-local.sh] -> 'ts java python csharp'
ok:   [.github/workflows/local-ci.yml] -> 'ts java python csharp'
ok:   [agent-context/skills/x.md] -> 'ts java python csharp'
ok:   [weird-new-toplevel/file.txt] -> 'ts java python csharp'
ok:   [docs/features/cli.md] -> ''
ok:   [README.md CHANGELOG.md] -> ''
ok:   [server/typescript/a.ts server/python/b.py] -> 'ts python'
ok:   [docs/x.md fixtures/y.json] -> 'ts java python csharp'
ALL PASS
ok:   union all -> 'ts java python csharp'
ok:   union reorders to canonical -> 'ts java python csharp'
ok:   union deduplicates -> 'ts java'
ok:   union single -> 'python'
ok:   union empty -> ''
ok:   union drops unknown tokens -> 'ts'
ok:   every lane green -> nothing to re-run -> ''
ok:   docs-only run skips a red java -> java still not green -> 'java'
ok:   one red ts lane selects the whole ts port -> 'ts'
ok:   skipped is walked past to the real verdict -> ''
ok:   cancelled is not green -> 'csharp'
ok:   a lane with no verdict in the window is not green -> 'python'
ok:   no history at all -> everything -> 'ts java python csharp'
ok:   every port-testing job in .github/workflows/local-ci.yml is mapped by lane_port -> ''
ok:   every lane in lane_port exists as a job in .github/workflows/local-ci.yml -> ''
all checks passed

── ▶ extract field kinds ──────────────────────────────────────────────
extract FieldKind: 4 ports agree on [STRING, INT, LONG, DOUBLE, BOOLEAN, ENUM, OBJECT] (1 sanctioned deviation(s): csharp)

── ▶ no-magic gate wired (5 ports) ──────────────────────────────────────────────
no-magic gate coverage: 5 ports wired; 13 shapes tracked, 0 known gaps

── ▶ test-file references resolve ──────────────────────────────────────────────
test-file references: 28 named, all resolve

── ▶ metamodel-version bump ──────────────────────────────────────────────
  metamodel-version: none change since v1.0.13; declared 1.0 — ok.
ok:   no change classifies as nothing
ok:   a removed subtype is BREAKING
ok:   an added subtype is ADDITIVE
ok:   a removed attr is BREAKING
ok:   an added OPTIONAL attr is ADDITIVE
ok:   an added REQUIRED attr is BREAKING
ok:   optional → required is BREAKING
ok:   required → optional is ADDITIVE
ok:   a changed valueType is BREAKING
ok:   a changed isArray is BREAKING
ok:   an enum member REMOVED is BREAKING
ok:   an enum member ADDED is ADDITIVE
ok:   an OPEN attr becoming a closed enum is BREAKING
ok:   a closed enum OPENING is ADDITIVE
ok:   a removed child rule is BREAKING
ok:   an added OPTIONAL child rule is ADDITIVE
ok:   an added MANDATORY child rule is BREAKING
ok:   raising min is BREAKING
ok:   lowering min is ADDITIVE
ok:   capping an unbounded max is BREAKING
ok:   lifting a cap is ADDITIVE
ok:   lowering a finite max is BREAKING
ok:   a removed commonAttr is BREAKING
ok:   a changed default subtype is BREAKING (it changes what an unqualified declaration MEANS)
ok:   a removed default subtype is BREAKING
ok:   an added default subtype is ADDITIVE
ok:   a type description change is PROSE ONLY
ok:   a `rules` change is PROSE ONLY — this is exactly #210, and why prose only warns
ok:   an attr description change is PROSE ONLY
ok:   no change requires no bump
ok:   additive requires a minor
ok:   post-1.0 breaking requires a major
ok:   pre-1.0 breaking requires a minor, not a major
ok:   post-1.0 a correction requires a minor, NOT a major
ok:   a correction must still move the version
ok:   pre-1.0 the correction flag changes nothing
ok:   a correction over `none` is still none
ok:   a correction over `additive` is still a minor
ok:   an unmoved version must NOT satisfy a minor
ok:   1.0 → 1.1 satisfies a minor
ok:   a major over-satisfies a minor
ok:   a mi

... [273773 bytes truncated] ...

lt). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (codegen drift), --docs (docs drift), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (codegen drift), --docs (docs drift), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (codegen drift), --docs (docs drift), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta verify — no template.* nodes found; the template gate had nothing to check.
meta verify — not run: schema (--db <url>), codegen (--codegen), docs (--docs), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it

test/unit/verify-output.test.ts:
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (codegen drift), --docs (docs drift), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta verify — 1 template(s) clean.
meta verify — not run: schema (--db <url>), codegen (--codegen), docs (--docs), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (codegen drift), --docs (docs drift), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta: failed to load metadata: ERR_INVALID_TEMPLATE: template "Broken" @payloadRef "DoesNotExist" does not resolve to an object.value or sourceless object.projection at root
  in meta.ai.json at $['metadata.root'].children[0]['template.output']
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (codegen drift), --docs (docs drift), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta verify — 1 template(s) clean.
meta verify — not run: schema (--db <url>), codegen (--codegen), docs (--docs), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (codegen drift), --docs (docs drift), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta: [Welcome] (email/html) ERR_VAR_NOT_ON_PAYLOAD: nonExistentField (prompts/e/html:1)
meta: meta verify — 1 drift error(s) across 1 template(s).
meta verify — not run: schema (--db <url>), codegen (--codegen), docs (--docs), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (codegen drift), --docs (docs drift), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta verify — 1 template(s) clean.
meta verify — not run: schema (--db <url>), codegen (--codegen), docs (--docs), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (codegen drift), --docs (docs drift), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta: [Doc] (document) ERR_VAR_NOT_ON_PAYLOAD: missingField (prompts/o/x:1)
meta: meta verify — 1 drift error(s) across 1 template(s).
meta verify — not run: schema (--db <url>), codegen (--codegen), docs (--docs), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it

 1289 pass
 3 skip
 0 fail
 1 snapshots, 3773 expect() calls
Ran 1292 tests across 169 files. [39.36s]

── ▶ ts unit suites ──────────────────────────────────────────────
bun install v1.3.14 (0d9b296a)

Checked 556 installs across 565 packages (no changes) [7.00ms]
@metaobjectsdev/metadata build: Exited with code 0
@metaobjectsdev/runtime-web build: Exited with code 0
@metaobjectsdev/angular build: Exited with code 0
@metaobjectsdev/react build: Exited with code 0
@metaobjectsdev/tanstack build: Exited with code 0
@metaobjectsdev/sdk bundle-agent-context: bundled agent-context → ~/.no-mistakes/worktrees/5b8c6c97e760/01M41CDX3434BCV2066YDQPWCZ/server/typescript/packages/sdk/agent-context
@metaobjectsdev/sdk bundle-agent-context: Exited with code 0
bun test v1.3.14 (0d9b296a)

test/registry-coverage.test.ts:

[registry-coverage] subtypes: 47/59 exercised (79.7%), 12 UNTESTED
[registry-coverage] untested subtypes:
  attr.boolean
  attr.class
  attr.double
  attr.expression
  attr.filter
  attr.int
  attr.intMap
  attr.long
  validator.atLeastOne
  validator.comparison
  validator.presentIff
  validator.requiredWhen
[registry-coverage] exercised subtypes with untested attrs: 28

 2793 pass
 0 fail
 9267 expect() calls
Ran 2793 tests across 182 files. [3.18s]
bun test v1.3.14 (0d9b296a)

 368 pass
 0 fail
 1392 expect() calls
Ran 368 tests across 22 files. [100.00ms]
bun test v1.3.14 (0d9b296a)

 667 pass
 14 skip
 0 fail
 1508 expect() calls
Ran 681 tests across 53 files. [3.26s]
bun test v1.3.14 (0d9b296a)

 140 pass
 0 fail
 597 expect() calls
Ran 140 tests across 28 files. [4.35s]
bun test v1.3.14 (0d9b296a)

 59 pass
 0 fail
 183 expect() calls
Ran 59 tests across 14 files. [689.00ms]
bun test v1.3.14 (0d9b296a)

 22 pass
 0 fail
 84 expect() calls
Ran 22 tests across 3 files. [285.00ms]
bun test v1.3.14 (0d9b296a)

 329 pass
 0 fail
 843 expect() calls
Ran 329 tests across 34 files. [693.00ms]
bun test v1.3.14 (0d9b296a)

 21 pass
 0 fail
 71 expect() calls
Ran 21 tests across 6 files. [35.00ms]
bun test v1.3.14 (0d9b296a)

 55 pass
 0 fail
 119 expect() calls
Ran 55 tests across 11 files. [123.00ms]
bun test v1.3.14 (0d9b296a)

 46 pass
 0 fail
 217 expect() calls
Ran 46 tests across 19 files. [471.00ms]
bun test v1.3.14 (0d9b296a)

 71 pass
 0 fail
 148 expect() calls
Ran 71 tests across 9 files. [160.00ms]
bun test v1.3.14 (0d9b296a)

 18 pass
 0 fail
 30 expect() calls
Ran 18 tests across 3 files. [788.00ms]
bun test v1.3.14 (0d9b296a)

 49 pass
 0 fail
 76 expect() calls
Ran 49 tests across 6 files. [627.00ms]
bun test v1.3.14 (0d9b296a)

 21 pass
 0 fail
 35 expect() calls
Ran 21 tests across 4 files. [271.00ms]

── ▶ completeness-gate (mutation) ──────────────────────────────────────────────
$ stryker run
�[32m13:58:02 (1335869) INFO ProjectReader�[39m Found 6 of 885 file(s) to be mutated.
�[32m13:58:02 (1335869) INFO Instrumenter�[39m Instrumented 6 source file(s) with 1704 mutant(s)
�[32m13:58:02 (1335869) INFO ConcurrencyTokenProvider�[39m Creating 6 test runner process(es).
�[32m13:58:02 (1335869) INFO DryRunExecutor�[39m Starting initial test run (command test runner with "perTest" coverage analysis). This may take a while.
�[32m13:58:03 (1335869) INFO DryRunExecutor�[39m Initial test run succeeded. Ran 1 tests in 0 seconds (net 932 ms, overhead 0 ms).
error: script "conformance:mutation" exited with code 143
Evidence: Gates lane real run (conformance.yml gates job command)

Source: Gates lane real run (conformance.yml gates job command) (local file: ~/.no-mistakes/evidence/01M41CDX3434BCV2066YDQPWCZ/gates-lane-real-run.log)

LOCAL CI PASSED — all 32 offline gates green under --strict-toolchains
- Outcome: ⚠️ 3 issues (1 warning, 2 infos) across 1 run (30m10s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • ⚠️ CONTRIBUTING.md:76 - The edit reaffirms 'GitHub Actions is disabled on this repository, so the files in .github/workflows/ no longer run' (and .no-mistakes.yaml:8 'that script is the ONLY thing that runs those checks at all'), but Actions is verifiably ENABLED on the repo right now (gh api repos/metaobjectsdev/metaobjects/actions/permissions → {"enabled":true}), matching project CLAUDE.md ('re-enabled 2026-09-20, and it stays on'). Consequence: the thin wrappers go live immediately — hygiene.yml fires on the next PR (and main's protection requires that status), conformance.yml on tonight's 04:41 UTC cron and the next v* tag — and contributors are told nothing in .github/workflows/ runs. The user intent's own background ('currently disabled … do not run today') carries the same outdated belief, so the author should know the change is load-bearing on merge, not dormant. Remedy is mechanical: reword to the conditional form scripts/ci-local.sh's header already uses ('When GitHub Actions is off, this script is the only thing that runs them') in CONTRIBUTING.md and .no-mistakes.yaml.
  • ℹ️ .github/workflows/conformance.yml:133 - The python conformance lane (--only python --no-integration) is not docker-free: gate_conf_python (scripts/ci-local.sh:644) runs the full pytest tree including tests/integration, whose PG-backed scenarios fall back to per-scenario docker run postgres containers when METAOBJECTS_TEST_PG_URL is unset (server/python/tests/integration/postgres_container.py mode 2; this job sets no sidecar). Green on ubuntu-latest (docker present) but pulls postgres:16-alpine and boots per-scenario containers — the cost the shared-sidecar pattern exists to avoid — crossing the 'integration-tests.yml owns the docker suites' boundary the change's own comments draw. Noting the tradeoff: splitting the suite would create a second definition of the python checks, which is what this change exists to eliminate; a job-level services: postgres + METAOBJECTS_TEST_PG_URL on the conformance matrix would remove the per-scenario cost if it ever matters.

🔧 Fix applied.
1 info still open:

  • ℹ️ scripts/ci-local.sh:95 - Two new comments say every conformance.yml call passes --no-integration (scripts/ci-local.sh:94-96 "conformance.yml (--only gates / ts-fast / csharp / java-fast / python, and java-slow for the reactor, all with --no-integration)"; conformance.yml:15-16 "Each job ... calls the script with that lane's --only selector and --no-integration"), but the gates job (conformance.yml:71) runs scripts/ci-local.sh --only gates --strict-toolchains without it. Behaviorally void — verified via MO_CI_LIST_ONLY that the gates lane selects no docker/Postgres steps either way — but the wrapper-contract documentation this change exists to make misstates the actual invocation.
⚠️ **Test** - 3 issues (1 warning, 2 infos)
  • ⚠️ The full flagless scripts/ci-local.sh regression run was still executing at report time — green through 26 consecutive steps (all 32 offline gates, TS conformance, TS unit suites) and inside the Stryker completeness-gate; the csharp / java-fast / java-reactor / python / docker-integration sections had not yet been reached. No failure of any kind so far. The run is still writing to the evidence log; the completed portion plus the driven wrapper mechanics cover every line this change touches, but the untouched ports' suites did not finish within the phase. Log: full-ci-local-run.log in the evidence directory.
  • ℹ️ conformance.yml's THIN WRAPPER header says each job calls the script 'with that lane's --only selector and --no-integration', but the gates job correctly omits --no-integration (its lane has no docker half, so the flag would be a no-op). Prose imprecision only; behavior verified correct.
  • ℹ️ actionlint reports SC2155 at local-ci.yml:285. Verified pre-existing: the same warning fires at base commit b136109 (line 284 before this branch's +1 comment line). This change's local-ci.yml edit is comment-only; the three rewritten workflows (hygiene.yml, conformance.yml, integration-tests.yml) pass actionlint clean.
  • Live validation: ✅ go - 8 of 10 scenarios driven live against the product
Scenario Result Live Evidence
hygiene.yml is a thin wrapper: its only run step calls scripts/ci-local.sh --only leak-scan --strict-toolchains with MO_CI_LEAK_BASE at the PR base, and the job id 'leak-scan' (branch-protection statu… ✅ pass live semantic-wrapper-assertions.txt + real run 'MO_CI_LEAK_BASE=origin/main scripts/ci-local.sh --only leak-scan --strict-toolchains' → LOCAL CI PASSED (exit 0)
conformance.yml is a thin wrapper: jobs gates/conformance/java-reactor each carry one ci-local.sh run step with lane selector and --strict-toolchains, no native bun/mvn/dotnet bodies remain, JVM lanes… ✅ pass live semantic-wrapper-assertions.txt (48/48) + wrapper-list-only-drives.txt (all conformance invocations exit 0)
conformance.yml's gates job runs for real: all 32 offline gates pass under --strict-toolchains, including fixture-lint, doc-template drift and embedded-library drift (the three jobs the old workflow c… ✅ pass live gates-lane-real-run.log → LOCAL CI PASSED
integration-tests.yml is a thin wrapper: 4-lane matrix (ts-slow/csharp/java-slow/python) calls ci-local.sh --integration-only, job-level postgres sidecar with METAOBJECTS_TEST_PG_URL, both sentinel en… ✅ pass live semantic-wrapper-assertions.txt + list-only: ts-slow --integration-only lists exactly ts build (for integration), sidecar, migrate-ts real-PG suite, runtime-ts PG matrix, integration-tests (ts); all f…
Wrapper/script drift fails loudly, never silently: unknown lanes, missing --only value, unknown flags, and the --integration-only combos with --no-integration or --quick all exit 2 with named messages ✅ pass live flag-contract-adversarial.txt (7/7 cases as expected)
The leak gate actually fails on a leak: an added line with an absolute home path makes the scanner exit 1 naming file and line (driven via the scanner's staged-diff mode; committing the leak was block… ✅ pass live leak-scan-failure-mode.txt → exit 1 with 'CONTRIBUTING.md Debug notes: my build cache lives at ~/secret-build-cache.'; worktree restored clean
Round-1 fix: CONTRIBUTING.md and .no-mistakes.yaml say the workflows run on GitHub as thin wrappers over scripts/ci-local.sh and the script stays the pre-PR gate; no stale 'Actions is disabled' prose… ✅ pass live grep of touched prose (only the accurate 'When GitHub Actions is off' conditional remains) + gh api actions/permissions → enabled=true + git show --stat 1cd896a (CONTRIBUTING.md, .no-mistakes.yaml o…
Round-1 declined item NOT implemented: the conformance matrix carries no job-level postgres sidecar and the python lane stays as reviewed (no services block on any conformance.yml job) ✅ pass live semantic-wrapper-assertions.txt → 'conformance: NO services/sidecar on any job' PASS
Full regression: flagless scripts/ci-local.sh run-everything (all-port conformance, java reactor, mutation gate, 5-port docker integration) ⏸️ untested no The prior payload did not establish a live result: the flagless run was launched and was green through 26 steps, but it was still executing the Stryker completeness-gate when the phase had to report a…
conformance.yml's java-reactor job verbatim (MO_CI_JACOCO=1 reactor with JaCoCo ON) driven live ⏸️ untested no Queued behind the full regression run to avoid two concurrent maven reactors; the flagless run in progress exercises the default jacoco-skipped path, so the MO_CI_JACOCO toggle was verified only by li…
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains
  • actionlint on hygiene.yml, conformance.yml, integration-tests.yml (all clean) and local-ci.yml (SC2155 confirmed pre-existing at base)
  • python3 /tmp/verify-wrappers.py — 48 semantic assertions over parsed workflow YAML (thin-wrapper shape, lanes, env consumers, declined-sidecar absence, permissions)
  • MO_CI_LIST_ONLY=1 drives of all 11 exact wrapper invocations from the three workflows (evidence: wrapper-list-only-drives.txt)
  • Real run: MO_CI_LEAK_BASE=origin/main scripts/ci-local.sh --only leak-scan --strict-toolchains → PASSED (hygiene.yml's command)
  • Real run: scripts/ci-local.sh --only gates --strict-toolchains → PASSED, all 32 offline gates (conformance.yml's gates job)
  • Adversarial flag contract: --only bogus / --only with no value / --integration-only+--no-integration / --integration-only+--quick / unknown flag / drifted lane 'typescript' all exit 2 with named messages; --help exits 0
  • Adversarial leak drive: staged home-path leak → bash .githooks/leak-scan.sh exits 1 naming file+line; working tree reverted clean afterwards
  • gh api repos/metaobjectsdev/metaobjects/actions/permissions → enabled=true (the corrected docs claim is true)
  • Configured baseline (ran before this phase): scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains → success
  • Full regression scripts/ci-local.sh (flagless) launched; green through 26 steps (gates, TS conformance, TS unit suites), still inside the Stryker completeness-gate at report time; stray scripts/ci-local.sh --quick --no-integration run also still executing
⚠️ **Document** - 1 info
  • ℹ️ .claude/skills/releasing/SKILL.md:168 - Pre-existing, not made stale by this change: the releasing skill still says 'GitHub Actions is disabled on this repository (2026-09-16)' (also line 222 'inoperative while GitHub Actions is disabled'), but Actions has been enabled since 2026-09-20 and publish-csharp.yml is live. Out of this phase's scope on two counts: the file concerns the publish workflows, not the three check workflows this branch wrapped, and the round-1 decision explicitly scoped Actions-prose corrections to CONTRIBUTING.md, .no-mistakes.yaml and the scripts/ci-local.sh header. Flagging only so it is not lost; no edit made. Historical specs/plans under docs/superpowers/ that name the removed conformance-kotlin job are dated archives and were deliberately left as records.
✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

dmealing and others added 4 commits October 3, 2026 12:32
…r ci-local.sh

The three hosted check workflows carried their own step bodies, duplicating
what scripts/ci-local.sh runs, so the two definitions could drift. Each job
now checks out, installs its lane's toolchain and calls the script, following
local-ci.yml's shape.

Script additions, all opt-in so existing modes list identical steps:
- --only leak-scan runs the leak scan alone (hygiene.yml's PR gate);
  MO_CI_LEAK_BASE points it at the PR's base branch.
- --no-integration / --integration-only split a lane into its checks and its
  docker/Postgres half, so conformance.yml and integration-tests.yml each run
  their half of the same lanes.
- MO_CI_JACOCO=1 keeps JaCoCo on in the Java reactor, preserving the
  instrumented second environment conformance.yml's nightly exists for.
- gate_conf_csharp re-runs ApiDocsCrossPort after Cli.Tests builds the CLI;
  the whole-project run before it soft-skips on a clean checkout, which the
  workflow's explicit ordering used to cover.

Jobs regroup onto the script's lanes: typecheck and completeness fold into
ts-fast, conformance-kotlin into java-fast, fixture-lint and the drift jobs
into gates, migrate-ts-pg into ts-slow. leak-scan keeps its job name (the
required status). Triggers are unchanged.
Replace 'mirrors the hosted lanes' — backwards after the thin-wrapper
inversion — with the single-definition fact the script header and
CONTRIBUTING.md now carry.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@dmealing
dmealing merged commit 70423f9 into main Oct 3, 2026
1 check passed
@dmealing
dmealing deleted the fm/mo-workflow-thin-wrappers branch October 3, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant