From 7318b0bc31be06065b0031a5c7792f2bed97181e Mon Sep 17 00:00:00 2001 From: fosterwei Date: Mon, 12 Oct 2026 11:00:48 +0000 Subject: [PATCH] Make the per-compound calculators answer the whole query The first version was a calculator with a dose table under it, about 750 words. It missed most of what people actually search for on these terms. The dominant intent is one question in different words: how many units is this dose, in this vial, with this much water. People want to read that off a chart rather than type it in, which is why every page ranking for these terms has one. These now do: a table per vial size of 2, 5 and 10 mg, columns for 1, 2 and 3 mL of diluent, cells in units on a U-100 syringe, with anything over 100 units marked because it will not fit in one syringe. The arithmetic was checked against published charts: a 2 mg vial in 1 mL gives 12.5 units for 0.25 mg, and in 2 mL gives 25, which is what the reference tables say. The rows are what make the chart belong to a compound rather than being the same grid nineteen times: they are the absolute doses that studies of that compound actually administered. Per-kilogram figures are deliberately excluded from the parser. For four compounds that leaves nothing to chart, because every dose ever published for them is per kilogram, and those pages now say so in a section of their own rather than quietly dropping a block. Multiplying a rat milligram-per-kilogram figure by a human body weight is the one operation this site must not imply, so the page names it and explains why interspecies scaling is not multiplication. Five more blocks come from cited data the records already held and the first version ignored: weight-normalised doses with the reason they are off the chart, escalation schedules where a trial titrated, how long studies ran, storage and stability as published, and routes. A FAQ answers the unit question in the searcher's own words and says where the numbers come from. Mean length goes from about 750 words to 1,562, and the additions are computed arithmetic or quoted evidence rather than filler. SourceIds.astro exists because Astro frontmatter is TypeScript, not JSX, so a helper returning markup cannot live up there. The tests now check each page either charts doses or explains why it cannot, never both, and that the FAQ and the refusal are present, since the refusal is what keeps these inside the site's scope rule. Verified by removing each of the three in turn. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/test_calculators.mjs | 18 +++ src/components/SourceIds.astro | 16 ++ src/lib/calculators.ts | 70 +++++++++ src/pages/calculators/[slug].astro | 237 ++++++++++++++++++++++------- 4 files changed, 290 insertions(+), 51 deletions(-) create mode 100644 src/components/SourceIds.astro diff --git a/scripts/test_calculators.mjs b/scripts/test_calculators.mjs index c8204f1..ea04e4e 100644 --- a/scripts/test_calculators.mjs +++ b/scripts/test_calculators.mjs @@ -105,6 +105,24 @@ if (fs.existsSync(CALC_DIR)) { // the generic tool, so an empty one means the page should not have shipped. const tableRows = (html.match(/[\s\S]*?<\/tbody>/i) || [''])[0].match(//gi) || []; if (tableRows.length === 0) fails.push(`calculators/${slug}: dose table is empty`); + // Either the page charts absolute doses, or it explains why it cannot. + // A page with neither has a hole where its main reason to exist should be. + const hasChart = /reconstitution chart/i.test(html); + const hasExplainer = /Why there is no units chart/i.test(html); + if (!hasChart && !hasExplainer) { + fails.push(`calculators/${slug}: no units chart and no explanation for its absence`); + } + if (hasChart && hasExplainer) { + fails.push(`calculators/${slug}: shows a chart and also says it has none`); + } + // A charted page must actually contain computed unit figures, not an empty grid. + if (hasChart && !/mL diluent/.test(html)) { + fails.push(`calculators/${slug}: chart has no diluent columns`); + } + // The FAQ and the refusal are the blocks that answer the query and keep the + // page inside the site's scope rule; neither is optional. + if (!/Questions people ask/i.test(html)) fails.push(`calculators/${slug}: no FAQ`); + if (!/will not take a body weight/i.test(html)) fails.push(`calculators/${slug}: no refusal statement`); const m = html.match(/