Skip to content

Working with these docs

This repo describes behaviour: what the product does, the domain rules, data and sync formats, APIs, and decisions that bind more than one platform. How a platform implements that behaviour stays in its own repo (vulture-ios/Docs, vulture-android/docs, vulture-web/docs).

Put it inWhen it is
docs/features/<area>/<id>.mda user-facing feature (one page each, with the feature: status block)
docs/domain/a calculation both apps must get identical
docs/data/stored or synced data, file formats, links
docs/api/an endpoint, ours or a third party’s
docs/accounts/subscriptions, entitlements, sign-in
docs/decisions/a settled decision, with its date and scope
docs/analyses/<yyyy-mm>-<slug>/the analysis of a feature before it is built
  1. Copy docs/analyses/_template.md to docs/analyses/<yyyy-mm>-<slug>/index.md. In Claude Code, from this repo: “Analyse <feature> for iOS and Android.” CLAUDE.md describes the steps.
  2. The analysis reads both codebases, writes the behaviour spec, and moves shared rules into domain/, data/ or api/.
  3. The owner answers the open questions, and the status becomes agreed.
  4. In each platform repo: “Implement ../vulture-docs/docs/analyses/<slug>.”
  5. When it ships: update the feature page’s status block (with since), and set the analysis to shipped.

Every page needs a title; don’t add an H1. The other fields are optional:

description: One sentence.
scope: shared | ios | android | web # for decisions and platform notes
status: draft | agreed | building | shipped | superseded # analyses
origin: vulture-android/docs/x.md # set when a page was moved here
feature: # feature pages only
area: Flight map
paid: true
ios: { status: done, since: "4.1.0", paths: [Packages/Flights], notes: optional }
android: { status: partial, since: unreleased, paths: [feature/flightmap] }
web: { status: n/a }

since is the marketing version a feature first shipped in, one of the versions in src/data/releases.json. While a feature is usable on main but in no build yet, it is unreleased; a feature that is built but switched off has no since until it is switched on. A TestFlight or Google Play testing build counts as shipped. The iOS releases and Android releases pages are built from it.

The platform sessions set unreleased as part of the work (their CLAUDE.md says so). The deploy lanes of vulture-ios and vulture-android then stamp it after every upload:

Terminal window
node ../vulture-docs/scripts/release.mjs <ios|android> <version> --build <n> # or: pnpm release …

It turns that platform’s since: unreleased into the version, adds the build (and a new version) to src/data/releases.json, and commits only those files in vulture-docs, without pushing. Options: --date yyyy-mm-dd (default today, UTC) and --no-commit.

Link pages with relative .md paths (../data/sync-schema.md#4-deterministic-ids), which work both on disk and on the site.

A feature page shows its screenshots under the status table, one tab per platform: iPhone, iPad, Mac, Watch and Android, light only. src/data/screenshots.json names, per feature (its path under docs/features/, without .md) and platform, the source image in the app repos next to this one. pnpm screenshots copies them, scaled down as WebP, into src/assets/screenshots/ and removes the ones the file no longer names. Run it after the apps regenerate their images, then build.

The sources today are the App Store captures in vulture-ios/fastlane/source-screenshots/ (the paywall crops there are Mac views) and the Roborazzi test images in vulture-android/**/src/test/screenshots/, which show screens without the map.

Terminal window
pnpm install
pnpm dev # http://localhost:4321
pnpm build # fails on invalid frontmatter