All Mermaid figures referenced by ../DEFENSIVE-PUBLICATION.md, collected here with captions. They render natively on GitHub.
Both gates resolve before any model/tool call; the engine only enqueues.
flowchart TB
subgraph CFG[Configuration Bridge]
I[scan_interval_ms]
K[per-type cooldowns]
AV[per-type role availability]
end
T[Scan Timer] -->|every interval| L{"For each registered,<br/>non-cold agent"}
L --> SRC
subgraph SRC[Single-pass Signal Scan]
D[Deadline Source]
S[Stale-item Source]
PT[Pattern / Remediation Source]
PC[Peer-coordination Source]
end
D --> CAND[("Candidate Action Set<br/>typed: tau")]
S --> CAND
PT --> CAND
PC --> CAND
CAND --> GA{"Availability Gate<br/>A of role and tau?"}
AV -.-> GA
GA -- no --> X1[drop]
GA -- yes --> GC{"Cooldown Gate<br/>now minus last >= cooldown of tau?"}
K -.-> GC
GC -- no --> X2[drop]
GC -- yes --> EQ["Enqueue item +<br/>set last of agent,tau = now"]
EQ --> EV[emit observability event]
EQ --> Q[(Agent Work Queue)]
Q --> CL["Existing Cognitive Loop<br/>executes / acts"]
I -.-> T
Lifecycle of a single candidate action through the two gates.
stateDiagram-v2
[*] --> Emitted: source produces candidate (type=tau)
Emitted --> AvailabilityCheck
AvailabilityCheck --> Dropped_Role: A(role,tau)=0
AvailabilityCheck --> CooldownCheck: A(role,tau)=1
CooldownCheck --> Dropped_Cooldown: now-last < cooldown(tau)
CooldownCheck --> Enqueued: now-last >= cooldown(tau)
Enqueued --> LedgerUpdated: set last(agent,tau)=now
LedgerUpdated --> EventEmitted: emit proactive:action
EventEmitted --> [*]
Dropped_Role --> [*]
Dropped_Cooldown --> [*]
COOLDOWN_LEDGER is keyed on (agent_id, type) — the per-action-type cooldown made concrete. There is no per-item key in the primary cooldown table.
erDiagram
AGENT ||--o{ REGISTRATION : "registered in"
AGENT ||--o{ COOLDOWN_LEDGER : "has cooldowns"
AGENT ||--o{ QUEUE_ITEM : "owns"
ACTION_TYPE ||--o{ COOLDOWN_LEDGER : "keyed by"
ACTION_TYPE ||--o{ AVAILABILITY_RULE : "governed by"
ACTION_TYPE ||--o{ QUEUE_ITEM : "classifies"
AGENT {
string agent_id PK
string role
string activity_tier
}
REGISTRATION {
string agent_id PK
timestamp registered_at
}
ACTION_TYPE {
string type PK
int cooldown_ms
}
COOLDOWN_LEDGER {
string agent_id PK
string type PK
timestamp last_executed_at
}
AVAILABILITY_RULE {
string type PK
string allowed_roles
}
QUEUE_ITEM {
string item_id PK
string agent_id FK
string type
string channel
string priority
string subject
string body
string metadata
string source_app
timestamp created_at
}
Sequence for the worked example, showing class-cooldown suppression on the second tick.
sequenceDiagram
participant Timer
participant Engine
participant Sources
participant AvailGate as Availability Gate
participant CDGate as Cooldown Gate
participant Ledger
participant Queue
Timer->>Engine: tick t0
Engine->>Sources: scan(agent-bob)
Sources-->>Engine: [deadline-reminder, stale-review]
Engine->>AvailGate: (member, deadline-reminder)?
AvailGate-->>Engine: allow
Engine->>CDGate: now - last(bob,deadline-reminder) >= 1h?
CDGate->>Ledger: get(bob, deadline-reminder)
Ledger-->>CDGate: null
CDGate-->>Engine: pass
Engine->>Queue: enqueue(deadline-reminder)
Engine->>Ledger: set(bob, deadline-reminder)=t0
Note over Engine,Queue: stale-review also enqueued (first time)
Timer->>Engine: tick t1 (t0+2min)
Engine->>Sources: scan(agent-bob)
Sources-->>Engine: [stale-review]
Engine->>AvailGate: (member, stale-review)?
AvailGate-->>Engine: allow
Engine->>CDGate: now - last(bob,stale-review) >= 4h?
CDGate->>Ledger: get(bob, stale-review)
Ledger-->>CDGate: t0
CDGate-->>Engine: FAIL (2min < 4h)
Note over Engine,Queue: dropped — no duplicate stale spam