Recipe 11 — Building a topical file

Goal

Build a file about a concept rather than a site: every fact the pool holds about “prompt caching” (or “cookies”, or “llms.txt”), from every source, filed under the subject’s child concepts, deduplicated, with disagreements kept visible. The result is a /t/<slug>/ family — llms.txt + llms-facts.txt + manifest.json — served beside the source-axis families and listed under ## Topics on the root index.

When not to use it

Steps

  1. Gather the pool. Any mix of units.jsonl, llms-facts.txt and reference.md files. The subject must be a concept-tree node; its children become the sections.

  2. Build. Assignment runs keyword → file-affinity → embedding centroid → ## Shared; --no-embed skips the centroid step and costs no embeddings.

    PYTHONPATH=scripts .venv/bin/python -m docset_refine topical \
      --from outputs/exports/docs.claude.com.llms/llms-facts.txt \
      --from outputs/exports/platform.openai.com.llms/llms-facts.txt \
      --from outputs/exports/openrouter.ai.llms/llms-facts.txt \
      --subject "prompt caching" \
      --out llms-topical/prompt-caching.llms/ \
      --base-url http://127.0.0.1:8788/t/prompt-caching \
      --register

    --register writes llmsFile onto the tree node so the node page and the MCP lookup know the file exists.

  3. Read the assignment. manifest.json records how many facts each section took and how many fell to ## Shared. A large ## Shared means the children are wrong or missing — a tree question, not a builder bug. Hand edits go in manifest.overrides, which survives a rebuild (generate, don’t edit).

  4. Test it as an agent would. The lint’s deterministic passes first, then the agent test: the optimizer hands a model the file and a bank of questions about the subject and counts how many it answers in at most two hops.

    .venv/bin/python scripts/llms_lint.py check llms-topical/prompt-caching.llms/ --json
    /ldo llms-topical/prompt-caching.llms/llms.txt --agent-test
  5. Serve. llms_serve.py picks the directory up at /t/prompt-caching/… and lists it under ## Topics on the root.

Expected output

llms.txt with an H1 naming the subject, a blockquote stating the source count and the pool, and one H2 per child concept, each link carrying its unit count; llms-facts.txt in the unit grammar with every line keeping its original source anchor — a fact from the OpenAI docs still points at platform.openai.com. Where two sources make competing claims the lines sit together under the same section; in this step neither is dropped, and the precedence ladder is the designed mechanism for choosing.

manifest.json for a three-source pool of a few thousand units typically shows most facts assigned by keyword, a minority by centroid, and a ## Shared in the low tens.

The agent test reports N/10 answered in ≤ 2 hops; the bar the optimizer uses is 8 for an index.

Cost

Estimated: the build is seconds for a few thousand units with --no-embed, or one embedding per unassigned unit without it (the centroid step embeds only what keyword and file-affinity did not place). The agent test is the only model spend — a bank of ten questions, each a short model call — estimated at a few tens of thousands of model tokens per run, to be replaced by the CI-measured figure when the runnable examples land.

Runnable in step 4 (playground).