[HOLD-for-9.0.0][skip-runtime-e2e] docs(decisions): canonical /decisions verdict values #80
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: Definition of Done | |
| # Enforces CLAUDE.md HARD RULE #0: a user-facing feature is not done until | |
| # demonstrated through its actual runtime. See axonflow-claude-plugin#59 | |
| # for the doctrine + lint-no-mocks rationale. | |
| on: | |
| pull_request: | |
| # Drop `edited` — re-runs the gate on title/body edits without any code change. | |
| types: [opened, synchronize, reopened] | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| lint-no-mocks-in-runtime-e2e: | |
| name: Lint — no mocks in runtime-e2e/ | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Run lint | |
| run: | | |
| if [ -x scripts/lint-no-mocks-in-runtime-e2e.sh ]; then | |
| ./scripts/lint-no-mocks-in-runtime-e2e.sh | |
| else | |
| echo "lint-no-mocks-in-runtime-e2e.sh not present — skipping (older branch)." | |
| fi | |
| runtime-e2e-required: | |
| name: Runtime E2E required for user-facing changes | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Detect user-facing surface changes | |
| id: detect | |
| env: | |
| BASE: ${{ github.event.pull_request.base.sha }} | |
| HEAD: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| set -uo pipefail | |
| # Python SDK user-facing surface: the published package source | |
| # (axonflow/), build/install metadata (pyproject.toml), and the | |
| # examples/ tree that downstream users copy from. Changes here | |
| # are observable from a `pip install axonflow` + `from axonflow | |
| # import AxonFlow` flow against a real running agent. | |
| USER_FACING_GLOBS=( | |
| 'axonflow/' | |
| 'pyproject.toml' | |
| 'examples/' | |
| ) | |
| CHANGED=$(git diff --name-only "$BASE" "$HEAD" || true) | |
| echo "Changed files in PR:" >&2 | |
| printf ' %s\n' $CHANGED >&2 | |
| MATCHED="" | |
| for f in $CHANGED; do | |
| for pat in "${USER_FACING_GLOBS[@]}"; do | |
| case "$f" in | |
| "$pat"*|*"/$pat"*) | |
| MATCHED="$MATCHED $f" | |
| break | |
| ;; | |
| esac | |
| done | |
| done | |
| RUNTIME_E2E_TOUCHED=$(echo "$CHANGED" | grep -c '^runtime-e2e/' || true) | |
| { | |
| echo "user_facing_changed=$([ -n "$MATCHED" ] && echo true || echo false)" | |
| echo "runtime_e2e_touched=$RUNTIME_E2E_TOUCHED" | |
| } >> "$GITHUB_OUTPUT" | |
| if [ -n "$MATCHED" ]; then | |
| echo "User-facing files changed:" >&2 | |
| for f in $MATCHED; do echo " - $f" >&2; done | |
| fi | |
| - name: Check escape-hatch justification | |
| id: hatch | |
| if: steps.detect.outputs.user_facing_changed == 'true' && steps.detect.outputs.runtime_e2e_touched == '0' | |
| env: | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| PR_BODY: ${{ github.event.pull_request.body }} | |
| run: | | |
| set -uo pipefail | |
| if [[ "$PR_TITLE" == *"[skip-runtime-e2e]"* ]]; then | |
| if echo "$PR_BODY" | grep -q '## Skip-runtime-e2e justification'; then | |
| echo "Escape hatch active." >&2 | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "::error::PR title carries [skip-runtime-e2e] but body has no '## Skip-runtime-e2e justification' section." | |
| exit 1 | |
| fi | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Enforce runtime-e2e/ presence | |
| if: steps.detect.outputs.user_facing_changed == 'true' && steps.detect.outputs.runtime_e2e_touched == '0' && steps.hatch.outputs.skip != 'true' | |
| run: | | |
| cat <<'EOF' >&2 | |
| ::error::This PR touches real SDK code (axonflow/, pyproject.toml, examples/) | |
| without runtime-e2e/ test in the same PR. | |
| Per CLAUDE.md HARD RULE #0: | |
| A user-facing feature is not done until you have demonstrated it | |
| working through its actual runtime — a real `from axonflow import | |
| AxonFlow` over real httpx against a real running AxonFlow agent. | |
| Mocks, stubs, MagicMock, httpx_mock.add_response, and capture-stub | |
| harnesses do NOT count as runtime proof. | |
| To resolve, do ONE of: | |
| 1. Add a test under runtime-e2e/<feature>/test.py that invokes | |
| the SDK against a real running agent and asserts on the | |
| agent's response (not on a mocked response object). | |
| 2. If genuinely internal (build / deps / lint baseline / docs), | |
| add `[skip-runtime-e2e]` to PR title AND a | |
| `## Skip-runtime-e2e justification` section to PR body. | |
| See: axonflow-internal-docs/engineering/E2E_EXAMPLES_TESTING_WORKFLOW.md | |
| EOF | |
| exit 1 | |
| - name: All clear | |
| if: steps.detect.outputs.user_facing_changed == 'false' || steps.detect.outputs.runtime_e2e_touched != '0' || steps.hatch.outputs.skip == 'true' | |
| run: | | |
| if [ "${{ steps.detect.outputs.user_facing_changed }}" = 'false' ]; then | |
| echo "No user-facing surface changed. Gate not applicable." >&2 | |
| elif [ "${{ steps.detect.outputs.runtime_e2e_touched }}" != '0' ]; then | |
| echo "User-facing change detected and runtime-e2e/ updated in same PR. ✓" >&2 | |
| else | |
| echo "Escape hatch active with valid justification. ✓" >&2 | |
| fi |