Observation
On a fresh trellis init --claude install (verified against v0.4.0-beta.8), the session-start.py hook produces additionalContext that is consistently over 30 KB. Claude Code appears to truncate additionalContext at around 20 KB, emitting only a ~2 KB preview plus a fallback file path. In practice the AI does not read the fallback file unless explicitly prompted, so anything past the 2 KB preview is effectively invisible — including ## ACTIVE TASKS, ## CURRENT TASK, and ## MY TASKS.
End result: the session-start context injection silently breaks for any project whose combined size pushes it past ~20 KB, which is most non-trivial projects. The developer sees "task state not being surfaced" without any error.
Data
Measurements from the hook output on two projects (one clean vanilla install, one lightly used):
| Segment |
Project A (vanilla) |
Project B (light use) |
<session-context> |
~150 B |
~150 B |
<current-state> (tasks, git, journal) |
1.6 KB |
1.7 KB |
<workflow> (full workflow.md) |
12.5 KB |
16.6 KB |
<guidelines> (spec index files) |
6.3 KB |
5.0 KB |
<instructions> (full start.md) |
11.0 KB |
11.0 KB |
total additionalContext |
~31 KB |
~36 KB |
Both are over the observed ~20 KB truncation threshold. The two large blocks marked ★ in the table above account for 75%+ of the payload.
How I verified the truncation behavior
Using claude -p --session-id <uuid> to spawn a fresh Claude Code instance inside the project and asking it to report what it actually received:
- Fresh instance replies: "additionalContext was truncated; only a ~2 KB preview was available; saved to
tool-results/hook-<id>-additionalContext.txt"
- The Active Tasks / Current Task sections are not visible in the visible preview
- The instance does not proactively read the fallback file
After reducing the hook output to well under 20 KB (see "Possible approaches" below), a fresh Claude instance reports:
- "No truncation. I can see 3 active tasks, the current task name, and the full workflow section index."
Possible approaches (not prescriptive)
I experimented with a couple of out-of-tree patches on my own projects and both worked well; sharing here as ideas for upstream discussion, not as a fait-accompli solution.
Approach A — Dynamic TOC for workflow.md
Instead of reading workflow.md verbatim into <workflow>, session-start.py scans the file at session-start time and emits a compact table of contents with line ranges for each ## section. Claude navigates by line range and reads specific sections on-demand via Read offset+limit. A compact form:
<workflow>
`.trellis/workflow.md` — read on-demand by section (use Read with offset+limit):
- L7-18 (12L): Table of Contents
- L19-88 (70L): Quick Start
- L89-150 (62L): Workflow Overview
- L151-202 (52L): Session Start Process
- L203-240 (38L): Development Process
- L241-270 (30L): Session End
- L271-337 (67L): File Descriptions
- L338-368 (31L): Best Practices
- L369-406 (38L): Quick Reference
- L407-420 (14L): Summary
</workflow>
Typical size: ~500–700 bytes instead of 12–16 KB. Claude still has full content access — it just reads it lazily.
Approach B — Remove start.md from session-start entirely
start.md is the /trellis:start slash command definition. Claude Code already expands it as a prompt when the user types the command. Injecting it at session-start as well is duplicate work: the user either types the command (in which case they get the full content on demand) or they don't (in which case the 11 KB is unused ballast). Other slash commands (brainstorm, finish-work, check, record-session) are not pre-injected — so this would restore symmetry.
Replace the current <instructions> block with a 1-line pointer like:
<instructions>
For task startup workflow, type the slash command `/trellis:start`. It expands the full guide on demand. Other slash commands (brainstorm / finish-work / check / record-session) follow the same pattern — not pre-injected here.
</instructions>
Typical savings: ~11 KB.
Combined result on my measurement projects
With both Approach A and B applied:
| Project |
Before |
After |
| A (vanilla) |
31 KB |
9.3 KB |
| B (light use) |
36 KB |
8.4 KB |
Both drop well under the truncation threshold. Fresh claude -p instances in both projects confirm they now see the full <current-state> and <workflow> TOC, with room to spare (~11 KB headroom before hitting the limit).
Reproduction
mkdir /tmp/trellis-repro && cd /tmp/trellis-repro && git init
npx @mindfoldhq/trellis@beta init --claude -u test-user -y
# Measure session-start output size:
echo '{"session_id":"t","source":"startup"}' \
| python3 .claude/hooks/session-start.py \
| python3 -c 'import sys,json; d=json.load(sys.stdin); ctx=d["hookSpecificOutput"]["additionalContext"]; print(f"{len(ctx.encode(\"utf-8\"))} bytes")'
Typical vanilla output: 30+ KB on a repo with a handful of tasks and spec files.
Why I'm raising this as an issue, not a PR
This is a design-space question more than a clear bug:
- Is the truncation behavior I'm seeing a Claude Code platform limit, a trellis design assumption, or both?
- If workflow.md / start.md pre-injection is intentional, what's the rationale? (I'd like to understand before proposing a change — I might be missing a reason.)
- If reducing the payload is welcome, which approach fits better — dynamic TOC, symmetry with other slash commands, a config toggle, all three?
I'm happy to turn either or both approaches into a PR if there's interest, but I'd rather discuss the direction first so the work doesn't go to waste.
Environment
- trellis: 0.4.0-beta.8 (also observed on beta.9)
- Claude Code: 4.6
- OS: macOS 14 (Darwin 23.6)
- Python: 3.11
- node: 20.x
Related
Observation
On a fresh
trellis init --claudeinstall (verified against v0.4.0-beta.8), thesession-start.pyhook producesadditionalContextthat is consistently over 30 KB. Claude Code appears to truncateadditionalContextat around 20 KB, emitting only a ~2 KB preview plus a fallback file path. In practice the AI does not read the fallback file unless explicitly prompted, so anything past the 2 KB preview is effectively invisible — including## ACTIVE TASKS,## CURRENT TASK, and## MY TASKS.End result: the session-start context injection silently breaks for any project whose combined size pushes it past ~20 KB, which is most non-trivial projects. The developer sees "task state not being surfaced" without any error.
Data
Measurements from the hook output on two projects (one clean vanilla install, one lightly used):
<session-context><current-state>(tasks, git, journal)<workflow>(fullworkflow.md)<guidelines>(spec index files)<instructions>(fullstart.md)additionalContextBoth are over the observed ~20 KB truncation threshold. The two large blocks marked ★ in the table above account for 75%+ of the payload.
How I verified the truncation behavior
Using
claude -p --session-id <uuid>to spawn a fresh Claude Code instance inside the project and asking it to report what it actually received:tool-results/hook-<id>-additionalContext.txt"After reducing the hook output to well under 20 KB (see "Possible approaches" below), a fresh Claude instance reports:
Possible approaches (not prescriptive)
I experimented with a couple of out-of-tree patches on my own projects and both worked well; sharing here as ideas for upstream discussion, not as a fait-accompli solution.
Approach A — Dynamic TOC for
workflow.mdInstead of reading
workflow.mdverbatim into<workflow>,session-start.pyscans the file at session-start time and emits a compact table of contents with line ranges for each##section. Claude navigates by line range and reads specific sections on-demand viaRead offset+limit. A compact form:Typical size: ~500–700 bytes instead of 12–16 KB. Claude still has full content access — it just reads it lazily.
Approach B — Remove
start.mdfrom session-start entirelystart.mdis the/trellis:startslash command definition. Claude Code already expands it as a prompt when the user types the command. Injecting it at session-start as well is duplicate work: the user either types the command (in which case they get the full content on demand) or they don't (in which case the 11 KB is unused ballast). Other slash commands (brainstorm,finish-work,check,record-session) are not pre-injected — so this would restore symmetry.Replace the current
<instructions>block with a 1-line pointer like:Typical savings: ~11 KB.
Combined result on my measurement projects
With both Approach A and B applied:
Both drop well under the truncation threshold. Fresh
claude -pinstances in both projects confirm they now see the full<current-state>and<workflow>TOC, with room to spare (~11 KB headroom before hitting the limit).Reproduction
Typical vanilla output: 30+ KB on a repo with a handful of tasks and spec files.
Why I'm raising this as an issue, not a PR
This is a design-space question more than a clear bug:
I'm happy to turn either or both approaches into a PR if there's interest, but I'd rather discuss the direction first so the work doesn't go to waste.
Environment
Related
SubagentStop/truncat/ size-related SessionStart discussion