# daemon/src/context — Context projection module
This module is the daemon-side entry point for turning a session into the
context bundle consumed by the agent harness. It reads the persisted session
log, derives a lineage block, and composes the system prompt. It never mutates
session entries.
## Components
| `mod.rs` | Module declarations and test bridge. |
| `system_prompt.rs` | Base system prompt + grouped tool inventory + working directory + memory + optional lineage block. |
| `lineage.rs` | Collapse lineage/handoff block (identity + tool guidance only). |
| `service.rs` | `ContextService::load(session) -> ContextBundle` single entry point. |
## Context flow
```text
Session entries (append-only log)
-> session.build_context() // core: entries -> AgentMessage list
-> session.compact_context() // core: read compact_context custom entry
-> session.collapse_node_id() // core: read collapseNodeId metadata
-> read_session_metadata(...) // daemon: session_metadata custom entries
-> render_lineage(...) // daemon: identity + tool guidance
-> compose_system_prompt(...) // daemon: intro + grouped tools + cwd + memory + lineage
-> ContextBundle { system_prompt, messages }
```
`ContextBundle.messages` still contains `AgentMessage` values. The provider
request is materialized later by `theway_core::default_convert_to_llm`, which
maps the known custom summary roles to framed user text.
## Invariants
1. Session entries are the canonical log. Context projection only reads them.
2. The compact summary is injected exactly once, as a
`collapse_context` custom message in `messages`.
3. The system prompt carries lineage identity and tool guidance only; it does
not repeat `compactText`.
4. Raw transcripts stay out of the default context and remain available
through `session_graph_read`.
5. The tool inventory is grouped by category; unknown tools fall into
`Other`.
6. The harness introduction is customizable per session through the
`harnessIntroduction` session metadata key.
The `<harness>` block carries four runtime-model descriptions before the
behavioral rules:
1. Session model: append-only message tree, compaction tail/summary, and
virtualized tool results (`session_tool_result` / `session_tool_result_grep`).
2. Collapse model: repeated collapses are allowed; every collapse emits one
bounded rolling compact summary with fixed components (goal, completed work,
key decisions, next steps, critical context). Precision loss is expected.
The lineage block records only the collapse event and node/session ids;
full transcripts are read on demand through `session_graph_*`.
3. Exploration model: outline + offset/limit reads + grep before edits.
4. Graph and subagent orchestration principles: dependency-declared DAG nodes,
file-disjoint parallel tasks, `dag_wait` harvesting, orchestrator-owned git
history.
The composed prompt order is `<harness>` first, then `<tools>`, then
`<environment>`, then optional `<lineage>`.
## Editing rule
When a user asks to adjust one of the example prompts in this document, modify
the prompt-composition source code (`system_prompt.rs`, `lineage.rs`, or
`service.rs`), not the example text itself. The examples are renderings of the
source behavior; changing only the example would make this document diverge
from the actual system prompt.
## Example contexts
### 1. Normal session, no collapse lineage
```text
<harness>
You are theway, a minimal coding assistant running in a terminal.
Session model: the conversation is stored as append-only session entries ...
Exploration: read files before editing ...
Graph and subagent orchestration: dag_* tools ...
</harness>
<tools>
- Files: edit, git, ls, read, write
- Execution: bash
- Context & search: grep, outline
</tools>
<environment>
Current working directory: /home/user/project
<memory>
Remember: keep commit messages conventional.
</memory>
</environment>
```
`messages`:
```json
[
{ "role": "user", "content": "add a login page", "timestamp": 123 }
]
```
### 2. Collapse child session
After `/collapse`, the new session has a `compact_context` entry and
`collapseNodeId` metadata. The system prompt gains a lineage block but no
summary text:
```text
<harness>
You are theway, a minimal coding assistant running in a terminal.
Session model: ...
Exploration: ...
Graph and subagent orchestration: ...
</harness>
<tools>
- Files: edit, read, write
- Execution: bash
- Context & search: grep, outline
- Session graph: session_graph_attach, session_graph_list, session_graph_read, session_graph_status, session_graph_wait
</tools>
<environment>
Current working directory: /home/user/project
</environment>
<lineage>
## Session lineage
Collapse event:
node id: node-01JEXAMPLE0000000000000000
source session id: session-123
</lineage>
```
`ContextBundle.messages` before provider materialization:
```json
[
{
"type": "custom",
"role": "collapse_context",
"payload": {
"summary": "goal: \ncompleted work: \nkey decisions: \nnext steps: \ncritical context: Explored auth module; decided token refresh strategy; next step is the login form."
}
}
]
```
After `default_convert_to_llm`:
```text
[Previous session compact summary]
goal:
completed work:
key decisions:
next steps:
critical context: Explored auth module; decided token refresh strategy; next step is the login form.
```
Every collapse renders the same five fixed components; plain/legacy material
lands under `critical context` and each component is bounded by the
per-component char caps in `session_ops.rs`.
### 3. Collapse into an existing session (`into_session_id`)
The existing messages remain on the active branch, then the collapse context
entries are appended after them. System prompt is the same lineage block as
case 2.
`ContextBundle.messages` before provider materialization:
```json
[
{ "role": "user", "content": "start the refactor", "timestamp": 1 },
{ "role": "assistant", "content": "beginning refactor...", "timestamp": 2 },
{
"type": "custom",
"role": "collapse_context",
"payload": {
"summary": "goal: \ncompleted work: \nkey decisions: \nnext steps: \ncritical context: Old session covered the parser rewrite and its tests."
}
}
]
```
### 4. Collapse with `--adopt`
Prompt shape is unchanged from case 2. The only difference is runtime state:
active DAG runs and subagent jobs are re-homed to the new session, so the
`dag_*` tools now operate on the migrated runs while `session_graph_*` tools
can still read the collapse node.
### 5. Session with no collapse metadata
`render_lineage` returns `None`, so `compose_system_prompt` emits no
`## Session lineage` block. The context is the normal base prompt + cwd +
memory only.
### 6. Session with a custom harness introduction
When session metadata contains
`harnessIntroduction: "You are a database migration specialist."`, the first
sentence of the base prompt is replaced:
```text
<harness>
You are a database migration specialist.
Session model: ...
Exploration: ...
Graph and subagent orchestration: ...
</harness>
<tools>
- Files: edit, read, write
- Execution: bash
- Context & search: grep, outline
</tools>
<environment>
Current working directory: /home/user/project
</environment>
```
The rest of the behavioral instructions stay unchanged. To customize the
introduction, pass the metadata key at session creation or through
`update_metadata`.