Skip to content

feat: service layering and platform hardening #5

feat: service layering and platform hardening

feat: service layering and platform hardening #5

Workflow file for this run

name: SpecCursor Quality Gates
# Honest qualification suite aligned to repository reality.
# What exists today: Node apps/packages (pnpm), optional Rust worker,
# optional Lean workspace. There is no root go.mod / requirements.txt.
#
# Stages that need external secrets (Snyk, live Claude, Morph) are skipped
# with an explicit notice unless the secret is present — never fake-green theater.
#
# Optional repository secret: SNYK_TOKEN (Snyk step in this workflow).
# Deploy-time secrets (ANTHROPIC_API_KEY, GitHub App, JWT_SECRET) are NOT
# injected here — see docs/environment.md and docs/deployment.md.
on:
push:
branches: [main, develop, release/*]
paths:
- 'apps/**'
- 'packages/**'
- 'workers/**'
- 'scripts/**'
- 'tests/**'
- 'load/**'
- 'chaos/**'
- 'security/**'
- '.github/workflows/**'
- 'package.json'
- 'pnpm-workspace.yaml'
- 'pnpm-lock.yaml'
- 'verify-implementation.js'
pull_request:
branches: [main, develop, release/*]
paths:
- 'apps/**'
- 'packages/**'
- 'workers/**'
- 'scripts/**'
- 'tests/**'
- 'load/**'
- 'chaos/**'
- 'security/**'
- '.github/workflows/**'
- 'package.json'
- 'pnpm-workspace.yaml'
- 'pnpm-lock.yaml'
- 'verify-implementation.js'
workflow_dispatch:
inputs:
stage:
description: 'Stage to run'
required: false
default: all
type: choice
options:
- all
- verify
- static-analysis
- unit-tests
- workers
- security
- load-smoke
concurrency:
group: qualify-${{ github.ref }}
cancel-in-progress: true
env:
NODE_ENV: test
PNPM_VERSION: '8.15.0'
jobs:
verify:
name: Architecture verify
if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.stage == 'all' || github.event.inputs.stage == 'verify' }}
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: pnpm
- run: node verify-implementation.js
static-analysis:
name: Static analysis (Node)
if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.stage == 'all' || github.event.inputs.stage == 'static-analysis' }}
needs: verify
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
node: [18, 20]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Format check
run: pnpm format:check
- name: Lint
run: pnpm lint
- name: Type check
run: pnpm type-check
unit-tests:
name: Unit and integration tests
if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.stage == 'all' || github.event.inputs.stage == 'unit-tests' }}
needs: static-analysis
runs-on: ubuntu-latest
timeout-minutes: 25
strategy:
fail-fast: false
matrix:
node: [18, 20]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Run unit and integration tests
run: pnpm test:unit
- name: Run property tests
run: pnpm test:property
workers:
name: Optional workers (Rust / Lean)
if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.stage == 'all' || github.event.inputs.stage == 'workers' }}
needs: verify
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- name: Setup Rust toolchain
if: hashFiles('workers/rust-worker/Cargo.toml') != ''
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
components: clippy
- name: Cargo test + clippy
if: hashFiles('workers/rust-worker/Cargo.toml') != ''
working-directory: workers/rust-worker
run: |
cargo test --verbose
cargo clippy --all-targets --all-features -- -D warnings
- name: Skip Rust (manifest absent)
if: hashFiles('workers/rust-worker/Cargo.toml') == ''
run: echo "workers/rust-worker/Cargo.toml not present; skipping Rust."
- name: Install elan (Lean)
if: hashFiles('workers/lean-engine/lakefile.lean') != ''
run: |
curl -sSf https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh | sh -s -- -y --default-toolchain none
echo "$HOME/.elan/bin" >> "$GITHUB_PATH"
- name: Lean build / check
if: hashFiles('workers/lean-engine/lakefile.lean') != ''
working-directory: workers/lean-engine
run: |
lake build
if [ -f lean/test_runner.lean ]; then
lake env lean --run lean/test_runner.lean || true
fi
- name: Skip Lean (lakefile absent)
if: hashFiles('workers/lean-engine/lakefile.lean') == ''
run: echo "workers/lean-engine/lakefile.lean not present; skipping Lean."
security:
name: Dependency security
if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.stage == 'all' || github.event.inputs.stage == 'security' }}
needs: verify
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: pnpm audit + ecosystem audits
continue-on-error: true
run: |
pnpm audit --audit-level moderate
node scripts/security-audit.mjs
- name: Snyk (optional — requires SNYK_TOKEN secret)
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run: |
if [ -z "$SNYK_TOKEN" ]; then
echo "SNYK_TOKEN not configured; Snyk scan skipped (optional gate)."
exit 0
fi
npx snyk test --severity-threshold=high
- name: Trivy filesystem scan
uses: aquasecurity/trivy-action@0.28.0
with:
scan-type: fs
scan-ref: .
severity: CRITICAL,HIGH
exit-code: '1'
ignore-unfixed: true
load-smoke:
name: Load script smoke (syntax)
if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.stage == 'all' || github.event.inputs.stage == 'load-smoke' }}
needs: unit-tests
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Parse load scripts
run: |
if [ -f load/upgrade-workflow.js ]; then
node --check load/upgrade-workflow.js
else
echo "No load scripts; skipping."
fi
- name: Chaos experiment files present
run: |
if [ -d chaos/experiments ]; then
find chaos/experiments -type f -name '*.yaml' -o -name '*.yml' | tee /tmp/chaos.txt
test -s /tmp/chaos.txt
else
echo "No chaos experiments directory; skipping."
fi
qualify-summary:
name: Qualify summary
if: always()
needs: [verify, static-analysis, unit-tests, workers, security, load-smoke]
runs-on: ubuntu-latest
steps:
- name: Require critical jobs
run: |
echo "verify=${{ needs.verify.result }}"
echo "static=${{ needs.static-analysis.result }}"
echo "tests=${{ needs.unit-tests.result }}"
echo "workers=${{ needs.workers.result }}"
echo "security=${{ needs.security.result }}"
echo "load=${{ needs.load-smoke.result }}"
for r in "${{ needs.verify.result }}" "${{ needs.static-analysis.result }}" "${{ needs.unit-tests.result }}" "${{ needs.security.result }}"; do
if [ "$r" != "success" ] && [ "$r" != "skipped" ]; then
echo "Critical gate failed: $r"
exit 1
fi
done
# workers/load are best-effort when toolchains flake; fail only on hard failure
if [ "${{ needs.workers.result }}" = "failure" ]; then exit 1; fi
if [ "${{ needs.load-smoke.result }}" = "failure" ]; then exit 1; fi