Skip to content

feat(docs): render every page and ship a runnable image - #88

Merged
Fidasek009 merged 13 commits into
mainfrom
feat/docs-deployment
Oct 11, 2026
Merged

Fidasek009 merged 13 commits into
mainfrom
feat/docs-deployment

Conversation

@Fidasek009

@Fidasek009 Fidasek009 commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Makes apps/docs deployable: every page in content/docs renders, and the site ships as a runnable image.

Three of four pages 404'd. The only route imported one MDX file directly, and the compile-ahead collection imported MDX eagerly and server-only, so client-side navigation had no way to resolve a page body. Both are replaced by a splat route over the Fumadocs macro collection, which compiles one lazy chunk per page. The sidebar advertises the page tree, so it was shipping three dead links.

The site also drops its /docs prefix. /problems was the only published URL on that origin and it was never prefixed, so the origin carried two conventions; nothing else lives there, so pages are now served at the root (docs.talqo.chat/deployment). No redirects or aliases: /docs and /docs/* 404.

docs.talqo.chat gets its own image rather than joining the root one: ADR-0016 scopes that image to the self-hosted product, no operator needs the docs site, and the published problem type URIs (https://docs.talqo.chat/problems#{code}, compiled into generated API schemas) pin the site to that origin. The CD pipeline that publishes it is a separate PR.

Prerendering is deliberately left off. It writes HTML that nothing serves without additional server logic, and static versus server-rendered is a deployment decision rather than a routing fix.

The /problems page now reads what happened, why, and how to fix it, with code, status, and type URI on one muted line; anchors and URIs are unchanged. It links back to the documentation index but otherwise stays chrome-free, since API errors anchor straight into it. Sidebar narrowed 268px to 220px on desktop (mobile drawer untouched), verified by screenshot. Above 120rem the layout goes full-bleed so the sidebar sticks to the viewport edge instead of floating mid-screen; smaller viewports are untouched. It writes HTML that nothing serves without additional server logic, and static versus server-rendered is a deployment decision rather than a routing fix.

How to test

  1. bun run dev:docs, then open each of /, /deployment, /ai-provider-configuration, /embed-chat-integration, and /problems. Sidebar links must resolve rather than 404.
  2. docker build -f apps/docs/Dockerfile -t talqo-docs . from the repository root, then docker run --rm -p 3002:3002 talqo-docs.
  3. Against http://127.0.0.1:3002, confirm /, the four doc pages, and /problems return 200, a hashed /assets/... file returns 200, an unknown path returns the 404 page, and docker ps reports the container healthy.
  4. TALQO_DOCS_PORT="" bun src/serve.ts (from apps/docs) must fail fast naming the variable, not bind a random port.

Notes for reviewers:

  • TanStack Start resolves src/server.ts as its SSR build entry and overwrites dist/server/server.js with it, which is why the process entry is src/serve.ts.
  • /problems is a static route sibling to the root splat and relies on static paths outranking the splat. The catalog carries all 45 codes; PROBLEM_CODES lives in @talqo/shared/problem-codes and both apps consume it, so API-side additions cannot drift from the docs page. Generated contracts are byte-identical.
  • No ADR for the separate image: it follows directly from ADR-0016's scope plus "no operator needs it", and the rationale lives as a one-line comment in apps/docs/Dockerfile.
  • No automated serve tests by owner decision; verification is the manual steps above plus the catalog parity unit test.

Review round (Fildass1, verified empirically): fixed a /docs/mcp-connections link that arrived via the main merge after the prefix purge; over-limit slugs now 404 instead of 500ing with the message; hashed assets carry Cache-Control: immutable with a traversal guard; the problems intro states the type URI pattern once; dropped the stale preview script; healthcheck failure path is explicit; port parsing is digits-only and the log shows the bound hostname.

Gap: bun run actions:check could not run locally because actionlint is not installed here. No workflow files changed.

Checklist

  • PR title and summary are descriptive.
  • Max one DB migration per PR.
  • Docs updated.
  • Tests included.
  • Architecture-affecting changes follow the architecture guide.
  • I have seen this code, I have run this code, and I take responsibility for this code.

Prepared with opencode (space-bunny-free).

Comment thread apps/docs/vite.config.ts Resolved
Comment thread apps/docs/AGENTS.md Resolved
@Fidasek009
Fidasek009 force-pushed the feat/docs-deployment branch from 7f1fff9 to d86afa4 Compare October 8, 2026 09:03
@Fidasek009
Fidasek009 marked this pull request as ready for review October 8, 2026 09:25
@kilo-code-bot

kilo-code-bot Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Roast 🔥

Verdict: No Issues Found | Recommendation: Merge

Oh wait, this PR is actually clean. I need to sit down. I had my flamethrower warmed up and everything.

The incremental diff is substantial — widget CSS consolidation (inlined + scoped via vite-plugin-css-injected-by-js), legacy widget-config and data-talqo-widget removal, async snippet, autoscroll, plus all the docs/ADR/architecture updates. And it's all... correct. The plugin ordering (widgetCssPlugin post-phase before cssInjectedByJs post-phase) is right, the autoscroll useLayoutEffect-with-no-deps pattern is intentional and well-commented, the traversal guard in serve.ts is solid, the allowPublic import removal is clean, and all the test changes match the new behavior. The ADR and architecture doc updates accurately reflect the single-attribute, single-endpoint public surface.

📊 Overall: Like a developer who actually reads code review comments AND knows how to use a bundler plugin — I'm not crying, you're crying.

Files Reviewed (25 files)
  • apps/api/src/app.test.ts - Wildcard route test removed
  • apps/api/src/app.ts - widget-config CORS + route removed
  • apps/api/src/http/static.test.ts - widget.css test removed
  • apps/api/src/http/static.ts - widget.css removed from WIDGET_PATHS
  • apps/api/src/modules/embed/embed.integration.test.ts - widget-config test removed
  • apps/api/src/modules/embed/embed.routes.test.ts - widget-config tests removed
  • apps/api/src/modules/embed/embed.routes.ts - legacyWidgetConfigRoutes removed
  • apps/docs/content/docs/embed-chat-integration.mdx - async, widget-config docs updated
  • apps/e2e/tests/embed.spec.ts - widget.css removed from mock server + test
  • apps/e2e/tests/embeds.spec.ts - async snippet test added
  • apps/e2e/tests/fixtures/host.html - async added
  • apps/web/src/routes/dashboard/embeds/-embed-snippet.test.ts - async test added
  • apps/web/src/routes/dashboard/embeds/-embed-snippet.ts - async added to snippet
  • apps/widget/AGENTS.md - Single-file bundle documented
  • apps/widget/package.json - Added vite-plugin-css-injected-by-js
  • apps/widget/src/bundle.test.ts - CSS scope + deferred mount tests
  • apps/widget/src/connected-widget.test.tsx - Autoscroll tests
  • apps/widget/src/embedded-widget.tsx - Autoscroll implementation
  • apps/widget/src/lib/css-scope.ts - New: scoped selector checker
  • apps/widget/src/widget.test.tsx - Stylesheet tests removed
  • apps/widget/src/widget.tsx - ensureWidgetStylesheet + data-talqo-widget removed
  • apps/widget/vite.config.ts - CSS injection plugin + scoping rewrite
  • docs/adr/0013-own-public-integration-identity-in-embeds.md - Updated
  • docs/architecture.md - Widget section updated
  • bun.lock - Generated, skipped
Previous Review Summaries (2 snapshots, latest commit 29e6d67)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 29e6d67)

Verdict: No Issues Found | Recommendation: Merge

Oh wait, this PR is actually clean. I need to sit down. I had my flamethrower warmed up and everything.

All 9 previous review findings are resolved in this incremental diff:

  • vite preview script: removed 🗑️
  • .catch() on healthcheck fetch: added ✅
  • localhost → bound hostname in logs: fixed 🔧
  • Regex port validation: implemented ✅
  • Immutable cache for /assets/*: served 🔒
  • throw notFound() on invalid slugs: done 🎯
  • /docs/mcp-connections → /mcp-connections: fixed 🔗
  • URI pattern in problems page: documented 📝
  • AGENTS.md instructions: tightened 📋

New code (widget markdown, embed form agent removal, DialogFooter simplification, theme toggle default flip, migration edits) is well-structured — no bugs, no security issues, no logic errors. The markdown component correctly strips node props, omits rehype-raw, and the XSS test covers the attack surface.

📊 Overall: Like a developer who actually reads code review comments — rare, appreciated, and slightly suspicious. I'm going to need a stronger drink.

Files Reviewed (28 files)
  • apps/api/drizzle/0005_widget.sql - Default flip, consistent with source
  • apps/api/drizzle/meta/0005_snapshot.json - Snapshot sync
  • apps/api/drizzle/meta/0006_snapshot.json - Snapshot sync
  • apps/api/drizzle/meta/0007_snapshot.json - Snapshot sync
  • apps/api/drizzle/meta/0008_snapshot.json - Snapshot sync
  • apps/api/src/modules/agent/agent.service.ts - Comment fix
  • apps/api/src/modules/embed/embed.schema.ts - Default flip
  • apps/docs/Dockerfile - Healthcheck .catch() added
  • apps/docs/content/docs/embed-chat-integration.mdx - Link and wording fixes
  • apps/docs/package.json - preview script removed
  • apps/docs/src/lib/problem-catalog.ts - Guidance text updated
  • apps/docs/src/routes/$.tsx - notFound() on invalid slugs
  • apps/docs/src/routes/problems.tsx - URI pattern documented
  • apps/docs/src/serve.ts - Immutable cache, regex port, hostname log
  • apps/e2e/tests/embeds.spec.ts - Reassignment test removed
  • apps/web/src/features/embeds/embed-appearance-form.ts - Agent selector removed from form
  • apps/web/src/locales/cs.json - Locale cleanup
  • apps/web/src/locales/en.json - Locale cleanup
  • apps/web/src/locales/zh.json - Locale cleanup
  • apps/web/src/routes/dashboard/embeds/$embedId.tsx - Agent selector removed from UI
  • apps/widget/AGENTS.md - CSS isolation guidance updated
  • apps/widget/package.json - Added react-markdown, remark-gfm
  • apps/widget/src/components/ui/markdown.tsx - New markdown component
  • apps/widget/src/connected-widget.test.tsx - Markdown tests
  • apps/widget/src/embedded-widget.tsx - Markdown integration
  • apps/widget/src/index.css - Markdown styling
  • docs/adr/0018-render-chat-markdown-in-the-widget.md - New ADR
  • packages/shared/src/widget-appearance.ts - Default flip
  • packages/ui/src/components/dialog.tsx - DialogFooter simplified
  • bun.lock - Generated, skipped

Previous review (commit d86afa4)

Verdict: No Issues Found | Recommendation: Merge

Oh wait, this PR is actually clean. I need to sit down. I had my flamethrower warmed up and everything.

📊 Overall: Like finding a unicorn in production — I didn't think clean PRs existed anymore, but here we are. The splat route, shared problem codes, port validation, CSS adjustments, and the noExternal build-scoping are all well-executed. The commit history shows responsive review handling too — comments shortened, AGENTS.md rewritten, key order preserved. Solid work.

Files Reviewed (22 files, 4 generated/skip)
  • apps/api/src/http/problem.ts - Clean re-export of shared codes
  • apps/docs/AGENTS.md - Action-oriented, no unnecessary restrictions
  • apps/docs/Dockerfile - Multi-stage, healthcheck, correct
  • apps/docs/content/docs/embed-chat-integration.mdx - URL fix only
  • apps/docs/content/docs/index.mdx - URL fix only
  • apps/docs/package.json - Deps and scripts correct
  • apps/docs/src/lib/problem-catalog.test.ts - Clean parity test via shared package
  • apps/docs/src/lib/problem-catalog.ts - Added missing entries, removed stale one
  • apps/docs/src/lib/source.ts - Macro-based, baseUrl "/"
  • apps/docs/src/routeTree.gen.ts - Generated, skipped
  • apps/docs/src/routes/$.tsx - Slug validation, 404 vs 500 fix, DocsLayout wrapper
  • apps/docs/src/routes/__root.tsx - Link component, client-side 404 nav
  • apps/docs/src/routes/problems.tsx - Chrome-free layout, concise
  • apps/docs/src/serve.ts - Port validation, edge cases handled
  • apps/docs/src/styles/app.css - Sidebar + full-bleed CSS, correct breakpoints
  • apps/docs/tsconfig.json - Cleaned up paths
  • apps/docs/vite.config.ts - Build-scoped noExternal, comment concise
  • docs/SRS.md - Accurate NFR update
  • docs/architecture.md - Clean removal of redundant docs section
  • packages/shared/package.json - New export added
  • packages/shared/src/problem-codes.ts - Key order preserved per main
  • bun.lock - Generated, skipped
  • apps/docs/source.config.ts - Deleted
  • apps/docs/src/mdx.d.ts - Deleted
  • apps/docs/src/routes/docs.index.tsx - Deleted
  • apps/docs/src/routes/docs.tsx - Deleted
  • apps/docs/src/routes/index.tsx - Deleted
  • apps/docs/src/serve.integration.test.ts - Deleted

Reviewed by deepseek-v4-flash · Input: 135.9K · Output: 20.2K · Cached: 1M

@Fidasek009
Fidasek009 requested a review from a team October 8, 2026 09:33

@Fildass1 Fildass1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thorough pass on 03b0cbd8, verified empirically rather than by reading: tsc (docs/api/shared), bun test, oxlint, oxfmt, knip all pass; vite build succeeds and the SSR bundle has zero external imports; the runtime stage was reproduced without node_modules (dist + serve.ts only) and works; the image builds and the container reports healthy with all routes behaving as designed (/, all 5 content pages, /problems → 200; unknown paths and /docs/* → 404; hashed assets → 200; TALQO_DOCS_PORT="" fails fast). The contract-parity claim holds: PROBLEM_CODES key order is byte-identical to main, openapi.json and both generated clients are unchanged, and the three new catalog statuses match the API's actual responses (403 / 409 / 400).

Findings are inline: one shipped link that 404s (should fix before merge), malformed slugs rendering 500s with an internal message, hashed assets served without a cache policy, and the problems page no longer showing the type URI pattern, plus three nits.

Comment thread apps/docs/src/routes/$.tsx Resolved
Comment thread apps/docs/src/serve.ts Resolved
Comment thread apps/docs/src/routes/problems.tsx Resolved
Comment thread apps/docs/package.json Resolved
Comment thread apps/docs/Dockerfile Resolved
Comment thread apps/docs/src/serve.ts Resolved
Fidasek009 and others added 13 commits October 11, 2026 11:35
Three of four content pages 404'd and nothing ran the built site. The
sidebar is rendered from the page tree, so it advertised three dead links.

docs.index.tsx imported one MDX file directly, which cannot cover a slug.
Underneath, the compile-ahead collection glob-imported MDX eagerly and
server-only, so client-side navigation had no way to resolve a page body.
Both are replaced by a splat route over the Fumadocs macro collection,
which compiles one lazy chunk per page. source.config.ts, .source/, and
the @content alias are gone.

docs.talqo.chat gets its own image: ADR-0016 scopes the single image to the
self-hosted product, no operator needs the docs site, and the published
problem type URIs pin it to that origin's root. The SSR bundle is built
with noExternal so the runtime stage carries only dist and the entry point.

src/serve.ts is deliberately not src/server.ts: TanStack Start resolves
src/server.ts as its SSR build entry and overwrites dist/server/server.js
with it.

Prerendering stays off. It writes HTML nothing would serve without more
server logic, and static versus server-rendered is a deployment call
rather than a routing fix.

Co-Authored-By: Space Bunny Free <noreply@t3.chat>
The only published URL on the docs origin was prefixed with /docs while
/problems was not, so the origin exposed two conventions. Nothing else
lives there, so the pages drop the prefix: /, /deployment,
/embed-chat-integration, /ai-provider-configuration.

A root splat now renders every page, which folds the /docs layout into it
and removes a route file. /problems stays a static sibling and still
outranks the splat. The integration test now asserts that no rendered
link 404s, which is the invariant the prefixed layout got wrong.

The container healthcheck probes / rather than /docs, which dropping the
prefix turned into a 404 and a permanently failing image.

Co-Authored-By: Space Bunny Free <noreply@t3.chat>
The problem catalog was missing agent-file-not-retryable and
embed-disabled while the API emits both. The codes are now covered by a
static parity check against apps/api/src/http/problem.ts, which fails in
either direction without a server or a build.

TALQO_DOCS_PORT="" and "0" silently bound a random port while the
healthcheck probed 3002. Non-integer or out-of-range ports now fail fast
with the received value.

A client collection miss rendered a 500; it is a 404. The doc server fn
now validates slug shape and size instead of passing input through.

The serve integration test is gone; manual verification per the PR steps
replaces it, and the parity check above keeps the published problem URIs
honest.

Co-Authored-By: Space Bunny Free <noreply@t3.chat>
…ecture

The parity check read API source as text from a docs test, which is a
dependency wearing a trenchcoat. PROBLEM_CODES now lives in
@talqo/shared/problem-codes, consumed by both apps; generated contracts
are byte-identical before and after.

The architecture guide no longer documents the docs app beyond the one
line it always had. The image-separation rationale lives in the
Dockerfile, and NFR-1.3 is rewritten short to match what ships: one image
for API, dashboard, and widget, its own image for docs.

Co-Authored-By: Space Bunny Free <noreply@t3.chat>
Co-Authored-By: Space Bunny Free <noreply@t3.chat>
main already sets agentGuidance: false; this branch forked before that,
so turbo kept re-adding its managed block here. Aligning stops it.

Co-Authored-By: Space Bunny Free <noreply@t3.chat>
Resolving all SSR dependencies into the bundle breaks the dev module
runner (`module is not defined` from CJS React, every page 500s). The
runtime image still needs the self-contained bundle because it ships no
node_modules, so the option now applies to `vite build` only.

Co-Authored-By: Space Bunny Free <noreply@t3.chat>
Each problem entry now reads what happened, why, and how to fix it,
with code, status, and type URI demoted to one muted line. Anchors and
URIs are untouched. The page also links back to the documentation index;
it stays chrome-free otherwise because API errors anchor straight into it.

Sidebar 268px to 220px on desktop via the theme variable, verified by
screenshot on desktop and mobile (drawer untouched below the breakpoint).

Co-Authored-By: Space Bunny Free <noreply@t3.chat>
Fumadocs centers a fixed-width grid, so past 1080p the sidebar floats
mid-screen inside growing gutters. Above 120rem the layout goes
full-bleed instead: sidebar and TOC at the edges, article centered.
1080p, mobile, and the build are untouched. Verified by screenshot at
1536, 2560, and 390px widths.

Co-Authored-By: Space Bunny Free <noreply@t3.chat>
Status and code move under the title, fix guidance uses a shorter
label, and the per-entry type URI line is gone. Anchors and the catalog
data are untouched, so published problem URIs still resolve.

Co-Authored-By: Space Bunny Free <noreply@t3.chat>
Co-Authored-By: Space Bunny Free <noreply@t3.chat>
Key order feeds Object.values straight into the generated OpenAPI and
client schemas. Alphabetizing the moved entries reordered two unions and
broke contracts:check. Order is load-bearing here, not cosmetic.

Co-Authored-By: Space Bunny Free <noreply@t3.chat>
The main merge added an mcp-connections page with a /docs-prefixed link
in another file; it now points at /mcp-connections like the sidebar does.

Over-limit slugs threw Error and rendered a 500 leaking the message.
Validation rejections are missing addresses, so they 404 like any other
unknown slug.

Hashed assets now carry an immutable cache policy instead of bare
ETag/Last-Modified. The static mount is gone; paths are resolved
explicitly with the same traversal guard as the API's static responder.

The problems intro states the type URI pattern once, since per-entry
URIs are gone but the page is still their dereference target. Also:
dropped the stale preview script, made the healthcheck failure path
explicit, logged the bound hostname, and strict-parsed the port
(digits only, then range).

Co-Authored-By: Space Bunny Free <noreply@t3.chat>
@Fidasek009
Fidasek009 force-pushed the feat/docs-deployment branch from 29e6d67 to e9d4960 Compare October 11, 2026 09:37
@Fidasek009
Fidasek009 merged commit 58b20e7 into main Oct 11, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants