Files
arah2/CONTRIBUTING.md

13 KiB
Raw Permalink Blame History

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. 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 for prerequisites and first-run instructions, and docs/DEVELOPMENT.md for the full local development setup. The short version:

  1. Clone and install — npm workspaces install all five packages at once:

    git clone http://gitmirah:3000/c-fu/arah2.git
    cd arah2
    npm install
    
  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 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:

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, 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, 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 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, and GPL-3.0 is a deliberate project law (decision D-04, per 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 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.

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 (the signed privacy audit), BUDGETS.md (the measured budget record), SEARCH-QUALITY.md and ROAD-GRAPH.md (validation records), and KNOWN-LIMITATIONS.md. If a reviewer asks "where was this decided?", the answer is one of those files.