How Compact and Clear Context Triggers Use Two-Phase Timer Architecture in Munder Difflin
Compact and clear context triggers employ a two-phase "timer-gate" architecture that first waits for a configurable time interval, then validates context usage against a percentage threshold before queuing the slash command, ensuring housekeeping only occurs when necessary.
Managing terminal context efficiently is critical for long-running AI agents in the chaitanyagiri/munder-difflin repository. The context triggers compact and clear automate this cleanup through a sophisticated two-phase timer system that balances periodic maintenance with performance optimization.
The Two-Phase Timer-Gate Design
The architecture separates timing concerns from execution logic through distinct gates. According to the source code in src/renderer/src/hooks/useHive.ts, the fire function (around line 1094) orchestrates this dual-phase validation before enqueuing either /compact or /clear commands.
Phase 1: Time-Based Gate (everyMs)
The first phase is controlled by a standard JavaScript timer mechanism. A setTimeout or setInterval is armed using the everyMs property defined in the ContextRule interface (located in src/shared/triggers.ts lines 77-88).
When the interval elapses, the timer fires and enters the trigger logic. This ensures the system only evaluates context state at periodic intervals rather than continuously polling. The default configuration specifies a 2-hour interval (7,200,000ms) for both triggers, though this is customizable via user configuration.
Phase 2: Context Fill Gate (minContextPct)
Immediately after the timer fires, the second gate validates whether action is actually necessary. The system checks the agent's current contextFill percentage against minContextPct (or minContextPctLargeWindow for very large windows).
If the context fill exceeds the threshold, the command is queued; otherwise, the trigger simply re-arms the timer without executing. This prevents needless compaction on idle agents or those with minimal context usage, while guaranteeing housekeeping occurs once the window becomes sufficiently full.
// src/renderer/src/hooks/useHive.ts (lines 1094-1101)
const fire = (action: 'compact' | 'clear', rule: ContextRule) => {
// Phase 1 complete: timer has fired
if (!rule.enabled) return; // Gate check: enabled?
if (contextFill < rule.minContextPct) return; // Phase 2: context threshold
enqueueCommand(action, rule.message); // Execute: queue /compact or /clear
};
Source Code Implementation
The two-phase architecture relies on three core components that handle configuration, defaults, and runtime execution.
ContextRule Schema and Defaults
The trigger configuration structure is defined in src/shared/triggers.ts (lines 77-88), specifying the parameters for both gates:
interface ContextRule {
enabled: boolean;
everyMs: number; // Phase 1: timer interval
minContextPct: number; // Phase 2: threshold for normal windows
minContextPctLargeWindow: number; // Phase 2: threshold for large windows
message: string; // Context preservation hint
}
Default values established in lines 133-141 set conservative baselines: compact triggers every 2 hours at 60% fill, while clear uses the same interval at 90% fill.
Configuration Merging
To ensure the timer always operates with complete rule objects, src/main/config.ts provides the withTriggerDefaults function (lines 11-14). This deep-merges persisted user configuration with DEFAULT_CONTEXT_TRIGGER constants, guaranteeing that optional partial configs still contain valid everyMs and minContextPct values for the two-phase logic.
Configuring Context Triggers
Users customize the dual-gate behavior through the contextTrigger section of their configuration file. Adjusting everyMs modulates the timer frequency, while changing minContextPct tightens or loosens the execution condition.
{
"contextTrigger": {
"compact": {
"enabled": true,
"everyMs": 3600000, // Phase 1: fire every hour
"minContextPct": 50, // Phase 2: fire at 50% fill
"minContextPctLargeWindow": 30,
"message": "Keep the current task and next step"
},
"clear": {
"enabled": false,
"everyMs": 7200000,
"minContextPct": 90,
"minContextPctLargeWindow": 80,
"message": ""
}
}
}
Manual Execution and Debugging
For scenarios requiring immediate context management, the two-phase architecture can be bypassed entirely by manually queuing commands.
Bypassing the Timer-Gate
The following pattern skips both the time-based and context-fill gates, executing the command immediately:
import { compactionCommandForProvider } from '@/shared/providerAutomation';
const cmd = compactionCommandForProvider(provider, {
enabled: true,
everyMs: 0, // Irrelevant for manual invocation
minContextPct: 0, // Skip context check
minContextPctLargeWindow: 0,
message: 'Summarise the sprint goal'
});
store.enqueueMessage(agentId, cmd);
Observing Timer Cadence
To verify the Phase 1 timer is firing correctly, insert diagnostic logging in src/renderer/src/hooks/useHive.ts:
const fire = (action, rule) => {
console.log(`[trigger] ${action} timer fired; enabled=${rule.enabled}`);
// ...existing gate logic
};
This log appears each time the time-based gate activates, independent of whether the context-fill gate permits execution.
Summary
- Compact and clear context triggers use a two-phase architecture separating timing from execution logic.
- Phase 1 uses
everyMsto establish periodic evaluation cadence viasetTimeout. - Phase 2 validates
contextFillagainstminContextPctbefore queuing commands, preventing unnecessary operations. - Configuration resides in
src/shared/triggers.tswith runtime logic insrc/renderer/src/hooks/useHive.ts. - Manual bypass is possible by directly calling
compactionCommandForProviderand enqueuing messages outside thefirefunction.
Frequently Asked Questions
What happens if context usage never exceeds the threshold?
If the agent's context fill remains below minContextPct when the timer fires, the trigger skips execution and re-arms the timer for the next interval. This cycle repeats indefinitely until either the context grows sufficiently or the timer is disabled, ensuring zero-overhead for idle agents.
How do I adjust the timer interval for compact triggers?
Modify the everyMs property in your config.json under contextTrigger.compact. This value represents milliseconds between Phase 1 evaluations. For example, setting "everyMs": 3600000 creates an hourly check, while the default 7200000ms creates a 2-hour cycle.
Can I disable the automatic triggers while keeping manual commands?
Yes. Set "enabled": false for either trigger in your configuration. This disables the two-phase timer logic in useHive.ts, but manual invocations via compactionCommandForProvider remain fully functional since they bypass the fire function entirely.
Where does the timer state live in the application architecture?
The active timer references and fire callback logic reside in the renderer process within src/renderer/src/hooks/useHive.ts. However, the configuration defaults and merging logic exist in the main process at src/main/config.ts, with the trigger schema defined in the shared module at src/shared/triggers.ts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →