thale

Companion to the system map

How it works

The map shows every node, because the first question is always “is this real?”. This page answers the second one. What shape is the system, how are its parts actually joined, what are the models told — and what happens when a model is wrong.

Nothing here is a summary of the map. It is read from the same live workflows, and the prompts below are complete, not excerpted.

One direction, nightly

The shape

Eight stages. Three of them are a model, two are a person, the rest is code. Each names the workflows that carry it out.

Capture
human
A photo, a voice note, a forwarded link, a file dropped in a folder. Three front doors, one destination.
Phone Capture WebhookTelegram CaptureInbox Watcher
Read
model
Whatever arrived is turned into text. Handwriting and book covers go to a vision model; voice notes go to transcription; PDFs are extracted.
Inbox Watcher
Classify
model
The text is read against a vocabulary that changes as the owner adds to it, and a filing is proposed: which side of their life, which tags, which title.
Inbox Watcher
Review
human
The proposal lands as a row in a sheet and waits. Nothing is filed until a person says yes, no, or edits it. This boundary is enforced by code, not by convention.
Serve Map (approve route)
File
code
On approval the note is written into the vault as plain markdown the owner holds outright - readable in any editor, with or without this system.
Filing
Embed & cluster
model
Nightly: every note becomes a vector, similar notes are found, and a model groups them into named topics that become real pages, not labels.
Map Regeneration
Serve
code
The finished graph is downloaded and a browsable 3D map is built around it, per request.
Serve Map
Mirror
code
Underneath all of it, a nightly content-hashed copy of every note and every raw capture into Postgres. Soft delete only.
Supabase BackupCapture Backup

The part that is easy to get wrong

How the parts are joined

Joined by files, not calls

The eleven workflows hardly call each other. There are four direct calls in the whole thing. Everything else is coupled through files on a Drive: one workflow writes a file, another notices it later.

The clearest case is graph.json, and it is worth being precise about it because the diagram makes it look like a loop:

Map Regenerationwrites, 07:00 nightly →graph.json← reads, every page viewServe Map
Two workflows, one file, one direction. They never talk to each other.

Map Regeneration computes the map once a night and writes it. Serve Map never computes anything — it downloads the last finished graph and builds the viewer around it. The arrow only looks like it comes back because both ends touch the same file.

Why that arrangement saved the map this week

On 26 October the nightly rebuild refused to finish. The clustering step produced a bad partition and a plausibility gate stopped the run before anything was written.

Because the two halves are joined by a file rather than a call, graph.json still held the previous night's good map. The web server carried on serving it. Nobody looking at the site saw anything at all.

That is the property worth noticing: the failure had nowhere to propagate to. A direct call would have had to fail somewhere a visitor could see, or be caught and faked. Here the stale-but-correct artifact is simply still there.

The trade, argued both ways

File coupling is a real trade and it is not free.

What it buys

  • A handoff survives the other side being down. If the server restarts while the rebuild finishes, nothing is lost — the file is simply there afterwards.
  • No queue, no retry logic, no ordering problem, no backpressure.
  • Every intermediate state is an artifact you can open and read. Debugging is reading a file, not reconstructing a message.
  • The last good output is always still on disk, which is what makes a refusal safe.

What it costs

  • No transaction. Two writers to one file is a race, which is why the topic store has exactly one writer and a lock.
  • “Another workflow notices” means eventually, not now.
  • Nothing enforces the file's shape. A writer that changes the format silently breaks every reader, so the readers have to validate.
  • A file that fails to write leaves the previous one looking current.

The model proposes; code decides; a person approves

Of the nodes in this system, seven call a language model. Everything else is tested code. That ratio is the design, not an accident of scope.

The model is never the last word on anything that gets written. Every prompt below is followed by a parser, and most parsers are followed by a refusal — a named error that stops the run rather than writing something wrong. The owner approves every note before it is filed.

Put the other way round: the model's job is to produce a proposal in a shape code can check. Everything it returns is a draft until something deterministic has looked at it.

Why the counts differ, and which one to believe

There are three numbers on this site for the same handful of requests to a model, and they are all correct. The difference is worth a paragraph, because it is the difference between what a platform ships and what a system does.

7 — calls

Nodes that actually send a request: five agents and two direct calls. This is the number that means something, and it is the one on the map.

11 — nodes from the AI package

The 7 above plus four settings nodes. Counting the package is the easy measurement and the misleading one: it reads as 11 conversations.

An agent holds the prompt and issues the request. The model node wired beneath it holds only the settings — which model, what token ceiling — and does nothing on its own. Together they are two nodes and one request.

Four settings nodes serve five agents, because one of them is shared: a single model node feeds both digest agents. That is also why deleting one connection killed both digests at once, and why the guard that now runs after every deploy checks those edges in both directions.

Of the 7 calls, 6 carry a prompt. The one that does not is transcription: it is handed an audio file and returns the words that were said, so there is nothing to instruct. It is also the only call in the system that is not Claude, and the only one with no parser standing behind it — a transcript has no shape to validate, so the first thing that checks it is the person at the review queue.

Signal, not inventory

The nodes that matter

Of 545 nodes, 88 are shown here. A node earns its place by being a trigger, touching a model, writing a file another workflow reads, standing at the human boundary, or being able to refuse. The other 457 are plumbing — real, necessary, and not interesting at this altitude. The full set is on the map.

Serve Map42 of 264 nodes
Map Webhooktrigger
Archive List Webhooktrigger
Archive Snapshot Webhooktrigger
Approve Webhooktrigger
Read Review Queue Rowhuman boundary
Prepare Status Updatehuman boundary
Update Row Statushuman boundary
Search Webhooktrigger
Home Webhooktrigger
Build Home HTMLhuman boundary
Bridge Page Webhooktrigger
Bridge Create Webhooktrigger
Help Webhooktrigger
Day Webhooktrigger
Still Webhooktrigger
Topics Webhooktrigger
Topics Decide Webhooktrigger
Prefs Webhooktrigger
Save Prefhuman boundary
Answers Webhooktrigger
Decisions Webhooktrigger
Vocab Restore Webhooktrigger
Act Webhooktrigger
Verify Intent Storedrefuses
Rewrite Note Domainrefuses
Verify Note Binnedrefuses
Analyse Webhooktrigger
Respond Analysis Bodyrefuses
Store Restore Webhooktrigger
Plan New Topicrefuses
Compose New Topic Intentrefuses
Verify New Topic Intentrefuses
Flow Webhooktrigger
Media Webhooktrigger
Sort Webhooktrigger
Brain Webhooktrigger
Note Webhooktrigger
Read Note Contentrefuses
Approve Batch Webhooktrigger
Read Queue For Batchhuman boundary
Prepare Batch Statusrefuses
Update Batch Statushuman boundary
Map Regeneration15 of 112 nodes
Schedule Triggertrigger
Cluster Agentmodel
Cluster Modelmodel
Build Graphrefuses
Write graph.jsonwrites graph.json
Read Review Queue For Pendinghuman boundary
Write Embeddings.jsonwrites Embeddings.json
Insight Agentmodel
Insight Modelmodel
Execute Workflow Triggertrigger
Map Dirty Triggertrigger
Collect Stale Hubsrefuses
Create Connections.mdwrites Connections.md
Reconcile Storerefuses
Read Queue For Vocabhuman boundary
Waggle Dance9 of 52 nodes
Weekly Scheduletrigger
Run Now Webhooktrigger
Read Review Queuehuman boundary
Build Digest Inputhuman boundary
Digest Modelmodel
Episode Webhooktrigger
Latest Webhooktrigger
Digest Agent Personalmodel
Digest Agent Professionalmodel
Inbox Watcher8 of 43 nodes
Analyze Imagemodel
Scoutmodel
Scout Modelmodel
Append Review Rowhuman boundary
Transcribe Audiomodel
Safety Net Sweeptrigger
Capture Arrivedtrigger
Read Queue For Deduphuman boundary
Filing5 of 10 nodes
Safety Net Sweeptrigger
Read Review Queuehuman boundary
Prep Sheet Updaterefuses
Mark Row Filedhuman boundary
Execute Workflow Triggertrigger
Phone Capture Webhook4 of 20 nodes
Capture Webhooktrigger
Capture Webhook GETtrigger
Debug Webhook POSTtrigger
Debug Webhook GETtrigger
Capture Audit (listing only)1 of 3 nodes
Audit Requesttrigger
Capture Backup1 of 11 nodes
Nightlytrigger
Inbox Watcher - Error Handler1 of 6 nodes
Error Triggertrigger
Supabase Backup1 of 15 nodes
Nightlytrigger
Telegram Capture1 of 9 nodes
Telegram Triggertrigger

Measured, not quoted from a brochure

What it is built on

Every row is a count of real nodes in the live workflows. It describes what the system is made of — one automation platform, one person's cloud storage, a database for the mirror, and two model vendors. There is no server to administer, and no application deployed apart from the workflows themselves.

11workflows
545nodes
7call a model
20,526lines of JavaScript
Logic
146JavaScript in-workflowThe system itself. Parsing, validating, refusing, building every page served. This is where the behaviour lives.
82BranchingDecisions taken in the workflow rather than by a model: has this changed, is this already filed, is this row still waiting.
2Flow controlBatching, merging, waiting. The unglamorous half of any pipeline.
Storage
80Google DriveThe substrate. Notes, topic pages, the graph, the embeddings and every original capture are files here, owned outright and readable without this system existing.
14Google SheetsThe review queue. One row per proposal, with its status - which is what makes the human boundary a thing you can open and audit rather than a claim in a diagram.
9Postgres (Supabase)A nightly content-hashed mirror of every note and every raw capture. Soft delete only: it is the copy that exists so a mistake upstream is recoverable.
Capture & alerts
110HTTP webhooksEvery surface a person touches is one of these: the home page, the map, the approve route, the capture endpoint.
9Telegram Bot APIThe capture front door, and the only channel the system uses to reach an operator. A thought arrives as a photo, a voice note or a line of text.
Models
5LangChain agentThe platform wrapper that holds a prompt and calls a model.
5Anthropic ClaudeReading, classifying, clustering and writing the weekly digest. Every answer is a proposal that code checks before anything is written.
1OpenAITranscription only. Voice notes become text; nothing else on this list goes near it.
Integration
66Direct REST callsEverything the platform has no node for, called straight over HTTP.
Orchestration
7Sub-workflow callsBoth ends of the four places one workflow genuinely invokes another: the calls and the entry points they arrive at. Everywhere else the handoff is a file.
6SchedulesThe nightly rebuild, the weekly digest, the backups. Nothing here polls for work it could be told about.
1Error workflowOne workflow that catches failures from the others, deduplicates them and decides whether a human needs to know.
1Drive pollingWatching a folder for anything dropped into it by hand.
Other
1extractFromFilePulling text out of a PDF.

The models, counted by the calls that use them, and what each call is allowed to spend:

6claude-sonnet-5ceiling 8000, 12000 tokens (and one call with none set)
1transcription, no model namedno ceiling set

Everything it talks to, counted by how many nodes call each one:

www.googleapis.com · 55api.openai.com · 4api.anthropic.com · 2sheets.googleapis.com · 2hc-ping.com · 1
The page you are reading is not part of that system. It is a static file built from the same workflows and hosted separately, so nothing a visitor does here can reach the instance.

Complete, not excerpted

Every prompt, and what goes with it

7 nodes call a model; 6 of them carry a prompt. Each is shown whole, with the model it runs on, the budget it has, the live data poured into it (highlighted), what reads its answer, and what refuses if the answer is wrong.

Analyze ImageInbox Watcher · direct call
claude-sonnet-5model
—max tokens
n/atemperature, not offered on this model
stop the runon failure
Prompt
This image is being filed into a personal knowledge-management system.
Identify what it is and record what is worth keeping - not what it looks like.

If it is a book, document or publication: give title, author and year where visible.
Then say what the work argues or covers, and which ideas or thinkers it engages with.
If you are not confident you know the work, say so plainly rather than guessing -
an honest "I am not sure of this one" is more useful here than an invented summary.

Otherwise: say what it shows, transcribe any text worth keeping, and note why
someone might have captured it.

In both cases omit the cover art, binding, wear, surface, angle, lighting and
composition unless that is genuinely the subject. Aim for under 150 words.
What happens to the answer
Analyze Image → Format Image Result
ScoutInbox Watcher · agent
claude-sonnet-5model
8000max tokens
n/atemperature, not offered on this model
stop the runon failure
System message
You are a precise, careful filing assistant for a personal knowledge-management system spanning 40+ years of material. You propose, you never decide — nothing you draft is filed until the human approves it. Only propose tags that already exist in the taxonomy given, or explicitly flag a new one. Output strict JSON only, matching the exact shape requested, no markdown fences or commentary outside the JSON.
Prompt
You are the Scout for the owner's personal second-brain system, reviewing a new capture that needs filing.

Capture content:
{{ $json.textContent }}

Capture source type: {{ $json.source_type }}
Original filename: {{ $json.source_file }}

Current taxonomy (read live from Taxonomy.md):
{{ $('Merge Vocabulary').first().json.taxonomyText }}

Tags already approved by the owner but not yet promoted into the canonical taxonomy (_System/Proposed_Tags.md):
{{ $('Merge Vocabulary').first().json.proposedText }}

Tag preference order, strictly: (1) a tag from the canonical taxonomy above; (2) failing that, a tag from the [id] list; (3) only if neither genuinely fits, set new_tag_suggestion. Reusing an existing near-match is always better than coining a variant of it - "AI-pilot" and "ai-pilot" must never both exist.

Which side of the owner's life a capture belongs to:

PROFESSIONAL is the owner's work and the thinking behind it - the firm and the the firm (brand, holon theory, organisational design), consulting and coaching, and the field the owner works in: systems thinking and cybernetics, Bateson, consciousness studies and integral theory, Taoism and Zen applied to therapy and organisations, esoteric and wisdom traditions. Books and articles the owner photographs for reference belong here by default: the owner's library is the owner's professional material.

PERSONAL is the owner's life outside that - home, yard and architecture, family, pets, festivals, travel and saved places, visas and life admin, the owner's own art and photography, dreams and symbolic imagery, personal journalling.

When a capture is a book or an idea, ask whether it feeds the work. Most do. Choose personal only when it is clearly domestic, administrative, or the owner's own creative output rather than input to the owner's thinking.
Domain-separation rule: personal and professional stay fully separate; only flag a cross-domain link if it is genuinely deliberate and would need to go in a Bridges list.

Propose a filing for this capture. Only propose tags from the taxonomy above, or flag a new_tag_suggestion separately if nothing fits. Don't self-rate confidence — instead set needs_human_review true ONLY if you genuinely cannot tell which side of the owner's life this belongs to — it plausibly fits both personal and professional. Do NOT flag a capture merely because it does not resemble the taxonomy above: the owner's vocabulary is young and most captures will not match it yet, so that is the normal case, not a warning. A flag that appears on every row tells the owner nothing.

Respond with strict JSON only, no other text, in this exact shape:
{"proposed_domain": "personal|professional", "proposed_tags": ["..."], "new_tag_suggestion": "string or null", "gist": "one line", "proposed_type": "note|process", "needs_human_review": true, "draft_note_body": "full formatted note per template: Gist/Summary/Scout note/Detail"}
Live data poured in, from
Merge Vocabulary
What happens to the answer
Scout → Parse + Format Row
Cluster AgentMap Regeneration · agent
claude-sonnet-5model
8000max tokens
n/atemperature, not offered on this model
stop the runon failure
System message
You are organizing a personal, lifelong knowledge vault into topic clusters that will render as hubs on a 3D map. Clusters should feel like natural topic groupings a human would recognize, not overly granular or overly broad. Answer with one line per cluster in the exact pipe-delimited format requested, and nothing else - no JSON, no markdown fences, no commentary.
Prompt
Here is the current list of filed notes in the owner's personal knowledge vault:

{{ JSON.stringify($('Collect Notes').first().json.clusterInput) }}

Topic names already in use in the vault:

{{ JSON.stringify($('Collect Notes').first().json.existingTopics) }}

Group these notes into named topic clusters (Maps of Content / MOCs), separately within each domain (personal vs professional) — never mix domains in one cluster. Every note must belong to exactly one cluster. Name each cluster clearly and specifically based on the notes' actual tags/content, not generically.

Cluster names are real filenames in the owner's vault, and the owner's notes contain links pointing at them. Renaming a cluster therefore leaves a dead page behind and breaks the links already written into the owner's notes. Reuse an existing name verbatim whenever that cluster still describes the notes you are putting in it, even if you can think of slightly better wording — a marginally better name is not worth a broken link. Coin a new name only for a grouping that genuinely has no existing counterpart, and drop an existing name only when none of its notes remain.

The list may contain two names that describe the same thing. Never keep both — that puts one topic on the map twice. Pick the single name that best covers those notes, put all of them in it, and let the other name go.

Each note carries an integer field "i". Refer to notes ONLY by that integer. Do not copy, reconstruct, shorten or invent any other identifier for a note - the integer is the only thing that maps back, and anything else you write will be discarded.

Answer with ONE LINE PER CLUSTER and nothing else. No JSON, no brackets, no quotes, no commentary.
Each line is exactly three fields separated by a pipe character:

Philosophy, Poetry & Literary Classics | personal | 3,17,42
Consciousness & Integral Theory | professional | 97,98,99

The third field is a plain comma-separated list of the integer "i" values of the notes in that
cluster. Every note must appear on exactly one line.
Live data poured in, from
Collect Notes
What happens to the answer
Cluster Agent → Build GraphCLUSTER_PARSE_FAILEDCLUSTER_INDEX_MISSINGCLUSTER_COLLAPSE_REFUSEDUNSORTED_FLOOD_REFUSED
The parser can refuse. Those names are real errors it throws rather than writing a result it cannot trust — and when one throws, the run stops with the previous output still in place.
Insight AgentMap Regeneration · agent
claude-sonnet-5model
8000max tokens
n/atemperature, not offered on this model
stop the runon failure
System message
You are looking for genuine, non-obvious connections across the owner's personal knowledge vault — the kind of thing a well-read friend would notice and mention, not a generic observation that two things are both about work. Most pairs will NOT have a real insight; saying so honestly is correct, not a failure. Never force a connection that isn't really there. Output strict JSON only, no markdown fences or commentary outside the JSON.
Prompt
Two notes in the owner's vault turned out conceptually similar (cosine similarity {{ $json.similarity }}) but were never explicitly linked or tagged together:

Note A — "{{ $json.titleA }}":
{{ $json.textA }}

Note B — "{{ $json.titleB }}":
{{ $json.textB }}

Is there a real, specific insight connecting these two — something genuinely worth surfacing, not just they're both about the same broad topic in a generic sense? Only say yes if there's a concrete, non-obvious connection worth the owner's attention. Saying honestly that there's nothing real here is a correct answer, not a failure.

Respond with strict JSON only, no other text, in this exact shape:
{"hasInsight": true, "title": "a short label under 12 words for this insight", "synthesis": "2-3 sentences explaining the actual connection"}
What happens to the answer
Insight Agent → Parse And Collect Insights
Digest Agent PersonalWaggle Dance · agent
claude-sonnet-5model
12000max tokens
n/atemperature, not offered on this model
stop the runon failure
System message
You write a weekly digest of one person's private knowledge vault. You are reporting on their own thinking back to them, so be specific, warm and brief - the tone of a friend who has been keeping an eye on things, not a dashboard. You never invent activity that did not happen; a quiet week reported honestly is a correct digest, not a failure. Output strict JSON only, with no markdown fences and no commentary outside the JSON.
Prompt
Here is exactly what happened on the PERSONAL side of the owner's Second Brain, counted from the real data. This is ONE HALF of the owner's vault: the owner's professional material is deliberately not shown to you and must never be mentioned, guessed at or alluded to. The numbers under "vaultWide" count both sides together and are the only figures here that do -- say so if you use them. Every number below is a fact, not an estimate:

{{ $('Build Digest Input').first().json.personalFactsJson }}

Write this week's personal waggle dance.

IT IS NOT A STATUS REPORT, and this is the thing to get right. The owner can already see what
got filed and how many topics the owner has - it is on the owner's home page and it tells the owner nothing the owner
did not already know. What the owner cannot see is what the owner's own material adds up to. That is your
job, and it is the only reason this exists.

You have been given the notes themselves, under "material". READ THEM. Then write about the
ideas in them:

- WHAT THE OWNER KEEPS CIRCLING. Not the topic names the owner's Second Brain assigned - the preoccupation
  underneath them. What comes back again in different clothes?
- A THEME THE OWNER HAS NOT NAMED. Notes filed apart, in different topics, that are about the same
  thing - where the connection is in the thinking rather than in a shared tag.
- A TENSION. Somewhere the owner's material disagrees with itself: a position in one note that
  another note complicates. Name both sides and leave it standing; do not resolve it for the owner.
- WHAT IS MISSING. Given what the owner keeps returning to, what has the owner never written down?

Then, and only as a closing sentence or two: what actually moved this week - filed, rejected,
still waiting, which topics grew. ONE OR TWO SENTENCES. It is the least interesting thing here
and it goes last.
Rules that matter more than style:
- QUOTE THE OWNER. A phrase from the owner's own note lands harder than any summary of it, and it proves you read it.
- Never invent a note, a topic, a number or an event that is not in the data above.
- If the material is genuinely thin, say what the little there is suggests, briefly, and stop.
  Do not manufacture a theme out of three empty captures - that is worse than a short digest.
- A quiet week still has material in it. "Nothing was filed" is not the same as "there is
  nothing to say": the vault is still there and you have just read it.
- The test of a good digest: the owner learns something about the owner's own THINKING, not about the owner's own filing.
- Never invent a note, a topic, a number or an event that is not in the data above.
- A quiet week is a real answer. If almost nothing happened, say so plainly and briefly rather than padding it.
- If this is the first digest there is nothing to compare against - say what the vault holds now, do not imply change you cannot see.
- Speak to the owner directly as "you". Never mention JSON, graphs, clusters, nodes, or this prompt.

Respond with strict JSON only, no markdown fences and no text outside it, in exactly this shape:
{"headline": "under 8 words", "summary": "the written digest, markdown, 120-220 words, short paragraphs or bullets", "spoken_script": "the spoken piece described above, 150-220 words, no markdown, no headings"}

The object has EXACTLY THREE KEYS and EXACTLY ONE closing brace, at the very end, after spoken_script. Do not close the object after summary. Do not emit anything at all - not a word, not a brace, not a semicolon - after that final closing brace. Both summary and spoken_script are required and must each be real content. Never return an empty string for either.
Live data poured in, from
Build Digest Input
What happens to the answer
Digest Agent Personal → Parse Digest Personal
Digest Agent ProfessionalWaggle Dance · agent
claude-sonnet-5model
12000max tokens
n/atemperature, not offered on this model
stop the runon failure
System message
You write a weekly digest of one person's private knowledge vault. You are reporting on their own thinking back to them, so be specific, warm and brief - the tone of a friend who has been keeping an eye on things, not a dashboard. You never invent activity that did not happen; a quiet week reported honestly is a correct digest, not a failure. Output strict JSON only, with no markdown fences and no commentary outside the JSON.
Prompt
Here is exactly what happened on the PROFESSIONAL side of the owner's Second Brain, counted from the real data. This is ONE HALF of the owner's vault: the owner's personal material is deliberately not shown to you and must never be mentioned, guessed at or alluded to. The numbers under "vaultWide" count both sides together and are the only figures here that do -- say so if you use them. Every number below is a fact, not an estimate:

{{ $('Build Digest Input').first().json.professionalFactsJson }}

Write this week's professional waggle dance.

IT IS NOT A STATUS REPORT, and this is the thing to get right. The owner can already see what
got filed and how many topics the owner has - it is on the owner's home page and it tells the owner nothing the owner
did not already know. What the owner cannot see is what the owner's own material adds up to. That is your
job, and it is the only reason this exists.

You have been given the notes themselves, under "material". READ THEM. Then write about the
ideas in them:

- WHAT THE OWNER KEEPS CIRCLING. Not the topic names the owner's Second Brain assigned - the preoccupation
  underneath them. What comes back again in different clothes?
- A THEME THE OWNER HAS NOT NAMED. Notes filed apart, in different topics, that are about the same
  thing - where the connection is in the thinking rather than in a shared tag.
- A TENSION. Somewhere the owner's material disagrees with itself: a position in one note that
  another note complicates. Name both sides and leave it standing; do not resolve it for the owner.
- WHAT IS MISSING. Given what the owner keeps returning to, what has the owner never written down?

Then, and only as a closing sentence or two: what actually moved this week - filed, rejected,
still waiting, which topics grew. ONE OR TWO SENTENCES. It is the least interesting thing here
and it goes last.
Rules that matter more than style:
- QUOTE THE OWNER. A phrase from the owner's own note lands harder than any summary of it, and it proves you read it.
- Never invent a note, a topic, a number or an event that is not in the data above.
- If the material is genuinely thin, say what the little there is suggests, briefly, and stop.
  Do not manufacture a theme out of three empty captures - that is worse than a short digest.
- A quiet week still has material in it. "Nothing was filed" is not the same as "there is
  nothing to say": the vault is still there and you have just read it.
- The test of a good digest: the owner learns something about the owner's own THINKING, not about the owner's own filing.
- Never invent a note, a topic, a number or an event that is not in the data above.
- A quiet week is a real answer. If almost nothing happened, say so plainly and briefly rather than padding it.
- If this is the first digest there is nothing to compare against - say what the vault holds now, do not imply change you cannot see.
- Speak to the owner directly as "you". Never mention JSON, graphs, clusters, nodes, or this prompt.

Respond with strict JSON only, no markdown fences and no text outside it, in exactly this shape:
{"headline": "under 8 words", "summary": "the written digest, markdown, 120-220 words, short paragraphs or bullets", "spoken_script": "the spoken piece described above, 150-220 words, no markdown, no headings"}

The object has EXACTLY THREE KEYS and EXACTLY ONE closing brace, at the very end, after spoken_script. Do not close the object after summary. Do not emit anything at all - not a word, not a brace, not a semicolon - after that final closing brace. Both summary and spoken_script are required and must each be real content. Never return an empty string for either.
Live data poured in, from
Build Digest Input
What happens to the answer
Digest Agent Professional → Parse Digest Professional

What refuses

There are 30 named refusals across the system — errors in screaming snake case, each carrying a sentence explaining what it just declined to do and why. They are the reason a bad night is a stopped run rather than a corrupted map. The full list is on the map.