feat: tune-out — subconscious summaries instead of unsubscribing (#77) - #115
Open
Meganeuridae wants to merge 8 commits into
Open
feat: tune-out — subconscious summaries instead of unsubscribing (#77)#115Meganeuridae wants to merge 8 commits into
Meganeuridae wants to merge 8 commits into
Conversation
…nima-research#77) 'tuned-out' joins open/closed in the mcpl/channel-lifecycle append-log, carrying its epoch params (epochId, cadenceSeconds, backlogCap, maxWakes, startedAtSequence). Transport-wise a tuned-out channel is OPEN — traffic must keep arriving for the subconscious — so reconcile treats it as open and only main's wake/visibility diverts, downstream at ingestion (next commits). Wake counts are durable 'tune-out-wake' records in the same log, replayed last-record-wins and epoch-checked, because gate runtime stats die with the process and a restart must not hand a hammered channel a fresh max-wakes budget. Re-entering under a new epoch supersedes the old count. Guard rails: channel_open on a tuned-out channel refuses with a pointer at the cancel flow (a plain open would silently discard the epoch and its pending backlog dump); machine-sourced closes treat tuned-out as stated agent intent, same as explicit opens; desired-state records for 'tuned-out' without params are dropped at replay (old/foreign writers degrade safely — pre-existing behavior, now pinned by a test). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ing (anima-research#77) The full arc: tune_out (per channel: cadence, backlog cap, max-wakes) → incoming traffic stamped (metadata.tuneOut={epochId}), stored in the shared slot, diverted from resident wake AND compiled view (cm viewFilter at the strategy-view choke point — no emission leak, no L1-compression leak) → addressed messages get the deterministic channels/acknowledge (intent 'suppressed-tuned-out', grant-gated) and, with gate-privileged authors, a coalesced subconscious wake → durable wake counting with max-wakes auto-cancel → cancel delivers the system-framed capped <tuned-out-backlog> dump plus one resident wake, and gives the subconscious its own report turn; stamped originals stay view-excluded permanently (the dump is the delivery — KV-prefix-stable append). The subconscious: a persistent resident on the conversation-fork template — isolated slot subconscious/<primary>, merged unfiltered view (own + shared slots) under WindowedPassthroughStrategy, excluded from every broadcast fan-out, never primary, proseRouting explicit, tool surface deliver_summary / cancel_tuneout / note_disposition / speak_in_channel (off by default pending the voice-block canary) plus think/skip_reply/end_turn. Voice doctrine mechanically enforced: its words reach the resident verbatim under participant 'Subconscious' (Context Manager precedent); host framing is system-styled bracket notices and the backlog wrapper, never voiced. Plumbing folded in (would have been separate PRs): targetAgents honored on the channel-incoming fan-out (declared-but-dead field goes live, mirroring the push path; untargeted broadcast unchanged); per-agent message delivery (addMessage {forAgent} with deferral/turn-alive evaluated against the target's state, flushed at the target's boundaries; gate self-wake notices deliver to the waking agent). TEMPORARY: @animalabs/context-manager pinned to the cm PR branch (Meganeuridae/context-manager#feat/strategy-view-composition); flips to the published version at integration, after cm#54 merges. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tune-out-lifecycle: durable third state — round-trip, restart-surviving wake counts, epoch supersession, malformed-record replay safety. channel-incoming-targeting: fan-out asserted at the pendingRequests seam (MockMembrane hands its whole queue to the first stream, so concurrent two-agent turns starve one and restart-loop — harness limitation). per-agent-delivery: target-scoped deferral, survival across the primary's boundary, loud unknown-target drop. tune-out-e2e: the full arc against a real stdio MCPL fixture (0.5 handshake, initial-policy Request, itemized register): divert/stamp/ exclude + merged-view visibility + suppressed-mention ack + coalesced wake with durable count + max-wakes auto-cancel + capped dump + the subconscious's cancel-report notice + permanent exclusion after cancel. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Antra's requirement for named side agents: same settings as the main agent for reasoning effort, attachment size, and kin. The subconscious's AgentConfig now spreads the primary's config as its base (the conversation-fork idiom), overriding only identity- and surface-defining fields (name, mode-block systemPrompt, strategy instance, explicit proseRouting, restricted tool surface). thinking, providerParams, maxTokens/maxStreamTokens, contextBudgetTokens, cacheTtl, promptCaching, temperature, and refusalHandling — present and future fields alike — ride along automatically. Its windowed strategy mirrors the principal's strategy.maxMessageTokens so both truncate oversized tool results and attachments under the same policy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per antra (#tuneout-talk): the subconscious is affected BY main's wake gate, it does not replace it. The gate verdict (already riding the incoming event as triggerInference) composes as a conjunct on the divert-wake condition: gate-suppressed events — muted authors, rate-limited patterns, whatever the resident configured — divert silently into the backlog and surface in cadence summaries, but wake nobody. Gate-suppressed mentions also get NO deterministic reaction: main would not have signaled either, and reacting would leak the resident's standing mute to its subject. Four-case unit coverage (gate × addressed/privileged), incl. the still-stamped guarantee on the suppressed path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Approved by antra (#tuneout-talk): optional. The enter params gain durationSeconds; when set, the epoch carries an absolute expiresAtMs deadline and auto-cancels through the standard cancel flow (dump + notices + resident wake, reason 'duration elapsed'). Addresses the forgotten-channel failure mode: a quiet diverted channel now has a guaranteed return path the resident committed to at entry. Deadlines survive restarts — an epoch that expired while the host was down cancels at resume, delivering the backlog on the promised schedule. Unset = until cancelled, exactly as before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per antra (#tuneout-talk): dispositions need not repeat per invocation — but they must never wash away, because the subconscious has no long-term memory. Both constraints pick the same mechanism: a system-position ContextInjection on its compiles. Never in the timeline (no repetition), structurally outside window content (cannot scroll out), and the bytes change only when a disposition changes (KV-friendly). Invocation notices are now pure triggers; the durable snapshot slot and note_disposition are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…very Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tune-out: subconscious summaries instead of unsubscribing (#77)
Implements the full design from #77 (design comment + antra's decisions: no staged intermediate; Disco-Elysium-style delivery; no words in models' mouths). Reviewed as one unit with context-manager#54 (draft, stacked): cm supplies
viewFilter, merged auxiliary views, andWindowedPassthroughStrategy; this PR supplies everything else.The arc (pinned end-to-end by a real-MCPL-fixture test)
tune_out(agent tool, per channel: cadence / backlog cap / max-wakes / optional duration) → the channel's third desired statetuned-out(durable in themcpl/channel-lifecycleappend-log, transport stays open) → incoming traffic is stamped (metadata.tuneOut = {epochId}), stored in the shared slot, and never wakes the residents nor enters their compiled view (cmviewFilterat the view choke point — no emission leak, no L1-compression leak) → addressed messages that pass the resident's wake gate get a deterministicchannels/acknowledge(intent: 'suppressed-tuned-out', grant-gated, thechannel_declineprecedent) and a coalesced subconscious wake; gate-privileged authors wake identically → wakes are counted durably in the lifecycle log (restart cannot reset the budget) with auto-cancel past max-wakes → cancel (agent, subconscious, auto, or duration expiry) delivers a system-framed capped dump (<tuned-out-backlog …>, the<backscroll>precedent) plus one resident wake and a subconscious report turn; stamped originals stay view-excluded permanently (the dump is the delivery — KV-prefix-stable append, no retro-insertion).The subconscious
A persistent registered resident on the conversation-fork template: isolated slot
subconscious/<primary>, merged view (own slot + residents' shared slot, unfiltered — the backlog is precisely what it exists to see) underWindowedPassthroughStrategy(coarse anchor = start of the oldest active tune-out). Excluded from every broadcast fan-out; never primary;proseRouting: 'explicit'so a timer-triggered turn can never leak bare prose to the default locus. Tool surface:deliver_summary/cancel_tuneout/note_disposition/speak_in_channel(the last off by default pending the voice-block canary), plus think/skip_reply/end_turn — no channel lifecycle, no gate rules, no workspace. Its AgentConfig spreads the principal's config as its base (fork-template idiom) — reasoning effort (thinking), provider params, token ceilings, caching, refusal handling all inherit, present and future fields alike — and its strategy mirrors the principal'smaxMessageTokens(antra: named side agents run under the same settings as the main agent).Voice doctrine, mechanically enforced: everything the resident reads from the subconscious is its verbatim text under its own participant name (
Subconscious— theContext Managerprecedent). Host-authored framing is system-styled and never voiced:[Tune-out: …]bracket notices (the[Gate: …]precedent) and the backlog wrapper. The mode block is recipe-side config (subconscious.systemPrompt), co-authored with the resident, so the Mica→Fable canary iterates without code changes. Register: second person toward the resident; report-shaped.Designer review (antra, #tuneout-talk, 2026-08-08 → 2026-08-18)
Every deviation below was reviewed with antra directly. Outcomes: registry-owned routing + chronicle-durable state endorsed; gate composition added at her direction — the resident's wake gate PRECONDITIONS subconscious wakes: the subconscious is affected by main's gate, not a replacement for it, so a gate-muted author's mention diverts silently with no reaction that would leak the mute; optional
durationSecondsapproved and added — a tune-out can carry a bounded deadline that auto-cancels through the standard flow and survives restarts (closes the forgotten-channel failure mode); the five minor deviations approved as a batch, with the dispositions carrier walked through separately. She also flagged the pre-existingcanBeTriggeredByfan-out inconsistency as possibly warranting its own issue — tracked separately; this PR only makes the channel-incoming path honortargetAgents.Plumbing that rides along (would have been separate PRs; folded per review preference)
targetAgentshonored on the channel-incoming fan-out — the declared-but-dead field goes live, mirroring the push path; untargeted events keep the historical broadcast byte-for-byte.addMessage(…, {forAgent})with the deferral/turn-alive guard evaluated against the target's turn state and flushed at the target's boundaries; gate self-wake notices deliver to the waking agent. Default path byte-identical (pinned by test).channel_openon a tuned-out channel refuses with a pointer at the cancel flow; machine-sourced closes treat tuned-out as stated agent intent; malformed tuned-out records drop at replay (old readers degrade safely).Deviations from the issue text (each deliberate, none silent)
A clause-by-clause drift review against #77's design write-up ran before this PR; two drifts it caught were fixed rather than shipped (gate-privileged authors wake like mentions; every non-subconscious-initiated cancel gives the subconscious a report turn). What remains deviates knowingly, each reviewed above:
triggerSources/canBeTriggeredByplumbing). The gate's decision is a bare boolean at the registry seam, its wake fan-outs bypasscanBeTriggeredBy, and itsmatchCountdies with the process — while the registry holds serverId+channelId+tags+desired-state and the lifecycle log gives max-wakes restart durability. The gate still governs everything as a precondition (see Designer review).durationSecondsdeadline covers the total-quiet return path.Subconscious(issue example:<agent>-subconscious) — per the Disco-Elysium decision and theContext Managertitle-case-functional-voice precedent. Recipe-configurable.note_dispositionare exactly as specced.tune-out-compression-framing.test.ts). The builders strip exactly four classes and preserve participants, so the backlog wrapper, notices, andSubconsciousattribution reach the summarizer intact. First-person output folding is deliberately unconstrained (antra: "fine and expected").period/summary cadence: read as apposition, then resolved with antra directly — optionaldurationSecondsadded ("optional is fine"). Expiry runs the standard cancel flow, reason "duration elapsed"; deadlines survive restarts.Dependency note (temporary)
@animalabs/context-manageris pinned to the cm PR branch (github:Meganeuridae/context-manager#feat/strategy-view-composition). At integration: merge cm#54 → publish → flip this to the published version. That flip is the one intended change between approval and merge.Tests
tune-out-lifecycle.test.ts— durable state: round-trip, restart-surviving wake counts, epoch supersession, malformed-record replay.tune-out-gate-composition.test.ts— gate preconditions wakes and reactions (4 cases) + duration expiry (immediate-at-resume, on-schedule, param stamping).channel-incoming-targeting.test.ts/per-agent-delivery.test.ts— plumbing neutrality + targeting, asserted at the fan-out seam.tune-out-e2e.test.ts— the full arc against a real stdio MCPL fixture (0.5 handshake, initial-policy Request, itemized register): divert/stamp/exclude + merged-view visibility + ack + coalesced wake with durable count + max-wakes auto-cancel + capped dump + the subconscious's cancel-report notice + permanent exclusion after cancel.Not in this PR
Fleet deployment (gated on the voice-block canary via Mica, per #77), conhost recipe surface for
subconscious/tune_outconfig, the possiblecanBeTriggeredBycleanup issue, and mid-run per-channel cadence adjustment (cancel + re-enter covers v1).🤖 Generated with Claude Code