# METHOD DECK SPEC (the team, 2026-09-30 19:2xZ). Founder order via the team: "how about we get a lot more cards."

Pattern of record: the sunrise card ("A Simple Exercise", the reference living-card implementation and
the reference implementation). Card contract: the team's internal handoff files
(FROZEN) with the v2 additive fields the sunrise card mints (method, receipt, provenance chain). Reference implementation:
the reference implementation (address, qsha256, verifyCard). BUILD AND PREVIEW ONLY. Nothing is published.

## Layout (all under this folder)
- `methods/<slug>.cjs`      one method module per card (writers produce these; the ONLY thing writers produce)
- `tools/mint.cjs`          mints `cards/<slug>.card.json` from each method module (one shared minter, all cards uniform)
- `tools/verify.cjs`        replays every example of every method, checks expected answers, recomputes every card address
                            and method sha256 against the card, exits nonzero on any mismatch
- `cards/<slug>.card.json`  minted cards (generated, never hand-edited)
- `MANIFEST.json`           generated: [{slug, title, deck_category, file, card_sha256, method_sha256, address}]
- `INDEX.md`                generated one-page index: title, one-line description, inputs, worked example, address
- `RECEIPT.md`              generated: sha256 of MANIFEST.json, counts, verify result, minted_at

## Method module interface (CommonJS, Node built-ins only, NO dependencies, NO network, NO Date.now() or Math.random())
```js
module.exports = {
  slug: "compound-interest",                 // kebab-case, unique, equals the file name
  title: "Compound interest",                // short
  deck_category: "finance",                  // finance | units | currency | dates | timezones | health | cooking | astronomy | tax | geometry | other
  description: "One sentence: what the method computes.",
  inputs: [                                  // named, typed, all documented
    { name: "principal", type: "number", unit: "currency units", required: true, description: "Starting amount", min: 0 },
    { name: "annual_rate_percent", type: "number", unit: "%", required: true, description: "Nominal annual rate" },
    { name: "compounds_per_year", type: "integer", required: true, description: "Compounding periods per year", enum: [1, 2, 4, 12, 52, 365] },
    { name: "years", type: "number", unit: "years", required: true, description: "Term" }
  ],
  outputs: [ { name: "final_amount", unit: "currency units", description: "Amount after the term" } ],
  steps: [                                   // the human-readable deterministic method, in order, no em dashes
    "Convert the annual rate to a decimal: r = annual_rate_percent / 100.",
    "Periodic rate i = r / compounds_per_year; periods n = compounds_per_year * years.",
    "final_amount = principal * (1 + i) ^ n, rounded to 2 decimals."
  ],
  constants: [],                             // [{ name, value, unit, source: <index into sources> }] for any fixed number used
  sources: [                                 // at least one; every constant and every table cites one
    { kind: "url", value: "https://...", title: "...", publisher: "...", retrieved_utc: "2026-09-30T00:00:00Z", note: "..." }
    // kind in: url | doi | isbn | edition_page | statute | standard
  ],
  disclaimer: null,                          // string for health/tax/legal cards (see below), else null
  questionText(inputs) { return "..."; },   // one plain sentence a person would ask, with the input values filled in
  run(inputs) {                              // PURE and DETERMINISTIC; validate inputs, throw Error(message) on bad input
    return { answer: "1,647.01", values: { final_amount: 1647.01 } };   // answer: the string a card shows; values: numbers
  },
  examples: [                                // >= 2: examples[0] is the WORKED EXAMPLE minted as the card; the rest are test vectors
    { name: "worked example", inputs: { principal: 1000, annual_rate_percent: 5, compounds_per_year: 12, years: 10 },
      expected: { answer: "1,647.01" }, check: "independently computed with the formula above" },
    { name: "test vector: annual compounding", inputs: { ... }, expected: { answer: "..." }, check: "..." }
  ]
};
```
Rules for writers:
- Deterministic and replayable: same inputs, same answer, forever. Round only at the end and state the rounding in steps.
- Anything with a date-dependent constant (currency rates, tax tables, leap seconds) carries the DATE of the table in
  `constants` and `sources` and in the card's question ("using the IRS 2026 table", "at the ECB reference rate of 2026-09-29").
  Never fetch live rates; the card is a method plus a cited, dated table.
- Health cards (BMI, dosing by weight): `disclaimer` = "General formula only. Not medical advice. Dosing decisions belong to a
  licensed clinician and the product's label." Dosing cards take the per-kilogram dose as an INPUT; never bake a drug dose in.
- Tax cards: cite the exact IRS publication or revenue procedure and tax year; brackets go in `constants` with that source.
- Currency: use the ECB euro reference rates for a stated date (or another official central-bank table), cited with that date.
- Time zones: fixed-offset arithmetic with the rules stated; if DST is involved, the DST rule (start and end) is a constant with a
  source; never depend on the host machine's tz database.
- Astronomy (moon phase, tides): moon phase by Meeus mean synodic month from a cited reference new moon; tides by the rule of
  twelfths from a given high and low (state that real tide tables come from NOAA and cite it); mark approximations in steps.
- No em dashes anywhere. American English. Every `sources[].retrieved_utc` is a real UTC timestamp read from the clock.
- Every example's expected answer must be checked by an independent computation (state how in `check`).

## What the minter produces (tools/mint.cjs, writers do not touch)
For each module: `question = questionText(examples[0].inputs)`, `answer = run(examples[0].inputs).answer` (must equal expected),
`madeAt = new Date().toISOString()` at mint time, `category = "other"`, `answeredBy = "method"`, `answerMethod = "method"`,
address per CARD_CONTRACT_v1 section 3 (`proveit_card:` pearl, sha256 of JSON.stringify([pearl]), first 32 hex),
`qsha256` per section 4 (NFKC, collapse whitespace, trim, lowercase), `source` = sources[0] mapped to the contract's
`{kind, value, retrieved_utc}`, `sources` = display list of all, `method = {name: title, version: "1", source_url_or_file:
"methods/<slug>.cjs", sha256_of_method_file: sha256(file bytes)}`, `deck_id = "method-deck"`, `deck_title = "Method Deck"`,
`receipt = {inSubstrate:false, local:true, modelCallsAtMint:0, tokensAtMint:0}`, plus `method_deck = {inputs, outputs, steps,
constants, examples, disclaimer, deck_category}` so a card is self-describing, and the v2 provenance chain with one link
(the method) exactly as sunrise_card.js addProvenanceFields does. Card JSON is written with 2-space indent, LF, UTF-8 no BOM.
