th-cc50cd: OpenCode lifecycle plugin — sessions on the th-mail bus #1375
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: PR Checks | |
| on: | |
| pull_request: | |
| branches: [main] | |
| # Also fire on direct push to main so the branch carries a | |
| # completed-check-run history. Required by branch-protection's | |
| # "require status checks" rule — without push triggering these, | |
| # an admin-bypass hotfix lands with no check history. Pearl | |
| # th-e392d9. | |
| push: | |
| branches: [main] | |
| merge_group: | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true' | |
| # dorny/paths-filter queries a PR's changed files through the API. | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| jobs: | |
| rust: | |
| # Pearl th-a165b4: Windows joins the test matrix so cross-platform | |
| # breakage is caught at PR time rather than at release time. Linux | |
| # stays the canonical gate — it alone runs fmt/clippy/release-build | |
| # (those are platform-independent, so duplicating them just burns | |
| # Windows minutes, which bill at 2x). | |
| name: Rust checks (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 45 | |
| strategy: | |
| # Report both platforms on a failure — knowing whether a break is | |
| # Windows-only or universal is the whole point of the matrix. | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| test_args: '' | |
| # smooth-daemon JOINED this lane in th-a59af5. It is Big | |
| # Smooth itself, so excluding it meant the actual product | |
| # was never compiled — let alone tested — on a platform we | |
| # intend to ship to, and every Windows break in it landed | |
| # green. The blocker was one leaf dependency: web-push -> | |
| # ece -> openssl-sys, and windows-latest has no OpenSSL (no | |
| # vcpkg tree, no OPENSSL_DIR). `web-push` is now declared | |
| # only for non-Windows targets, so Windows gives up phone | |
| # notifications (/push/* 503s there) and gains the whole | |
| # agent engine. Do not re-add the exclude to make a red | |
| # Windows build green — that is the bug it was hiding. | |
| # | |
| # smooth-diver's e2e and six smooth-pearls test modules | |
| # drive a real pearl store, which shells out to the | |
| # smooth-dolt binary — a CGO Go binary needing ICU, with no | |
| # Windows build (see the skipped build step above). They are | |
| # filtered out by name rather than passing vacuously. The | |
| # rest of smooth-pearls (dolt.rs, registry, the non-Unix | |
| # dolt_server stub) still runs here. | |
| - os: windows-latest | |
| test_args: >- | |
| --workspace | |
| --exclude smooai-smooth-diver | |
| -E 'not (package(smooai-smooth-pearls) & test(/^(agents|memory|memory_tools|messaging|store|tools)::/))' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # th-6578ee: a bash/JSON/doc-only PR was compiling the whole Rust | |
| # workspace on TWO platforms (Windows bills at 2x) plus a full web | |
| # build — ~45 min of runner time to validate a shell script. | |
| # | |
| # The filter gates STEPS, not the job. The job must still run and | |
| # report: branch protection requires these checks, and a workflow- | |
| # level `paths:` would make them never appear at all, blocking the PR | |
| # forever. Same reason the `push:` trigger exists (pearl th-e392d9). | |
| # | |
| # PR events only. On push-to-main and merge_group there is no | |
| # reliable base to diff against, and those are exactly the moments a | |
| # skipped check would be expensive to be wrong about — so they run | |
| # everything. | |
| - name: Detect changes | |
| id: filter | |
| if: github.event_name == 'pull_request' | |
| uses: dorny/paths-filter@v3 | |
| with: | |
| filters: | | |
| rust: | |
| - 'crates/**' | |
| - 'Cargo.toml' | |
| - 'Cargo.lock' | |
| - 'rust-toolchain.toml' | |
| - 'rustfmt.toml' | |
| - 'go/**' | |
| - 'scripts/build-smooth-dolt.sh' | |
| - '.github/workflows/pr-checks.yml' | |
| # One boolean instead of repeating the compound condition on a dozen | |
| # steps. `shell: bash` because the Windows leg defaults to pwsh, | |
| # where `>> $GITHUB_OUTPUT` is `$env:GITHUB_OUTPUT`. | |
| - name: Gate | |
| id: gate | |
| shell: bash | |
| run: echo "run=${{ github.event_name != 'pull_request' || steps.filter.outputs.rust == 'true' }}" >>"$GITHUB_OUTPUT" | |
| - name: Install system dependencies | |
| if: steps.gate.outputs.run == 'true' && runner.os == 'Linux' | |
| run: sudo apt-get update && sudo apt-get install -y libdbus-1-dev libcap-ng-dev libicu-dev pkg-config protobuf-compiler | |
| # No apt on Windows — protoc is the only native build-time tool the | |
| # workspace needs there (tonic-build in smooth-scribe et al.). | |
| - name: Install protoc | |
| if: steps.gate.outputs.run == 'true' && runner.os == 'Windows' | |
| uses: arduino/setup-protoc@v3 | |
| with: | |
| repo-token: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: dtolnay/rust-toolchain@stable | |
| if: steps.gate.outputs.run == 'true' | |
| with: | |
| components: rustfmt, clippy | |
| - uses: Swatinem/rust-cache@v2 | |
| if: steps.gate.outputs.run == 'true' | |
| - uses: pnpm/action-setup@v4 | |
| if: steps.gate.outputs.run == 'true' | |
| - uses: actions/setup-node@v4 | |
| if: steps.gate.outputs.run == 'true' | |
| with: | |
| node-version: 22 | |
| - name: Build web UI | |
| if: steps.gate.outputs.run == 'true' | |
| shell: bash | |
| run: | | |
| cd crates/smooth-web/web | |
| pnpm install --no-frozen-lockfile | |
| pnpm build | |
| # smooth-dolt is a CGO Go binary. On Linux/macOS the gozstd + | |
| # go-icu-regex CGO deps want ICU (build-smooth-dolt.sh points CGO | |
| # at Homebrew/apt ICU). On Windows the `gms_pure_go` build tag drops | |
| # the ICU path entirely, so only a C compiler (mingw gcc, present on | |
| # the runner) is needed — proven on a Windows SSM box, see | |
| # docs/Operations/Windows-Build-Box-Runbook.md. Pearl-store *tests* | |
| # still skip on Windows (they need the Unix-socket dolt server; the | |
| # TCP transport is pearl th-5f35a5) — but the binary must keep | |
| # compiling so it can't silently regress with another unix-only call. | |
| - name: Build smooth-dolt (embedded Dolt for pearl memory tests) | |
| if: steps.gate.outputs.run == 'true' && runner.os == 'Linux' | |
| run: bash scripts/build-smooth-dolt.sh | |
| - name: Build smooth-dolt (Windows — CGO via mingw, gms_pure_go drops ICU) | |
| if: steps.gate.outputs.run == 'true' && runner.os == 'Windows' | |
| shell: bash | |
| run: cd go/smooth-dolt && CGO_ENABLED=1 go build -tags gms_pure_go -o smooth-dolt.exe . | |
| - name: Install nextest | |
| if: steps.gate.outputs.run == 'true' | |
| uses: taiki-e/install-action@nextest | |
| - name: Format check | |
| if: steps.gate.outputs.run == 'true' && runner.os == 'Linux' | |
| run: cargo fmt --check | |
| - name: Clippy | |
| if: steps.gate.outputs.run == 'true' && runner.os == 'Linux' | |
| # Honor the lint levels declared in [workspace.lints]: clippy::all | |
| # (deny) + unused_must_use/unsafe_code fail the build; pedantic and | |
| # nursery are advisory `warn` by design, so a blanket `-D warnings` | |
| # would weaponize them (and every new lint a stable Rust bump adds). | |
| run: cargo clippy | |
| - name: Build examples (needed by cross-process file-lock test, pearl th-9799fa) | |
| if: steps.gate.outputs.run == 'true' | |
| run: cargo build --examples --workspace | |
| # bash on both legs so the filter expression quotes predictably | |
| # (pwsh would need its own escaping for the `&` and parentheses). | |
| - name: Test | |
| if: steps.gate.outputs.run == 'true' | |
| shell: bash | |
| run: cargo nextest run --profile ci ${{ matrix.test_args }} | |
| env: | |
| NEXTEST_EXPERIMENTAL_LIBTEST_JSON: 1 | |
| # One report per PR — publishing from both legs would double every | |
| # entry in the check summary. | |
| - name: Publish test report | |
| if: always() && steps.gate.outputs.run == 'true' && runner.os == 'Linux' | |
| uses: ctrf-io/github-test-reporter@v1 | |
| with: | |
| report-path: target/nextest/ci/junit.xml | |
| report-format: junit | |
| continue-on-error: true | |
| - name: Build | |
| if: steps.gate.outputs.run == 'true' && runner.os == 'Linux' | |
| run: cargo build --release -p smooai-smooth-cli | |
| web: | |
| name: Web checks | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Ungated on purpose: this job is a required check, and a changeset | |
| # naming any package other than '@smooai/smooth' breaks the Release | |
| # workflow's `changeset version` on main (it has recurred three times). | |
| - name: Changeset package names | |
| run: | | |
| bad=$(grep -HEn "^['\"]?[@A-Za-z0-9/_-]+['\"]?: *(patch|minor|major)\s*$" .changeset/*.md | grep -v "@smooai/smooth" || true) | |
| if [ -n "$bad" ]; then | |
| echo "$bad" | |
| echo "::error::changesets must name '@smooai/smooth' — any other package name breaks Release's changeset version" | |
| exit 1 | |
| fi | |
| # Same step-gating rationale as the rust job above: the job must keep | |
| # reporting for branch protection, so only the work is skipped. | |
| - name: Detect changes | |
| id: filter | |
| if: github.event_name == 'pull_request' | |
| uses: dorny/paths-filter@v3 | |
| with: | |
| filters: | | |
| web: | |
| - 'crates/smooth-web/web/**' | |
| - '.github/workflows/pr-checks.yml' | |
| - name: Gate | |
| id: gate | |
| run: echo "run=${{ github.event_name != 'pull_request' || steps.filter.outputs.web == 'true' }}" >>"$GITHUB_OUTPUT" | |
| - uses: pnpm/action-setup@v4 | |
| if: steps.gate.outputs.run == 'true' | |
| - uses: actions/setup-node@v4 | |
| if: steps.gate.outputs.run == 'true' | |
| with: | |
| node-version: 22 | |
| - name: Install | |
| if: steps.gate.outputs.run == 'true' | |
| run: cd crates/smooth-web/web && pnpm install --no-frozen-lockfile | |
| - name: Build | |
| if: steps.gate.outputs.run == 'true' | |
| run: cd crates/smooth-web/web && pnpm build |