Skip to content

Extract OpenKnowledge marketing site into a private ok-marketing app#419

Merged
inkeep-oss-sync[bot] merged 1 commit into
mainfrom
copybara/sync
Jul 1, 2026
Merged

Extract OpenKnowledge marketing site into a private ok-marketing app#419
inkeep-oss-sync[bot] merged 1 commit into
mainfrom
copybara/sync

Conversation

@inkeep-oss-sync

Copy link
Copy Markdown
Contributor

No description provided.

…(#2334)

* docs(open-knowledge): add marketing-site-separation spec

PRD plus technical spec for extracting the marketing site into its own private, non-mirrored Next.js app (private/ok-marketing), leaving docs and the product routes public and preserving every current URL via Vercel Microfrontends.

Covers the vendored-core editor-preview mechanism (committed dist plus a CI drift-guard), the component untangle, the cutover and rollback plan, and the verification substrate. Includes the decision log, spec-local evidence, and two cold-reader audits.

Internal only; specs/ is not mirrored to the public repo.

* [US-002] scaffold private/ok-marketing Next.js app + workspace member

* [US-003] vendor core dist behind a shim with a rebuild-and-diff drift guard

Give the marketing app the live editor preview's core slice without
crossing the pnpm/Bun boundary at build time. Commit core's built barrel
(dist/index.mjs plus its hashed sibling chunk) under
private/ok-marketing/vendor/open-knowledge-core/ behind a thin shim
package.json that re-exports it and mirrors core's dependency block, and
consume it via a file: dep. This is the raw committed-dist path the
US-001 spike certified, not the ok-editor-preview wrapper fallback.

scripts/verify-core-dist.mjs rebuilds core with Bun and diffs its fresh
dist closure against the committed copy, failing with the regenerate
command on drift; --write (regen-core-dist) refreshes it. The closure is
resolved from index.mjs's own local imports, so a content-hash rename of
the shared chunk is caught rather than assumed away. The drift
classifier is unit tested.

Supporting wiring:
- .gitignore exempts the vendored dist from the dist/ build-output rule
- biome files.includes and tsconfig exclude vendor/ so the formatter and
  typechecker skip the minified barrel and the tiptap-typed .d.ts
- carve private/ok-marketing/vendor out of the dep-drift and
  workspace-membership trap scans, since the shim's mirrored dep block is
  a vendored artifact, not a workspace member

Also reconcile the shared-dep drift the US-002 scaffold introduced but
did not check: align next to ^16.2.4 to match the rest of the monorepo,
and grandfather the remaining heterogeneous frontend devDeps alongside
copilot-app in the drift allowlist. Monorepo traps and lockfiles are
green.

* [US-004] untangle (home)-coupled components out of the docs app

Move footer, dot-texture, and marketing-button out of the docs app's
(home) route group into neutral src/components/ so the staying routes
(/continue, /d/[encoded]) no longer reach into (home). All eight
importers repointed to the @/components/* alias. No file outside (home)
imports from (home) anymore; docs build and typecheck stay green.

* [US-005] relocate marketing landing + shared chrome into ok-marketing

Copy the (home) landing closure out of the docs app into the private
marketing app with the directory structure preserved so @/ aliases resolve,
replacing the docs-coupled pieces:

- getGitHubStars: direct GitHub REST fetch in lib/github-stars.ts (no core
  import), returning null on failure at the network trust boundary.
- hero: static, self-contained preview. The chat intro animation is kept
  verbatim; the live TipTap editor pane is replaced by a step-driven static
  document plus an inline activity chart. The live editor lands in US-006.
- provider: next-themes forced-light, dropping the fumadocs RootProvider.
- global.css: self-contained (no fumadocs imports), class-based dark
  variant, fd-background and fd-muted-foreground registered as theme tokens.
- next.config: PostHog /ingest rewrites; instrumentation-client copied.

Duplicates the shared presentational set, replicates the root layout, fonts,
JsonLd and PostHog wiring, and copies the referenced public assets. New deps
are aligned to existing scanned versions to keep dependency drift minimal.

* [US-006] wire the live OK editor preview in ok-marketing via vendored core

Bring the ok-editor closure and the two ui components it needs into the
marketing app, and restore the live hero-preview so the hero mounts the real
TipTap editor instead of the static placeholder. The editor imports its four
client symbols (MarkdownManager, sharedExtensions, CodeBlockFidelity,
PREVIEW_THEME_TOKENS) from the vendored core shim.

Add the TipTap and CodeMirror runtime deps at the versions the docs app uses so
they dedupe to a single @tiptap instance shared with the shim. The vendored
shim exposes only four symbols, so the extension packages' command-type
augmentations are loaded via a devDependency-backed ambient declaration.

Client bundle carries the editor pipeline with zero node: builtins; core source
and exports are unchanged.

* [US-007] drop docs (home) root, migrate middleware to proxy

Docs no longer answers /; the marketing landing now lives in the private
marketing app. Remove the (home) route group and its orphaned exclusive
dependencies (the ok-editor live-preview closure, ok-icon, ui/sheet,
ui/tooltip) so the public docs no longer carries the editor-to-core
coupling that the separation moves to the private app.

Rename middleware.ts to proxy.ts (Next 16), keeping the www to apex 308
canonicalization and the /.well-known/* carve-out that serves AASA as a
direct 200 on both apex and www. Verified against next 16.2.3 that the
config export stays named config.

Simplify provider.tsx (drop the dead /-only light theme pin) and make the
sitemap docs-only (drop the root entry so the public standalone deploy
does not advertise a 404 root). Document the /docs default route in a
mirror-safe next.config.ts comment.

* [US-008] wire Vercel Microfrontends config (docs default, marketing child)

Compose openknowledge.ai from two Vercel projects via Vercel Microfrontends,
config only. The docs app stays the default (apex + every current path); the
marketing app is a child claiming only / plus its asset prefix.

- add @vercel/microfrontends to both apps; wrap both next.configs with
  withMicrofrontends
- microfrontends.json in the docs (default) app: docs=default (no routing),
  marketing=child routing / and /marketing-assets/:path*
- move marketing public/ assets under /marketing-assets so they route to the
  marketing zone (the docs app still serves /images/* and /ok-logo.png, so a
  shared root path would hijack docs assets on the apex)
- duplicate HSTS (includeSubDomains; preload) in the marketing next.config
- MICROFRONTENDS.md documents the topology, Fallback Environment=Production
  intent, the app-name-must-match-Vercel-project check, and that the production
  apex cutover is a separate human step (not executed here)
- contract test pins the marketing routing entry against the app's real package
  name and public assets

The docs app builds green off-Vercel with the wrapper and config present, so the
public mirror's standalone docs build is unaffected.

* [US-009] point marketing subscribe form at the apex /api/subscribe cross-origin

Extract the newsletter network contract into lib/subscribe.ts: a single-source
subscribeSchema, an absolute apex SUBSCRIBE_ENDPOINT (derived from SITE_URL,
env-overridable for local dev), and subscribeToNewsletter(). The marketing zone
has no /api route, so the form must POST cross-origin to the product app's
endpoint (which already serves wildcard CORS + an OPTIONS preflight for exactly
this caller) rather than a relative path that would 404.

Pin the cross-app request contract in subscribe.test.ts: endpoint is absolute
apex + cross-origin, the POST sends a JSON { email } body that parses under a
mirror of the docs route schema, and a non-ok response maps to { ok: false }.

* [US-010] add ok-marketing CI validation gate + verify-core-dist wiring

Add .github/workflows/ok-marketing-validation.yml validating the private
marketing app on changes. A thin caller of the reusable _subtree-validation.yml
runs the standard build/lint/typecheck/test matrix (copilot-app precedent,
composite install, marketing-scoped paths). A bespoke verify-core-dist job the
reusable cannot express sets up Node plus Bun, installs the Open Knowledge
workspace, and runs the rebuild-and-diff drift guard so a stale vendored core
dist fails CI with the regenerate command.

The gate aggregator tolerates cancelled/skipped and keeps abandoned fail-closed
(passes check:gate-cancel-resilient). The drift guard fires on marketing paths
only, not core source: it rebuilds current core on every marketing PR so a
since-moved core surfaces the moment marketing is touched, without coupling
every core change to the marketing vendor. Registering it as a required check in
the branch-protection ruleset is a follow-up repo-admin step, noted in the
workflow header.

* [US-011] add pre-cutover URL/AASA/HSTS sweep + render smoke + cutover runbook

* fix(ci): harden ok-marketing drift-guard + prune dead editor deps

Phase 7 no-skimp fixes from the adversarial review gate.

verify-core-dist gate (ok-marketing-validation.yml):
- single-source the path scope via a paths job consumed by both the matrix
  caller and the drift gate, so the two cannot silently diverge
- the drift gate now also fires on public/open-knowledge/packages/core
  changes, so a core-source edit re-runs the rebuild-and-diff on the same PR
  that drifts the vendored dist (genuinely fail-closed)
- the aggregate gate now includes detect-core-dist-changes in needs and fails
  closed on its failure; a transient path-detect failure previously skipped
  the guard while the required gate stayed green

marketing:
- delete the dead download-links.ts resolver fork; fold the two consumed
  constants (DMG_ASSET_NAME, STABLE_DMG_URL) into site.ts
- add vendored-core-contract.test.ts: runs the real vendored .mjs against the
  hand-authored .d.ts so a core signature change cannot leave the types stale

docs:
- remove 11 tiptap/codemirror deps + @floating-ui/dom, unused since the (home)
  editor was removed; they were mirroring to the public repo and reintroducing
  the editor-dep surface the separation exists to shed

* chore(mirror): exclude docs/microfrontends.json from the public mirror

docs/microfrontends.json names the private @inkeep/ok-marketing app and its routes. openknowledge.ai deploys from agents-private (confirmed: the open-knowledge-docs Vercel project Root Directory is public/open-knowledge/docs), so the public mirror is not a Vercel deploy source and the file is inert there. Excluding it keeps the OSS repo from disclosing an internal app name and route while the private composed deploy keeps the file unchanged. The withMicrofrontends wrapper and the @vercel/microfrontends dependency stay public (harmless and inert in the public standalone docs build).

* fix(boundaries): exclude vendored re-export shims from the private-package name map

The vendored core shim (private/ok-marketing/vendor/open-knowledge-core) re-uses the
public package name @inkeep/open-knowledge-core so the private marketing app can consume
it via a file: dependency. check:boundaries keyed its private-package map by name and
walked all of private/, so the shim registered that public name as private-only and both
scans (public source imports + public workspace: manifests) misresolved the OK app's
legitimate public-core references to the private shim. Skip vendor/ dirs the same way
node_modules is skipped: committed dependency output, not first-party private source.

* chore(docs): shed orphaned (home)-landing code after the marketing separation

US-007 dropped the docs (home) landing; these were its private supporting code,
now unused in docs (0 importers, verified by knip) and already relocated to
private/ok-marketing:
- use-is-in-view / use-prefers-reduced-motion scroll hooks (deleted)
- CodexBrandIcon / CodexBrandMonoIcon (plain CodexIcon stays; the now-unused useId
  import drops with them)
- EXAMPLE_KB_SHARE_URL share-splash constant

Completes the separation's goal of shedding the landing footprint from the
public-mirrored docs app, the source-level analog of the dead editor-dep removal.
check:drift (knip) and docs typecheck are green.

* fix(ci): make vendored dist + mirror docs build survive the main merge

- verify-core-dist: regenerate the vendored core dist. The merge pulled main's
  core changes, so the committed vendored index.mjs was stale vs a fresh build.
- mirror-runnability (required check): the materialized mirror docs build threw
  MicrofrontendsError because withMicrofrontends() needs a microfrontends.json,
  which the mirror-exclude removes. Guard the wrapper behind an existsSync check
  so the public standalone docs build is a plain Next.js app, while agents-private
  (which keeps the config) still gets Microfrontends. Confirmed locally via
  check:ok-mirror-runnable (materialized export builds clean).

* fix(ok-marketing): address review findings (JSON-LD, subscribe timeout, DRY)

- layout.tsx: drop the copy-pasted alternateName 'OpenKnowledge Docs' from the
  marketing WebSite JSON-LD (factually wrong for the apex; SITE_NAME already names it).
- subscribe.ts: add AbortSignal.timeout(10s) to the cross-origin subscribe POST so a
  hung docs zone cannot lock the form's submit state forever.
- footer.tsx: import GITHUB_URL/DISCORD_URL/X_URL from site.ts instead of re-hardcoding
  them (site.ts is the single source; the merge added those exports).
- url-continuity-sweep.mjs: add .catch() so a throw mid-sweep fails loud with a non-zero
  exit instead of an unhandled rejection (pre-cutover safety gate).
- instrumentation-client.ts: document why the PostHog defaults date stays '2025-11-30'
  (posthog-js is override-pinned to 1.316.1 here; docs is a separate Bun workspace).

* chore(ok-marketing): address review pending-recommendations

- lint-staged + check-pre-push SUBTREES: register private/ok-marketing so staged
  files route to its Biome formatter and the scoped pre-push runs its checks
  (every peer subtree already had both entries).
- site.ts: document the DMG_ASSET_NAME lockstep with the docs download-links.ts
  canonical source (duplicated, not imported, across the workspace boundary).
- AGENTS.md + CI.md: enumerate the new private/ok-marketing subtree and its OK
  Marketing Validation workflow (AGENTS.md kept under the 38,500-byte guard).
- site.test.ts + subscribe.test.ts: cover metaDescription's four branches, the
  SUBSCRIBE_ENDPOINT env override, and the abort-signal on the fetch.

* fix(ok-marketing): restore the Brand link in the marketing footer

Visual QA against prod caught that the relocated footer dropped the /brand link:
prod shows Brand, Terms of Service, Privacy; the marketing footer had only Terms
and Privacy. Add it back with the same internal/external handling as the docs
footer. /brand is served by the docs zone and Vercel Microfrontends routes it
there, so it stays a same-tab in-product link.

* test(check-pre-push): pin ok-marketing in the SUBTREES expected set

Follow-on to registering private/ok-marketing in check-pre-push.mjs SUBTREES: the
'SUBTREES contains the expected set' guard hardcodes the full list to catch
accidental additions/removals, so it must list ok-marketing too. (The pre-push
guard correctly flagged the omission.)

---------

GitOrigin-RevId: b0f3669926d09339e1a09b7983e20cef8dac2ea3

@inkeep-internal-ci inkeep-internal-ci Bot 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.

Automated approval from agents-private public-mirror-sync (run: https://github.com/inkeep/agents-private/actions/runs/28537441244). Source of truth is the monorepo; direct edits on inkeep/open-knowledge are overwritten on next sync.

@inkeep-oss-sync
inkeep-oss-sync Bot merged commit 28b914f into main Jul 1, 2026
1 check passed
@inkeep-oss-sync
inkeep-oss-sync Bot deleted the copybara/sync branch July 1, 2026 18:09
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.

1 participant