Grand Diomande Research · Full HTML Reader

Cross-Pollination Architecture Specification

1. [Overview](#1-overview) 2. [Prediction Engine](#2-prediction-engine) 3. [Safety Rails](#3-safety-rails) 4. [ACC Integration — Swipeable Prediction Cards](#4-acc-integration--swipeable-prediction-cards) 5. [Autonomous Mode](#5-autonomous-mode) 6. [Push Notifications](#6-push-notifications) 7. [Feedback Loop & DPO Training](#7-feedback-loop--dpo-training) 8. [API Reference](#8-api-reference) 9. [Data Models](#9-data-models) 10. [Deployment & Configuration](#10-deployment--configuration)

Agents That Account for Themselves architecture technical paper candidate score 70 .md

Full Public Reader

Cross-Pollination Architecture Specification

> PULSE V1 — Wave 1 Deliverable [A3]
> Status: COMPLETE | Author: Clawdbot Agent | Date: 2025-07-29

---

Table of Contents

1. [Overview](#1-overview)
2. [Prediction Engine](#2-prediction-engine)
3. [Safety Rails](#3-safety-rails)
4. [ACC Integration — Swipeable Prediction Cards](#4-acc-integration--swipeable-prediction-cards)
5. [Autonomous Mode](#5-autonomous-mode)
6. [Push Notifications](#6-push-notifications)
7. [Feedback Loop & DPO Training](#7-feedback-loop--dpo-training)
8. [API Reference](#8-api-reference)
9. [Data Models](#9-data-models)
10. [Deployment & Configuration](#10-deployment--configuration)

---

1. Overview

What Is Cross-Pollination?

Cross-pollination is the system by which PULSE agents proactively generate, predict, and execute work without explicit human instruction. A personal Twin model predicts the user's likely next actions, routes them to the appropriate agent channel, and optionally executes autonomously — all while maintaining strict safety rails and a human-in-the-loop escape hatch.

Why It Matters

Traditional AI assistants are reactive: the user speaks, the assistant responds. Cross-pollination inverts this. The system observes the user's patterns (time of day, project state, recent conversations, unfinished tasks) and generates the next move before the user asks. The user's role shifts from director to reviewer — swipe right to approve, swipe left to dismiss.

This is the core mechanism behind the "Age of Leisure" vision: agents that work while you walk.

System Boundaries

┌─────────────────────────────────────────────────────────────────────┐
│                        PULSE Ecosystem                              │
│                                                                     │
│  ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────────────┐ │
│  │   ACC    │   │ Life OS  │   │VisionClaw│   │    Compass       │ │
│  │ (phone)  │   │ (watch)  │   │(glasses) │   │    (web)         │ │
│  └────┬─────┘   └────┬─────┘   └────┬─────┘   └───────┬──────────┘ │
│       │              │              │                  │            │
│       └──────────────┴──────────────┴──────────────────┘            │
│                              │                                      │
│                    ┌─────────▼──────────┐                           │
│                    │  Pulse Gateway API │                           │
│                    │  /api/v1/*         │                           │
│                    └─────────┬──────────┘                           │
│                              │                                      │
│              ┌───────────────┼───────────────┐                      │
│              │               │               │                      │
│     ┌────────▼───────┐ ┌────▼────┐ ┌────────▼──────┐               │
│     │  CrossPoll     │ │  Twin   │ │  Prediction   │               │
│     │  Engine        │ │  Model  │ │  Store        │               │
│     └────────┬───────┘ └────┬────┘ └────────┬──────┘               │
│              │               │               │                      │
│              └───────────────┴───────────────┘                      │
│                              │                                      │
│                    ┌─────────▼──────────┐                           │
│                    │  Clawdbot Gateway  │                           │
│                    │  (Agent Sessions)  │                           │
│                    └────────────────────┘                           │
└─────────────────────────────────────────────────────────────────────┘

Key Terminology

TermDefinition
Cross-pollinationAn autonomous agent action triggered by prediction rather than user command
PredictionA generated prompt + target channel + confidence score
Twin ModelUser's fine-tuned LLM (SFT/DPO on conversation history) that mimics their decision style
Prediction CardUI element in ACC showing a pending prediction (swipeable)
Autonomous Mode30-minute silence-triggered cron that generates and optionally executes predictions
DPO PairA (chosen, rejected) training example derived from user accept/reject of predictions
Safety RailRate limits, cost caps, loop detectors, and kill switches that constrain autonomy

---

2. Prediction Engine

2.1 Architecture

The Prediction Engine is the brain of cross-pollination. It determines what to suggest, where to route it, and how confident it is.

┌──────────────────────────────────────────────────────┐
│                  Prediction Engine                     │
│                                                        │
│  ┌─────────────┐   ┌──────────────┐   ┌────────────┐ │
│  │   Context    │   │    Twin      │   │  Channel   │ │
│  │  Assembler   │──▶│   Model      │──▶│  Router    │ │
│  │             │   │  (Together)  │   │            │ │
│  └──────┬──────┘   └──────────────┘   └─────┬──────┘ │
│         │                                    │        │
│    ┌────▼────┐                         ┌─────▼─────┐  │
│    │ Signals │                         │ Prediction│  │
│    │ Collector│                        │ Emitter   │  │
│    └─────────┘                         └───────────┘  │
└──────────────────────────────────────────────────────┘

2.2 Signal Collection

The Context Assembler gathers input signals to form a prediction context:

SignalSourceWeightDescription
Time of daySystem clock0.3Morning = review/plan, afternoon = deep work, evening = summarize
Silence durationLast user message timestamp0.2Longer silence → higher autonomy willingness
Active tasksTask Store (`/api/v1/tasks`)0.2Stale tasks get priority
Recent conversationsAgent session history0.15Continue threads, follow up on open questions
Git stateRepository hooks0.1Uncommitted changes, open PRs, failing CI
CalendarCalendar integration0.05Upcoming meetings → prepare context

2.3 Twin Model Prediction

When the Twin SFT/DPO model is available, the Prediction Engine delegates to it for high-fidelity predictions:

POST /api/v1/twin/serve (internal)
{
  "model": "twin-sft-v1",
  "messages": [
    {
      "role": "system",
      "content": "You are a prediction engine for Mohamed's AI agent system. Based on the current context, predict the single most valuable action to take right now. Return JSON: {\"prompt\": string, \"channel\": string, \"confidence\": 0-1, \"reasoning\": string}"
    },
    {
      "role": "user",
      "content": "<assembled context: time, tasks, recent activity, silence duration>"
    }
  ],
  "temperature": 0.3,
  "max_tokens": 200
}

Fallback (pre-Twin): Template-based generation using time-of-day heuristics and channel rotation. This is the current implementation in `crosspoll-engine.ts`:

typescript
// Time-of-day prompt templates (simplified)
const PROMPT_TEMPLATES = {
  morning: ["What's the most important task for today?", ...],
  afternoon: ["What tasks have been stuck the longest?", ...],
  evening: ["Summarize today's progress across all channels.", ...],
};

2.4 Channel Routing

Each prediction targets a specific agent channel based on task type:

ChannelSpecialtyExample Predictions
`koji-core`Code, builds, tests"Run tests on the most recently changed modules"
`chronicle-hub`Documentation, writing"Update CHANGELOG for the last 5 commits"
`dream-weaver`Creative exploration"Brainstorm 3 alternative approaches to the caching problem"
`research`Analysis, investigation"Research competitor pricing changes this week"
`garden`Knowledge graph, memory"Update the project dependency graph"

Routing logic:

1. Twin model suggests channel (if available)         → use it
2. Task has explicit channel affinity (code → koji)   → use affinity
3. Round-robin across channels not triggered recently  → balance load
4. Weighted random based on time-of-day patterns      → fallback

2.5 Confidence Scoring

Every prediction receives a confidence score `[0.0, 1.0]`:

RangeMeaningAction
`0.0 - 0.29`Low confidenceDiscard, do not show to user
`0.3 - 0.59`Medium confidenceShow as prediction card, do NOT auto-execute
`0.6 - 0.79`High confidenceShow as prediction card with "suggested" badge
`0.8 - 1.0`Very high confidenceAuto-execute in autonomous mode (with safety rails)

Confidence factors:

confidence = (
  twin_model_confidence * 0.4 +          // Twin's self-reported confidence
  pattern_match_score * 0.25 +            // How well this matches historical patterns
  task_urgency_score * 0.2 +              // Stale tasks boost urgency
  time_relevance_score * 0.15             // Morning review gets boost at 8am
)

---

3. Safety Rails

Safety rails are non-negotiable. Autonomous agents without constraints are a liability. Every cross-pollination action passes through the safety pipeline before execution.

3.1 Safety Architecture

┌──────────────────────────────────────────────────────────────┐
│                     Safety Pipeline                           │
│                                                                │
│  Prediction ──▶ [Rate Limiter] ──▶ [Loop Detector] ──▶       │
│                                                                │
│  ──▶ [Cost Cap] ──▶ [Content Filter] ──▶ [Human Gate] ──▶    │
│                                                                │
│  ──▶ [Kill Switch Check] ──▶ ✅ Execute / ❌ Block            │
│                                                                │
└──────────────────────────────────────────────────────────────┘

3.2 Rate Limiter

Prevents prediction/execution spam:

ParameterDefaultConfigurableDescription
`crossPollIntervalMs`600,000 (10 min)YesMinimum time between triggers
Max triggers per hour6DerivedFrom interval × 60min
Burst allowance0NoNo burst — strict interval enforcement

Implementation (existing in `crosspoll-engine.ts`):

typescript
if (lastTriggerAt) {
  const elapsed = Date.now() - new Date(lastTriggerAt).getTime();
  if (elapsed < config.crossPollIntervalMs) {
    return { triggered: false, message: `Rate limited: ${remaining}s` };
  }
}

3.3 Loop Detection

Detects and breaks agent-to-agent infinite loops:

Loop: Agent A predicts → triggers Agent B → Agent B predicts → triggers Agent A → ...

Detection algorithm:

Track: recentChains[] = [{ from, to, timestamp }]

For each new trigger(from, to):
  1. Check if (to → from) exists in recentChains within last 5 minutes
  2. Check if chain depth > 3 (A→B→C→D = depth 4 → BLOCK)
  3. Check if same (from, to) pair triggered > 2x in last 10 minutes

If any condition met → BLOCK + log warning + increment loopDetectionCount

Data structure:

typescript
interface ChainLink {
  fromChannel: string;
  toChannel: string;
  predictionId: string;
  timestamp: string;
}

// Rolling window of last 50 chain links
const recentChains: ChainLink[] = [];
const MAX_CHAIN_DEPTH = 3;
const LOOP_WINDOW_MS = 300_000; // 5 minutes
const DUPLICATE_THRESHOLD = 2;

3.4 Cost Caps

Prevents runaway spending from autonomous operations:

CapDefaultScopeAction on Breach
Per-hour cost$2.00Sliding 1-hour windowBlock all triggers until window resets
Per-day cost$20.00Calendar day (midnight reset)Block + notify user
Per-trigger estimate$0.05 | Single execution | Used for tracking (1 agent turn ≈ $0.05)
Per-month budget$200.00Calendar monthHard stop + require manual re-enable

Implementation:

typescript
function estimateCostThisHour(): number {
  cleanHourlyTriggers();
  return hourlyTriggers.length * COST_PER_TRIGGER;
}

if (estimateCostThisHour() >= config.crossPollCostCapPerHour) {
  return { triggered: false, message: `Cost cap reached` };
}

3.5 Content Filter

Prevents the prediction engine from generating harmful, destructive, or out-of-scope actions:

RuleDescription
No destructive commandsBlock predictions containing `rm -rf`, `drop table`, `delete production`
No external communicationBlock predictions that would send emails, tweets, or public messages
No financial actionsBlock predictions involving payments, transfers, purchases
Scope boundaryPredictions must target registered channels only

3.6 Human-in-the-Loop Triggers

Certain conditions always escalate to the human, regardless of confidence:

┌─────────────────────────────────────────────────┐
│          Human-in-the-Loop Trigger Matrix         │
├──────────────────────┬──────────────────────────┤
│ Condition            │ Action                    │
├──────────────────────┼──────────────────────────┤
│ confidence < 0.6     │ Show card, don't execute  │
│ Estimated cost > $1  │ Require explicit approval │
│ Targets production   │ Always require approval   │
│ Novel channel combo  │ Show card + "NEW" badge   │
│ 3+ rejections in row │ Pause autonomous mode     │
│ Content filter hit   │ Block + log for review    │
│ Daily cost > 80%     │ Notify + request guidance │
│ First run ever       │ Onboarding mode: all cards│
└──────────────────────┴──────────────────────────┘

3.7 Kill Switch

Emergency stop for all cross-pollination activity:

POST /api/v1/crosspoll/trigger { "force": false }
→ Checks config.crossPollEnabled

Gateway config:
  plugins:
    entries:
      pulse-gateway-api:
        config:
          crossPollEnabled: false  ← Master kill switch

Kill switch hierarchy:

1. Gateway config (`crossPollEnabled: false`) — survives restarts
2. API toggle (`POST /api/v1/crosspoll/config { enabled: false }`) — runtime only
3. ACC UI toggle — sends API toggle via Gateway
4. Watch complication — long-press to emergency stop

---

4. ACC Integration — Swipeable Prediction Cards

4.1 Mission Control Layout

Prediction cards appear in the ACC (Agent Command Center) Mission Control view as a horizontally swipeable card stack:

┌─────────────────────────────────────────────┐
│  Mission Control                         ⚙️  │
├─────────────────────────────────────────────┤
│                                             │
│  ┌─── Active Agents ───────────────────┐   │
│  │  🤖 koji-core    ● running  3.2k tk │   │
│  │  📝 chronicle    ● idle     1.1k tk │   │
│  │  🔬 research     ● running  5.4k tk │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─── Cross-Pollination ───── ON 🟢 ──┐   │
│  │                                     │   │
│  │  ┌──────────────────────────────┐   │   │
│  │  │  🔮 Prediction               │   │   │
│  │  │                              │   │   │
│  │  │  "Run tests on recently      │   │   │
│  │  │   changed modules"           │   │   │
│  │  │                              │   │   │
│  │  │  → koji-core  ⬤ 72%         │   │   │
│  │  │                              │   │   │
│  │  │  ← DISMISS    APPROVE →     │   │   │
│  │  └──────────────────────────────┘   │   │
│  │        · · ● · ·  (3 of 5 cards)   │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─── Recent Tasks ───────────────────┐    │
│  │  ✅ Deploy compass   2m ago        │    │
│  │  🔄 Update docs      running       │    │
│  │  ⏳ Twin fidelity    queued        │    │
│  └─────────────────────────────────────┘   │
└─────────────────────────────────────────────┘

4.2 Card Data Model

swift
// ACC (SwiftUI + TCA)
struct PredictionCard: Identifiable, Equatable {
    let id: String                    // "pred_abc123"
    let prompt: String                // "Run tests on recently changed modules"
    let targetChannel: String         // "koji-core"
    let confidence: Double            // 0.72
    let reasoning: String?            // "3 modules changed since last test run"
    let createdAt: Date
    let source: PredictionSource      // .twinModel | .templateEngine | .predictorHook
    var status: PredictionStatus      // .pending | .approved | .rejected | .expired
}

enum PredictionSource: String, Codable {
    case twinModel = "twin-model"
    case templateEngine = "pulse-store"
    case predictorHook = "predictor-hook"
}

enum PredictionStatus: String, Codable {
    case pending, approved, rejected, expired
}

4.3 Swipe Gestures

┌────────────────────────────────────┐
│                                    │
│    ←── Swipe LEFT = DISMISS ──→    │
│         (sends reject to API)      │
│         (card animates off-left)   │
│         (DPO: negative signal)     │
│                                    │
│    ←── Swipe RIGHT = APPROVE ──→   │
│         (sends confirm to API)     │
│         (triggers execution)       │
│         (DPO: positive signal)     │
│         (haptic: success)          │
│                                    │
│    ←── Swipe UP = SNOOZE ──→       │
│         (re-queue for later)       │
│         (no DPO signal)            │
│                                    │
│    ←── Long Press = DETAILS ──→    │
│         (show reasoning, cost est) │
│         (edit prompt before run)   │
│                                    │
└────────────────────────────────────┘

4.4 ACC ↔ Gateway API Flow

mermaid
sequenceDiagram
    participant ACC as ACC (iOS)
    participant GW as Pulse Gateway
    participant PS as Prediction Store
    participant CE as CrossPoll Engine

    Note over ACC: Mission Control opens
    ACC->>GW: GET /api/v1/twin/predict?count=5
    GW->>PS: latest(5)
    PS-->>GW: [predictions...]
    GW-->>ACC: { predictions, crossPollStatus }
    ACC->>ACC: Render swipeable cards

    Note over ACC: User swipes RIGHT on card
    ACC->>GW: POST /api/v1/crosspoll/trigger<br/>{ targetChannel: "koji-core",<br/>  hint: "Run tests..." }
    GW->>CE: trigger()
    CE->>CE: Safety pipeline check
    CE->>PS: confirm(predictionId)
    CE-->>GW: { triggered: true, prediction }
    GW-->>ACC: 200 OK

    Note over ACC: User swipes LEFT on card
    ACC->>GW: POST /api/v1/predictions/{id}/reject
    GW->>PS: reject(id)
    GW-->>ACC: 200 OK

4.5 Real-Time Updates (WebSocket)

ACC subscribes to WebSocket events for live prediction updates:

typescript
// WebSocket event types for cross-pollination
type CrossPollEvent =
  | { type: "prediction.new"; prediction: Prediction }
  | { type: "prediction.executed"; predictionId: string; result: TaskResult }
  | { type: "prediction.expired"; predictionId: string }
  | { type: "crosspoll.status"; status: CrossPollStatus }
  | { type: "crosspoll.paused"; reason: string }
  | { type: "crosspoll.resumed" };

---

5. Autonomous Mode

5.1 Overview

Autonomous Mode activates when the user has been silent for ≥30 minutes. It runs a cron-based cycle that generates predictions, evaluates them against safety rails, and executes high-confidence actions without user intervention.

5.2 State Machine

                    ┌──────────┐
         ┌─────────│ DISABLED  │◄────── Kill switch / config
         │         └────┬─────┘
         │              │ crossPollEnabled = true
         │              ▼
         │         ┌──────────┐
         │    ┌───▶│ WATCHING  │◄──── User sends message
         │    │    └────┬─────┘           │
         │    │         │ silence ≥ 30min │
         │    │         ▼                 │
         │    │    ┌──────────┐           │
         │    │    │ PRIMING  │           │
         │    │    │(1st pred)│           │
         │    │    └────┬─────┘           │
         │    │         │ prediction ready│
         │    │         ▼                 │
         │    │    ┌──────────┐           │
         │    │    │AUTONOMOUS│───────────┘
         │    │    │ (active) │     user speaks → reset
         │    │    └────┬─────┘
         │    │         │ 3 rejections or cost cap
         │    │         ▼
         │    │    ┌──────────┐
         │    └────│  PAUSED  │
         │         └────┬─────┘
         │              │ manual resume or 2h timeout
         │              ▼
         └─────────────(back to WATCHING)

5.3 Cron Schedule

# Autonomous cross-pollination cron (managed by Clawdbot)
# Fires every 30 minutes, checks silence condition

*/30 * * * *  clawdbot cron:run pulse-crosspoll-auto

# The cron handler:
1. Check last user message timestamp
2. If silence < 30min → skip (WATCHING state)
3. If silence ≥ 30min → enter PRIMING
4. Generate prediction via Twin model (or template fallback)
5. Run through safety pipeline
6. If confidence ≥ 0.8 AND all safety checks pass → execute
7. If confidence 0.3-0.79 → emit prediction card to ACC (no execute)
8. If confidence < 0.3 → discard
9. Log result to predictions.jsonl

5.4 Autonomous Execution Flow

mermaid
flowchart TD
    CRON[30-min Cron Fires] --> SILENCE{User silent ≥30min?}
    SILENCE -->|No| SKIP[Skip — reset timer]
    SILENCE -->|Yes| CONTEXT[Assemble Context]
    CONTEXT --> PREDICT[Twin Model Predict]
    PREDICT --> SAFETY{Safety Pipeline}
    SAFETY -->|Blocked| LOG_BLOCK[Log block reason]
    SAFETY -->|Pass| CONFIDENCE{Confidence?}
    CONFIDENCE -->|≥ 0.8| EXECUTE[Auto-Execute]
    CONFIDENCE -->|0.3–0.79| CARD[Emit Prediction Card]
    CONFIDENCE -->|< 0.3| DISCARD[Discard]
    EXECUTE --> NOTIFY[Push Notification]
    EXECUTE --> RECORD[Record for DPO]
    CARD --> NOTIFY_CARD[Notification: New suggestion]
    LOG_BLOCK --> METRICS[Update Safety Metrics]

5.5 Session Boundaries

Autonomous operations run in isolated sessions to prevent context contamination:

typescript
// Each autonomous trigger gets its own session
const autonomousSession = {
  id: `auto_${Date.now()}`,
  parentPrediction: prediction.id,
  maxTokens: 50_000,        // Hard cap per autonomous session
  maxDurationMs: 120_000,   // 2 minute max execution time
  allowedTools: [            // Restricted tool access
    "read", "write", "edit", "exec",
    "web_search", "web_fetch"
  ],
  blockedTools: [            // Never in autonomous mode
    "message", "tts", "browser"
  ],
};

5.6 Quiet Hours

Autonomous mode respects quiet hours:

PeriodBehavior
08:00 - 23:00Full autonomous mode
23:00 - 08:00Predictions generated but never auto-executed; cards queued for morning
User-defined DNDSame as quiet hours — queue, don't execute

---

6. Push Notifications

6.1 Notification Targets

Cross-pollination events push to all active PULSE surfaces:

┌──────────────────────────────────────────────────┐
│              Notification Router                   │
│                                                    │
│  Event ──▶ ┌──────────┐                           │
│            │ ACC      │ ← Rich card notification   │
│            │ (iPhone) │   with approve/reject       │
│            ├──────────┤                             │
│            │ Life OS  │ ← Watch complication update │
│            │ (Watch)  │   + haptic tap              │
│            ├──────────┤                             │
│            │VisionClaw│ ← Spoken summary via        │
│            │(Glasses) │   bone conduction speaker   │
│            ├──────────┤                             │
│            │ Discord  │ ← 🔮 embed in #bridge      │
│            │          │                             │
│            ├──────────┤                             │
│            │ Compass  │ ← Browser notification      │
│            │  (Web)   │   (if tab open)             │
│            └──────────┘                             │
└──────────────────────────────────────────────────┘

6.2 Notification Types

EventACC (Phone)Life OS (Watch)VisionClaw (Glasses)Discord
New prediction (medium conf.)Banner + cardComplication badge
New prediction (high conf.)Banner + cardHaptic + complicationSpoken alert🔮 embed
Auto-executed actionSilent update in Mission ControlComplication refresh✅ reaction
Execution completedBanner: "Done: {result}"Haptic successSpoken summaryResult embed
Execution failedAlert: "Failed: {error}"Haptic failure❌ reaction
Cost cap approachingAlert: "80
Autonomous mode pausedBanner: "Paused: 3 rejections"Complication: ⏸Status update

6.3 ACC Push Notification Payload

json
{
  "aps": {
    "alert": {
      "title": "🔮 Cross-Pollination",
      "subtitle": "koji-core • 72% confidence",
      "body": "Run tests on recently changed modules"
    },
    "sound": "prediction.caf",
    "badge": 1,
    "category": "CROSSPOLL_PREDICTION",
    "thread-id": "crosspoll"
  },
  "predictionId": "pred_abc123",
  "targetChannel": "koji-core",
  "confidence": 0.72,
  "actions": ["approve", "reject", "snooze"]
}

Actionable notification categories:

swift
// iOS notification actions (no app open required)
let approveAction = UNNotificationAction(
    identifier: "APPROVE",
    title: "✅ Approve",
    options: [.foreground]
)
let rejectAction = UNNotificationAction(
    identifier: "REJECT",
    title: "❌ Dismiss",
    options: [.destructive]
)
let snoozeAction = UNNotificationAction(
    identifier: "SNOOZE",
    title: "⏰ Later",
    options: []
)

6.4 Watch Complication

Life OS surfaces cross-pollination status via Watch complications:

┌─────────────────────────────────┐
│  Circular Complication (small)  │
│                                 │
│         🔮 3                    │
│    (3 pending predictions)      │
│                                 │
│  Tap → opens Life OS            │
│  Long press → toggle auto mode  │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│  Rectangular Complication       │
│                                 │
│  🔮 "Run tests on modules"     │
│  koji-core • 72% • 2m ago      │
│                                 │
│  Tap → approve                  │
│  Force tap → options            │
└─────────────────────────────────┘

6.5 Glasses (VisionClaw) Alerts

For high-confidence predictions while the user is wearing glasses:

[Bone conduction speaker, brief]

"Cross-pollination suggestion: Run tests on recently changed modules.
 Confidence seventy-two percent. Say 'approve' or 'skip'."

User: "Approve"
→ Voice confirmation: "Running. I'll update you when done."

---

7. Feedback Loop & DPO Training

7.1 Overview

Every user interaction with a prediction card generates a training signal. These signals are collected, formatted as DPO (Direct Preference Optimization) pairs, and periodically exported for Twin model fine-tuning.

┌──────────────────────────────────────────────────────────┐
│                    Feedback Loop                          │
│                                                           │
│  User Action ──▶ Signal Store ──▶ DPO Formatter ──▶      │
│                                                           │
│  ──▶ Training Export ──▶ Together AI Fine-Tune ──▶       │
│                                                           │
│  ──▶ Updated Twin Model ──▶ Better Predictions ──▶ ...   │
│                                                           │
└──────────────────────────────────────────────────────────┘

7.2 Signal Collection

User ActionSignal TypeDPO ValueWeight
Swipe right (approve)PositiveChosen1.0
Swipe left (reject)NegativeRejected1.0
Swipe up (snooze)Neutral— (excluded)0.0
Auto-executed → no complaintImplicit positiveChosen0.5
Auto-executed → user undoesImplicit negativeRejected0.8
Card expires (ignored)Weak negativeRejected0.3
User edits prompt then approvesCorrectiveBoth (original=rejected, edited=chosen)1.0

7.3 DPO Pair Format

jsonl
{"prompt": "<context>", "chosen": "Run tests on changed modules in koji-core", "rejected": "Summarize today's progress in chronicle-hub", "metadata": {"timestamp": "2025-07-29T14:30:00Z", "confidence_chosen": 0.72, "confidence_rejected": 0.45}}
{"prompt": "<context>", "chosen": "Check for failed pipelines in CI", "rejected": "Explore creative brainstorming ideas", "metadata": {"timestamp": "2025-07-29T15:00:00Z", "confidence_chosen": 0.85, "confidence_rejected": 0.38}}

Pair generation strategy:

For each approved prediction P_approved:
  Find the most recent rejected prediction P_rejected with:
    - Same or similar context window (within 1 hour)
    - Different target channel (diverse negatives)

  Emit DPO pair:
    chosen  = P_approved.prompt + " → " + P_approved.targetChannel
    rejected = P_rejected.prompt + " → " + P_rejected.targetChannel
    prompt  = assembled context at P_approved.createdAt

7.4 Training Pipeline

mermaid
flowchart LR
    SIGNALS[Signal Store<br/>predictions.jsonl] --> BATCH[Batch Processor<br/>every 24h]
    BATCH --> FORMAT[DPO Formatter]
    FORMAT --> VALIDATE[Quality Gate<br/>≥50 pairs minimum]
    VALIDATE -->|Pass| EXPORT[Export to<br/>dpo-pairs.jsonl]
    VALIDATE -->|Fail| ACCUMULATE[Accumulate<br/>more signals]
    EXPORT --> UPLOAD[Upload to<br/>Together AI]
    UPLOAD --> FINETUNE[DPO Fine-Tune<br/>on Twin model]
    FINETUNE --> DEPLOY[Deploy new<br/>Twin version]
    DEPLOY --> PREDICT[Prediction Engine<br/>uses new model]
    PREDICT --> SIGNALS

7.5 Training Schedule

MilestoneTriggerAction
Initial SFTOne-time (already submitted)Base Twin model from 38K conversation records
Initial DPOAfter SFT completes + 882 curated pairsFirst preference alignment layer
Cross-poll DPO v150+ accept/reject pairs accumulatedFirst cross-poll-specific DPO round
Ongoing DPOEvery 100 new pairs OR weeklyContinuous improvement
Model swapDPO job completesHot-swap model ID in Gateway config (no rebuild)

7.6 Accuracy Tracking

The system tracks prediction accuracy over time:

typescript
interface PredictionMetrics {
  totalPredictions: number;
  approved: number;
  rejected: number;
  expired: number;
  autoExecuted: number;
  autoExecutedUndone: number;

  // Derived
  approvalRate: number;        // approved / (approved + rejected)
  autoSuccessRate: number;     // (autoExecuted - autoExecutedUndone) / autoExecuted
  avgConfidence: number;       // mean confidence of all predictions
  avgApprovedConfidence: number; // mean confidence of approved predictions
  avgRejectedConfidence: number; // mean confidence of rejected predictions

  // Calibration: is confidence score well-calibrated?
  // Bucket predictions by confidence range, check if approval rate matches
  calibration: {
    bucket: string;          // "0.3-0.4", "0.4-0.5", etc.
    predictions: number;
    approvalRate: number;    // Should be close to the bucket midpoint
  }[];
}

Metrics endpoint:

GET /api/v1/crosspoll/metrics
→ {
    "period": "last_7_days",
    "totalPredictions": 142,
    "approvalRate": 0.64,
    "autoSuccessRate": 0.91,
    "avgConfidence": 0.58,
    "calibration": [
      { "bucket": "0.3-0.4", "predictions": 23, "approvalRate": 0.30 },
      { "bucket": "0.4-0.5", "predictions": 31, "approvalRate": 0.42 },
      { "bucket": "0.5-0.6", "predictions": 28, "approvalRate": 0.57 },
      { "bucket": "0.6-0.7", "predictions": 35, "approvalRate": 0.69 },
      { "bucket": "0.7-0.8", "predictions": 18, "approvalRate": 0.78 },
      { "bucket": "0.8-1.0", "predictions": 7,  "approvalRate": 0.86 }
    ],
    "trendDirection": "improving",
    "lastDpoTraining": "2025-07-28T00:00:00Z"
  }

7.7 Cold Start Strategy

Before the Twin model is available or before enough DPO pairs accumulate:

Phase 0 — Template Only (Day 1-2):
  - Time-of-day heuristics
  - Channel round-robin
  - Fixed confidence = 0.4 (always show card, never auto-execute)
  - Goal: Collect initial accept/reject signals

Phase 1 — SFT Twin (Day 2-3):
  - Twin model generates predictions
  - Confidence from model's self-assessment
  - Auto-execute threshold raised to 0.8
  - Goal: Higher quality predictions, start accumulating DPO pairs

Phase 2 — DPO Twin (Day 3+):
  - Preference-aligned predictions
  - Calibrated confidence scores
  - Full autonomous mode enabled
  - Continuous improvement loop active

---

8. API Reference

8.1 Endpoints

`GET /api/v1/twin/predict`

Returns pending predictions for display in ACC/Watch.

Query Parameters:

ParamTypeDefaultDescription
`count`int3Number of predictions to return (max: 10)
`channel`stringFilter by target channel

Response:

json
{
  "predictions": [
    {
      "id": "pred_abc123",
      "prompt": "Run tests on recently changed modules",
      "targetAgent": "main",
      "targetChannel": "koji-core",
      "confidence": 0.72,
      "reasoning": "3 modules changed since last test run; morning review pattern",
      "createdAt": "2025-07-29T14:30:00Z",
      "status": "pending",
      "source": "twin-model"
    }
  ],
  "count": 1,
  "timestamp": "2025-07-29T14:30:05Z",
  "crossPollStatus": {
    "enabled": true,
    "lastTriggerAt": "2025-07-29T14:20:00Z",
    "totalTriggers": 47,
    "costThisHour": 0.15,
    "costCapPerHour": 2.00
  }
}

`POST /api/v1/crosspoll/trigger`

Manually trigger a cross-pollination cycle (or approve a prediction).

Request Body:

json
{
  "targetChannel": "koji-core",
  "hint": "Run tests on recently changed modules",
  "force": false,
  "predictionId": "pred_abc123"
}

Response:

json
{
  "triggered": true,
  "prediction": { "id": "pred_abc123", "..." : "..." },
  "targetChannel": "koji-core",
  "message": "Cross-pollination prompt generated for #koji-core",
  "status": { "..." : "..." }
}

`POST /api/v1/predictions/{id}/confirm`

Confirm (approve) a prediction. Generates a positive DPO signal.

`POST /api/v1/predictions/{id}/reject`

Reject (dismiss) a prediction. Generates a negative DPO signal.

`GET /api/v1/crosspoll/metrics`

Returns accuracy and calibration metrics (see §7.6).

`PATCH /api/v1/crosspoll/config`

Runtime configuration update (does not persist across restarts).

json
{
  "enabled": true,
  "intervalMs": 600000,
  "costCapPerHour": 2.00,
  "autoExecuteThreshold": 0.8,
  "quietHoursStart": 23,
  "quietHoursEnd": 8
}

8.2 WebSocket Events

Subscribe to `ws://gateway/pulse` for real-time cross-pollination events:

typescript
// Client subscribes
ws.send(JSON.stringify({ type: "subscribe", channels: ["crosspoll"] }));

// Server pushes
{ "type": "prediction.new", "prediction": {...} }
{ "type": "prediction.executed", "predictionId": "pred_abc123", "taskId": "task_xyz" }
{ "type": "prediction.expired", "predictionId": "pred_abc123" }
{ "type": "crosspoll.status", "status": {...} }
{ "type": "crosspoll.paused", "reason": "3 consecutive rejections" }
{ "type": "crosspoll.resumed" }

8.3 RPC Methods

For internal Clawdbot agent-to-agent communication:

typescript
// Gateway RPC
gateway.call("pulse.crosspoll.trigger", { channel: "koji-core", hint: "..." })
gateway.call("pulse.predictions.latest", { count: 5 })
gateway.call("pulse.crosspoll.status", {})

---

9. Data Models

9.1 Supabase Schema (Future: Persistent Storage)

sql
-- Predictions table
CREATE TABLE predictions (
  id          TEXT PRIMARY KEY,           -- "pred_abc123"
  user_id     UUID REFERENCES auth.users, -- Owner
  prompt      TEXT NOT NULL,
  target_agent TEXT DEFAULT 'main',
  target_channel TEXT,
  confidence  REAL NOT NULL CHECK (confidence >= 0 AND confidence <= 1),
  reasoning   TEXT,
  status      TEXT NOT NULL DEFAULT 'pending'
              CHECK (status IN ('pending','confirmed','rejected','expired','executed')),
  source      TEXT DEFAULT 'template',   -- 'template' | 'twin-model' | 'predictor-hook'
  created_at  TIMESTAMPTZ DEFAULT NOW(),
  confirmed_at TIMESTAMPTZ,
  executed_at TIMESTAMPTZ,
  task_id     TEXT,                       -- Links to tasks table if executed
  dpo_exported BOOLEAN DEFAULT FALSE     -- Has this been exported for training?
);

-- RLS: users can only see their own predictions
ALTER TABLE predictions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users see own predictions"
  ON predictions FOR SELECT
  USING (auth.uid() = user_id);

-- DPO training pairs
CREATE TABLE dpo_pairs (
  id          SERIAL PRIMARY KEY,
  user_id     UUID REFERENCES auth.users,
  context     TEXT NOT NULL,              -- Assembled context at prediction time
  chosen      TEXT NOT NULL,              -- Approved prediction
  rejected    TEXT NOT NULL,              -- Rejected prediction
  chosen_confidence  REAL,
  rejected_confidence REAL,
  created_at  TIMESTAMPTZ DEFAULT NOW(),
  exported_at TIMESTAMPTZ                -- When exported for training
);

-- Cross-pollination execution log
CREATE TABLE crosspoll_log (
  id          SERIAL PRIMARY KEY,
  user_id     UUID REFERENCES auth.users,
  prediction_id TEXT REFERENCES predictions(id),
  trigger_type TEXT NOT NULL CHECK (trigger_type IN ('manual','autonomous','cron')),
  target_channel TEXT NOT NULL,
  prompt      TEXT NOT NULL,
  result      JSONB,                     -- Task result if executed
  cost_estimate REAL DEFAULT 0.05,
  blocked     BOOLEAN DEFAULT FALSE,
  block_reason TEXT,
  created_at  TIMESTAMPTZ DEFAULT NOW()
);

-- Indexes
CREATE INDEX idx_predictions_status ON predictions(status);
CREATE INDEX idx_predictions_user ON predictions(user_id, created_at DESC);
CREATE INDEX idx_dpo_exported ON dpo_pairs(exported_at) WHERE exported_at IS NULL;
CREATE INDEX idx_crosspoll_log_user ON crosspoll_log(user_id, created_at DESC);

9.2 In-Memory Models (Current Implementation)

The V1 implementation uses in-memory stores (see `prediction-store.ts`, `crosspoll-engine.ts`). Migration to Supabase happens in Wave 2-3 when auth is integrated.

typescript
// Current: In-memory prediction store
const MAX_PREDICTIONS = 100;
const predictions: Prediction[] = [];

// Future: Supabase-backed store
const { data } = await supabase
  .from('predictions')
  .select('*')
  .eq('user_id', userId)
  .eq('status', 'pending')
  .order('created_at', { ascending: false })
  .limit(count);

9.3 File-Based Storage (Predictor Hook)

The next-prompt-predictor Clawdbot hook stores predictions in JSONL:

[home-path]

{"prediction":"Run tests","confidence":0.72,"channel":"koji-core","reasoning":"...","timestamp":"2025-07-29T14:30:00Z"}
{"prediction":"Update docs","confidence":0.45,"channel":"chronicle-hub","reasoning":"...","timestamp":"2025-07-29T14:40:00Z"}

The `twin-predict` route reads both the in-memory store AND this file, merging results.

---

10. Deployment & Configuration

10.1 Plugin Configuration

yaml
# [home-path] (or equivalent)
plugins:
  entries:
    pulse-gateway-api:
      enabled: true
      config:
        # Twin model
        twinModelId: "ft:twin-sft-v1:together:2025-02"
        twinApiKey: "${TOGETHER_API_KEY}"

        # Cross-pollination
        crossPollEnabled: true
        crossPollIntervalMs: 600000          # 10 minutes
        crossPollCostCapPerHour: 2.00        # $2/hour max
        crossPollAutoExecuteThreshold: 0.8   # Auto-execute above this

        # Autonomous mode
        autonomousModeEnabled: true
        silenceThresholdMs: 1800000          # 30 minutes
        quietHoursStart: 23                  # 11 PM
        quietHoursEnd: 8                     # 8 AM

        # Storage
        tasksDir: "[home-path]
        predictionsDir: "[home-path]

10.2 Feature Flags

FlagDefaultDescription
`crossPollEnabled``false`Master switch for all cross-pollination
`autonomousModeEnabled``false`Whether silence triggers autonomous execution
`twinModelEnabled``false`Use Twin model vs template fallback
`dpoExportEnabled``false`Export DPO pairs for training
`pushNotificationsEnabled``true`Send push notifications for predictions
`watchComplicationsEnabled``true`Update Watch complications
`glassesAlertsEnabled``false`Spoken alerts on glasses (opt-in)

10.3 Monitoring

Health check:  GET /api/v1/health → includes crossPollStatus
Metrics:       GET /api/v1/crosspoll/metrics → accuracy, calibration
Logs:          [home-path]
Predictions:   [home-path]
DPO pairs:     [home-path]

10.4 Sprint Task Dependencies

Wave 1:  [A3] This spec ← YOU ARE HERE
         [A6] Predictor spec (companion doc)

Wave 3:  [C8] /crosspoll/predict endpoint (implements §8.1)

Wave 4:  [D8] Safety rails (implements §3)

Wave 5:  [E4] Autonomous mode (implements §5)
         [E7] DPO export pipeline (implements §7.4)

Wave 6:  [F1] Push notifications (implements §6)
         [F2] ACC cross-poll UI (implements §4)
         [F6] Swipeable prediction cards (implements §4.3)

---

Appendix A: Complete End-to-End Flow (Age of Leisure)

14:00  Mohamed puts laptop away, puts on glasses, walks out.
       └─ Last message timestamp: 14:00

14:30  [CRON] Silence ≥ 30min detected. Autonomous mode: PRIMING.
       └─ Context assembled: 3 stale tasks, afternoon patterns, recent koji-core activity
       └─ Twin model predicts: "Run vitest on pulse-gateway-api — 2 test files changed since last run"
       └─ Confidence: 0.84 (above auto-execute threshold)
       └─ Safety pipeline: ✅ rate OK, ✅ no loops, ✅ cost $0.15/$2.00, ✅ content safe
       └─ AUTO-EXECUTE → fires on koji-core channel

14:31  [koji-core agent] Runs: cd Desktop/pulse-gateway-api && npm test
       └─ Result: 24 tests passed, 0 failed
       └─ Task created: task_test_run_001 (status: completed)

14:31  [PUSH] → ACC: Silent update in Mission Control
       [PUSH] → Watch: Complication updates ✅
       [PUSH] → Discord: ✅ reaction on prediction message

14:32  [PREDICTION] New prediction generated: "Update CROSSPOLL-SPEC.md with test results"
       └─ Confidence: 0.52 (medium — show card, don't execute)
       └─ ACC: Banner notification + swipeable card added

14:45  Mohamed glances at Watch. Sees ✅ and a 🔮 badge.
       Raises wrist: "What happened?"
       [Life OS] → Gateway → "Tests passed on pulse-gateway-api. I also have a suggestion
                                to update the spec with results. Want me to do that?"
       Mohamed: "Yeah, go ahead."
       [Life OS] → Gateway → POST /api/v1/predictions/pred_xyz/confirm
       └─ DPO signal: POSITIVE (chosen = "update spec", rejected = previous expired card)
       └─ Execution fires on chronicle-hub

15:00  [CRON] Next autonomous cycle.
       └─ Twin predicts: "Check Shopify inventory levels for Serenity Soother"
       └─ Confidence: 0.41 (below auto-execute, above card threshold)
       └─ ACC: New prediction card pushed

15:15  Mohamed checks phone at a café. Opens ACC Mission Control.
       Sees 2 cards: one about Shopify (swipes left — not now),
       one about "brainstorm new meditation categories" (swipes right).
       └─ DPO signals: reject(shopify), approve(brainstorm)
       └─ dream-weaver agent starts brainstorming session

16:00  Mohamed heads home. Opens laptop.
       └─ Autonomous mode: WATCHING (user active)
       └─ Mission Control shows: 2 tasks completed autonomously,
          1 approved manually, 1 rejected. DPO pairs: 3 new.

---

Appendix B: Glossary

TermDefinition
ACCAgent Command Center — iOS app for managing agents and chains
CompassWeb dashboard for pipeline monitoring and agent control
Cross-pollinationSystem for autonomous agent action driven by prediction
DPODirect Preference Optimization — training technique using chosen/rejected pairs
Life OSwatchOS app providing wrist-based agent interaction
PULSEThe unified platform (7 surfaces, 1 Gateway)
SFTSupervised Fine-Tuning — initial model training on conversation data
TwinUser's personalized AI model trained on their conversation history
VisionClawSmart glasses app providing visual AI and hands-free interaction

---

Specification complete. Ready for implementation starting Wave 3 [C8].

Promotion Decision

Promote into a technical note or architecture paper with implementation anchors.

Source Anchor

pulse-gateway-api/docs/CROSSPOLL-SPEC.md

Detected Structure

Method · Evaluation · References · Code Anchors · Architecture