From d541ed8c39f618cf6d8c1f7e77896676d4b72a73 Mon Sep 17 00:00:00 2001 From: fosterwei Date: Mon, 12 Oct 2026 14:02:34 +0000 Subject: [PATCH] Embed each compound's calculator on its record, and link both ways Every compound that has a calculator page now carries the calculator on its own record too, directly under the dosage chart, with a link to the full page and a card in Related records. The calculator page already linked back, so the pair is now joined in both directions and neither is an orphan. Placement is deliberate. The outline on all 19 qualifying records puts the calculator immediately after "dosage chart: studies versus what circulates", so a reader who has just seen which figures were studied and which merely circulate is one scroll from working out what a vial yields, rather than finding the tool in a different section. The block renders only for a record that qualifies for a calculator page, by the same rule that decides whether that page exists: finished, reviewed, and carrying at least two distinct published dose figures. Fifteen compounds get neither, which is the point. Calculators.astro gains id, heading and headingLevel props so the one component can sit inside a compound page under that page's own H2 with its tool heading demoted to H3, instead of producing two H2s or a copy of the markup. The arithmetic still has exactly one implementation and one test suite. The tests now check every compound that has a calculator page: a link to it, a calculator section, an output element, and the embedded script computing the known case. 55 assertions, up from 36. Verified by removing the link, cutting the section, and breaking the script on a built page; each exits 1. The first attempt at the section cut used a regex that never matched, so that fault was not exercised until the cut was done by index. Worth recording because a test that passed only because the injection failed would have looked identical to a test that works. Co-Authored-By: Claude Fable 5.1 --- research/intents/aod-9604.json | 1 + research/intents/ara-290.json | 1 + research/intents/bpc-157.json | 1 + research/intents/cagrilintide.json | 1 + research/intents/dsip.json | 1 + research/intents/ghrp-6.json | 1 + research/intents/humanin.json | 1 + research/intents/ipamorelin.json | 1 + research/intents/melanotan-ii.json | 1 + research/intents/mots-c.json | 1 + research/intents/retatrutide.json | 1 + research/intents/selank.json | 1 + research/intents/semax.json | 1 + research/intents/ss-31.json | 1 + research/intents/survodutide.json | 1 + research/intents/tb-500.json | 1 + research/intents/tesamorelin.json | 1 + research/intents/tesofensine.json | 1 + research/intents/thymosin-alpha-1.json | 1 + scripts/test_calculators.mjs | 24 ++++++++++++++++++++++++ src/components/Calculators.astro | 16 ++++++++++++---- src/layouts/CompoundV2.astro | 17 +++++++++++++++-- 22 files changed, 70 insertions(+), 6 deletions(-) diff --git a/research/intents/aod-9604.json b/research/intents/aod-9604.json index b665144..ed3e282 100644 --- a/research/intents/aod-9604.json +++ b/research/intents/aod-9604.json @@ -530,6 +530,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/ara-290.json b/research/intents/ara-290.json index 0face7b..f39cc74 100644 --- a/research/intents/ara-290.json +++ b/research/intents/ara-290.json @@ -477,6 +477,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/bpc-157.json b/research/intents/bpc-157.json index b30a685..b09945f 100644 --- a/research/intents/bpc-157.json +++ b/research/intents/bpc-157.json @@ -416,6 +416,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/cagrilintide.json b/research/intents/cagrilintide.json index 8c1f6f8..ebebc5a 100644 --- a/research/intents/cagrilintide.json +++ b/research/intents/cagrilintide.json @@ -196,6 +196,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/dsip.json b/research/intents/dsip.json index 4323ad8..c742bfc 100644 --- a/research/intents/dsip.json +++ b/research/intents/dsip.json @@ -397,6 +397,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/ghrp-6.json b/research/intents/ghrp-6.json index 4855771..0cfa977 100644 --- a/research/intents/ghrp-6.json +++ b/research/intents/ghrp-6.json @@ -544,6 +544,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/humanin.json b/research/intents/humanin.json index eed9be9..e27d614 100644 --- a/research/intents/humanin.json +++ b/research/intents/humanin.json @@ -463,6 +463,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/ipamorelin.json b/research/intents/ipamorelin.json index 269120e..cd2efb0 100644 --- a/research/intents/ipamorelin.json +++ b/research/intents/ipamorelin.json @@ -304,6 +304,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/melanotan-ii.json b/research/intents/melanotan-ii.json index 2db0058..f3fa55f 100644 --- a/research/intents/melanotan-ii.json +++ b/research/intents/melanotan-ii.json @@ -543,6 +543,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/mots-c.json b/research/intents/mots-c.json index 7c5fb5c..e4a5168 100644 --- a/research/intents/mots-c.json +++ b/research/intents/mots-c.json @@ -209,6 +209,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/retatrutide.json b/research/intents/retatrutide.json index 6731552..1a7e458 100644 --- a/research/intents/retatrutide.json +++ b/research/intents/retatrutide.json @@ -295,6 +295,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "escalation", "adverse", "who", diff --git a/research/intents/selank.json b/research/intents/selank.json index eb46891..c72a8fe 100644 --- a/research/intents/selank.json +++ b/research/intents/selank.json @@ -194,6 +194,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/semax.json b/research/intents/semax.json index 63c3dd6..a11e956 100644 --- a/research/intents/semax.json +++ b/research/intents/semax.json @@ -209,6 +209,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/ss-31.json b/research/intents/ss-31.json index d406129..6fd4a2a 100644 --- a/research/intents/ss-31.json +++ b/research/intents/ss-31.json @@ -484,6 +484,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/survodutide.json b/research/intents/survodutide.json index 870c693..cacb67e 100644 --- a/research/intents/survodutide.json +++ b/research/intents/survodutide.json @@ -507,6 +507,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/tb-500.json b/research/intents/tb-500.json index 2f6fb45..c3b6a7a 100644 --- a/research/intents/tb-500.json +++ b/research/intents/tb-500.json @@ -210,6 +210,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/tesamorelin.json b/research/intents/tesamorelin.json index b4b53cb..58cf87c 100644 --- a/research/intents/tesamorelin.json +++ b/research/intents/tesamorelin.json @@ -241,6 +241,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/tesofensine.json b/research/intents/tesofensine.json index 89f8e81..4fed07d 100644 --- a/research/intents/tesofensine.json +++ b/research/intents/tesofensine.json @@ -487,6 +487,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/research/intents/thymosin-alpha-1.json b/research/intents/thymosin-alpha-1.json index 6a358d0..195bfd2 100644 --- a/research/intents/thymosin-alpha-1.json +++ b/research/intents/thymosin-alpha-1.json @@ -474,6 +474,7 @@ "evidence", "doses", "dosage-chart", + "calculator", "adverse", "who", "biomarkers", diff --git a/scripts/test_calculators.mjs b/scripts/test_calculators.mjs index ea04e4e..b76fa51 100644 --- a/scripts/test_calculators.mjs +++ b/scripts/test_calculators.mjs @@ -139,6 +139,30 @@ if (fs.existsSync(CALC_DIR)) { } } +// Each compound that has a calculator page also embeds the calculator on its +// own record and links to the page. The two must agree: a calculator page with +// no inbound link from its compound is an orphan, and a compound page whose +// embedded tool does not compute is the same defect as the empty tools were. +if (fs.existsSync(CALC_DIR)) { + for (const slug of fs.readdirSync(CALC_DIR, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name)) { + const file = path.join(DIST, 'compounds', slug, 'index.html'); + if (!fs.existsSync(file)) { fails.push(`compounds/${slug}: no compound page for calculator page`); continue; } + const html = fs.readFileSync(file, 'utf8'); + if (!html.includes(`href="/calculators/${slug}"`)) fails.push(`compounds/${slug}: no link to /calculators/${slug}`); + if (!/
/.test(html)) { fails.push(`compounds/${slug}: no calculator section`); continue; } + if (!/id="r-out"/.test(html)) { fails.push(`compounds/${slug}: calculator section has no output element`); continue; } + const m = html.match(/