Skip to content

feat(web): React UI v2.0 — Phase 2 Tasks 23-25 (SSE + WS + reducer) #84

feat(web): React UI v2.0 — Phase 2 Tasks 23-25 (SSE + WS + reducer)

feat(web): React UI v2.0 — Phase 2 Tasks 23-25 (SSE + WS + reducer) #84

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
quality:
name: Lint / Type-check / Test / Sonar
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0 # SonarCloud needs full git history for blame attribution
- name: Set up Python 3.11
uses: actions/setup-python@v6.2.0
with:
python-version: "3.11"
- name: Set up uv
uses: astral-sh/setup-uv@v6.7.0
with:
# Pin uv version for reproducible CI; bump deliberately when bumping locally.
version: "0.11.7"
enable-cache: true
- name: Lockfile freshness gate (HARD-02)
# Fails the build if pyproject.toml drifts from uv.lock — no silent
# resolves on CI, no surprise transitive upgrades. Phase 14 / SC-4.
run: uv lock --check
- name: Install dependencies (from lockfile)
# `--frozen` forbids re-resolving; uv installs the exact set pinned in
# uv.lock with hash verification. Phase 14 / SC-3.
run: uv sync --frozen --extra dev
- name: Bundle staleness gate (HARD-08)
# Regenerates dist/* from src/runtime + examples/* and fails the
# build if anything in dist/ would change. Forces every PR that
# touches src/runtime, examples/, or the bundler to commit fresh
# bundles — the air-gap deploy bundle stays repaired by
# construction (Phase 16 / BUNDLER-01 + HARD-08). Contributors
# run `python scripts/build_single_file.py` before every push;
# see docs/DEVELOPMENT.md.
run: |
uv run python scripts/build_single_file.py
git diff --exit-code dist/
- name: Lint (ruff)
run: uv run ruff check src/ tests/
- name: Type check (pyright) (HARD-03)
# Phase 19 -- the gate is now fail-on-error against ``src/runtime``.
# The earlier 54-error backlog was resolved via type-annotation
# tightening + per-line ``# pyright: ignore[<rule>] -- <rationale>``
# comments for legitimate stub gaps. ``pyproject.toml`` carries
# the ``[tool.pyright]`` block (``include = ["src"]``,
# ``extraPaths = ["src"]``, ``typeCheckingMode = "basic"``).
# Test files and ``dist/`` bundles are out of scope for this
# phase; future phases may extend coverage outward.
run: uv run pyright src/runtime
- name: Test with coverage
# Dummy env vars satisfy the strict ``_interpolate`` check that
# config.yaml's ``${OLLAMA_API_KEY}`` / ``${OPENROUTER_API_KEY}``
# placeholders trigger when ``load_config()`` runs. Tests don't
# call live providers; values just need to exist. Live smoke
# tests are gated separately by ``OLLAMA_LIVE=1``.
env:
# Empty API keys so live-provider smoke tests gated by
# ``if not os.environ.get(KEY)`` correctly skip. The
# ``_interpolate`` strict-mode check only requires the var
# to EXIST in the environment (any value, incl. empty).
OLLAMA_API_KEY: ""
OPENROUTER_API_KEY: ""
AZURE_OPENAI_KEY: ""
AZURE_DEPLOYMENT: ""
AZURE_ENDPOINT: https://ci-dummy.example/
EXTERNAL_MCP_URL: https://ci-dummy.example/
EXT_TOKEN: ci-dummy
run: uv run pytest --cov=src/runtime --cov-report=xml --junitxml=junit.xml
- name: Skill-prompt-vs-schema lint (SKILL-LINTER-01)
# Phase 21. Walks every examples/*/skills/*/system.md and asserts
# that every referenced tool name + arg field exists in the
# canonically discovered tool inventory (AST-extracted from
# examples/*/mcp_server*.py + mcp_servers/*.py) and the typed
# patch models (UpdateIncidentPatch). Catches LLM-emit-vs-schema
# drift like `findings_triage` vs `findings.triage`, hallucinated
# injected args, and unknown tool names. Binary-pass gate.
run: uv run python scripts/lint_skill_prompts.py
- name: SonarCloud Scan
uses: SonarSource/sonarqube-scan-action@v8.0.0
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}