Files
chainlit--chainlit/docs/plans/2026-04-06-ci-cache-p2-react-client-build-cache.md
T
wehub-resource-sync b7f52be4c9
CI / Run CI (push) Has been cancelled
CI / check-backend (push) Has been cancelled
CI / check-frontend (push) Has been cancelled
CI / tests (push) Has been cancelled
CI / e2e-tests (push) Has been cancelled
Copilot Setup Steps / copilot-setup-steps (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:48:47 +08:00

11 KiB

CI Cache Optimization P2 Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Build the frontend artifact that CI still duplicates today exactly once per commit and restore it in downstream jobs, cutting repeated @chainlit/react-client builds from the check-frontend matrix.

Architecture: On the current feat/refactor-scripts branch, P1 is already implemented and P3 has already collapsed the old prepare/validate split in e2e-tests. The original P2 write-up in docs/research/ci-cache-optimization.md assumes pnpm run buildUi still runs in tests and e2e-tests, but that is no longer true in the current workflows. The remaining explicit duplicated frontend build is pnpm --filter @chainlit/react-client build inside the three-leg check-frontend matrix, so this plan seeds libs/react-client/dist once in ci.yaml, restores it in check-frontend.yaml, and leaves tests, e2e-tests, and backend checks untouched.

Tech Stack: GitHub Actions reusable workflows, actions/cache@v5, pnpm 9, TypeScript 5, tsup

Assumptions

  • P1 is already merged on this branch: e2e-tests.yaml uses actions/cache@v5 for the Cypress binary and no longer uses cypress-io/github-action.
  • P3 is already merged on this branch: e2e-tests.yaml has a single prepare job and no separate validate job.
  • Backward compatibility matters more than theoretical optimality. Do not rename existing top-level CI jobs that branch protection may already depend on.
  • The cache boundary for this iteration is only libs/react-client/dist. Do not broaden scope to frontend/dist or libs/copilot/dist unless a fresh measurement proves they are still rebuilt in CI.

Success Criteria

  • check-frontend no longer runs pnpm --filter @chainlit/react-client build in each matrix job.
  • A new cache-seeding job restores or builds libs/react-client/dist once per github.sha.
  • check-frontend fails fast if the expected cache is missing, instead of silently rebuilding.
  • check-backend, tests, and e2e-tests keep their current behavior.
  • A second CI run for the same commit shows a cache hit in the seed job and no rebuild.

File Map

Action File Responsibility
Modify .github/workflows/ci.yaml Add a one-time cache-seeding job and wire existing jobs to depend on it
Modify .github/workflows/check-frontend.yaml Restore libs/react-client/dist from cache and remove the duplicated inline build
Modify docs/research/ci-cache-optimization.md Update P2 notes so the research doc matches the current branch reality

Task 1: Seed @chainlit/react-client Build Cache Once

Files:

  • Modify: .github/workflows/ci.yaml

  • Step 1: Add a dedicated cache-seeding job

Insert this job above check-frontend in .github/workflows/ci.yaml:

build-react-client:
  name: Build @chainlit/react-client once
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v6
    - uses: ./.github/actions/pnpm-node-install
      name: Install Node, pnpm and dependencies.
    - name: Cache @chainlit/react-client dist
      id: react-client-dist
      uses: actions/cache@v5
      with:
        path: libs/react-client/dist
        key: react-client-build-${{ github.sha }}
    - name: Build @chainlit/react-client
      if: steps.react-client-dist.outputs.cache-hit != 'true'
      run: pnpm --filter @chainlit/react-client build

Why this exact shape:

  • github.sha is the safest cache key for compiled output because it matches the exact source tree used by downstream jobs.

  • actions/cache@v5 restores immediately and saves at job completion, so downstream jobs can rely on the cache after needs: build-react-client.

  • ubuntu-latest matches the current check-frontend runner, so no cross-OS cache settings are needed.

  • Step 2: Wire check-frontend and the CI aggregator to the new job

In .github/workflows/ci.yaml, change the existing jobs to:

check-frontend:
  needs: [build-react-client]
  uses: ./.github/workflows/check-frontend.yaml
  secrets: inherit

ci:
  runs-on: ubuntu-slim
  name: Run CI
  if: always()
  needs: [check-backend, build-react-client, check-frontend, tests, e2e-tests]

The extra build-react-client entry in ci.needs is required. Without it, a failed seed job can cause check-frontend to be skipped and the final ci job would not treat that upstream failure as a hard failure.

  • Step 3: Verify the workflow file still formats cleanly

Run:

pnpm format-check:files .github/workflows/ci.yaml

Expected: exit code 0 and no output complaining about YAML formatting.

  • Step 4: Commit
git add .github/workflows/ci.yaml
git commit -m "ci: seed react-client build cache once per commit"

Task 2: Restore the Cache in check-frontend

Files:

  • Modify: .github/workflows/check-frontend.yaml

  • Step 1: Replace the inline build step with cache restore + explicit failure

In .github/workflows/check-frontend.yaml, replace:

- name: Build @chainlit/react-client
  run: pnpm --filter @chainlit/react-client build

with:

- name: Restore @chainlit/react-client dist
  id: react-client-dist
  uses: actions/cache@v5
  with:
    path: libs/react-client/dist
    key: react-client-build-${{ github.sha }}

- name: Fail if @chainlit/react-client dist cache is missing
  if: steps.react-client-dist.outputs.cache-hit != 'true'
  run: |
    echo "❌ Error: expected react-client build cache for ${GITHUB_SHA}"
    exit 1

Do not add a fallback rebuild here. The point of P2 is to prove the seed job is the single source of truth for this build artifact. Silent rebuilds would hide broken cache wiring.

  • Step 2: Keep the matrix commands unchanged

After the cache restore, the job should still run the existing commands exactly as they are today:

- name: ${{ matrix.name }}
  run: ${{ matrix.command }}

This preserves the existing job names:

  • Linting: frontend
  • Formatting: frontend
  • Type checking: frontend

and avoids unnecessary branch-protection churn.

  • Step 3: Run the local smoke checks in the same order CI depends on

Run:

pnpm --filter @chainlit/react-client build
pnpm lint
pnpm format-check
pnpm type-check

Expected:

  • pnpm --filter @chainlit/react-client build writes libs/react-client/dist

  • pnpm lint passes

  • pnpm format-check passes

  • pnpm type-check passes

  • Step 4: Commit

git add .github/workflows/check-frontend.yaml
git commit -m "ci: restore react-client build output in frontend checks"

Task 3: Update the Research Doc So P2 Is No Longer Stale

Files:

  • Modify: docs/research/ci-cache-optimization.md

  • Step 1: Rewrite the P2 problem statement to match the current branch

Update the P2 section so it says the following, in substance:

### P2: Build `@chainlit/react-client` once at CI startup, share via cache

**Status**: Still useful on `feat/refactor-scripts`, but the scope is narrower than the original draft.

**Current reality on this branch**:

- `tests.yaml` no longer runs `pnpm run buildUi`
- `e2e-tests.yaml` no longer runs `pnpm run buildUi`
- `check-frontend.yaml` still runs `pnpm --filter @chainlit/react-client build` once per matrix leg

**Approach**: Seed `libs/react-client/dist` once in `ci.yaml` with `actions/cache`, then restore it in `check-frontend.yaml` and fail on cache miss instead of rebuilding.

Keep the rest of the research document intact. This is a surgical doc correction, not a fresh rewrite.

  • Step 2: Update the expected impact table

Change the P2 estimate from the old “buildUi in 7 jobs” numbers to the narrower current branch estimate:

| Change                                                       | Current                | After       | Saving                        |
| ------------------------------------------------------------ | ---------------------- | ----------- | ----------------------------- |
| P2: cache `@chainlit/react-client` build in `check-frontend` | ~20-40s per matrix leg | ~5s restore | ~45-90s cumulative per CI run |

Use the final measured numbers from the first successful CI run if they differ materially from this estimate.

  • Step 3: Verify markdown formatting

Run:

pnpm format-check:files docs/research/ci-cache-optimization.md

Expected: exit code 0.

  • Step 4: Commit
git add docs/research/ci-cache-optimization.md
git commit -m "docs: narrow P2 cache optimization scope to current workflows"

Task 4: Verify the New Workflow Behavior in CI

Files:

  • No file changes

  • Step 1: Push the branch and wait for the CI workflow to finish

Run:

git push

Expected: a new CI workflow run starts for the current commit.

  • Step 2: Inspect the first run

Verify in the GitHub Actions UI:

  • build-react-client shows either:

    • cache miss + one pnpm --filter @chainlit/react-client build, or
    • cache hit + skipped build on a rerun of the same commit
  • each check-frontend matrix job restores react-client-build-${{ github.sha }}

  • no check-frontend matrix job logs an inline pnpm --filter @chainlit/react-client build

  • Step 3: Re-run the same commit once

Use the GitHub Actions UI to re-run the workflow for the same SHA.

Expected:

  • build-react-client shows cache-hit: true

  • the build step is skipped

  • all check-frontend matrix jobs still restore the cache and pass

  • Step 4: Record the measured timings in the research doc

If the measured timing differs from the estimate in Task 3, update the numbers in docs/research/ci-cache-optimization.md before merging.

  • Step 5: Commit
git add docs/research/ci-cache-optimization.md
git commit -m "docs: record measured P2 CI cache results"

Non-Goals

  • Do not reintroduce a full buildUi job unless fresh evidence shows tests or e2e-tests need it again.
  • Do not cache frontend/dist or libs/copilot/dist in this change.
  • Do not change pnpm-node-install or uv-python-install; P2 does not require action-level abstraction.
  • Do not change publish workflows as part of this task.

Self-Review

Spec coverage: This plan covers P2 only and explicitly adapts it to the current branch state after P1 and P3. It does not assume the stale “buildUi in 7 jobs” model from the research draft.

Placeholder scan: No TODO, TBD, or “handle appropriately” placeholders remain. Each workflow edit has exact YAML and each verification step has an exact command or a concrete UI check.

Consistency: The cache key is consistently react-client-build-${{ github.sha }} and the cached path is consistently libs/react-client/dist in both producer and consumer jobs.