Skip to content

TAN-824: establish solution planning contract and correct CI metadata #2695

TAN-824: establish solution planning contract and correct CI metadata

TAN-824: establish solution planning contract and correct CI metadata #2695

Workflow file for this run

name: CI
on:
push:
branches: [main, feat/engine]
pull_request:
branches: [main, feat/engine]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
DOCS_SITE_URL: https://docs.tandem.ac/
DOCS_BASE_PATH: /
# Superseded PR commits share a group; pushes, schedules and manual runs are independent.
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
jobs:
lint-frontend:
name: Lint Frontend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Setup pnpm
uses: pnpm/action-setup@eae0cfeb286e66ffb5155f1a79b90583a127a68b # v2
with:
version: 10.34.5
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 20
cache: pnpm
cache-dependency-path: apps/tandem-desktop/pnpm-lock.yaml
- name: Install desktop dependencies
run: pnpm -C apps/tandem-desktop install --frozen-lockfile
- name: Run desktop TypeScript check
run: pnpm -C apps/tandem-desktop exec tsc --noEmit
- name: Run desktop blackboard tests
run: pnpm -C apps/tandem-desktop test:blackboard
- name: Validate i18n parity
run: pnpm i18n:parity
- name: Validate Incident Monitor external log fixture
run: pnpm incident-monitor:fixture:test
- name: Validate Incident Monitor terminology
run: pnpm incident-monitor:terminology
- name: Run linter
run: pnpm -C apps/tandem-desktop lint
test-control-panel:
name: Test Control Panel
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Setup pnpm
uses: pnpm/action-setup@eae0cfeb286e66ffb5155f1a79b90583a127a68b # v2
with:
version: 10.34.5
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24
cache: pnpm
cache-dependency-path: packages/tandem-control-panel/pnpm-lock.yaml
- name: Install control panel dependencies
run: pnpm -C packages/tandem-control-panel install --frozen-lockfile
- name: Install Playwright Chromium
run: pnpm -C packages/tandem-control-panel exec playwright install --with-deps chromium
- name: Typecheck control panel
run: pnpm -C packages/tandem-control-panel typecheck
- name: Build control panel
run: pnpm -C packages/tandem-control-panel build
- name: Run control panel contract tests
run: pnpm -C packages/tandem-control-panel test
- name: Run control panel Playwright and accessibility tests
run: pnpm -C packages/tandem-control-panel exec playwright test --retries=0
- name: Pack and verify control panel release archive
shell: bash
run: |
set -euo pipefail
package_dir="$RUNNER_TEMP/control-panel-package"
mkdir -p "$package_dir"
npm pack --ignore-scripts \
--pack-destination "$package_dir" \
./packages/tandem-control-panel
archive="$(find "$package_dir" -maxdepth 1 -name '*.tgz' -print -quit)"
bash scripts/verify-release-archive.sh control-panel "$archive"
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: control-panel-playwright-report
path: packages/tandem-control-panel/playwright-report
if-no-files-found: ignore
retention-days: 14
- name: Upload Playwright failure artifacts
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: control-panel-playwright-failures
path: packages/tandem-control-panel/test-results
if-no-files-found: ignore
retention-days: 14
- name: Upload control panel release archive
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: control-panel-release-package
path: ${{ runner.temp }}/control-panel-package/*.tgz
if-no-files-found: error
retention-days: 7
audit-javascript-lockfiles:
name: Audit JavaScript Lockfiles
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Setup pnpm
uses: pnpm/action-setup@eae0cfeb286e66ffb5155f1a79b90583a127a68b # v2
with:
version: 10.34.5
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24
- name: Audit every JavaScript lockfile
run: node scripts/audit-javascript-workspaces.mjs
lint-backend:
name: Backend Rust Gates
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 2
- name: Verify release artifact and workflow security policy
run: |
node --test scripts/generate-artifact-manifest.test.mjs scripts/verify-workflow-security.test.mjs
node scripts/verify-workflow-security.mjs --self-test
- name: Verify route policy and focused security retest inventories
run: |
node --test scripts/verify-route-policy-inventory.test.mjs scripts/verify-security-retest-matrix.test.mjs
node scripts/verify-route-policy-inventory.mjs --self-test
node scripts/verify-route-policy-inventory.mjs
node scripts/verify-security-retest-matrix.mjs --self-test
node scripts/verify-security-retest-matrix.mjs
- name: Check touched Rust/TSX file sizes
run: bash scripts/ci-file-size-check.sh
- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libappindicator3-dev \
librsvg2-dev \
libglib2.0-dev \
pkg-config \
build-essential
- name: Setup shared Rust CI
uses: ./.github/actions/setup-rust-ci
with:
cache-profile: desktop-default
components: rustfmt, clippy
workspaces: |
. -> target
apps/tandem-desktop/src-tauri -> apps/tandem-desktop/src-tauri/target
- name: Check formatting
run: cargo fmt --manifest-path apps/tandem-desktop/src-tauri/Cargo.toml -- --check
- name: Run clippy
run: cargo clippy --manifest-path apps/tandem-desktop/src-tauri/Cargo.toml -- -D warnings || true
- name: Run desktop Rust tests
run: cargo test --manifest-path apps/tandem-desktop/src-tauri/Cargo.toml
- name: Verify memory DB blast-radius boundary
run: node scripts/verify-memory-db-blast-radius.mjs
- name: Verify memory key lifecycle evidence
run: node scripts/verify-memory-key-lifecycle.mjs
- name: Check tandem-server production panic surface
run: |
node scripts/check-rust-panic-surface.mjs --self-test
node scripts/check-rust-panic-surface.mjs
- name: Verify provider egress boundary guard
run: |
node scripts/verify-provider-egress-boundary.mjs --self-test
node scripts/verify-provider-egress-boundary.mjs
- name: Verify protected audit results are handled
run: |
node scripts/verify-protected-audit-results.mjs --self-test
node scripts/verify-protected-audit-results.mjs
- name: Check tandem-server panic lints
run: cargo clippy -p tandem-server --lib --no-deps -- -A warnings
- name: Verify public/enterprise-lite/enterprise-full build modes
run: bash scripts/check-build-modes.sh
test-sdks:
name: Test SDKs
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Setup pnpm
uses: pnpm/action-setup@eae0cfeb286e66ffb5155f1a79b90583a127a68b # v2
with:
version: 10.34.5
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 20
cache: pnpm
cache-dependency-path: packages/tandem-client-ts/pnpm-lock.yaml
- name: Install TS SDK dependencies
working-directory: packages/tandem-client-ts
run: pnpm install --frozen-lockfile
- name: Typecheck TS SDK
working-directory: packages/tandem-client-ts
run: pnpm typecheck
- name: Build TS SDK
working-directory: packages/tandem-client-ts
run: pnpm build
- name: Test TS SDK
working-directory: packages/tandem-client-ts
run: pnpm test
- name: Smoke test TS SDK module formats
working-directory: packages/tandem-client-ts
run: |
node --input-type=module -e "await import('./dist/index.js')"
node -e "require('./dist/index.cjs')"
- name: Pack and verify TS SDK release archive
shell: bash
run: |
set -euo pipefail
package_dir="$RUNNER_TEMP/sdk-release-packages"
mkdir -p "$package_dir"
npm pack --ignore-scripts \
--pack-destination "$package_dir" \
./packages/tandem-client-ts
archive="$(find "$package_dir" -maxdepth 1 -name '*.tgz' -print -quit)"
bash scripts/verify-release-archive.sh ts-sdk "$archive"
- name: Setup Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- name: Install Python dependencies
working-directory: packages/tandem-client-py
run: python -m pip install --disable-pip-version-check build ".[dev]"
- name: Test Python SDK
working-directory: packages/tandem-client-py
run: python -m pytest tests/
- name: Build and verify Python SDK release archives
working-directory: packages/tandem-client-py
shell: bash
run: |
set -euo pipefail
python -m build
wheel="$(find dist -maxdepth 1 -name '*.whl' -print -quit)"
sdist="$(find dist -maxdepth 1 -name '*.tar.gz' -print -quit)"
bash ../../scripts/verify-release-archive.sh python-sdk "$wheel"
bash ../../scripts/verify-release-archive.sh python-sdist "$sdist"
- name: Upload SDK release archives
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: sdk-release-packages
path: |
${{ runner.temp }}/sdk-release-packages/*.tgz
packages/tandem-client-py/dist/*
if-no-files-found: error
retention-days: 7
test-postgres-storage:
name: Test PostgreSQL Storage
runs-on: ubuntu-22.04
services:
postgres:
image: pgvector/pgvector:pg16
env:
POSTGRES_PASSWORD: tandem
POSTGRES_DB: tandem
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres -d tandem"
--health-interval 5s
--health-timeout 5s
--health-retries 10
env:
TANDEM_TEST_POSTGRES_URL: postgres://postgres:tandem@localhost:5432/tandem
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Setup shared Rust CI
uses: ./.github/actions/setup-rust-ci
with:
cache-profile: postgres-storage
components: clippy
- name: Run PostgreSQL memory contract tests
run: cargo test -p tandem-memory --features postgres postgres_store::tests -- --test-threads=1
- name: Lint PostgreSQL memory backend
run: cargo clippy -p tandem-memory --features postgres --lib -- -D warnings
- name: Run SQLite-only storage conformance and scale gates
run: cargo test -p tandem-server --no-default-features --features storage-sqlite backend_conformance_tests -- --test-threads=1
- name: Require PostgreSQL conformance and scale test discovery
run: |
test -n "$TANDEM_TEST_POSTGRES_URL"
cargo test -p tandem-server --no-default-features --features storage-postgres \
high_volume_event_replay_stays_bounded_per_goal -- --list \
| grep -F 'high_volume_event_replay_stays_bounded_per_goal'
- name: Run PostgreSQL-only storage conformance and scale gates
run: cargo test -p tandem-server --no-default-features --features storage-postgres backend_conformance_tests -- --test-threads=1
- name: Run verified SQLite to PostgreSQL round-trip migration
run: cargo test -p tandem-server --features storage-postgres stateful_runtime::orchestration_store::transfer -- --test-threads=1
- name: Run storage backend unit tests
run: cargo test -p tandem-server --features storage-postgres stateful_runtime::backend -- --test-threads=1
- name: Check the sqlite-only stateful backend build
run: cargo test -p tandem-server --no-default-features --features storage-sqlite server_context_installs_deny_capable_policy_and_real_ledger --lib
- name: Check the postgres-only stateful backend build
run: cargo test -p tandem-server --no-default-features --features storage-postgres server_context_installs_deny_capable_policy_and_real_ledger --lib
- name: Check the combined stateful backend build
run: cargo test -p tandem-server --no-default-features --features storage-sqlite,storage-postgres server_context_installs_deny_capable_policy_and_real_ledger --lib
- name: Lint the PostgreSQL-only stateful backend
run: cargo clippy -p tandem-server --no-default-features --features storage-postgres --lib -- -D warnings
- name: Check the migration CLI with PostgreSQL support
run: cargo check -p tandem-ai --features storage-postgres
build:
name: Build (${{ matrix.platform }})
# Only run builds on manual trigger or workflow_dispatch
if: github.event_name == 'workflow_dispatch'
needs: [lint-frontend, lint-backend]
strategy:
fail-fast: false
matrix:
include:
- platform: ubuntu-22.04
target: x86_64-unknown-linux-gnu
- platform: macos-latest
target: x86_64-apple-darwin
- platform: macos-latest
target: aarch64-apple-darwin
- platform: windows-latest
target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Setup pnpm
uses: pnpm/action-setup@eae0cfeb286e66ffb5155f1a79b90583a127a68b # v2
with:
version: 10.34.5
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 20
cache: pnpm
cache-dependency-path: apps/tandem-desktop/pnpm-lock.yaml
- name: Setup shared Rust CI
uses: ./.github/actions/setup-rust-ci
with:
cache-profile: desktop-release-${{ matrix.target }}
targets: ${{ matrix.target }}
workspaces: apps/tandem-desktop/src-tauri -> apps/tandem-desktop/src-tauri/target
- name: Install Linux dependencies
if: matrix.platform == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libappindicator3-dev \
librsvg2-dev \
patchelf \
pkg-config \
build-essential
- name: Install dependencies
run: pnpm -C apps/tandem-desktop install
- name: Build Tauri app
uses: tauri-apps/tauri-action@fce9c6108b31ea247710505d3aaaa893ee6768d4 # v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
with:
projectPath: apps/tandem-desktop
args: --target ${{ matrix.target }}
- name: Verify macOS DMG (Gatekeeper)
if: matrix.platform == 'macos-latest'
continue-on-error: true
shell: bash
run: |
set -euo pipefail
DMG=""
for p in \
"apps/tandem-desktop/src-tauri/target/${{ matrix.target }}/release/bundle/dmg" \
"apps/tandem-desktop/src-tauri/target/release/bundle/dmg" \
; do
if [ -d "$p" ]; then
DMG="$(ls -1 "$p"/*.dmg 2>/dev/null | head -n 1 || true)"
if [ -n "$DMG" ]; then break; fi
fi
done
if [ -z "$DMG" ]; then
DMG="$(find apps/tandem-desktop/src-tauri/target -path "*/bundle/dmg/*.dmg" -maxdepth 6 -print 2>/dev/null | head -n 1 || true)"
fi
if [ -z "$DMG" ]; then
echo "No DMG found."
exit 1
fi
echo "DMG: $DMG"
MOUNT="$RUNNER_TEMP/tandem-dmg-mount"
mkdir -p "$MOUNT"
hdiutil attach -nobrowse -readonly -mountpoint "$MOUNT" "$DMG"
APP="$(find "$MOUNT" -maxdepth 2 -name "*.app" -print | head -n 1 || true)"
if [ -z "$APP" ]; then
echo "No .app found in mounted DMG."
hdiutil detach "$MOUNT" || true
exit 1
fi
echo "App: $APP"
codesign --verify --deep --strict --verbose=2 "$APP"
spctl -a -vvv --type execute "$APP"
hdiutil detach "$MOUNT"
- name: Upload artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: tandem-${{ matrix.target }}
path: |
apps/tandem-desktop/src-tauri/target/${{ matrix.target }}/release/bundle/
if-no-files-found: ignore
dco:
name: DCO Sign-Off
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 0
- name: Check Developer Certificate of Origin sign-offs
run: node scripts/check-dco.mjs
docs-validate:
name: Validate Docs
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Setup pnpm
uses: pnpm/action-setup@eae0cfeb286e66ffb5155f1a79b90583a127a68b # v2
with:
version: 10.34.5
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24
cache: pnpm
cache-dependency-path: guide/pnpm-lock.yaml
- name: Install docs dependencies
working-directory: guide
run: pnpm install --frozen-lockfile
- name: Validate docs parity
run: node scripts/verify-docs-parity.mjs
- name: Verify license map
run: node scripts/verify-license-map.mjs
- name: Install ripgrep
run: sudo apt-get update && sudo apt-get install --yes --no-install-recommends ripgrep
- name: Verify licensing terms
run: node scripts/verify-licensing-terms.mjs
- name: Type check docs
working-directory: guide
run: pnpm check
- name: Build docs
working-directory: guide
env:
DOCS_SITE_URL: ${{ env.DOCS_SITE_URL }}
DOCS_BASE_PATH: ${{ env.DOCS_BASE_PATH }}
run: pnpm build
- name: Validate built docs links and assets
run: node scripts/verify-built-docs-links.mjs guide/dist artifacts/docs-link-report.json
- name: Validate docs under a non-root base path
env:
DOCS_BASE_PATH: /docs/
run: |
npm --prefix guide run build
node scripts/verify-built-docs-links.mjs guide/dist artifacts/docs-link-report-subpath.json
- name: Upload docs validation report
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: docs-link-validation
path: artifacts/docs-link-report*.json
if-no-files-found: ignore
retention-days: 7
docs:
name: Deploy Docs
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
runs-on: ubuntu-latest
needs: [lint-frontend, docs-validate]
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Setup pnpm
uses: pnpm/action-setup@eae0cfeb286e66ffb5155f1a79b90583a127a68b # v2
with:
version: 10.34.5
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24
cache: pnpm
cache-dependency-path: guide/pnpm-lock.yaml
- name: Configure Pages
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
with:
enablement: true
- name: Install dependencies
working-directory: guide
run: pnpm install --frozen-lockfile
- name: Validate docs parity
run: node scripts/verify-docs-parity.mjs
- name: Build docs
working-directory: guide
env:
DOCS_SITE_URL: ${{ env.DOCS_SITE_URL }}
DOCS_BASE_PATH: ${{ env.DOCS_BASE_PATH }}
run: pnpm build
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
with:
path: guide/dist
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4