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 separatescripts/executive/prompts.pymodule is the prompt source for the direct/router mode inloop.pyβ don't confuse the two.
Escalation (human-in-the-loop)
Escalation is the loop's only blocking interaction with bio-zack:
decide(orrevise_decide) emits an<escalate>β¦</escalate>block with the question.- A regex
derived_outputlifts the block intoescalate_reason; thehuman_reviewstep'swhenclause fires on non-null. - Stepwise pauses the job as an
externalexecutor and surfaces the prompt (visible in the Stepwise UI / Telegram). - bio-zack's answer (
guidance) feedsrevise_decide, which continues the same agent session. - 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
runningcycles 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
relationshipsandsleepdimensions are health/relationship-adjacent. The engine stores scores and gap strings indata/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.mdthat are missing or (for working-memory paths) untouched for 21+ days, - ambient_size β the byte budget of
CLAUDE.mdplus 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.pyis deprecated (2026-06-10). It was meant to splitreflection.mdinto a stable ambient half and a volatile half but was never wired into/sleep; running it now would clobber the curatedidentity-state.md. The de-facto split today:identity-state.mdis the stable, ambient-imported half (curated directly by the OODA agent and/sleep),reflection.mdis 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 anddata/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
/sleeprun 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.