feat: add coding-agent least-privilege flagship policy and demo (#273) #252
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 | |
| # Contract: this workflow runs the same gate as `make ci` | |
| # (fmt-check -> lint -> type -> test -> example). Each step below invokes a | |
| # Makefile target so the local gate and CI cannot drift (see Makefile and | |
| # docs/agent-context/workflows.md). Change the steps here only by changing the | |
| # Makefile. | |
| on: | |
| push: | |
| branches: ["main", "copilot/**"] | |
| pull_request: | |
| branches: ["main"] | |
| workflow_call: | |
| # Least-privilege by default; jobs needing more declare it explicitly. | |
| permissions: | |
| contents: read | |
| # Cancel superseded runs on the same ref so a new push to a PR stops the | |
| # previous run instead of burning runner time. Keyed on the ref so distinct | |
| # branches/PRs stay independent. | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| test: | |
| name: "Python ${{ matrix.python-version }}" | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: pip | |
| cache-dependency-path: pyproject.toml | |
| - name: Install dependencies | |
| run: pip install -e ".[dev]" | |
| # Each step is a Makefile target — see the contract note at the top. | |
| - name: Format check | |
| run: make fmt-check | |
| - name: Lint | |
| run: make lint | |
| - name: Type check | |
| run: make type | |
| - name: Test | |
| run: make test | |
| - name: Examples | |
| run: make example | |
| - name: Coverage HTML report | |
| if: always() | |
| run: python -m coverage html | |
| - name: Upload coverage HTML | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: coverage-html-${{ matrix.python-version }} | |
| path: htmlcov/ | |
| if-no-files-found: ignore | |
| # Prove that each declared >= floor is real rather than aspirational. Resolve | |
| # direct requirements to their lower bounds while allowing transitive | |
| # dependencies to remain solvable, then run the full behavioral suite. | |
| floor-deps: | |
| name: "Floor dependencies (Python 3.10)" | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python 3.10 | |
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 | |
| with: | |
| python-version: "3.10" | |
| - name: Install uv | |
| run: python -m pip install uv | |
| - name: Install declared direct floors | |
| run: uv pip install --system --resolution lowest-direct -e ".[dev]" | |
| - name: Run suite at the floors | |
| run: pytest -q | |
| bare-install: | |
| name: "Bare install (no extras)" | |
| runs-on: ubuntu-latest | |
| needs: test | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python | |
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| cache-dependency-path: pyproject.toml | |
| # No extras: proves the "minimal deps (httpx + pydantic)" claim holds. | |
| - name: Install (no extras) | |
| run: pip install . | |
| - name: Import the full public API | |
| run: | | |
| python -c "import weaver_kernel as w; \ | |
| missing = [n for n in w.__all__ if not hasattr(w, n)]; \ | |
| assert not missing, f'missing public symbols: {missing}'; \ | |
| print(f'imported {len(w.__all__)} public symbols')" | |
| - name: Run the README quickstart | |
| run: python examples/readme_quickstart.py | |
| - name: Assert optional extras are genuinely absent | |
| run: | | |
| for mod in mcp yaml opentelemetry tiktoken weaver_contracts; do | |
| if python -c "import $mod" 2>/dev/null; then | |
| echo "::error::optional dependency '$mod' is importable in a bare install" | |
| exit 1 | |
| fi | |
| done | |
| echo "no optional extras leaked into the base install" | |
| - name: Assert the MCP-extra-missing error is helpful | |
| run: | | |
| python - <<'PY' | |
| from weaver_kernel.drivers.mcp_support import import_optional | |
| try: | |
| import_optional("mcp.client.session") | |
| except ImportError as exc: | |
| assert "weaver-kernel[mcp]" in str(exc), f"unhelpful error: {exc}" | |
| print("MCP-extra-missing error is documented and actionable") | |
| else: | |
| raise AssertionError("expected ImportError without the mcp extra") | |
| PY | |
| security-audit: | |
| name: "Dependency audit (pip-audit)" | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python | |
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| cache-dependency-path: pyproject.toml | |
| - name: Install pip-audit | |
| run: pip install pip-audit | |
| # Resolve weaver-kernel's *runtime* dependency tree in an isolated venv | |
| # (no extras, no pip-audit) and audit exactly that, so pip-audit's own | |
| # dependencies can never cause a failure unrelated to what adopters ship. | |
| - name: Resolve runtime dependency tree | |
| run: | | |
| python -m venv /tmp/runtime | |
| /tmp/runtime/bin/pip install . | |
| /tmp/runtime/bin/pip freeze --exclude-editable \ | |
| | grep -viE '^(weaver-kernel|pip|setuptools)([=@ ]|$)' > runtime-requirements.txt | |
| echo "Auditing:"; cat runtime-requirements.txt | |
| # Policy: fail on any known vulnerability in the runtime tree. Document a | |
| # false positive by appending `--ignore-vuln <ID>` here with a comment | |
| # (see README "Security automation"). | |
| - name: Audit runtime dependencies | |
| run: pip-audit --strict --desc --requirement runtime-requirements.txt | |
| conformance: | |
| name: "Weaver-spec conformance" | |
| runs-on: ubuntu-latest | |
| needs: test | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python | |
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| cache-dependency-path: pyproject.toml | |
| # Real validation (no echo): map kernel Frame/ActionTrace/token onto the | |
| # published weaver-contracts dataclasses and assert they validate. When | |
| # dgenio/weaver-spec#4 ships weaver_contracts.conformance, add its runner | |
| # here as an additional step. | |
| - name: Install conformance extra | |
| run: pip install ".[conformance]" pytest pytest-asyncio | |
| - name: Report contract version | |
| run: python -c "from weaver_kernel.conformance import contract_version; print('weaver-contracts', contract_version())" | |
| - name: Run conformance mapping tests | |
| run: python -m pytest tests/test_conformance.py -q |