Problem
MeetingProtocolConfig.auto_create_tasks defaults to True. When enabled, MeetingOrchestrator._create_tasks() spawns Task objects from action items extracted from meeting minutes. If task execution emits events that match MeetingScheduler.trigger_event() patterns, new meetings are triggered.
MeetingScheduler.trigger_event() has no deduplication or cooldown -- any matching event fires the meeting immediately:
matching = tuple(mt for mt in self._config.types if mt.trigger == event_name)
async with asyncio.TaskGroup() as tg:
tasks = [tg.create_task(self._execute_meeting(mt, context)) for mt in matching]
This creates a potential unbounded loop:
- Sprint planning generates action items -> tasks created
- Task completion emits events -> post-sprint meeting triggered
- Post-sprint generates more action items -> more tasks
- Repeat until budget hard stop
The only backstop today is the budget enforcer, which is blunt and expensive to hit.
Source: docs/research/multi-agent-failure-audit.md (R1), closes research #690.
Solution
Add min_interval_seconds: int | None = None to MeetingTypeConfig (in communication/meeting/config.py). Track last-triggered timestamps in MeetingScheduler per meeting type. In trigger_event(), skip execution if the meeting was triggered within min_interval_seconds.
Alternatively (or additionally): add max_tasks_per_meeting: int | None = None to MeetingProtocolConfig to cap how many tasks a single meeting can spawn.
Files
src/synthorg/communication/meeting/config.py -- add min_interval_seconds field
src/synthorg/communication/meeting/scheduler.py -- enforce cooldown in trigger_event()
Problem
MeetingProtocolConfig.auto_create_tasksdefaults toTrue. When enabled,MeetingOrchestrator._create_tasks()spawnsTaskobjects from action items extracted from meeting minutes. If task execution emits events that matchMeetingScheduler.trigger_event()patterns, new meetings are triggered.MeetingScheduler.trigger_event()has no deduplication or cooldown -- any matching event fires the meeting immediately:This creates a potential unbounded loop:
The only backstop today is the budget enforcer, which is blunt and expensive to hit.
Source:
docs/research/multi-agent-failure-audit.md(R1), closes research #690.Solution
Add
min_interval_seconds: int | None = NonetoMeetingTypeConfig(incommunication/meeting/config.py). Track last-triggered timestamps inMeetingSchedulerper meeting type. Intrigger_event(), skip execution if the meeting was triggered withinmin_interval_seconds.Alternatively (or additionally): add
max_tasks_per_meeting: int | None = NonetoMeetingProtocolConfigto cap how many tasks a single meeting can spawn.Files
src/synthorg/communication/meeting/config.py-- addmin_interval_secondsfieldsrc/synthorg/communication/meeting/scheduler.py-- enforce cooldown intrigger_event()