Repository navigation
Speculative Symbol Metadata
Speculative design. Open, not settled — thinking and proposals, not shipped behavior. See Roadmap for what's in flight.
This follows the examples-as-data migration (Examples as Data). Steps 1-7 below are
done (#288, #301, #302, #306, #307, and the catalog-dump retirement it inspired, #317/#320),
and packages/reference/scripts/migrate/ is gone. The open questions below are still open.
Each head gets three files, side by side, where its reference record lives today:
| file | holds | written by |
|---|---|---|
<Head>.yaml |
what the symbol is: every fact about the head that isn't an example | hand |
<Head>.examples.yaml |
the examples list, exactly as examples: is today |
hand |
<Head>.implementations.yaml |
one entry per example id: our forms, each system's run | collect-forms, oracle scan |
- Every example gets an implementations entry, even when that entry is empty (
{}). yaml.test already enforces this, so the rule carries over unchanged. - MathJSON stays canonical in the examples file. notatio and every other form stay derived, in the implementations file.
- A head can have a
<Head>.yamland no examples file. That is how we get the facts out of TS for the 130-odd heads we map or rename but never document by hand (see below).
The head-level list in <Head>.yaml is bindings: (it was implementations:, which clashed with the per-example <Head>.implementations.yaml).
This table comes from a survey of main at the time of writing. The counts are only there for scale.
| today | heads | becomes |
|---|---|---|
reference/src/crosswalk/curated.ts CURATED
|
~180 (30 without a record) |
references: (the field already exists; the curated rows merge into it) |
curated.ts FUNGRIM_NAMES, DLMF_NAMES, WIKIDATA_FIXES, WIKIDATA_CONFIRMED, CATALOG_ALIASES
|
~110 across the five tables | a names: map, e.g. { fungrim: RiemannZeta, dlmf: …, wikidata: Q…, catalog: … }, with a flag for whether the Wikidata ID is confirmed |
wolfram/src/to-wolfram.ts HEADS
|
~520 (130 without a record) |
names.wolfram, set only when Wolfram's name differs from ours |
oracle/src/mappings.ts MAPPINGS
|
~86 (24 without a record) | head-level bindings: rows with origin: mapped, one row per system: form: sympy, template: "primepi($1)", plus arity, threadArg and note
|
statistics/src/naming.ts RENAMED
|
about a dozen |
formerly: [<catalog name>], a data alias like the numerals renames |
ce.declare({ description }) in the symbol packages |
a few dozen | a single source, summary, which the declare call reads from generated data |
These stay where they are:
- The generated
*-data.tsfiles (wikidata, fungrim, oeis, dlmf, inventory, …), because they are fetched; the loader joins them by name.
The catalog dump (packages/catalog/src/catalog-data.ts, not in the table above because it
predates this survey) went the same way in a later pass: REFERENCES onto catalog: and
names.catalog, CARRIERS/COLLECTIONS/STATS/MAPS onto grades/carrier/unbounded/
catalogCarrier/statOn/mapOn. catalog/src/reference-fixes.ts did NOT stay, against this
doc's original expectation: once the dump it corrected was gone, its fixes were already
permanently baked into the records it wrote (the codemod read the FIXED rows), so nothing was
left for it to apply at load.
MAPPINGS is the move that pays off most. The page already shows the head-level bindings: list as "the systems it maps to", and MAPPINGS is a second hand-kept copy of the same fact.
Heads like Add, Power, Rational and Equal carry Wolfram names and oracle templates but have no record. Each gets a metadata-only <Head>.yaml in packages/reference/entries/, with no examples file. Today the stub: engine pages are all generated. A stub that carries hand data would become a written file, and the rest would stay generated.
to-wolfram and the oracle emitters run in the browser and in CI, and they need synchronous lookups. So do the ce.declare descriptions. There are two ways to give them the data:
- A generated
*-data.tsper consumer, pinned by a test that the generated file is current. This is how the forms work today (heads-data.tsdid too, until the manifest replaced it: Manifest). - The
virtual:reference-entriesVite module plus the Node loader. This only works where the consumer already runs under Vite or the loader.
I'd default to the generated files. No consumer has to learn about YAML, and tree-shaking keeps the browser bundles small.
Every step lands as a codemod with a before/after test. At the codemod commit, a test deep-compares the old TS export against the table rebuilt from YAML, as its consumer sees it. A follow-up PR then deletes the TS table and the test. The codemods stay in packages/reference/scripts/migrate/ until the last step.
- Split
examples:out into<Head>.examples.yaml. This step is purely mechanical: the loader andwriteEntrieslearn the three-file layout, the.vscodeschemas get the new glob, and the site's data comes out identical before and after. - Move
curated.tsintoreferences:andnames:. Its only consumer is the crosswalk, andcheck-crosswalk.tsalready fetches every URL. - Move
RENAMEDintoformerly:. - Move the Wolfram
HEADSintonames.wolfram. The test:toWolframgives the same output for every key ofHEADSacross the round-trip golden set. - Move
MAPPINGSintobindings:rows. The test:emitgives the same source for every example on every system, and the nightly drift check stays quiet across the switch. - Replace the declare descriptions with
summary. - Delete
migrate/.
- Does
details:stay in<Head>.yaml? It's prose likesummary, so I'd say yes. - Heads such as
FormDiscriminantare documented on their family's page. Should each get its own metadata file with apage:pointer to the family, or stay discoverable only through the family's record? - Should the crosswalk's derived rows ever be written back into
references:? I'd say no: derived data stays derived, and the page joins the two.