Chapter 28Lesson 05~245 minutes

Checkpoint Lab — Monorepos, Path Filters, Changed-File Detection, and Selective Pipelines

Prove selective CI for three change sets, deliberately break shared-dependency fan-out, and use a full-suite contract check to catch the selector defect before repair.

CheckpointSelector contractFallback suiteEvidenceRepair

Learning objectives

  • Implement a checkpoint that predicts selection for API, Web and shared-library changes.
  • Prove a deliberately broken dependency selector under-selects a shared change.
  • Use an independent full selector-contract suite to fail on that defect and preserve the first failure.
  • Repair the selector and verify both selective and full modes with one stable aggregate check.
  • Produce an evidence packet that distinguishes event/revision, selector, matrix and governance state.

1. Checkpoint scenario: prove optimization and its safety net

The checkpoint avoids live forks, cloud accounts and production repositories. Three synthetic change sets model API-only, Web-only and shared-library changes. The selector has correct and intentionally broken variants; broken forgets that Web depends on libs/shared.

Run selective mode, then an independent full-contract mode that enumerates authoritative component expectations. The broken selector must fail the full contract. Preserve that run and repair the graph rather than normalizing the failure.

2. Preflight and safety boundary

  • Use a disposable repository; same-repository branches are sufficient.
  • Use ubuntu-24.04, checkout v7.0.1 3d3c42e5aac5ba805825da76410c181273ba90b1, setup-python v7.0.0 5fda3b95a4ea91299a34e894583c3862153e4b97, Python 3.13, upload-artifact v7.0.1 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a.
  • Keep permissions at {} except contents: read for checkout.
  • No secret, OIDC token, release/package permission, environment, cloud target or self-hosted runner is needed.
  • If branch protection is not changed in the disposable repo, record the required-check configuration as simulated rather than claiming it was enforced.

3. Selector contract fixture

The authoritative component inventory and expected change-set mapping are separate from the generated matrix. The important case is the shared path, which must select both applications. The broken switch exists only to create controlled under-selection evidence.

COMPONENTS = {"api", "web"}
def select(paths, broken=False):
    chosen=set(); full=False
    for path in paths:
        if path.startswith("apps/api/"): chosen.add("api")
        elif path.startswith("apps/web/"): chosen.add("web")
        elif path.startswith("libs/shared/"):
            chosen.add("api")
            if not broken: chosen.add("web")
        elif path.startswith("docs/") or path == "README.md": pass
        else: full=True
    return sorted(COMPONENTS if full else chosen)
CASES = [
    (["apps/api/app.py"], ["api"]),
    (["apps/web/app.py"], ["web"]),
    (["libs/shared/contract.txt"], ["api", "web"]),
]

4. Predict state changes before running

Run Expected selector output Expected jobs Expected required result
correct + selective + api [api] API only success
correct + selective + web [web] Web only success
correct + selective + shared [api, web] API + Web success
broken + selective + shared [api] API only — intentionally incomplete may be green; evidence proves under-selection
broken + full-contract + shared contract expected api+web, actual api full contract failure; preserve first-failure run
correct + full-contract + shared all fixtures verified full contract success after repair

5. Exact checkpoint workflow

name: Monorepo selector checkpoint
on:
  workflow_dispatch:
    inputs:
      selector_variant:
        type: choice
        options: [correct, broken]
        default: correct
      suite:
        type: choice
        options: [selective, full-contract]
        default: selective
      change_set:
        type: choice
        options: [api, web, shared]
        default: shared
permissions: {}
jobs:
  select:
    name: Selection evidence
    runs-on: ubuntu-24.04
    permissions:
      contents: read
    outputs:
      components: ${{ steps.select.outputs.components }}
      count: ${{ steps.select.outputs.count }}
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
        with:
          python-version: "3.13"
      - id: select
        shell: bash
        env:
          VARIANT: ${{ inputs.selector_variant }}
          CHANGE_SET: ${{ inputs.change_set }}
        run: |
          set -euo pipefail
          python .github/scripts/checkpoint_selector.py "$VARIANT" "$CHANGE_SET" | tee selection.json
          COMPONENTS="$(python -c 'import json; print(json.dumps(json.load(open("selection.json"))["components"],separators=(",",":")))')"
          COUNT="$(python -c 'import json; print(len(json.load(open("selection.json"))["components"]))')"
          printf 'components=%s\ncount=%s\n' "$COMPONENTS" "$COUNT" >> "$GITHUB_OUTPUT"
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        if: ${{ always() }}
        with:
          name: checkpoint-selection-${{ github.run_id }}-${{ github.run_attempt }}
          path: selection.json
          retention-days: 7
  selected-tests:
    name: Selected ${{ matrix.component }}
    needs: select
    if: ${{ inputs.suite == 'selective' && needs.select.outputs.count != '0' }}
    runs-on: ubuntu-24.04
    permissions:
      contents: read
    strategy:
      fail-fast: false
      matrix:
        component: ${{ fromJSON(needs.select.outputs.components) }}
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
        with:
          python-version: "3.13"
      - shell: bash
        env:
          COMPONENT: ${{ matrix.component }}
        run: python "apps/$COMPONENT/test_component.py"
  full-contract:
    name: Full selector contract
    if: ${{ inputs.suite == 'full-contract' }}
    runs-on: ubuntu-24.04
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
        with:
          python-version: "3.13"
      - name: Enumerate authoritative inventory and assert every selector fixture
        shell: bash
        env:
          VARIANT: ${{ inputs.selector_variant }}
        run: python .github/scripts/test_selector_contract.py "$VARIANT"
  required:
    name: Monorepo CI / required
    needs: [select, selected-tests, full-contract]
    if: ${{ always() }}
    runs-on: ubuntu-24.04
    permissions: {}
    steps:
      - name: Map run mode to required outcome
        shell: bash
        env:
          SUITE: ${{ inputs.suite }}
          SELECT_RESULT: ${{ needs.select.result }}
          SELECTED_RESULT: ${{ needs.selected-tests.result }}
          FULL_RESULT: ${{ needs.full-contract.result }}
          COUNT: ${{ needs.select.outputs.count }}
        run: |
          set -euo pipefail
          test "$SELECT_RESULT" = success
          if [ "$SUITE" = selective ]; then
            if [ "$COUNT" = 0 ]; then test "$SELECTED_RESULT" = skipped; else test "$SELECTED_RESULT" = success; fi
            test "$FULL_RESULT" = skipped
          else
            test "$FULL_RESULT" = success
            test "$SELECTED_RESULT" = skipped
          fi

The full-contract job does not call the incremental selector to decide which cases to test. It knows the authoritative inventory and expected fixtures independently, so the safety net does not share the same omission.

6. Execute and preserve the six-run sequence

  1. Run correct/selective/api; retain selection artifact and aggregate result.
  2. Run correct/selective/web.
  3. Run correct/selective/shared; verify two matrix cells.
  4. Run broken/selective/shared; observe only API was selected and do not call this proof of correctness.
  5. Run broken/full-contract/shared; the selector contract must fail. Preserve this first-failure run ID/attempt and logs.
  6. Repair the Web dependency edge, commit, then run correct/full-contract/shared.
  7. Rerun correct/selective/shared to prove the original change set now creates API + Web cells.

7. Required evidence packet

Field What to retain
Event/run workflow_dispatch inputs, source/workflow SHA, run ID/attempt.
Runner/tool ubuntu-24.04, action SHAs, Python version.
Selector revision exact commit containing selector + dependency graph.
Change set api/web/shared synthetic path list or real base/head/merge-base in PR mode.
Selection selection.json, component array, reasons/fallback.
Job graph selected matrix cells or full-contract job, plus skipped jobs.
Failure evidence broken/full-contract first-failure log showing expected api+web versus actual api.
Governance stable Monorepo CI / required result and whether actually required or simulated.
Fallback policy full-contract/manual/scheduled cadence and authoritative inventory source.
Limitations synthetic checkpoint does not exercise a production-scale dependency graph or native 3,000-file boundary.

8. Map the checkpoint back to real pull requests

The synthetic change_set isolates selector correctness. In a real PR, replace it with Lesson 2’s chain: event base/head SHA → proven merge base → NUL-safe changed paths → ownership/dependency closure. Keep the same selector contract tests and aggregate check.

9. Why the full suite is a different control

The full-contract lane is not “select all using the selector.” It enumerates authoritative components and fixture expectations directly. In a larger monorepo, the independent control might be a nightly build-system command that knows every workspace/package, a release qualification lane, or a graph-consistency test.

10. Cleanup and rollback

  • Keep the intentionally failed run/artifact until evidence is reviewed.
  • Remove the broken teaching switch before adapting to production.
  • Delete the disposable repository/branch only after exporting desired evidence.
  • If you temporarily configured branch protection, remove only the exact disposable rule/check after verification.
  • No cloud/package/environment cleanup is needed because the lab creates none.

11. Production hardening checklist

  • Event revision pair and merge base are explicit and verifiable.
  • Changed paths are NUL-safe data; renames/deletions are conservative.
  • Ownership/dependency graph is versioned and regression-tested.
  • Unknown/global changes fan out broadly.
  • Matrix JSON and empty selection are explicit.
  • Stable required aggregate exists for every applicable PR.
  • Independent full coverage does not depend on incremental selection.
  • First-failure selector evidence survives reruns and merge investigations.

12. What Chapter 28 adds — and the bridge to Chapter 29

Chapter 28 adds evidence-backed selective execution to the operating model. A repository can reduce monorepo CI work while still proving base/head identity, complete path handling, dependency closure, selected job results and one stable governance check. Chapter 29 turns these patterns into reusable platform pipelines and golden paths so multiple repositories can consume versioned organization-wide delivery contracts.

13. Checkpoint summary

You have proved both sides of selective CI: the fast path can choose only affected components, and an independent full-contract path can expose a deliberately incomplete dependency selector. That is the difference between an optimization and an unverified skip.

Next lesson

Reusable Platform Pipelines, Golden Paths, and Organization-Wide Delivery Design: Core Concepts and Mental Model

Continue with the next lesson to build on the current concepts, evidence, security boundaries, and operational practices.

Knowledge check

Why is the broken selective shared run allowed to appear green?

What evidence must be preserved before repairing the selector?

Why must the full-contract lane not ask the incremental selector what to test?

After repairing shared fan-out, what is the smallest equivalent proof?

What does Chapter 29 add next?

Official references and version notes

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.