From 43d3b480d94d27c3bb2ede33d4d47a909ebf0462 Mon Sep 17 00:00:00 2001
From: Oleh Omelchenko
Date: Fri, 26 Jun 2026 23:09:47 +0300
Subject: [PATCH] =?UTF-8?q?Copy:=20pare=20product=20claims=20to=20what=20w?=
=?UTF-8?q?e=20can=20certify,=20cut=20rule-of-three=20cadence,=20record=20?=
=?UTF-8?q?arch=2010=20=C2=A710?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.claude/skills/alignment/SKILL.md | 6 ++-
README.md | 24 ++++++++++--
.../10-interaction-and-feedback.md | 37 +++++++++++++++++++
src/app/components/AboutModal.tsx | 20 +++++-----
src/app/components/Onboarding.tsx | 4 +-
src/app/feedback.ts | 8 ++--
src/landing/Landing.tsx | 28 +++++++-------
7 files changed, 92 insertions(+), 35 deletions(-)
diff --git a/.claude/skills/alignment/SKILL.md b/.claude/skills/alignment/SKILL.md
index bf9a4c8..e51c6d2 100644
--- a/.claude/skills/alignment/SKILL.md
+++ b/.claude/skills/alignment/SKILL.md
@@ -155,7 +155,11 @@ role`) or the rule it demonstrates. - **Positional sub-section cross-refs.** Cit
14. **User-facing copy**: keep user-visible strings centralized and written for users (sentence
case, active voice, no "please", no exclamation marks in errors). If/when an i18n layer
- exists, route strings through it instead of hardcoding.
+ exists, route strings through it instead of hardcoding. **Product claims** (landing,
+ About, onboarding, value props) follow `arch 10 §10`: claim only what we can certify, no
+ absolutes (never/always/fully/everything), no durability the platform doesn't back ("saved",
+ not "permanent"), and state a posture once per surface — reduce uncertain promises, keep the
+ real ones.
15. **Chart-builder guidance reasons over role, not raw type** (`src/core/chart-builder.ts`):
a `builderWarnings` rule (or any measure/dimension decision) must ask the post-transform
diff --git a/README.md b/README.md
index b168a69..8e19b8a 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
A browser-based **snippet manager for [Vega-Lite](https://vega.github.io/vega-lite/)
visualizations**. Author chart specs as JSON, watch them render live, and keep a personal,
-searchable library — fully local, offline-capable, no account.
+searchable library — local, offline-capable, no account.
> Astrolabe is a **spec-driven rebuild** on an architecture adapted from its sibling
> project Syto. The authoritative behavioral contract lives in [`docs/spec/`](docs/spec/).
@@ -23,10 +23,28 @@ npm run typecheck
npm test # Vitest
```
+## What it does
+
+- **Editor + live preview** — write Vega-Lite specs as JSON in Monaco with schema-aware
+ validation and autocomplete; the chart re-renders as you type.
+- **Snippet library** — save, search, tag, and organize specs. Each snippet carries a stable
+ published version plus a separate editable draft, so you can tinker without losing a
+ known-good copy.
+- **Reusable datasets** — store data once (inline, or fetched once from a URL and snapshotted)
+ and reference it by name from many snippets.
+- **Chart Builder** — a no-JSON on-ramp that generates a spec from field, mark, and encoding
+ choices. The JSON stays the source of truth and is always editable.
+- **Theming & fonts** — custom chart themes with a visual Theme Builder, a curated font roster,
+ and your own uploaded font faces.
+- **Local-first** — your library lives in the browser (IndexedDB); offline-capable and installable
+ (PWA), with import/export for backup and transfer. No account, no server.
+
## Status
-**M0 — Skeleton.** Toolchain green (typecheck, tests, build, PWA). The three-pane shell
-renders; features land milestone by milestone per the implementation plan (MVP at end of M1).
+Active development, **pre-1.0** and not yet publicly released — well past the initial milestones
+and usable day to day; the first public release will be `1.0.0`. Behavior is specified in
+[`docs/spec/`](docs/spec/) and the architecture patterns in
+[`docs/architecture/`](docs/architecture/00-overview.md).
## License
diff --git a/docs/architecture/10-interaction-and-feedback.md b/docs/architecture/10-interaction-and-feedback.md
index bca4876..dc162e6 100644
--- a/docs/architecture/10-interaction-and-feedback.md
+++ b/docs/architecture/10-interaction-and-feedback.md
@@ -683,6 +683,39 @@ choice isn't re-litigated per surface:
_(Consulted via /council → WAI-ARIA APG tabs/accordion/disclosure, IBM Carbon
accordion usage, NN/g #6/#8.)_
+## 10. Product claims & promise copy
+
+Declarative copy — the landing, the About modal, onboarding, empty-state value props —
+makes **claims** about the product, not just feedback about an action. The care the §3
+triad gives error wording applies here too: **say only what we can certify, and say it
+once.** An overstated claim reads as insecurity, and the first time a user catches one
+being false it costs more trust than the claim ever bought (NN/g credibility; GOV.UK
+"don't oversell"). The test is not modesty for its own sake — it is that every sentence
+survives a skeptical reading.
+
+- **Claim what we can certify — not the future, not what we don't control.** "No account,
+ no server" is structural and always true (there is no backend). "Never leave your machine"
+ is a vow over every future build and every edge case; state the posture instead — "stored
+ locally on your device; there's no server to send them to."
+- **No absolutes.** _never · always · fully · entirely · everything._ One edge case or one
+ future feature falsifies them, and the reader feels the overreach even when it happens to
+ hold. Prefer the scoped form: "works offline" over "fully offline"; "your library lives in
+ the browser" over "everything lives in the browser."
+- **Don't promise durability the platform doesn't back.** Browser storage (IndexedDB, no
+ `persist()`) is best-effort and the browser may evict it. A chart is **saved**, not kept
+ forever — route the permanence claim through **export**, which is the real backup.
+- **State a posture once per surface.** Repeating "local / no account / no server / offline"
+ across the hero, the lede, and a feature grid is three chances to sound unsure of it. Give
+ the posture one home and let the other surfaces describe the product.
+- **Match the register, and don't under-sell.** Astrolabe is a free, spare-time tool: the
+ voice is plain and matter-of-fact, not manifesto. But concrete, true capabilities —
+ portable Vega-Lite JSON, two authoring modes, custom themes — are claims worth making
+ plainly. Reducing promises means cutting the _uncertain_ ones, never the real ones.
+
+SOUL.md §"Local-Only by Default" is the internal **intent** and may be absolute; this
+section governs how that intent is **phrased to users**, where the promise should be only as
+strong as we can keep.
+
---
## Do / Don't
@@ -694,6 +727,8 @@ accordion usage, NN/g #6/#8.)_
- Adopt the APG keyboard pattern for new widgets; route all global keys through the one
router.
- Mark **optional** fields, not required ones (GOV.UK) — e.g. "Comment (optional)".
+- In product claims, say only what we can certify, once per surface; route durability
+ through export.
- Consult `/council` when this contract is silent — then record the answer back here.
**Don't**
@@ -709,3 +744,5 @@ accordion usage, NN/g #6/#8.)_
- Don't ship a **dead disabled control** as a placeholder for an unbuilt feature — a
disabled button explains nothing and is skipped by assistive tech (GOV.UK, NN/g). Omit the
action until it works, then show it enabled (e.g. "Build Chart" appears with M4).
+- Don't use absolutes in product claims (never/always/fully/everything) or promise what the
+ platform can't keep — say "saved," not "permanent."
diff --git a/src/app/components/AboutModal.tsx b/src/app/components/AboutModal.tsx
index e6bbd3e..8b9ca42 100644
--- a/src/app/components/AboutModal.tsx
+++ b/src/app/components/AboutModal.tsx
@@ -47,9 +47,8 @@ export function AboutModal() {
v{__APP_VERSION__}
- A local-first workspace for authoring, organizing, and previewing Vega-Lite charts. Edit
- JSON, see the chart update live, and keep a personal library of snippets — with no
- account, no server, and full offline support.
+ A local-first workspace for authoring and organizing Vega-Lite charts. Edit the JSON,
+ watch it render live, and keep a personal library of snippets.
@@ -74,17 +73,18 @@ export function AboutModal() {
Privacy
- Astrolabe runs entirely in your browser. Your snippets, datasets, and settings are stored
- locally and never leave your machine.
+ Astrolabe runs in your browser. Your snippets, datasets, and settings are stored locally
+ on your device — there’s no server to send them to.
- - No account, no sign-in, no server-side storage.
- - The app runs no analytics or tracking — no cookies, no telemetry, no profiling.
+ - No account or sign-in.
-
- The only outbound network requests are ones you create: URL-sourced datasets you add
- yourself.
+ The app itself runs no analytics or tracking. Its host (Cloudflare) records standard,
+ aggregate traffic like any web server — not your library or the charts you build, which
+ stay in your browser.
- - After the first load, the app works fully offline.
+ - The only outbound requests are ones you make: datasets you load from a URL.
+ - After the first load, the app works offline.
-
Use the header’s import / export buttons to move your library between devices.
diff --git a/src/app/components/Onboarding.tsx b/src/app/components/Onboarding.tsx
index fae3033..8fe4cfa 100644
--- a/src/app/components/Onboarding.tsx
+++ b/src/app/components/Onboarding.tsx
@@ -137,8 +137,8 @@ export function Onboarding() {
Welcome to Astrolabe
- A local library for your Vega-Lite charts — authored as JSON, rendered live, and kept on
- your device.
+ A local library for your Vega-Lite charts — write the spec as JSON, watch it render live,
+ and keep it all on your device.
diff --git a/src/app/feedback.ts b/src/app/feedback.ts
index 6502469..355cc81 100644
--- a/src/app/feedback.ts
+++ b/src/app/feedback.ts
@@ -1,10 +1,10 @@
/**
* Project feedback channel.
*
- * There is no server and no tracker (see the About modal — no telemetry of any
- * kind); feedback is a plain email the user composes and sends from their own
- * client. The address is a Cloudflare Email Routing alias that forwards to the
- * author, so it can be retired without exposing or churning a personal inbox.
+ * The app sends no telemetry of its own (see the About modal); feedback is a
+ * plain email the user composes and sends from their own client. The address is
+ * a Cloudflare Email Routing alias that forwards to the author, so it can be
+ * retired without exposing or churning a personal inbox.
*
* Shared by the Support modal and the About modal — a contact address is worth
* a single source of truth so the two surfaces can't drift.
diff --git a/src/landing/Landing.tsx b/src/landing/Landing.tsx
index d9d3b43..8d448b2 100644
--- a/src/landing/Landing.tsx
+++ b/src/landing/Landing.tsx
@@ -369,8 +369,7 @@ export function Landing(): ReactNode {
Astrolabe is a local studio for Vega-Lite. Write a spec by hand or build one by
- clicking, give it a theme, and keep all your charts in one library — in the browser,
- with no account and no server.
+ clicking, give it a theme, and keep all your charts in one searchable library.
@@ -399,8 +398,8 @@ export function Landing(): ReactNode {
The editor is Monaco with the Vega-Lite schema loaded, so you get validation,
- autocompletion, and inline docs without going online. Each chart keeps a draft and a
- published version: edit the draft, publish when it's ready, revert when it isn't.
+ autocompletion, and inline docs without going online. Each chart keeps an editable
+ draft alongside a published version you can revert to.
If you'd rather not start from JSON, the builder works from the kind of chart you
@@ -426,7 +425,7 @@ export function Landing(): ReactNode {
One library
- Every chart you make stays, with the data behind it.
+ Every chart you make is saved, with the data behind it.
Store a dataset once and reference it by name from as many charts as you want. Rename
@@ -434,8 +433,8 @@ export function Landing(): ReactNode {
Load data by pasting CSV or JSON, or by fetching a URL. You can also lift inline data
- out of a spec into a shared dataset. Search, sort, and duplicate as the collection
- grows.
+ out of a spec into a shared dataset. Search and sort as the collection grows, and
+ duplicate a snippet to start a variant.
@@ -599,17 +598,16 @@ export function Landing(): ReactNode {
Your charts are
yours.
- Astrolabe runs in your browser and stores everything locally — no account, no server.
- What it produces is ordinary Vega-Lite JSON, so you can read it, edit it in other tools,
- or keep it long after you've stopped using Astrolabe.
+ Astrolabe runs in your browser, and what it makes is ordinary Vega-Lite JSON — read it,
+ edit it in other tools, or take it elsewhere whenever you want.
private
-
Stays on your machine
+
Local to your browser
- Charts, data, and themes are saved in your browser. Install it and it works with no
- connection.
+ Charts, data, and themes are saved in this browser, and it keeps working offline
+ once installed.
@@ -624,8 +622,8 @@ export function Landing(): ReactNode {
yours
Set up your way
- Two ways to author, your own fonts and themes, your own library. Arrange it to match
- how you work.
+ Author by hand or by clicking, with your own fonts and themes. Arrange the library
+ to match how you work.