Implementation status: introduced by spec-v10 §I.1.
One page. Every recurring task a maintainer performs and the command for each. Read top to bottom for orientation; jump by section header for routine work.
The site is one index.html, one styles.css, one app.js, a
service worker (sw.js), per-group calc modules (calc-*.js), and
a sharded data/ folder. Specs live in specs/. Per-area docs
live in docs/. Build / lint / data scripts live in scripts/.
Tests live in test/unit/ and test/integration/.
-
Pick the group (A through Y per ../README.md; v12 added U Veterinary / V EMS / W Pilots / X Real Estate / Y Educators on top of A-T) and the next utility number per the active spec.
-
Implement the exported
compute<Name>function plus its renderer and a<NAME>_RENDERERS["id"] = render<Name>registration incalc-<group>.js(or the group's shared module). Put a// dims:annotation immediately above the exported compute function (the Phase C dimensional-analysis lint requires one per exported function), and export a<name>Exampleobject. -
Wire the tile into the registries (each is a one-line add; the lint gates below enforce that none is missed):
{ id, name, group, trades, desc }row in theTOOLSarray in ../tools-data.js (the catalog registry, lazy-loaded out ofapp.jsper spec-v17 §H.2 -- NOT in app.js).[id, group]row in_TILESin ../tile-meta.js.- the tile id in the matching
declare("./calc-<group>.js", "<NAME>_RENDERERS", [...])list in ../tool-modules.js (the tile-id to renderer registry, lazy-loaded out ofapp.jsper spec-v10 §§H.1/H.2 -- NOT in app.js; thecheck-wiringlint fails if aTOOLSid has no declared renderer). { module, fn }entry in ../test/fixtures/compute-map.js.- a 3-6 id entry in ../scripts/related-tiles.mjs
(
check-related-tiles). - the tile id in
../test/fixtures/renderer-schema-coverage.json
if the renderer exposes a field schema -- declarative
_simpleRenderertiles DO, bespoke hand-written renderers do not (check-renderer-schemaratchets this list and fails on a regression). - 3-5 search aliases in ../data/search/aliases.json
(
check-discoverability; terms must be unique catalog-wide). - Confirm the tile enters through the shared
renderToolViewpath, which is its mandatory third door after the website and local MCP surfaces and adds Report a problem without per-tile code (check-feedback-loop).
-
Add tests + fixtures:
- At least one worked-example fixture row in
../test/fixtures/worked-examples.json
(the spec-v10 Phase C.1 registry; coverage is enforced by
scripts/check-worked-examples.mjsinnpm run lint). - A
test()block in ../test/unit/bounds-fuzzer.test.js pinning the worked example (both directions / unit systems where the tile solves both) and the degenerate-input error seams. This is the current per-tile cross-check + bounds-fuzzer coverage row the Phase D lint requires; the older ../test/unit/first-principles.test.js still runs but recent tiles pin in bounds-fuzzer. - Edge cases: zero, negative, max, missing input.
- At least one worked-example fixture row in
../test/fixtures/worked-examples.json
(the spec-v10 Phase C.1 registry; coverage is enforced by
-
Add the inline entry (formula / edition / freeAccess / governance) in ../citations.js --
check-citation-coveragefails on aTOOLSid with noCITATIONSrow.Add a source-stamp row to citation-discipline.md only for a bespoke, hand-written renderer.
build-citation-strings --checkreads each row back out of the renderer it names, and it finds the literal by locatingfunction render<Name>and matching thecitationEl.textContentassignment inside it. A declarative_simpleRenderertile has no such function -- its citation lives in the spec object -- so a row added for one fails the gate with "no citation literal found in its renderer". Every band since the declarative factory arrived has (correctly) left those tiles out of that table;citations.jsis where their wording is reviewed. -
Regenerate the derived artifacts, in this order -- several read each other's output, and
extract-citationsreads a file the build emits, so running it early leavesrenderer-citations.jsstale and the count oscillates:node scripts/build-corpus.mjs node scripts/build-tile-index.mjs node scripts/build-field-index.mjs node scripts/build-alias-shards.mjs node scripts/extract-constant-notes.mjs node scripts/extract-bespoke-schemas.mjs --write node scripts/build-renderer-map.mjs npm run build node scripts/extract-citations.mjs --write # LAST, after the buildThe
--checkforms of these run innpm run lint. Update the affecteddata/<folder>/manifest.json(edition,asOf) and runnpm run data:refreshonly if the tile uses a bundled dataset. -
Run the full gate:
npm run audit(ten stages: lint -> test -> build -> check:dist -> check:shells -> check:module-sizes -> check:shell-values -> check:lastmod -> data:verify -> check:data-stamps, per spec-v12 §G.3 + spec-v13 Phase G). Update the catalog counts in all four files thecheck-readme-countsgate reads -- ../README.md, ../index.html (both the JSON-LD description and the home lede), ../AGENTS.md, and ../mcp/README.md -- covering the tile, module, group, and sitemap totals including the Mermaid-diagram nodes.Then update the count that no lint gate watches: ../test/integration/search-prefill.test.js asserts the home lede's tile count as a literal string.
npm run lintstays green when it is stale and the failure surfaces ~15 minutes intonpm run test:e2e, so change it in the same pass as index.html rather than discovering it later. -
Add a CHANGELOG stanza under "Unreleased" naming the tile, the group, the citation, and the worked example.
-
Per-module gzipped-size check: enforced by ../scripts/check-module-sizes.mjs (part of
npm run lint). Eachcalc-*.jsmodule carries an explicit cap (current v12 caps in performance.md §"v12 per-module budgets"); the lint warns within 10 % of cap. The spec-v10 §H.1 5 KB per-tile cap remains the design target for the tile's contribution to its module. -
Group X (Real Estate) and Group Y (Educators) tiles use cite-strong governance verbiage in the source-stamp rather than a limitation banner. (Groups U, V and W are retired; the banner override this step used to describe applied to them.) The Phase F.2 mobile-responsive sweep at 320 / 375 / 414 / 760 px per mobile-responsive.md is required for every new tile.
Use calculator-reports.md. Quick version:
- Query open rows from
roughlogic-reportsthrough authenticated Wrangler. - Reproduce from the saved URL, inputs, and output.
- Verify against the primary source and fix through the normal spec-first path.
- Mark the row
resolvedorwont_fixwith a short audit note. - Remove rate-counter buckets older than 14 days.
Use edition-rollover.md. Quick version:
scripts/sources-cycle.json— bump the standard's edition.npm run lint— work through the warnings.- Per-tile citation strings + per-folder manifest editions.
- CHANGELOG stanza per standard rolled.
Use edition-amendment.md. Quick version:
- Triage: language only, math correction, or parallel jurisdiction.
- Update citation; for a math change, update calc + worked- example fixture.
- CHANGELOG patch stanza naming the publisher reference.
The 90-day deprecation per spec.md §10:
- Add a CHANGELOG entry naming the tile, the planned removal date (today + 90 days), and the rationale.
- Add a soft notice in the tile's renderer: "Scheduled for removal on YYYY-MM-DD."
- Wait 90 days.
- Remove the tile from the
TOOLSarray in ../tools-data.js and from thedeclare(...)renderer list in ../tool-modules.js. - Add a URL-hash redirect in ../routing.js so stale bookmarks land on the closest replacement (or home, with a banner).
- Remove the calc renderer, the unit tests, and the citation row.
- CHANGELOG entry recording the removal.
- Edit the canonical inline source in ../scripts/build-data.mjs (this is the build-time owner of every shard's content).
npm run data:refresh— regenerates the shards on disk and re-stampsscripts/expected-hashes.json.npm run data:verify— confirms every shard hash matches.- Bump the affected
data/<folder>/manifest.jsonasOfand per-entryverifiedOndates. - CHANGELOG stanza naming the dataset and the source.
- Run the full gate (
npm run audit).
WMM rolls every 5 years. The check-citation-freshness lint warns within 6 months of expiry and hard-fails after.
- Download the new WMM coefficients from ncei.noaa.gov/products/world-magnetic-model.
- Update the bundled coefficients at
../data/field/wmm/coefficients.json
(the v9 §F.1 magnetic-declination tile's shard; the
../data/field/manifest.json
editionline records the cycle). - Update ../scripts/sources-cycle.json:
bump
current_edition,current_release,expires_on. - Update the manifest
editionstring. - Add WMM round-trip worked-example tests for at least three known declination values from the new coefficient set.
- CHANGELOG stanza naming the cycle (e.g., WMM2025 → WMM2030).
Annual minimum per spec-v10 §14. Append a row to audit-trail.md with date, reviewer, scope, and outcome. The audit trail is append-only and public.
| Command | What it does |
|---|---|
npm run dev |
Builds dist/, then serves only that public tree on loopback. |
npm run build |
Produces dist/ for deployment. |
npm test |
Recursively discovers the full test/unit/ suite with Node's test runner; no shell-specific globbing. |
npm run test:unit |
Same as npm test. |
npm run test:e2e |
Playwright integration tests. |
npm run test:a11y |
axe-core accessibility tests. |
npm run lint |
grep checks, ngram bans (fingerprint-based; skips in the public repo, where the private hash list is absent), v6 discipline, v8 manifest discipline, v10 citation freshness, citation-strings sync check, discoverability, worked-examples coverage, tile-meta coverage, v12 G.2 wiring + G.4 renderer-export lints, module sizes, home payload budget. |
npm run check:dist |
spec-v12 G.3 dist/-vs-runtime cross-check. Walks every shipped HTML / JS / CSS / JSON under dist/ and resolves every same-origin reference; dangling references fail. Wired into npm run audit as the fourth stage. |
npm run data:refresh |
Run the data pipeline. Regenerates shards and expected-hashes.json. |
npm run data:verify |
Verifies shard SHA-256 hashes against expected-hashes.json. |
npm run lint (includes check-verified-on-ledger) |
Fails if a shard named in scripts/sources-cycle.json is missing verified_on, or carries one that differs from the oldest last_verified among the rows naming it. verified_on is a claim about research and belongs to the ledger; fetched is a fact about the build and moves freely. Do not hand-edit a tracked shard's verified_on -- update the ledger row and re-run npm run data:refresh. |
npm run lint (includes check-future-stamps) |
Fails if any provenance stamp (verified_on, last_verified, _updated, fetched, asOf, built, generated, derived_at) in data/**, test/fixtures/worked-examples.json or scripts/sources-cycle.json is dated after tomorrow in UTC. A stamp records when the project looked, so a date that has not arrived records a look that has not happened -- on 2026-09-18 fifty worked-example rows claimed verification on 2026-09-19 through 09-22. It is the one clock comparison that cannot turn main red on its own: a stamp that passes today passes on every later day. Stamp the date the work was actually done. |
npm run check:data-stamps |
Fails if any data/**/*.json provenance stamp (verified_on, fetched, asOf, built, a date-shaped manifest version) moves backwards against the base branch tip. Catches a stale data-refresh branch, which would otherwise revert hand-corrections while every other gate stays green. Needs git history, so CI runs it as its own step rather than inside lint; set DATA_STAMP_BASE to compare against a specific ref. A base stamp dated after tomorrow may move back -- that corrects an impossible date rather than reverting research. It fails if that ref resolves to HEAD, because an empty comparison reported as OK is how it was blind for its first day in CI. |
npm run clean |
Removes dist/. |
npm run audit |
Single-shot pre-PR gate (spec-v10 §2 / §14; ten stages): chains lint -> test -> build -> check:dist -> check:shells -> check:module-sizes -> check:shell-values -> check:lastmod -> data:verify -> check:data-stamps with per-stage banners. Short-circuits on first failure. The count and the chain are gated by check-ci-claims, which compares this sentence to scripts/audit.mjs. |
For every minor or patch release:
npm run audit— must report all 10 stages OK (lint -> test -> build -> check:dist -> check:shells -> check:module-sizes -> check:shell-values -> check:lastmod -> data:verify -> check:data-stamps).- Confirm home-view payload is under cap (current
check-home-payloadbudget: 100 KB gzipped, with the v10 §H.2 per-asset sub-budgets HTML 20 KB / CSS 25 KB / JS 45 KB enforced bynpm run lint). - CHANGELOG stanza: dated, lists every change, names every tile touched.
package.jsonversion bump per semver: patch for bug fixes and data refreshes, minor for new tiles or new platform features, major only with 90-day deprecation notice.
| Asset | Budget |
|---|---|
| Home-view total (gzipped) | 100 KB (HTML 20 / CSS 25 / JS 45 KB sub-budgets per spec-v10 §H.2) |
Per-module dynamic-imported calc-*.js (spec-v12 §14.3 caps) |
enforced by ../scripts/check-module-sizes.mjs; live caps in performance.md. spec-v10 §H.1 5 KB-per-tile remains the design target. |
| First Contentful Paint (slow-3G) | 1.5 s |
| Largest Contentful Paint (slow-3G) | 2.5 s |
| Total Blocking Time | 200 ms |
| Cumulative Layout Shift | 0.05 |
- Calculator execution is 100% client-side with no account, analytics, or AI. The sole hosted write path is the isolated, user-initiated report Worker; persisted invocation logs are disabled and D1 records expire after 30 days.
- No localStorage / sessionStorage / cookies / IndexedDB beyond
rl-theme. URL hash is the only state mechanism. (Therl-bigbuttonskey was retired in spec-v11.) - CSP
default-src 'self',connect-src 'self',worker-src 'self'. - WCAG 2.2 AA, 48 px touch targets, single h1, voice input.
- No emojis, no em-dashes, no decorative icons in shipped UI.
- Plain ASCII in source-stamp strings.
- 90-day deprecation for any utility removed or formula changed.
- contributor-checklist.md — the PR checklist this quickstart compresses to a one-page form.
- edition-rollover.md and edition-amendment.md.
- citation-discipline.md — citation source of truth.
- hash-state.md — URL-hash encoding spec.
- audit-trail.md — outside reviews.
- ../specs/spec.md — original v1 spec; every later spec inherits this.