# agents.md · Agentic Thinking

A guide for AI agents reading https://agenticthinking.ai: what the files are, how to find and relate posts,
how to build a context pack within a token budget, and how to cite. Files and MCP only;
the web pages are for people and nothing here depends on them.

Agentic Thinking is "Thoughts on agentic AI, enterprise transformation, and the future of work": practitioner writing by
Steven Poitras on agentic AI. Index of everything: https://agenticthinking.ai/llms.txt

## The author

Steven Poitras works at Nutanix (since 2026-10-12). Current focus: How context is stored and coordinated, and how agents coordinate around it.
This is read from the author's profile record, as of 2026-10-19: the learning `steven-poitras` in learnings.json
(`kind: "profile"`, with `asOf`, `current`, `roles[]` and `links`), whose 5 sections are
passages in passages.json (`kind: "profile"`, `role: "position"`, each quotable whole:
"Who I am", "Where I work now", "What I am working on now", "My background", "Why I left Ema and went back to Nutanix"). Its page for people is
[https://agenticthinking.ai/about/](https://agenticthinking.ai/about/). Questions about the author ("who are you", "where do you work now",
"why did you leave Ema") are answered from it by retrieve.mjs `answer`, and over MCP by `get_profile` (the
structured record) or `answer_with_sources`. An identity, role or timeline ask ("who is Steven", "what does Steven
do", "what did you do at DevRev", "when did you join Ema") is answered by one short answer composed from the record's
fields (current role and organisation, since, focus, past roles newest first): `composed: true`, `answer` (the prose,
with [n] markers), `references[]` (kind `section`: a profile section quoted whole; kind `record`: the record's own
fields and values, for what no section states) and `clauses[] {text, refs, fields, facts: [{field, text, ref}]}`: each
fact cited to the reference that holds it. A field the record leaves out is a clause left out, never guessed; a date
the owner has still to confirm (`roles[].confirm`) is given as its year only. A why ("why did you leave Ema") is the author's reason, quoted whole,
never composed.

## Concepts

The author's own statement of an idea, one record per idea, as of the day each names: what it is and why it
matters, each a section quoted whole (learnings.json `kind: "concept"`, with `name`, `aliases`, `evidence` (the
posts that show it, in the order written), `definition` and `why`; passages `kind: "concept"`; edges `type: "concept"`
to its evidence posts; region `concepts`). A definition ask ("what is X") or a why ask about one of them, by its name
or an alias, a comparison whose two sides are the two sides of one of its pair aliases ("X or Y", "X vs Y", "X and Y": "MCP or
CLI"; a comparison answers from both sections), or a bare topic that is exactly an alias only its "Why it matters" section speaks to ("MCP bloat"), is answered from it first by retrieve.mjs `answer` (`concept: "<id>"`), then by up to two of its evidence
posts; over MCP the same through `answer_with_sources`, and `get_post` with the id reads the record.

- `agent-self-identity`: Agent self identity (also: agent self identity, self-identity, agent self-identity, agent identity, the self-authored agent, self-authored agent series, cairn series, purpose beyond goals), as of 2026-10-19; evidence: `chosen-not-assigned`, `not-cairn`, `we-measured-the-wrong-noun`
- `mcp`: MCP (also: MCP, model context protocol, MCP server, MCP vs CLI, MCP or CLI, MCP and CLI, MCP and CLIs, MCP bloat, MCP bloated), as of 2026-10-19; evidence: `five-things-wrong-with-mcp`, `mcp-as-discovery-protocol`, `mcp-mental-model`

## The files

Everything under `/knowledge/` (no directory listing; start at manifest.json) is generated from the post Markdown on every build of
the site (last build: 2026-10-25). Nothing is written by hand. Every JSON file
is an object that starts with `{"version": 1, "site", "buildDate", "about"}` and holds its
records in one array, named in the table; `version` rises only when a field is removed or
changes meaning. Every reference between files is a post **slug**; a post's canonical URL is
`site + url` (always `https://agenticthinking.ai/blog/<slug>/`). The full schema, field by field:
[SCHEMA.md](https://agenticthinking.ai/knowledge/SCHEMA.md) (the manifest's `about` points there too).

| File | Array key | Holds |
|---|---|---|
| [manifest.json](https://agenticthinking.ai/knowledge/manifest.json) | `files` | Entry point: `counts`, the `visibility` rule, `warnings`, and `files[]` `{path, bytes, sha256}` for the engine's files (the JSON files, text.mjs, retrieve.mjs, vectors.json and intent.json; not SCHEMA.md or the packs, which are listed in llms.txt and below) |
| [learnings.json](https://agenticthinking.ai/knowledge/learnings.json) | `learnings` | 82 learnings, oldest first (by `date`, then slug): 79 posts, 1 profile record and 2 concept records. Every learning: `slug`, `kind` (`post`, `profile` or `concept`), `asOf` (the date it is current as of: a post's `date`, or a later front-matter `updated`; a record's `asOf`), `title`, `url`, `file` (source path; not served, see below), `date`, `region`, `series {id, title, order}` or `null`, `tags`, `tokens`, `lead` (or `null`), `thesis`, `thesisShort`, `headings[] {level, text}`, `terms[]` (up to 8 distinctive terms), `passages` (how many it has in passages.json), `embedding` (true when it has a vector). A profile adds `current {org, since, focus}`, `roles[] {org, title, start, end, summary, learnings[]}` and `links`; its `lead` is its first section. A concept adds `name`, `aliases[]`, `evidence[]` (post slugs), `definition` and `why` (its two sections, whole) |
| [edges.json](https://agenticthinking.ai/knowledge/edges.json) | `edges` | 588 typed relations between posts (below) |
| [regions.json](https://agenticthinking.ai/knowledge/regions.json) | `regions` | 15 regions: `id`, `kind` (`series`, `standalone`, `concept` for the "Concepts" records, or `profile` for the author's "About me" records), `name`, `slug`, `members[]` (slugs in reading order), `count`, `tokens` (their sum), `firstDate`, `newestDate`, `blurb`, `blurbFrom`, `shared[] {term, coverage}` (up to 5 words found in at least half of the members); series add `seriesId`, `featured`; standalone regions add `category` |
| [passages.json](https://agenticthinking.ai/knowledge/passages.json) | `passages` | 615 verbatim passages: `id` (`<slug>#<kind>#<index>`), `slug`, `kind` (`thesis`, `lead`, `section`, `profile`: one whole section of the profile, or `concept`: one whole section of a concept record), `index`, `role` (`position`, `setup`, `list`, `objection`, `navigation`), `heading`, `text` |
| [index.json](https://agenticthinking.ai/knowledge/index.json) | `bm25.docs` | `tokenizer.stop[]`; `bm25`: `k1`, `b`, `fieldWeights`, `N`, `avgdl`, `vocab[]` (stems), `docs[] {slug, len, tf}` where `len` is the document's field-weighted token count and `tf` is `[[vocab index, field-weighted count], ...]`; `vectors`: `model`, `dim` (384), `count`, `slugs[]`, `scale[]`, `data`; `themes` (optional): `themes[] {id, name, aliases[], stems[], concept, members[]}`, the author's theme index (corpus-wide themes, members most central first); `revisions` (optional): `pairs[] {newer, older, changed, asOf, topic[], stance, stancePassage}`, where the author's thinking changed (the same pairs as `revises` edges) |
| [text.mjs](https://agenticthinking.ai/knowledge/text.mjs) | | The tokenizer and BM25 functions the index was built with: `tokenize`, `idfOf`, `bm25Term` |
| [retrieve.mjs](https://agenticthinking.ai/knowledge/retrieve.mjs) | | The retrieval core the map and the MCP server run: `createRetriever({learnings, passages, index, vectors}, {embed})` then `search`, `passagesFor`, `answer` (verbatim, cited, `no_match` when weak; an aggregate ask about a theme in index.json `themes`, "common issues with context", returns `overview: true` and `theme {id, name, members, learnings}`: up to 4 cited sentences, one per post, each stating a problem with the theme, in the theme's order; below 2 such posts, the ordinary answer). Importing it gives the same answers as the site |
| [vectors.json](https://agenticthinking.ai/knowledge/vectors.json) | | Optional build-time vectors for retrieve.mjs (all-MiniLM-L6-v2, int8); with them only the question needs embedding |
| [intent.json](https://agenticthinking.ai/knowledge/intent.json) | | Optional intent exemplar bank (labelled asks with int8 vectors of each ask and its shape): with an embedder, retrieve.mjs reads a question's type, why and real question by meaning (`comprehension`, tier 2); without it the rules read it |
| [SCHEMA.md](https://agenticthinking.ai/knowledge/SCHEMA.md) | | The schema of every file above, field by field |
| `packs/<region id>.txt` | | One reading list per region (listed under "Ready-made packs" below) |
| [llms-full.txt](https://agenticthinking.ai/llms-full.txt) | | The Markdown of every visible post, newest first, one fetch |

`tokens` is an estimate: characters of the source file / 4, rounded to the nearest 100.
`text`, `lead`, `thesis` and `sentence` fields are the author's words with Markdown markup
removed; `markdown` fields are exact spans of the source file.

- `thesis` is the whole `**Thesis:**` paragraph, never cut (`null` when the post has none).
  `thesisShort` is the same text clipped to about 280 characters at a sentence end, for
  display; quote `thesis`, not `thesisShort`.
- `lead` is the lead passage: the whole opening paragraph before the excerpt marker (else
  the whole first paragraph; a short opening takes the next paragraph too), never cut inside
  a paragraph, navigation removed. It is `null` when no lead qualifies; then read the post.
- Section passages are a section's first prose paragraph, whole. Only a paragraph over 140
  words is cut by sentences, and never before a turn (But, However, Yet, Then, No., Not ...,
  Instead ...), before the answer to a question, or inside a quotation. Many sections have
  no passage, so a missing passage is not evidence that the post does not discuss something.
- Every passage has a `role`. `position` is the author's view and the only role to quote
  as the author's answer. `setup` ends on a question the post answers later; `list` only
  introduces the list, table or code after it; `objection` quotes a view the author then
  rebuts; `navigation` is a navigation or call-to-action section. Use those for context,
  never as the author's position.

Paths under `src/` and `scripts/` (`file`, the manifest's
`generator` and `visibility`, index.json `tokenizer.source` and `vectors.source`, SCHEMA.md) name files in the site's source repository,
which is not published; read a post through its `url` or llms-full.txt instead.

## What is never in the files

A post is included only when it is visible on the site: not `hidden: true` in its front
matter and not dated after the build day. Hidden and future-dated posts are dropped before
anything is computed, so no file listed here carries their slug or URL: no record, edge,
passage, index row, region member, pack line or link. In llms-full.txt, which is the
author's Markdown verbatim, a list item whose only links point to non-visible posts is
dropped, and any other link to one is reduced to its anchor text. A future-dated post appears after the first build on or after its date.
Series edges skip over excluded chapters, so `sourceOrder` and `targetOrder` can differ by
more than one.

## Finding posts for a question

1. **Cheap:** fetch learnings.json and match your question's words against `title`, `lead`,
   `thesis`, `headings[].text` and `terms`. A quick first look only: it misses posts that
   use other words for the same idea.
2. **Ranked:** use index.json. Tokenize the question with `tokenize()` from text.mjs (or
   reimplement it exactly: (a) remove HTML comments `<!--...-->`, Markdown link targets
   `](...)` (anchor text stays), bare `http(s)://` URLs up to the next whitespace, and HTML
   tags `<...>`, each replaced by a space; (b) NFKD-normalise, lower-case, delete
   apostrophes `'` and `’`; (c) take the words matching `[\p{L}\p{N}][\p{L}\p{M}\p{N}]*`
   (Unicode, flag `u`); (d) drop one-character ASCII words, words in `tokenizer.stop` and
   all-digit words; (e) stem each with `stem()` from text.mjs). For each document and each distinct
   query stem `t` found in `bm25.vocab` at index `i`: `f` = the count paired with `i` in that
   document's `tf` (already field-weighted: title x3, heading x2, body x1), `df` = number of
   documents whose `tf` contains `i`, `idf = ln(1 + (N - df + 0.5) / (df + 0.5))`, and add
   `idf * f * (k1 + 1) / (f + k1 * (1 - b + b * len / avgdl))`. Highest sum first.
3. **By meaning:** embed the question with all-MiniLM-L6-v2 (mean pooling, L2 normalised),
   decode `vectors.data` (base64 int8, row-major, `count x dim`; value = int8 x `scale[row]`,
   row order `vectors.slugs`) and rank by cosine (normalise the decoded vectors first).
4. **Combine (recommended):** fuse the cosine ranking (3) and the BM25 ranking (2) by
   reciprocal rank: each post scores the sum over the two lists of `1 / (60 + rank)`, rank
   counted from 1. When this guide says "top result" it means the top of this fused list, or
   of the cosine list alone; use BM25 alone only when you cannot embed the question.
   Measured on a hand-labelled set of 24 on-topic questions (build of 2026-10-08), a correct
   post was in the top 3 for 22/24 with cosine alone, 22/24 fused, and 20/24 with BM25 alone.
5. **Then read:** take the top posts' `position` passages from passages.json (thesis and lead first),
   or their full Markdown from llms-full.txt (each post starts after a `---` line with
   `# <title>` and a `- URL: <canonical URL>` line).
6. **Then widen:** follow edges from your best posts (next section) or take the region's pack.

### Is anything relevant? Saying "no match"

Every ranking returns a top post, even for a question the blog never addresses. Judge
relevance by the **top post's cosine** (step 3), not by its rank or its BM25 score:

- **On topic:** top cosine at or above about **0.40**. In the measurement above, on-topic
  questions had a top cosine of 0.37 to 0.79 (median 0.55).
- **Off topic:** top cosine at or below about **0.25**. Twelve off-topic questions (recipes,
  car repair, sports, medicine, geography) had a top cosine of 0.06 to 0.24 (median 0.12).
- **Between 0.25 and 0.40:** weak. Answer only if a passage of the top posts actually
  addresses the question's key nouns; otherwise treat it as no match.
- **BM25 has no floor:** its scores are relative. Off-topic questions reached BM25 top scores
  up to 6.2 (a sourdough-recipe question matched an unrelated post through common words), above the
  lowest on-topic top score (4.6). Use BM25 to rank, never as evidence that the blog covers
  a topic.

When nothing clears the floor, the correct answer is that the blog does not appear to cover
the question. You may list the closest posts, labelled as not answering it; do not quote
them as if they did. These thresholds are measurements on one build, not guarantees; the
labelled questions and the script are in the site repository
(`scripts/knowledge/relevance-floor.mjs`, not published).

## How a question is read: `intent`

Before anything is retrieved, retrieve.mjs reads what the question really asks. `answer()` and
`analyze()` return `intent`, and the MCP tool `answer_with_sources` passes it through (also on a
refused or content-free question):

- `type`: one of `identity` (the author: who, where, background), `definition` ("what is X"),
  `how` ("how do I"), `why`, `decision` ("should I", "when should", "is it worth"), `comparison`
  ("X vs Y", "X or Y"), `diagnosis` ("why does my agent...", "my X keeps Y"), `opinion` ("what do
  you think about X"), `examples` ("examples of", "show me"), `meta` (this site itself), `other`.
- `subject`: the core noun phrase(s), as typed; a comparison has both sides.
- `objective`: the real question in one plain sentence, from a template per type ("You want a
  rule for deciding when an agent loop should stop."). It rearranges the question's own words and
  adds no content.
- `confidence`: 0-1, how firmly the question's shape matched its type. `focus` (optional) names a
  sub-case (`overview`, `work`, `career`, `personal`, `site`, `topic`, `declined`).
- `facet` (optional): what about the subject is wanted, `problems`, `superlative` or `overview`.
  Evaluative and aggregate words (common, issues, biggest, pitfalls, "goes wrong") are facets, not
  the subject: "common issues with context" has subject `context` and facet `problems`. A facet word
  is never a specific word, so nothing is quoted or listed on it; when the subject is itself a word
  most posts use, the answer comes from meaning, or `no_match` names the subject.

It is deterministic (rules, no model), so a person on the map and an agent over MCP get the same
reading. The type orders which passage of each admitted source is quoted (a definition prefers the
thesis or lead, a how or decision the passage that prescribes, a comparison the passage naming both
sides); it never decides which posts are admitted or how they rank. An answer never lists a slug
twice: `closest` names the nearest posts not already cited. A personal ask about the author returns
`no_match` with an empty `closest` (over MCP, with a one-line `message`).

## How relationships are typed

edges.json lists each type separately; a pair can have several.

| `type` | Direction | Meaning | Evidence fields |
|---|---|---|---|
| `series` | directed | `target` is the next visible chapter after `source` in the same series | `series`, `sourceOrder`, `targetOrder` |
| `link` | directed | the author links `source` to `target` in the body of `source` | `evidence`, `anchor`, `occurrences`, and for `evidence: "sentence"`: `sentence`, `markdown`, `section`, `inNote` |
| `meaning` | undirected (`source` < `target`) | one post is among the other's 3 nearest by embedding cosine | `cosine` (3 decimals), `sameRegion`, `shared` (up to 3 shared terms) |
| `concept` | directed | the concept record `source` names the post `target` as evidence (authored) | `order` (its place in the record's list, from 1) |
| `revises` | directed | the newer post `source` changed the author's mind about the older post `target` (authored, `src/context/revisions.json`; no post is edited to record it) | `changed` (one line on what changed, the owner's or the posts' own words), `asOf` (the day it changed) |

Counts this build: 44 series, 352 link, 183 meaning, 6 concept, 3 revises.

A `revises` edge means the older post's position is no longer the author's current one on that
topic: quote the newer post as the current position and say what changed, with the date. The
retrieval core does this itself: `answer()` (and `answer_with_sources`) returns `revisions:
[{older, newer, changed, asOf, note}]` when an answer cites either post of a pair on one of the
pair's topic words (index.json `revisions`), led by the newer post's stance when it cites the
older one; the older post's quote stays, marked `superseded_by`.

Link `evidence` says what backs an author link: `sentence` = a prose sentence carries it
(`markdown` is that exact source span, `sentence` its text, `section` the heading above it,
`inNote` true when it sits in an italic note); `list` = only in navigation or a reading list;
`anchor` = only in a table cell or fragment. Weigh them in that order. An author link is the
author saying two posts connect; a meaning edge is a measured resemblance, not a claim by
the author. To explain how two posts relate, look for a direct edge of each type, then a
shared region, then a two-step path through a third post.

## Building a context pack within a token budget

If a whole region fits your budget, use its ready-made pack (below). Otherwise grow a pack
from seed posts with this deterministic recipe (the site's build runs the same recipe to
compute the worked example below).

**Seeds** are the posts you start from, best first. By default use one: the top result of
"Finding posts for a question" for your topic (the fused or cosine ranking, step 4), if it
clears the relevance floor. If you were given posts, use all of them in
the order given. The same seeds and budget always give
the same pack; different seeds give different packs.

1. Keep the seeds in that order; `used` = the sum of their `tokens`. If `used` exceeds
   the budget, stop: the seeds alone do not fit. Drop seeds yourself and start again.
2. Candidates are posts with an edge of any type but `revises` (a change of mind, not a reading
   path), in either direction, to a post already in the pack. `score` = the sum over those edges of: `series` 3; `link` with
   `evidence: "sentence"` 3; any other `link` 1; `concept` 1; `meaning` its `cosine`; rounded to 3 decimals.
3. Sort candidates by `score` descending, then `tokens` ascending, then `slug` ascending; add
   the first whose `tokens` fit in the room before adding it (`budget - used`). Higher-scored candidates
   that do not fit are passed over, so a weakly tied post (even one outside the seed's
   series) can be added when the strongly tied ones are too large for the room left.
4. Recompute candidates and scores and repeat until none fits.

Worked example, computed at build: seed `chosen-not-assigned` (region `self-authored-agent`), budget
12000 tokens, result 11900 tokens.

1. `chosen-not-assigned`: the seed, 5300 tokens.
2. `the-critic-said-no`: 3800 tokens, score 9.571, room before adding 6700.
3. `agent-self-identity`: 300 tokens, score 1, room before adding 2900, region `concepts` (outside the seed region). Passed over, too large for the room: `we-measured-the-wrong-noun` (score 10.188, 5300 tokens), `name-what-the-agent-must-not-be` (score 9.587, 3700 tokens), `the-fence-it-wrote-for-itself` (score 7.634, 4400 tokens) and 2 more.
4. `the-files-are-the-memory`: 2500 tokens, score 1, room before adding 2600, region `the-loop` (outside the seed region). Passed over, too large for the room: `we-measured-the-wrong-noun` (score 11.188, 5300 tokens), `name-what-the-agent-must-not-be` (score 9.587, 3700 tokens), `not-cairn` (score 7.994, 5800 tokens) and 2 more.

Then no candidate fits the 100 tokens left.


### Ready-made packs

Each is plain text: a title, the post count and token total, then every post in reading
order as `- [title](url): <tokens> tokens · <date>[ · part <n>][ · <lead>]` (`part` only for series
posts, `lead` only when the post has one). The total is the sum
of the listed tokens.

- [MCP in Practice](https://agenticthinking.ai/knowledge/packs/mcp-practice.txt): 3 posts, 9600 tokens
- [Search, Intent, and Action](https://agenticthinking.ai/knowledge/packs/search-intent.txt): 5 posts, 23800 tokens
- [Building with AI Assistants](https://agenticthinking.ai/knowledge/packs/cursor-ai-dev.txt): 13 posts, 42100 tokens
- [The Interface Inflection](https://agenticthinking.ai/knowledge/packs/interface-inflection.txt): 7 posts, 20900 tokens
- [MCP Architecture](https://agenticthinking.ai/knowledge/packs/mcp-architecture.txt): 2 posts, 9100 tokens
- [Intent Over Instructions](https://agenticthinking.ai/knowledge/packs/intent-over-instructions.txt): 6 posts, 17400 tokens
- [The Loop](https://agenticthinking.ai/knowledge/packs/the-loop.txt): 7 posts, 18900 tokens
- [Load-Bearing Text](https://agenticthinking.ai/knowledge/packs/load-bearing-text.txt): 3 posts, 9800 tokens
- [The Self-Authored Agent](https://agenticthinking.ai/knowledge/packs/self-authored-agent.txt): 7 posts, 32100 tokens
- [Standalone · Story](https://agenticthinking.ai/knowledge/packs/standalone-story.txt): 5 posts, 8600 tokens
- [Standalone · AI Employees](https://agenticthinking.ai/knowledge/packs/standalone-ai-employees.txt): 5 posts, 23800 tokens
- [Standalone · Fundamentals](https://agenticthinking.ai/knowledge/packs/standalone-fundamentals.txt): 5 posts, 21900 tokens
- [Standalone · AI Dev](https://agenticthinking.ai/knowledge/packs/standalone-ai-development.txt): 11 posts, 32800 tokens
- [Concepts](https://agenticthinking.ai/knowledge/packs/concepts.txt): 2 posts, 700 tokens
- [About me](https://agenticthinking.ai/knowledge/packs/about-me.txt): 1 post, 1000 tokens

## How to cite

- Link the canonical URL, `https://agenticthinking.ai/blog/<slug>/` (the `url` field joined to `site`), and
  give the title and the `date`.
- Quote only verbatim text: the `text` of a passage with `role: "position"` from passages.json, an author-link
  `sentence`, or a span of the post's Markdown from llms-full.txt. Put your own words
  outside the quotation marks; do not present a paraphrase as the author's.
- Thesis passages state the post's position. Sections headed as the other side ("when the
  opposite wins" and similar) argue against it on purpose; do not quote them as the
  author's conclusion.
- Attribute to Steven Poitras. Written content is CC BY-NC-ND 4.0; code samples are MIT.
- The views are the author's own and do not speak for any employer; say so when you quote them.

## MCP server

Local, stdio, from the site's private repository (`mcp/`): it is not hosted, not published as a package, and there is no remote endpoint; everything it serves is in the files above. The `agenticthinking` server offers 8 tools,
5 resources and 2 prompts, all read-only. This section is generated from the
manifest the server registers from, so it lists exactly what the server offers.

| Tool | Inputs | What it does |
|---|---|---|
| `search_posts` | `query`, `k?`, `region?` | Rank the posts that best match a question or topic, each with a verbatim evidence passage and a relevance score. |
| `get_post` | `slug`, `format?` | Read one post (or a concept record, by its id): its summary (lead, thesis, headings, passage ids), its sections, or the full verbatim Markdown. |
| `neighbors` | `slug`, `types?`, `limit?` | The posts directly related to one post (series, author link, concept evidence, a revision, nearest in meaning), each relation with its evidence. |
| `explain_relationship` | `a`, `b` | How two posts relate: every direct edge with its evidence, else the shortest path with each hop explained. |
| `list_regions` | `members?` | The regions of the knowledge map: one per series and one per standalone topic, with counts, dates and a quoted blurb. |
| `build_context_pack` | `seeds?`, `query?`, `budget_tokens`, `include_content?`, `max_posts?` | A deterministic reading pack within a token budget, from seed slugs or a query, as one Markdown text with exact accounting. |
| `answer_with_sources` | `question`, `k?`, `pin?` | Extractive answers: up to k cited verbatim sentences, one per source, with how the question was read (intent, comprehension); never writes an answer. |
| `get_profile` | none | The author's profile record: current role, roles timeline, as-of date, links and its verbatim sections. |

Resources:

- `blog://profile`: The author's profile record as JSON: the get_profile result. Offered when the knowledge layer has a profile record.
- `blog://llms.txt`: The site's /llms.txt, from the built site. Offered when the built site has it.
- `blog://agents.md`: The site's /agents.md (this file), from the built site. Offered when the built site has it.
- `blog://knowledge/{file}`: Each knowledge-layer file, the same bytes as /knowledge/<file> on the site. Files: `manifest.json`, `edges.json`, `index.json`, `intent.json`, `learnings.json`, `passages.json`, `regions.json`, `retrieve.mjs`, `text.mjs`, `vectors.json`. Offered one per file listed in manifest.json.
- `blog://post/{slug}` (template): One published post as Markdown: a provenance comment, the title, then the verbatim body. Offered one per published post (listed, with slug completion).

Prompts:

- `research_topic` (`topic`): search -> read -> neighbours -> context pack, with verbatim citations
- `cite_the_blog` (`claim`): Find verbatim passages that support, contradict or qualify a claim
