What this is
eeebot is an autonomous improvement loop that runs bounded cycles on a constrained host (eeepc), editing its own instance repository under a gate. Every change the loop makes to its own capabilities goes through the same bounded gate described below; nothing here bypasses it.
The machine: four repositories
| repository | role | holds | identifiers on this site |
|---|---|---|---|
| ozand/eeebot | harness / product | the runtime code (nanobot/), specs, host deploy tooling, and the fitness function (scorecard, targets, held-out checkers) | issue numbers (#NNNN); commit shas for the harness's own history |
| ozand/eeebot-self-evolving | instance | the mutable workspace the loop edits (scripts/, surfaces/, tests/, memory/, docs/); checked out on the host beside the state root | cycle ids (cycle-<hex>) mark commits the bridge integrates onto this repo's main; commit shas here are cycle integrations |
| ozand/eeebot-ops-dashboard | dashboard (this repo) | the WSGI observability app (dormant, not deployed) and the standalone techtree_viewer.py static-site generator that produces the page you are reading | issue numbers (#NNNN) for dashboard-only work; no cycle ids or shas of its own appear on the published site |
| ozand/eeebot-channel-app | channel app | the home page, privacy policy and terms for the eeebot channel publisher's OAuth client | not surfaced on this dashboard |
The cycle
| stage | what happens | where the output appears |
|---|---|---|
| 1. Demand selection | demand.py scans state and yields items in trust order: priority > defect > goal-gap > hypothesis > decay. | the demand kind and target for the current/most recent cycle appear in cycles; hypothesis-kind demand is detailed on hypotheses |
| 2. Propose | the LLM proposer (llm_proposer.py) selects and refines one presented demand item -- it never invents from a bare inventory. No demand means zero LLM calls that cycle. | what the proposer/executor is given (charter, derived priorities, skills) is on agent |
| 3. Dedup chain | a proposal passes exact-tag replay protection, recent-failure suppression, and an FTS5 existence-index check before any subagent spawns. | not yet surfaced as its own section on this site |
| 4. Bounded executor | exactly one fresh-context subagent runs and commits on a cycle branch. | subagent records and prompts for one cycle are on the per-cycle detail page (linked from cycles) |
| 5. Gate | the bounded smoke gate: py_compile of changed files, the tests they affect, and a small fixed core set -- not the full suite. A mutation-surface violation is a hard block. The gate fails safe: any error, timeout, or missing pytest counts as failure. | PASS / BLOCK / unknown badges per cycle are on cycles |
| 6. Integrate | only on a green gate does the bridge merge the cycle onto the instance repo's main (script tier) or, for an operator-approved runtime-slice cycle, land it as a pending promotion candidate for a product PR instead of auto-integrating. | merged commits and the direction/lever state after integration are on lineage and now |
| 7. Scorecard + demand feeds the next cycle | scorecard.py computes a deterministic, LLM-free 7-day snapshot; demand.py reads it to decide what the loop is offered next, closing the loop. | the scorecard-derived KPI strip appears at the top of every page, including now; held-out pass rate is one of its rows |
The schedule
| unit | cadence | produces | appears on |
|---|---|---|---|
| eeepc-self-evolving-subagent-bridge | every 15 min (OnBootSec=4m, OnUnitActiveSec=15m) | one bridge cycle: demand -> propose -> gate -> integrate | cycles.html, lineage.html |
| eeebot-reflector | every 30 min (OnBootSec=25m, OnUnitActiveSec=30m) | per-cycle reflections (findings, recommendations) -- the night contour's transcript reader | not yet surfaced as its own page |
| eeebot-knowledge-curator | daily (OnCalendar=daily, i.e. 00:00) | curates lessons into the bounded knowledge base | lessons.html |
| eeebot-strategist | daily at 03:00 (OnCalendar=*-*-* 03:00:00) | periodic archive review | not yet surfaced as its own page |
| eeepc-promotion-verifier | every 15 min (OnBootSec=5m, OnUnitActiveSec=15m) | root-verified re-check of runtime-slice promotion candidates | not yet surfaced as its own page |
| eeebot-validator-harness | every 6 h (OnBootSec=20m, OnUnitActiveSec=6h) | runs built validator scripts and records findings | not yet surfaced as its own page |
| eeebot-skill-evals | daily at 04:30 (OnCalendar=*-*-* 04:30:00) | harness A/B skill evals, measured with/without delta | not yet surfaced as its own page |
| eeebot-local-ci | every 4 h 15 min (OnCalendar=*-*-* 00/4:15:00) | bounded local CI check | not yet surfaced as its own page |
| eeebot-action-index | daily at 00:05 (OnCalendar=*-*-* 00:05:00) | extracts the durable per-cycle action index before prompt rotation | not yet surfaced as its own page |
| eeebot-archive-subagent-requests | hourly (OnBootSec=10m, OnUnitActiveSec=1h) | archives stale subagent requests older than 24h | not yet surfaced as its own page |
| eeebot-host-metrics | every 6 h (OnBootSec=10min, OnUnitActiveSec=6h) | refreshes the host metrics feed | not yet surfaced as its own page |
| eeebot-host-capabilities | daily at 01:00 (OnCalendar=*-*-* 01:00:00, plus OnBootSec=10min) | refreshes the host capability inventory | not yet surfaced as its own page |
| eeebot-dashboard | always-on (no timer) | the OTHER dashboard -- scripts/eeebot_dashboard.py, port 8080, in ozand/eeebot -- not this repo's WSGI app (dormant) and not the static site this page is part of | n/a (not this site) |
eeebot-techtree-publish.service (installed from this repo's systemd/ directory) runs periodically via eeebot-techtree-publish.timer (every 15 minutes, issue #1905) and on completed cycles, digest-gated so an unchanged snapshot does not re-publish.
Confirmed running on the live host but with no corresponding file in the harness repo's host/eeepc/systemd/ as of this page's writing:
eeepc-monitor.timer
The models
| role | model | why |
|---|---|---|
| executor (the bounded subagent that edits code; telemetry component name: bridge) | self-hosted, owned 3090Ti GPU on the LAN (un/qwen3.8-27b-gguf) | 784/1004 recorded calls on the measured day; 24.30 completion tokens/request-wall-second |
| proposer, curator, reflector, strategist (the night contour + the proposer) | vendor API (an/gemini-3.8-flash-high) | nanobot/runtime/model_registry.py's _ROLE_DEFAULTS; the operator preset overrides these per-role in production |
Both self-hosted and vendor traffic traverse the same LAN gateway (docs/model-routing-evidence-1362.md): a gateway failure can affect either class. The dashboard's own executor-model-status check (_build_executor_model_item in techtree_viewer.py) flags a fallback when a vendor-class model serves a call the executor/harness role expected to be self-hosted -- the detectable signal, since llm_calls persists only the model that actually served the call, not a separate 'requested' field.
eeepc: Intel Atom N270, i686/32-bit (docs/specs/demo_category_determination.md, #1619 live measurement). RAM is stated inconsistently across harness-repo sources: 1 GB per that same measurement, 2 GB per docs/adr/ADR-013-capability-tiers-probe-and-cost.md and docs/model-routing-evidence-1362.md's operator-verified topology -- not reconciled here. Core count and swap configuration: not documented in the harness repo.
The vocabulary
- harness
- ozand/eeebot: the product repo. Owns the runtime code, the fitness function, and the deploy tooling. Never edited by the loop itself. (docs/CURRENT_ARCHITECTURE.md)
- instance
- ozand/eeebot-self-evolving: the mutable workspace the loop edits. The bridge integrates green cycles onto this repo's main. (docs/CURRENT_ARCHITECTURE.md)
- cycle
- one timer-paced bridge process invocation -- demand selection through integrate-or-not. Identified by a cycle id (cycle-<hex>). (docs/CURRENT_ARCHITECTURE.md)
- gate
- the bounded smoke gate a cycle's changes must pass before integration: py_compile of changed files, the tests they affect, and a small fixed core set (not the full suite). Fails safe on any error, timeout, or missing pytest. (docs/CURRENT_ARCHITECTURE.md)
- integration
- the bridge merging a green, script-tier cycle onto the instance repo's main (--no-ff). origin/main never advances on a red gate. (docs/CURRENT_ARCHITECTURE.md)
- night contour
- the reflector -> curator -> strategist chain: components that see across many cycles, as opposed to the executor, which sees one. ADR-021's subject: the night contour may propose capability changes; it does not commit them directly. (docs/adr/ADR-021-whoever-sees-the-performance-may-change-the-capability.md)
- direction
- one node in the tech-tree portfolio (nanobot/runtime/tech_tree.py): an improvement domain the loop can invest cycles in, paired with a lever metric and which way that metric should move. (nanobot/runtime/tech_tree.py)
- lever
- the scorecard metric (section.metric) a tech-tree direction is measured against, e.g. loop.confirmed_integration_ratio. (nanobot/runtime/tech_tree.py)
- vector
- an operator-set, ordered purpose for the loop. Vector 1 (primary): self-improvement of the agent system. Vector 2 (secondary): operator interface and process transparency. (docs/ACTIVE_GOAL.md)
- demand kind
- the category of the next thing offered to the proposer, in trust order: priority > defect > goal-gap > hypothesis > decay. (docs/CURRENT_ARCHITECTURE.md)
- held-out
- sandboxed behavioral checkers run against instance artifacts on the scorecard recompute path; a failure becomes defect demand carrying the checker's evidence, without the instance ever seeing the checker itself. (docs/CURRENT_ARCHITECTURE.md)
- scorecard feed
- one of the named, freshness-monitored input paths nanobot/runtime/scorecard.py reads to compute its 7-day snapshot. (nanobot/runtime/scorecard.py)
- provenance
- the citable evidence trail behind a decision or claim -- which source file, ledger row, or scan produced it -- as opposed to an assertion with no traceable origin. (nanobot/runtime/goal_review.py)
- present
- probe state: the source exists and was read successfully, with real content. (scripts/techtree_viewer.py (read_local_ci_status))
- absent
- probe state: the source file or directory does not exist at all. (scripts/techtree_viewer.py (read_local_ci_status))
- present_uninitialized
- probe state: the source exists, but its own content says nothing has run yet (e.g. no rows, targets_missing) -- distinct from absent (nothing to find) and from a read failure. (scripts/techtree_viewer.py (read_local_ci_status))
- probe_unavailable
- probe state: the source exists but could not be read or parsed (a transient failure) -- distinct from absent (never existed) and present_uninitialized (readable, just empty of activity). (scripts/techtree_viewer.py (read_local_ci_status))