epistemic redo

This commit is contained in:
Levi Neuwirth 2026-04-11 19:40:58 -04:00
parent b80fe2fee7
commit d113671e96
8 changed files with 111 additions and 118 deletions

View File

@ -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 # 0100 integer (%)
importance: 3 # 15 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 (`<details>`) |
|-------|----------------|------------------------|
| `status` | chip (always shown if present) | — |
| `confidence` | `72%` | — |
| `importance` | `●●●○○` | — |
| `evidence` | `●●○○○` | — |
| Field | Compact rows | Expanded `<dl>` |
|-------|-------------|------------------|
| *(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 0100. 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

View File

@ -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 0100 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 0100 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 15 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 (01)
-- Formula: raw = conf/100 · 0.6 + (ev 1)/4 · 0.4 (01)
-- 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

View File

@ -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 `<details>`: 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 `<dl>`: 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` (15) | `●●●○○` rendered in Haskell |
| `$evidence-dots$` | frontmatter `evidence` (15) | same |
| `$overall-score$` | computed from `confidence` + `evidence` | trust score: `conf/100 · 0.6 + (ev1)/4 · 0.4`, clamped 0100. 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 |

View File

@ -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 <details> 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
============================================================ */

View File

@ -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);
}

View File

@ -7,7 +7,7 @@
<div class="library-controls-options" role="group" aria-label="Sort order">
<button class="library-sort-btn" data-sort="date">date</button>
<button class="library-sort-btn" data-sort="title">title</button>
<button class="library-sort-btn" data-sort="score">epistemic effort</button>
<button class="library-sort-btn" data-sort="score">trust</button>
</div>
</div>

View File

@ -17,9 +17,21 @@
$for(affiliation-links)$$if(affiliation-url)$<a href="$affiliation-url$">$affiliation-name$</a>$else$$affiliation-name$$endif$$sep$ · $endfor$
</div>
$endif$
$if(status)$
<div class="meta-row meta-epistemic-strip" data-pagefind-ignore="all">
$if(overall-score)$<span class="ep-trust" title="Trust score: $overall-score$/100 (confidence × 0.6 + evidence × 0.4)"><span class="ep-score">$overall-score$%</span> trust</span>$endif$
<span class="ep-status">$status$</span>
$if(confidence)$<span class="ep-row" title="Confidence">$confidence$% confidence</span>$endif$
$if(importance-dots)$<span class="ep-row" title="Importance: $importance$/5"><span class="ep-dots">$importance-dots$</span> importance</span>$endif$
$if(evidence-dots)$<span class="ep-row" title="Evidence quality: $evidence$/5"><span class="ep-dots">$evidence-dots$</span> evidence quality</span>$endif$
$if(scope)$<span class="ep-row" title="Scope">$scope$ scope</span>$endif$
$if(novelty)$<span class="ep-row" title="Novelty">$novelty$ novelty</span>$endif$
$if(practicality)$<span class="ep-row" title="Practicality">$practicality$ practicality</span>$endif$
</div>
$endif$
<nav class="meta-row meta-pagelinks" aria-label="Page sections">
<a href="#version-history">History</a>
$if(status)$<a href="#epistemic">$if(overall-score)$<span class="ep-link-label">Epistemic</span><span class="meta-overall-score" title="Overall score: $overall-score$/100">$overall-score$%</span>$else$Epistemic$endif$</a>$endif$
$if(status)$<a href="#epistemic">Epistemic</a>$endif$
$if(bibliography)$<a href="#bibliography">Bibliography</a>$endif$
$if(backlinks)$<a href="#backlinks">Backlinks</a>$endif$
$if(repository)$<a href="$repository$">Repository</a>$endif$

View File

@ -35,23 +35,11 @@
$if(status)$
<div class="meta-footer-section meta-footer-epistemic" id="epistemic">
<h3><a href="/colophon.html#living-documents">Epistemic</a></h3>
<div class="ep-compact">
<span class="ep-status">$status$</span>
$if(confidence)$<span class="ep-confidence" title="Confidence">$confidence$%</span>$endif$
$if(importance-dots)$<span class="ep-dots" title="Importance: $importance$/5">$importance-dots$</span>$endif$
$if(evidence-dots)$<span class="ep-dots" title="Evidence quality: $evidence$/5">$evidence-dots$</span>$endif$
</div>
<details class="ep-details">
<summary class="ep-summary">detail</summary>
<dl class="ep-expanded">
<dt>Stability</dt><dd>$stability$</dd>
$if(scope)$<dt>Scope</dt><dd>$scope$</dd>$endif$
$if(novelty)$<dt>Novelty</dt><dd>$novelty$</dd>$endif$
$if(practicality)$<dt>Practicality</dt><dd>$practicality$</dd>$endif$
$if(last-reviewed)$<dt>Last reviewed</dt><dd>$last-reviewed$</dd>$endif$
$if(confidence-trend)$<dt>Confidence trend</dt><dd>$confidence-trend$</dd>$endif$
</dl>
</details>
<dl class="ep-expanded">
<dt>Stability</dt><dd>$stability$</dd>
$if(last-reviewed)$<dt>Last reviewed</dt><dd>$last-reviewed$</dd>$endif$
$if(confidence-trend)$<dt>Confidence trend</dt><dd>$confidence-trend$</dd>$endif$
</dl>
</div>
$endif$