Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 78 additions & 47 deletions .github/workflows/noema-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,13 @@ jobs:
contents: read
id-token: write
pull-requests: read
outputs:
transport_capacity_unavailable: ${{ steps.noema_prepare.outputs.transport_capacity_unavailable }}
transport_retry_eligible: ${{ steps.noema_prepare.outputs.transport_retry_eligible }}
transport_retry_delay_seconds: ${{ steps.noema_prepare.outputs.transport_retry_delay_seconds }}
transport_retry_next_attempt: ${{ steps.noema_prepare.outputs.transport_retry_next_attempt }}
provider_attempt_count: ${{ steps.noema_prepare.outputs.provider_attempt_count }}
transport_http_status: ${{ steps.noema_prepare.outputs.transport_http_status }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.event.client_payload.target_repository || github.repository }}
Expand Down Expand Up @@ -794,53 +801,6 @@ jobs:
echo "::notice::Noema model phase produced no publishable envelope; publication is skipped."
fi

- name: Schedule bounded Noema transport re-dispatch
if: >-
failure()
&& env.PR_NUMBER != ''
&& steps.noema_prepare.outputs.transport_capacity_unavailable == 'true'
&& steps.noema_prepare.outputs.transport_retry_eligible == 'true'
env:
GH_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN || steps.noema_github_app_token.outputs.token || github.token }}
DELAY_SECONDS: ${{ steps.noema_prepare.outputs.transport_retry_delay_seconds }}
NEXT_ATTEMPT: ${{ steps.noema_prepare.outputs.transport_retry_next_attempt }}
PROVIDER_ATTEMPT_COUNT: ${{ steps.noema_prepare.outputs.provider_attempt_count || '' }}
TRANSPORT_HTTP_STATUS: ${{ steps.noema_prepare.outputs.transport_http_status || '' }}
run: |
set -euo pipefail
if ! [[ "${DELAY_SECONDS}" =~ ^[1-9][0-9]*$ ]] || [ "${DELAY_SECONDS}" -gt 300 ]; then
echo "::error::Noema transport re-dispatch refused a non-bounded delay."
exit 1
fi
if ! [[ "${NEXT_ATTEMPT}" =~ ^[1-9][0-9]*$ ]]; then
echo "::error::Noema transport re-dispatch refused a malformed attempt counter."
exit 1
fi
echo "::notice::Noema provider capacity unavailable (http_status=${TRANSPORT_HTTP_STATUS:-unknown}, provider_attempt_count=${PROVIDER_ATTEMPT_COUNT:-unknown}); waiting ${DELAY_SECONDS}s before same-head continuation re-dispatch ${NEXT_ATTEMPT}."
sleep "${DELAY_SECONDS}"
live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"
live_head="$(jq -r '.head.sha // empty' <<<"$live_pr")"
live_state="$(jq -r '.state // empty' <<<"$live_pr")"
if [ "${live_head,,}" != "${EXPECTED_HEAD_SHA,,}" ] || [ "$live_state" != "open" ]; then
echo "::notice::Noema transport re-dispatch retired because the live head moved or closed."
exit 0
fi
jq -n \
--arg target_repository "$TARGET_REPOSITORY" \
--argjson pr_number "$PR_NUMBER" \
--arg pr_head_sha "$EXPECTED_HEAD_SHA" \
--argjson transport_retry_attempt "$NEXT_ATTEMPT" \
'{
event_type: "noema-review",
client_payload: {
target_repository: $target_repository,
pr_number: $pr_number,
pr_head_sha: $pr_head_sha,
transport_retry_attempt: $transport_retry_attempt
}
}' | gh api -X POST "repos/${TARGET_REPOSITORY}/dispatches" --input -
echo "::notice::Scheduled Noema transport continuation re-dispatch for ${TARGET_REPOSITORY}#${PR_NUMBER} at ${EXPECTED_HEAD_SHA} (attempt ${NEXT_ATTEMPT})."

- name: Upload contextual-orchestrator sidecar evidence on failure
if: failure() && env.PR_NUMBER != ''
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand Down Expand Up @@ -889,3 +849,74 @@ jobs:
exit 1
fi
python3 "$GITHUB_WORKSPACE/.github/actions/noema-review/two_phase.py" --repo "$TARGET_REPOSITORY" --pr-number "$PR_NUMBER" --expected-head "$EXPECTED_HEAD_SHA" --publish-verdict-file "$verdict_file"

noema-transport-redispatch:
needs: [admit-current-head, noema-review]
if: >-
always()
&& needs.admit-current-head.outputs.admitted == 'true'
&& needs.noema-review.result == 'failure'
&& needs.noema-review.outputs.transport_retry_eligible == 'true'
&& needs.noema-review.outputs.transport_capacity_unavailable == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: write
pull-requests: read
env:
TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.event.client_payload.target_repository || github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number || github.event.client_payload.pr_number || '' }}
EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.client_payload.pr_head_sha || '' }}
steps:
- name: Schedule bounded Noema transport re-dispatch
if: >-
needs.noema-review.outputs.transport_capacity_unavailable == 'true'
&& needs.noema-review.outputs.transport_retry_eligible == 'true'
env:
GH_TOKEN: ${{ github.token }}
DELAY_SECONDS: ${{ needs.noema-review.outputs.transport_retry_delay_seconds }}
NEXT_ATTEMPT: ${{ needs.noema-review.outputs.transport_retry_next_attempt }}
PROVIDER_ATTEMPT_COUNT: ${{ needs.noema-review.outputs.provider_attempt_count || '' }}
TRANSPORT_HTTP_STATUS: ${{ needs.noema-review.outputs.transport_http_status || '' }}
run: |
set -euo pipefail
if [ "$TARGET_REPOSITORY" != "$GITHUB_REPOSITORY" ]; then
echo "::error::Noema continuation can dispatch only to its own repository."
exit 1
fi
if ! [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || ! [[ "$EXPECTED_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then
echo "::error::Noema continuation refused malformed pull request identity."
exit 1
fi
if ! [[ "${DELAY_SECONDS}" =~ ^[1-9][0-9]*$ ]] || [ "${DELAY_SECONDS}" -gt 300 ]; then
echo "::error::Noema transport re-dispatch refused a non-bounded delay."
exit 1
fi
if ! [[ "${NEXT_ATTEMPT}" =~ ^[1-9][0-9]*$ ]]; then
echo "::error::Noema transport re-dispatch refused a malformed attempt counter."
exit 1
fi
echo "::notice::Noema provider capacity unavailable (http_status=${TRANSPORT_HTTP_STATUS:-unknown}, provider_attempt_count=${PROVIDER_ATTEMPT_COUNT:-unknown}); waiting ${DELAY_SECONDS}s before same-head continuation re-dispatch ${NEXT_ATTEMPT}."
sleep "${DELAY_SECONDS}"
live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"
live_head="$(jq -r '.head.sha // empty' <<<"$live_pr")"
live_state="$(jq -r '.state // empty' <<<"$live_pr")"
if [ "${live_head,,}" != "${EXPECTED_HEAD_SHA,,}" ] || [ "$live_state" != "open" ]; then
echo "::notice::Noema transport re-dispatch retired because the live head moved or closed."
exit 0
fi
jq -n \
--arg target_repository "$TARGET_REPOSITORY" \
--argjson pr_number "$PR_NUMBER" \
--arg pr_head_sha "$EXPECTED_HEAD_SHA" \
--argjson transport_retry_attempt "$NEXT_ATTEMPT" \
'{
event_type: "noema-review",
client_payload: {
target_repository: $target_repository,
pr_number: $pr_number,
pr_head_sha: $pr_head_sha,
transport_retry_attempt: $transport_retry_attempt
}
}' | gh api -X POST "repos/${TARGET_REPOSITORY}/dispatches" --input -
echo "::notice::Scheduled Noema transport continuation re-dispatch for ${TARGET_REPOSITORY}#${PR_NUMBER} at ${EXPECTED_HEAD_SHA} (attempt ${NEXT_ATTEMPT})."
19 changes: 18 additions & 1 deletion tests/test_noema_orchestrator_workflow_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,21 @@ def test_noema_review_credentials_and_llm_use_orchestrator_free() -> None:
assert "secrets: inherit" not in workflow


def test_transport_redispatch_uses_a_separate_scoped_job() -> None:
"""A provider failure can dispatch again without giving model work write access."""
workflow = workflow_text("noema-review.yml")
review = workflow.split("\n noema-review:", 1)[1].split("\n noema-transport-redispatch:", 1)[0]
dispatch = workflow.split("\n noema-transport-redispatch:", 1)[1]
assert " contents: read" in review
assert "Schedule bounded Noema transport re-dispatch" not in review
assert " contents: write" in dispatch
assert "needs.noema-review.result == 'failure'" in dispatch
assert "needs.noema-review.outputs.transport_retry_eligible == 'true'" in dispatch
assert "GH_TOKEN: ${{ github.token }}" in dispatch
assert 'live_head="$(jq -r' in dispatch
assert '"repos/${TARGET_REPOSITORY}/dispatches"' in dispatch


def _expected_head_from_workflow_run_event(event: dict) -> str:
"""Mirror EXPECTED_HEAD's ``||`` fallback chain for a ``workflow_run`` event.

Expand Down Expand Up @@ -476,7 +491,9 @@ def test_noema_review_job_has_no_job_level_timeout() -> None:
docs/doctoring/autofix-and-noema-review-model-job-timeout-removal.md.
"""
workflow = workflow_text("noema-review.yml")
job = workflow.split(" noema-review:\n", 1)[1]
job = workflow.split(" noema-review:\n", 1)[1].split(
" noema-transport-redispatch:\n", 1
)[0]

match = re.search(r"^ timeout-minutes: (\d+)$", job, flags=re.MULTILINE)
assert match is None, (
Expand Down
Loading