From 3d4a272e94874fde2ae1b89c71632ba3f764d062 Mon Sep 17 00:00:00 2001 From: hotragn Date: Fri, 21 Aug 2026 19:46:38 -0400 Subject: [PATCH] Run the two typecheck scripts nothing ran, and build the images from their lockfiles Two gates were written, committed, and never reached. `agent-computer` and `supervisor` each ship `"typecheck": "tsc --noEmit"`. Root `typecheck` is `bun run --filter '*' typecheck`, which enumerates workspaces, and `workspaces` is app, server and worker. So neither script has ever run: the process holding the only `spawn` in the deployment, and the only one holding a Docker socket, reached an image without a type check. Both pass today, so this is a gate turned on rather than a backlog cleared. A separate job rather than another line in `static`, because each installs from its own lockfile and the root install cannot produce their dependency trees. Matrixed so each reports its own result, and in `verify` so branch protection needs no new entry. The lockfiles are the other half, and they were worse than unenforced. Every deployable but the root one installed with a plain `bun install`, and its `bun.lock` was not in the build context at all, so the committed file could not have been honoured however the command was written. Each build resolved afresh: an image built next month was not the image built today, which is the drift the note about pinning Bun is already there to prevent, twenty-three lines above the install that ignored it. It is also what #64's provenance attestation signs, so the digest was exact and part of what went into it was decided by nothing in this repository. `agent-computer`, `supervisor`, `agent-bot` and `agent-langgraph` now copy their lockfile and install `--frozen-lockfile`. All four were checked against their committed lockfile first, so this pins what is already resolved rather than asking for a refresh. The `examples/` packages keep their lockfiles and are left alone: nothing installs them in CI or in an image, so there is no build to pin. --- .github/workflows/ci.yml | 37 ++++++++++++++++++++++++++++++++++++- CHANGELOG.md | 10 ++++++++++ Dockerfile | 7 +++++-- agent-bot/Dockerfile | 5 +++-- agent-computer/Dockerfile | 6 ++++-- agent-langgraph/Dockerfile | 5 +++-- supervisor/Dockerfile | 5 +++-- 7 files changed, 64 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efc75863..3d07ede8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -214,13 +214,48 @@ jobs: - if: always() run: docker rm -f openbot-ci >/dev/null 2>&1 || true + # The two packages `static` cannot reach. + # + # Root `typecheck` is `bun run --filter '*' typecheck`, which enumerates workspaces, and `workspaces` + # is app, server and worker. `agent-computer` and `supervisor` are deployables of their own with their + # own lockfiles, and both have shipped a `typecheck` script that nothing ever ran — so a change to + # the process holding the only `spawn` in the deployment, or to the only thing holding a Docker + # socket, reached an image without a type check. + # + # A separate job rather than another line in `static` because each installs from its own lockfile: + # the root install cannot produce their dependency trees. Matrixed so each reports its own result, + # and `fail-fast: false` so one failing does not hide the other's answer. + deployables: + name: types, ${{ matrix.package }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + package: [agent-computer, supervisor] + # Nothing here needs a browser, and the install would otherwise be a few hundred megabytes for + # `agent-computer`. Bun does not run an untrusted package's postinstall anyway; this says so on + # purpose rather than relying on it. + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + with: + bun-version: 1.3.14 + - run: bun install --frozen-lockfile + working-directory: ${{ matrix.package }} + - run: bun run typecheck + working-directory: ${{ matrix.package }} + # One check for branch protection to require. A new job above is covered by this without anybody # remembering to add it to a list, and a job that was skipped for the wrong reason is not a pass. verify: name: verify runs-on: ubuntu-latest if: always() - needs: [static, test, build, migrations, image] + needs: [static, deployables, test, build, migrations, image] steps: - name: Require every check env: diff --git a/CHANGELOG.md b/CHANGELOG.md index c2456167..7429998b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,16 @@ Sessions survive and nobody signs in again. ### Changed +- **The images are built from the lockfiles that are committed.** Every deployable but the root one + installed with a plain `bun install`, and its `bun.lock` was not even in the build context, so the + committed file could not have been honoured and each build resolved dependencies afresh. An image + built next month was not the image built today, which is the drift the pinned Bun version was + already there to prevent, one layer down — and it is also what a build provenance attestation is + signing. `agent-computer`, `supervisor`, `agent-bot` and `agent-langgraph` now copy their lockfile + and install `--frozen-lockfile`. CI also typechecks `agent-computer` and `supervisor`, which each + shipped a `typecheck` script that nothing ran: they are not workspaces, so root `typecheck` never + reached the process holding the only `spawn` in the deployment, or the only one holding a Docker + socket. - **This deployment does not search documents itself.** A Bot answers from a live system by calling that system's own search as the person asking, so the vendor decides what they may see and there is no second copy of anybody's documents here to keep in step, to secure, or to leave behind when diff --git a/Dockerfile b/Dockerfile index 473ba1a6..24b97ac0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,8 +45,11 @@ COPY server/package.json server/package.json COPY worker/package.json worker/package.json RUN bun install --frozen-lockfile -COPY agent-computer/package.json agent-computer/package.json -RUN cd agent-computer && bun install +# Its lockfile too, and installed against it. `agent-computer/bun.lock` is committed but was not in +# the build context, so it could not have been honoured: this resolved afresh every build, which is +# the drift the note about pinning Bun above is there to prevent, one layer down. +COPY agent-computer/package.json agent-computer/bun.lock agent-computer/ +RUN cd agent-computer && bun install --frozen-lockfile # A second tree with the build-time dependencies left out, for the runtime stage to take. Vite, # biome and the test tooling are a gigabyte that nothing in a running container imports. diff --git a/agent-bot/Dockerfile b/agent-bot/Dockerfile index 6251f01a..2a3c720b 100644 --- a/agent-bot/Dockerfile +++ b/agent-bot/Dockerfile @@ -5,8 +5,9 @@ FROM oven/bun:1.3-alpine WORKDIR /app RUN chown bun:bun /app USER bun -COPY --chown=bun:bun agent-bot/package.json ./agent-bot/ -RUN cd agent-bot && bun install +COPY --chown=bun:bun agent-bot/package.json agent-bot/bun.lock ./agent-bot/ +# Installed against the committed lockfile, which was not in the build context before. +RUN cd agent-bot && bun install --frozen-lockfile # Preserve the repo layout so `../../shared/bot-prompt` resolves the same in the image and locally. COPY --chown=bun:bun shared ./shared diff --git a/agent-computer/Dockerfile b/agent-computer/Dockerfile index 594db89e..be36bffc 100644 --- a/agent-computer/Dockerfile +++ b/agent-computer/Dockerfile @@ -11,8 +11,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends unzip \ ENV PATH="/root/.bun/bin:${PATH}" WORKDIR /app -COPY agent-computer/package.json ./ -RUN bun install +COPY agent-computer/package.json agent-computer/bun.lock ./ +# Installed against the committed lockfile. It was not in the build context before, so the file +# existed and could not have been honoured. +RUN bun install --frozen-lockfile COPY agent-computer/src ./src diff --git a/agent-langgraph/Dockerfile b/agent-langgraph/Dockerfile index a8223fa8..c3c49e7b 100644 --- a/agent-langgraph/Dockerfile +++ b/agent-langgraph/Dockerfile @@ -5,8 +5,9 @@ FROM oven/bun:1.3-alpine WORKDIR /app -COPY agent-langgraph/package.json ./agent-langgraph/ -RUN cd agent-langgraph && bun install +COPY agent-langgraph/package.json agent-langgraph/bun.lock ./agent-langgraph/ +# Installed against the committed lockfile, which was not in the build context before. +RUN cd agent-langgraph && bun install --frozen-lockfile # The repo's own layout, kept exactly, so `../../shared/bot-prompt` resolves the same here as it # does on a laptop. Flattening src to /app/src would change the depth and break only on deploy. diff --git a/supervisor/Dockerfile b/supervisor/Dockerfile index e45689f0..6e75fe2d 100644 --- a/supervisor/Dockerfile +++ b/supervisor/Dockerfile @@ -10,8 +10,9 @@ FROM ghcr.io/spiffe/spire-server:1.15.1 AS spire FROM oven/bun:1.3.14-alpine WORKDIR /app -COPY supervisor/package.json ./ -RUN bun install +COPY supervisor/package.json supervisor/bun.lock ./ +# Installed against the committed lockfile, which was not in the build context before. +RUN bun install --frozen-lockfile COPY --from=spire /opt/spire/bin/spire-server /usr/local/bin/spire-server