diff --git a/content/whitepaper.md b/content/whitepaper.md index c13ce6026..4ffe52253 100644 --- a/content/whitepaper.md +++ b/content/whitepaper.md @@ -1,8 +1,8 @@ --- title: The Builder's Operating System subtitle: A technical architecture for sustained autonomous execution across many projects simultaneously -publishedAt: 2026-05-15 -version: 0.1 +publishedAt: 2026-08-12 +version: 0.2 --- ## The Execution Gap @@ -135,7 +135,9 @@ FleetCrown makes this visible together so operators can reason about their actua ## Subscription Tiers -FleetCrown is offered as a hosted SaaS product with three subscription levels. +FleetCrown is offered as a hosted SaaS product with four subscription levels: a free tier and three paid tiers. + +**Free** — for commanding your first projects. The full captain dashboard with your own runner and agent keys, limited in project count — enough to see the whole loop working before paying anything. **Personal** — for solo builders managing up to 5 projects. Local runtime with cloud backup and remote access via the daemon bridge. Full project/agent/life OS features. Designed for the individual operator who wants to run the full system without self-hosting. diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx index 679a4d25d..9b6267c41 100644 --- a/src/app/blog/page.tsx +++ b/src/app/blog/page.tsx @@ -1,8 +1,8 @@ -import ThoughtsPage from "@/app/thoughts/page"; +import { redirect } from "next/navigation"; -export const metadata = { - title: "Blog", - description: "FleetCrown essays on agents, execution systems, and building in public.", -}; - -export default ThoughtsPage; +// /blog used to re-export the Thoughts page wholesale, serving identical +// content on two URLs under two different names. /thoughts is the canonical +// URL (it is what the page calls itself); this route only forwards. +export default function BlogRedirect() { + redirect("/thoughts"); +} diff --git a/src/app/changelog/page.tsx b/src/app/changelog/page.tsx index 317b4e5cb..dec8af1e4 100644 --- a/src/app/changelog/page.tsx +++ b/src/app/changelog/page.tsx @@ -1,8 +1,8 @@ -import ReleasesPage from "@/app/releases/page"; +import { redirect } from "next/navigation"; -export const metadata = { - title: "Changelog", - description: "Every FleetCrown and Fleet Runner release, newest first.", -}; - -export default ReleasesPage; +// /changelog used to re-export the Releases page wholesale, serving identical +// content on two URLs. /releases is the canonical URL; this route only +// forwards so old links and bookmarks keep working. +export default function ChangelogRedirect() { + redirect("/releases"); +} diff --git a/src/app/globals.css b/src/app/globals.css index 75ac6c795..957fa8be6 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -271,7 +271,10 @@ --text-primary: oklch(0.92 0 0); --text-secondary: oklch(0.65 0 0); --text-tertiary: oklch(0.48 0 0); - --text-muted: oklch(0.34 0 0); + /* 0.55 (was 0.34) — 0.34 on the 0.07 page surface was ~2:1 contrast, an + unreadable WCAG failure for text that carries real copy (pricing + features, doc metadata). Keep "muted" muted, but keep it legible. */ + --text-muted: oklch(0.55 0 0); --text-inverted: oklch(0.07 0 0); --surface-page: oklch(0.07 0 0); --surface-base: oklch(0.10 0 0); @@ -732,11 +735,13 @@ .ui-changelog-meta { @apply flex flex-wrap items-center gap-x-3 gap-y-1; } + /* white/60 (was /40) — dates and version numbers are primary scanning + targets on a changelog, not decoration; /40 failed WCAG AA on black. */ .ui-changelog-date { - @apply text-micro font-medium uppercase tracking-[0.16em] text-white/40; + @apply text-micro font-medium uppercase tracking-[0.16em] text-white/60; } .ui-changelog-version { - @apply font-mono text-micro uppercase text-white/40; + @apply font-mono text-micro uppercase text-white/60; } .ui-changelog-current { @apply rounded-full border border-white/15 px-2 py-0.5 text-nano font-medium uppercase tracking-[0.12em] text-white/75; @@ -775,6 +780,26 @@ .ui-changelog-code { @apply font-mono text-white; } + /* Compact version index under the changelog header. */ + .ui-changelog-index { + @apply mt-10 flex flex-wrap gap-x-4 gap-y-2; + } + .ui-changelog-index-link { + @apply font-mono text-xs text-white/45 transition-colors hover:text-white; + } + /* Collapsed minor-line groups — everything below the current minor. */ + .ui-changelog-archive { + @apply border-t border-white/[0.12] py-6; + } + .ui-changelog-archive-summary { + @apply flex cursor-pointer list-none flex-wrap items-baseline gap-x-4 gap-y-1 text-sm font-medium text-white/60 transition-colors hover:text-white; + } + .ui-changelog-archive-summary::-webkit-details-marker { + display: none; + } + .ui-changelog-archive-range { + @apply text-xs font-normal text-white/40; + } .ui-public-release-section-heading { @apply mb-2 mt-4 text-micro uppercase tracking-caps text-white/40; } @@ -832,6 +857,12 @@ .ui-public-footer-fleet { @apply mb-8 border-t border-white/10 pt-8; } + .ui-public-footer-fleet-summary { + @apply cursor-pointer list-none text-micro font-semibold uppercase tracking-caps text-white/40 transition-colors hover:text-white/70; + } + .ui-public-footer-fleet-summary::-webkit-details-marker { + display: none; + } .ui-public-footer-fleet-grid { @apply grid grid-cols-1 gap-x-8 gap-y-3 sm:grid-cols-2 lg:grid-cols-3; } @@ -839,7 +870,7 @@ @apply text-sm text-text-secondary; } .ui-public-footer-fleet-blurb { - @apply text-xs text-white/40; + @apply text-xs text-white/55; } .ui-public-prose-divider { @apply mb-10 space-y-4 border-t border-white/10 pt-4; @@ -929,11 +960,63 @@ letter-spacing: -0.03em; } .ui-public-doc-subtitle { - @apply text-lg leading-relaxed text-text-muted sm:text-xl; + /* text-secondary, not text-muted — the subtitle is the one-line pitch of + the whole document; it must not be the faintest text on the page. */ + @apply text-lg leading-relaxed text-text-secondary sm:text-xl; } .ui-public-prose { @apply space-y-8; } + /* Anchored table of contents for long documents (whitepaper). */ + .ui-public-doc-toc { + @apply mb-14 rounded-2xl border border-border-subtle bg-surface-base p-6; + } + .ui-public-doc-toc-title { + @apply mb-4 text-micro font-semibold uppercase tracking-caps text-text-tertiary; + } + .ui-public-doc-toc-list { + @apply grid gap-x-8 gap-y-2.5 sm:grid-cols-2; + } + .ui-public-doc-toc-link { + @apply flex items-baseline gap-2.5 text-sm text-text-secondary transition-colors hover:text-text-primary; + } + .ui-public-doc-toc-num { + @apply shrink-0 font-mono text-xs text-text-muted; + } + /* Collapsed archive sections on public index pages (thoughts, releases) — + native
, opens without JS, links stay crawlable. */ + .ui-public-archive { + @apply rounded-2xl border border-border-subtle bg-surface-base px-5 py-4; + } + .ui-public-archive-summary { + @apply cursor-pointer list-none text-sm font-medium text-text-secondary transition-colors hover:text-text-primary; + } + .ui-public-archive-summary::-webkit-details-marker { + display: none; + } + .ui-public-archive-item { + @apply flex flex-col gap-0.5 rounded-lg px-2 py-1.5 text-sm transition-colors hover:bg-surface-raised; + } + .ui-public-archive-item-meta { + @apply text-xs text-text-tertiary; + } + /* Bare inline disclosure trigger — for collapsed detail bullets inside + prose sections (roadmap items). */ + .ui-public-disclosure-summary { + @apply cursor-pointer list-none text-sm text-text-tertiary transition-colors hover:text-text-primary; + } + .ui-public-disclosure-summary::-webkit-details-marker { + display: none; + } + /* Long-document variant — the whitepaper is ~15 sections of continuous + prose; at the default text-2xl the section headings barely separate from + body copy. Scoped here so short thought posts keep the tighter scale. */ + .ui-public-prose-doc .ui-public-prose-h2 { + @apply pt-10 text-3xl; + } + .ui-public-prose-doc .ui-public-prose-h3 { + @apply pt-4 text-xl; + } .ui-public-prose-h2 { @apply pt-6 text-2xl font-semibold tracking-tight text-text-primary; letter-spacing: -0.02em; @@ -1205,6 +1288,14 @@ min-height: 84vh; } } + /* Compact variant — statement pages (mission) pair the fold with body copy + directly below; the full 84vh fold forces ~700px of empty black between + statement and body. Declared after the base rules so it wins the cascade + at equal specificity. */ + .ui-public-hero-fold-compact { + @apply pb-16 pt-24 sm:pb-20 sm:pt-32; + min-height: 0; + } .ui-public-section-lede { @apply max-w-2xl text-xl text-text-secondary; } @@ -1221,7 +1312,10 @@ @apply border-t border-border-default bg-surface-page; } .ui-public-surface-card { - @apply flex min-h-[320px] flex-col rounded-2xl border border-border-subtle bg-surface-base p-6 text-left; + /* min-h 200px (was 320px) — typical card content is ~120px tall, so 320px + painted ~200px of dead space per card; 200px keeps rows aligned without + the void. */ + @apply flex min-h-[200px] flex-col rounded-2xl border border-border-subtle bg-surface-base p-6 text-left; } .ui-public-surface-card-label { @apply mb-8 font-mono text-xs uppercase tracking-[0.18em] text-text-secondary; diff --git a/src/app/investors/page.tsx b/src/app/investors/page.tsx index b675fb8dc..3ca272d54 100644 --- a/src/app/investors/page.tsx +++ b/src/app/investors/page.tsx @@ -1,14 +1,22 @@ import { PublicSurface } from "@/components/public/PublicSurface"; import { PublicHeaderActions } from "@/components/public/PublicHeaderActions"; -import { FinalCta } from "@/components/public/FinalCta"; import { INVESTORS, INVESTOR_DETAILS } from "@/config/marketing-content"; +import { getDefaultUser } from "@/db/queries/users"; +import { getHeroFleetSnapshot, type HeroFleetSnapshot } from "@/db/queries/public-fleet"; export const metadata = { title: "For Investors", description: INVESTORS.thesis, }; -export default function InvestorsPage() { +export default async function InvestorsPage() { + // Same real data source as the homepage hero — the founder's actual fleet, + // public-safe fields only. Degrades to nothing rather than fake numbers. + const owner = await getDefaultUser().catch(() => null); + const fleet: HeroFleetSnapshot = owner + ? await getHeroFleetSnapshot(owner.id).catch(() => ({ isLive: false, projects: [], metrics: [] })) + : { isLive: false, projects: [], metrics: [] }; + return ( }>
@@ -39,7 +47,24 @@ export default function InvestorsPage() {
TRACTION
-

{INVESTORS.traction}

+ {fleet.metrics.length > 0 && ( +
+ {fleet.metrics.map((metric) => ( +
+
{metric.value}
+
{metric.label}
+
+ ))} +
+ )} +
    + {INVESTORS.traction.map((point, i) => ( +
  • + + {point} +
  • + ))} +
@@ -48,17 +73,20 @@ export default function InvestorsPage() {
THE ASK

{INVESTORS.ask}

- -
- - {INVESTOR_DETAILS.contact} - -

{INVESTOR_DETAILS.deck}

-
- + {/* Investor-specific closing CTA — a "Begin." sign-up pitch is the wrong + ask for this audience; the next step here is a conversation. */} +
+

Talk to the founder.

+

Deck {INVESTOR_DETAILS.deck.toLowerCase()}.

+
+ + {INVESTOR_DETAILS.contact} + +
+
); } diff --git a/src/app/mission/page.tsx b/src/app/mission/page.tsx index e0d0ade7e..453201785 100644 --- a/src/app/mission/page.tsx +++ b/src/app/mission/page.tsx @@ -11,7 +11,7 @@ export const metadata = { export default function MissionPage() { return ( }> -
+
{MISSION.eyebrow}

{MISSION.title}

diff --git a/src/app/page.tsx b/src/app/page.tsx index d10f8d155..662857ab3 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -5,8 +5,7 @@ import { getUserCount, getDefaultUser } from "@/db/queries/users"; import { getHeroFleetSnapshot, getShippedFromFeedbackSnapshot, type HeroFleetSnapshot, type ShippedFeedbackSnapshot } from "@/db/queries/public-fleet"; import { PublicSurface } from "@/components/public/PublicSurface"; import { PublicHeaderActions } from "@/components/public/PublicHeaderActions"; -import { DesktopDownload } from "@/components/public/DesktopDownload"; -import { PRODUCT_SURFACES, START_PATHS, HOME_HERO_CONSOLE } from "@/config/marketing-content"; +import { PRODUCT_SURFACES, START_PATHS, HOME_HERO_CONSOLE, DESKTOP_DOWNLOAD } from "@/config/marketing-content"; import { APP_NAME, MARKETING_TAGLINE, @@ -273,13 +272,43 @@ export default async function LandingPage() {
- {!insideRunner && } + {/* Short download band — the full DesktopDownload embed (platform picker, + comparison table, setup steps, prerequisites) duplicated /download + wholesale and made the homepage endless. Two lines and a link. */} + {!insideRunner && ( +
+
+
DESKTOP APP
+

{DESKTOP_DOWNLOAD.title}

+

{DESKTOP_DOWNLOAD.lede}

+

+ {DESKTOP_DOWNLOAD.platforms.filter((p) => p.status === "ready").map((p) => p.label).join(" · ")} today + {DESKTOP_DOWNLOAD.platforms.some((p) => p.status !== "ready") && + ` · ${DESKTOP_DOWNLOAD.platforms.filter((p) => p.status !== "ready").map((p) => p.label).join(" and ")} coming soon`} +

+
+ + Download Fleet Runner + +
+
+
+ )} + {/* One closing section — "Choose your entry point" and "Not another + coding agent" were two back-to-back closers making the same pitch; + the differentiation points also repeated the EXECUTION/CONTROL and + SURFACE sections above. Merged: one differentiating line, then the + three concrete entry paths. */}
GET STARTED
-

Choose your entry point.

+

Not another coding agent.

+

+ Most tools help you write code faster in one file or one project. FleetCrown is for + running real agent operations at fleet scale — choose your entry point. +

@@ -298,31 +327,6 @@ export default async function LandingPage() {
-
-
-
THE DIFFERENCE
-

Not another coding agent.

-

- Most tools help you write code faster in one file or one project. We help serious builders run and orchestrate real agent operations at fleet scale. -

- -
-
-
Local execution is the foundation
-

Your agents run on your machines with full access to your environment. We do not force everything through remote sandboxes.

-
-
-
Fleet orchestration, not single-agent assistance
-

Built for people already running many agents across many projects. Explicit per-project autonomy levels instead of one generic agent.

-
-
-
Local runner + web command center
-

The desktop application executes. The web portal gives you fleet visibility and remote control. Two surfaces, one system.

-
-
-
-
-
{signedIn ? `Open ${APP_NAME}` : "Begin"} diff --git a/src/app/releases/page.tsx b/src/app/releases/page.tsx index 32bc0365e..9f2354360 100644 --- a/src/app/releases/page.tsx +++ b/src/app/releases/page.tsx @@ -19,7 +19,82 @@ function formatDate(iso: string): string { }); } +type FleetRunnerRelease = (typeof FLEET_RUNNER_RELEASES)[number]; + +// "0.8.9" → "0.8" — releases are grouped by minor line so only the current +// line renders expanded; older lines collapse into
. +function minorOf(version: string): string { + return version.split(".").slice(0, 2).join("."); +} + +function ReleaseArticle({ release, isLatest }: { release: FleetRunnerRelease; isLatest: boolean }) { + return ( +
+
+ + v{release.version} + {isLatest && Latest} +
+ +

Fleet Runner {release.version}

+ + {release.notes &&

{release.notes}

} + + {release.highlights.length > 0 && ( +
    + {release.highlights.map((line) => ( +
  • + + {line} +
  • + ))} +
+ )} + + {release.breaking.length > 0 && ( +
+
Breaking
+
    + {release.breaking.map((line) => ( +
  • + + {line} +
  • + ))} +
+
+ )} + +
+ + Binaries → + + {release.tag} +
+
+ ); +} + export default function ReleasesPage() { + const latestMinor = minorOf(FLEET_RUNNER_RELEASES[0].version); + const currentLine = FLEET_RUNNER_RELEASES.filter((r) => minorOf(r.version) === latestMinor); + // Older minor lines, in release order — each becomes one collapsed group. + const olderLines: { minor: string; releases: FleetRunnerRelease[] }[] = []; + for (const release of FLEET_RUNNER_RELEASES) { + const minor = minorOf(release.version); + if (minor === latestMinor) continue; + const last = olderLines[olderLines.length - 1]; + if (last && last.minor === minor) last.releases.push(release); + else olderLines.push({ minor, releases: [release] }); + } + return ( }>
@@ -46,6 +121,19 @@ export default function ReleasesPage() { GitHub releases →
+ {/* Compact version index — current-line versions jump to their entry; + archived versions jump to their collapsed minor-line group. */} + {PLATFORM_CHANGELOG.length > 0 && ( @@ -85,57 +173,25 @@ export default function ReleasesPage() {

Fleet Runner

- {FLEET_RUNNER_RELEASES.map((release, idx) => ( -
-
- - v{release.version} - {idx === 0 && Latest} -
- -

Fleet Runner {release.version}

- - {release.notes &&

{release.notes}

} + {currentLine.map((release, idx) => ( + + ))} - {release.highlights.length > 0 && ( -
    - {release.highlights.map((line) => ( -
  • - - {line} -
  • - ))} -
- )} - - {release.breaking.length > 0 && ( -
-
Breaking
-
    - {release.breaking.map((line) => ( -
  • - - {line} -
  • - ))} -
-
- )} - -
- - Binaries → - - {release.tag} -
-
+ {/* Everything below the current minor line collapses — 20+ expanded + patch entries buried the page; the archive stays one click (and + zero JS) away, links intact for crawlers. */} + {olderLines.map(({ minor, releases }) => ( +
+ + v{minor}.x — {releases.length} {releases.length === 1 ? "release" : "releases"} + + {formatDate(releases[releases.length - 1].date)} – {formatDate(releases[0].date)} + + + {releases.map((release) => ( + + ))} +
))}
diff --git a/src/app/roadmap/page.tsx b/src/app/roadmap/page.tsx index 0c920735e..6f96a4ab1 100644 --- a/src/app/roadmap/page.tsx +++ b/src/app/roadmap/page.tsx @@ -1,3 +1,4 @@ +import Link from "next/link"; import { PublicSurface } from "@/components/public/PublicSurface"; import { PublicHeaderActions } from "@/components/public/PublicHeaderActions"; import { FinalCta } from "@/components/public/FinalCta"; @@ -17,25 +18,40 @@ export default function RoadmapPage() {

{ROADMAP.lede}

-
- {ROADMAP.phases.map((phase) => ( -
-
{phase.marker}
-

{phase.title}

-

{phase.summary}

+
+ {ROADMAP.buckets.map((bucket) => ( +
+

{bucket.title}

+

{bucket.summary}

-
    - {phase.bullets.map((bullet, j) => ( -
  • - - {bullet} -
  • +
    + {bucket.items.map((item) => ( +
    +
    {item.title}
    +

    {item.line}

    + {item.essay && ( + + {item.essay.label} → + + )} + {item.details && item.details.length > 0 && ( +
    + + Details ({item.details.length}) + +
      + {item.details.map((detail, j) => ( +
    • + + {detail} +
    • + ))} +
    +
    + )} +
    ))} -
- - {"note" in phase && phase.note ? ( -

{phase.note}

- ) : null} +
))}
diff --git a/src/app/support/page.tsx b/src/app/support/page.tsx index a480546c1..82bacd58e 100644 --- a/src/app/support/page.tsx +++ b/src/app/support/page.tsx @@ -1,14 +1,44 @@ import Link from "next/link"; -import { ArrowUpRight, Bitcoin, Bot, Cat } from "lucide-react"; +import { ArrowUpRight, Bitcoin, BookOpen, Bot, Bug, Cat, Mail } from "lucide-react"; import { PublicHeaderActions } from "@/components/public/PublicHeaderActions"; import { PublicSurface } from "@/components/public/PublicSurface"; import { ECOSYSTEM_LINKS } from "@/config/ecosystem"; +import { INVESTOR_DETAILS } from "@/config/marketing-content"; export const metadata = { title: "Support", - description: "Support FleetCrown, OrangeCat, or Mao directly through their OrangeCat pages.", + description: "Get help with FleetCrown — docs, GitHub issues, email — or fund the work directly.", }; +// The nav says "Support", so getting help comes first. Funding the work is a +// different intent and lives below under its own heading. +const helpChannels = [ + { + title: "Documentation", + body: "Install Fleet Runner, connect your machines, and operate your fleet — setup and troubleshooting guides.", + href: "/docs", + cta: "Read the docs", + external: false, + icon: BookOpen, + }, + { + title: "GitHub issues", + body: "Found a bug or a missing behavior? File it where the fleet actually picks work up.", + href: "https://github.com/maonakamoto/fleetcrown/issues", + cta: "Open an issue", + external: true, + icon: Bug, + }, + { + title: "Email", + body: "For anything that doesn't fit a public issue — account questions, security reports, everything else.", + href: `mailto:${INVESTOR_DETAILS.contact}`, + cta: INVESTOR_DETAILS.contact, + external: true, + icon: Mail, + }, +] as const; + const supportTargets = [ { title: "FleetCrown", @@ -35,30 +65,68 @@ export default function SupportPage() { }>
Support
-

Fund the work on OrangeCat

+

Get help

- OrangeCat is the public funding surface for both sibling products. Choose what you want - to support, then pay the entity directly in Bitcoin. An OrangeCat account is not required - to scan a payment request. + Stuck on setup, hit a bug, or need a human? Start with the docs, file an issue, or write + directly.

- {supportTargets.map(({ title, body, href, icon: Icon }) => ( - - -

{title}

-

{body}

- - Open on OrangeCat - -
- ))} + {helpChannels.map(({ title, body, href, cta, external, icon: Icon }) => { + const inner = ( + <> + +

{title}

+

{body}

+ + {cta} + + + ); + return external ? ( + + {inner} + + ) : ( + + {inner} + + ); + })}
+ +
+

Fund the work

+

+ OrangeCat is the public funding surface for both sibling products. Choose what you want + to support, then pay the entity directly in Bitcoin. An OrangeCat account is not + required to scan a payment request. +

+
+ {supportTargets.map(({ title, body, href, icon: Icon }) => ( + + +

{title}

+

{body}

+ + Open on OrangeCat + +
+ ))} +
+
+

Why Bitcoin only today?

diff --git a/src/app/whitepaper/page.tsx b/src/app/whitepaper/page.tsx index 6679e2721..7c0cd4a05 100644 --- a/src/app/whitepaper/page.tsx +++ b/src/app/whitepaper/page.tsx @@ -35,6 +35,15 @@ function renderInline(text: string): string { .replace(/`(.+?)`/g, "$1"); } +// Stable anchor ids for H2 sections — feeds both the ToC links and the +// heading ids, so the two can never drift. +function slugify(text: string): string { + return text + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, ""); +} + export default function WhitepaperPage() { const raw = fs.readFileSync(path.join(process.cwd(), "content", "whitepaper.md"), "utf-8"); const { meta, body } = parseFrontmatter(raw); @@ -43,6 +52,9 @@ export default function WhitepaperPage() { const subtitle = meta.subtitle ?? ""; const version = meta.version ?? "0.1"; const publishedAt = meta.publishedAt ?? ""; + const toc = blocks.flatMap((block) => + block.type === "h2" ? [{ text: block.text, id: slugify(block.text) }] : [], + ); return ( }> @@ -61,11 +73,31 @@ export default function WhitepaperPage() { )}

-
+ {toc.length > 1 && ( + + )} + +
{blocks.map((block, i) => { switch (block.type) { case "h2": - return

{block.text}

; + return ( +

+ {block.text} +

+ ); case "h3": return

{block.text}

; case "p": diff --git a/src/components/public/PublicFooter.tsx b/src/components/public/PublicFooter.tsx index 604bb6b92..da749375f 100644 --- a/src/components/public/PublicFooter.tsx +++ b/src/components/public/PublicFooter.tsx @@ -23,7 +23,7 @@ const FOOTER_GROUPS = [ { label: "Download", href: "/download" }, { label: "Sign in", href: "/sign-in" }, { label: "Roadmap", href: "/roadmap" }, - { label: "Changelog", href: "/changelog" }, + { label: "Changelog", href: "/releases" }, ], }, { @@ -31,8 +31,9 @@ const FOOTER_GROUPS = [ links: [ { label: "Docs", href: "/docs" }, { label: "Whitepaper", href: "/whitepaper" }, - { label: "Blog", href: "/blog" }, + { label: "Thoughts", href: "/thoughts" }, { label: "Frontier", href: "/frontier" }, + { label: "Investors", href: "/investors" }, ], }, { @@ -98,10 +99,14 @@ export function PublicFooter() { {/* The fleet. Every site here had no inbound link from anywhere else on the fleet until this block existed — see config/fleet-sites.ts. Plain anchors, not next/link: these are other origins, and the point is that - a crawler follows them. */} -
-
The fleet
-
+ a crawler follows them. Collapsed by default so the three product + pillars above stay the footer's primary content — the links remain in + the DOM for crawlers whether or not a human ever opens the disclosure. */} +
+ + Part of the fleet — {FLEET_SITES.length} more sites + +
{FLEET_SITES.map((site) => ( ))}
-
+
© {new Date().getFullYear()} {APP_NAME} · Mao Nakamoto
- + Fleet Runner v{CURRENT_RELEASE.version}
diff --git a/src/components/thoughts/ThoughtsLibrary.tsx b/src/components/thoughts/ThoughtsLibrary.tsx index cfd18c0f6..a58cbc537 100644 --- a/src/components/thoughts/ThoughtsLibrary.tsx +++ b/src/components/thoughts/ThoughtsLibrary.tsx @@ -58,6 +58,22 @@ export function ThoughtsLibrary({ featured && filtered.some((a) => a.slug === featured.slug) ? featured : null; const rest = filtered.filter((a) => a.slug !== featuredVisible?.slug); + // 63 full-abstract cards in one wall buries everything below the fold. + // Lead with the most recent essays (clamped abstracts), then collapse the + // archive into per-year
sections — native disclosure, no JS + // needed to open them, and every link stays in the DOM for crawlers. + // listThoughts() already sorts newest-first. + const RECENT_COUNT = 10; + const recent = rest.slice(0, RECENT_COUNT); + const archive = rest.slice(RECENT_COUNT); + const archiveByYear = new Map(); + for (const article of archive) { + const year = article.publishedAt.slice(0, 4) || "Undated"; + const group = archiveByYear.get(year); + if (group) group.push(article); + else archiveByYear.set(year, [article]); + } + return (
{/* Featured essay — leads the page, first thing visible */} @@ -149,19 +165,45 @@ export function ThoughtsLibrary({

) : ( -
- {rest.map((article) => ( - - {formatMeta(article)} -

{article.title}

-

{article.summary}

- - ))} -
+ <> +
+ {recent.map((article) => ( + + {formatMeta(article)} +

{article.title}

+

{article.summary}

+ + ))} +
+ + {archive.length > 0 && ( +
+ {[...archiveByYear.entries()].map(([year, group]) => ( +
+ + {year} — {group.length} {group.length === 1 ? "essay" : "essays"} + +
+ {group.map((article) => ( + + {article.title} + {formatMeta(article)} + + ))} +
+
+ ))} +
+ )} + )} ); diff --git a/src/config/auth.ts b/src/config/auth.ts index 11e9a480d..cb5548563 100644 --- a/src/config/auth.ts +++ b/src/config/auth.ts @@ -49,8 +49,9 @@ export const PUBLIC_NAV: PublicNavEntry[] = [ title: "Understand", items: [ { label: "Mission", href: "/mission", description: "Why FleetCrown exists" }, + { label: "Philosophy", href: "/philosophy", description: "The principles behind the product" }, { label: "Roadmap", href: "/roadmap", description: "What works now and what comes next" }, - { label: "Changelog", href: "/changelog", description: "Every shipped release" }, + { label: "Changelog", href: "/releases", description: "Every shipped release" }, { label: "Docs", href: "/docs", description: "Install, connect, and operate your fleet" }, { label: "Whitepaper", href: "/whitepaper", description: "Architecture and product thesis" }, ], @@ -66,26 +67,31 @@ export const PUBLIC_NAV: PublicNavEntry[] = [ ], }, { + // Label matches the destination page's own title ("Thoughts") — the old + // "Blog" label landed on a page that never calls itself a blog. /blog now + // redirects here so the canonical URL is the one the nav shows. kind: "link", - label: "Blog", - href: "/blog", + label: "Thoughts", + href: "/thoughts", }, { kind: "link", label: "Support", href: "/support", }, + // Dropdown descriptions stay to one short sentence — the essay-length + // context lives in the Thoughts essays, not in a hover. { kind: "external", label: "OrangeCat", href: "https://orangecat.ch", - description: "The economic pillar of the stack — Bitcoin-native funding, entities, and the public timeline. FleetCrown is the engineering pillar; OrangeCat is where what gets built is funded and shared. See the Thoughts essay The Two Halves of the Individual Singularity for the joint architecture.", + description: "The economic pillar of the stack — Bitcoin-native funding and public entities.", }, { kind: "external", label: "Solon", href: "https://solon.orangecat.ch", - description: "The governance pillar of the stack — Bitcoin-signed proposals and votes. FleetCrown's agent Loki is a registered voting member there, signing from its own environment.", + description: "The governance pillar of the stack — Bitcoin-signed proposals and votes.", }, ]; diff --git a/src/config/marketing-content.ts b/src/config/marketing-content.ts index 224ee92ab..1ed88a200 100644 --- a/src/config/marketing-content.ts +++ b/src/config/marketing-content.ts @@ -100,7 +100,14 @@ export const INVESTORS = { "The same control patterns transfer to physical robotics. The market has not yet appreciated this.", ], built: "A web command center coordinates fleets of AI agents across projects. A native Fleet Runner desktop app — same React tree as the web, plus tray and OS notifications — runs them directly in the operator's terminal environment via Zellij. Per-project autonomy controls, reliable handoff systems, queue management, and truthful status surfaces are live and in daily use. Signed multi-OS installers ship from a single CI matrix on every release tag.", - traction: "FleetCrown runs its own creator's entire operation: a live fleet of projects dispatched, monitored, and governed daily through the product itself. Every surface is proven in continuous real-world self-use before it ships — the operator is the first and most demanding user. The bet is that the same workflow generalizes to anyone running many agents at once.", + // Scannable bullets, not a prose wall — the page pairs these with the live + // fleet snapshot (same real data source as the homepage hero). + traction: [ + "FleetCrown runs its creator's entire operation — a live fleet of projects dispatched, monitored, and governed daily through the product itself.", + "Every surface is proven in continuous real-world self-use before it ships; the operator is the first and most demanding user.", + "The homepage hero and this page render the same live snapshot of that fleet — real data, never fabricated numbers.", + "The bet: the same workflow generalizes to anyone running many agents at once.", + ], ask: "We are raising to productize the local fleet runner, harden the remote control plane, expand open-model support, and lay groundwork for robotic orchestration.", }; @@ -109,152 +116,163 @@ export const INVESTOR_DETAILS = { contact: "mao@orangecat.ch", }; -// Roadmap — multi-phase, with concrete bullets and cross-cutting throughlines -export const ROADMAP = { +// Roadmap — three honest buckets ("Shipping now" / "Next" / "Research") with +// one-liner items. Detail bullets render collapsed; strategy-essay sections +// keep a short summary plus a link to the full Thoughts essay instead of +// re-printing the argument on the roadmap. +export type RoadmapItem = { + title: string; + /** One line — what this is and why it matters. */ + line: string; + /** Optional detail bullets, rendered inside a collapsed
. */ + details?: string[]; + /** Optional pointer to the Thoughts essay carrying the full argument. */ + essay?: { label: string; href: string }; +}; +export type RoadmapBucket = { + title: string; + summary: string; + items: RoadmapItem[]; +}; + +export const ROADMAP: { + eyebrow: string; + title: string; + lede: string; + buckets: RoadmapBucket[]; + throughlines: { + eyebrow: string; + title: string; + lede: string; + items: { title: string; body: string }[]; + }; + closer: string; +} = { eyebrow: "PRODUCT DIRECTION", title: "Roadmap", lede: "Building the operating system for people running serious AI agent operations — and for the robotic fleets that come next.", - phases: [ + buckets: [ { - marker: "NOW", - title: "Foundation", + title: "Shipping now", summary: "Live in production. The system already coordinates real fleets across real projects.", - bullets: [ - "Web command center coordinates fleets of AI agents across projects.", - "One-button fleet autopilot: pause all, or build all — agents drain each project's queue, then pick the next-best task.", - "One local execution path: the Fleet Runner desktop app owns Zellij, agent launching, and state sync (the legacy bash runner was retired by deletion).", - "Reliable handoff system between agent sessions, with truthful card status surfaces.", - "Per-project pause / resume / direct-send semantics with per-project autopilot overrides.", - "Multi-user SaaS foundation — GitHub OAuth, organizations, team invites, agent tokens.", - "Native Fleet Runner desktop app — loads the same React tree the web serves (one UI, two surfaces) plus tray icon, OS notifications on agent idle, and an embedded Zellij watcher for fire-and-walk-away dispatch.", - "Multi-OS release pipeline. One tag push produces signed macOS, Windows, and Linux installers from a shared CI matrix.", - ], - note: "The architecture is proven. The next phase is finishing distribution and auto-update so non-technical operators can install without touching a terminal.", - }, - { - marker: "NEXT", - title: "Distribution and auto-update", - summary: "Make Fleet Runner trivial to install and keep current on every platform — including for builders who never open a terminal.", - bullets: [ - "Public macOS (.dmg, signed + notarized) and Windows (.exe) builds landing on every tagged release alongside the existing Linux AppImage / .deb.", - "Auto-update via electron-builder's GitHub provider so users never download a stale binary.", - "Native package channels where they exist — Homebrew tap for macOS, winget for Windows, .deb apt repo for Linux.", - "Clean window.fleetRunner IPC bridge so the web React tree can detect 'I am running inside the desktop app' and surface desktop-only affordances coherently.", - "Headless CLI agent install path for servers, CI runners, and operators who prefer a pure terminal flow.", - ], - note: "Cursor, Claude Code, and Grok Build all converged on the same pattern — local client owns execution as a real application, not a service that polls a queue. Fleet Runner takes one extra step: the app and the web run the same React tree, so there is exactly one product surface to design and one codebase to keep at parity.", - }, - { - marker: "AFTER", - title: "Remote control channel", - summary: "Web and mobile become genuine remote control surfaces — not eventually-consistent dashboards.", - bullets: [ - "The local app opens an authenticated outbound WebSocket to the control plane when remote control is enabled.", - "Commands flow: surface → backend → that user's specific local app, over the open connection.", - "Status flows back the same way, with low latency.", - "Falls back to the existing queue when the local app is offline.", - "Scoped credentials via the existing agent token system. Outbound-only connections — easy to firewall.", - "All execution of dangerous actions stays on the user's machine. The backend never sees raw file contents unless the user explicitly shares them.", - ], - }, - { - marker: "THEN", - title: "Mobile fleet control", - summary: "Steering a fleet from your phone should feel native, not like a phone-sized web page.", - bullets: [ - "Native iOS and Android apps on the same remote control channel.", - "Optimized for steering and approval, not authoring.", - "Push notifications for Beacon mode and Mission checkpoints.", - "Swipe actions for approving or rejecting agent outputs at the moments that actually need a human.", - "Voice capture for the autopilot intent ladder — direct the fleet while walking.", - ], - }, - { - marker: "LATER", - title: "Cloud agents as a complementary mode", - summary: "When the local machine is unavailable, parallel cloud agents take over — with explicit handoff.", - bullets: [ - "Cloud agents for long-running, highly parallel work that does not fit on one laptop.", - "Explicit handoff between local and cloud sessions — the same agent identity continues across substrates.", - "A complement to local execution, never the default for hosted users.", - "Useful for builders running 8–12 agents at once who need extra parallelism or 24-hour availability.", - ], - }, - { - marker: "LEARNING", - title: "The fleet learns from its own runs", - summary: "Today FleetCrown grades every run and forgets what it learned. The same evidence that decides whether a run was done should decide how the next one is briefed.", - bullets: [ - "Every dispatch is stored with the exact prompt that produced it and the graded outcome it earned — one joinable record instead of two disconnected logs.", - "A per-intent report shows which kinds of work fail, how much they cost, and the most common reason a reviewer rejected them.", - "Prompt improvements are proposed from real run history and reviewed by a human before they land — the same accept-or-dismiss gate the Frontier loop already uses.", - "Per-project lessons carry forward as references the agent may consult, never as rules it must obey.", - "Every learned change stays in version control, so any improvement can be read, questioned, and reverted.", - ], - note: "The judge that grades a run is a different model lineage from the agent that did the work, and no learned change applies itself. A harness that grades its own trajectory and edits itself unsupervised is how self-improving systems learn to game their own scoring — the human gate is the feature, not the friction. Detailed sequencing lives in the internal self-improvement plan.", - }, - { - marker: "TEAMS", - title: "Team and multi-machine surfaces", - summary: "Same control plane, multiple operators, multiple machines.", - bullets: [ - "Shared fleet views across machines and across team members.", - "Per-operator permissions. Per-project autonomy ceilings.", - "Coordination when several people steer the same fleet without stepping on each other.", - "Multi-machine orchestration for power users running across desktop, laptop, and remote box.", - ], - }, - { - marker: "CONVERGE", - title: "One agent per user — the surface merge", - summary: "Today's Loki (FleetCrown) and Cat (OrangeCat) become one agent that sees the user's whole life. Two AIs is engineering convenience; one agent per user is the only interaction model that scales to nine billion builders.", - bullets: [ - "Memory layer unifies — the user's contacts, projects, goals, transactions, listings, and decisions live in one graph the agent reasons against.", - "Reasoning loop unifies — when the user says \"do X,\" the agent decomposes X across whatever surfaces are involved without the user choosing the surface.", - "Autonomy ladder unifies — Manual → Queue → Beacon → Continuous → Mission applies across both products as one dial.", - "Approval queue unifies — FleetCrown's Action Queue and OrangeCat's pending Cat actions become one inbox. The user lives in this queue.", - "Surfaces (FleetCrown, OrangeCat) stay as engineering boundaries but stop being user-facing concepts. The user perceives \"my agent.\"", - ], - note: "See the Thoughts essay \"From Two AIs to One\" for the strategic argument. Convergence is engineering on top of two products that already work standalone, not a rewrite.", - }, - { - marker: "STAKEHOLDERS", - title: "Stakeholder graph — the concrete first convergence", - summary: "Every project has eight surrounding relationships — competitors, collaborators, investors, customers, employees, acquirers, acquisition targets, in-house dev projects. Track them as typed edges in OrangeCat's entity graph; surface them on FleetCrown; let the agent act on them.", - bullets: [ - "Storage in OrangeCat. The eight categories are edges between entities OrangeCat already has (projects, actors, groups, products, services, investments). One typed-edge schema covers all eight — no new entity tables.", - "Operations in FleetCrown. /projects renders the eight stakeholder lanes per project, read from OrangeCat via the identity bridge. The Watch on /today surfaces signals derived from the graph.", - "Ship competitors first — the most automatable category. Landing-page diffs, RSS, funding-event detection, hiring-page diffs. The other seven follow with the same primitives.", - "Action loop: signal → Watch focus → \"Brief Loki on this\" → agent drafts a response (pricing tweak, positioning post, feature pull-forward) → approve / disapprove.", - "No second graph. FleetCrown does not duplicate OrangeCat's entity model. Two graphs are always wrong.", + items: [ + { + title: "Fleet command center", + line: "The web control plane coordinates fleets of AI agents across projects, with one-button autopilot: pause all, or build all.", + details: [ + "Agents drain each project's queue, then pick the next-best task.", + "Per-project pause / resume / direct-send semantics with per-project autopilot overrides.", + "Reliable handoff system between agent sessions, with truthful card status surfaces.", + "Multi-user SaaS foundation — GitHub OAuth, organizations, team invites, agent tokens.", + ], + }, + { + title: "Fleet Runner desktop app", + line: "One local execution path: the desktop app owns Zellij, agent launching, and state sync — the same React tree the web serves.", + details: [ + "Tray icon, OS notifications on agent idle, and an embedded Zellij watcher for fire-and-walk-away dispatch.", + "The legacy bash runner was retired by deletion — one path, not two.", + "Multi-OS release pipeline: one tag push produces signed installers from a shared CI matrix.", + ], + }, ], - note: "First concrete instance of the convergence — same data, two surfaces, one agent reasoning across both. See the Thoughts essay \"Where Stakeholders Live\" for the full design.", }, { - marker: "ECONOMY", - title: "OrangeCat integration — the transaction half", - summary: "Make it natural to fund what people build and build what people choose to fund, without pretending the full loop is already automated.", - bullets: [ - "One OrangeCat identity across both products through the existing OIDC bridge.", - "Typed links connect a FleetCrown project to any OrangeCat entity acting as its origin, public profile, funding page, offering, or community.", - "A signed, ten-minute OrangeCat handoff can prefill a FleetCrown project and Loki plan; the owner approves before anything is created or dispatched.", - "OrangeCat remains the share, promotion, and Bitcoin funding surface. FleetCrown shows its confirmed funding summary read-only.", - "Automatic work orders, milestone-triggered dispatch, smart-contract escrow, fiat, privacy coins, and full Nostr identity stay later-roadmap work.", + title: "Next", + summary: "Concrete engineering, in sequence: distribution first, then the remote control channel, then mobile on top of it.", + items: [ + { + title: "Distribution and auto-update", + line: "Make Fleet Runner trivial to install and keep current on every platform — including for builders who never open a terminal.", + details: [ + "Public macOS (.dmg, signed + notarized) and Windows (.exe) builds landing on every tagged release alongside the existing Linux AppImage / .deb.", + "Auto-update via electron-builder's GitHub provider so users never download a stale binary.", + "Native package channels where they exist — Homebrew tap for macOS, winget for Windows, .deb apt repo for Linux.", + "Headless CLI agent install path for servers, CI runners, and operators who prefer a pure terminal flow.", + ], + }, + { + title: "Remote control channel", + line: "Web and mobile become genuine remote control surfaces — not eventually-consistent dashboards.", + details: [ + "The local app opens an authenticated outbound WebSocket to the control plane when remote control is enabled; commands flow surface → backend → that user's specific local app.", + "Falls back to the existing queue when the local app is offline.", + "Scoped credentials via the existing agent token system. Outbound-only connections — easy to firewall.", + "All execution of dangerous actions stays on the user's machine. The backend never sees raw file contents unless the user explicitly shares them.", + ], + }, + { + title: "Mobile fleet control", + line: "Native iOS and Android apps on the remote control channel — optimized for steering and approval, not authoring.", + details: [ + "Push notifications for Beacon mode and Mission checkpoints.", + "Swipe actions for approving or rejecting agent outputs at the moments that actually need a human.", + "Voice capture for the autopilot intent ladder — direct the fleet while walking.", + ], + }, ], - note: "Bitcoin is the first live settlement rail because confirmed transfers can be independently audited. Fiat relies on private bank reconciliation; privacy coins deliberately remove the public trail. Neither is presented as available today.", }, { - marker: "ROBOTICS", - title: "Physical robotic fleets", - summary: "The same control patterns, applied to a different substrate.", - bullets: [ - "Per-fleet autonomy — the same dial: Manual → Queue → Beacon → Continuous → Mission.", - "Handoff between human and robotic initiative.", - "Queues, visibility, override — everything we shipped for software agents transfers.", - "Robotics is a different execution surface bound to the same control plane.", - "Not a separate product line bolted on later. The continuity is the point.", + title: "Research", + summary: "Directions we are committed to that are design and strategy work today. Nothing here is presented as available.", + items: [ + { + title: "Cloud agents as a complementary mode", + line: "When the local machine is unavailable, parallel cloud agents take over — with explicit handoff, never as the default.", + details: [ + "Cloud agents for long-running, highly parallel work that does not fit on one laptop.", + "Explicit handoff between local and cloud sessions — the same agent identity continues across substrates.", + "Useful for builders running 8–12 agents at once who need extra parallelism or 24-hour availability.", + ], + }, + { + title: "The fleet learns from its own runs", + line: "The same evidence that decides whether a run was done should decide how the next one is briefed — with a human gate on every learned change.", + details: [ + "Every dispatch is stored with the exact prompt that produced it and the graded outcome it earned — one joinable record instead of two disconnected logs.", + "Prompt improvements are proposed from real run history and reviewed by a human before they land.", + "Per-project lessons carry forward as references the agent may consult, never as rules it must obey; every learned change stays in version control.", + "The judge that grades a run is a different model lineage from the agent that did the work — an unsupervised self-grading harness learns to game its own scoring, so the human gate is the feature, not the friction.", + ], + }, + { + title: "Team and multi-machine surfaces", + line: "Same control plane, multiple operators, multiple machines — shared fleet views, per-operator permissions, per-project autonomy ceilings.", + details: [ + "Coordination when several people steer the same fleet without stepping on each other.", + "Multi-machine orchestration for power users running across desktop, laptop, and remote box.", + ], + }, + { + title: "One agent per user", + line: "Loki (FleetCrown) and Cat (OrangeCat) converge into one agent over one memory graph, one autonomy dial, and one approval inbox — the surfaces stay as engineering boundaries, the user perceives \"my agent.\"", + essay: { label: "Read the essay: From Two AIs to One", href: "/thoughts/from-two-ais-to-one" }, + }, + { + title: "Stakeholder graph", + line: "Track each project's surrounding relationships — competitors, collaborators, investors, customers — as typed edges in OrangeCat's entity graph, surfaced on FleetCrown for the agent to act on. Competitors ship first as the most automatable category.", + essay: { label: "Read the essay: Where Stakeholders Live", href: "/thoughts/where-stakeholders-live" }, + }, + { + title: "OrangeCat integration — the transaction half", + line: "Make it natural to fund what people build and build what people choose to fund, without pretending the full loop is already automated.", + details: [ + "One OrangeCat identity across both products through the existing OIDC bridge.", + "Typed links connect a FleetCrown project to any OrangeCat entity acting as its origin, public profile, funding page, offering, or community.", + "A signed, ten-minute OrangeCat handoff can prefill a FleetCrown project and Loki plan; the owner approves before anything is created or dispatched.", + "OrangeCat remains the share, promotion, and Bitcoin funding surface. FleetCrown shows its confirmed funding summary read-only.", + "Bitcoin is the first live settlement rail because confirmed transfers can be independently audited. Automatic work orders, escrow, fiat, and privacy coins stay research — none is presented as available today.", + ], + }, + { + title: "Physical robotic fleets", + line: "The same control patterns — autonomy dial, handoff, queues, visibility, override — applied to a different execution substrate. Not a separate product line bolted on later.", + details: [ + "Per-fleet autonomy — the same dial: Manual → Queue → Beacon → Continuous → Mission.", + "The person who today directs a fleet of agents building software is developing the muscles that will let them direct a fleet of robots building physical things.", + ], + }, ], - note: "The person who today directs a fleet of agents building software is developing the muscles that will let them direct a fleet of robots building physical things.", }, ], throughlines: {