From d113671e96ea1a01f51115a299ec2c54e12f06ae Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Sat, 11 Apr 2026 19:40:58 -0400 Subject: [PATCH] epistemic redo --- WRITING.md | 27 +++++--- build/Contexts.hs | 32 ++++++--- spec.md | 3 +- static/css/components.css | 102 +++++++++++----------------- static/js/popups.js | 27 ++++---- templates/library.html | 2 +- templates/partials/metadata.html | 14 +++- templates/partials/page-footer.html | 22 ++---- 8 files changed, 111 insertions(+), 118 deletions(-) diff --git a/WRITING.md b/WRITING.md index b13bc6b..50e9fac 100644 --- a/WRITING.md +++ b/WRITING.md @@ -59,7 +59,7 @@ no-collapse: true # optional; disables collapsible h2/h3 sections js: scripts/my-widget.js # optional; per-page JS file (see Page scripts) # js: [scripts/a.js, scripts/b.js] # or a list -# Epistemic Effort — all optional; the entire section is hidden unless `status` is set +# Epistemic profile — all optional; the entire section is hidden unless `status` is set status: "Working model" # Draft | Working model | Durable | Refined | Superseded | Deprecated confidence: 72 # 0–100 integer (%) importance: 3 # 1–5 integer (rendered as filled/empty dots ●●●○○) @@ -648,19 +648,26 @@ copies all JS files from `content/` to `_site/` automatically. The epistemic footer section appears when `status` is set. All other fields are optional and are shown or hidden independently. -| Field | Compact display | Expanded (`
`) | -|-------|----------------|------------------------| -| `status` | chip (always shown if present) | — | -| `confidence` | `72%` | — | -| `importance` | `●●●○○` | — | -| `evidence` | `●●○○○` | — | +| Field | Compact rows | Expanded `
` | +|-------|-------------|------------------| +| *(trust score)* | bordered chip + small-caps "trust" label on the primary row | — | +| `status` | small-caps chip on the primary row, alongside the trust chip | — | +| `confidence` | `· 72% confidence` | — | +| `importance` | `· ●●●○○ importance` | — | +| `evidence` | `· ●●○○○ evidence quality` | — | +| `scope` | `· average scope` (if set) | — | +| `novelty` | `· moderate novelty` (if set) | — | +| `practicality` | `· moderate practicality` (if set) | — | | `stability` | — | auto-computed from git history | -| `scope` | — | if set | -| `novelty` | — | if set | -| `practicality` | — | if set | | `last-reviewed` | — | most recent commit date | | `confidence-trend` | — | ↑/↓/→ from last two `confidence-history` entries | +The **trust score** is auto-computed as `confidence × 0.6 + ((evidence − 1) / 4) × 0.4`, +clamped 0–100. It is deliberately narrow: it answers "how much should you trust the +central claim?" and nothing else. Importance, scope, novelty, and practicality are +*not* folded in — they are orientation signals shown alongside the chip so a high +trust score on a personal essay cannot be misread as broad significance. + **Stability** is auto-computed from `git log --follow` at every build. The heuristic: ≤1 commits or age <14 days → *volatile*; ≤5 commits and age <90 days → *revising*; ≤15 commits or age <365 days → *fairly stable*; ≤30 diff --git a/build/Contexts.hs b/build/Contexts.hs index bc18d42..151ed41 100644 --- a/build/Contexts.hs +++ b/build/Contexts.hs @@ -306,13 +306,25 @@ confidenceTrendField = field "confidence-trend" $ \item -> do lastTwo [a, b] = Just (a, b) lastTwo (_ : rest) = lastTwo rest --- | @$overall-score$@: weighted composite of confidence (50 %), --- evidence quality (30 %), and importance (20 %), expressed as an --- integer on a 0–100 scale. --- Returns @noResult@ when any contributing field is absent, so +-- | @$overall-score$@: weighted composite of confidence (60 %) and +-- evidence quality (40 %), expressed as an integer on a 0–100 scale. +-- +-- Importance is intentionally excluded from the score: it answers +-- "should you read this?", not "should you trust it?", and folding +-- the two together inflated the number and muddied its meaning. +-- It still appears in the footer as an independent orientation +-- signal — just not as a credibility input. +-- +-- The 1–5 evidence scale is rescaled as @(ev − 1) / 4@ rather than +-- plain @ev / 5@. The naive form left a hidden +6 floor (since +-- @1/5 = 0.2@) and a midpoint of 0.6 instead of 0.5; the rescale +-- makes evidence=1 contribute zero and evidence=3 contribute exactly +-- half, so a "true midpoint" entry (conf=50, ev=3) lands on 50. +-- +-- Returns @noResult@ when confidence or evidence is absent, so -- @$if(overall-score)$@ guards the template safely. -- --- Formula: raw = conf/100·0.5 + ev/5·0.3 + imp/5·0.2 (0–1) +-- Formula: raw = conf/100 · 0.6 + (ev − 1)/4 · 0.4 (0–1) -- score = clamp₀₋₁₀₀(round(raw · 100)) overallScoreField :: Context String overallScoreField = field "overall-score" $ \item -> do @@ -320,16 +332,14 @@ overallScoreField = field "overall-score" $ \item -> do let readInt s = readMaybe s :: Maybe Int case ( readInt =<< lookupString "confidence" meta , readInt =<< lookupString "evidence" meta - , readInt =<< lookupString "importance" meta ) of - (Just conf, Just ev, Just imp) -> + (Just conf, Just ev) -> let raw :: Double - raw = fromIntegral conf / 100.0 * 0.5 - + fromIntegral ev / 5.0 * 0.3 - + fromIntegral imp / 5.0 * 0.2 + raw = fromIntegral conf / 100.0 * 0.6 + + fromIntegral (ev - 1) / 4.0 * 0.4 score = max 0 (min 100 (round (raw * 100.0) :: Int)) in return (show score) - _ -> fail "overall-score: confidence, evidence, or importance not set" + _ -> fail "overall-score: confidence or evidence not set" -- | All epistemic context fields composed. epistemicCtx :: Context String diff --git a/spec.md b/spec.md index 79929db..7e96da4 100644 --- a/spec.md +++ b/spec.md @@ -145,7 +145,7 @@ Auto-derived at build time: `stability` (from `git log --follow`), `last-reviewe **Bottom metadata footer:** - **Version history** — three-tier priority: (1) frontmatter `history` list with authored notes → (2) git log dates (date-only) → (3) `date-created` / `date-modified` fallback. `make build` auto-commits `content/` before building, keeping git history current. -- **Epistemic** (if `status` set) — compact: status chip · confidence % · importance dots · evidence dots; expanded `
`: stability · scope · novelty · practicality · last reviewed · confidence trend +- **Epistemic** (if `status` set) — primary row: trust-score chip + status; labeled rows beneath: confidence · importance · evidence · scope · novelty · practicality (each shown only if its frontmatter field is set); always-visible `
`: stability · last reviewed · confidence trend - **Bibliography** — formatted citations + Further Reading - **Backlinks** — auto-generated; each entry shows source title (link) + collapsible context paragraph @@ -648,6 +648,7 @@ Implemented across `build/Stability.hs`, `build/Contexts.hs`, `templates/partial | `$confidence$` | frontmatter `confidence` | via `defaultContext` | | `$importance-dots$` | frontmatter `importance` (1–5) | `●●●○○` rendered in Haskell | | `$evidence-dots$` | frontmatter `evidence` (1–5) | same | +| `$overall-score$` | computed from `confidence` + `evidence` | trust score: `conf/100 · 0.6 + (ev−1)/4 · 0.4`, clamped 0–100. Importance/scope/novelty/practicality intentionally excluded — see colophon "Living Documents" for the rationale. | | `$confidence-trend$` | frontmatter `confidence-history` list | ↑ / ↓ / → from last two entries | | `$stability$` | auto-computed via `git log --follow` | always resolves; never fails | | `$last-reviewed$` | most recent commit date | formatted "%-d %B %Y"; `noResult` if no commits | diff --git a/static/css/components.css b/static/css/components.css index 0c7d588..2bfca98 100644 --- a/static/css/components.css +++ b/static/css/components.css @@ -627,15 +627,6 @@ nav.site-nav { text-decoration: underline; text-underline-offset: 2px; } -/* When the score box is present, suppress the link-level underline and - restore it only on the label text — leaves the gap and box undecorated. */ -.meta-pagelinks a:has(.meta-overall-score):hover { - text-decoration: none; -} -.meta-pagelinks a:has(.meta-overall-score):hover .ep-link-label { - text-decoration: underline; - text-underline-offset: 2px; -} .meta-pagelinks a + a::before { content: " · "; color: var(--border); @@ -644,6 +635,19 @@ nav.site-nav { white-space: pre; } +/* Top-of-page parallel tag strip — surfaces the full epistemic profile + inline so a reader sees status, trust score, and every orientation + axis at a glance, without scrolling to the footer. The trust chip + anchors the left edge; status sits next to it; the labeled rows + follow with their leading centerdot separators. Wraps freely. */ +.meta-epistemic-strip { + display: flex; + flex-wrap: wrap; + align-items: baseline; + justify-content: center; + gap: 0.35rem 0.55rem; +} + /* ============================================================ BOTTOM METADATA FOOTER @@ -808,15 +812,6 @@ details[open] > .backlink-summary::before { with an expandable
for the full profile. ============================================================ */ -/* Compact row — chips laid out inline */ -.ep-compact { - display: flex; - flex-wrap: wrap; - align-items: baseline; - gap: 0.35rem 0.55rem; - margin-bottom: 0.5rem; -} - /* Status chip */ .ep-status { font-family: var(--font-sans); @@ -826,15 +821,18 @@ details[open] > .backlink-summary::before { color: var(--text-muted); } -/* Confidence percentage */ -.ep-confidence { +/* Every labeled row beneath the primary line shares one class so new + epistemic fields can be added in the template without touching CSS. + Used by confidence, importance, evidence, scope, novelty, practicality. */ +.ep-row { font-family: var(--font-sans); font-size: 0.72rem; color: var(--text-faint); } -.ep-confidence::before { content: "·\00a0"; color: var(--border); } +.ep-row::before { content: "·\00a0"; color: var(--border); } -/* Dot scale (importance / evidence) */ +/* Dot scale (importance / evidence) — only ever used nested inside an + .ep-row wrapper, which already supplies the centerdot separator. */ .ep-dots { font-size: 0.55rem; letter-spacing: 0.12em; @@ -842,34 +840,30 @@ details[open] > .backlink-summary::before { line-height: 1; vertical-align: 0.15em; } -.ep-dots::before { content: "·\00a0"; color: var(--border); font-size: 0.72rem; vertical-align: 0; } +.ep-row .ep-dots::before { content: none; } -/* Expandable detail block */ -.ep-details { - margin-top: 0.1rem; -} - -.ep-summary { - font-size: 0.68rem; +/* Trust chip on the primary row — bordered numeric chip plus a small-caps + "trust" label, mirroring .ep-status typography so the two sit together. */ +.ep-trust { font-family: var(--font-sans); - color: var(--text-faint); - cursor: pointer; - list-style: none; - user-select: none; + font-size: 0.72rem; + font-variant-caps: all-small-caps; + letter-spacing: 0.05em; + color: var(--text-muted); } - -.ep-summary::-webkit-details-marker { display: none; } - -.ep-summary::before { - content: "▸\00a0"; - font-size: 0.6em; - vertical-align: 0.1em; +.ep-score { + font-family: var(--font-sans); + font-size: 0.72rem; + font-variant-caps: normal; + font-variant-numeric: tabular-nums; + letter-spacing: 0.03em; + color: var(--text-muted); + border: 1px solid var(--border-muted); + border-radius: 2px; + padding: 0.05em 0.5em; + margin-right: 0.25em; } -details[open] > .ep-summary::before { content: "▾\00a0"; } - -.ep-summary:hover { color: var(--text-muted); } - /* Definition list for expanded profile */ .ep-expanded { display: grid; @@ -894,24 +888,6 @@ details[open] > .ep-summary::before { content: "▾\00a0"; } } -/* Overall score percentage in the top metadata nav (adjacent to Epistemic link). - Renders as a compact numeric chip: "72%" in small-caps sans, separated from - the link text by a faint centerdot. */ -.meta-overall-score { - font-family: var(--font-sans); - font-size: 0.68rem; - font-variant-numeric: tabular-nums; - letter-spacing: 0.03em; - color: var(--text-muted); - display: inline-block; - text-decoration: none; - border: 1px solid var(--border-muted); - border-radius: 2px; - padding: 0.05em 0.5em; - margin-left: 0.5em; - vertical-align: 0.05em; -} - /* ============================================================ PAGINATION NAV ============================================================ */ diff --git a/static/js/popups.js b/static/js/popups.js index ab43c84..86f3ea5 100644 --- a/static/js/popups.js +++ b/static/js/popups.js @@ -641,9 +641,12 @@ return html; } - /* Epistemic jump link — reads the #epistemic section already in the DOM - and renders a compact summary: status/confidence/dots + expanded DL. - All ep-* classes are already styled via components.css. + /* Epistemic jump link — pulls the parallel-tag strip from the top of + the page (which holds the author-declared orientation tags) and + the expanded DL from the #epistemic footer section (which holds + the git-derived stability/last-reviewed/trend). The popup combines + both so a reader hovering the link mid-page sees the full profile + without scrolling. Returns a DocumentFragment instead of an HTML string so the popup receives cloned nodes (defense in depth — if a future change ever @@ -651,25 +654,21 @@ would still see exactly the same already-rendered DOM rather than a re-parsed string). */ function epistemicContent() { - var section = document.getElementById('epistemic'); - if (!section) return Promise.resolve(null); - var wrap = document.createElement('div'); wrap.className = 'popup-epistemic'; - var compact = section.querySelector('.ep-compact'); - if (compact) { - var compactClone = compact.cloneNode(true); - wrap.appendChild(compactClone); + var strip = document.querySelector('.meta-epistemic-strip'); + if (strip) { + wrap.appendChild(strip.cloneNode(true)); } - var expanded = section.querySelector('.ep-expanded'); + var section = document.getElementById('epistemic'); + var expanded = section ? section.querySelector('.ep-expanded') : null; if (expanded) { - var expandedClone = expanded.cloneNode(true); - wrap.appendChild(expandedClone); + wrap.appendChild(expanded.cloneNode(true)); } - if (!compact && !expanded) return Promise.resolve(null); + if (!strip && !expanded) return Promise.resolve(null); return Promise.resolve(wrap); } diff --git a/templates/library.html b/templates/library.html index e9d9435..afe30f1 100644 --- a/templates/library.html +++ b/templates/library.html @@ -7,7 +7,7 @@
- +
diff --git a/templates/partials/metadata.html b/templates/partials/metadata.html index 62a1121..2211728 100644 --- a/templates/partials/metadata.html +++ b/templates/partials/metadata.html @@ -17,9 +17,21 @@ $for(affiliation-links)$$if(affiliation-url)$$affiliation-name$$else$$affiliation-name$$endif$$sep$ · $endfor$ $endif$ + $if(status)$ +
+ $if(overall-score)$$overall-score$% trust$endif$ + $status$ + $if(confidence)$$confidence$% confidence$endif$ + $if(importance-dots)$$importance-dots$ importance$endif$ + $if(evidence-dots)$$evidence-dots$ evidence quality$endif$ + $if(scope)$$scope$ scope$endif$ + $if(novelty)$$novelty$ novelty$endif$ + $if(practicality)$$practicality$ practicality$endif$ +
+ $endif$