Contracts
source-eligibility.contract.md
Contract: scripts/ci/source_eligibility.py
Pure decision (unit-tested, no network, no git)
resolve_source(trigger: TriggerMeta, candidates: list[CandidateRun], default_branch: str) -> SourceDecision
- Deterministic function of its arguments only. No I/O, no
subprocess, no clock. - Applies, in order: dispatch →
NoFallback;event=pull_requestorconclusion=failure→NotAMainSource; else pick the most-recentconclusion=successcandidate whoseheadBranch == trigger.head_branch, else whoseheadBranch == default_branch→EligibleSource; none →NoEligibleSource. - Raises
ValueErrorONLY on malformed input (missing required field, non-intdatabaseId). A legitimate absence of source is aNo*variant, never an exception.
Edge (main())
- Args mirror
select_source_artifacts.py: read injected JSON files/args; thegh api|jq/gh run list --jsoncall lives in the workflow, not the module. - Emits to stdout /
$GITHUB_OUTPUT, exactly two keys:
run-id=<databaseId or empty>
eligibility=<slug>
- MUST NOT emit
complete,missing, orcoverage(those belong toreconcile_shards.py). - MUST exit 0 for every
SourceDecisionvariant (including the empty ones). Non-zero exit only on malformed input.
Workflow wiring (.github/workflows/ci-aggregate.yml, last-success step)
- The step's
run:invokespython3 scripts/ci/source_eligibility.py ...— no inlinegh run list ... || truedecision logic remains. - The step still emits
run-idto$GITHUB_OUTPUTunder the same output name the downstreamdownload-previousstep reads (steps.last-success.outputs.run-id), so no downstream step changes. - The
workflow_dispatchguard (if: github.event_name != 'workflow_dispatch') is preserved (belt-and-braces withNoFallback).
Frozen boundaries (assert unchanged)
# round-trip: skip: illustrative contract anchors, not a parseable mission artifact
frozen:
- path: .github/workflows/ci-aggregate.yml
step: "Fail loudly if the reconciled shard set is incomplete" # ~:292-299
rule: run-block byte-identical (pinned by test)
- path: scripts/ci/reconcile_shards.py
symbol: must_be_fresh
rule: unchanged; a SELECTED shard absent from current stays fatal
- path: .github/workflows/ci-router.yml # #4347, Stage 1
rule: not touched
- path: .github/workflows/ci-fleet-verdict.yml # #4371, Stage 1
rule: not touched
Test obligations
1. Red-first unit cases (assert the REJECTIONS, not a happy string): pr-head-trigger, failed-trigger, dispatch-no-fallback, eligible push-main, eligible source-branch, no-success-source, plus unrelated-branch success rejected and red source-branch run rejected. 2. Execution-grounded wiring guard (mirror tests/ci/test_aggregate_source.py:146-153,309-327): extract the real last-success step from ci-aggregate.yml, assert it invokes scripts/ci/source_eligibility.py, execute it against a stubbed no-eligible-source inventory, assert run-id="" + a named eligibility slug. 3. Guard-preservation pin: assert the fail-closed step's run: text is byte-identical.