feat(web): add a Codewhale Enterprise page (/enterprise)

A separate page for Codewhale Enterprise (CURRENT_DECISIONS §57) that
lists what it will deliver, not how: evals that define done, training
data from your team's accepted work, a fine-tuned open-weight model you
own, and RL environments that keep improving it, each tested on your own
holdout. Available today: the open agent for any model (including
self-served vLLM, SGLang and Ollama) and saved sessions with receipts.
Planned: managed eval and training environments, qualification on each
new base model, routing between rented and owned models, private
deployment. It says no customer model has been trained, names no price
or customer, and keeps the method private (founder, 2026-10-27). Calls
to action: a design-partner email to [email protected] and /install.

English and Simplified Chinese; other locales fall back to English
(enterprise.ts is an optional locale file). The type lives beside the
English dictionary so types.ts, which another session is editing, stays
untouched. Added to the sitemap; no nav or footer link yet.

Updates lib/docs-ia.test.ts: the sitemap now has 112 entries (110 + /enterprise in en and zh).

Checks: check-locales PASS; vitest lib/docs-ia.test.ts 19 passed; tsc --noEmit exit 0; eslint on the touched
files exit 0; vitest page-meta + i18n dictionaries + locale-layout
8 files, 72 passed; /en and /zh/enterprise render on the local dev
server (HTTP 200, no console errors, no overflow at 375 px). Full npm
run check and a deploy not run.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
CodeWhale BotandClaude Opus 5.5 committed 2026-10-27 16:09:32 -07:00
1 parent e9201f5d38
commit 2a4406a57d
7 files changed
+288 -2

No files matched your search

+101
View File
@@ -0,0 +1,101 @@
import Link from "next/link";
import { Icon } from "@/components/icon";
import { PageHeader, Section } from "@/components/page-header";
import { Status } from "@/components/status-badge";
import { getEnterprise } from "@/lib/i18n/dictionaries";
import { buildPageMetadata } from "@/lib/page-meta";
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
const copy = getEnterprise(locale);
return buildPageMetadata({ path: "/enterprise", locale, title: copy.metaTitle, description: copy.metaDescription });
}
export default async function EnterprisePage({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
const t = getEnterprise(locale);
const mailto = `mailto:${t.partnerEmail}?subject=${encodeURIComponent("Codewhale Enterprise design partner")}`;
return (
<>
<PageHeader
kicker={t.kicker}
title={t.title}
lede={t.lead}
pose="think"
meta={<Status tone="attention">{t.status}</Status>}
actions={
<div className="actions">
<a className="btn btn-primary btn-lg" href={mailto}>{t.partnerCta}</a>
<Link className="btn btn-ghost" href={`/${locale}/install`}>{t.secondaryCta}</Link>
</div>
}
/>
<div className="page-body">
<Section id="enterprise-loop" title={t.loopTitle} scope={t.loopScope}>
<ol className="steps">
{t.loop.map((step, index) => (
<li key={step.title}>
<span className="gs-step-index" aria-hidden="true">{index + 1}</span>
<h3>{step.title}</h3>
<p>{step.body}</p>
</li>
))}
</ol>
</Section>
<Section id="enterprise-status" title={t.statusTitle} scope={t.statusScope}>
<div className="roadmap-tracks">
{[
{ title: t.todayTitle, tone: "ready" as const, items: t.today },
{ title: t.plannedTitle, tone: "idle" as const, items: t.planned },
].map((track) => (
<section key={track.title} className="roadmap-track" aria-label={track.title}>
<div className="roadmap-track-head">
<h3><Status tone={track.tone}>{track.title}</Status></h3>
</div>
<ul className="group-card" role="list">
{track.items.map((item) => (
<li key={item.title} className="group-row roadmap-item">
<p className="roadmap-item-title">{item.title}</p>
<p className="roadmap-item-note">{item.body}</p>
</li>
))}
</ul>
</section>
))}
</div>
</Section>
<section className="page-section" aria-label={t.controlTitle}>
<div className="grid-2">
<div className="tile">
<span className="tile-icon" aria-hidden="true"><Icon name="folder" /></span>
<h2 className="section-title">{t.dataTitle}</h2>
<p>{t.dataBody}</p>
</div>
<div className="tile">
<span className="tile-icon" aria-hidden="true"><Icon name="shield" /></span>
<h2 className="section-title">{t.engineTitle}</h2>
<p>{t.engineBody}</p>
</div>
</div>
</section>
<section className="page-section" aria-labelledby="enterprise-partners">
<div className="callout">
<p className="callout-title" id="enterprise-partners">
<Icon name="users" className="icon" />
{t.partnerTitle}
</p>
<p>{t.partnerBody}</p>
<div className="actions">
<a className="btn btn-secondary" href={mailto}>{t.partnerEmail}</a>
</div>
</div>
</section>
</div>
</>
);
}
+1 -1
View File
@@ -5,7 +5,7 @@ import { readCatalogue } from "@/lib/ratatui/catalogue";
// Public, indexable routes (locale-prefixed). /admin and /api are
// intentionally excluded; see app/robots.ts.
const PATHS = ["", "/product", "/features", "/install", "/constitution", "/models", "/plugins", "/runtime", "/docs", "/docs/auth", "/docs/configuration", "/docs/guide", "/docs/hooks", "/docs/mcp", "/docs/modes", "/docs/review", "/docs/fleet", "/docs/runtime-api", "/docs/sandbox", "/docs/subagents", "/docs/tools", "/docs/troubleshooting", "/docs/trust", "/docs/vocabulary", "/docs/web", "/docs/work", "/docs/skills", "/docs/claude-code", "/docs/pet", "/faq", "/roadmap", "/feed", "/digest", "/changelog", "/contribute", "/community", "/signin", "/legal/terms", "/legal/privacy"];
const PATHS = ["", "/product", "/features", "/install", "/constitution", "/models", "/plugins", "/runtime", "/docs", "/docs/auth", "/docs/configuration", "/docs/guide", "/docs/hooks", "/docs/mcp", "/docs/modes", "/docs/review", "/docs/fleet", "/docs/runtime-api", "/docs/sandbox", "/docs/subagents", "/docs/tools", "/docs/troubleshooting", "/docs/trust", "/docs/vocabulary", "/docs/web", "/docs/work", "/docs/skills", "/docs/claude-code", "/docs/pet", "/faq", "/roadmap", "/enterprise", "/feed", "/digest", "/changelog", "/contribute", "/community", "/signin", "/legal/terms", "/legal/privacy"];
export default function sitemap(): MetadataRoute.Sitemap {
const catalogue = readCatalogue();
+1 -1
View File
@@ -97,7 +97,7 @@ describe("sitemap and hreflang preservation", () => {
const existingEntries = sitemapEntries.filter(
(entry) => new URL(entry.url).pathname.split("/")[2] !== "ratatui",
);
expect(existingEntries).toHaveLength(110);
expect(existingEntries).toHaveLength(112);
expect(sitemapEntries.filter(entry => entry.url.endsWith("/install")).map(entry => entry.url))
.toEqual([`${SITE_URL}/en/install`]);
expect(sitemapEntries.some(entry => entry.url.endsWith("/pricing"))).toBe(false);
+102
View File
@@ -0,0 +1,102 @@
export interface EnterpriseDict {
metaTitle: string;
metaDescription: string;
kicker: string;
status: string;
title: string;
lead: string;
partnerCta: string;
secondaryCta: string;
loopTitle: string;
loopScope: string;
loop: { title: string; body: string }[];
statusTitle: string;
statusScope: string;
todayTitle: string;
today: { title: string; body: string }[];
plannedTitle: string;
planned: { title: string; body: string }[];
controlTitle: string;
dataTitle: string;
dataBody: string;
engineTitle: string;
engineBody: string;
partnerTitle: string;
partnerBody: string;
partnerEmail: string;
}
export const enterprise: EnterpriseDict = {
metaTitle: "Codewhale Enterprise · In development",
metaDescription: "Codewhale Enterprise turns your team's work into evals, training data and models you own. In development; open to design partners.",
kicker: "Codewhale Enterprise",
status: "In development",
title: "Own the intelligence your team runs on",
lead: "Your team's work already runs through Codewhale. Enterprise turns that work into evals, training data and models you own. It is in development and open to design partners.",
partnerCta: "Become a design partner",
secondaryCta: "Try the open-source agent",
loopTitle: "What you get",
loopScope: "Every model is tested on your own held-out tasks before it replaces anything.",
loop: [
{
title: "Evals that define done",
body: "An eval suite built from your team's real work, with a held-out set that decides whether a model is good enough.",
},
{
title: "Training data from your work",
body: "Data drawn from work your team already accepted, with sensitive details removed.",
},
{
title: "A model you own",
body: "A fine-tuned open-weight model you control, compared with your current model on quality, cost and speed.",
},
{
title: "Environments that keep improving it",
body: "RL environments built from your own tasks, so your model keeps improving on the work that matters to you.",
},
],
statusTitle: "Available today and planned",
statusScope: "The open-source agent is available now. Enterprise is in development, and no customer model has been trained yet.",
todayTitle: "Available today",
today: [
{
title: "An agent for any model",
body: "Hosted providers, any OpenAI-compatible endpoint, and models you serve yourself with vLLM, SGLang or Ollama.",
},
{
title: "Saved sessions with receipts",
body: "Every session keeps its tool calls, results and approvals, so your team's work is on record.",
},
],
plannedTitle: "Planned for Enterprise",
planned: [
{
title: "Managed eval and training environments",
body: "Environments that run your evals and training without depending on a laptop.",
},
{
title: "Qualification on every new base model",
body: "When a better open model ships, it is tested on your holdout and promoted only if it passes.",
},
{
title: "Routing between rented and owned models",
body: "Each workflow goes to the model, rented or owned, with the best accepted result per cost.",
},
{
title: "Private deployment",
body: "Run the agent and Enterprise where your data must stay.",
},
],
controlTitle: "You keep control",
dataTitle: "Your data stays where you choose",
dataBody: "Your sessions, datasets and models stay in places you choose. Training runs only on compute you approve.",
engineTitle: "One Rust engine with approvals",
engineBody: "The same engine runs your workflows and Enterprise. Ask, Auto-Review and Full Access permissions and receipts apply to both.",
partnerTitle: "Design partners",
partnerBody: "Bring one workflow your team repeats. You get a baseline of your current model on your own holdout, then a test of an owned model against it. Email us to start.",
partnerEmail: "[email protected]",
};
+6
View File
@@ -1,5 +1,7 @@
import { docsClaudeCode as enDocsClaudeCode } from "./en/docs-claude-code";
import { docsClaudeCode as zhDocsClaudeCode } from "./zh/docs-claude-code";
import { enterprise as enEnterprise, type EnterpriseDict } from "./en/enterprise";
import { enterprise as zhEnterprise } from "./zh/enterprise";
import { docsSkills as enDocsSkills } from "./en/docs-skills";
import { docsSkills as zhDocsSkills } from "./zh/docs-skills";
/**
@@ -640,6 +642,10 @@ export function getDocsSkills(locale: string): DocsPageDict {
return locale === "zh" ? zhDocsSkills : enDocsSkills;
}
export function getEnterprise(locale: string): EnterpriseDict {
return locale === "zh" ? zhEnterprise : enEnterprise;
}
export function getDocsClaudeCode(locale: string): DocsPageDict {
return locale === "zh" ? zhDocsClaudeCode : enDocsClaudeCode;
}
@@ -0,0 +1,76 @@
import type { EnterpriseDict } from "../en/enterprise";
export const enterprise: EnterpriseDict = {
metaTitle: "Codewhale Enterprise · 开发中",
metaDescription: "Codewhale Enterprise 把团队的工作变成评测、训练数据和你自己拥有的模型。开发中,欢迎设计合作伙伴。",
kicker: "Codewhale Enterprise",
status: "开发中",
title: "拥有团队赖以工作的智能",
lead: "团队的工作已经在 Codewhale 中进行。Enterprise 把这些工作变成评测、训练数据和你自己拥有的模型。目前处于开发阶段,欢迎设计合作伙伴加入。",
partnerCta: "成为设计合作伙伴",
secondaryCta: "试用开源智能体",
loopTitle: "你将得到什么",
loopScope: "每个模型在替换任何东西之前,都先在你自己的保留任务上测试。",
loop: [
{
title: "定义“完成”的评测",
body: "基于团队真实工作构建的评测套件,由保留集判断模型是否够好。",
},
{
title: "来自你工作的训练数据",
body: "数据取自团队已经认可的工作,并去除敏感信息。",
},
{
title: "你自己拥有的模型",
body: "由你掌控的微调开放权重模型,并在质量、成本和速度上与当前模型对比。",
},
{
title: "持续改进模型的环境",
body: "基于你自己的任务构建的强化学习环境,让模型在你最在意的工作上持续变好。",
},
],
statusTitle: "现已可用与计划中",
statusScope: "开源智能体现已可用。Enterprise 正在开发中,目前还没有训练过客户模型。",
todayTitle: "现已可用",
today: [
{
title: "支持任何模型的智能体",
body: "托管服务商、任何 OpenAI 兼容端点,以及你用 vLLM、SGLang 或 Ollama 自行部署的模型。",
},
{
title: "带回执的已保存会话",
body: "每个会话都保留工具调用、结果和审批记录,团队的工作有据可查。",
},
],
plannedTitle: "Enterprise 计划",
planned: [
{
title: "托管的评测与训练环境",
body: "运行评测和训练的环境,不再依赖笔记本电脑。",
},
{
title: "每个新基座模型都重新验证",
body: "更好的开放模型发布时,先在你的保留集上测试,通过后才会上线。",
},
{
title: "在租用模型与自有模型之间路由",
body: "每个工作流都交给每单位成本下被接受结果最好的模型,无论租用还是自有。",
},
{
title: "私有部署",
body: "在数据必须停留的地方运行智能体和 Enterprise。",
},
],
controlTitle: "由你掌控",
dataTitle: "数据留在你选择的地方",
dataBody: "你的会话、数据集和模型都留在你选择的地方。训练只在你批准的算力上进行。",
engineTitle: "同一个带审批的 Rust 引擎",
engineBody: "运行工作流和 Enterprise 的是同一个引擎。Ask、Auto-Review 和 Full Access 权限以及回执对两者同样适用。",
partnerTitle: "设计合作伙伴",
partnerBody: "带来一个团队反复做的工作流。你会得到当前模型在你自己保留集上的基线,再测试一个自有模型与之对比。发邮件即可开始。",
partnerEmail: "[email protected]",
};
+1
View File
@@ -27,6 +27,7 @@ const FILES = ["chrome.ts", "home.ts"];
// lookup time — so absence is a valid state, never a failure.
const OPTIONAL_FILES = [
"features.ts",
"enterprise.ts",
"docs-skills.ts",
"docs-claude-code.ts",
"docs-guide.ts",