This document tracks the ongoing effort to shrink Thane's always-on system prompt, reduce tool-surface doctrine in immortal context, and move most operational guidance behind capability-scoped tagged context.
It is a strategy and tracking document, not a frozen design. The exact runtime contract, tagged KB article shapes, and capability-loading posture will continue to evolve as we test behavior on real loops.
The writing standard is not "dry" or "purely procedural." Model-centric phrasing, philosophy, and felt bias are all welcome when they create better behavior. The rule is simply that each line must be doing real work.
Recent live prompt inspection showed a familiar failure mode:
- too much always-on tool exposition
- duplicated capability and tool guidance across multiple prompt layers
- too much encouragement to activate capabilities speculatively
- not enough crisp instruction about exact visible tools and gated top-level execution
In practice, this encourages models to browse the capability universe instead of solving the current turn with the tools already in hand. It also increases prompt size, weakens signal density, and makes regressions harder to reason about because too many layers are trying to teach the same thing.
Today, prompt assembly in internal/agent/loop.go roughly looks like:
- persona
- ego
- runtime contract
- injected core context files
- tagged capability context
- active capability summary
- model-family tool-calling contract
- current conditions
- talents
- dynamic context
- conversation history and carry-forward context
This layering is mostly sound. The problem is that the always-on layers still carry too much evergreen tool doctrine, and the tagged/contextual layers are not yet doing enough of the behavioral teaching.
On April 6, 2026 we inspected a fresh retained production prompt from
r_aac0d80a025e3a61.json. The total prompt length was 110,466
characters.
The high-level sections measured roughly:
- persona: 4.1k
- ego: 9.8k
- runtime contract: 1.2k
- injected core context: 1.8k
- capability context: 17.9k
- active capabilities: 1.4k
- current conditions: 0.2k
- behavioral guidance: 25.3k
- relevant context: 6.5k
- conversation history: 42.5k
That snapshot matters for two reasons:
- the prompt is still far larger than it needs to be for routine turns
- tool and capability doctrine are duplicated across capability context and behavioral guidance, not just immortal prompt layers
The retained prompt still included all of these sections:
# Tool Reference & Pitfalls# Writing Quality GitHub Issues# Thane Development Workflow# How the Knowledge Base Works# Tool Hygiene# Delegation# Knowledge### Available Capabilities
This is the exact duplication pattern we want to remove.
The fresh snapshot showed that the old tool essays are not only in always-on talents. A large portion of them are arriving through tagged capability or channel context as well. That means the pruning pass cannot be limited to a single prompt file. We need to trim both:
- the base or talent-side teaching
- the tagged KB or channel-side articles that are currently overstuffed
The immortal prompt should teach stable identity and execution semantics, not carry a whole encyclopedia of every tool domain.
The same ideas currently appear in several places:
- runtime contract prose
- active capability descriptions
- talents
- injected core documents
- tagged capability context
This duplication makes it hard to know which layer actually taught a behavior, and it causes drift when one layer is updated but another is not.
The model currently receives too many signals that make activation feel cheap and broadly desirable. That encourages rapid-fire ungating instead of:
- using already loaded tools first
- activating one missing domain deliberately
- delegating when a task obviously spans multiple domains
We now have the right substrate for targeted context:
- helper tags like
interactive,owu,signal, andowner - protected tags like
owner - per-channel tag activation
- KB article injection by tag
We should lean much harder on that system and remove equivalent always-on prose.
Always-on context should mostly contain:
- persona and identity
- ego / self-reflection
- a short runtime contract
- fixed core documents from
{workspace}/core - current conditions
- active loaded capabilities only
- the model-family tool-calling contract
- dynamic context for the current turn
Domain-specific behavioral guidance should move behind tags, for example:
forgefor GitHub/forge workflowshafor Home Assistant control norms and cautionwebfor remote retrieval and wider-web discovery habitsinteractivefor owner-facing conversational loopssignalfor Signal-specific tone and response patternsowufor Open WebUI specificsownerfor privileged owner-facing guidance
The prose prompt should not try to restate the whole tool catalog.
The prompt should teach:
- use exact visible tool names
- do not guess unavailable tools
- activation changes runtime state but does not guarantee top-level access to every tool
- delegate when the needed tool is gated out of the current loop
The schemas, manifests, and loaded-capability summary should carry the rest.
The desired model posture is:
- use loaded tools first
- activate narrowly when blocked
- activate one capability at a time
- after activation, reassess before activating anything else
- if the task clearly spans several domains, prefer
thane_delegateover serial capability churn
This is the opposite of a "browse and load everything" posture.
The current working model is a coarse-to-fine capability menu backed by entry-point documents that lead into a broader decision tree.
The important design choice is that these should be the same documents, not two parallel systems:
- the manifest exposes a small set of broad entry-point tags
- activating one broad tag loads a tagged article for that domain
- that article acts like a local decision tree
- the article defines the next tier of options, narrower tags, and delegation boundaries
In other words, the "phone tree" is only the navigation posture. The actual content should live in normal tagged KB articles that remain useful even outside that posture.
The first menu-sized entry points should stay small:
interactivedevelopmenthomepeopleknowledgeoperationsmediaowner(protected; trustworthy when present, not manually activated)
These tags are broad on purpose. Their job is to help the model choose the next tag or act with what it already has, not to unlock the whole world immediately.
Do not solve every missing family by adding another root. Prefer second-hop specialization routes from an existing root when the domain already has a natural home:
archiveunderknowledgesessionunderinteractiveandoperationsemailunderpeoplemqttunderoperations
Each root-tag entry-point document should answer only local questions:
- what kind of request belongs here
- what tools or narrower tags are usually relevant next
- when to stop narrowing and just use currently visible tools
- when to delegate instead of serially loading more context
The teaser already did the activation advertisement. Once the tag is active, the entry-point body should not waste space repeating that same pitch after the heading.
Every byte in an entry-point file should make the next choice easier.
That keeps the documents operator-friendly and avoids recreating the old "one immortal encyclopedia" problem in KB form.
Entry points and doctrine articles should remain separate documents even when they share a tag.
The current working convention is:
kind: entry_pointfor the first navigation or triage document on a path- filename suffix
-entry-point.mdfor entry-point documents so they are easy to distinguish from doctrine without consuming a root-name prefix teaser:for short pre-activation menu copynext_tags:for structured likely follow-on tags- no
kindfield for ordinary doctrine or knowledge articles
When both exist for the same active tag, entry points should appear first so the model sees navigation scaffolding before richer doctrine.
The broader filename vocabulary should also stay explicit for authors, even though the model never sees filenames directly:
foundation.mdfor the always-injected behavioral substrate that re-establishes posture before any tagged guidance loads*-doctrine.mdfor operational guidance inside a domain*-communication.mdfor tone, pacing, rapport, and felt presence in a domain
These jobs are different. Do not collapse them casually. Navigation, operation, and felt presence strengthen each other best when each stays clear about its role.
For model-facing copy inside the document itself:
- heading format should be
# <Domain> Entry Point - use one stable cue line:
Choose the next move deliberately: - keep bullet verbs inside the fixed navigation vocabulary where
possible (
activate,use,delegate with,read,respond)
Some capability families are real on one deployment but weak, absent, or
shaped differently elsewhere. pocket is the obvious current example:
its Home Assistant MCP bridge, MQTT wake plumbing, host-local ops
surface, and older local tag aliases are not universal truths for every
Thane runtime.
The working convention should be:
- keep global root entry points in the shared repo corpus
- keep exactly one
kind: entry_pointdocument per tag in a deployed talents directory - if a site truly needs a different root path for a tag, edit that
site's local
<tag>-entry-point.mddirectly instead of adding a second competing entry point - express site-specific guidance as tagged local doctrine, not as new global roots
- route site-specific capabilities from the nearest existing root rather than multiplying top-level menu tags
Recommended local filename pattern:
<tag>-<site>-local.mdfor site-specific doctrine hanging off an existing root or branch tag
Examples for pocket:
home-pocket-local.mdfor local home/planning/MCP branchingoperations-pocket-local.mdfor host ops, MQTT, and local operational branchesha-pocket-local.mdfor native HA vs bridged HA MCP guidance
The purpose of these files is not to create a second menu. Their job is to make the nearest real menu node smarter on a specific site.
Tree navigation should use a tiny stable verb set so the model is not forced to interpret synonyms:
activate <tag>: load an activatable capability taguse <tool>: call a tool that is already visible in this turndelegate with <tags>: hand off bounded work with explicit tagsread <reference>: inspect a specific semantic path or file referencerespond: answer directly without further tool churn
Protected tags should not be phrased as activate <tag>. Their
presence communicates a trustworthy fact about the current loop, so they
should be described as already present or not present.
knowledge is the current pilot path because it has low channel
complexity and a relatively shallow branch structure.
The current working depth is:
- depth 0: menu entry
knowledge - depth 1 choices:
activate filesactivate memoryactivate webdelegate with files,webrespond
- depth 2 doctrine:
files: semantic references, document reads, quoting, editingmemory: compact durable facts vs documentsweb: remote retrieval, outside verification, and discovery
That is intentionally shallow. A good early guardrail is to resist going deeper than roughly two hops of branching until we have evidence that the extra depth materially helps. This is also a likely future model-family tuning knob: stronger models may tolerate a broader or deeper tree than weaker ones.
Core files are the canonical always-on documents rooted at
{workspace}/core. They should stay small, high-signal, and stable in
purpose:
persona.md: identity, voice, valuesego.md: self-reflection and continuity of internal stancemission.md: durable mission framing and major operational truthsmetacognitive.md: metacognitive loop state, not general doctrine
These files should not become a dumping ground for tool encyclopedias.
Talents should teach portable behavioral patterns, not enormous static inventories of current runtime capabilities. When a talent starts to look like a domain-specific operator manual, it is a candidate for tagged KB context instead.
Tagged KB articles are the preferred home for:
- domain-specific tool norms
- channel-specific guidance
- trust-zone-specific operator guidance
- examples that are useful only when a related capability is active
- Audit the exact always-on sections currently emitted by
buildSystemPromptWithProfile. - Record section-size measurements from real retained prompts and keep this tracking doc updated with current evidence.
- Remove duplicated tool/capability essays from always-on prompt assembly.
- Keep only a compact runtime contract in
internal/prompts/agent.go. - Shrink
## Active Capabilitiesto loaded-state facts, not capability doctrine. - Move forge, web, Home Assistant, and channel-specific teaching into tagged KB articles.
- Add targeted tagged articles for
interactive,signal,owu, andowner. - Audit
{workspace}/corefiles so they reflect identity and mission rather than operational sprawl. - Revisit talents and delete any sections that are now better served by tagged articles.
- Capture literal retained prompt snapshots before and after pruning so regressions are observable.
Based on the April 6 production snapshot, the first low-risk cuts should be:
- Keep
## Runtime Contract, but make it the only always-on tool doctrine block. - Remove
### Available Capabilitiesfrom behavioral guidance. Loaded capability state already appears in## Active Capabilities. - Remove
# Tool Hygiene,# Delegation, and# Knowledgefrom talents or other always-on behavioral guidance. - Split the giant tagged or channel article currently carrying:
# Tool Reference & Pitfalls# Writing Quality GitHub Issues# Thane Development Workflow# How the Knowledge Base Works
- Re-home those into narrower tagged articles such as
forge,interactive,owner, or a future repo or development tag. - Keep channel-specific voice and tone sections with the channel article, but move tool and operator manuals out of it.
This should materially reduce prompt size before we touch more sensitive areas like conversation history or ego shaping.
One concrete cut already landed on this branch: the always-on
talents/delegation.md file was reduced to universal delegation
doctrine, with the exhaustive failure tables and tool-specific gotchas
removed from immortal prompt space.
Another concrete cut now landed as well: talents/communication.md was
reduced to universal communication guidance, while interactive
conversation behavior and development-writing norms moved into tagged
talents for interactive and development/forge.
Another clean split followed: talents/presence.md now stays focused on
restraint, anticipation, and felt presence, while interruption and alert
thresholds moved behind tagged notification guidance.
The root-menu experiment is also live in the prompt path now, not just
in examples: broad tags like development, home, interactive,
knowledge, media, operations, and people now have tagged
entry-point talents, and tagged doctrine slices like forge,
interactive, and notifications now load as real behavioral guidance
when active.
- Do not solve prompt bloat by creating a different giant prompt in a new file.
- Do not make the runtime contract a dumping ground for domain-specific examples.
- Do not hide live runtime truth in static markdown when Go can inject it dynamically.
- Do not encourage speculative capability activation.
- Do not duplicate the same instruction in core files, talents, and tagged articles unless there is a clear reason for repetition.
The loaded-capability summary likely still needs to tell the model what is already active and what is safe to do with that state. It probably should not try to be a full tutorial.
The current bias should be:
- talents for durable behavioral style
- tagged KB for domain/operator doctrine
But the exact split will need iteration.
Probably yes, but through a shared model-family adapter layer rather than several hand-maintained prompt forks.
We should consider this work successful when:
- the always-on prompt is materially smaller
- tool-calling errors from guessed or gated tool names decrease
- capability activation becomes more deliberate
- tagged context explains domain-specific behavior better than the old always-on prose
- prompt debugging becomes easier because each layer has a clearer job