Skip to content

fix: prevent unbounded distill prompt growth from leiter's own agent sessions - #120

Open
scode wants to merge 1 commit into
hookless/8-drain-fixfrom
hookless/9-self-session
Open

scode wants to merge 1 commit into
hookless/8-drain-fixfrom
hookless/9-self-session

Conversation

@scode

@scode scode commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Second find from the live E2E battery, and the worse one: the headless distill child is itself an agent session, so
its transcript — containing the full distill prompt, i.e. every transcript emitted that cycle — lands in the session
store and gets emitted by the next scan. Each cycle's payload embeds the previous cycle's payload; growth is
unbounded. On the test box the fourth cycle hit "Prompt is too long: ~3.7M tokens" and distillation was permanently
wedged. A cron setup on an active box would reach the same wall within days.

The fix marks headless prompts with a fixed first-line sentinel, and the scan records sentinel-classified sessions as
processed without emission — their content is synthetic (leiter's own prompt plus the agent's summary), so nothing is
lost. Two review rounds shaped the details into something considerably more careful than the first cut:

  • Detection is positional, not substring-anywhere: a session is a child only when its FIRST user message begins with
    the sentinel — a position injected tool output, quoted docs, or soul-synced block content can never occupy.
    Content-anywhere matching would have handed prompt injection a silent suppress-from-learning primitive, and via the
    soul-sync channel (sentinel in soul → managed block → every future transcript) a global learning shutdown.
  • The classification runs in every channel the child can land in: the Claude scan, the Codex rollout scan (an
    agent_command of codex exec writes the child there — the growth loop would have survived that documented
    configuration untouched), and legacy-log rendering (a still-hooked migrating box's SessionEnd tombstone copies the
    child transcript into logs, where an unaccounted copy could re-wedge the exact failure being fixed).
  • --dry-run elides every sentinel occurrence from its output, so inspecting the prompt cannot mark the inspecting
    session; the interactive leiter soul distill payload carries no sentinel at all, keeping the invoking session
    learnable. Excluded session ids are logged, not silently absorbed.

A run whose only changes are child sessions commits their watermarks through the existing empty-only self-commit path
without spawning an agent.

changelog: include

@scode
scode force-pushed the hookless/8-drain-fix branch from d0f69f7 to cc66fd1 Compare July 7, 2026 06:18
@scode
scode force-pushed the hookless/9-self-session branch from ee8e5da to 5ce1a60 Compare July 7, 2026 06:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant