Packaged-Dependency Smoke Contracts (issue #349)
Context: #330 requires every executable skill to declare its runtime
dependencies in skills/<id>/requirements.txt. The nightly
.github/workflows/packaged-deps-nightly.yml smoke-tests those declarations in
clean-room venvs. Issue #349 promotes that pipeline from report-only toward
fail-closed.
Structure
scripts/check_skill_deps.pydeclaresSMOKE_CONTRACTS(per skill, one or moreSmokeContractentries) andPENDING_SMOKE_CONTRACTS(wave-2 backlog). Every third-party-required executable skill must be in one of the two sets —scripts/tests/test_smoke_contracts.pyenforces the partition.- The module is intentionally free of
subprocess/network imports (guarded bytest_check_module_has_no_network_surface), so the nightly runner can import the table without pyyaml or any project venv. scripts/smoke_contracts_runner.pyexecutes the table: fresh venv per skill, declared-requirements-only install, reverse import probes, contract commands. It writesdeps-nightly-smoke.jsonand exits nonzero on any failure — this exit-code fix is what makes the checklist item 4 flip meaningful.--rootallows running the same runner against an alternate checkout.
Contract semantics
scriptmust be repo-relative insideskills/<id>/scripts/.args/expect_exit/expect_out: the clean-room command, expected exit code, and required output substrings (the fixture expectation).fixture: optional repo-relative file backing the expectation; verified present by validation and recorded in the evidence.- Skip entries (empty
script) requireskip_reason; they are recorded as skipped in the evidence instead of executed. - Wave-1 contracts were dry-run on the 2026-10-03 worktree; evidence links in the #349 PR.
Rollout (maintainer sequencing from #349)
- This PR (contracts + runner + exit codes) keeps
continue-on-error: true— report-only promotion trial. - Trigger one
workflow_dispatchrun; confirm every contract row isokin thepackaged-deps-nightly-reportartifacts. - After the 2-week green window completes (effective green from 2026-09-27,
so ≥ 2026-10-10/11) a follow-up PR removes
continue-on-error: trueand renames the job to fail-closed. The nightlyreportstep stays exit-0: declaration enforcement already failsci.ymlviarun_check; the report is evidence, not a gate.
Adding a contract (wave 2)
- Dry-run the candidate command on a clean venv
(
uv venv && uv pip install -r skills/<id>/requirements.txt). - Add the
SmokeContractentry with a distinctiveexpect_outtoken. - If coverage would move a skill out of
PENDING_SMOKE_CONTRACTS, remove it from there in the same change — the partition test fails otherwise.