Repository navigation
feat(web): rebuild the landing page as a marketing page, split from /login - #679
Conversation
…login `/` redirected every anonymous visitor to `/login`, so the front door was an auth route titled "Sign in": a form before an explanation, with the product's strongest verifiable claims — one command, works with no account, MIT, self-hostable — below the fold of a page nobody had a reason to scroll. `/` is now the marketing page and `/login` does only the job its URL names. The hero's primary action is the install command rather than sign-in, because the CLI's offline mode needs no account and an auth-first ordering described a requirement that does not exist. Three overstated claims are corrected: "never asks the same question twice" (an absolute the retrieval model cannot guarantee), a bare "Free" badge (now "Free · 5,000 memories", the actual free-plan cap), and a flat Claude Code / Cursor / Codex list (Codex is feature-flagged and experimental). Two new sections state what the page was missing: where lore physically lives, and what LoreKit does not do.
|
The dashboard preview for this PR — redeployed on each push that changes the web app.
Preview always points at this PR's latest commit; Deployment is the immutable build of |
|
/review |
…stop scoring grep collisions as blast radius Both found by the first fanned-out review run against mthines/lorekit#679. The workspace was materialized under os.tmpdir(). The host's file tools are scoped to the agent workspace, so `/tmp/prr-clone-*` was not merely awkward — the standards finder's sub-agent had `read`, `glob` and `cat` all REFUSED against it, returned zero findings, and had to disclose that its empty result meant "could not open the governing docs" rather than "the diff conforms". A checkout now lands under $PR_REVIEWER_SCRATCH / /tmp/workspace, with os.tmpdir() kept as the last rung for a host that has no workspace. The impact graph resolved consumers by word-boundary grep alone. On #679 that gave `metadata` — the Next.js route-segment export of app/page.tsx, which nothing can import — 87 consuming files across 8 packages, and `wait`, a non-exported helper the diff does not even change, another 27. Fourteen of fourteen sampled hits were collisions: an audit record's `metadata:` property, the English word in a comment, an unrelated `wait` method. blast_radius scored 260 / band high on that, which routes Phase C to `deep` — the most expensive tier — on pure noise. gateConsumers() now intersects the grep hits with the import edges importersOf() already resolves, for ES modules only (a Go or Python same-package reference needs no import and is left alone), and records `consumer_gate` on any symbol where it bit so an empty list is never misread as "nothing depends on this".
…te deletes real edges A monorepo declares `"@/*": ["./src/*"]` in packages/<name>/tsconfig.json, not at the root, and a paths target is relative to the config that declares it. aliasMap read the root only, so every aliased import stayed unresolved — which was survivable while importers were merely informational and is not now that gateConsumers intersects consumers against them: an unresolvable alias reads as "no file imports this module" and deletes real consumers. Measured on mthines/lorekit#679, same commit, three runs: before the gate band high, score 260 — 87 fake consumers on `metadata` gate, root alias only band low, score 2 — real importers deleted too gate + this fix band medium, score 17 — CopyCommand back to its 3 real consumers (LandingHero, GetStartedCard, BlogPostCta), every landing component back to page.tsx, and 192 `metadata` + 60 `wait` collisions still dropped
|
/review |
|
⚡ Running Agent Reviewer | Review… |
🟡 4 findingsSplits the marketing landing page from the login-only auth page, sharing header/backdrop between them and adding an install-command-first hero. Warnings: description omits AuthHashCatcher duplication; 4 non-blocking finding(s) 1 advisory finding below the confidence bar — see Less certain below.
Less certain (1) — advisory, below the confidence bar
Review detailsNeeds attention
Impact — 4 changed exports · 8 consumers checked
Found Quality — produced 7 → posted inline 4 (1 consolidated at dedupe, 1 contradicted and dropped, 1 deferred below confidence bar) Run full · 904 lines in delta · tier deep · depth checkout · blast_radius=medium, semver_delta=none, high_stakes=0, risky_shapes=none Nothing to report — optimality, measurability (1 paths classified), integrations (not activated), 0 files skipped.
|
…dle, prepared context (#201) * feat(pr-reviewer): Agent0 fast path — workspace install, compiled bundle, prepared context The review pipeline runs on three harnesses. In a Dash0 Agent0 Automation sandbox, four host facts make the CLI's assumptions false, and all four are paid before Step 0 runs: 1. the native file reader is scoped to the workspace, so an install under $HOME/.claude degrades every read to a sed slice against a ~50 KB cap — ~16 sequential calls to ingest a 277 KB agent definition; 2. the host's skill tool resolves a fixed enum of its own built-ins and never the filesystem, so no install ordering can make a repo-owned lens visible to it (20/29 runs in one week hit this; the mandated retry recovered 0/26 across two weeks); 3. custom agent types are not dispatchable and delegation is one level deep; 4. the GitHub credential is repo-scoped, so `gh api /user` 401s and Step 0.5's identity read can never succeed. Three new pieces, none of which changes a phase, finding, gate or verdict: - scripts/agent0-setup.sh — the source of truth for an automation's sandbox.setupScript. Installs the agent tree, the six lenses as files, and the compiled bundle INSIDE the workspace, then writes RUN-CONSTRAINTS.md and exports the resolved paths. Runs once and is cached between runs, replacing an in-session install re-executed on all 34 runs of a measured week. Fails closed. - scripts/build-agent0-bundle.mjs — compiles pr-reviewer.md plus the mandatory core rules into one file, so no run pays a per-phase read: 16 bash slices become 3 native reads. A concatenation with a preamble, never a paraphrase — a compiler that rewrote the pipeline would be a second pipeline to keep in sync. Fails closed on a missing core rule. - scripts/prepare-review.mjs — executes the deterministic half (Steps 0, 0.5, 0.7's GitHub fallback rung, 0.8, 1.1, 1.1b, 1.2, shape classification, Phase B) in one process, ~6 s measured, and emits a ~12 KB index plus sidecars. Closes the torn-state window by construction: head, base, diff, patch list and impact graph are bound from one metadata read. It does no LoreKit read, makes no judgment and writes nothing to GitHub, and the artifact says so. A degraded rung is never an exit code — it is an entry in anomalies[], because a review that could not materialize a checkout is a narrower review, not a failed one. rules/agent0-runtime.md documents the harness and is referenced from the agent's Imports; it is inert on every other harness. Both scripts carry --self-test (9 and 19 cases). * fix(pr-reviewer): fold the three spellings of one GitHub App login before deciding REVIEW_RELATION `gh pr view --json author` reports an App author as `app/dash0-dev`, the REST comment and review payloads report the same actor as `dash0-dev[bot]`, and a human configuring the automation types `dash0-dev`. A raw comparison therefore reads the agent's own PR as `cross` — which is not cosmetic, since that flag decides whether findings carry context-asymmetry hedging. * fix(pr-reviewer): three bugs the first live run against lorekit#679 found - prepare-review.mjs creates the sidecar directory. `--out` names a file in a directory the caller has not necessarily made, and the first write into it is a sidecar, so an absent dir surfaced as `ENOENT … pr-files.json` — which reads as a missing input rather than an absent output dir, and aborted the whole prepare step. - agent0-setup.sh no longer dies on an unset $DASH0_AGENT_ENV. That variable is not set in every sandbox, and under `set -u` the expansion exits bash at a line sitting BEFORE the verification block — an install that printed a bundle summary and then stopped one step short of the check that exists to catch it. The resolved paths are now written to $PR_REVIEWER_ROOT/env.sh unconditionally and appended to the env channel only when one is present. - impactSummary is derived from the keys build-impact-graph.mjs actually emits. It read `impact.changed_exports`, which does not exist; the graph's own name is `symbols[]`. Every run reported `0 changed exports` — on lorekit#679, a diff whose graph holds 23 symbols (13 exported) and scores band: high. Adds changedSymbols, blastScore and the band's own `why` lines, so a `high` earned by a common identifier is distinguishable from one earned by a real export. * fix(pr-reviewer): put the checkout where sub-agents can read it, and stop scoring grep collisions as blast radius Both found by the first fanned-out review run against mthines/lorekit#679. The workspace was materialized under os.tmpdir(). The host's file tools are scoped to the agent workspace, so `/tmp/prr-clone-*` was not merely awkward — the standards finder's sub-agent had `read`, `glob` and `cat` all REFUSED against it, returned zero findings, and had to disclose that its empty result meant "could not open the governing docs" rather than "the diff conforms". A checkout now lands under $PR_REVIEWER_SCRATCH / /tmp/workspace, with os.tmpdir() kept as the last rung for a host that has no workspace. The impact graph resolved consumers by word-boundary grep alone. On #679 that gave `metadata` — the Next.js route-segment export of app/page.tsx, which nothing can import — 87 consuming files across 8 packages, and `wait`, a non-exported helper the diff does not even change, another 27. Fourteen of fourteen sampled hits were collisions: an audit record's `metadata:` property, the English word in a comment, an unrelated `wait` method. blast_radius scored 260 / band high on that, which routes Phase C to `deep` — the most expensive tier — on pure noise. gateConsumers() now intersects the grep hits with the import edges importersOf() already resolves, for ES modules only (a Go or Python same-package reference needs no import and is left alone), and records `consumer_gate` on any symbol where it bit so an empty list is never misread as "nothing depends on this". * fix(pr-reviewer): read per-package tsconfig paths, or the consumer gate deletes real edges A monorepo declares `"@/*": ["./src/*"]` in packages/<name>/tsconfig.json, not at the root, and a paths target is relative to the config that declares it. aliasMap read the root only, so every aliased import stayed unresolved — which was survivable while importers were merely informational and is not now that gateConsumers intersects consumers against them: an unresolvable alias reads as "no file imports this module" and deletes real consumers. Measured on mthines/lorekit#679, same commit, three runs: before the gate band high, score 260 — 87 fake consumers on `metadata` gate, root alias only band low, score 2 — real importers deleted too gate + this fix band medium, score 17 — CopyCommand back to its 3 real consumers (LandingHero, GetStartedCard, BlogPostCta), every landing component back to page.tsx, and 192 `metadata` + 60 `wait` collisions still dropped --------- Co-authored-by: mads.thines <[email protected]>
# Conflicts: # packages/web/src/app/(auth)/login/page.tsx
…nto, honest next steps
Raises the PR's honest copy to a marketing page that sells:
- Hero leads with the problem and resolves it in one fixation
("Your coding agents forget. LoreKit remembers."), the README's own framing.
The install command gets the amber frame and the page's only filled button;
trust points move under it, to the moment of the click.
- The live demo becomes the hero's product shot, directly below it, and tilts
flat as it scrolls in (CSS view() timeline, transform-only, flat at rest).
- New "01 / the problem" before/after, and a "03 / what you get" bento that
absorbs MemoryCost and adds review-comment learning, one store for every
tool, and grooming — each with a small picture of the behaviour.
- One heading system (mono numbered eyebrow, 30-36px title) and two repeated
signature moves (lk-glow-frame, lk-gradient-text) across every section.
- Sticky blurred header; the Blog link from #685 now lives in the shared
LandingHeader (the merge kept the slim /login).
Copy corrected against the CLI: install with a blank token still writes the
hosted MCP entry, so "works offline with no account" directly under the
command promised a setup one command does not produce. The hero and the
closing card now name both real paths — hosted (sign in, paste a token) and
offline (skip the token, switch to local mode) — at equal weight. Webhook
lessons are described as candidates that expire, matching webhook.ts.
Review findings applied: AuthHashCatcher comment no longer claims a move;
LandingBackdrop uses tokens, not hex; StepBadge replaces the duplicated
class; one GitHubMark replaces four inline copies of the SVG path.
Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
…flow
From review of the preview:
- Before/after: the speaker column was 44px, so "agent ›" wrapped its chevron
onto a second line on every agent row, and the long memory.write row wrapped
too — the two transcripts drifted out of day-for-day alignment. Columns are
now sized in ch (nowrap), the rows are short enough to stay on one line, and
the cards sit side by side only from lg.
- How it works: the README ASCII box rendered with its top-right corner one
column short of the right edge, left-aligned in a frame twice its width, and
the step cards' connecting rule showed only as stubs in the gaps. Replaced
both with one flow — session A → memory.write → store → memory.list →
session B — where each step is a node and the calls label the arrows
(vertical below lg). Uses the same fix/ssr-window lesson as the live demo.
- Two-sentence headings put the gradient payoff on its own line instead of
breaking mid-sentence ("…starts from / zero. Yours…").
Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
… CTA Reuses the blog's reading model rather than inventing a second one: ReadingTelemetry now mounts on / with contentType 'landing' (slug 'home'), measured against the hero-to-closing-card wrapper. The home page therefore emits the same content.scroll_depth / content.section_read / content.read events as a blog post, split by lorekit.content.type. - sections.ts is the single source of the eight section heading ids; every section imports its id from it and the page hands the ordered list to the telemetry, so a renamed heading cannot silently drop out of the data (sections.spec.ts pins the list). - landing.cta_clicked (cta x placement, both closed unions) via one LandingLink for every link on the page: header, hero GitHub pill, the hero's hosted/offline choice, the closing card. get-token vs offline-guide from the hero is the direct read on which path visitors want. - landing.demo_tab_selected: which demo use case a visitor picks (the auto-playing first tab is not counted). - GetStartedButton (only caller was the closing card) folds into it as a tracked link. - docs/otel.md: new landing.* table; the Content section no longer claims /docs is tracked — it never mounted ReadingTelemetry. Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
…AUDE.md
The "01" example ("Zod, not Yup" written once, read back) argued for a
rules-file line, not for LoreKit. Replaced it with the thing a rules file
cannot do:
- "01 / why not just a CLAUDE.md?" — Your CLAUDE.md is frozen. Your lore
keeps learning. A month in the life of one lesson, in four beats, each a
shipped mechanic: LEARNS (agent writes it on friction, unprompted),
RECALLS (a teammate's failing call gets it matched by the error text),
PROMOTES (seen 3x -> permanent rule), PRUNES (unopened 90 days ->
archived, restorable). Tries go 4 -> 1 -> 0, the loop closes back to the
start, then a CLAUDE.md vs LoreKit table states it plainly — and positions
LoreKit as how you find out what earns a place in your CLAUDE.md.
- Hero: "LoreKit remembers." -> "LoreKit makes them learn."; the lede now
names the loop (write on friction, recall on the error, promote, archive).
- Live demo: the static "Code conventions" tab becomes "Promotion" — a CI
flake seen for the third time gets promoted to a rule; next session is
green first time. Tab telemetry id code-conventions -> promotion.
- Scroll-staggered card rise (lk-step) and a rail drawn as the section
enters (lk-draw); transform-only, finished at rest.
- Section id the-problem -> learning-loop (sections.ts, docs/otel.md).
Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
…up + team From the preview review: - 01 visual bugs: the rail through the stage icons ran between each stage's day label and verb (labels sat beside the icon on the rail's line) — labels now sit under the icon. Transcripts get a fixed four-line height so outcomes line up across cards (no more bottom-pinned text with gaps). The "and round again" label no longer masks the dashed line with an opaque box that didn't match the glow; it sits under the line. - 02 retold 01's loop (write -> store -> read). Now it answers how the day-4 teammate got the lesson: connect each agent (install's real status lines; Cursor is three copied files), lessons stay with their repo (narrow -> broad scope ladder in the scope colours), one shared set for the team (bind repo::acme/* to the org; members read+write one copy, CI reads with an lk_ro_ token, unbound stays personal). "Wire it once. The whole team learns." No arrow labels to cramp. - Bento: "One store for every tool" duplicated the new team step; replaced with "Every lesson in plain sight" (Lore Explorer + greppable markdown). Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
|
Darkplane did not approve this pull request. It scored 54.12 of 100, and 75 was required.
An AI evaluator produced these scores from the diff, the repository history, and the pull request text. The pull request text is author-controlled, so treat the scores as signals rather than proof. Reviewed for commit 356c095. |
Summary
/redirected every anonymous visitor to/login, so the front door was an auth route titled Sign in. This PR makes/a marketing page and leaves/logindoing only the job its URL names. The second pass turns that page from accurate into persuasive: it leads with the problem, shows the product in the first screen, and makes the install command the one obvious action. Copy is still held to what the product does today.Baseline it has to beat (Dash0 RUM,
web, 2026-09-11 → 2026-10-11, production/login): 134 landing sessions, 30 (22%) started sign-in, 11 (8%) opened docs, 0 recordedinstall_command.copied(the command sat at the bottom of/login). No copy event has ever been recorded, so a broken event is not fully ruled out; the first copies after deploy will confirm it.Changes
Structure —
/renders the landing page;/loginkeeps only the auth form plus a "See what LoreKit does" link. Signed-in redirect to/loreand the query-string auth-callback rescue are unchanged.AuthHashCatcheris mounted on/as well as/login(comment corrected: it was not moved).mainfrom #685 is merged; its Blog link now lives in the sharedLandingHeader.Reading order — hero → live demo (the hero's product shot, peeking above the fold) →
01why not just a CLAUDE.md? (one lesson's month — learns → recalls → promotes → prunes — then a CLAUDE.md vs LoreKit table) →02how it works (setup and team sharing, not a second telling of the loop: connect each agent → lessons stay with their repo via scopes → bind a scope to the org for one shared copy; CI reads with a read-only token) →03what you get (bento: memory cost, review-comment learning, every lesson in plain sight, grooming) →04where your lore lives →05what LoreKit doesn't do → closing call to action.Persuasion, by barrier
Your coding agents forget. LoreKit makes them learn.A rules file can hold a convention someone already knew; it can't learn what nobody knew, hand it back when the same error recurs, promote what keeps recurring, or retire what nobody uses. The hero lede, section01and the demo'sPromotiontab all tell that loop, and section01answers the "why not just CLAUDE.md?" objection by name — positioning LoreKit as how a team finds out what earns a place in its CLAUDE.md. Each beat is a shipped mechanic (friction-gated write nudge, error-matched recall on a failing tool call, recurrence-ranked injection +lorekit-setuppromotion, retention-policy archive); the scenario's numbers are labelled as a story.Copy corrected against the CLI —
installwith a blank token still writes the hosted MCP entry (packages/cli/src/commands/install.mjs); offline needs the local MCP entry plus"mode": "local"(/docs/offlinesteps 3 and 6). So "Works offline with no account" directly under the command, and the old step 2 "your agent starts remembering", promised a setup one command does not produce. The hero and closing card now name both real paths at equal weight: Hosted (sign in, paste a token) and Offline (skip the token, switch to local mode). The webhook cell says review comments become candidate lessons that expire (supabase/functions/mcp/webhook.ts), not "durable lessons automatically". The demo caption no longer lists Codex as supported.Visual system — one
SectionHeading(mono numbered eyebrow, 30–36px title, 16px lede) and exactly two repeated signature moves:lk-glow-frame(amber hairline + glow, reserved for the product and its actions) andlk-gradient-text(the payoff phrase per heading). Sticky blurred header (mainnow usesoverflow-x-clipso sticky works). New token--color-accent-bright.MemoryCostis folded into the bento's widest cell.Motion — CSS only, transform/opacity only, resting state = finished state:
lk-rise(hero stagger),lk-reveal(sections,view()timeline), newlk-tilt(the demo window lies back and lifts flat bycover 35%). Safari/Firefox and reduced-motion visitors get the static page.Review findings applied — AuthHashCatcher comment;
LandingBackdropuses tokens instead of hex;StepBadgereplaces the duplicated class; oneGitHubMarkreplaces four inline copies of the SVG path (header, hero, footer,LoginButton). Not done: aCopyCommandsize test (see Verification).Telemetry — the page answers "how far do they scroll, what do they see, what do they click" with the blog's own model, not a second one:
ReadingTelemetry(the blog's component) mounts on/withcontent.type=landing,slug=home, measured hero → closing card. Samecontent.scroll_depth(25/50/75/100),content.section_read(per-section engaged dwell) andcontent.read(one summary per view) as a blog post, so one dashboard splits the two bylorekit.content.type. Section ids live incomponents/landing/sections.tsand every heading imports its id from there, so a renamed heading can't silently drop out of the data (sections.spec.ts).landing.cta_clickedwith closed-unioncta(get-token/offline-guide/sign-in/docs/blog/github) ×placement(header/hero/get-started), through oneLandingLinkfor every link on the page.get-tokenvsoffline-guidefrom the hero is the direct read on which path visitors want.landing.demo_tab_selected— which use case a visitor picks (the auto-playing tab isn't counted).install_command.copiedwithlanding-hero/landing-get-started, kept apart so "did the command alone convert" and "did the whole page convert" stay separable.docs/otel.mddocuments the new events; its Content section no longer claims/docsis tracked (it never mounted the component).Verification
tsc --noEmitover the changed files and their imports (55 source files) — clean. The fullnx typecheck webexceeds this sandbox's memory; CI'sTypecheck, Test & Lint (affected)is the authoritative run.SiteFooteron untouched lines).track(incl. the newlanding.*andcontent.type=landingwire shapes),lore-events,reading,sdk-event-callers,analytics-id-literals,landing/sections— 84/84 pass.globals.csscompiled through@tailwindcss/postcss— new utilities and signature classes generate./polish→review-looppipeline — this was authored by Agent0 outside Claude Code, so treat it as a draft for review.Context
/docs/offlinealready does. Separately,README.mdstill calls webhook lessons "durable"; that is a follow-up.installwith a blank token offered to set up local mode, the no-account promise would be true in one command, and the hero could say so.