Repository navigation
Measure and trim the Next.js build-trace step in the webapp build #568
Description
Part of #554. Change set E. Work on main. Rulings: #554 rulings 1, 4, 5, 15, 16.
Summary
The longest webapp next build phase is "Collecting build traces". It takes 99.3–118.6 s (median 107.4 s) in the prod Docker build and 39.0–67.6 s (median 54.7 s) in 🏗️ Build Verification. Nobody knows which work fills it, and Next 15.5.27 has no switch to turn it off.
This issue measures first: a trace artifact from Build Verification and two timing lines in the Docker log. Only if the go rule passes does next.config.js get outputFileTracingExcludes for build-only packages. An excluded runtime file breaks the image, so Step 8 proves each exclude locally first.
Measured windows ("Collecting build traces ..." to "Route (pages)"):
| Run | Deploy job (Docker) | Docker window s | next build s |
Build Verification job (hosted) | Hosted window s |
|---|---|---|---|---|---|
| 39398067267 | 118549257854 | 99.7 | 226.6 | 118548278109 | 39.0 |
| 39297468969 | 118217497479 | 99.3 | 185.4 | 118215792135 | 67.6 |
| 39275929831 | 118142985559 | 110.3 | 248.6 | 118141679143 | 42.2 |
| 38392614215 | 115281929767 | 118.6 | 262.5 | 115280995457 | 42.6 |
| 38239392350 | 114780192678 | 111.7 | 255.6 | 114778980229 | 66.8 |
| 38068638114 | 114262648443 | 104.4 | 209.1 | 114261976214 | 66.7 |
In job 118217497479 the other phases were shorter: type check 30.1 s, compile 36.7 s, page data and static pages 11.4 s.
Depends on / Blocks
Depends on nothing; blocks nothing. Same files, no order: #560 (apps/webapp/next.config.js lines 77-94, the Dockerfile builder stage), #565 (moves 🏗️ Build Docker Images into an images job), #557 (the comment at Dockerfile.bun:214-218).
Current behaviour
Links are pinned to d0a53756f8676556358b6adc6c2397d2ed7c64c8. Upstream Next links are pinned to tag v15.5.27.
-
Standalone, traced from the monorepo root (
next.config.js:70-75):output: 'standalone',outputFileTracingRoot: path.join(__dirname, '../../'), nooutputFileTracingExcludes. -
A custom
webpackfunction (next.config.js:135-167) turns the webpack build worker off (index.ts:1613-1618), so tracing runs in the main process after the compile. -
collectBuildTracesruns on every non-generatebuild (index.ts:2610-2611). -
The window covers two jobs. The spinner starts at
index.ts:3826and waits for the trace atindex.ts:3972. It then copies every traced file into.next/standalone(index.ts:4014-4028), thewrite-standalone-directoryspan (index.ts:699). The route table prints after both (index.ts:4040). So the window is the tail ofnode-file-trace-buildplus the whole copy. -
Span names are
node-file-trace-build(collect-build-traces.ts:111) andapply-include-excludes(:611). WithDEBUG=next:build:build-traces, Next printsfinished build tracing <ms>ms(:737). -
How an exclude saves time. A key that matches
next-serverfeeds the trace-time ignore list (collect-build-traces.ts:180-187, used at:342-345). An ignored file is never read, followed or copied. The key'*'matchesnext-server; a one-route key acts after tracing and saves copy time only. The matcher usescontains: trueon paths relative to the monorepo root (:190-203), so**/node_modules/<package>/**matches Bun store paths, for examplenode_modules/.bun/rollup@4.63.1/node_modules/rollup/dist/rollup.js. -
Build-time packages through Sentry.
@sentry/nextjs10.76.2 depends onrollup,@rollup/plugin-commonjs,@sentry/bundler-pluginsand@sentry/webpack-plugin(bun.lock:1465).@sentry/cliships one native binary per platform (bun.lock:1441). Step 5 measures whether the trace reaches them. -
Build Verification keeps nothing (
prod.docs.plus.yml:390-396):- name: 🏗️ Build Webapp run: bun run --filter @docs.plus/webapp build:ci
The runner throws away
apps/webapp/.next/trace. The repo pinsactions/upload-artifactat043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1(prod.docs.plus.yml:250-258). -
The Docker build prints no timing (
Dockerfile.bun:212-219):RUN --mount=type=cache,target=/app/apps/webapp/.next/cache bunx next buildThe layer is never reused (the git hash is inlined), so changing this line costs no cache hit.
-
The image ships only the traced output (
Dockerfile.bun:242-252):.next/standalone,.next/staticandpublic. -
Missing-file checks today. The image
HEALTHCHECKfetches/api/health(Dockerfile.bun:280-282).🩺 Verify Deploymentrunssmoke webapp 3000 /api/health(prod.docs.plus.yml:766); a failure triggers🚨 Rollback on Failure. Neither check loads a pad; Step 8 and Accountability cover that by hand. -
/c/[channelId], a Cypress harness page, returns 404 withoutNEXT_PUBLIC_E2E=true([channelId].tsx:80-87) but still loads the chatroom module graph, the largest page bundle (214 kB in job 118217497479). A correct image answers 404; a broken one answers 500.
Target behaviour
- After Steps 1-3, kept after close: every Build Verification run uploads
webapp-next-trace-<sha>(traceand every*.nft.jsonfromapps/webapp/.next, 7 days), and the upload never fails the job. Every webapp Docker build printsstarting build tracesandfinished build tracing <ms>ms; the image and cache do not change. A "Measured, " comment splits the window into trace and copy time. - Only if the Step 6 go rule passes, after Step 7: one
'*'key with candidate globs..next/standalonegains no file and loses only excluded packages and their dependencies. The image starts healthy, a pad loads, and/c/<id>answers 404, not 500. The Docker window falls by at least 20 s, or Step 7 is reverted.
Implementation steps
Read every file in Read first before Step 0. "Comment here" means a comment on this issue, never a code comment.
Step 0. Read and check the base
Run on current main. Compare with Current behaviour.
git show main:apps/webapp/next.config.js | sed -n '70,75p;135,137p'
git show main:apps/webapp/docker/Dockerfile.bun | sed -n '212,219p;242,252p;280,282p'
git show main:.github/workflows/prod.docs.plus.yml | sed -n '250,258p;390,399p;766p'
git show main:apps/webapp/package.json | grep -n '"next":'
grep -n 'outputFileTracingExcludes' apps/webapp/next.config.js
grep -c -E "next:build:build-traces|finished build tracing|'node-file-trace-build'" apps/webapp/node_modules/next/dist/build/collect-build-traces.js
grep -c "'write-standalone-directory'" apps/webapp/node_modules/next/dist/build/index.js- The last two counts must each be at least
1;0means a renamed span or namespace. Stop and comment here. - Expect
"next": "15.5.27"or another15.5.x. If Next is 16 or later, stop and comment here (Turbopack by default). - Expect no
outputFileTracingExcludesmatch. If one exists, comment here before you add a second. - Find a moved line by text. Skip the duplicate type check inside the webapp Docker build #560 moves
next.config.jslines after 76 and addsbuilderlines. Make CI failures reach Telegram, and fix stale CI facts #557 replaces the five comment lines above theRUN --mount=type=cache,target=/app/apps/webapp/.next/cache bunx next buildline with four; check only that theRUNline is unchanged. - If any other text differs, stop and comment here with the new text.
- If Build the deploy images while the quality gates run #565 has landed, read the
imagesjob in Step 5.
Step 1. Upload the trace files from Build Verification
File: .github/workflows/prod.docs.plus.yml, job build. Add this right after 🏗️ Build Webapp (line 396), before 🏗️ Build Admin Dashboard:
# Measurement aid for the "Collecting build traces" phase; it must never block a deploy.
- name: 📤 Upload webapp build trace
if: ${{ !cancelled() }}
continue-on-error: true
timeout-minutes: 5
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: webapp-next-trace-${{ github.sha }}
path: |
apps/webapp/.next/trace
apps/webapp/.next/*.nft.json
apps/webapp/.next/server/**/*.nft.json
if-no-files-found: warn
include-hidden-files: true
retention-days: 7
overwrite: trueinclude-hidden-files: trueis required: the action merges the paths into the rootapps/webapp/.nextand skips dot paths by default, so it would upload zero files silently.- Copy the SHA pin from line 252. Never use a floating tag.
overwrite: truelets "Re-run jobs" reuse the name.
actionlint -config-file .github/actionlint.yaml .github/workflows/prod.docs.plus.ymlStep 2. Print Next's trace timing inside the Docker build
File: apps/webapp/docker/Dockerfile.bun, the RUN ... bunx next build line (line 219 at the pinned commit). Replace that one line with:
# DEBUG prints "starting build traces" and "finished build tracing <ms>ms", so the
# deploy log splits the "Collecting build traces" phase into trace and copy time.
RUN --mount=type=cache,target=/app/apps/webapp/.next/cache \
DEBUG=next:build:build-traces bunx next buildKeep the comment above the RUN line as on main (#557 owns it). Add no ARG or ENV, so no install cache key changes.
docker build --check -f apps/webapp/docker/Dockerfile.bun .Then check that the lines print, under make dev-backend, not make dev-local (Step 8 says why). .env.local sets NODE_ENV=development and dotenv does not override it, so every local build here sets NODE_ENV=production.
bun scripts/check-next-server.ts
(cd apps/webapp && NODE_ENV=production DEBUG=next:build:build-traces bunx dotenv -e ../../.env.local -- bunx next build 2>&1 | grep 'build-traces')
rm -rf apps/webapp/.nextcheck-next-server.ts must exit 0. Expect starting build traces and finished build tracing <n>ms; ignore the laptop number. Then stop make dev-backend and start make dev-local again.
Step 3. Record the artifact in the workflow reference
File: .github/WORKFLOWS.md, §Active workflows, the workflows/prod.docs.plus.yml entry. Add one bullet after "Stages:":
- Build Verification uploads the webapp Next trace files as the `webapp-next-trace-<sha>` artifact, kept 7 days. The webapp Docker build prints Next's `next:build:build-traces` debug lines. Both are measurement aids and never fail a runStep 4. Hand-over checkpoint for the measurement change
git diff -- .github/workflows/prod.docs.plus.yml apps/webapp/docker/Dockerfile.bun .github/WORKFLOWS.mdShow the diff and stop for review. Then commit and push per SP2 (#554), as its own commit. The measurement rides normal deploys (ruling 4).
A webapp deploy is a push whose tip subject is a valid (build): subject with a front or back token and no no-deploy token. Only these set app_deploy=true in .github/scripts/parse-build-trigger.sh, so the deploy job runs (prod.docs.plus.yml:431-435) and rebuilds the webapp image.
Step 5. Read the spans and post the baseline split
You need one push run with Build Verification and two webapp deploys, all after Step 4.
-
Hosted spans. Print the 25 longest spans:
RUN=<run id> gh run download "$RUN" --repo docs-plus/docs.plus --pattern 'webapp-next-trace-*' --dir /tmp/next-trace ls -R /tmp/next-trace | head bun -e ' const ev = (await Bun.file(process.argv.at(-1)).text()).trim().split("\n").flatMap((l) => JSON.parse(l)); const byId = new Map(ev.map((e) => [e.id, e])); for (const e of [...ev].sort((a, b) => b.duration - a.duration).slice(0, 25)) console.log((e.duration / 1e6).toFixed(1).padStart(7), "s", e.name.padEnd(36), "parent:", byId.get(e.parentId)?.name ?? "-"); ' /tmp/next-trace/webapp-next-trace-*/trace
durationis in microseconds. Recordnode-file-trace-build,apply-include-excludesandwrite-standalone-directory. -
Traced files per package, each file counted once by its innermost
node_modules/<package>:bun -e ' const path = require("node:path"); const dir = path.resolve(process.argv.at(-1)); const files = new Set(); for await (const f of new Bun.Glob("**/*.nft.json").scan(dir)) { const nft = path.join(dir, f); for (const x of (await Bun.file(nft).json()).files) files.add(path.resolve(path.dirname(nft), x)); } const count = new Map(); for (const f of files) { const m = f.match(/node_modules\/(@[^/]+\/[^/]+|[^@/.][^/]*)\//g); const pkg = m ? m.at(-1).slice(13, -1) : "(workspace)"; count.set(pkg, (count.get(pkg) ?? 0) + 1); } console.log("traced files:", files.size); for (const [p, n] of [...count].sort((a, b) => b[1] - a[1]).slice(0, 40)) console.log(String(n).padStart(6), p); ' /tmp/next-trace/webapp-next-trace-*
Mark each package on the Step 7 candidate list. Sum their files.
-
Hosted window, from the same run's Build Verification job (second timestamp minus first):
JOB=<Build Verification job id> gh api --allow-escape-sequences "repos/docs-plus/docs.plus/actions/jobs/${JOB}/logs" | sed 's/\x1b\[[0-9;]*m//g' \ | grep -m2 -E 'Collecting build traces|Route \(pages\)'
-
Docker split, for each deploy, from the job that holds
🏗️ Build Docker Images:JOB=<deploy or images job id> gh api --allow-escape-sequences "repos/docs-plus/docs.plus/actions/jobs/${JOB}/logs" | sed 's/\x1b\[[0-9;]*m//g' > /tmp/deploy.log
After Try running the backend and webapp image builds at the same time #567, webapp lines carry
[front]and the two builds share BuildKit ids. Ifgrep -q '\[front\] ' /tmp/deploy.logsucceeds, rungrep '\[front\] ' /tmp/deploy.log | sed 's/\[front\] //' > /tmp/front.logand use/tmp/front.logbelow.LOG=/tmp/deploy.log # or /tmp/front.log, as above STEP=$(grep 'webapp builder' "$LOG" | grep -m1 'next build' | awk '{print $2}') grep -E "^\S+ ${STEP} .*(Collecting build traces|next:build:build-traces|Route \(pages\))|^\S+ ${STEP} DONE" "$LOG"
The third column is BuildKit seconds since the step began:
- window =
Route (pages)minusCollecting build traces; - trace part =
finished build tracingminusCollecting build traces; - copy part =
Route (pages)minusfinished build tracing.
- window =
-
Comment here, headed
Measured, <YYYY-MM-DD> (before excludes):Source Run id Job id Window s Trace part s Copy part s next buildsM3 s Docker … … … … … … … Docker … … … … … … … Hosted … … … — — — — Add the three hosted span seconds;
node-file-trace-buildstarts atindex.ts:2610-2611, before the window opens at:3826, so it is not a share of the window. Add the📤 Upload webapp build traceseconds, the traced and candidate file totals and their ratio. Add the projected saving, median Docker window × candidate ratio, labelled "read from code, not measured".
Step 6. Apply the go rule
- Go when the Step 5 projected saving is at least 30 s; span totals do not count. Continue at Step 7.
- No go: comment here with the reason and the numbers. Skip Steps 7-9. The maintainer closes the issue as measured.
Step 7. Add outputFileTracingExcludes
File: apps/webapp/next.config.js. Insert right after outputFileTracingRoot (line 75):
// Build-time packages the standalone server never loads. The '*' key also feeds
// Next's trace-time ignore, so their files are neither walked nor copied.
// Add an entry only with the start-and-route proof in AGENTS.md §Docker. Never sharp or @img.
outputFileTracingExcludes: {
'*': [
'**/node_modules/rollup/**',
'**/node_modules/@rollup/**',
'**/node_modules/@sentry/cli/**',
'**/node_modules/@sentry/cli-*/**'
]
},Write only globs whose package showed traced files in Step 5. The four above show the shape. Pick from this list only:
| Glob | Why it is a candidate |
|---|---|
**/node_modules/rollup/**, **/node_modules/@rollup/** |
dependencies of @sentry/nextjs (bun.lock:1465) and a bundler |
**/node_modules/@sentry/webpack-plugin/**, **/node_modules/@sentry/bundler-plugins/** |
Sentry's build plugins (bun.lock:1439, :1465, :1483) |
**/node_modules/@sentry/cli/**, **/node_modules/@sentry/cli-*/** |
the sourcemap upload CLI and its native binaries (bun.lock:1441) |
**/node_modules/webpack/**, **/node_modules/terser/**, **/node_modules/esbuild/**, **/node_modules/@esbuild/** |
bundlers and minifiers |
**/node_modules/@swc/core/**, **/node_modules/@swc/core-*/**, **/node_modules/typescript/** |
compilers |
**/node_modules/workbox-build/**, **/node_modules/next-pwa/** |
service worker generation at build time |
**/node_modules/@babel/core/**, **/node_modules/sass/** |
coverage instrumentation (next.config.js:136-163) and style compile |
- Never exclude (runtime packages):
sharp,@img/*,next,@next/*,react,react-dom,styled-jsx,@swc/helpers;@sentry/nextjs,@sentry/node,@sentry/core,@opentelemetry/*;@supabase/*,@tiptap/*,prosemirror-*,yjs,y-*,lib0,@hocuspocus/*;- any
@docs.plus/*package, and anything underapps/,packages/orextensions/.
- A package on neither list needs a maintainer ruling. Comment here with its file count.
- Keep one key,
'*'.
(cd apps/webapp && NODE_ENV=production NEXT_PUBLIC_RESTAPI_URL=http://localhost:4000 node -e "console.log(JSON.stringify(require('./next.config.js').outputFileTracingExcludes))" 2>/dev/null | tail -1)On main it prints undefined; after the edit, the '*' key with your globs. Use node: on 2026-10-20, bun -e failed the workbox-runtime-caching.js self-check. If the value is not unchanged, stop and comment here.
Step 8. Prove it locally
Use make dev-backend, not make dev-local: it runs the webapp inside concurrently --kill-others-on-fail (Makefile:212-215), so stopping the webapp can stop REST, WS and the worker. bun scripts/check-next-server.ts must exit 0 and print nothing. Build "before" from HEAD and "after" with Step 7. Use cp, never git stash.
-
Save your edit, then build "before", from the repo root:
cp apps/webapp/next.config.js /tmp/next.config.after.js git show HEAD:apps/webapp/next.config.js > apps/webapp/next.config.js rm -rf apps/webapp/.next (cd apps/webapp && NODE_ENV=production bunx dotenv -e ../../.env.local -- bunx next build) find apps/webapp/.next/standalone \( -type f -o -type l \) | sort > /tmp/standalone-before.txt du -sh apps/webapp/.next/standalone
-
Serve "before" as the image does (
Dockerfile.bun:286,staticandpublicbeside it):cd apps/webapp cp -R public .next/standalone/apps/webapp/public cp -R .next/static .next/standalone/apps/webapp/.next/static for e in hyperlink hypermultimedia indent inline-code placeholder mermaid; do mkdir -p .next/standalone/extensions/extension-$e cp -R ../../extensions/extension-$e/dist ../../extensions/extension-$e/package.json .next/standalone/extensions/extension-$e/ done NODE_ENV=production PORT=3999 HOSTNAME=127.0.0.1 bunx dotenv -e ../../.env.local -- bun .next/standalone/apps/webapp/server.js > /tmp/standalone.log 2>&1 & sleep 5 SLUG="trace-check-$(date +%s)" for p in /api/health / "/${SLUG}" /c/trace-check "/_next/image?url=%2Ficons%2Ffavicon-16x16.png&w=32&q=75"; do printf '%s %s\n' "$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:3999${p}")" "$p" done | tee /tmp/codes-before.txt grep -c -E 'Cannot find module|MODULE_NOT_FOUND' /tmp/standalone.log pkill -f '.next/standalone/apps/webapp/server.js' cd ../..
The loop copies what
Dockerfile.bun:260-274copies, after item 1 listed the trace. -
Restore your edit, build "after", and serve it the same way:
cp /tmp/next.config.after.js apps/webapp/next.config.js git diff --stat -- apps/webapp/next.config.js # expect only your Step 7 lines rm -rf apps/webapp/.next (cd apps/webapp && NODE_ENV=production bunx dotenv -e ../../.env.local -- bunx next build) find apps/webapp/.next/standalone \( -type f -o -type l \) | sort > /tmp/standalone-after.txt du -sh apps/webapp/.next/standalone
Repeat the item 2 block with
tee /tmp/codes-after.txtand the sameSLUGshape. -
Compare:
comm -13 /tmp/standalone-before.txt /tmp/standalone-after.txt | wc -l comm -23 /tmp/standalone-before.txt /tmp/standalone-after.txt \ | sed -E 's#.*node_modules/((@[^/]+/)?[^/]+).*#\1#' | sort | uniq -c | sort -rn diff <(cut -d' ' -f1 /tmp/codes-before.txt) <(cut -d' ' -f1 /tmp/codes-after.txt)
- The first count is
0. - Each removed package is excluded, or its dependency in
bun.lock(grep -n '"<package>": \[' bun.lock), and not on the never-exclude list. A full path with nonode_modulesis a removed workspace file and fails. On a failure, stop and comment here. - The
diffprints nothing. Expect200for/api/health,/, the slug and/_next/image, and404for/c/trace-check(200in both if.env.localsetsNEXT_PUBLIC_E2E=true). - Both
grep -cruns print0.
- The first count is
-
Browser check on "after". Start it as in item 2. Open
http://127.0.0.1:3999/<a new slug>in Chrome, type a heading and a line, and open the heading's chat. Pass: editor and chat render, with no failed chunk or module error. -
Clean up. Stop the server and run
rm -rf apps/webapp/.next. Then stopmake dev-backendand startmake dev-localagain. -
Comment here, headed
Proof, <YYYY-MM-DD> (Step 8): both line counts, bothdu -shsizes, the item 4 count and removed-package list, both code lists and the browser result.
Step 9. Docs for the exclude change
In the same change as Step 7, edit the "Only if Step 7 runs" files in Docs to update.
Review checkpoint
git diff -- apps/webapp/next.config.js AGENTS.md apps/webapp/CHANGELOG.mdShow the diff with the Step 5 and Step 8 comments, and stop for review. Then commit and push per SP2 (#554), as its own commit.
Acceptance criteria
-
📤 Upload webapp build tracefollows🏗️ Build Webapp, pinned to043fb46d1a93c77aae656e7c1c64a875d1fc6a0a, withcontinue-on-error: true,timeout-minutes: 5,include-hidden-files: trueandretention-days: 7. - One push run after Step 4 shows
webapp-next-trace-<sha>, holdingtraceand at least oneserver/pages/*.nft.json. - The first webapp deploy after Step 4 prints
starting build tracesandfinished build tracing <n>msunder the webappnext buildstep. -
actionlint -config-file .github/actionlint.yaml .github/workflows/prod.docs.plus.ymlprints nothing. - A
Measured, <date> (before excludes)comment holds the Step 5 table for two deploys and one hosted run. - A comment here records go or no go with the numbers.
- If go:
next.config.jshas one'*'key, and every glob is on the candidate list or has its own ruling comment. - If go: the Step 8 comment passes every item 4 check and the browser check.
- If go: the after-excludes comment shows the keep rule result, and Step 7 is kept or reverted to match.
-
.github/WORKFLOWS.mdlists the artifact. If Step 7 runs,AGENTS.md§Docker andapps/webapp/CHANGELOG.mdrecord it in the same change. - If Step 7 is kept: the
AGENTS.mdbullet gives the before and after Docker windows and the deploy run ids. If reverted: the bullet and the CHANGELOG line are gone.
Verification
Before each hand-over:
actionlint -config-file .github/actionlint.yaml .github/workflows/prod.docs.plus.yml
docker build --check -f apps/webapp/docker/Dockerfile.bun .
bun run lint
bun run format
bun run check:agent-docs # only when AGENTS.md changed (Step 9)Triage and gating do not change, so skip parse-build-trigger.test.sh and dry_run_subject.
After the merge. Record per SP3 (#554); metrics are defined in #569.
| Change | Metric | Read from | Baseline | Expected |
|---|---|---|---|---|
| Steps 1-2 | M2g | first push run after Step 4 | the median M2g of the last 3 (build): deploys before Step 4, in the same "Ext suite ran" slice |
at most +10 s |
| Steps 1-2 | M3 | first webapp deploy after Step 4 | median 546 s | within the baseline range, 316–866 s |
| Steps 1-2 | Docker window | same deploy, Step 5 command | 99.3–118.6 s, median 107.4 s | no change |
| Step 7 | Docker window | first two webapp deploys after Step 7 | the Step 5 median | at least 20 s lower |
| Step 7 | M3 and M2i | same two deploys | M3 median 546 s | fall by about the window drop |
| Step 7 | M1 | same two deploys | median 1090 s, p90 1386 s | falls by about the window drop; after #565, only when M2i is later than M2g |
| Step 7 | M4, M5, W | same two deploys | M4 0–72 s, M5 57–70 s | no change |
| Step 7 | Hosted window, and M6 | next PR or non-deploy push | hosted median 54.7 s; PR M6 median 519 s | record only |
Accountability
- The builder posts here:
Measured, <YYYY-MM-DD> (before excludes): Step 5.Proof, <YYYY-MM-DD> (Step 8): the local proof.Measured, <YYYY-MM-DD> (after excludes): per SP3 ([Tracking] Faster, change-aware CI/CD pipeline #554), for the two deploys after Step 7, plus job ids, window, trace and copy parts, and the keep rule result.
- The maintainer, within one hour of the first Step 7 deploy, checks and posts
Checked, <YYYY-MM-DD>:https://docs.plus/api/healthreturns200, andhttps://docs.plus/c/trace-checkreturns404, not500.- A real pad loads in Chrome, takes typing, and opens a heading chat.
https://docs.plus/_next/image?url=%2Ficons%2Ffavicon-16x16.png&w=32&q=75returns200.- The webapp container log has no
Cannot find moduleorMODULE_NOT_FOUNDline. - 24 hours later, GlitchTip shows no new missing-module error.
- Stop condition, revert Step 7 per SP4 ([Tracking] Faster, change-aware CI/CD pipeline #554) when any of these holds:
🩺 Verify Deploymentfails on a Step 7 deploy (it rolls back by itself; revert before the next deploy).- Any maintainer check above fails.
- The median Docker window of the first two Step 7 deploys is less than 20 s below the Step 5 median.
- Stop condition for Steps 1-2: Build Verification grows by more than 30 s, or either change makes a job fail.
Rollback
Revert per SP4 (#554).
- Steps 1-3: no image content changes, so no deploy is needed. The artifacts expire after 7 days.
- Step 7: deploy the revert with the tip subject
(build): front. Neverno-deploy,observabilityor a bare(build):; none deploys the webapp. The revert also removes the Step 9AGENTS.mdbullet and CHANGELOG line. - No host step and no GitHub setting changes in this issue.
Docs to update
.github/WORKFLOWS.md§Active workflows,workflows/prod.docs.plus.ymlentry: the artifact and the debug lines (Step 3).- Only if Step 7 runs:
AGENTS.md§Docker: one bullet with the rule and the never-exclude list, no numbers yet. The rule: "AnoutputFileTracingExcludesentry ships only after two proofs. A file-list diff removes only the excluded packages and their own dependencies. A standalone start serves/api/health, a pad and/c/<id>with the same codes as before." Runbun run check:agent-docs.apps/webapp/CHANGELOG.md[Unreleased]### Internal: one line, "The standalone build no longer traces or ships build-only packages: ."
- After the keep rule passes: a second hand-over adds the before and after Docker windows and run ids to that bullet. Show
git diff -- AGENTS.md, runbun run check:agent-docs, stop for review, then commit and push per SP2 ([Tracking] Faster, change-aware CI/CD pipeline #554).
Out of scope
- Turbopack for the production build:
next-pwa, thechunkGraphexclude and the Sentry plugin need webpack. - Changing
output: 'standalone'oroutputFileTracingRoot(Dockerfile.bun:242-286depends on both). experimental.webpackBuildWorkerorexperimental.parallelServerBuildTraces; file a separate issue with the Step 5 data.- A Next 16 upgrade, or moving
withSentryConfigto@sentry/nextjs/config. - Any dependency change. The
metascraperpin and the Bun floor of1.4.0stay. - Any other Dockerfile edit (Skip the duplicate type check inside the webapp Docker build #560, Make the webapp Docker installs fast and cacheable #559, Make CI failures reach Telegram, and fix stale CI facts #557).
- A new automated test. Step 8, the image health check and
🩺 Verify Deploymentcover the failure mode. - Extra or A/B deploys (ruling 4), the settled
AGENTS.md§Docker rules, and.next/cachein CI.
Read first
AGENTS.md§Package Manager, §Git And Commits, §Test Policy (nevernext buildagainst a live dev server) and §Docker.apps/webapp/CLAUDE.md..github/WORKFLOWS.mdand.github/workflows/prod.docs.plus.yml, jobsbuildanddeploy.apps/webapp/next.config.jsandapps/webapp/docker/Dockerfile.bun, in full.- Next 15.5.27 sources:
packages/next/src/build/collect-build-traces.tsandpackages/next/src/build/index.ts, lines 3820-4045. - The
tech-writerskill, for the comments and theAGENTS.mdbullet.
Activity