Skip to content

[HOLD-for-9.0.0][skip-runtime-e2e] docs(decisions): canonical /decisions verdict values #55

[HOLD-for-9.0.0][skip-runtime-e2e] docs(decisions): canonical /decisions verdict values

[HOLD-for-9.0.0][skip-runtime-e2e] docs(decisions): canonical /decisions verdict values #55

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:
# Trim out non-diff-changing event types: dropped `edited` (title/body
# edit re-runs the gate without any code change). `reopened` retained for
# PRs that were closed and reopened with new commits.
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
# Java SDK user-facing surface: the published library code under
# src/main/java/, the Maven manifest pom.xml that downstream
# consumers depend on, and runnable example sources under examples/.
USER_FACING_GLOBS=(
'src/main/java/'
'pom.xml'
'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 (src/main/java/, pom.xml, examples/) without runtime-e2e/ test
but does not add or update any 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 — real SDK class loaded by a
real JVM, real HTTP request against a real running AxonFlow agent.
Mocks, stubs, WireMock, MockWebServer, capture-stub harnesses do
NOT count as runtime proof.
To resolve, do ONE of:
1. Add a test under runtime-e2e/<feature>/ that invokes the
feature through the actual user-facing surface (real
AxonFlow client class wired to a real running agent).
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