docs: full docs-update pass — 9 GSD docs regenerated + verified (434→454 claims; verifier corrections applied)
This commit is contained in:
+248
@@ -0,0 +1,248 @@
|
||||
<!-- generated-by: gsd-doc-writer -->
|
||||
# Contributing to arah²
|
||||
|
||||
arah² (the `araharah` repository) is a privacy-respecting, Telegram-first GPS navigator
|
||||
for the Kuala Lumpur metro — a grammY bot + Cloudflare Worker, a Vite/MapLibre Mini App,
|
||||
a design-token package, a curated KL gazetteer, and the routing engine behind the
|
||||
turn-by-turn guidance. Code is **GPL-3.0**; the map-derived data files are **ODbL-1.0**.
|
||||
|
||||
This file tells you what belongs in the project, how to set up, and — most importantly —
|
||||
which gates a change must pass before a maintainer will look at it. The repo has no CI,
|
||||
so *you* are the CI.
|
||||
|
||||
---
|
||||
|
||||
## The charter — what belongs here, and what never will
|
||||
|
||||
arah²'s core value: **a user finds a local destination via Telegram and gets there with
|
||||
reliable turn-by-turn guidance — tracked by nothing beyond what they explicitly opted in
|
||||
to.** The project charter (`.planning/PROJECT.md`, tracked in this repo) records the
|
||||
scope boundaries and, crucially, *why* each exclusion was made, so they don't get
|
||||
re-litigated every milestone.
|
||||
|
||||
**In scope (v1):** the Telegram bot command surface, destination search over the curated
|
||||
KL gazetteer, the Mini App webview, route display, live turn-by-turn guidance with
|
||||
hybrid chat turn-cards, KL-metro tuning, and the Telemetry Rams design system.
|
||||
|
||||
**Out of scope — the deferred-ideas law** (from `.planning/PROJECT.md` "Out of Scope"):
|
||||
|
||||
- **Incident reports** (`/accident`, `/traffic`, `/hazard` + broadcasts) — navigation
|
||||
first; reports ship after the core works. v1 reports *nothing*.
|
||||
- **Gamification, points, leaderboards** — depends on reports; deferred with them.
|
||||
- **Regional clusters via Telegram groups** — unvalidated social hypothesis.
|
||||
- **Voice-to-text reporting, offline mode, anonymized public API** — future ideas.
|
||||
- **Push notifications beyond Telegram chat** (Resend email etc.) — Telegram itself is
|
||||
the channel.
|
||||
- **Independent mobile apps** — the Mini App webview flow *is* the product.
|
||||
- **Any paid-tier infrastructure.** The $0 law is a product constraint, not an
|
||||
aspiration — see [docs/BUDGETS.md](docs/BUDGETS.md). IDEAS.md's RabbitMQ,
|
||||
Prometheus/Grafana, Neon, and Resend entries are aspirational; do not wire them in.
|
||||
|
||||
If your proposal touches any of these, the bar is high: it needs a new charter decision,
|
||||
not just a PR. Architecture seams for the deferred work exist deliberately (e.g. the
|
||||
`SearchSource` interface), but the seams stay unused.
|
||||
|
||||
---
|
||||
|
||||
## Development setup
|
||||
|
||||
Read [docs/GETTING-STARTED.md](docs/GETTING-STARTED.md) for prerequisites and first-run
|
||||
instructions, and [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for the full local
|
||||
development setup. The short version:
|
||||
|
||||
1. Clone and install — npm workspaces install all five packages at once:
|
||||
|
||||
```bash
|
||||
git clone http://gitmirah:3000/c-fu/arah2.git
|
||||
cd arah2
|
||||
npm install
|
||||
```
|
||||
|
||||
<!-- VERIFY: the clone URL is the maintainer's internal Gitea origin (per `git remote -v` and README.md), not a public URL — forks/substitutes should clone their own fork's URL. -->
|
||||
|
||||
2. Copy `bot/.dev.vars.example` to `bot/.dev.vars` and fill in `BOT_TOKEN`,
|
||||
`WEBHOOK_SECRET`, and `WEBHOOK_URL` (gitignored; never commit it).
|
||||
3. `npm run build` — theme tokens first, then the web bundle (the theme build is an
|
||||
input to the web build).
|
||||
4. `npm run dev` — Worker on <http://localhost:8787>.
|
||||
|
||||
There is no linter, formatter, or commit-hook setup to install — see
|
||||
[Code standards](#code-standards) below.
|
||||
|
||||
---
|
||||
|
||||
## Code standards
|
||||
|
||||
- **No ESLint, Prettier, Biome, or `.editorconfig` exists.** There is no `lint` or
|
||||
`format` script in any workspace and no CI to enforce one. Style is a review-time
|
||||
convention; the mechanically enforced gates are listed in the next section. Match the
|
||||
existing source: explicit `.ts` extensions on relative imports, `node:`-prefixed
|
||||
builtins, `.mjs` for scripts/tests, heavy header comments stating what a file is and
|
||||
which decision IDs it satisfies.
|
||||
- **TypeScript strictness is real:** both `bot/tsconfig.json` and `web/tsconfig.json`
|
||||
use `strict` + `noUncheckedIndexedAccess` + `verbatimModuleSyntax`; `search/` and
|
||||
`routing/` follow the same shape. `tsc --noEmit` (`npm run *:typecheck`) is the gate.
|
||||
- **Design tokens:** never hardcode a color, spacing, font, or radius that belongs to a
|
||||
token. `theme/src/tokens.css` is the single source of truth; `npm run audit` flags
|
||||
off-token values in rendered CSS. Never edit `stitch_minimalist_route_navigator/` —
|
||||
the mockups are read-only reference.
|
||||
- **All user-facing copy lives in the i18n strings files** (`bot/src/i18n/strings.ts`,
|
||||
`web/src/i18n/strings.ts`) with `en`/`ms` locale parity (gate `COPY1`). No inline copy
|
||||
in shell modules (gate `COPY5`).
|
||||
|
||||
---
|
||||
|
||||
## The gates — what must be green before review
|
||||
|
||||
There is **no CI pipeline** (no `.github/`, no `.gitea/` workflows). Run these by hand
|
||||
before opening a change, and paste the output into your PR:
|
||||
|
||||
```bash
|
||||
npm run build # FIRST — web:test's BA cases read web/dist
|
||||
npm run bot:test # bot/test/runner.mjs
|
||||
npm run web:test # web/test/runner.mjs
|
||||
npm run search:test # search/test/runner.mjs
|
||||
npm run routing:test # routing/test/runner.mjs
|
||||
npm test -w theme # theme/scripts/audit.test.mjs (no root passthrough)
|
||||
npm run bot:typecheck
|
||||
npm run web:typecheck
|
||||
npm run search:typecheck
|
||||
npm run routing:typecheck
|
||||
npm run audit # off-token CSS audit — only if you touched theme/ or rendered CSS
|
||||
```
|
||||
|
||||
Order matters in one place: **`npm run build` before `npm run web:test`**, or the
|
||||
build-artifact gates (BA1–BA10) fail with a "build first" message.
|
||||
|
||||
### The repo's law enforcement: the two audit CLIs
|
||||
|
||||
Two maintainer-time tools (nothing in any `src` tree imports them) enforce the project's
|
||||
non-negotiable laws. **A PR that makes either one fail is a regression, full stop.**
|
||||
|
||||
- **`npm run privacy-audit`** ([docs/PRIVACY-AUDIT.md](docs/PRIVACY-AUDIT.md),
|
||||
`scripts/privacy-audit.mjs` + `scripts/lib/`) — the privacy law:
|
||||
- **TTL-only storage:** every KV `.put(` must carry `expirationTtl` on the same line,
|
||||
referencing a named `SCREAMING_SNAKE` constant (the `kv-ttl` gate; PG1 is the
|
||||
suite-level twin). The four KV families are the complete storage surface.
|
||||
- **Category-only logs:** no log line may carry a coordinate, chat id, user id,
|
||||
token, initData payload, geometry, or serialized payload (the `log-vocabulary`
|
||||
gate).
|
||||
- **Zero telemetry:** no analytics, beacons, tracking pixels, third-party CDN, or
|
||||
error-reporting host — in source *and* in the shipped `web/dist` bundle
|
||||
(`storage-api`, `runtime-hosts`, `bundle-telemetry` gates). v1 has nothing to opt
|
||||
into.
|
||||
- **`npm run budget-audit`** ([docs/BUDGETS.md](docs/BUDGETS.md),
|
||||
`scripts/budget-audit.mjs` + `scripts/lib/budget-gates.mjs`) — the $0 law: bundle
|
||||
sizes, KV write classes (the cron tick writes **zero** — source-proven), subrequest
|
||||
inventories, and the golden corpora, all re-derived against the Cloudflare free-plan
|
||||
ceilings. If you add a route class or a KV write path, its inventory row must follow
|
||||
the code.
|
||||
|
||||
Both audits are GPL-3.0 and reusable by forks; rerun them over your own tree before your
|
||||
first driver. The `--live` legs (KV expiry, cache headers) need Cloudflare credentials
|
||||
and are maintainer-time.
|
||||
|
||||
### The capture harness
|
||||
|
||||
For any Mini App UI change, `npm run web:capture` (Playwright, against the harness build
|
||||
in `web/dist-harness/`) is the verification surface: screenshot legs over every
|
||||
boot/route/guidance state, a DSGN-02 side-by-side against the committed mockup, and the
|
||||
GEO-03 runtime storage probe that fails if any app-controlled storage surface is
|
||||
non-empty after boot-to-live. Include the relevant captures in your PR. Outputs are
|
||||
gitignored; regenerate, never commit.
|
||||
|
||||
---
|
||||
|
||||
## Branch and commit conventions
|
||||
|
||||
- The active branch is **`main`**. Work lands there via atomic commits; if you prefer a
|
||||
branch, `feat/`, `fix/`, and `docs/` prefixes match the commit vocabulary. No
|
||||
branch-protection or required-reviewer config exists.
|
||||
- Commit messages are **conventional-commit style with a phase-scoped type**:
|
||||
`type(phase): subject` — types `feat`, `fix`, `docs`, `chore`, `test`; the scope is a
|
||||
phase identifier (e.g. `07-06`). Examples from `git log`:
|
||||
|
||||
```
|
||||
fix(07-06): launch-gate decisions — KV cron retune to 720/day (the $0 law holds)
|
||||
test(07-06): RED — SA6 doc-pointer static-text class vector (URL literals only)
|
||||
```
|
||||
|
||||
Subject is lowercase, imperative, and carries an em-dash clause explaining the *why*
|
||||
or the observable failure. Reference decision IDs (`D-09`, `PRIV-02`, `GEO-03`) in
|
||||
parentheses when relevant. One concern per commit.
|
||||
|
||||
---
|
||||
|
||||
## Pull requests
|
||||
|
||||
There is no PR template and no configured review process (this is a private Gitea
|
||||
repo — `git remote -v` points at `http://gitmirah:3000/c-fu/arah2.git`). If you open a
|
||||
PR there, reviewers look for:
|
||||
|
||||
- **All gates green, pasted.** The full list from [the gates](#the-gates--what-must-be-green-before-review)
|
||||
above, plus `npm run privacy-audit` and `npm run budget-audit` if you touched any
|
||||
storage, logging, network, bundle, or KV surface — i.e. if in doubt, run both audits.
|
||||
- **The observable before/after**, stated like a commit subject: what was broken, what
|
||||
it does now.
|
||||
- **Gate-relevant surface called out explicitly.** Touched a KV write? Say which
|
||||
`expirationTtl` and which gate re-verified it. Touched `web/src`? `GEO3`/`COPY1`/`COPY5`
|
||||
re-run. Touched the command surface? `CS4`/`CS6` re-run.
|
||||
- **Screenshots** for Mini App changes (from `npm run web:build:harness &&
|
||||
npm run web:capture`).
|
||||
- **No generated artifacts committed.** `theme/dist`, `web/dist`, `web/dist-harness`,
|
||||
and all `screenshots/` directories are gitignored by design — always rebuild. In
|
||||
particular, committed `theme/dist` would go stale because the web build consumes it
|
||||
at build time without vendoring.
|
||||
|
||||
---
|
||||
|
||||
## Licensing your contribution
|
||||
|
||||
- **Code is GPL-3.0.** The full text is in [LICENSE](./LICENSE), and GPL-3.0 is a
|
||||
deliberate project law (decision D-04, per [LICENSES.md](./LICENSES.md)): a fork must
|
||||
be able to read, change, and redistribute the whole working system. There is **no CLA
|
||||
and no DCO configured** — no `Signed-off-by` lines exist in the commit history, and no
|
||||
sign-off mechanism is set up. By submitting a PR you agree your code contribution is
|
||||
licensed under GPL-3.0 alongside the rest of the project. If you add a new source
|
||||
file, a copyright header is welcome but not required.
|
||||
- **Map-derived data is ODbL-1.0, not GPL.** The curated gazetteer
|
||||
(`search/src/data/kl-gazetteer.json`), the route fixtures
|
||||
(`routing/src/data/route-fixtures.json`), and the derived road graph are all curated
|
||||
or derived from OpenStreetMap data — **© OpenStreetMap contributors, ODbL 1.0**
|
||||
(<https://opendatacommons.org/licenses/odbl/1-0/>). The attribution clause travels
|
||||
with derived databases, so:
|
||||
- Never remove the `_license` / `_attribution` / `_notice` envelope fields from a
|
||||
data file.
|
||||
- Any new data file derived from OSM must carry the same envelope.
|
||||
- Any restructured/derived index of the gazetteer carries the same notice (ODbL §4.4
|
||||
share-alike for Derivative Databases). Code and data are separately licensed — ODbL
|
||||
§2.3(a) excludes computer programs.
|
||||
- See [docs/ROAD-GRAPH.md](docs/ROAD-GRAPH.md) for the road-graph licensing surface.
|
||||
|
||||
---
|
||||
|
||||
## Reporting issues and proposing ideas
|
||||
|
||||
There are no issue templates (no `.github/ISSUE_TEMPLATE/`). The repository lives on a
|
||||
private Gitea instance at `http://gitmirah:3000/c-fu/arah2` — use its issue tracker if
|
||||
you have access. <!-- VERIFY: Gitea issue-tracker availability and URL path — the remote is confirmed via `git remote -v`, but the tracker is on the maintainer's internal instance and cannot be verified from the repository contents. -->
|
||||
|
||||
For a bug report, include:
|
||||
|
||||
- Steps to reproduce, expected vs actual behavior, and environment (which Telegram
|
||||
client surface — `?mock=` value if you used the harness — and OS).
|
||||
- The failing gate output, verbatim: the runners and audits print evidence strings that
|
||||
are meant to be quoted, never paraphrased.
|
||||
|
||||
For a feature proposal, first check the charter above: if it's in the deferred list,
|
||||
argue why the deferral no longer holds, and expect that discussion to happen at the
|
||||
charter level (`.planning/PROJECT.md`), not in the PR.
|
||||
|
||||
**Where decisions live.** Project-level decisions are recorded in `.planning/PROJECT.md`
|
||||
(charter, key decisions), `.planning/ROADMAP.md`, and per-phase documents under
|
||||
`.planning/phases/` — all tracked in the repo. The checked-in evidence records are the
|
||||
`docs/` files: [PRIVACY-AUDIT.md](docs/PRIVACY-AUDIT.md) (the signed privacy audit),
|
||||
[BUDGETS.md](docs/BUDGETS.md) (the measured budget record),
|
||||
[SEARCH-QUALITY.md](docs/SEARCH-QUALITY.md) and [ROAD-GRAPH.md](docs/ROAD-GRAPH.md)
|
||||
(validation records), and [KNOWN-LIMITATIONS.md](docs/KNOWN-LIMITATIONS.md). If a
|
||||
reviewer asks "where was this decided?", the answer is one of those files.
|
||||
Reference in New Issue
Block a user