ADR-0096 (execution-surface identity admission) + fixes for the 3 confirmed-exploitable holes it surfaced (#2980, #2981, #2982) #8676
Workflow file for this run
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: CI | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| branches: | |
| - main | |
| # Superseded runs on the same PR/branch waste runners and delay feedback; | |
| # cancel them. Push runs to main group by commit ref as well, so an in-flight | |
| # main run is cancelled only by a newer main push. | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| filter: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| outputs: | |
| docs: ${{ steps.changes.outputs.docs }} | |
| core: ${{ steps.changes.outputs.core }} | |
| skilldocs: ${{ steps.changes.outputs.skilldocs }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v7 | |
| - uses: dorny/paths-filter@v4 | |
| id: changes | |
| with: | |
| filters: | | |
| docs: | |
| - 'apps/docs/**' | |
| - 'content/**' | |
| - 'pnpm-lock.yaml' | |
| - '.github/workflows/ci.yml' | |
| skilldocs: | |
| - 'skills/**' | |
| - 'content/docs/guides/skills.mdx' | |
| - 'packages/spec/scripts/build-skill-docs.ts' | |
| - '.github/workflows/ci.yml' | |
| core: | |
| - 'packages/**' | |
| - 'examples/**' | |
| - 'apps/!(docs)/**' | |
| - 'package.json' | |
| - 'pnpm-lock.yaml' | |
| - 'tsconfig.json' | |
| - '.github/workflows/ci.yml' | |
| test: | |
| name: Test Core | |
| needs: filter | |
| if: needs.filter.outputs.core == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v7 | |
| with: | |
| # Full history so `turbo --affected` can diff against the PR base. | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| - name: Enable Corepack | |
| run: corepack enable | |
| - name: Verify pnpm version | |
| run: pnpm --version | |
| - name: Get pnpm store directory | |
| shell: bash | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV | |
| - name: Setup pnpm cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: ${{ env.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-v3-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store-v3- | |
| - name: Setup Turbo cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: .turbo/cache | |
| key: ${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}-${{ github.sha }} | |
| restore-keys: | | |
| ${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}- | |
| ${{ runner.os }}-turbo-${{ github.job }}- | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # PRs: only test packages affected by the diff against the PR base. | |
| # spec sits at the root of the dependency graph, so spec-touching PRs | |
| # still run (close to) everything — but the many PRs that don't touch | |
| # spec skip the bulk of the 75-package matrix. | |
| - name: Run affected tests (PR) | |
| if: github.event_name == 'pull_request' | |
| env: | |
| TURBO_SCM_BASE: ${{ github.event.pull_request.base.sha }} | |
| run: pnpm turbo run test --affected | |
| # Push to main: full run, but exclude spec's plain test task — the | |
| # coverage step below executes the exact same 250-file spec suite once, | |
| # with coverage. Previously the spec suite ran twice per push. | |
| - name: Run all tests (push) | |
| if: github.event_name == 'push' | |
| run: pnpm turbo run test --filter=!@objectstack/spec | |
| - name: Generate coverage report | |
| if: github.event_name == 'push' | |
| run: pnpm --filter @objectstack/spec test:coverage | |
| - name: Upload coverage reports | |
| if: github.event_name == 'push' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: coverage-report | |
| path: packages/spec/coverage/ | |
| retention-days: 30 | |
| dogfood: | |
| name: Dogfood Regression Gate | |
| needs: filter | |
| if: needs.filter.outputs.core == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v7 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| - name: Enable Corepack | |
| run: corepack enable | |
| - name: Verify pnpm version | |
| run: pnpm --version | |
| - name: Get pnpm store directory | |
| shell: bash | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV | |
| - name: Setup pnpm cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: ${{ env.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-v3-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store-v3- | |
| - name: Setup Turbo cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: .turbo/cache | |
| key: ${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}-${{ github.sha }} | |
| restore-keys: | | |
| ${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}- | |
| ${{ runner.os }}-turbo-${{ github.job }}- | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # Boots real example apps in-process (in-memory SQLite) and exercises them | |
| # through the real HTTP + service stack — catches runtime regressions that | |
| # build / unit tests / spec-liveness pass over (e.g. the #2018 tz-bucketing | |
| # break, which was green on every static gate). | |
| - name: Boot example apps and exercise real user flows | |
| run: pnpm turbo run test --filter=@objectstack/dogfood | |
| # Replaces the former auto-verify dogfood tests: runs the published | |
| # `objectstack verify` engine over each example app through the CLI — | |
| # auto-derived CRUD round-trip fidelity + the cross-owner RLS invariant. | |
| # Exits non-zero on a real runtime failure, so it gates like the tests did. | |
| - name: Verify example apps via the `objectstack verify` CLI | |
| run: | | |
| pnpm turbo run build --filter=@objectstack/cli | |
| for app in examples/app-crm examples/app-showcase; do | |
| echo "::group::objectstack verify $app --rls" | |
| OS_LOG_LEVEL=error node packages/cli/bin/run.js verify --app "$app/objectstack.config.ts" --rls | |
| echo "::endgroup::" | |
| done | |
| build-core: | |
| name: Build Core | |
| needs: filter | |
| if: needs.filter.outputs.core == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v7 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| - name: Enable Corepack | |
| run: corepack enable | |
| - name: Verify pnpm version | |
| run: pnpm --version | |
| - name: Get pnpm store directory | |
| shell: bash | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV | |
| - name: Setup pnpm cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: ${{ env.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-v3-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store-v3- | |
| - name: Setup Turbo cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: .turbo/cache | |
| key: ${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}-${{ github.sha }} | |
| restore-keys: | | |
| ${{ runner.os }}-turbo-${{ github.job }}-${{ github.ref_name }}- | |
| ${{ runner.os }}-turbo-${{ github.job }}- | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build packages (excluding docs) | |
| run: pnpm build | |
| - name: Verify build outputs | |
| run: | | |
| if [ ! -d "packages/spec/dist" ]; then | |
| echo "Error: packages/spec/dist directory not found" | |
| exit 1 | |
| fi | |
| if [ ! -d "packages/spec/json-schema" ]; then | |
| echo "Error: packages/spec/json-schema directory not found" | |
| exit 1 | |
| fi | |
| echo "Build outputs verified successfully" | |
| # Capability packages (services / triggers / plugins) are loaded by the | |
| # multi-tenant runtime via a DYNAMIC import of their published entry. If a | |
| # package ships a dts-only / half-built / 0-byte `dist` (an interrupted | |
| # build, or a package retired in source but still referenced), that import | |
| # resolves to nothing and the capability SILENTLY fails to load — e.g. | |
| # record-change automation never fires, with no user-visible signal. The | |
| # build config always emits JS, so this can only happen by accident; assert | |
| # every buildable capability package actually produced its declared runtime | |
| # entry. Self-maintaining: reads each package's own `main`, skips dirs with | |
| # no package.json (e.g. a retired service-feed/service-ai leftover). | |
| - name: Verify capability packages ship a runtime entry (no dts-only / half-built) | |
| run: | | |
| fail=0 | |
| for d in packages/triggers/* packages/services/* packages/plugins/*; do | |
| [ -f "$d/package.json" ] || continue | |
| has_build=$(node -p "Boolean((require('./$d/package.json').scripts||{}).build)" 2>/dev/null || echo false) | |
| [ "$has_build" = "true" ] || continue | |
| main=$(node -p "require('./$d/package.json').main || 'dist/index.js'" 2>/dev/null || echo dist/index.js) | |
| if [ ! -s "$d/$main" ]; then | |
| echo "✗ $d: missing/empty runtime entry '$main' — dts-only or unbuilt? dynamic import would silently fail" | |
| fail=1 | |
| fi | |
| done | |
| if [ "$fail" -ne 0 ]; then exit 1; fi | |
| echo "✓ all buildable capability packages ship a runtime JS entry" | |
| - name: Analyze bundle size | |
| run: pnpm --filter @objectstack/spec analyze | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: build-output | |
| path: | | |
| packages/spec/dist/ | |
| packages/spec/json-schema/ | |
| retention-days: 30 | |
| build-docs: | |
| name: Build Docs | |
| needs: filter | |
| if: needs.filter.outputs.docs == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v7 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| - name: Enable Corepack | |
| run: corepack enable | |
| - name: Verify pnpm version | |
| run: pnpm --version | |
| - name: Get pnpm store directory | |
| shell: bash | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV | |
| - name: Setup pnpm cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: ${{ env.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-v3-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store-v3- | |
| - name: Setup Next.js cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: apps/docs/.next/cache | |
| key: ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('apps/docs/**.[jt]s', 'apps/docs/**.[jt]sx', 'content/**/*.mdx') }} | |
| restore-keys: | | |
| ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}- | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build Docs | |
| run: pnpm --filter @objectstack/docs build | |
| check-skill-docs: | |
| name: Check Skill Docs | |
| needs: filter | |
| if: needs.filter.outputs.skilldocs == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v7 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| - name: Enable Corepack | |
| run: corepack enable | |
| - name: Get pnpm store directory | |
| shell: bash | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV | |
| - name: Setup pnpm cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: ${{ env.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-v3-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store-v3- | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Check skill docs are generated from SKILL.md frontmatter | |
| run: pnpm --filter @objectstack/spec check:skill-docs |