Skip to content

Executive Loop

The Executive Loop is silicon-zack's autonomous self-direction engine. On a configurable schedule it gathers working memory, recent conversations, briefing data, and health signals, runs a full agent reflection cycle (Orient β†’ Decide β†’ Act β†’ Log), and either takes action or just updates working memory. A second layer β€” the Homeostatic Equilibrium (v2) subsystem β€” scores life across eight dimensions and governs how much autonomous action the loop is allowed to take. This page documents both, plus the watchdog that keeps the loop honest and the (now-dormant) CEO Transformation goal that shipped alongside it.

Framing: the OODA loop is how silicon-zack "thinks while bio-zack is away." The equilibrium engine is the conscience that keeps that thinking from drifting into noise β€” it caps interruptions, blocks unsafe actions, and learns which domains have earned more autonomy. Together they implement the manifesto's homeostasis target as running code.


Architecture at a glance

                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   schedule_gate β”‚  Stepwise schedule "exec-loop" (poll)        β”‚
   (pre-flight)  β”‚  β†’ schedule_gate.py decides launch / skip    β”‚
                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                     β”‚ gates pass
                                     β–Ό
                        flows/executive/FLOW.yaml  (single "ooda" agent session)
                        gather_context β†’ orient β†’ decide
                                     β”‚
                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                  <escalate>             no escalation
                          β”‚                      β”‚
                   human_review            act β†’ log
                          β”‚                      β”‚
                   revise_decide ──loop≀3xβ”€β”€β”˜    β”‚
                                                 β–Ό
                                            post_log.py β†’ loop.db, git diff

   ── governance (pure-Python, no LLM) ────────────────────────────────
   equilibrium.py  β†’  gates.py / alert_budget.py / circuit_breaker.py
                      adaptive_thresholds.py / trust.py / firebreaks.py

Two distinct execution paths share the same loop.db store:

Path Trigger Where the cycle runs Timeout
Stepwise flow (primary) Poll schedule exec-loop, gated by schedule_gate.py flows/executive/FLOW.yaml agent session n/a (Stepwise-managed)
Router / direct mode POST /trigger, answer/follow-up/resume endpoints, scheduler scripts/executive/loop.py:run_cycle() 2100s (35 min), see _CYCLE_TIMEOUT

The API scheduler holds an asyncio.Lock (_executive_lock, api/src/scheduler.py:285) so router-spawned cycles cannot overlap.


The OODA cycle (Stepwise flow)

The loop runs as a Stepwise flow at flows/executive/FLOW.yaml (version 2.2). Every cycle is one persistent Claude session named ooda, so context carries across steps without re-priming.

gather_context.py   (script β€” assemble cycle_id + context + pre_snapshot)
      β”‚
      β–Ό
   orient    (agent β€” read context, synthesize the situation)
      β”‚
      β–Ό
   decide    (agent β€” pick a gap/work item, or skip; may emit <escalate>)
      β”‚
      β”œβ”€β”€[<escalate>…</escalate>]──▢ human_review  (Stepwise external pause)
      β”‚                                   β”‚  bio-zack provides guidance
      β”‚                                   β–Ό
      β”‚                             revise_decide  (agent, same session)
      β”‚                                   β”‚
      └◀────────── loops to human_review up to 3Γ— (max_iterations: 3) β”€β”€β”˜
      β”‚
      β–Ό  [no escalation]
    act     (agent β€” execute work: tools, edits, git, messages)
      β”‚
      β–Ό
    log     (agent β€” summarize the outcome)
      β”‚
      β–Ό
 post_log.py  (script β€” persist to loop.db, capture git diff)

Steps

Step Executor Notes
gather_context script (gather_context.py) Produces cycle_id, context, and a pre_snapshot (git state) used later for the diff.
orient agent (prompts/orient.md) Reads assembled context, synthesizes the situation.
decide agent (prompts/decide.md) Chooses what to do. A derived_output regex extracts a trailing <escalate>…</escalate> block into escalate_reason.
human_review external Runs only when escalate_reason is non-null. Surfaces the question as a Stepwise external-executor pause; bio-zack's reply becomes guidance.
revise_decide agent (prompts/revise_decide.md) Folds guidance back into the same session. Can re-escalate; loops back to human_review up to 3 times.
act agent (prompts/act.md) Runs only when there was no escalation. Executes the chosen work.
log agent (prompts/log.md) Summarizes the outcome.
post_log script (post_log.py) Persists the cycle to loop.db and captures the git diff against pre_snapshot.

Note: The OODA-flow prompts live in flows/executive/prompts/ (orient.md, decide.md, revise_decide.md, act.md, log.md). The separate scripts/executive/prompts.py module is the prompt source for the direct/router mode in loop.py β€” don't confuse the two.

Escalation (human-in-the-loop)

Escalation is the loop's only blocking interaction with bio-zack:

  1. decide (or revise_decide) emits an <escalate>…</escalate> block with the question.
  2. A regex derived_output lifts the block into escalate_reason; the human_review step's when clause fires on non-null.
  3. Stepwise pauses the job as an external executor and surfaces the prompt (visible in the Stepwise UI / Telegram).
  4. bio-zack's answer (guidance) feeds revise_decide, which continues the same agent session.
  5. The loop can escalate at most 3 times before it must proceed or abandon.

In direct/router mode the equivalent of escalation is a blocked cycle: decide produces a question, the cycle is stored with status='blocked', and bio-zack answers via the API (POST /cycles/{id}/answer), which spawns a resume cycle carrying resume_context.


Configuration

Config is a key/value table inside loop.db, merged over defaults in scripts/executive/store.py:

Setting Default Description
enabled false Master on/off switch (the loop is opt-in).
start_hour 5 Earliest cycle start (PT).
end_hour 22 Latest cycle start (PT).
interval_minutes 90 Minimum time between cycles. The 90-min floor is intentional β€” it matches the watchdog's active-hours threshold so a healthy loop never trips its own alarm.
max_daily_cycles 20 Hard cap on cycles per day (skipped cycles excluded).
max_daily_real_cost_usd 2.0 Daily budget, enforced against real spend only.

Pre-flight gate

Before each scheduled tick launches the flow, scripts/executive/schedule_gate.py checks every gate and emits JSON (launch) or nothing (skip):

  • enabled flag is on,
  • current PT hour is within [start_hour, end_hour),
  • daily cycle count < max_daily_cycles,
  • daily real cost < max_daily_real_cost_usd,
  • and as a side effect it reaps orphaned running cycles older than 30 min.
python scripts/executive/schedule_gate.py   # exit 0 + JSON = launch; empty = skip

Cost tracking

Each cycle records two figures:

  • cost_usd (imputed) β€” total cost including a sunk-cost estimate for Claude Max usage.
  • real_cost_usd (real) β€” only out-of-pocket API spend (e.g. OpenRouter).

Daily limits are enforced against real_cost_usd so the Claude-Max sunk cost never pauses the loop.

Dimension cooldowns

store.py defines per-dimension cooldowns (hours) so the loop doesn't re-pick the same gap before external state can change β€” fast dimensions like inbox/system-health every ~2h, slow bio-zack-dependent ones (relationships) at most ~once per 12h. Cooldowns are read from the gap_addressed column of completed cycles.


Cycle data model

One row per cycle in the cycles table (loop.db). Key columns:

Column group Columns
Identity / timing cycle_id, started_at, ended_at, status, duration_seconds, session_id
OODA summaries observe_summary, orient_summary, decide_summary, act_summary, outcome
Work / output chosen_work, files_changed, actions_taken, git_diff
Cost / tokens cost_usd, real_cost_usd, input_tokens, output_tokens
Q&A (blocked path) question_asked, question_channel, question_ref, answer_received, answer_received_at, resumed_from
v2 equilibrium equilibrium_snapshot, gap_addressed, escalation_level, mission_frame, precedent_task_key

Companion tables in the same DB: comments (per-cycle or global feedback), config (key/value), directives (standing instructions), equilibrium_history (composite-score snapshots), and subjective_overrides (manual per-dimension score overrides).

Cycle statuses

running ─▢ completed | blocked | error | skipped | cancelled
Status Meaning
running In flight.
completed Finished and logged.
blocked Waiting on a human answer (direct-mode escalation). Resume via /cycles/{id}/answer.
error Failed, timed out, or orphaned by a process restart.
skipped Gate said no work / no action. Excluded from daily counts.
cancelled Manually cancelled via /cycles/{id}/cancel (stored as error with outcome "Cancelled by user").

Orphan handling: cleanup_orphaned_cycles() marks running cycles older than max_age_seconds (default 30 min) as error with outcome "Orphaned: process restart", so a crash or restart never leaves a zombie.


Directives

bio-zack can post standing instructions that steer future cycles without blocking the current one. They live in the directives table and flow through orient/decide:

Method Endpoint Purpose
POST /directives Add a directive ({ "content": "…" }).
GET /directives List directives (optional status filter).
DELETE /directives/{id} Remove a directive.

A directive starts status='pending'; once orient/decide has seen it, mark_directives_incorporated() moves it to incorporated.


Homeostatic Equilibrium (v2)

The equilibrium subsystem is pure Python β€” no LLM, no network (except optional Precedent sync). It reads existing data files, scores life across eight dimensions, and gates autonomous action. It is the structural generalization of the sleep gate.

Scoring engine β€” scripts/executive/equilibrium.py

compute_equilibrium() runs eight dimension scorers and produces an EquilibriumSnapshot:

Dimension Reads (generically)
sleep recent sleep averages
training training load / activity logs
commitments the commitments store
inbox email/inbox state
calendar upcoming events
relationships a relationship-health signal
goals active goal progress
system_health vita service-monitor state

Each scorer returns a DimensionScore:

@dataclass
class DimensionScore:
    dimension: str
    score: float           # 0-100
    severity: str          # none | low | moderate | high | critical
    trend: str             # improving | declining | stable | unknown
    gap: str               # human-readable gap description
    data_age_hours: float  # freshness of the underlying data
    details: dict
    suggested_escalation: int  # 1-5
    weight: float          # effective weight in the composite

The composite is a weighted average. Base weights come from data/executive/equilibrium-manifest.json; an operating mode (maintenance | crunch | recovery | vacation) applies per-dimension mode_multipliers to reweight (e.g. recovery mode amplifies sleep and relationships, suppresses inbox). A layer_hierarchy rule can suppress higher-layer dimensions when a physiological-layer dimension drops below threshold. Top gaps are ranked by score βˆ’ weightΓ—50 (low score + high weight = highest priority). Subjective overrides (manual, time-boxed) can pin any dimension's score.

python scripts/executive/equilibrium.py          # print dashboard
python scripts/executive/equilibrium.py --json   # JSON snapshot

Snapshots are recorded to the equilibrium_history table via store.record_equilibrium(), feed the OODA loop's context, and back the weekly board review (scripts/executive/weekly_review.py). An equilibrium_sync scheduler loop recomputes and records the snapshot on a cadence (api/src/scheduler.py:_equilibrium_sync_loop).

PII: the relationships and sleep dimensions are health/relationship-adjacent. The engine stores scores and gap strings in data/executive/; treat those values as private β€” this page documents the mechanism only.

Governance modules

These deterministic modules sit between "the loop wants to act" and "the action happens." All are no-LLM, no-network.

Module File Role
Gates gates.py Declarative gates from the manifest. Types: hard (blocks specific actions, e.g. sleep gate blocks intervals), soft (advisory), scope (caps capacity, e.g. max active goals), tempo (reduces alert frequency), upgrade (blocks adding new items). Entry point is_action_blocked().
Alert budget alert_budget.py Caps daily interventions (~1 interruptive + ~2 non-interruptive), resets midnight PT, priority preemption, overflow queue β†’ weekly review (denied β‰  dropped). can_send_alert() / record_alert_sent().
Circuit breaker circuit_breaker.py Trips if engagement compliance < 30% for 7 days β†’ halves the alert budget; restores when compliance > 50% (checked weekly). Uses Feedback-Learning-Loop data.
Adaptive thresholds adaptive_thresholds.py Self-tunes per-dimension thresholds to keep success rate in a 60–85% band (+2 if >85% for 30d, βˆ’2 if <60% for 30d; ≀1 change/7d/dim; clamped to manifest min/max). Runs nightly from /sleep.
Trust trust.py Per-domain trust scores 0.0–1.0, asymmetric learning (success +0.02, failure βˆ’0.10 β‰ˆ 5:1), per-domain ceiling, βˆ’0.01/day decay when unused (applied during /sleep). Maps to firebreak thresholds.
Firebreaks firebreaks.py Second-layer enforcement for external actions, independent of capability tokens. Levels: allow β†’ allow_if_mandated β†’ warn β†’ escalate β†’ block. allow_if_mandated passes only if the task is in trusted_tasks; trust scores drive the effective threshold.
Contracts contracts.py Contract-first "done" criteria for scheduler tasks (outcome deterministic vs effort best-effort), validated post-completion.

Trust β†’ firebreak mapping: capability tokens answer "can this tool be used?"; firebreaks answer "should this specific invocation proceed, or escalate?". A domain that keeps succeeding earns a higher trust score, which lowers its firebreak policy level toward allow; failures push it back toward escalate/block.

Where equilibrium state lives

Path Contents
data/executive/equilibrium-manifest.json weights, mode multipliers, gates, layer hierarchy, alert-budget & circuit-breaker config
data/executive/adaptive_state.json adaptive-threshold overrides + history
data/executive/trust_scores.json Β· trust_events.jsonl trust state + outcome log
data/executive/firebreaks.json Β· firebreak_log.jsonl firebreak config + decision log
data/executive/gate_events.jsonl gate activation log
data/executive/alert_budget_state.json daily alert budget + overflow
data/executive/circuit_breaker_state.json circuit-breaker state

Self-audit: staleness detection

scripts/executive/detect_stale_systemic_issues.py is the nightly "lying-instrument" detector tied to the executive layer. It flags entries in the ambient-imported identity-state.md that have gone stale or self-contradictory, across several classes:

  • shipped-but-listed β€” a systemic-issue row pointing at a proposal already marked SHIPPED,
  • dated_headers β€” (refreshed YYYY-MM-DD) section leads older than 14 days,
  • dead_pointers β€” file paths cited from CLAUDE.md / ORIENTATION.md that are missing or (for working-memory paths) untouched for 21+ days,
  • ambient_size β€” the byte budget of CLAUDE.md plus its @-imports (every agent pays this on every invocation),
  • semantic_claims β€” a free-prose claim graded against its live primary source (e.g. a sleep-gate status that contradicts a live gate check).

This is the antibody for "shipped mitigations and completed-fix claims are instruments that can lie" β€” a recurring failure class in this codebase.

Tombstone: scripts/executive/split_reflection.py is deprecated (2026-06-10). It was meant to split reflection.md into a stable ambient half and a volatile half but was never wired into /sleep; running it now would clobber the curated identity-state.md. The de-facto split today: identity-state.md is the stable, ambient-imported half (curated directly by the OODA agent and /sleep), reflection.md is the volatile working memory (read explicitly, never ambient).


OODA Freshness Watchdog

A dedicated watchdog (scripts/watchdogs/ooda_freshness.py) runs every ~15 minutes to detect dark windows where the loop stops producing cycles. It is deliberately not built on Stepwise so it survives the very Stepwise outage it is meant to detect, and it reads data/executive/loop.db directly.

Rule Threshold
Active hours (5am–10pm PT) alert if no completed/blocked/no-action cycle in the last 90 min (ACTIVE_HOURS_THRESHOLD_MINUTES)
Overnight (10pm–5am PT) alert if no cycle in the last 4 h (OVERNIGHT_THRESHOLD_MINUTES = 240)
Suppression skips alerting when the service monitor already flags Stepwise as down (no double-alerting)
Dedup identical alerts within 4 h collapse via a stable dedup_key (cycle-id based)
PYTHONPATH=scripts uv run python -m watchdogs.ooda_freshness
PYTHONPATH=scripts uv run python -m watchdogs.ooda_freshness --check-only

The watchdog was added after a multi-day dark-loop outage in May 2026: a Stepwise failure stopped the loop silently and nothing noticed for days. The lesson β€” surface life β‰  durable layer alive β€” is baked into its independence.


API endpoints (/api/v1/executive)

Method Endpoint Purpose
GET /cycles List cycles (limit, status, since).
GET /cycles/{id} Full cycle detail with comments.
POST /trigger Manually trigger a cycle (fires the Stepwise exec-loop schedule).
POST /cycles/{id}/answer Store an answer for a blocked cycle and kick off a resume cycle.
POST /cycles/{id}/resume Re-run a blocked cycle with its already-stored answer.
POST /cycles/{id}/followup Send a follow-up prompt to a completed/blocked cycle (spawns a new followup_context cycle).
POST /cycles/{id}/cancel Cancel a running/blocked cycle.
GET / PUT /config Read / partially update schedule + budget config.
GET / PUT /work-context Read / write the editable work-context markdown.
POST /cycles/{id}/comments Β· POST /comments Β· GET /comments Per-cycle and global feedback.
POST / GET / DELETE /directives Β· /directives/{id} Standing directives.
GET /stats Today's aggregate stats + remaining budget.

Equilibrium API (v2)

Method Endpoint Purpose
GET /equilibrium Current snapshot (recomputed live, with active overrides applied).
GET /equilibrium/history Recent snapshots for trend analysis.
POST /equilibrium/override Add a subjective per-dimension score override.
GET /equilibrium/weekly-review Weekly review payload for board meetings.
GET /equilibrium/gates Current gate status + layer health.
GET /equilibrium/alert-budget Alert budget + overflow queue.
GET /equilibrium/circuit-breaker Circuit-breaker state.
GET /equilibrium/adaptive-thresholds Adaptive-threshold status + history.
GET /tasks Β· POST /tasks/sync Equilibrium task backlog (Precedent) / record a snapshot.

The API runs on port 33800 (./run api).


Web UI

Route Component Shows
/executive web/src/features/executive/ExecutivePage.tsx Cycle list (grouped by date, status badges, expandable detail with OODA summaries, files changed, git diff, token usage, comments), enable/disable + "Run Now", stats cards, collapsible config and work-context editors. The header status reads e.g. Every {interval_minutes}m Β· {start}:00–{end}:00 PT.
/executive/equilibrium web/src/routes/executive/equilibrium.tsx The v2 equilibrium dashboard (dimension scores, gates, budget, trends).

web/src/routes/executive/index.tsx is a thin route shim; the real cycle-list UI lives in the feature directory.


CEO Transformation (dormant)

A structured 90-day goal that shipped alongside the loop (start 2026-02-12, target 2026-05-11) to track a shift in operating mode through daily reflections and time-allocation tracking.

Status: dormant. The goal JSON still reads status: "active", but the 90-day window has fully elapsed and data/ceo-mode/daily/ holds essentially no recent reflections (the most recent is from early in the launch month). The feature is documented here for completeness, not as a live system.

Each daily reflection captures: highest_leverage, delegation_check, energy_fulfillment (1–10), a rotating rotation_question + rotation_answer, and free-form notes.

CEO API (/api/v1/ceo)

Method Endpoint Purpose
GET /goal Goal with computed day_number, days_remaining, progress_pct (dates hardcoded to the launch window).
GET /progress Work-percentage progress view, with a staleness/confidence freshness guard.
GET /scorecard Pre-built scorecard memory view, freshness-guarded.
GET /reflections List reflections, newest first.
GET /reflections/{date_str} A specific day's reflection.
POST /reflections/{date_str} Save/update a day's reflection.
GET /timeline 90-day timeline with per-day completion + streak.

The /scorecard and /progress endpoints apply render_view_metric() so a stale memory view renders "unknown" rather than a misleading stale number β€” the same freshness-of-source discipline the staleness detector enforces.


File locations

Path Contents
flows/executive/FLOW.yaml Stepwise OODA flow definition (v2.2).
flows/executive/prompts/ OODA-flow agent prompts (orient, decide, revise_decide, act, log).
flows/executive/gather_context.py Β· post_log.py Cycle context assembly / persistence + git diff.
scripts/executive/loop.py Direct/router-mode cycle executor (run_cycle).
scripts/executive/prompts.py Prompts for direct mode (not the flow prompts).
scripts/executive/store.py SQLite store: cycles, comments, config, directives, equilibrium history, overrides.
scripts/executive/schedule_gate.py Pre-flight gate for the Stepwise poll schedule.
scripts/executive/equilibrium.py v2 scoring engine.
scripts/executive/{gates,alert_budget,circuit_breaker,adaptive_thresholds,trust,firebreaks,contracts}.py Governance modules.
scripts/executive/weekly_review.py Weekly equilibrium review generator.
scripts/executive/detect_stale_systemic_issues.py Nightly staleness / lying-instrument detector.
scripts/watchdogs/ooda_freshness.py OODA freshness watchdog.
api/src/routers/executive.py Β· ceo.py API routers.
api/src/services/scheduler_autonomy.py run_executive_cycle / run_equilibrium_sync_cycle.
data/executive/loop.db SQLite store.
data/executive/work-context.md Editable work-item list.
data/executive/reflection.md Β· identity-state.md Volatile working memory / stable ambient identity.
data/executive/cycle_history.jsonl Write-on-success cycle history.
data/executive/equilibrium-manifest.json Equilibrium config.
goals/active/ceo-transformation-90day.json Β· data/ceo-mode/daily/{date}.json Β· memory/views/ceo-scorecard.json CEO Transformation (dormant).

Where to go next

  • Sleep Guardian β€” the sleep gate that the equilibrium gate framework generalizes.
  • Consolidation β€” the nightly /sleep run that drives adaptive thresholds, trust decay, and the staleness detector.
  • Board β€” where the weekly equilibrium review surfaces.
  • Improve β€” the proposal pipeline many of these modules were shipped from.
  • Homeostasis β€” the prediction ledger that instruments sync deviation.
  • Model Guidance β€” model routing for the OODA agent session.