Related Tips Cosine Score Badge Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Show each related tip's cosine similarity as a monospace cos θ = 0.512 badge on the Related Tips cards.

Architecture: The score already exists in topKNeighbors() but is dropped when _og/build-related.ts writes _og/related.json. We add a 3-decimal rounding helper in _lib/related-tips.ts, persist the rounded score in the JSON index, carry it through _config.ts onto data.relatedTips, and render it in _includes/tip.vto. The score field is optional everywhere, so an old-format index or a related: front-matter override still builds and simply shows no badge.

Tech Stack: Deno, Lume 3 (static site generator), Vento templates, Tailwind CSS. Tests use Deno's native test runner with jsr:@std/assert.

Spec: docs/superpowers/specs/2026-07-26-related-tips-score-badge-design.md

Global Constraints


Task 1: roundScore helper (TDD)

Files:

Interfaces:

Append to _lib/related-tips_test.ts:

Deno.test("roundScore - rounds to 3 decimals", () => {
  assertEquals(roundScore(0.51234), 0.512);
  assertEquals(roundScore(0.5), 0.5);
  assertEquals(roundScore(0.9995), 1);
  assertEquals(roundScore(0), 0);
  assertEquals(roundScore(-0.12345), -0.123);
});

Also add roundScore to the existing import block at the top of the test file:

import {
  buildEmbeddingText,
  contentHash,
  cosineSimilarity,
  roundScore,
  stripMarkdown,
  topKNeighbors,
} from "./related-tips.ts";

Run: deno task test Expected: FAIL — the module has no export named roundScore (a type/compile error counts as the failing state).

Append to _lib/related-tips.ts:

/**
 * Round a similarity score to 3 decimal places for the committed index —
 * enough precision to compare neighbors, stable across recomputes (no float
 * noise in diffs).
 */
export function roundScore(score: number): number {
  return Math.round(score * 1000) / 1000;
}

Run: deno task test Expected: PASS (all tests, including the new roundScore test).

git add _lib/related-tips.ts _lib/related-tips_test.ts
git commit -m "feat: add roundScore helper for related-tips index"

Task 2: Persist score in related.json

Files:

Interfaces:

In _og/build-related.ts, the import block from ../_lib/related-tips.ts (near the top, around line 13) currently includes topKNeighbors, type Neighbor, etc. Add roundScore to it:

import {
  buildEmbeddingText,
  contentHash,
  type Neighbor,
  roundScore,
  topKNeighbors,
} from "../_lib/related-tips.ts";

(Keep whatever names are already imported there — only add roundScore, alphabetically ordered.)

Replace this block in _og/build-related.ts:

const index: Record<string, { slug: string; tip_number: number }[]> = {};
for (const t of tips) {
  index[t.slug] = topKNeighbors(t.slug, vectors, TOP_K).map((n: Neighbor) => ({
    slug: n.slug,
    tip_number: tipNumberBySlug.get(n.slug)!,
  }));
}

with:

const index: Record<
  string,
  { slug: string; tip_number: number; score: number }[]
> = {};
for (const t of tips) {
  index[t.slug] = topKNeighbors(t.slug, vectors, TOP_K).map((n: Neighbor) => ({
    slug: n.slug,
    tip_number: tipNumberBySlug.get(n.slug)!,
    score: roundScore(n.score),
  }));
}

Run: deno task related Expected output includes: All tips are cache hits — no embedding call needed. followed by Wrote _og/related.json — <N> tips, top 3 related each.

If it instead reports cache misses / asks for OPENROUTER_API_KEY, STOP and report back — something is off with the cache and no content should have changed.

Run: head -12 _og/related.json Expected: every neighbor entry now has a score field with ≤3 decimals, e.g.:

{
  "algorithms-in-everyday-things": [
    {
      "slug": "think-about-yourself-in-1-2-5-10-50-years",
      "tip_number": 200,
      "score": 0.464
    },

(Exact score values will differ; only the shape matters.) Also confirm scores are descending within each tip's list — topKNeighbors sorts best-first.

Run: deno task test && deno task build Expected: tests PASS, build succeeds (the extra score field is ignored by the current _config.ts — that's fine until Task 3).

git add _og/build-related.ts _og/related.json
git commit -m "feat: persist cosine similarity scores in related.json"

Task 3: Carry score through _config.ts and render badge in tip.vto

Files:

Interfaces:

Change:

type RelatedRef = { slug: string; tip_number: number };

to:

type RelatedRef = { slug: string; tip_number: number; score?: number };

(score is optional so an old-format related.json still type-checks and builds.)

In the relatedTips resolution block, the fallback branch currently reads:

        : (relatedIndex[slug] ?? [])
          .map((r) => tipBySlug.get(r.slug))
          .filter((r): r is TipRef => !!r);

Replace it with:

        : (relatedIndex[slug] ?? [])
          .map((r) => {
            const tip = tipBySlug.get(r.slug);
            return tip ? { ...tip, score: r.score } : undefined;
          })
          .filter((r): r is TipRef & { score?: number } => !!r);

Leave the related: front-matter override branch (the Array.isArray(data.related) side) untouched — those refs carry no score by design.

The related card currently reads:

      <a href="{{ related.url }}" class="block p-4 border-4 border-black bg-white hover:bg-neo-yellow shadow-neo transition-colors">
        <div class="text-xs font-bold opacity-60 mb-1">Tip {{ related.tip_number }}</div>
        <div class="font-black">{{ related.title }}</div>
      </a>

Replace the header <div> so it becomes a flex row with the score right-aligned:

      <a href="{{ related.url }}" class="block p-4 border-4 border-black bg-white hover:bg-neo-yellow shadow-neo transition-colors">
        <div class="mb-1 flex items-baseline justify-between gap-2 text-xs opacity-60">
          <span class="font-bold">Tip {{ related.tip_number }}</span>
          {{ if related.score !== undefined }}
          <span class="font-mono">cos θ = {{ related.score.toFixed(3) }}</span>
          {{ /if }}
        </div>
        <div class="font-black">{{ related.title }}</div>
      </a>

Notes for the implementer:

Run: deno task test && deno task build Expected: tests PASS, build succeeds.

Then run: grep -o "cos θ = 0\.[0-9]*" _site/algorithms-in-everyday-things/index.html | head -3 Expected: three matches like cos θ = 0.464 (values will differ), matching the tip's entries in _og/related.json.

Run deno task serve in the background, open http://localhost:3000/algorithms-in-everyday-things/ (Lume's default port; use whatever port the serve output prints), and confirm on the Related Tips cards:

Stop the server when done. If a browser isn't available, a careful read of the built HTML plus the grep in Step 4 is an acceptable substitute — say so in the report.

git add _config.ts _includes/tip.vto
git commit -m "feat: show cosine similarity badge on Related Tips cards"

Task 4: Final verification

Files: none modified.

Interfaces: none.

Run: deno task test && deno task build Expected: all tests PASS; build succeeds with no new warnings.

Run: git status Expected: clean (all changes committed in Tasks 1–3). Do NOT push — the user pushes to main when they're ready to ship.