fix: Docker sandbox agent execution + role template rendering + --model propagation for non-Claude CLIs #682
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: docs-drift | |
| # Reads the doc inventory in docs/playbooks/docs-drift.md and checks that | |
| # every source-of-truth file referenced by a doc still exists, that the | |
| # canonical AGENTS.md / CLAUDE.md / CONVENTIONS.md / .goosehints / | |
| # .cursor/rules/*.mdc outputs agree with `bernstein agents-md verify`, and | |
| # that every enumerated doc file is present on disk. On pull requests the | |
| # report is posted as a non-blocking PR comment so the change can land in | |
| # the same PR. On pushes to main the gate fails so the drift gets fixed in | |
| # a follow-up. | |
| on: | |
| pull_request: | |
| paths: | |
| - "docs/**" | |
| - "src/bernstein/**" | |
| - "scripts/check_docs_drift.py" | |
| - "scripts/check_data_freshness.py" | |
| - "scripts/gen_agents_md.py" | |
| - ".github/workflows/docs-drift.yml" | |
| - "AGENTS.md" | |
| - "CLAUDE.md" | |
| - "CONVENTIONS.md" | |
| - ".goosehints" | |
| - "README.md" | |
| - "CONTRIBUTING.md" | |
| push: | |
| branches: [main] | |
| paths: | |
| - "docs/**" | |
| - "src/bernstein/**" | |
| - "scripts/check_docs_drift.py" | |
| - "scripts/check_data_freshness.py" | |
| - "scripts/gen_agents_md.py" | |
| - ".github/workflows/docs-drift.yml" | |
| - "AGENTS.md" | |
| - "CLAUDE.md" | |
| - "CONVENTIONS.md" | |
| - ".goosehints" | |
| - "README.md" | |
| - "CONTRIBUTING.md" | |
| concurrency: | |
| group: docs-drift-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| drift-check: | |
| name: Run drift check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - name: Harden runner (audit mode) | |
| uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 | |
| with: | |
| python-version: "3.13" | |
| - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 | |
| with: | |
| enable-cache: true | |
| - name: Install bernstein for agents-md verify | |
| run: uv sync --no-dev --frozen | |
| continue-on-error: true | |
| - name: Run docs drift checker | |
| id: drift | |
| run: | | |
| set +e | |
| python scripts/check_docs_drift.py > drift-report.md | |
| rc=$? | |
| echo "report_rc=${rc}" >> "$GITHUB_OUTPUT" | |
| # Re-run in JSON form for the clean-flag decision. | |
| python scripts/check_docs_drift.py --json > drift-report.json | |
| # Extract the top-level "clean" boolean so later steps can branch. | |
| clean=$(python -c "import json,sys; print(json.load(open('drift-report.json')).get('clean', False))") | |
| echo "clean=${clean}" >> "$GITHUB_OUTPUT" | |
| cat drift-report.md | |
| shell: bash | |
| - name: Upload drift report | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: docs-drift-report | |
| path: | | |
| drift-report.md | |
| drift-report.json | |
| retention-days: 14 | |
| - name: Comment drift report on the pull request | |
| if: github.event_name == 'pull_request' && steps.drift.outputs.clean != 'True' | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const body = fs.readFileSync('drift-report.md', 'utf8'); | |
| const tag = '<!-- docs-drift-report -->'; | |
| const issue_number = context.payload.pull_request.number; | |
| const { owner, repo } = context.repo; | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner, repo, issue_number, per_page: 100, | |
| }); | |
| const existing = comments.find(c => c.body && c.body.includes(tag)); | |
| const payload = `${tag}\n${body}`; | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner, repo, comment_id: existing.id, body: payload, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner, repo, issue_number, body: payload, | |
| }); | |
| } | |
| - name: Fail the gate on main-branch drift | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| run: | | |
| python scripts/check_docs_drift.py --strict | |
| docs-data-freshness: | |
| # Advisory check: flags time-stamped metric lines (stars, downloads, | |
| # dated qualifiers) older than 30 days as a soft warning. On push to | |
| # main, markers older than 60 days fail this job. This job is NOT in | |
| # the canary list of required checks: time-stamped metrics drift | |
| # between scheduled refreshes and the gate catches them automatically. | |
| name: Data freshness (advisory) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Harden runner (audit mode) | |
| uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 | |
| with: | |
| python-version: "3.13" | |
| - name: Run data-freshness check (soft on PR, strict on push to main) | |
| run: | | |
| set -e | |
| if [ "${GITHUB_EVENT_NAME}" = "push" ] && [ "${GITHUB_REF}" = "refs/heads/main" ]; then | |
| python scripts/check_data_freshness.py --strict | |
| else | |
| python scripts/check_data_freshness.py | |
| fi | |
| shell: bash | |