Skip to content

Add the consumed-file check — the go-live CI guardrail#16

Merged
mmcky merged 2 commits into
mainfrom
ci/consumed-file-check
Jul 17, 2026
Merged

Add the consumed-file check — the go-live CI guardrail#16
mmcky merged 2 commits into
mainfrom
ci/consumed-file-check

Conversation

@mmcky

@mmcky mmcky commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

The go-live CI guardrail from PLAN Phase 5 — the second of the two prerequisites (with branch protection, added 2026-07-17) that the amended ordering note requires before the first repoint (QuantEcon/lecture-python-intro#792) makes raw/main a production URL. This is the repo's first CI and its first PR under the new protect-main ruleset.

What it checks

For every manifest sidecar lectures/<datafile>.yml:

Condition Failure it prevents
Manifest filename matches the sidecar's own name a sidecar drifting from its dataset — the pairing Phase 2 promised CI would assert
Consumed file exists a rename/delete silently 404ing a live lecture build
Consumed file has integrity.sha256 recorded a consumed file whose changes can't be detected
Committed bytes hash to the recorded sha256 a byte change landing without the manifest acknowledging it

"Consumed" means the manifest's consumers list is non-empty. Files with no manifest yet (Phase 6 backfill pending) or empty consumers are out of scope — the full Phase 5 validation suite (schema, dtypes, invariant tests) subsumes this job when it lands.

The hash-mismatch message tells the author what a legitimate change looks like: a deliberate in-place correction updates integrity.sha256 in the same PR and plans rebuilds for the consumers (AGENTS.md, "Corrections vs vintages").

Verified

Ran locally against all five scenarios: passes on the clean tree (1 manifest, 1 consumed file checked), and fails with the right error on each failure mode — tampered bytes, deleted data file, sidecar/filename mismatch, and a consumed file with its hash nulled. The workflow run on this PR is the live proof.

Checkout uses lfs: true proactively: nothing is LFS-tracked today (Phase 3), but if a consumed file ever is, this job must hash the bytes, not the pointer.

Ticks the PLAN go-live checklist item in the same PR, per convention.

After merge

Once this job has run on main, it can be added to the protect-main ruleset as a required status check — happy to do that as a follow-up so a red check physically blocks merging.

Part of #8
See QuantEcon/meta#338

The narrowest possible test that a PR cannot break a live lecture (PLAN
Phase 5, go-live guardrails; prerequisite for the first repoint). For every
manifest sidecar in lectures/:

- the manifest's `filename` must match the sidecar's own name (the pairing
  Phase 2 promised CI would assert)
- if `consumers` is non-empty — a lecture reads the file in production —
  the data file must exist, integrity.sha256 must be recorded, and the
  committed bytes must hash to it. The mismatch message points at the
  corrections-vs-vintages rule so a deliberate in-place fix knows to update
  the hash in the same PR.

Files with no manifest yet (Phase 6 backfill) or empty consumers are out of
scope; the full Phase 5 validation suite subsumes this job when it lands.

Runs on every PR and on pushes to main. Checkout uses lfs: true proactively:
nothing is LFS-tracked today (Phase 3), but if a consumed file ever is, the
job must hash bytes, not pointer text.

Verified locally: passes on the clean tree (1 consumed file checked), and
fails correctly on each of the four failure modes — tampered bytes, deleted
data file, sidecar/filename mismatch, and a consumed file with no recorded
hash.

Ticks the PLAN go-live checklist item in the same PR, per convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 17, 2026 00:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces the minimal “consumed-file” CI guardrail (PLAN Phase 5) to prevent PRs from breaking datasets that are already referenced by live lecture consumers, and marks the checklist item as completed in PLAN.md.

Changes:

  • Add a new GitHub Actions workflow that runs on PRs and on pushes to main to validate consumed datasets against their manifest sidecars.
  • Add a Python checker script that verifies sidecar/filename alignment and validates integrity.sha256 + on-disk bytes for manifests with non-empty consumers.
  • Update PLAN.md to mark the guardrail as implemented and reference the workflow file.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
PLAN.md Marks the minimal consumed-file CI guardrail as completed and links to the workflow.
.github/workflows/consumed-file-check.yml Adds the workflow wiring (checkout with lfs: true, setup Python, run checker).
.github/scripts/check_consumed_files.py Implements the consumed-file guardrail checks (sidecar pairing + sha256 verification).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/scripts/check_consumed_files.py
Comment thread .github/scripts/check_consumed_files.py Outdated
Both crashes would still have failed CI (non-zero exit via the traceback), so
the guardrail was never bypassable — but a guardrail should diagnose, not
stack-trace, and should keep scanning past one bad file:

- Empty or invalid YAML: caught per file (yaml.YAMLError, or a non-mapping
  parse result) and reported as ::error:: with the filename; the remaining
  manifests still get checked.
- Non-dict `integrity` (e.g. a stray string): treated as missing, so a
  consumed file lands in the existing "sha256 not recorded" error instead of
  raising AttributeError.

Re-verified all five original scenarios plus three new ones: empty sidecar,
unparseable YAML, and integrity-as-string — each fails cleanly with the right
message and exit 1, no tracebacks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mmcky
mmcky merged commit c6e4504 into main Jul 17, 2026
1 check passed
@mmcky
mmcky deleted the ci/consumed-file-check branch July 17, 2026 01:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants