| name | generate-landing |
| description | Generate a tailored client landing instance — a typed LandingContent module — from a provisioned client's OKF repo and tenant manifest. Use after a client is ingested and served (KNOWLEDGE-BRIEF generated) to produce a client-specific marketing landing that re-frames Dossier's universal messaging in the client's own vocabulary, with a hero artifact that is a REAL atom from the client's repo. Human-curated authoring, not a hosted runtime. |
Generate a Client Landing Instance
Author one LandingContent value — a tailored landing for a provisioned client — that renders through packages/app/src/lib/components/LandingPage.svelte into the exact same markup as Dossier's own landing. Only the copy changes; theme, layout, motion, and the five-stage loop shape never do. The output's canonical home is the client's own repo (sovereignty); the copy under packages/app is only ever a demo of that.
This is a Claude Code skill — humans curate, agents extend (DEC-0004, DEC-0032). It writes a data module, never runtime/server/DB code.
The contract (read first — it is the cage)
- Output type:
packages/app/src/lib/content/landing/schema.ts — LandingContent. Every field you fill is a string, boolean, or token segment. There is no color/font/radius/motion field anywhere, by design: a tailored instance physically cannot carry a palette (#2).
- Reference instance:
packages/app/src/lib/content/landing/dossier.ts — Dossier's own value. This is your fallback for any slot you cannot truthfully fill from the client's OKF, and the source of every universal slot.
- Round-trip proof:
packages/app/test/landing-dossier-roundtrip.test.ts — the pattern your client's hero artifact must also satisfy: body.map(t => t.text).join('') equals the raw atom excerpt, byte-for-byte.
Inputs
- The client's OKF repo path (system of record) and tenant manifest
clients/<id>/tenants/<t>/dossier.tenant.json (clientId, vertical, okfDir), e.g. RBA → clients/rba/tenants/rba-consulting/ (vertical: digital-experience-agency, okfDir: okf).
- The client's
KNOWLEDGE-BRIEF.md (the served-OKF summary) and, optionally, a small brand manifest: wordmark (text, or a monochrome currentColor SVG of the name — never a palette), meta title/description seed, GitHub/repo href, which atom is the hero, per-section framing notes. Reuses the clients/<id>/src/_manifest.json convention.
- The Dossier default instance (
dossier.ts) — fallback + source of universal slots.
Procedure
- Read the contract, then the client. Read
schema.ts and dossier.ts. Then read the tenant manifest, KNOWLEDGE-BRIEF.md, and the served OKF through the tenant-scoped @dossier/mcp tools (search_concepts → get_concept → get_related) — generate the page the way the product retrieves, so every claim has a provenance trail.
- Derive brand + vertical. Take the wordmark and meta from the brand manifest, falling back to the client anchor atom (
type: client) — its title and description are honest seed copy (e.g. RBA → "RBA Consulting"; description from clients/rba-consulting.md). Use a { kind: 'text', text } wordmark unless the client supplied its own monochrome currentColor SVG.
- Select a REAL hero atom from the client's repo (#4, #5). Prefer an atom with rich typed edges that best demonstrates "typed, sourced, confidence-scored" — a
workflow with stages, or a decision if one exists. For RBA there is no decision atom, so use workflows/rba-project-delivery-lifecycle.md (real, with typed stages + outcomes, and it pairs naturally with the loop diagram). Set artifact.path to the client-repo-relative path; show the frontmatter + body verbatim, trimmed only — never reworded.
- Transcribe the hero atom into
OkfToken[]. Use the skill-side helper tokenizeOkf(raw): OkfToken[] (split the raw atom into bare-text runs and key/type/comment spans — the only three classes landing.css defines), then curate which spans read well. The tok field may ONLY be 'key' | 'type' | 'comment' | null — there is no other value, so you cannot leak a stray class or hex (#2). Assert the round-trip body.map(t => t.text).join('') === rawAtom before emitting (see Verify).
- Fill the TAILORABLE slots (below) using ONLY facts present in the client's OKF as vocabulary — re-frame Dossier's value props (now carried by the OKF beat + loop, NOT a features array) in the client's words, never invent new ones.
- Carry the UNIVERSAL slots forward unchanged in shape (below) — re-frame, never delete, weaken, or contradict (#3).
- Fall back honestly. Any slot you cannot truthfully fill from an atom → use the Dossier-default copy from
dossier.ts rather than inventing. Keep confidence honest (RBA's atoms are inferred; never narrate them as verified).
TAILORABLE per client (re-frame in the client's vocabulary)
| Field | Source of truth |
|---|
brand.wordmark / brand.homeAriaLabel / brand.metaTitle / brand.metaDescription | brand manifest + client anchor atom |
hero.eyebrow / hero.headline / hero.subhead | re-frame the sovereignty thesis for the client; subhead still says their memory is files in their git |
hero.primaryCtaLabel / hero.secondaryCtaLabel | CTA labels |
okf.artifact (path · note · body · caption) | a REAL atom from the client's repo (step 3–4) — the field lives on okf, NOT hero (relocated into the OKF explainer beat; see DEC-0037) |
okf.lead / okf.payoff / each okf.properties[].detail | re-frame the format for the client; the four property terms and the explainable-retrieval payoff are the standard itself (re-word the gloss, never drop a property) |
loop.stages[].label/sub | re-word the labels; the five-stage shape stays fixed |
loop.returnArcLabel | re-frame; keep the "humans curate · agents extend" meaning |
getStarted.head.* / getStarted.selfServe.* / footer.tagline | the get-started head + the single self-serve door body + footer copy (the agency design-partner door was removed in DEC-0051 — there is NO getStarted.partner) |
UNIVERSAL — preserved in EVERY instance (re-frame, never remove — #3)
NOTE (DEC-0049): there is NO features array anymore — the Features beat was cut. The
universal value props live STRUCTURALLY in the OKF beat (okf.properties + okf.payoff)
and the loop (the five stages + the return arc). Re-frame those; never reintroduce a
features list.
- Sovereignty thesis — the client's institutional memory is plain Markdown + YAML in their own git repo; owned and portable, never locked in. Must survive in
hero.subhead and the OKF properties (Atomic Markdown + YAML, In your own git) + okf.lead.
- Five-stage learning loop —
loop.stages is a fixed 5-tuple: Ingest → Extract → OKF graph → Serve → Curate, with the OKF node at index 2 (okf: true). The SVG geometry and return arc assume exactly five nodes with OKF at index 2 — validate this.
- Provenance / confidence — every atom is typed, sourced, confidence-scored (
verified · asserted · inferred). Lives in the Typed + sourced + scored OKF property and the hero artifact caption.
- GraphRAG explainability — retrieval returns the traversed subgraph + the exact edges. Lives in
okf.payoff (the "atoms and edges it actually traversed, never a black box" line).
- "Humans curate, agents extend" — the
loop.returnArcLabel.
- Direct-adopter door —
getStarted.selfServe carries the "install it as one Claude Code plugin / days-long install" reframe (re-homed from the cut "Run it yourself" feature).
- North star —
northStar.quote and northStar.citationWork ship byte-identical to dossier.ts for every instance.
Rules
- Provenance discipline (hard line). Never fabricate facts about the client. Every client-specific claim must trace to an atom in the client's OKF; if a slot can't be grounded, fall back to the Dossier-default copy. No invented names, metrics, logos, customers, or pricing. RBA's KB has no pricing atom — never write a price.
- Theme is never tailorable (#2). No color, font, radius, or motion. The skill refuses any hex literal in the manifest or output. Brand warmth comes from type + restraint, not tint.
- The hero artifact is real (#4, #5). A real atom, shown verbatim/trimmed, byte-round-tripping. Never a mock atom, never reworded.
- Five stages, OKF at index 2. Re-word labels freely; never add, drop, or reorder a stage, and never move the OKF node.
- No template edits.
LandingPage.svelte, landing.css, HeroLoop.svelte, CaptureForm.svelte, and the schema.ts type module are out of scope — you only write a LandingContent value.
Output
- Canonical: one
LandingContent value export const <id>Landing: LandingContent written to the client's own repo — clients/<id>/landing/landing.config.ts (the system of record; sovereignty, DEC-0001).
- In-package demo only: a copy at
packages/app/src/lib/content/landing/examples/<id>.ts, header-commented DEMO — canonical home is the client's repo; regenerated by generate-landing, and registered in examples/registry.ts (the dev-only preview route reads the registry). The examples/ dir is exclusively for Dossier's own demo renders, never a client's source of truth.
- A summary: which slots are client-tailored vs Dossier-default, plus the hero atom's
path / confidence / source.
Verify (build-proven — never claim it works without these passing)
pnpm --filter @dossier/app check (runs svelte-check) — the instance conforms to schema.ts with no any. (This is @dossier/app's type gate.)
- Round-trip —
body.map(t => t.text).join('') equals the raw hero atom excerpt (the landing-dossier-roundtrip.test.ts pattern, applied to the client's atom).
- Hero atom is real —
artifact.path resolves to a file under okfDir, and the shown body matches that file (trimmed only).
- Universal-strings present — assert a frozen set of required substrings appears in the rendered instance: the sovereignty phrase, the five stage labels in order with OKF at index 2, the provenance phrase (
verified/asserted/inferred), the GraphRAG "traversed subgraph + edges" phrase, "humans curate · agents extend", and the byte-identical northStar.quote/citationWork.
- No hex — grep the output for
#[0-9a-fA-F]{3,8} → must be empty (#2).
- Renders —
pnpm --filter @dossier/app build succeeds and /preview/<id> renders (the SvelteKit src/routes/preview/[client] route; dev only — prerender = false + the demo registry imported only inside the import.meta.env.DEV branch, so a production build 404s /preview/* and never bundles the client copy).
- Theme/universal visual diff — render
/preview/<id> and Dossier's / light + dark; confirm identical theme, layout, and loop choreography, and that every universal message survives the re-frame. Save captures under clients/<id>/site-shots/ (never repo root). Report any delta honestly — do not claim parity without the diff.
Does NOT
Build any server/DB/hosted-runtime code. The hosted control plane is DEC-0032's future build and is untouched (#6).