From 453ee86969b1b0a024f7de25c0f0e7b6b8ba816a Mon Sep 17 00:00:00 2001 From: Rafael Richards Date: Mon, 15 Jun 2026 07:05:01 -0400 Subject: [PATCH 1/5] ci: standardize m-stdlib CI onto the Go `m` toolchain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the legacy Python m-cli (m-dev-tools/m-cli, pip-installed inside a yottadb/yottadb-base container) with the Go `m` (vista-cloud-dev/m-cli) for fmt/lint/test/coverage — unblocking VSL/MSL Phase B item 4 (the reusable m-ci.yml can't generalize across two toolchains). - ci.yml: ubuntu-latest runner builds `m` from m-cli (clone+build, proxy- lag-immune, M_CLI_REF default main) → Python drift gates (unchanged) → fmt/lint (Go m, engine-free) → m-test-engine container (--docker, the locally-proven path) → make test/coverage byte mode. Keeps the shared `arch` waterline gate + a fail-soft IRIS portability job. - Makefile: engine flags via $(M_ENGINE_FLAGS); --routines src; lint via scripts/m-lint-gate.sh; coverage --lcov + aggregate --min-percent=85; CORE_SRC excludes optional modules; ci/ci-json replace TAP with -o json. - scripts/m-lint-gate.sh: preserves `--error-on=error` semantics (zero error-severity findings) the Go m lacks a native flag for. - AGENTS.md: Setup/Test rewritten for the Go m; new CI/toolchain section. Local: make ci green — 45 suites / 2434 pass / 0 fail, aggregate coverage 92.79%. Real-CI proof is the point of this push. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 323 ++++++++++++++++----------------------- AGENTS.md | 55 +++++-- Makefile | 95 ++++++++---- scripts/m-lint-gate.sh | 48 ++++++ 4 files changed, 286 insertions(+), 235 deletions(-) create mode 100755 scripts/m-lint-gate.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2dd1137..583c409 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,13 +1,24 @@ -# m-stdlib CI. +# m-stdlib CI — standardized onto the **Go `m`** toolchain +# (github.com/vista-cloud-dev/m-cli), replacing the legacy Python m-cli +# (github.com/m-dev-tools/m-cli). One binary runs fmt / lint / test / coverage; +# the same `m arch check` already powers the shared waterline gate. # -# Container pinned to yottadb/yottadb-base:latest-master per Phase 0 §6.0 -# verification (2026-04-30). YottaDB r2.07 at pin time; mumps/mupip live -# at /opt/yottadb/current/ and are reachable after sourcing ydb_env_set. +# Engine transport (the spike's real unknown — settled here): +# We run on a plain `ubuntu-latest` runner and bring up the **m-test-engine** +# YottaDB container, then drive the Go `m` over `--docker m-test-engine` +# (Option 2 in the spike). This is the path proven in local dev — there is no +# host YDB on the dev box either, so `--docker` is the de-risked, identical +# invocation. (Option 1 — running INSIDE yottadb/yottadb-base with the Go `m` +# LocalEngine — would also work but is unproven with the Go `m`'s `ydb`-binary +# resolution; we chose the proven path.) # -# IRIS portability check (auxiliary track A5) reintroduced at v0.0.4 as -# a fail-soft (continue-on-error) job. See impl-plan §4 (vendor scope) -# and §8.6 (STDLOG ships alongside the IRIS re-add). The job's purpose -# is to surface portability regressions over time, not to gate merges. +# The Python DRIFT-GATE GENERATORS stay Python: tools/gen-manifest.py / +# gen-skill.py / gen-doctests.py are not the M toolchain. They are pure-stdlib +# (argparse/json/re/pathlib) so the ubuntu runner's system python3 suffices — no +# pip install, no container. +# +# IRIS portability (auxiliary) remains a fail-soft (continue-on-error) job: +# it surfaces regressions without gating merges. name: CI on: @@ -15,230 +26,162 @@ on: branches: [main] pull_request: +permissions: + contents: read + +env: + # Ref of vista-cloud-dev/m-cli to build the Go `m` from. Pin to a tag at + # Phase B item 5 (tag m-cli + pin m-cli-ref); `main` until then. Matches the + # arch-waterline gate's clone-and-build (proxy-lag-immune — see that workflow). + M_CLI_REF: main + GO_VERSION: "1.26.x" + jobs: - # m/v waterline G1 gate (engine-free) — m-stdlib is layer m; the gate - # scans src/*.m for any ^VSL* (v-layer) reference. + # m/v waterline gate (G1–G4 + meta-shape) — reusable org workflow, builds its + # own `m` from m-cli and runs `m arch check .`. m-stdlib is layer m. arch: uses: vista-cloud-dev/.github/.github/workflows/arch-waterline.yml@main m-stdlib: runs-on: ubuntu-latest - container: - image: yottadb/yottadb-base:latest-master - options: --user root - strategy: - fail-fast: false - matrix: - ydb-image: ["latest-master"] # add tagged release lines as they're vetted - steps: - - name: Install git before checkout (so checkout does a real clone) - # Critical: the container ships without git. When - # actions/checkout@v4 runs inside the container and can't find - # git, it falls back to GitHub's REST API and downloads the - # source as a tarball — no .git directory. Every later step - # that calls git (manifest-check, skill-check, doctest-check) - # then errors with "Not a git repository". - # - # Installing git BEFORE checkout lets the action do a real - # `git clone` and leave a working .git in $GITHUB_WORKSPACE. - # Diagnosed 2026-05-09 via the previous safe.directory step's - # diagnostic listing of /__w/m-stdlib/m-stdlib/.git — file not - # found, but src/ etc. all present (tarball download). - run: | - apt-get update - apt-get install -y --no-install-recommends git ca-certificates - - uses: actions/checkout@v4 - - name: Install Python toolchain - run: | - apt-get install -y --no-install-recommends \ - python3.12 python3.12-venv python3.12-dev gcc make - python3.12 -m venv /tmp/venv - /tmp/venv/bin/pip install --upgrade pip - - - name: Trust workspace inside the container - # Wildcard safe.directory entry — fine on an ephemeral CI - # runner. Avoids "dubious ownership" errors when later git - # operations run as root against the checked-out workspace. - run: git config --global --add safe.directory '*' - - - name: Install m-cli + tree-sitter-m from git checkouts + - uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: true + + - name: Build the Go `m` toolchain from m-cli + # Clone + build rather than `go install …@${ref}`: the module proxy's + # branch→commit cache can lag a fresh m-cli merge by minutes, which would + # build a stale `m`. A shallow clone uses the exact branch/tag HEAD + # immediately (same rationale as arch-waterline.yml). CGO_ENABLED=0 makes + # a static binary that also runs inside any container if ever needed. run: | - # Distribution model: clone-and-install (no package registry). - # tree-sitter-m must land before m-cli so its local checkout - # satisfies m-cli's dependency declaration. - git clone --depth=1 https://github.com/m-dev-tools/tree-sitter-m /tmp/tree-sitter-m - git clone --depth=1 https://github.com/m-dev-tools/m-cli /tmp/m-cli - /tmp/venv/bin/pip install /tmp/tree-sitter-m - /tmp/venv/bin/pip install -e "/tmp/m-cli[lsp]" - + git clone --depth 1 --branch "$M_CLI_REF" \ + https://github.com/vista-cloud-dev/m-cli "$RUNNER_TEMP/m-cli" + ( cd "$RUNNER_TEMP/m-cli" && CGO_ENABLED=0 go build -trimpath -o "$RUNNER_TEMP/m" . ) + echo "$RUNNER_TEMP" >> "$GITHUB_PATH" + "$RUNNER_TEMP/m" version || true + + # ── Engine-free drift gates (Python generators — unchanged) ──────────── + # check-manifest also asserts dist/repo.meta.json is tracked + clean (the + # tier-1 contract the org catalog generator fetches by raw URL). - name: Manifest drift check - # Regenerates dist/stdlib-manifest.json + dist/errors.json from - # src/STD*.m via tools/gen-manifest.py and fails on diff against - # the committed artefacts. Mirrors `make fmt-check` — drift means - # somebody touched a `; doc:` block (or src/ structure) without - # re-running `make manifest`. Engine-free — only needs python3 - # and git, both already installed above. - # - # `check-manifest` wraps `manifest-check` and additionally - # asserts dist/repo.meta.json is tracked and committed (Phase 0 - # tier-1 contract — the org catalog generator fetches that file - # by raw URL and a missing/stale copy would break the smoke - # test in m-dev-tools/.github). - run: | - export PATH="/tmp/venv/bin:$PATH" - make check-manifest + run: make check-manifest - name: AI skill drift check - # Regenerates dist/skill/{SKILL.md,manifest-index.md,patterns.md, - # error-codes.md} from the manifest + tools/skill-patterns.md - # (WD1). Same drift model as manifest-check: any `; doc:` change - # that flows into the skill artefacts forces a regenerate-and- - # commit. Engine-free. - run: | - export PATH="/tmp/venv/bin:$PATH" - make skill-check + run: make skill-check - name: Doctest drift check - # Regenerates tests/STD*DOCTST.m from the manifest's @example - # tags (WD2). Drift means someone added or changed a Pattern-A - # @example without running `make doctest`. Engine-free — - # actually executing the generated doctests is folded into the - # regular `make test` step below, which globs `tests/` and so - # picks up the committed STD*DOCTST.m suites alongside the - # hand-written STD*TST.m ones. - run: | - export PATH="/tmp/venv/bin:$PATH" - make doctest-check + run: make doctest-check - name: docs/ prose-only gate - # Cross-repo guardrail: docs/ holds only human-readable prose. - # Non-prose artifacts (generated data, JSON/TSV output, examples, - # scaffolding templates) belong elsewhere (dist/, examples/, - # templates/, top-level domain dirs). Engine-free find-based check. run: make check-docs-prose - # The historical `make setup-ydb` step was removed: the Track A3 - # Makefile refactor (commit 6ff7c6d) dropped that target along - # with the vista-meta seeding model. The new flow runs `m test` - # via m-cli's LocalEngine transport, which derives ydb_routines - # from the workspace at runtime — no separate "initialise YDB - # workspace" step required. - + # ── Engine-free Go `m` gates ─────────────────────────────────────────── - name: Format check - run: | - . /opt/yottadb/current/ydb_env_set - export PATH="/tmp/venv/bin:$PATH" - make fmt-check + run: make fmt-check - name: Lint - run: | - . /opt/yottadb/current/ydb_env_set - export PATH="/tmp/venv/bin:$PATH" - make lint + # House gate = zero error-severity findings (scripts/m-lint-gate.sh); + # .m-cli.toml pins [lint] target-engine = "any", so this is also the + # cross-engine portability lint. + run: make lint - - name: Test (TAP) + # ── Engine-bound: m-test-engine (YottaDB) over `--docker` ────────────── + - name: Start m-test-engine (YottaDB) run: | - . /opt/yottadb/current/ydb_env_set - export PATH="/tmp/venv/bin:$PATH" - m test --format=tap | tee test-results.tap - - - name: Upload TAP + docker run -d --name m-test-engine \ + ghcr.io/m-dev-tools/m-test-engine:0.1.0 + echo "waiting for m-test-engine to report healthy…" + for _ in $(seq 1 60); do + status="$(docker inspect -f '{{.State.Health.Status}}' m-test-engine 2>/dev/null || echo starting)" + echo " health: $status" + [ "$status" = healthy ] && break + sleep 2 + done + test "$(docker inspect -f '{{.State.Health.Status}}' m-test-engine)" = healthy + + - name: Test (byte mode, core suites) + # make test = m test --engine ydb --docker m-test-engine --chset m + # --routines src $(CORE_SUITES) + run: make test + + - name: Coverage (aggregate ≥85%, LCOV) + # make coverage = m coverage --min-percent=85 --lcov coverage.lcov + # $(CORE_SRC) $(CORE_SUITES) — aggregate gate + tracefile. + run: make coverage + + - name: Machine-readable test + coverage JSON + # Reuses the Makefile's CORE_SRC/CORE_SUITES (no gate re-run, no duplicated + # optional-module exclusion logic). + run: make ci-json + + - name: Upload test + coverage artifacts if: always() uses: actions/upload-artifact@v4 with: - name: tap-${{ matrix.ydb-image }} - path: test-results.tap - - - name: Coverage (lcov) - run: | - . /opt/yottadb/current/ydb_env_set - export PATH="/tmp/venv/bin:$PATH" - m coverage --format=lcov > coverage.lcov || true - - - name: Upload coverage - if: matrix.ydb-image == 'latest-master' + name: m-stdlib-ci-results + path: | + test-results.json + coverage.json + coverage.lcov + if-no-files-found: ignore + + - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: files: coverage.lcov fail_ci_if_error: false # flip to true once the corpus is non-empty + - name: Stop m-test-engine + if: always() + run: docker rm -f m-test-engine || true + iris-portability-check: - # Auxiliary track A5: fail-soft IRIS portability surfacing. - # Runs on PRs only — push-to-main runs are reserved for the gating - # YottaDB job above. continue-on-error means a red IRIS run never - # blocks a merge; the artifact + check status make the regression - # visible in the PR view. + # Auxiliary IRIS portability surfacing — fail-soft (continue-on-error), PRs + # only. Brings up IRIS Community over `--docker` and runs the core suites via + # the Go `m`'s IRIS runner. A red run never blocks a merge; the job status + + # artifact make a portability regression visible. (The engine-agnostic fmt + + # target-engine=any lint already run in the gating job above.) if: github.event_name == 'pull_request' runs-on: ubuntu-latest continue-on-error: true - container: - image: intersystemsdc/iris-community:latest - options: --user root steps: - - name: Install git before checkout (so checkout does a real clone) - # Critical: the container ships without git. When - # actions/checkout@v4 runs inside the container and can't find - # git, it falls back to GitHub's REST API and downloads the - # source as a tarball — no .git directory. Every later step - # that calls git (manifest-check, skill-check, doctest-check) - # then errors with "Not a git repository". - # - # Installing git BEFORE checkout lets the action do a real - # `git clone` and leave a working .git in $GITHUB_WORKSPACE. - # Diagnosed 2026-05-09 via the previous safe.directory step's - # diagnostic listing of /__w/m-stdlib/m-stdlib/.git — file not - # found, but src/ etc. all present (tarball download). - run: | - apt-get update - apt-get install -y --no-install-recommends git ca-certificates - - uses: actions/checkout@v4 - - name: Install Python toolchain + - uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: true + + - name: Build the Go `m` toolchain from m-cli run: | - apt-get install -y --no-install-recommends \ - python3.12 python3.12-venv python3.12-dev gcc make - python3.12 -m venv /tmp/venv - /tmp/venv/bin/pip install --upgrade pip - - - name: Trust workspace inside the container - # Wildcard safe.directory entry — fine on an ephemeral CI - # runner. Avoids "dubious ownership" errors when later git - # operations run as root against the checked-out workspace. - run: git config --global --add safe.directory '*' - - - name: Install m-cli + tree-sitter-m from git checkouts + git clone --depth 1 --branch "$M_CLI_REF" \ + https://github.com/vista-cloud-dev/m-cli "$RUNNER_TEMP/m-cli" + ( cd "$RUNNER_TEMP/m-cli" && CGO_ENABLED=0 go build -trimpath -o "$RUNNER_TEMP/m" . ) + echo "$RUNNER_TEMP" >> "$GITHUB_PATH" + + - name: Start IRIS Community run: | - # Distribution model: clone-and-install (no package registry). - # tree-sitter-m must land before m-cli so its local checkout - # satisfies m-cli's dependency declaration. - git clone --depth=1 https://github.com/m-dev-tools/tree-sitter-m /tmp/tree-sitter-m - git clone --depth=1 https://github.com/m-dev-tools/m-cli /tmp/m-cli - /tmp/venv/bin/pip install /tmp/tree-sitter-m - /tmp/venv/bin/pip install -e "/tmp/m-cli[lsp]" - - - name: Format check (engine-agnostic) - run: /tmp/venv/bin/m fmt --check src/ tests/ - - - name: Lint (--target-engine=any) - # The --target-engine=any profile flags constructs that don't - # work on every supported engine. Treat findings here as the - # IRIS portability signal. - run: /tmp/venv/bin/m lint --error-on=error --target-engine=any src/ tests/ + docker run -d --name m-test-iris \ + intersystemsdc/iris-community:latest + echo "waiting for IRIS to report healthy…" + for _ in $(seq 1 90); do + status="$(docker inspect -f '{{.State.Health.Status}}' m-test-iris 2>/dev/null || echo starting)" + echo " health: $status" + [ "$status" = healthy ] && break + sleep 2 + done + docker inspect -f '{{.State.Health.Status}}' m-test-iris - name: Test under IRIS (best-effort) - # m-cli's IRIS runner support is itself work-in-progress. Until - # it lands fully, this step's purpose is to record the failure - # mode in the TAP artifact so the gap is visible. continue-on-error - # at the job level means a non-zero exit here never blocks a PR. - run: /tmp/venv/bin/m test --format=tap tests/ | tee iris-test-results.tap + # IRIS byte mode is inherent (--chset is a no-op); namespace USER. Reuses + # the Makefile's core-suite list via M_ENGINE_FLAGS. + run: make test M_ENGINE_FLAGS='--engine iris --docker m-test-iris --namespace USER' - - name: Upload IRIS TAP + - name: Stop IRIS if: always() - uses: actions/upload-artifact@v4 - with: - name: iris-tap - path: iris-test-results.tap + run: docker rm -f m-test-iris || true diff --git a/AGENTS.md b/AGENTS.md index fe9d556..06a0787 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -125,37 +125,62 @@ implement it here first; m-cli imports. ## Setup m-stdlib ships pure-M source; there is no compiled artifact to install. -The toolchain dependencies are `m-cli` (Python) and the YottaDB runtime. +The toolchain is the **Go `m`** (github.com/vista-cloud-dev/m-cli) — one +binary for fmt / lint / test / coverage — plus a YottaDB runtime (the +`m-test-engine` Docker container by default). The Python drift-gate +generators under `tools/` need only system `python3` (pure-stdlib). ```bash -# Clone m-cli alongside m-stdlib and install it into a venv. -git clone https://github.com/m-dev-tools/tree-sitter-m ~/projects/tree-sitter-m -git clone https://github.com/m-dev-tools/m-cli ~/projects/m-cli -cd ~/projects/m-cli -python3 -m venv .venv -.venv/bin/pip install -e ".[lsp]" ../tree-sitter-m +# Build the Go `m` from m-cli (one static binary; no venv). +git clone https://github.com/vista-cloud-dev/m-cli ~/vista-cloud-dev/m-cli +cd ~/vista-cloud-dev/m-cli && go build -o dist/m . # Engine: start m-test-engine for `make test` / `make coverage`. -make -C ~/projects/m-test-engine up # provides DockerEngine for m-cli +docker run -d --name m-test-engine ghcr.io/m-dev-tools/m-test-engine:0.1.0 ``` -The `M` Makefile variable defaults to `$HOME/projects/m-cli/.venv/bin/m`; -override it if your checkout lives elsewhere. +The `M` Makefile variable defaults to `m` on `$PATH`; override it to point at +the build, e.g. `make check M=$HOME/vista-cloud-dev/m-cli/dist/m`. The engine +transport is `$(M_ENGINE_FLAGS)` (default `--engine ydb --docker m-test-engine +--chset m`); override for a host-YDB box, e.g. +`make test M_ENGINE_FLAGS='--engine ydb --chset m'`. ## Test ```bash -make test # engine-bound; needs YDB transport (LocalEngine, DockerEngine, or SSHEngine) -make safe-test # same suites with auto-recovery from vista-meta failure modes -make coverage # gated at 85% per module +make test # core suites via $(M_ENGINE_FLAGS) (default: --docker m-test-engine, byte mode) +make safe-test # legacy vista-meta auto-recovery wrapper (not the Go-m path) +make coverage # aggregate line coverage gated ≥85% (--lcov coverage.lcov) make check # fmt-check + lint + test (fast dev loop) -make ci # check + TAP + JSON coverage (CI-shaped invocation) +make ci # check + coverage + ci-json (CI-shaped; writes *.json + coverage.lcov) ``` Engine-free checks (`fmt-check`, `lint`, `manifest-check`, -`skill-check`, `doctest-check`) work on a fresh clone without YDB +`skill-check`, `doctest-check`) work on a fresh clone without an engine configured. +## CI / toolchain (Go `m`) + +CI (`.github/workflows/ci.yml`) runs on a plain `ubuntu-latest` runner: it +builds the Go `m` from m-cli (`M_CLI_REF`, default `main` — pin to a tag at +VSL Phase B item 5), runs the Python drift gates + `m` fmt/lint (engine-free), +then brings up the `m-test-engine` container and runs `make test` / `make +coverage` over `--docker` (the locally-proven path). The shared `arch` +waterline gate and a fail-soft IRIS portability job round it out. + +Two reconciliations from the legacy Python m-cli are worth knowing: + +- **Lint severity.** The Go `m` has no `--error-on=` flag (its + `--check` fails on *any* finding). The house gate — zero **error**-severity + findings, style/warning advisory — lives in `scripts/m-lint-gate.sh` + (reads `m lint -o json`). `make lint` calls it. +- **Aggregate coverage.** `m coverage --min-percent` gates **aggregate** line + coverage across the measured set, not per-module. m-stdlib already treated + the few sub-85% modules (STDFS, STDHARN, …) as documented exceptions, so the + aggregate gate (currently ~93%) is the faithful continuation. The optional + modules (STDCOMPRESS/STDCRYPTO/STDHTTP — no core suites) are excluded from + the measured set (`CORE_SRC`) so they don't drag the aggregate down. + ## Build / generate The repo commits its own generated artefacts under `dist/` — every diff --git a/Makefile b/Makefile index 01375ec..ac0e8a1 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,29 @@ # m-stdlib Makefile. # -# Engine: m-cli's multi-transport runner picks LocalEngine (host YDB), -# DockerEngine (m-test-engine container), or SSHEngine (legacy -# vista-meta over SSH) per the resolver in m-cli/src/m_cli/engine.py. -# Engine-bound targets (test, coverage) fail with a helpful message -# from m-cli if no transport is detected; engine-free targets -# (manifest, manifest-check, skill-check, doctest-check, fmt, lint) -# work on a fresh clone with no engine configured. +# Toolchain: the **Go `m`** (github.com/vista-cloud-dev/m-cli) — one binary for +# fmt / lint / test / coverage. The Go `m` needs the engine transport named +# EXPLICITLY (it does not auto-detect like the legacy Python m-cli): engine-bound +# targets (test, coverage) pass `$(M_ENGINE_FLAGS)`, which defaults to the +# `m-test-engine` Docker container in byte mode. Engine-free targets (manifest, +# manifest-check, skill-check, doctest-check, fmt, lint) work on a fresh clone +# with no engine configured. SHELL := /bin/bash -# m-cli — Python entry point for `m fmt` / `m lint` / `m test` / `m coverage`. -# Resolved from $PATH by default. Maintainers who keep an unactivated -# m-cli venv can override per-shell or per-invocation, e.g. -# make fmt-check M=$HOME/projects/m-cli/.venv/bin/m +# m — the Go `m` binary (fmt / lint / test / coverage). Resolved from $PATH by +# default. Override per-shell or per-invocation when the binary lives elsewhere +# (e.g. a sibling m-cli build), e.g. +# make check M=$HOME/vista-cloud-dev/m-cli/dist/m M ?= m +# Engine transport for the Go `m`'s engine-bound commands (test, coverage). +# Default = the m-test-engine Docker container, byte (M) mode — m-stdlib is +# byte-oriented (STDB64/STDHEX/STDCSPRNG need `--chset m` on YDB; see AGENTS.md +# "Engine charset"). Override for a host-YDB dev box or a different transport, +# e.g. make test M_ENGINE_FLAGS='--engine ydb --chset m' (LocalEngine) +# make test M_ENGINE_FLAGS='--engine ydb --docker my-ydb --chset m' +M_ENGINE_FLAGS ?= --engine ydb --docker m-test-engine --chset m + # m-test-engine — local checkout for `make engine-up` / `engine-down`. # Override if you cloned it elsewhere. M_TEST_ENGINE ?= $(HOME)/projects/m-test-engine @@ -26,7 +34,7 @@ M_TEST_ENGINE ?= $(HOME)/projects/m-test-engine # override with `make kids VPKG=/path/to/v-pkg`. VPKG ?= $(HOME)/vista-cloud-dev/v-pkg/dist/v-pkg -.PHONY: all fmt fmt-check lint test test-optional safe-test coverage check ci clean print-env seed unseed manifest manifest-check check-manifest frontmatter skill skill-check skill-install doctest doctest-check doctest-run engine-up engine-down engine-status check-docs-prose kids check-kids +.PHONY: all fmt fmt-check lint test test-optional safe-test coverage check ci ci-json clean print-env seed unseed manifest manifest-check check-manifest frontmatter skill skill-check skill-install doctest doctest-check doctest-run engine-up engine-down engine-status check-docs-prose kids check-kids # vista-meta connection contract — silently included if present. # Preserves the maintainer's existing workflow but no longer hard-errors @@ -53,8 +61,12 @@ fmt: fmt-check: $(M) fmt --check src/ tests/ +# House lint gate = zero ERROR-severity findings (style/warning are advisory). +# The Go `m` has no `--error-on=` flag (its `--check` fails on ANY +# finding), so the severity filter lives in scripts/m-lint-gate.sh, which reads +# `m lint -o json` and exits non-zero only on error-severity findings. lint: - $(M) lint --error-on=error src/ tests/ + @M='$(M)' scripts/m-lint-gate.sh src/ tests/ # ── Engine lifecycle (m-test-engine container) ────────────────────── # @@ -90,22 +102,32 @@ seed: unseed: @./scripts/unseed-vista.sh -# Optional suites need compiled callout libs (libz/libzstd/libcrypto) that -# aren't part of the dependency-free core. They are excluded from the default -# `make test` and run explicitly via `make test-optional`. The same split is -# recorded per-module in dist/stdlib-manifest.json ("tier": "optional"). -OPTIONAL_SUITES := STDCOMPRESSTST STDCRYPTOTST STDCRYPTODOCTST STDHTTPTST -OPTIONAL_PATHS := $(addprefix tests/,$(addsuffix .m,$(OPTIONAL_SUITES))) -CORE_SUITES := $(filter-out $(OPTIONAL_PATHS),$(wildcard tests/*TST.m)) - -# `make test` runs the dependency-free core (no compiled callout libs). +# Optional modules need compiled callout libs (libz/libzstd/libcrypto) that +# aren't part of the dependency-free core. Both their SUITES and their SRC are +# excluded from the default `make test`/`make coverage` and run explicitly via +# `make test-optional`. The same split is recorded per-module in +# dist/stdlib-manifest.json ("tier": "optional") via the `@tier optional` tag. +OPTIONAL_MODULES := STDCOMPRESS STDCRYPTO STDHTTP +OPTIONAL_SUITES := STDCOMPRESSTST STDCRYPTOTST STDCRYPTODOCTST STDHTTPTST +OPTIONAL_PATHS := $(addprefix tests/,$(addsuffix .m,$(OPTIONAL_SUITES))) +OPTIONAL_SRC := $(addprefix src/,$(addsuffix .m,$(OPTIONAL_MODULES))) +CORE_SUITES := $(filter-out $(OPTIONAL_PATHS),$(wildcard tests/*TST.m)) +# CORE_SRC = the measured-coverage set. The Go `m coverage --min-percent` gate is +# AGGREGATE (not per-module), so measuring an optional module that has no core +# suite running would tank the aggregate — exclude them here and cover them under +# `make coverage-optional` once the callout libs are deployed. +CORE_SRC := $(filter-out $(OPTIONAL_SRC),$(wildcard src/*.m)) + +# `make test` runs the dependency-free core (no compiled callout libs). The Go +# `m` stages all of src/ via `--routines src` so ^STDASSERT + the modules under +# test resolve on the engine's routine path. test: - $(M) test $(CORE_SUITES) + $(M) test $(M_ENGINE_FLAGS) --routines src $(CORE_SUITES) # `make test-optional` runs the callout-backed suites; needs the .so libs # (libz/libzstd/libcrypto) and their ydb_xc_* descriptors deployed. test-optional: - $(M) test $(OPTIONAL_PATHS) + $(M) test $(M_ENGINE_FLAGS) --routines src $(OPTIONAL_PATHS) # `safe-test` runs the same suites through scripts/safe-test.sh, which # auto-recovers from the documented vista-meta container failure modes @@ -115,17 +137,30 @@ test-optional: safe-test: @./scripts/safe-test.sh tests/ +# `make coverage` measures CORE_SRC while running CORE_SUITES and writes LCOV. +# The Go `m` splits the positional paths into measured-routines (src/*.m) vs +# suites (*TST.m); `--lcov ` replaces the legacy `--format=lcov`. The +# `--min-percent=85` gate is AGGREGATE line coverage across CORE_SRC (the legacy +# Python m-cli gated per-module — see CORE_SRC note + AGENTS.md "CI / toolchain"). coverage: - $(M) coverage --min-percent=85 --format=lcov > coverage.lcov + $(M) coverage $(M_ENGINE_FLAGS) --min-percent=85 --lcov coverage.lcov $(CORE_SRC) $(CORE_SUITES) # `check` is the fast dev loop (fmt-check + lint + test). Coverage is gated -# per-module starting v0.0.1 — run it as `make coverage` or via `make ci`. +# (aggregate ≥85%) — run it as `make coverage` or via `make ci`. check: fmt-check lint test @echo "OK" -ci: check - $(M) test --format=tap $(CORE_SUITES) > test-results.tap - $(M) coverage --routines src --tests tests --format=json > coverage.json +# `make ci` is the CI-shaped invocation: the `check` gate, the coverage gate +# (aggregate ≥85% + coverage.lcov), and the machine-readable JSON artifacts. +ci: check coverage ci-json + +# `make ci-json` writes the machine-readable artifacts without re-running the +# gates — CI calls it after the discrete fmt/lint/test/coverage steps pass. The +# Go `m` has no TAP writer; `-o json` emits the structured result envelope +# (test-results.json) and the coverage JSON report (coverage.json). +ci-json: + $(M) test $(M_ENGINE_FLAGS) --routines src -o json $(CORE_SUITES) > test-results.json + $(M) coverage $(M_ENGINE_FLAGS) -o json $(CORE_SRC) $(CORE_SUITES) > coverage.json # ── Manifest generation (WA4: discoverability + tooling plan) ───────── # @@ -232,7 +267,7 @@ doctest-run: $(M) test tests/STD*DOCTST.m clean: - rm -rf coverage.lcov test-results.tap coverage.json + rm -rf coverage.lcov test-results.json coverage.json # ── MSL KIDS base (VSL T0b.2) ─────────────────────────────────────── # kids/std.build.json declares the STD* base that m-stdlib ships as a diff --git a/scripts/m-lint-gate.sh b/scripts/m-lint-gate.sh new file mode 100755 index 0000000..eb347f7 --- /dev/null +++ b/scripts/m-lint-gate.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# scripts/m-lint-gate.sh — House lint gate over the Go `m` toolchain. +# +# The Go `m` (github.com/vista-cloud-dev/m-cli) has no `--error-on=` +# flag: `m lint --check` fails (exit 3) on ANY finding regardless of severity. +# The house gate is narrower — "zero ERROR-severity findings" — letting +# style/warning findings be reported without reding CI (the same semantics the +# legacy Python m-cli's `m lint --error-on=error` provided). +# +# This wrapper reproduces that: run `m lint -o json` (which exits 0 and emits +# every diagnostic), print all findings for visibility, and exit non-zero only +# if any finding is severity "error". +# +# Usage: M= scripts/m-lint-gate.sh src/ tests/ +# (M defaults to `m` on $PATH — same convention as the Makefile.) +# +# Residual: once the Go `m` grows a native `--error-on=` (or +# `--fail-on`) flag, replace this wrapper with that flag in the Makefile. +set -euo pipefail + +M="${M:-m}" + +LINT_JSON="$("$M" lint -o json "$@")" +export LINT_JSON + +python3 - <<'PY' +import json, os, sys + +doc = json.loads(os.environ["LINT_JSON"]) +diags = doc.get("diagnostics") or [] + +by_sev = {} +for d in diags: + by_sev[d.get("severity", "?")] = by_sev.get(d.get("severity", "?"), 0) + 1 + print( + f' {d.get("severity","?"):8} {d.get("file","?")}:' + f'{d.get("line","?")}:{d.get("col","?")} ' + f'{d.get("rule","?")} {d.get("message","")}', + file=sys.stderr, + ) + +errors = by_sev.get("error", 0) +summary = ", ".join(f"{n} {s}" for s, n in sorted(by_sev.items())) or "none" +print(f"lint: {len(diags)} finding(s) [{summary}]; {errors} error-severity", file=sys.stderr) + +# House gate: error-severity findings fail; style/warning are advisory. +sys.exit(1 if errors else 0) +PY From a3434846eb4b5ddbbeecf8aa939bdd51b24d39db Mon Sep 17 00:00:00 2001 From: Rafael Richards Date: Mon, 15 Jun 2026 07:11:59 -0400 Subject: [PATCH 2/5] docs(memory): record the Go-`m` CI standardization (PR #12) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-repo memory for the CI toolchain switch — engine via --docker m-test-engine, the lint-severity wrapper, aggregate coverage, and the branch-base note vs the unmerged stdseed/meta-to-root branch. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/memory/MEMORY.md | 1 + docs/memory/ci-go-m-toolchain.md | 69 ++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 docs/memory/ci-go-m-toolchain.md diff --git a/docs/memory/MEMORY.md b/docs/memory/MEMORY.md index 0a56975..061b7c3 100644 --- a/docs/memory/MEMORY.md +++ b/docs/memory/MEMORY.md @@ -2,6 +2,7 @@ One line per memory file. Content lives in the files, not here. +- [ci-go-m-toolchain](ci-go-m-toolchain.md) — 2026-06-15, PR #12 (proven GREEN on real CI): m-stdlib CI moved off the legacy Python m-cli onto the **Go `m`**. Engine = `--docker m-test-engine` on ubuntu (NOT LocalEngine); `m` built in-workflow; byte mode `--chset m` + `--routines src` mandatory; lint severity gate via `scripts/m-lint-gate.sh` (Go m has no `--error-on`); coverage is **aggregate** (~92.8%, optional modules excluded); drift gates stay Python; IRIS fail-soft 44/45. Unblocks VSL Phase B item 4 (`m-ci.yml`). - [iris-native-backends](iris-native-backends.md) — PR #1: the 3 optional modules' IRIS dispatch arm uses the **inlined `$zversion["IRIS"` probe** (not a public engine helper — that part of the PR was dropped as superseded); dual-engine local-test runbook (**YDB needs `--chset m`**, rebuild `/tmp/m` for the flag); `m-test-iris` embedded-Python is non-functional so STDCOMPRESS-IRIS is unverifiable locally; how the stale PR was landed without merge/rebase/force-push (forward-commit-to-master-tree). - [dual-engine-validation-2026-06-14](dual-engine-validation-2026-06-14.md) — 2026-06-14 full sweep: YDB 49/49 (2585 assertions, 0 fail), IRIS 45/49 (crypto cluster hangs/aborts — B2 IRIS-backends PR unmerged on this branch). Durable IRIS-runner gotchas: no per-suite timeout; killing a hung iris docker-exec corrupts staging → false 0/0 until `docker restart`; stdout reports `ok:true` even on a failed run (trust shell exit / stderr). Coverage gate is aggregate, not per-module. - [stdseed-g2-engine-neutral](stdseed-g2-engine-neutral.md) — STDSEED made strictly engine-neutral for **G2** (Option B, 2026-06-14): FileMan default filer `fileViaDie` removed, `filer` now required (`U-STDSEED-NO-FILER`); G2 stays a pure deny-list (no exception pragma). Owed: re-home `fileViaDie^VSLSEED` in v-stdlib. Verified 37/37 both engines. diff --git a/docs/memory/ci-go-m-toolchain.md b/docs/memory/ci-go-m-toolchain.md new file mode 100644 index 0000000..684a79d --- /dev/null +++ b/docs/memory/ci-go-m-toolchain.md @@ -0,0 +1,69 @@ +--- +name: ci-go-m-toolchain +description: m-stdlib CI now runs the Go `m` (not the legacy Python m-cli); engine via --docker m-test-engine; lint/coverage reconciliations +metadata: + type: project +--- + +# m-stdlib CI standardized onto the Go `m` (2026-06-15) + +m-stdlib's `.github/workflows/ci.yml` was rewritten to run the **Go `m`** +(github.com/vista-cloud-dev/m-cli — fmt/lint/test/coverage in one binary), +replacing the **legacy Python m-cli** (github.com/m-dev-tools/m-cli, pip-installed +inside a `yottadb/yottadb-base` container). This unblocks VSL/MSL **Phase B item 4** +(the reusable `m-ci.yml` could not generalize across two M toolchains). Branch +`mstdlib-ci-go-m`, **PR #12**; **proven GREEN on a real Actions run** +(run 27541967332: arch ✓, m-stdlib ✓; iris fail-soft). + +**Why:** the org standardized on the Go `m` (the waterline `arch` gate already +builds + runs it); m-stdlib was the last repo on the Python toolchain. + +**How to apply (the non-obvious bits):** + +- **Engine transport = `--docker m-test-engine`, NOT LocalEngine.** There is no + host YDB on the dev box (or in CI) — the engine is the `m-test-engine` Docker + container. CI runs on a plain `ubuntu-latest`, `docker run -d --name + m-test-engine ghcr.io/m-dev-tools/m-test-engine:0.1.0`, waits for the + healthcheck, then `m test/coverage --engine ydb --docker m-test-engine`. This + is the locally-proven path (Option 2 in the spike). Option 1 (run INSIDE + yottadb/yottadb-base + Go-m LocalEngine) was NOT used — unproven with the Go m. +- **`m` is built in-workflow** via `git clone --depth 1 --branch $M_CLI_REF + vista-cloud-dev/m-cli && CGO_ENABLED=0 go build` (same proxy-lag-immune pattern + as `arch-waterline.yml`; do NOT `go install …@main`). `M_CLI_REF` defaults to + `main` — pin to a tag at Phase B item 5. +- **Byte mode is mandatory:** `--chset m` on YDB (STDB64/STDHEX/STDCSPRNG are + byte-exact); no-op on IRIS. Without it those suites silently report 0/0. +- **Routines must be staged:** `m test` only stages the suite files — pass + `--routines src` so `^STDASSERT` + the module-under-test resolve. +- **Lint severity gap.** The Go `m` has NO `--error-on=` flag; its + `--check` fails on ANY finding. The house gate (zero **error**-severity; + style/warning advisory) is reproduced by `scripts/m-lint-gate.sh` (reads + `m lint -o json`, exits non-zero only on error-severity). m-stdlib is clean: + ~140 findings, all style/warning, 0 error. → residual `needs:` for m-cli: a + native `--error-on`/`--fail-on` flag would let the wrapper go away. +- **Aggregate, not per-module, coverage.** `m coverage --min-percent` gates the + AGGREGATE line % across the measured set (the legacy "per module" comment was + stale — sub-85% modules like STDFS/STDHARN were already documented exceptions). + `make coverage` measures `CORE_SRC` (src minus the 3 optional modules + STDCOMPRESS/STDCRYPTO/STDHTTP, which have no core suites and would tank the + aggregate) and is green at **~92.8%**. `--lcov ` replaces `--format=lcov`; + `-o json` replaces `--format=tap`/`--format=json` (the Go m has no TAP writer). +- **Drift gates stay Python.** `tools/gen-manifest.py` / `gen-skill.py` / + `gen-doctests.py` are pure-stdlib (argparse/json/re/pathlib) — system `python3` + on the runner suffices, no pip, no container. +- **Makefile knobs:** `M` (binary, default `m` on PATH) and `M_ENGINE_FLAGS` + (default `--engine ydb --docker m-test-engine --chset m`). New `ci-json` target + writes the JSON artifacts without re-running the gate. + +**IRIS job:** ported to the Go `m` (ubuntu + `docker run intersystemsdc/iris- +community` + `m test --engine iris --docker m-test-iris`), kept fail-soft +(`continue-on-error`). First real IRIS-in-CI run = **44/45 suites pass** (1 +portability failure surfaced — the intended signal). Far better than the legacy +Python IRIS runner. See [[dual-engine-validation-2026-06-14]] for IRIS-runner +gotchas; [[iris-native-backends]] for the `--chset m` rebuild note. + +**Branch-base note:** built off `master`, so it carries the `dist/repo.meta.json` +(pre-Phase-B-item-1) location and `make check-manifest` form. The +`stdseed-engine-neutral-g2` branch (meta-to-root move + STDSEED G2) is a SEPARATE +unmerged PR; both touch `Makefile` + `AGENTS.md` in different regions — expect a +trivial rebase when one lands first. From 7a5b289ac33d87bf19088e713f792db0d5f0cb34 Mon Sep 17 00:00:00 2001 From: Rafael Richards Date: Mon, 15 Jun 2026 08:57:36 -0400 Subject: [PATCH 3/5] ci: use the reusable m-ci.yml instead of an inline job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace m-stdlib's inline Go-`m` CI job with a thin caller of the new reusable vista-cloud-dev/.github `m-ci.yml` (VSL Phase B item 4). The Makefile targets (drift gates + fmt/lint + test/coverage/ci-json) and the lint-severity gate are unchanged — m-ci.yml just provisions `m` + the engine and runs those targets. Pinned to @m-ci-reusable to prove the reusable workflow on real CI; flip to @main once the .github PR merges. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 197 +++++---------------------------------- 1 file changed, 23 insertions(+), 174 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 583c409..f375f77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,24 +1,15 @@ # m-stdlib CI — standardized onto the **Go `m`** toolchain -# (github.com/vista-cloud-dev/m-cli), replacing the legacy Python m-cli -# (github.com/m-dev-tools/m-cli). One binary runs fmt / lint / test / coverage; -# the same `m arch check` already powers the shared waterline gate. +# (github.com/vista-cloud-dev/m-cli), via the reusable **m-ci.yml** +# (vista-cloud-dev/.github). The reusable workflow provisions `m` + the +# m-test-engine container; the actual gating runs THIS repo's Makefile targets +# (engine-free: drift gates + fmt/lint; engine-bound: test/coverage/ci-json), +# so all repo-specific knowledge (core suites, byte-mode `--chset m`, +# `--routines src`, the lint-severity gate, optional-module exclusions, the 85% +# aggregate coverage gate) stays in the Makefile — shared by CI and local `make`. # -# Engine transport (the spike's real unknown — settled here): -# We run on a plain `ubuntu-latest` runner and bring up the **m-test-engine** -# YottaDB container, then drive the Go `m` over `--docker m-test-engine` -# (Option 2 in the spike). This is the path proven in local dev — there is no -# host YDB on the dev box either, so `--docker` is the de-risked, identical -# invocation. (Option 1 — running INSIDE yottadb/yottadb-base with the Go `m` -# LocalEngine — would also work but is unproven with the Go `m`'s `ydb`-binary -# resolution; we chose the proven path.) -# -# The Python DRIFT-GATE GENERATORS stay Python: tools/gen-manifest.py / -# gen-skill.py / gen-doctests.py are not the M toolchain. They are pure-stdlib -# (argparse/json/re/pathlib) so the ubuntu runner's system python3 suffices — no -# pip install, no container. -# -# IRIS portability (auxiliary) remains a fail-soft (continue-on-error) job: -# it surfaces regressions without gating merges. +# The Python drift-gate generators (tools/gen-*.py) stay Python and run as the +# `check-manifest`/`skill-check`/`doctest-check`/`check-docs-prose` engine-free +# targets below. name: CI on: @@ -26,162 +17,20 @@ on: branches: [main] pull_request: -permissions: - contents: read - -env: - # Ref of vista-cloud-dev/m-cli to build the Go `m` from. Pin to a tag at - # Phase B item 5 (tag m-cli + pin m-cli-ref); `main` until then. Matches the - # arch-waterline gate's clone-and-build (proxy-lag-immune — see that workflow). - M_CLI_REF: main - GO_VERSION: "1.26.x" - jobs: - # m/v waterline gate (G1–G4 + meta-shape) — reusable org workflow, builds its - # own `m` from m-cli and runs `m arch check .`. m-stdlib is layer m. + # m/v waterline gate (G1–G4 + meta-shape) — m-stdlib is layer m. arch: uses: vista-cloud-dev/.github/.github/workflows/arch-waterline.yml@main - m-stdlib: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GO_VERSION }} - check-latest: true - - - name: Build the Go `m` toolchain from m-cli - # Clone + build rather than `go install …@${ref}`: the module proxy's - # branch→commit cache can lag a fresh m-cli merge by minutes, which would - # build a stale `m`. A shallow clone uses the exact branch/tag HEAD - # immediately (same rationale as arch-waterline.yml). CGO_ENABLED=0 makes - # a static binary that also runs inside any container if ever needed. - run: | - git clone --depth 1 --branch "$M_CLI_REF" \ - https://github.com/vista-cloud-dev/m-cli "$RUNNER_TEMP/m-cli" - ( cd "$RUNNER_TEMP/m-cli" && CGO_ENABLED=0 go build -trimpath -o "$RUNNER_TEMP/m" . ) - echo "$RUNNER_TEMP" >> "$GITHUB_PATH" - "$RUNNER_TEMP/m" version || true - - # ── Engine-free drift gates (Python generators — unchanged) ──────────── - # check-manifest also asserts dist/repo.meta.json is tracked + clean (the - # tier-1 contract the org catalog generator fetches by raw URL). - - name: Manifest drift check - run: make check-manifest - - - name: AI skill drift check - run: make skill-check - - - name: Doctest drift check - run: make doctest-check - - - name: docs/ prose-only gate - run: make check-docs-prose - - # ── Engine-free Go `m` gates ─────────────────────────────────────────── - - name: Format check - run: make fmt-check - - - name: Lint - # House gate = zero error-severity findings (scripts/m-lint-gate.sh); - # .m-cli.toml pins [lint] target-engine = "any", so this is also the - # cross-engine portability lint. - run: make lint - - # ── Engine-bound: m-test-engine (YottaDB) over `--docker` ────────────── - - name: Start m-test-engine (YottaDB) - run: | - docker run -d --name m-test-engine \ - ghcr.io/m-dev-tools/m-test-engine:0.1.0 - echo "waiting for m-test-engine to report healthy…" - for _ in $(seq 1 60); do - status="$(docker inspect -f '{{.State.Health.Status}}' m-test-engine 2>/dev/null || echo starting)" - echo " health: $status" - [ "$status" = healthy ] && break - sleep 2 - done - test "$(docker inspect -f '{{.State.Health.Status}}' m-test-engine)" = healthy - - - name: Test (byte mode, core suites) - # make test = m test --engine ydb --docker m-test-engine --chset m - # --routines src $(CORE_SUITES) - run: make test - - - name: Coverage (aggregate ≥85%, LCOV) - # make coverage = m coverage --min-percent=85 --lcov coverage.lcov - # $(CORE_SRC) $(CORE_SUITES) — aggregate gate + tracefile. - run: make coverage - - - name: Machine-readable test + coverage JSON - # Reuses the Makefile's CORE_SRC/CORE_SUITES (no gate re-run, no duplicated - # optional-module exclusion logic). - run: make ci-json - - - name: Upload test + coverage artifacts - if: always() - uses: actions/upload-artifact@v4 - with: - name: m-stdlib-ci-results - path: | - test-results.json - coverage.json - coverage.lcov - if-no-files-found: ignore - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 - with: - files: coverage.lcov - fail_ci_if_error: false # flip to true once the corpus is non-empty - - - name: Stop m-test-engine - if: always() - run: docker rm -f m-test-engine || true - - iris-portability-check: - # Auxiliary IRIS portability surfacing — fail-soft (continue-on-error), PRs - # only. Brings up IRIS Community over `--docker` and runs the core suites via - # the Go `m`'s IRIS runner. A red run never blocks a merge; the job status + - # artifact make a portability regression visible. (The engine-agnostic fmt + - # target-engine=any lint already run in the gating job above.) - if: github.event_name == 'pull_request' - runs-on: ubuntu-latest - continue-on-error: true - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GO_VERSION }} - check-latest: true - - - name: Build the Go `m` toolchain from m-cli - run: | - git clone --depth 1 --branch "$M_CLI_REF" \ - https://github.com/vista-cloud-dev/m-cli "$RUNNER_TEMP/m-cli" - ( cd "$RUNNER_TEMP/m-cli" && CGO_ENABLED=0 go build -trimpath -o "$RUNNER_TEMP/m" . ) - echo "$RUNNER_TEMP" >> "$GITHUB_PATH" - - - name: Start IRIS Community - run: | - docker run -d --name m-test-iris \ - intersystemsdc/iris-community:latest - echo "waiting for IRIS to report healthy…" - for _ in $(seq 1 90); do - status="$(docker inspect -f '{{.State.Health.Status}}' m-test-iris 2>/dev/null || echo starting)" - echo " health: $status" - [ "$status" = healthy ] && break - sleep 2 - done - docker inspect -f '{{.State.Health.Status}}' m-test-iris - - - name: Test under IRIS (best-effort) - # IRIS byte mode is inherent (--chset is a no-op); namespace USER. Reuses - # the Makefile's core-suite list via M_ENGINE_FLAGS. - run: make test M_ENGINE_FLAGS='--engine iris --docker m-test-iris --namespace USER' - - - name: Stop IRIS - if: always() - run: docker rm -f m-test-iris || true + # M-library CI on the Go `m` (build m → engine-free gates → m-test-engine → + # test/coverage → fail-soft IRIS). + ci: + # NOTE: pinned to the @m-ci-reusable feature branch to PROVE the new reusable + # workflow on real CI. Flip to @main once the vista-cloud-dev/.github PR + # (m-ci.yml) merges — see this PR's description / the VSL Phase B tracker. + uses: vista-cloud-dev/.github/.github/workflows/m-ci.yml@m-ci-reusable + with: + engine-free-targets: "check-manifest skill-check doctest-check check-docs-prose fmt-check lint" + engine-targets: "test coverage ci-json" + codecov: true + run-iris: true From a617d5bbdd8fffcb7a636174601a765543b2493b Mon Sep 17 00:00:00 2001 From: Rafael Richards Date: Mon, 15 Jun 2026 09:02:19 -0400 Subject: [PATCH 4/5] docs(memory): note the move to the reusable m-ci.yml caller Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/memory/ci-go-m-toolchain.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/memory/ci-go-m-toolchain.md b/docs/memory/ci-go-m-toolchain.md index 684a79d..72a1ede 100644 --- a/docs/memory/ci-go-m-toolchain.md +++ b/docs/memory/ci-go-m-toolchain.md @@ -62,6 +62,17 @@ portability failure surfaced — the intended signal). Far better than the legac Python IRIS runner. See [[dual-engine-validation-2026-06-14]] for IRIS-runner gotchas; [[iris-native-backends]] for the `--chset m` rebuild note. +**Now a thin caller of the reusable `m-ci.yml`** (2026-06-15, same PR #12): +`.github/workflows/ci.yml` was refactored from an inline job to a one-block +caller of `vista-cloud-dev/.github` **`m-ci.yml`** (VSL Phase B item 4) — that +reusable workflow provisions `m` + the engine container and runs THIS repo's +Makefile targets (`engine-free-targets` = check-manifest/skill-check/doctest-check/ +check-docs-prose/fmt-check/lint; `engine-targets` = test/coverage/ci-json; +`run-iris: true`). All the reconciliations above stay in the Makefile (that's the +point — the workflow is repo-agnostic). Pinned `@m-ci-reusable` to prove it; +**flip → `@main` after the `.github` PR merges.** Detail → shared memory +`m-ci-reusable-workflow` (docs repo). + **Branch-base note:** built off `master`, so it carries the `dist/repo.meta.json` (pre-Phase-B-item-1) location and `make check-manifest` form. The `stdseed-engine-neutral-g2` branch (meta-to-root move + STDSEED G2) is a SEPARATE From fa433aa819479aec956aca605455900e9083384f Mon Sep 17 00:00:00 2001 From: Rafael Richards Date: Mon, 15 Jun 2026 09:18:19 -0400 Subject: [PATCH 5/5] ci: pin m-ci.yml reusable workflow to @main The vista-cloud-dev/.github m-ci.yml PR (#4) merged, so reference the stable @main ref instead of the @m-ci-reusable feature branch. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f375f77..e84a613 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,10 +25,7 @@ jobs: # M-library CI on the Go `m` (build m → engine-free gates → m-test-engine → # test/coverage → fail-soft IRIS). ci: - # NOTE: pinned to the @m-ci-reusable feature branch to PROVE the new reusable - # workflow on real CI. Flip to @main once the vista-cloud-dev/.github PR - # (m-ci.yml) merges — see this PR's description / the VSL Phase B tracker. - uses: vista-cloud-dev/.github/.github/workflows/m-ci.yml@m-ci-reusable + uses: vista-cloud-dev/.github/.github/workflows/m-ci.yml@main with: engine-free-targets: "check-manifest skill-check doctest-check check-docs-prose fmt-check lint" engine-targets: "test coverage ci-json"