throughLine-design-v2.md
throughLine-design-v2.md
v2.1, October 17, 2026
Supersedes v2.0 (earlier the same day), v1.0 (April 2026) and the July 2026 plan for in-session compression. v2.1 incorporates the results of the M0 smoke tests and the decision that throughLine should be enabled everywhere and cost nothing until it is needed.
A long problem outlives the context window. Over many turns and sessions the model loses the thread: early decisions, exact figures, sources and the original goal fade first, and the model starts re-researching what it already settled or contradicting itself.
Compression does not fix this on its own. context-compressor keeps a long conversation running by summarizing older turns, but every compaction is lossy, and once its summaries pass their budget the oldest are merged into denser ones. Its summarizer also sorts everything into task sections, so anything that is not a task (dates, what was said, how it was said) is the first to go. Its own benchmark on Qwen 3.8 27B recalled 2 of 3 seeded facts on the research fixture after a single round. A conversation can run indefinitely this way; the substance inside it cannot stay intact.
throughLine does two things, both outside the conversation the model sees:
The conversation becomes disposable for a problem, and compression becomes lossy only in what the model sees at once, not in what it can find.
Tested on LM Studio 0.4.26 with Qwen 3.8 27B, context-compressor (revision 27), simpleSearch, simpleMemory and rag-v1 enabled.
| Question | Result | Consequence for the design |
|---|---|---|
| Does text a preprocessor adds persist in chat history? | Yes. The stored message includes it and it is sent on every later turn. The chat window shows only what the user typed. | Old digests accumulate, so the per-turn digest must be small and the full view shown only when needed. |
| Do throughLine and context-compressor coexist? | Yes. Both ran on every turn, and stored history stayed complete after the compressor ran. | No change. Recall can rely on the stored history being whole. |
| Do two prompt preprocessors chain? | Yes. throughLine and rag-v1 both ran on an attachment turn and the answer was correct. | No conflict with attachments. |
| What can a preprocessor read? | The full history: roles, text, tool names and arguments, tool results with their call ids. Reading it took 0 to 2 ms on a 23,000-character chat. | Source checks and recall are feasible. |
| Is there a stable chat identity? | Yes. Each chat has an id (the last part of its working directory path) that matches the name of its file in .lmstudio\conversations\. A chat's first message is flagged as needing a name. | Ledgers and recall indexes are tied to chats by this id. |
| Can tools differ per chat? | Yes. LM Studio asks for the tool list on every generation and the request carries the chat's id. Some requests carry no chat (background work such as naming), and those get no tools. | Tools are offered only where they apply. |
| Can a preprocessor show something in the chat window? | Yes. Status lines appeared above each reply. | The user can see throughLine's state at no token cost. |
| Does the model notice injected text? | Yes, and it may comment on it unprompted (a "Hello" was answered with a remark about the test marker). | The digest needs framing that tells the model it is background state. |
| How is a deleted chat detectable? | Deleting a chat removes its conversation file entirely. Working directories are not real folders on disk and cannot be used. | Cleanup checks whether the chat's file still exists, by name only. |
| Can a command replace the user's message? | Yes. The model replied with exactly the requested sentence. | Commands work, though each still costs one short reply. |
Unrelated to throughLine: during the compressor test, one of the compressor's replies included LM Studio's internal reasoning separator as visible text. It has not been reproduced with throughLine disabled, but nothing throughLine does touches the model's output.
In scope for v2.1: per-chat ledgers that start on their own or by command, the turn header and full digest, ledger tools, recall, staleness reminders, health checks, project commands, handoff export, and cleanup after deleted chats.
Planned after evaluation (M5): analysis of attached documents too large for the context, section by section.
Out of scope: replacing context-compressor, autonomous multi-turn continuation without the user, summarizing conversation (context-compressor does that), personal memory (simpleMemory does that), web access (simpleSearch does that), and journal or diary features.
| Component | Holds | Lifetime |
|---|---|---|
| simpleMemory | Durable facts about the user | Permanent |
| throughLine ledger | The state of one problem | Life of the project |
| throughLine recall index | A searchable copy of one long chat | Life of the chat |
| context-compressor | A running summary of the conversation | Life of the chat |
| simpleSearch | Nothing persistent; enforces a research budget per run of tool calls | One reply |
Facts about the user never go in a ledger, and project state never goes in simpleMemory. The model's system prompt states both rules, and the ledger tools reject entries that look like personal facts about the user.
throughLine uses a prompt preprocessor and a tools provider. It does not register a prediction loop handler, because only one plugin can run the generation loop and context-compressor already does.
A chat is identified by its id. A project is a ledger with a name. A project is bound to one or more chats; a chat is bound to at most one project.
A chat gets a project in one of three ways:
/tl start starts a project in the current chat, with an optional goal./tl open <name> binds the current chat to an existing project, so work can move to a fresh chat at any time./tl off unbinds the chat and turns automatic start off for it. A project that started automatically and is still empty is deleted at once, since it was a false start.
A message that asks about the user's projects in plain words ("what's the status of my projects?") gets a list of all projects attached for that message, in any chat, at no cost to other messages.
Everything lives under %USERPROFILE%\.lmstudio\throughline\:
projects\<slug>\ledger.json: the current state, written atomically.| Section | Contents | Who changes it |
|---|---|---|
| Goal | One paragraph stating what the project must produce | The user, by command; the model may propose a change but not make it |
| Constraints | Rules the user has set for this project | The user, by command |
| Plan | Ordered steps, each with an id, text, status (todo, doing, done, blocked) and a short note | The model |
| Findings | Claims established so far, each with a source and a verification state. A source is a URL, a file path, "user", a turn of this chat ("turn 41") or, from M5, a section of an attached document ("section 7") | The model |
| Decisions | Choices made and the reason for each | The model, or the user by command |
| Open questions | Questions that block progress, each resolvable with an answer | The model |
The goal and constraints are protected because they are what drift hurts most. A model that quietly rewrites its own goal can declare any result a success.
The user may edit ledger.md directly, in any editor, including ones that escape Markdown characters or strip indentation when saving. The file's second line is an HTML comment holding the ledger version it was written from. On the next turn the preprocessor sees that the file differs from what throughLine last wrote, finds that version among the 50 kept, and merges by id: what the user changed relative to that version is applied to the current ledger, and anything the model recorded while the file was open is kept. Without the marker, the version sharing the most entries with the edit is used. The change is logged as a user edit, and the full state is shown with a one-line notice. If the file no longer parses, it is saved beside the ledger as ledger.edit-not-applied-<time>.md, ledger.md is rewritten from the ledger, and the model is told to tell the user.
On every user message in a chat with a project, the preprocessor appends a short header, about 60 to 150 tokens:
The full digest adds constraints, next steps, recent findings, decisions and open questions, capped at 600 tokens and trimmed oldest first with a count of what was left out and a pointer to ledger_view. It replaces the header on a turn when any of these holds:
/tl status.Both the header and the digest are cut from the ledger, never paraphrased by a model, so nothing in them can drift. Older headers stay in the stored history (M0, question 1); at about 100 tokens a turn they are a small cost, and the compressor summarizes them away with the rest of the older turns.
A status line above each reply shows the project and progress (throughLine: llm-runtimes, step 4 of 9). It is visible to the user only and costs no tokens.
What the model is offered depends on the chat:
| Chat | Tools offered |
|---|---|
| No project, history under the recall threshold | None |
| No project, history over the recall threshold | recall |
| Project | ledger_view, ledger_plan, ledger_record, ledger_resolve, and recall once over the threshold |
| No chat attached (background work) | None |
| Tool | Purpose |
|---|---|
ledger_view | Returns the full ledger or one section |
ledger_plan | Adds a step, or sets a step's status and note |
ledger_record | Adds a finding, decision or open question |
ledger_resolve | Closes an open question with its answer, optionally turning the answer into a finding |
recall | Searches the chat's full stored history and returns the best-matching passages, each with its turn number and the time it was first seen |
Each ledger tool returns only what changed: a status line, the changed entry, the current step and a next: line, about 50 to 100 tokens. This keeps ledger results well inside context-compressor's cap on tool output per reply, which simpleSearch results share.
A tool cannot read the chat history, only the preprocessor can. A finding whose source is a URL is therefore accepted as "unverified" when recorded. On the next user turn the preprocessor checks the history: if the URL appears in a read_webpage call or result, the finding becomes "verified"; if not, it becomes "not read" and is counted in the health line until the model reads the page or removes the finding. A finding sourced to a turn is verified when that turn exists in the chat's history. A finding with no source is marked unverified and counted the same way. This targets the citation problem seen in testing, where the model cited pages it had not read.
Recall starts for a chat once its stored history passes a threshold, 24,000 tokens by default (estimated from characters), which is about half of what context-compressor allows before compacting at 64k. Below the threshold the full history is still in the model's view and nothing is copied to disk.
Past the threshold the preprocessor builds the index from the full history once, then on each turn adds only the messages that are new since the last turn. Each entry holds the message role, the turn number, the time the preprocessor first saw it, and the text split into passages. Tool results are indexed by their text with the tool's name; a turn's own tool output is searchable from the next turn on, by which time it may have been summarized away.
recall ranks passages by word overlap with the query (the same ranking simpleSearch uses for focused reading) and returns the top few, about 1,500 tokens at most, with turn numbers so the model can say where something came from.
A command typed as the whole message is handled by the preprocessor, which replaces it with a one-line instruction for the model to confirm it. The status line shows the result.
| Command | Effect |
|---|---|
/tl start [goal] | Starts a project in this chat |
/tl open <name> | Binds this chat to an existing project |
/tl list | Lists projects with their status and last activity |
/tl off | Unbinds this chat and turns automatic start off for it |
/tl goal <text> | Sets or replaces the goal |
/tl constraint <text> | Adds a constraint |
/tl status | Shows the full digest in the chat |
/tl undo | Reverts the last change, from the event log |
/tl handoff | Writes handoff.md in the project folder |
/tl forget | Deletes this chat's project and recall index now |
When the plugin starts (at every LM Studio launch and every time plugins are toggled in a chat, so often), it checks each chat folder under chats\ against .lmstudio\conversations\, looking only at whether a file with that chat's id exists:
trash\, and 30 days later it is deleted. Opening it with /tl open at any point clears the mark./tl handoff writes handoff.md in three layers, matching v1.0:
The handoff exists for sharing a project's state outside LM Studio and for archiving; moving to a new chat needs only /tl open.
| Setting | Default | Meaning |
|---|---|---|
| Automatic start | On | Start a project after two consecutive tool-heavy turns |
| Tool calls for a heavy turn | 3 | Tool calls after one user message that count as heavy |
| Full digest cap | 600 tokens | Maximum size of the full digest |
| Verbatim window | 6,000 tokens | Match context-compressor's "Recent context kept verbatim" |
| Recall | On | Index long chats and offer recall |
| Recall threshold | 24,000 tokens | History size at which recall starts |
| Stale after turns | 2 | Turns with tool activity and no ledger change before a reminder |
| Orphan grace period | 7 days | Before an orphaned project moves to trash |
| Trash retention | 30 days | Before a trashed project is deleted |
| Versions kept | 50 | Ledger copies kept for undo |
Ledgers and recall indexes are plain JSON and Markdown under .lmstudio\throughline\. The plugin makes no network requests and has no dependencies beyond the LM Studio SDK.
A recall index is a second copy of a chat's text. It exists only for chats past the recall threshold, it is deleted as soon as its chat is deleted, /tl forget deletes it on request, and recall can be turned off entirely. Cleanup checks conversation files by name only and never opens them.
Before any public release, every string literal in the source, including tool descriptions, examples, test fixtures and error messages, is screened for personal information.
| Milestone | Work | Done when |
|---|---|---|
| M0 | Smoke tests | Done, October 17, 2026 (results above) |
| M1 | Ledger store, chat state, per-chat binding, commands, turn header and full digest, ledger tools with delta results, tool gating, status line | Done, October 17, 2026: a project survived two restarts and a move to a new chat with its state intact, the model planned and recorded through the tools, and a chat with no project received nothing |
| M2 | Automatic start, staleness reminders, health line, source verification, hand edits, status questions | Done, October 17, 2026: a project started on its own, unread sources were flagged and then fixed by the model, a hand edit made in an editor that escapes Markdown was merged and logged, and a status question was answered from the attached list. Reminders are covered by tests; the model recorded without needing one |
| M3 | Recall index and tool, cleanup after deleted chats, handoff, undo | Done, October 17, 2026: recall switched on by itself in a long chat and the model used it unprompted; deleting four chats removed their data within a minute and marked their three projects orphaned; handoff and undo worked. Live testing found that commands could overwrite an unmerged hand edit; every command and tool write now merges pending edits first. A 3,000-message chat adds about 30 ms per turn |
| M4 | Evaluation on a real long problem with Qwen 3.8 and Qwen 3.6, run twice: context-compressor alone, then context-compressor with throughLine | Results recorded against the criteria below, with the difference between the two runs |
| M5 | Large documents: the preprocessor parses an attached document too large for the context and saves it in sections in the chat's folder; a document_section tool reads one section by number; with a project, the plan gets one step per section and findings cite sections. The parsed copy is deleted with the chat | A document of at least 100,000 tokens is analyzed end to end, and questions about the whole of it are answered from the ledger with section citations |
Measured on a multi-session research problem with ten facts seeded early:
A planned companion, designed separately once throughLine is in use. Given a task, it would inspect the setup the model is actually running with (loaded model, context length, enabled tools and plugins, simpleSearch's self-test and budget, context-compressor's settings, the active ledger's health) and give a reasoned assessment of whether the setup is credibly up to the task, with specific suggestions for what is holding it back. throughLine's health line is a first, narrow version of that idea, limited to the state of one project.
v2.1, October 17, 2026
Supersedes v2.0 (earlier the same day), v1.0 (April 2026) and the July 2026 plan for in-session compression. v2.1 incorporates the results of the M0 smoke tests and the decision that throughLine should be enabled everywhere and cost nothing until it is needed.
A long problem outlives the context window. Over many turns and sessions the model loses the thread: early decisions, exact figures, sources and the original goal fade first, and the model starts re-researching what it already settled or contradicting itself.
Compression does not fix this on its own. context-compressor keeps a long conversation running by summarizing older turns, but every compaction is lossy, and once its summaries pass their budget the oldest are merged into denser ones. Its summarizer also sorts everything into task sections, so anything that is not a task (dates, what was said, how it was said) is the first to go. Its own benchmark on Qwen 3.8 27B recalled 2 of 3 seeded facts on the research fixture after a single round. A conversation can run indefinitely this way; the substance inside it cannot stay intact.
throughLine does two things, both outside the conversation the model sees:
The conversation becomes disposable for a problem, and compression becomes lossy only in what the model sees at once, not in what it can find.
Tested on LM Studio 0.4.26 with Qwen 3.8 27B, context-compressor (revision 27), simpleSearch, simpleMemory and rag-v1 enabled.
| Question | Result | Consequence for the design |
|---|---|---|
| Does text a preprocessor adds persist in chat history? | Yes. The stored message includes it and it is sent on every later turn. The chat window shows only what the user typed. | Old digests accumulate, so the per-turn digest must be small and the full view shown only when needed. |
| Do throughLine and context-compressor coexist? | Yes. Both ran on every turn, and stored history stayed complete after the compressor ran. | No change. Recall can rely on the stored history being whole. |
| Do two prompt preprocessors chain? | Yes. throughLine and rag-v1 both ran on an attachment turn and the answer was correct. | No conflict with attachments. |
| What can a preprocessor read? | The full history: roles, text, tool names and arguments, tool results with their call ids. Reading it took 0 to 2 ms on a 23,000-character chat. | Source checks and recall are feasible. |
| Is there a stable chat identity? | Yes. Each chat has an id (the last part of its working directory path) that matches the name of its file in .lmstudio\conversations\. A chat's first message is flagged as needing a name. | Ledgers and recall indexes are tied to chats by this id. |
| Can tools differ per chat? | Yes. LM Studio asks for the tool list on every generation and the request carries the chat's id. Some requests carry no chat (background work such as naming), and those get no tools. | Tools are offered only where they apply. |
| Can a preprocessor show something in the chat window? | Yes. Status lines appeared above each reply. | The user can see throughLine's state at no token cost. |
| Does the model notice injected text? | Yes, and it may comment on it unprompted (a "Hello" was answered with a remark about the test marker). | The digest needs framing that tells the model it is background state. |
| How is a deleted chat detectable? | Deleting a chat removes its conversation file entirely. Working directories are not real folders on disk and cannot be used. | Cleanup checks whether the chat's file still exists, by name only. |
| Can a command replace the user's message? | Yes. The model replied with exactly the requested sentence. | Commands work, though each still costs one short reply. |
Unrelated to throughLine: during the compressor test, one of the compressor's replies included LM Studio's internal reasoning separator as visible text. It has not been reproduced with throughLine disabled, but nothing throughLine does touches the model's output.
In scope for v2.1: per-chat ledgers that start on their own or by command, the turn header and full digest, ledger tools, recall, staleness reminders, health checks, project commands, handoff export, and cleanup after deleted chats.
Planned after evaluation (M5): analysis of attached documents too large for the context, section by section.
Out of scope: replacing context-compressor, autonomous multi-turn continuation without the user, summarizing conversation (context-compressor does that), personal memory (simpleMemory does that), web access (simpleSearch does that), and journal or diary features.
| Component | Holds | Lifetime |
|---|---|---|
| simpleMemory | Durable facts about the user | Permanent |
| throughLine ledger | The state of one problem | Life of the project |
| throughLine recall index | A searchable copy of one long chat | Life of the chat |
| context-compressor | A running summary of the conversation | Life of the chat |
| simpleSearch | Nothing persistent; enforces a research budget per run of tool calls | One reply |
Facts about the user never go in a ledger, and project state never goes in simpleMemory. The model's system prompt states both rules, and the ledger tools reject entries that look like personal facts about the user.
throughLine uses a prompt preprocessor and a tools provider. It does not register a prediction loop handler, because only one plugin can run the generation loop and context-compressor already does.
A chat is identified by its id. A project is a ledger with a name. A project is bound to one or more chats; a chat is bound to at most one project.
A chat gets a project in one of three ways:
/tl start starts a project in the current chat, with an optional goal./tl open <name> binds the current chat to an existing project, so work can move to a fresh chat at any time./tl off unbinds the chat and turns automatic start off for it. A project that started automatically and is still empty is deleted at once, since it was a false start.
A message that asks about the user's projects in plain words ("what's the status of my projects?") gets a list of all projects attached for that message, in any chat, at no cost to other messages.
Everything lives under %USERPROFILE%\.lmstudio\throughline\:
projects\<slug>\ledger.json: the current state, written atomically.| Section | Contents | Who changes it |
|---|---|---|
| Goal | One paragraph stating what the project must produce | The user, by command; the model may propose a change but not make it |
| Constraints | Rules the user has set for this project | The user, by command |
| Plan | Ordered steps, each with an id, text, status (todo, doing, done, blocked) and a short note | The model |
| Findings | Claims established so far, each with a source and a verification state. A source is a URL, a file path, "user", a turn of this chat ("turn 41") or, from M5, a section of an attached document ("section 7") | The model |
| Decisions | Choices made and the reason for each | The model, or the user by command |
| Open questions | Questions that block progress, each resolvable with an answer | The model |
The goal and constraints are protected because they are what drift hurts most. A model that quietly rewrites its own goal can declare any result a success.
The user may edit ledger.md directly, in any editor, including ones that escape Markdown characters or strip indentation when saving. The file's second line is an HTML comment holding the ledger version it was written from. On the next turn the preprocessor sees that the file differs from what throughLine last wrote, finds that version among the 50 kept, and merges by id: what the user changed relative to that version is applied to the current ledger, and anything the model recorded while the file was open is kept. Without the marker, the version sharing the most entries with the edit is used. The change is logged as a user edit, and the full state is shown with a one-line notice. If the file no longer parses, it is saved beside the ledger as ledger.edit-not-applied-<time>.md, ledger.md is rewritten from the ledger, and the model is told to tell the user.
On every user message in a chat with a project, the preprocessor appends a short header, about 60 to 150 tokens:
The full digest adds constraints, next steps, recent findings, decisions and open questions, capped at 600 tokens and trimmed oldest first with a count of what was left out and a pointer to ledger_view. It replaces the header on a turn when any of these holds:
/tl status.Both the header and the digest are cut from the ledger, never paraphrased by a model, so nothing in them can drift. Older headers stay in the stored history (M0, question 1); at about 100 tokens a turn they are a small cost, and the compressor summarizes them away with the rest of the older turns.
A status line above each reply shows the project and progress (throughLine: llm-runtimes, step 4 of 9). It is visible to the user only and costs no tokens.
What the model is offered depends on the chat:
| Chat | Tools offered |
|---|---|
| No project, history under the recall threshold | None |
| No project, history over the recall threshold | recall |
| Project | ledger_view, ledger_plan, ledger_record, ledger_resolve, and recall once over the threshold |
| No chat attached (background work) | None |
| Tool | Purpose |
|---|---|
ledger_view | Returns the full ledger or one section |
ledger_plan | Adds a step, or sets a step's status and note |
ledger_record | Adds a finding, decision or open question |
ledger_resolve | Closes an open question with its answer, optionally turning the answer into a finding |
recall | Searches the chat's full stored history and returns the best-matching passages, each with its turn number and the time it was first seen |
Each ledger tool returns only what changed: a status line, the changed entry, the current step and a next: line, about 50 to 100 tokens. This keeps ledger results well inside context-compressor's cap on tool output per reply, which simpleSearch results share.
A tool cannot read the chat history, only the preprocessor can. A finding whose source is a URL is therefore accepted as "unverified" when recorded. On the next user turn the preprocessor checks the history: if the URL appears in a read_webpage call or result, the finding becomes "verified"; if not, it becomes "not read" and is counted in the health line until the model reads the page or removes the finding. A finding sourced to a turn is verified when that turn exists in the chat's history. A finding with no source is marked unverified and counted the same way. This targets the citation problem seen in testing, where the model cited pages it had not read.
Recall starts for a chat once its stored history passes a threshold, 24,000 tokens by default (estimated from characters), which is about half of what context-compressor allows before compacting at 64k. Below the threshold the full history is still in the model's view and nothing is copied to disk.
Past the threshold the preprocessor builds the index from the full history once, then on each turn adds only the messages that are new since the last turn. Each entry holds the message role, the turn number, the time the preprocessor first saw it, and the text split into passages. Tool results are indexed by their text with the tool's name; a turn's own tool output is searchable from the next turn on, by which time it may have been summarized away.
recall ranks passages by word overlap with the query (the same ranking simpleSearch uses for focused reading) and returns the top few, about 1,500 tokens at most, with turn numbers so the model can say where something came from.
A command typed as the whole message is handled by the preprocessor, which replaces it with a one-line instruction for the model to confirm it. The status line shows the result.
| Command | Effect |
|---|---|
/tl start [goal] | Starts a project in this chat |
/tl open <name> | Binds this chat to an existing project |
/tl list | Lists projects with their status and last activity |
/tl off | Unbinds this chat and turns automatic start off for it |
/tl goal <text> | Sets or replaces the goal |
/tl constraint <text> | Adds a constraint |
/tl status | Shows the full digest in the chat |
/tl undo | Reverts the last change, from the event log |
/tl handoff | Writes handoff.md in the project folder |
/tl forget | Deletes this chat's project and recall index now |
When the plugin starts (at every LM Studio launch and every time plugins are toggled in a chat, so often), it checks each chat folder under chats\ against .lmstudio\conversations\, looking only at whether a file with that chat's id exists:
trash\, and 30 days later it is deleted. Opening it with /tl open at any point clears the mark./tl handoff writes handoff.md in three layers, matching v1.0:
The handoff exists for sharing a project's state outside LM Studio and for archiving; moving to a new chat needs only /tl open.
| Setting | Default | Meaning |
|---|---|---|
| Automatic start | On | Start a project after two consecutive tool-heavy turns |
| Tool calls for a heavy turn | 3 | Tool calls after one user message that count as heavy |
| Full digest cap | 600 tokens | Maximum size of the full digest |
| Verbatim window | 6,000 tokens | Match context-compressor's "Recent context kept verbatim" |
| Recall | On | Index long chats and offer recall |
| Recall threshold | 24,000 tokens | History size at which recall starts |
| Stale after turns | 2 | Turns with tool activity and no ledger change before a reminder |
| Orphan grace period | 7 days | Before an orphaned project moves to trash |
| Trash retention | 30 days | Before a trashed project is deleted |
| Versions kept | 50 | Ledger copies kept for undo |
Ledgers and recall indexes are plain JSON and Markdown under .lmstudio\throughline\. The plugin makes no network requests and has no dependencies beyond the LM Studio SDK.
A recall index is a second copy of a chat's text. It exists only for chats past the recall threshold, it is deleted as soon as its chat is deleted, /tl forget deletes it on request, and recall can be turned off entirely. Cleanup checks conversation files by name only and never opens them.
Before any public release, every string literal in the source, including tool descriptions, examples, test fixtures and error messages, is screened for personal information.
| Milestone | Work | Done when |
|---|---|---|
| M0 | Smoke tests | Done, October 17, 2026 (results above) |
| M1 | Ledger store, chat state, per-chat binding, commands, turn header and full digest, ledger tools with delta results, tool gating, status line | Done, October 17, 2026: a project survived two restarts and a move to a new chat with its state intact, the model planned and recorded through the tools, and a chat with no project received nothing |
| M2 | Automatic start, staleness reminders, health line, source verification, hand edits, status questions | Done, October 17, 2026: a project started on its own, unread sources were flagged and then fixed by the model, a hand edit made in an editor that escapes Markdown was merged and logged, and a status question was answered from the attached list. Reminders are covered by tests; the model recorded without needing one |
| M3 | Recall index and tool, cleanup after deleted chats, handoff, undo | Done, October 17, 2026: recall switched on by itself in a long chat and the model used it unprompted; deleting four chats removed their data within a minute and marked their three projects orphaned; handoff and undo worked. Live testing found that commands could overwrite an unmerged hand edit; every command and tool write now merges pending edits first. A 3,000-message chat adds about 30 ms per turn |
| M4 | Evaluation on a real long problem with Qwen 3.8 and Qwen 3.6, run twice: context-compressor alone, then context-compressor with throughLine | Results recorded against the criteria below, with the difference between the two runs |
| M5 | Large documents: the preprocessor parses an attached document too large for the context and saves it in sections in the chat's folder; a document_section tool reads one section by number; with a project, the plan gets one step per section and findings cite sections. The parsed copy is deleted with the chat | A document of at least 100,000 tokens is analyzed end to end, and questions about the whole of it are answered from the ledger with section citations |
Measured on a multi-session research problem with ten facts seeded early:
A planned companion, designed separately once throughLine is in use. Given a task, it would inspect the setup the model is actually running with (loaded model, context length, enabled tools and plugins, simpleSearch's self-test and budget, context-compressor's settings, the active ledger's health) and give a reasoned assessment of whether the setup is credibly up to the task, with specific suggestions for what is holding it back. throughLine's health line is a first, narrow version of that idea, limited to the state of one project.
projects\<slug>\ledger.md: a readable rendering, regenerated on every write, for the user to open and edit by hand.projects\<slug>\events.jsonl: an append-only log of every change, used for undo and review.projects\<slug>\versions\: the 50 most recent copies of ledger.json before each write.chats\<chat id>\state.json: the chat's binding, turn count, tool activity, digest position and history size.chats\<chat id>\recall\: the recall index, only for chats past the recall threshold.trash\: orphaned projects awaiting final deletion.doing, no step marked doing while steps remain, open questions older than ten turns, and a step marked done with nothing recorded during it.ledger_viewledger_plan can refuse to mark a step done until something was recorded during it./tl off ends it, and an early false start leaves nothing behind.[throughLine: background state for the assistant. Use it; do not mention it unless asked.]
Project "llm-runtimes" | Goal: Compare license, latest release and default context of seven inference runtimes
Now: 4. Verify default context lengths (doing) | 3 of 9 done
Changed since last turn: F12, F13 added; step 3 done
Health: 1 finding not yet verified
[end throughLine]
projects\<slug>\ledger.md: a readable rendering, regenerated on every write, for the user to open and edit by hand.projects\<slug>\events.jsonl: an append-only log of every change, used for undo and review.projects\<slug>\versions\: the 50 most recent copies of ledger.json before each write.chats\<chat id>\state.json: the chat's binding, turn count, tool activity, digest position and history size.chats\<chat id>\recall\: the recall index, only for chats past the recall threshold.trash\: orphaned projects awaiting final deletion.doing, no step marked doing while steps remain, open questions older than ten turns, and a step marked done with nothing recorded during it.ledger_viewledger_plan can refuse to mark a step done until something was recorded during it./tl off ends it, and an early false start leaves nothing behind.[throughLine: background state for the assistant. Use it; do not mention it unless asked.]
Project "llm-runtimes" | Goal: Compare license, latest release and default context of seven inference runtimes
Now: 4. Verify default context lengths (doing) | 3 of 9 done
Changed since last turn: F12, F13 added; step 3 done
Health: 1 finding not yet verified
[end throughLine]