Ship the landing publicly behind a docs-gate flag, capture demand through two honest doors, into a list we own
0022-public-landing-demand-capture
- Reversibility
- two-way door
DEC-0022 — Public landing behind a docs-gate, two honest capture doors, a list we own
Reversibility: two-way door — the docs-gate flag, the provider behind the one capture function, and the door copy are all swappable; the durable commitments are landing-public-now, owning/exporting the list, and honest capture.
Context
Recalibrate the Dossier brand identity — demote color, promote type + restraint + craft — then build the showcase landing built (Phase 1 + 2) a premium marketing/showcase landing at / on the @dossier/site Astro surface, sitting above the OKF knowledge-base (KB) surface that Astro Starlight as the docs-site generator + the product-owner, starlight-engineer, and documentation-engineer functions stood up on Astro Starlight (the docs render Dossier's own knowledge/ repo). The landing is now polish-grade; the question this decision answers is how to take it public.
Two facts shaped it:
- The human-facing surface should be live now, but the KB surface is not ready to be public. The Starlight site renders an institutional-memory KB (Dossier's own dogfood — decision records, the model, the log) that isn't a public artifact yet. We want the landing live without exposing the docs.
- Going public means capturing demand — and how we capture it is itself a statement about what Dossier is. The product thesis is sovereignty: "your knowledge is just files in your git — owned, portable" (Dossier — Mission & North Star / Adopt OKF as Dossier's canonical knowledge format). The capture mechanism has to be congruent with that, not contradict it.
This decision was made and built + verified this session (2026-06-16); it is three coupled sub-decisions.
Options considered
1. Holding the docs back — delete the Starlight work vs. gate it behind a reversible flag.
- (a) Delete / strip the Starlight surface so only the landing remains. This was the option recon surfaced. Rejected: it throws away the Astro Starlight as the docs-site generator + the product-owner, starlight-engineer, and documentation-engineer functions work, is irreversible, and splits the tree's truth — the docs would have to be rebuilt to come back.
- (b) Gate the docs behind a reversible
DOCS_ENABLEDenv flag, deleting nothing (chosen). The Starlight integration stays fully in the tree;packages/site/astro.config.mjsconditionally includes it (...(docsEnabled ? [starlight({…})] : [])). The flag defaults ON, so localdev/buildkeep the full docs site for development; production (Vercel) setsDOCS_ENABLED=falseviavercel.jsonbuild.env, so the production build emits onlysrc/pages/index.astro(+ the capture endpoint). Atomic, reversible, single-source-of-truth — flip one flag to bring the docs back.
2. Capturing demand — a classic position/referral waitlist vs. two honest doors.
- (a) A vanity waitlist (queue position, referral counter, "you're #N"). Rejected as tonally incongruent with Dossier's thesis: faking a queue/counter to manufacture urgency contradicts a pitch built on owned, portable, sovereign knowledge. It also optimizes for vanity signups over qualified demand.
- (b) Two doors + the existing GitHub CTA (chosen). A broad single-field early-access email ("Get the first build") for breadth, and a distinct, higher-intent design-partner door for agencies ("Run an agency? Bring sovereign learning loops to your clients") capturing email + an optional one-line note for depth. The GitHub CTA stays (build-in-public). The reasoning: our GTM is explicitly "delivered through the agencies that already serve them" (Dossier — Mission & North Star — the wedge), so the highest-value conversion is an agency raising its hand — a design-partner conversation, not a signup counter. Industry signal (mid-2026) reinforces it: for vertical/B2B AI, design partners are the real validation; "qualified demand, not vanity signups"; single-field forms convert ~3× multi-field. So: breadth (email) + depth (design partners) + authenticity (the public dogfooded repo).
3. Where the list lives — a hosted form/lock-in service vs. a list we own.
- (a) A turnkey waitlist/marketing SaaS that owns the audience. Rejected: it puts our own funnel behind someone else's lock-in — the opposite of the sovereignty we sell.
- (b) Own the list; isolate the provider behind one function (chosen). Submissions flow through a Vercel serverless function (
src/pages/api/subscribe.ts, an Astro endpoint withprerender = falsevia the@astrojs/verceladapter — the rest of the site stays static) into Buttondown, a subscriber list we can export anytime, no lock-in, tagged by door (early-access/design-partner). The provider call is isolated to that one function — a swappable seam (Resend is the noted alternative), the same seam discipline used across the platform. The endpoint accepts JSON or form-encoded (progressive enhancement: the form works with JS off), honeypot-filters bots, and fails honestly (503) if misconfigured rather than silently dropping signups.
Decision
Ship the marketing landing publicly now, hold the docs/KB surface back behind a reversible flag, capture demand through two honest doors, and own the list.
- Landing-only via a reversible
DOCS_ENABLEDflag, not deletion. The Starlight integration stays in the tree, gated inpackages/site/astro.config.mjs(...(docsEnabled ? [starlight({…})] : [])). Default ON (localdev/buildkeep the full docs site for development); production setsDOCS_ENABLED=falseviavercel.jsonbuild.env, so onlysrc/pages/index.astro(+ the capture endpoint) builds. Verified both ways: docs-off emits justindex.html; docs-on restores all KB routes + Pagefind. - Two honest doors + the GitHub CTA. (a) A broad single-field early-access email ("Get the first build"); (b) a distinct, higher-intent design-partner door for agencies ("Run an agency? Bring sovereign learning loops to your clients") capturing email + an optional one-line note; the existing GitHub build-in-public CTA stays. No vanity waitlist — no queue position, no referral counter.
- Own the list; host on Vercel. One parametrized, progressively-enhanced
CaptureForm.astroserves both doors and posts tosrc/pages/api/subscribe.ts(prerender = falsevia@astrojs/vercel; the rest of the site stays static) → Buttondown, exportable anytime, tagged by door. The provider is isolated to that one function (swappable seam; Resend the alternative). The endpoint accepts JSON or form-encoded (works JS-off), honeypot-filters bots, and returns 503 on misconfiguration rather than silently dropping a signup.
This realizes the launch story of Recalibrate the Dossier brand identity — demote color, promote type + restraint + craft — then build the showcase landing (take the built landing public) and gates Astro Starlight as the docs-site generator + the product-owner, starlight-engineer, and documentation-engineer functions's docs surface for production without discarding it.
Rationale
- Reversible flag > deletion, because the docs aren't wrong, just not-yet-public. A flag keeps a single source of truth and the Astro Starlight as the docs-site generator + the product-owner, starlight-engineer, and documentation-engineer functions work intact — flip
DOCS_ENABLEDto bring the full KB site back. Deleting it would be irreversible and would split the tree's truth into "what shipped" vs. "what we'd have to rebuild." Defaulting ON also means development keeps the full surface while production stays landing-only — no DX regression. - Honest capture is congruent with the product; a vanity waitlist contradicts it. Dossier's pitch is sovereignty and ownership; faking a queue/counter to manufacture urgency would undercut the thesis at the first touchpoint. Two real doors plus a public, dogfooded repo say the same thing the product does.
- The two doors match the GTM, not just conversion best-practice. The wedge is agencies (Dossier — Mission & North Star); the highest-value signal is an agency volunteering as a design partner, so a dedicated higher-intent door (email + a one-line note) sits beside the broad single-field email. The single-field/multi-field and design-partners-as-validation signals (mid-2026) corroborate the shape, but the why is the wedge.
- Owning + exporting the list mirrors the sovereignty thesis applied to our own funnel. Putting submissions into a list we can export anytime (Buttondown, tagged by door), behind a one-function swappable provider seam, is the product's own principle applied to itself — congruent, not merely expedient. Failing honestly (503) over silently dropping signups is the same "no fabricated status / no silent loss" discipline the platform holds elsewhere.
verified, notasserted. Built and reproduced this session: the capture endpoint has 11 passing tests (packages/site/test/subscribe.test.ts), and the docs-gate was verified both ways (docs-off → onlyindex.html; docs-on → all KB routes + Pagefind restored). Evidence exists for the mechanism (the flag gates correctly; the endpoint validates/tags/honeypots/fails-honestly). This is mechanism-verified — not a claim that the funnel converts; market validation is the open item, which is why the durable commitment, not the conversion outcome, carries the confidence.
Consequences
- The landing can ship public today; the KB stays private to production via one flag. Production builds only
index.astro+ the capture endpoint (DOCS_ENABLED=false); local dev keeps the full Starlight docs (default ON). Bringing the public docs surface back is a one-flag change — no rebuild. - Demand now lands in a list we own, segmented by intent. Early-access (breadth) and design-partner (depth, with a note) submissions are tagged in Buttondown and exportable anytime; the GitHub CTA continues to capture build-in-public interest. No vanity-metric surface to maintain or live up to.
- The provider is a swappable seam. Buttondown lives behind the single
subscribe.tsfunction; switching to Resend (or self-host) touches one file, not the form or the site. The endpoint's JS-off support and honeypot mean the doors work broadly and resist bot noise; a misconfig surfaces as a 503, never a silent drop. - Surface architecture stays sovereign. Only the landing/capture is added;
prerender = falseis scoped to the endpoint while the rest of the site stays static, and no sovereignknowledge/file is touched — Adopt OKF as Dossier's canonical knowledge format holds (the surface is still a derived, replaceable view; the OKF repo is untouched). - A minor UX sub-decision (noted, not a separate record): the design-partner door is a raised accent-edged card on normal surface tokens, not the dark
.ds-card--editorialinversion — because the editorial card hardcodes a fixed near-ink surface and nested form controls couldn't stay WCAG-correct across themes without reintroducing hex (a forbidden invariant under Establish the design system and the UX-engineering function). A proper dark-inverted form would need a small tokens-side addition in@dossier/design; deferred unless we decide we want it. The capture UI otherwise consumes@dossier/designtokens only (landing.css). - Two-way vs. durable. The
DOCS_ENABLEDgate, the provider behind the one capture function, and the door copy are all swappable (two-way door). The durable commitments are: landing public now, a list we own and can export, and honest capture (no manufactured queue).
Review
Revisit once the landing is actually deployed public on Vercel and demand starts arriving — confirm the docs-gate holds in the live production build, that Buttondown receives door-tagged submissions end-to-end (the seam works against the real provider, not just tests), and that the 503-on-misconfig path never silently drops a signup in production. Promote toward a funnel judgment only with real conversion data (mechanism is verified; market fit is not — that gap is why the open question is demand, not code). Decide the dark-inverted design-partner card (the deferred @dossier/design tokens-side addition) only if we want that treatment. When the KB surface is ready to be public, flipping DOCS_ENABLED on in production is the (reversible) re-entry — re-examine what of the dogfooded knowledge/ repo should be public at that point.