← Cycles
Design · c129

Deploy Build Time

parkedparked 2026-11-03 — see PICKUP.md; query-link fix took the deploy build from 17.5 to 4.4 min, next trims rankedcreated 2026-11-03· last activity 2026-11-03· mode: evolution. The deploy pipeline (c49, in place since 2026-10-18) stays as it is; this cycle trims what the crawler does.

Keep `npm run deploy` short as the lab grows. The prerender crawler renders every page it can reach, and the build's time goes wherever the crawl goes.

Why

On 2026-11-03 a deploy ran past 20 minutes, and two concurrent deploys collided in .nuxt-deploy. The phase-4 lab links to itself with query knobs (?findingId=…&policyId=…&team=…). Nitro's crawler rendered every permutation, about 19,000 of them, and then threw each away, because a query URL can't be written as a file. The lab keeps growing, so the build will drift back up unless something trims it.

Objective

  • A deploy build in a few minutes, outputting the same pages.
  • A ranked list of what to trim next, measured rather than guessed, ready for when the build grows again.

Scope

In: nuxt.config.ts prerender settings (deploy builds only), the server data the cycle pages read, slow or broken pages the crawler reaches. Out: the deploy script's shape (local build → deploy branch), Cloudflare, dev-server behaviour.

Key Changes

  • 2026-11-03 — skip query links in the deploy crawl (commit a7323795df on claude/jovial-dubinsky-36e37e, unpushed). Under DESIGN_DEPLOY_OUT, nitro.prerender.ignore skips any link with a ? and queues its bare path instead (captured from the prerender:routes hook), so a page linked only with a query still prerenders. Dev is untouched.
    • Measured on c123/owasp-agentic-tour plus the main checkout's 23 uncommitted lab paths (nuxt generate, deploy.sh step 1):
      BeforeAfter
      Total build1,051 s262 s
      Prerender phase967 s191 s
      index.html3,1263,125
    • Gained pages/phase4/mock/inventory/agents/detail and inventory/tools/detail: they were linked only with a query, so the live site was missing them. Dropped /cycles/design/c0 and /cycles/design/c97-harness-chip (+ record): 404 pages reached only from ?cycle= links on the c124 mock. Posture report and time-window pages are byte-identical.
    • The 1,660-page figure in the original log came from a build that was cut short; a full build writes about 3,125 pages.
  • 2026-11-03 — DataTimeline demo fixed (same commit). The demo passed timeframe: 'week', but Timeline matches Time values ('7days'), so its computed was undefined and SSR threw reading 'labels'. The demo now passes Object.values(Time).
  • 2026-11-03 — where the rest of the time goes (from the baseline log, summed render time across 12 cores, so per-page numbers are inflated by queueing):
    BucketRoutesSummed render time
    Query permutations (now skipped)19,034~21,000 s
    Path-only pages3,516~10,100 s
    └ /cycles/design/*1,099~7,800 s (doc pages avg 11 s, cycle pages avg 3.7 s)
    └ /components/_demos/*386~80 s
    /pages/data-swarm-ideas, -2-, -3-3~284 s each

    Top query offenders before the fix: findings (2,160), compliance/detail (1,787), policy-config/config-file (1,515).
  • 2026-11-03 — first real deploy with the fix (bd48264c3e on deploy, from c123/owasp-agentic-tour 0a2cb25691 + 23 uncommitted). nuxt generate took about 4 min (client 29 s, server 34 s, prerender 176 s, 6,256 routes, 3,126 index.html), but deploy.sh ran 931 s end to end. The other ~11 min came after the build: committing 17,863 files (820 MB) as a fresh tree and force-pushing it. The build is no longer the slow part of a deploy; the commit and push are.

Outcome

(open — parked after the first fix)

Options (ranked; none started)

  1. Time and trim the commit + push (now the biggest cost: ~11 of 15.5 min). Add timestamps to deploy.sh steps 3–4 first to see whether git add of 17k files or the upload dominates. Then: content-stable asset names so unchanged blobs dedupe, drop what Cloudflare doesn't need (e.g. _payload.json if unused, source maps), or upload with wrangler pages deploy (hash-based, only changed files) instead of a git push.
  2. Cache cycle data during prerender. Every cycle doc page calls listProjectCycles (server/data/cycles/buildCycles.ts), which rereads and parses all ~130 CYCLE.md files plus statusIndex and imageIndex, on every request. The week pages also run git log per request (window.ts, timeline.ts). Nothing changes mid-build, so memoise per process, gated on import.meta.prerender. Biggest next lever, invisible on the site.
  3. The three data-swarm pages (~284 s each). They read /api/ai-usage and /api/commits/detail (c62). Likely heavy git scans or a timeout; cache, fix, or prerender: false.
  4. Client-render the lab on deploy. routeRules: { '/pages/**': { ssr: false } } under DESIGN_DEPLOY_OUT makes each lab mock a cheap shell. Big cut; the mocks stop shipping as static HTML. Mark's call.
  5. Prune. Retire old phase-0 mocks or old cycles' doc pages; stop the crawler following direct /components/_demos/* links (small).
  6. Incremental deploys (reuse unchanged output): most moving parts, last resort.

Remaining prerender errors (content, not crawl cost)

Folder links in cycle records resolve to routes that don't exist: on a week page to /cycles/weeks/<folder> (400 "week must be a Monday"), on a cycle page to /cycles/design/<folder> (404), and through siteLinks to doc?path=<folder>/.md (404). No site route serves a cycle folder.

  • (mcp-guide-u4/) — c125 CYCLE.md (on c123/owasp-agentic-tour)
  • (tours-walk/) — c123
  • (todos-2026-09-23/), (todos-2026-09-24/) — c92; (todos-2026-09-30/) — c103
  • (sources/) — c121
  • claude-code-opus-5.5/phase2/studies/, phase3/bom/ — c99
  • c107: crates/qdash/ui/docs/data-interface.md, EXECUTIVE-OVERVIEW.md (not in the cycle dir)
  • /api/cycles/design/c105 — 404 in that build
  • /cycles/weeks/1999-12-27 — something links a week far in the past

Fix per record (point at a file inside the folder), or teach siteLinks to send folder links somewhere real.

Artifacts

Qpoint Brand Style Guide