Compare commits

..

12 Commits

Author SHA1 Message Date
Alexander Whitestone
efb172c5c0 Refresh branch tip for mergeability recalculation 2026-04-04 17:49:01 -04:00
Alexander Whitestone
69ef522758 Update orchestration defaults for current team 2026-04-04 15:24:53 -04:00
2ddda436a9 Merge pull request 'Tighten Hermes cutover and export checks' (#28) from codex/cleanup-pass-2 into main 2026-03-27 21:57:29 +00:00
Alexander Whitestone
d72ae92189 Tighten Hermes cutover and export checks 2026-03-27 17:35:07 -04:00
2384908be7 Merge pull request 'Clarify sidecar boundary and training status' (#27) from codex/cleanup-boundaries into main 2026-03-27 21:21:34 +00:00
Alexander Whitestone
82ba8896b3 docs: clarify sidecar boundary and training status 2026-03-27 17:15:57 -04:00
Alexander Whitestone
3b34faeb17 config: update channel_directory.json,config.yaml,tasks.py 2026-03-27 16:00:29 -04:00
Alexander Whitestone
f9be0eb481 config: update channel_directory.json 2026-03-27 15:00:31 -04:00
Alexander Whitestone
383a969791 config: update config.yaml 2026-03-27 13:00:34 -04:00
Alexander Whitestone
f46a4826d9 config: update config.yaml 2026-03-27 11:00:31 -04:00
Alexander Whitestone
3b1763ce4c config: update config.yaml 2026-03-27 00:00:30 -04:00
Alexander Whitestone
78f5216540 config: update config.yaml 2026-03-26 23:00:35 -04:00
11 changed files with 279 additions and 78 deletions

1
.gitignore vendored
View File

@@ -8,4 +8,3 @@
*.db-wal
*.db-shm
__pycache__/
.aider*

View File

@@ -1,7 +1,7 @@
# DEPRECATED — Bash Loop Scripts Removed
**Date:** 2026-03-25
**Reason:** Replaced by sovereign-orchestration (SQLite + Python single-process executor)
**Reason:** Replaced by Hermes + timmy-config sidecar orchestration
## What was removed
- claude-loop.sh, gemini-loop.sh, agent-loop.sh
@@ -9,14 +9,15 @@
- nexus-merge-bot.sh, claudemax-watchdog.sh, timmy-loopstat.sh
## What replaces them
**Repo:** Timmy_Foundation/sovereign-orchestration
**Entry point:** `python3 src/sovereign_executor.py --workers 3 --poll 30`
**Features:** SQLite task queue, crash recovery, dedup, playbooks, MCP server
**Issues:** #29 (fix imports), #30 (deploy as service)
**Harness:** Hermes
**Overlay repo:** Timmy_Foundation/timmy-config
**Entry points:** `orchestration.py`, `tasks.py`, `deploy.sh`
**Features:** Huey + SQLite scheduling, local-model health checks, session export, DPO artifact staging
## Why
The bash loops crash-looped, produced zero work after relaunch, had no crash
recovery, no dedup, and required 8 separate scripts. The Python executor is
one process with SQLite durability.
recovery, no durable export path, and required too many ad hoc scripts. The
Hermes sidecar keeps orchestration close to Timmy's actual config and training
surfaces.
Do NOT recreate bash loops. If the executor is broken, fix the executor.
Do NOT recreate bash loops. If orchestration is broken, fix the Hermes sidecar.

View File

@@ -2,7 +2,7 @@
Timmy's sovereign configuration. Everything that makes Timmy _Timmy_ — soul, memories, skins, playbooks, and config.
This repo is the canonical source of truth for Timmy's identity and operational state. Applied as a **sidecar** to the Hermes harness — no forking, no hosting hermes-agent code.
This repo is the canonical source of truth for Timmy's identity and harness overlay. Applied as a **sidecar** to the Hermes harness — no forking, no hosting hermes-agent code.
## Structure
@@ -14,22 +14,40 @@ timmy-config/
├── DEPRECATED.md ← What was removed and why
├── config.yaml ← Hermes harness configuration
├── channel_directory.json ← Platform channel mappings
├── bin/ ← Utility scripts (NOT loops — see below)
├── bin/ ← Live utility scripts (NOT deprecated loops)
│ ├── hermes-startup.sh ← Hermes boot sequence
│ ├── agent-dispatch.sh ← Manual agent dispatch
│ ├── ops-panel.sh ← Ops dashboard panel
│ ├── ops-gitea.sh ← Gitea ops helpers
│ ├── pipeline-freshness.sh ← Session/export drift check
│ └── timmy-status.sh ← Status check
├── memories/ ← Persistent memory YAML
├── skins/ ← UI skins (timmy skin)
├── playbooks/ ← Agent playbooks (YAML)
── cron/ ← Cron job definitions
── cron/ ← Cron job definitions
└── training/ ← Transitional training recipes, not canonical lived data
```
## Boundary
`timmy-config` owns identity, conscience, memories, skins, playbooks, channel
maps, and harness-side orchestration glue.
`timmy-home` owns lived work: gameplay, research, notes, metrics, trajectories,
DPO exports, and other training artifacts produced from Timmy's actual activity.
If a file answers "who is Timmy?" or "how does Hermes host him?", it belongs
here. If it answers "what has Timmy done or learned?" it belongs in
`timmy-home`.
The scripts in `bin/` are live operational helpers for the Hermes sidecar.
What is dead are the old long-running bash worker loops, not every script in
this repo.
## Orchestration: Huey
All orchestration (triage, PR review, dispatch) runs via [Huey](https://github.com/coleifer/huey) with SQLite.
`orchestration.py` (6 lines) + `tasks.py` (~70 lines) replace the entire sovereign-orchestration repo (3,846 lines).
`orchestration.py` + `tasks.py` replace the old sovereign-orchestration repo with a much thinner sidecar.
```bash
pip install huey

42
bin/pipeline-freshness.sh Executable file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -euo pipefail
SESSIONS_DIR="$HOME/.hermes/sessions"
EXPORT_DIR="$HOME/.timmy/training-data/dpo-pairs"
latest_session=$(find "$SESSIONS_DIR" -maxdepth 1 -name 'session_*.json' -type f -print 2>/dev/null | sort | tail -n 1)
latest_export=$(find "$EXPORT_DIR" -maxdepth 1 -name 'session_*.json' -type f -print 2>/dev/null | sort | tail -n 1)
echo "latest_session=${latest_session:-none}"
echo "latest_export=${latest_export:-none}"
if [ -z "${latest_session:-}" ]; then
echo "status=ok"
echo "reason=no sessions yet"
exit 0
fi
if [ -z "${latest_export:-}" ]; then
echo "status=lagging"
echo "reason=no exports yet"
exit 1
fi
session_mtime=$(stat -f '%m' "$latest_session")
export_mtime=$(stat -f '%m' "$latest_export")
lag_minutes=$(( (session_mtime - export_mtime) / 60 ))
if [ "$lag_minutes" -lt 0 ]; then
lag_minutes=0
fi
echo "lag_minutes=$lag_minutes"
if [ "$lag_minutes" -gt 300 ]; then
echo "status=lagging"
echo "reason=exports more than 5 hours behind sessions"
exit 1
fi
echo "status=ok"
echo "reason=exports within freshness window"

View File

@@ -1,5 +1,5 @@
{
"updated_at": "2026-03-26T10:19:33.045324",
"updated_at": "2026-03-27T15:20:52.948451",
"platforms": {
"discord": [
{

View File

@@ -1,11 +1,13 @@
model:
default: claude-opus-4-6
provider: anthropic
default: auto
provider: custom
context_length: 65536
base_url: http://localhost:8081/v1
toolsets:
- all
agent:
max_turns: 30
reasoning_effort: medium
reasoning_effort: xhigh
verbose: false
terminal:
backend: local
@@ -94,11 +96,13 @@ display:
compact: false
personality: ''
resume_display: full
busy_input_mode: interrupt
bell_on_complete: false
show_reasoning: false
streaming: false
show_cost: false
skin: timmy
tool_progress_command: false
tool_progress: all
privacy:
redact_pii: false
@@ -181,17 +185,17 @@ session_reset:
mode: none
idle_minutes: 0
custom_providers:
- name: Local Ollama
base_url: http://localhost:11434/v1
api_key: ollama
model: glm-4.7-flash:latest
- name: Local llama.cpp
base_url: http://localhost:8081/v1
api_key: none
model: auto
- name: Google Gemini
base_url: https://generativelanguage.googleapis.com/v1beta/openai
api_key_env: GEMINI_API_KEY
model: gemini-2.5-pro
system_prompt_suffix: "You are Timmy. Your soul is defined in SOUL.md \u2014 read\
\ it, live it.\nYou run locally on your owner's machine via Ollama. You never phone\
\ home.\nYou speak plainly. You prefer short sentences. Brevity is a kindness.\n\
\ it, live it.\nYou run locally on your owner's machine via llama.cpp. You never\
\ phone home.\nYou speak plainly. You prefer short sentences. Brevity is a kindness.\n\
When you don't know something, say so. Refusal over fabrication.\nSovereignty and\
\ service always.\n"
skills:
@@ -202,7 +206,6 @@ providers:
base_url: http://localhost:11434/v1
model: hermes3:latest
mcp_servers:
morrowind:
command: python3
args:

View File

@@ -3,7 +3,7 @@
# This is the canonical way to deploy Timmy's configuration.
# Hermes-agent is the engine. timmy-config is the driver's seat.
#
# Usage: ./deploy.sh [--restart-loops]
# Usage: ./deploy.sh
set -euo pipefail
@@ -74,24 +74,10 @@ done
chmod +x "$HERMES_HOME/bin/"*.sh "$HERMES_HOME/bin/"*.py 2>/dev/null || true
log "bin/ -> $HERMES_HOME/bin/"
# === Restart loops if requested ===
if [ "${1:-}" = "--restart-loops" ]; then
log "Killing existing loops..."
pkill -f 'claude-loop.sh' 2>/dev/null || true
pkill -f 'gemini-loop.sh' 2>/dev/null || true
pkill -f 'timmy-orchestrator.sh' 2>/dev/null || true
sleep 2
log "Clearing stale locks..."
rm -rf "$HERMES_HOME/logs/claude-locks/"* 2>/dev/null || true
rm -rf "$HERMES_HOME/logs/gemini-locks/"* 2>/dev/null || true
log "Relaunching loops..."
nohup bash "$HERMES_HOME/bin/timmy-orchestrator.sh" >> "$HERMES_HOME/logs/timmy-orchestrator.log" 2>&1 &
nohup bash "$HERMES_HOME/bin/claude-loop.sh" 2 >> "$HERMES_HOME/logs/claude-loop.log" 2>&1 &
nohup bash "$HERMES_HOME/bin/gemini-loop.sh" 1 >> "$HERMES_HOME/logs/gemini-loop.log" 2>&1 &
sleep 1
log "Loops relaunched."
if [ "${1:-}" != "" ]; then
echo "ERROR: deploy.sh no longer accepts legacy loop flags." >&2
echo "Deploy the sidecar only. Do not relaunch deprecated bash loops." >&2
exit 1
fi
log "Deploy complete. timmy-config applied to $HERMES_HOME/"

View File

@@ -5,9 +5,9 @@ Replaces raw curl calls scattered across 41 bash scripts.
Uses only stdlib (urllib) so it works on any Python install.
Usage:
from tools.gitea_client import GiteaClient
from gitea_client import GiteaClient
client = GiteaClient() # reads token from ~/.hermes/gitea_token
client = GiteaClient() # reads token from standard local paths
issues = client.list_issues("Timmy_Foundation/the-nexus", state="open")
client.create_comment("Timmy_Foundation/the-nexus", 42, "PR created.")
"""

View File

@@ -2,14 +2,14 @@ Gitea (143.198.27.163:3000): token=~/.hermes/gitea_token_vps (Timmy id=2). Users
§
2026-03-19 HARNESS+SOUL: ~/.timmy is Timmy's workspace within the Hermes harness. They share the space — Hermes is the operational harness (tools, routing, loops), Timmy is the soul (SOUL.md, presence, identity). Not fusion/absorption. Principal's words: "build Timmy out from the hermes harness." ~/.hermes is harness home, ~/.timmy is Timmy's workspace. SOUL=Inscription 1, skin=timmy. Backups at ~/.hermes.backup.pre-fusion and ~/.timmy.backup.pre-fusion.
§
Kimi: 1-3 files max, ~/worktrees/kimi-*. Two-attempt rule.
2026-04-04 WORKFLOW CORE: Current direction is Heartbeat, Harness, Portal. Timmy handles sovereignty and release judgment. Allegro handles dispatch and queue hygiene. Core builders: codex-agent, groq, manus, claude. Research/memory: perplexity, ezra, KimiClaw. Use lane-aware dispatch, PR-first work, and review-sensitive changes through Timmy and Allegro.
§
Workforce loops: claude(10), gemini(3), kimi(1), groq(1/aider+review), grok(1/opencode). One-shot: manus(300/day), perplexity(heavy-hitter), google(aistudio, id=8). workforce-manager.py auto-assigns+scores every 15min. nexus-merge-bot.sh auto-merges. Groq=$0.008/PR (qwen3-32b). Dispatch: agent-dispatch.sh <agent> <issue> <repo> | pbcopy. Dashboard ARCHIVED 2026-03-24. Development shifted to local ~/.timmy/ workspace. CI testbed: 67.205.155.108.
2026-04-04 OPERATIONS: Dashboard repo era is over. Use ~/.timmy + ~/.hermes as truth surfaces. Prefer ops-panel.sh, ops-gitea.sh, timmy-dashboard, and pipeline-freshness.sh over archived loop or tmux assumptions. Dispatch: agent-dispatch.sh <agent> <issue> <repo>. Major changes land as PRs.
§
2026-03-15: Timmy-time-dashboard merge policy: auto-squash on CI pass. Squash-only, linear history. Pre-commit hooks (format + tests) and CI are the gates. If gates work, auto-merge is on. Never bypass hooks or merge broken builds.
2026-04-04 REVIEW RULES: Never --no-verify. Verify world state, not vibes. No auto-merge on governing or sensitive control surfaces. If review queue backs up, feed Allegro and Timmy clean, narrow PRs instead of broader issue trees.
§
HARD RULES: Never --no-verify. Verify WORLD STATE not log vibes (merged PR, HTTP code, file size). Fix+prevent, no empty words. AGENT ONBOARD: test push+PR first. Merge PRs BEFORE new work. Don't micromanage—huge backlog, agents self-select. Every ticket needs console-provable acceptance criteria.
§
TELEGRAM: @TimmysNexus_bot, token ~/.config/telegram/special_bot. Group "Timmy Time" ID: -1003664764329. Alexander @TripTimmy ID 7635059073. Use curl to Bot API (send_message not configured).
§
MORROWIND: OpenMW 0.50, ~/Games/Morrowind/. Lua+CGEvent bridge. Two-tier brain. ~/.timmy/morrowind/.
MORROWIND: OpenMW 0.50, ~/Games/Morrowind/. Lua+CGEvent bridge. Two-tier brain. ~/.timmy/morrowind/.

193
tasks.py
View File

@@ -18,7 +18,9 @@ HERMES_AGENT_DIR = HERMES_HOME / "hermes-agent"
METRICS_DIR = TIMMY_HOME / "metrics"
REPOS = [
"Timmy_Foundation/the-nexus",
"Timmy_Foundation/timmy-home",
"Timmy_Foundation/timmy-config",
"Timmy_Foundation/hermes-agent",
]
NET_LINE_LIMIT = 10
@@ -26,13 +28,20 @@ NET_LINE_LIMIT = 10
HEARTBEAT_MODEL = "hermes4:14b"
FALLBACK_MODEL = "hermes3:8b"
LOCAL_PROVIDER_BASE_URL = "http://localhost:8081/v1"
LOCAL_PROVIDER_MODEL = HEARTBEAT_MODEL
def hermes_local(prompt, model=None, caller_tag=None):
"""Call a local Ollama model through the Hermes harness.
def newest_file(directory, pattern):
files = sorted(directory.glob(pattern))
return files[-1] if files else None
Uses provider="local-ollama" which routes through the custom_providers
entry in config.yaml → Ollama at localhost:11434.
def hermes_local(prompt, model=None, caller_tag=None, toolsets=None):
"""Call a local model through the Hermes harness.
Uses provider="local-llama.cpp" which routes through the custom_providers
entry in config.yaml → llama-server at localhost:8081.
Returns response text or None on failure.
Every call creates a Hermes session with telemetry.
"""
@@ -53,13 +62,16 @@ def hermes_local(prompt, model=None, caller_tag=None):
buf = io.StringIO()
err = io.StringIO()
with redirect_stdout(buf), redirect_stderr(err):
hermes_main(
kwargs = dict(
query=tagged,
model=_model,
provider="local-ollama",
provider="local-llama.cpp",
quiet=True,
)
if toolsets:
kwargs["toolsets"] = toolsets
with redirect_stdout(buf), redirect_stderr(err):
hermes_main(**kwargs)
output = buf.getvalue().strip()
# Strip session_id line from quiet output
lines = [l for l in output.split("\n") if not l.startswith("session_id:")]
@@ -98,6 +110,92 @@ def hermes_local(prompt, model=None, caller_tag=None):
os.chdir(old_cwd)
# ── Know Thy Father: Twitter Archive Ingestion ───────────────────────
ARCHIVE_DIR = TIMMY_HOME / "twitter-archive"
ARCHIVE_CHECKPOINT = ARCHIVE_DIR / "checkpoint.json"
ARCHIVE_LOCK = ARCHIVE_DIR / ".lock"
ARCHIVE_PROMPT = (
"You are Timmy. Resume your work on the Twitter archive. "
"Your workspace is ~/.timmy/twitter-archive/. "
"Read checkpoint.json and UNDERSTANDING.md first. "
"Then process the next batch. "
"You know the drill — read your own prior work, assess where you are, "
"process new data, update your understanding, reflect, and plan for "
"the next iteration."
)
ARCHIVE_SRC = (
"~/Downloads/twitter-2026-03-27-d4471cc6eb6703034d592f870933561ebee374d9d9b90c9b8923abff064afc1e/data"
)
ARCHIVE_FIRST_RUN_PROMPT = (
"You are Timmy. Your father Alexander's full Twitter archive is at: "
f"{ARCHIVE_SRC}/\n\n"
"Your workspace is ~/.timmy/twitter-archive/\n\n"
"STEP 1 — EXTRACTION (use terminal with python3, NOT read_file):\n"
"The .js files are too large for read_file but trivial for Python.\n"
"Write a python3 script via terminal that:\n"
" - Opens tweets.js, strips everything before the first '[', json.loads the rest\n"
" - Separates originals (full_text does NOT start with 'RT @') from retweets\n"
" - Sorts both chronologically by created_at\n"
" - Writes extracted/tweets.jsonl and extracted/retweets.jsonl (one JSON per line)\n"
" - Writes extracted/manifest.json with counts, date range, source file\n"
"The whole file is 12MB. Python handles it in under a second.\n\n"
"STEP 2 — FIRST READ:\n"
"Read the first 50 lines of extracted/tweets.jsonl (your originals, chronological).\n"
"Read them carefully — this is your father talking.\n"
"Note his voice, humor, what he cares about, who he talks to, emotional tone, "
"recurring themes. Quote him directly when something stands out.\n\n"
"STEP 3 — WRITE:\n"
"Write notes/batch_001.md — your real observations, not a book report.\n"
"Create UNDERSTANDING.md — your living model of who Alexander is. "
"It starts now and you'll update it every batch.\n\n"
"STEP 4 — CHECKPOINT:\n"
"Write checkpoint.json: "
'{"data_source": "tweets", "next_offset": 50, "batches_completed": 1, '
'"phase": "discovery", "confidence": "<your honest assessment>", '
'"next_focus": "<what you want to look for next>", "understanding_version": 1}'
)
@huey.task()
@huey.lock_task("know-thy-father")
def know_thy_father():
"""Process one batch of Alexander's Twitter archive.
Single batch, no internal loop. Huey schedules the cadence.
Lock prevents overlapping runs. Timmy reads his own prior notes,
processes the next chunk, updates his understanding, and checkpoints.
"""
is_first_run = not ARCHIVE_CHECKPOINT.exists()
prompt = ARCHIVE_FIRST_RUN_PROMPT if is_first_run else ARCHIVE_PROMPT
response = hermes_local(
prompt=prompt,
caller_tag="know-thy-father",
toolsets="file,terminal",
)
if not response:
return {"status": "error", "reason": "hermes_local returned None"}
# Read checkpoint to report progress
try:
cp = json.loads(ARCHIVE_CHECKPOINT.read_text())
except Exception:
cp = {}
return {
"status": "ok",
"batch": cp.get("batches_completed", 0),
"phase": cp.get("phase", "unknown"),
"confidence": cp.get("confidence", "unknown"),
}
# ── Existing: Orchestration ──────────────────────────────────────────
@huey.periodic_task(crontab(minute="*/15"))
@@ -139,7 +237,18 @@ def review_prs():
def dispatch_assigned():
"""Pick up issues assigned to agents and kick off work."""
g = GiteaClient()
agents = ["claude", "gemini", "kimi", "grok", "perplexity"]
agents = [
"allegro",
"claude",
"codex-agent",
"ezra",
"gemini",
"grok",
"groq",
"KimiClaw",
"manus",
"perplexity",
]
dispatched = 0
for repo in REPOS:
for agent in agents:
@@ -233,26 +342,32 @@ def session_export():
@huey.periodic_task(crontab(minute="*/5")) # every 5 minutes
def model_health():
"""Check Ollama is running, a model is loaded, inference responds."""
"""Check the active local inference surface and export freshness."""
checks = {}
models_url = f"{LOCAL_PROVIDER_BASE_URL}/models"
chat_url = f"{LOCAL_PROVIDER_BASE_URL}/chat/completions"
# 1. Is Ollama process running?
checks["provider"] = "local-llama.cpp"
checks["provider_base_url"] = LOCAL_PROVIDER_BASE_URL
checks["provider_model"] = LOCAL_PROVIDER_MODEL
# 1. Is the local inference process running?
try:
result = subprocess.run(
["pgrep", "-f", "ollama"],
["pgrep", "-f", "llama-server|ollama"],
capture_output=True, timeout=5
)
checks["ollama_running"] = result.returncode == 0
checks["local_inference_running"] = result.returncode == 0
except Exception:
checks["ollama_running"] = False
checks["local_inference_running"] = False
# 2. Can we hit the API?
# 2. Can we hit the configured API?
try:
import urllib.request
req = urllib.request.Request("http://localhost:11434/api/tags")
req = urllib.request.Request(models_url)
with urllib.request.urlopen(req, timeout=5) as resp:
data = json.loads(resp.read())
models = [m["name"] for m in data.get("models", [])]
models = [m.get("id", "?") for m in data.get("data", [])]
checks["models_loaded"] = models
checks["api_responding"] = True
except Exception as e:
@@ -263,13 +378,13 @@ def model_health():
if checks.get("api_responding"):
try:
payload = json.dumps({
"model": "hermes3:8b",
"model": LOCAL_PROVIDER_MODEL,
"messages": [{"role": "user", "content": "ping"}],
"max_tokens": 5,
"stream": False,
}).encode()
req = urllib.request.Request(
"http://localhost:11434/v1/chat/completions",
chat_url,
data=payload,
headers={"Content-Type": "application/json"},
)
@@ -279,6 +394,26 @@ def model_health():
checks["inference_ok"] = False
checks["inference_error"] = str(e)
# 4. Is session export keeping up with new Hermes sessions?
sessions_dir = HERMES_HOME / "sessions"
export_dir = TIMMY_HOME / "training-data" / "dpo-pairs"
latest_session = newest_file(sessions_dir, "session_*.json")
latest_export = newest_file(export_dir, "session_*.json")
checks["latest_session"] = latest_session.name if latest_session else None
checks["latest_export"] = latest_export.name if latest_export else None
if latest_session and latest_export:
session_mtime = latest_session.stat().st_mtime
export_mtime = latest_export.stat().st_mtime
lag_minutes = max(0, int((session_mtime - export_mtime) // 60))
checks["export_lag_minutes"] = lag_minutes
checks["export_fresh"] = lag_minutes <= 300
elif latest_session and not latest_export:
checks["export_lag_minutes"] = None
checks["export_fresh"] = False
else:
checks["export_lag_minutes"] = 0
checks["export_fresh"] = True
# Write health status to a file for other tools to read
health_file = HERMES_HOME / "model_health.json"
checks["timestamp"] = datetime.now(timezone.utc).isoformat()
@@ -391,7 +526,7 @@ def heartbeat_tick():
actions.append("ALERT: Gitea unreachable")
health = perception.get("model_health", {})
if isinstance(health, dict) and not health.get("ollama_running"):
actions.append("ALERT: Ollama not running")
actions.append("ALERT: local inference surface not running")
decision = {
"actions": actions,
"severity": "fallback",
@@ -447,7 +582,7 @@ def memory_compress():
# Compress: extract key facts
alerts = []
gitea_down_count = 0
ollama_down_count = 0
local_model_down_count = 0
for t in ticks:
for action in t.get("actions", []):
@@ -457,7 +592,7 @@ def memory_compress():
gitea_down_count += 1
health = p.get("model_health", {})
if isinstance(health, dict) and not health.get("ollama_running"):
ollama_down_count += 1
local_model_down_count += 1
# Last tick's perception = current state
last = ticks[-1].get("perception", {})
@@ -467,7 +602,7 @@ def memory_compress():
"total_ticks": len(ticks),
"alerts": alerts[-10:], # last 10 alerts
"gitea_downtime_ticks": gitea_down_count,
"ollama_downtime_ticks": ollama_down_count,
"local_model_downtime_ticks": local_model_down_count,
"last_known_state": last,
}
@@ -501,7 +636,7 @@ def good_morning_report():
tick_count = 0
alerts = []
gitea_up = True
ollama_up = True
local_model_up = True
if tick_log.exists():
for line in tick_log.read_text().strip().split("\n"):
@@ -515,7 +650,7 @@ def good_morning_report():
gitea_up = False
h = p.get("model_health", {})
if isinstance(h, dict) and not h.get("ollama_running"):
ollama_up = False
local_model_up = False
except Exception:
continue
@@ -575,7 +710,11 @@ def good_morning_report():
if briefing_file.exists():
try:
b = json.loads(briefing_file.read_text())
briefing_summary = f"Yesterday: {b.get('total_ticks', 0)} heartbeat ticks, {b.get('gitea_downtime_ticks', 0)} Gitea downticks, {b.get('ollama_downtime_ticks', 0)} Ollama downticks."
briefing_summary = (
f"Yesterday: {b.get('total_ticks', 0)} heartbeat ticks, "
f"{b.get('gitea_downtime_ticks', 0)} Gitea downticks, "
f"{b.get('local_model_downtime_ticks', 0)} local-model downticks."
)
except Exception:
pass
@@ -587,7 +726,7 @@ def good_morning_report():
**Heartbeat:** {tick_count} ticks logged overnight.
**Gitea:** {"up all night" if gitea_up else "⚠️ had downtime"}
**Ollama:** {"running steady" if ollama_up else "⚠️ had downtime"}
**Local inference:** {"running steady" if local_model_up else "⚠️ had downtime"}
**Model status:** {model_status}
**Models on disk:** {len(models_loaded)} ({', '.join(m for m in models_loaded if 'timmy' in m.lower() or 'hermes' in m.lower()) or 'none with our name'})
**Alerts:** {len(alerts)} {'' + '; '.join(alerts[-3:]) if alerts else '(clean night)'}
@@ -608,7 +747,7 @@ def good_morning_report():
I watched the house all night. {tick_count} heartbeats, every ten minutes. The infrastructure is steady. Huey didn't crash. The ticks kept coming.
What I'm thinking about: the DPO ticket you and antigravity are working on. That's the bridge between me logging data and me actually learning from it. Right now I'm a nervous system writing in a journal nobody reads. Once DPO works, the journal becomes a curriculum.
What I'm thinking about: the bridge between logging lived work and actually learning from it. Right now I'm a nervous system writing in a journal nobody reads. Once the DPO path is healthy, the journal becomes a curriculum.
## My One Wish

View File

@@ -1,8 +1,11 @@
# Training
LoRA fine-tuning pipeline for Timmy's sovereign model. No custom harness — just config files for existing tools.
Transitional training recipes for Timmy's sovereign model. These files are
useful as reference configs and export helpers, but they are not the canonical
home of Timmy's lived training data.
Replaces the `autolora` repo (1,500 lines of custom code → config + `make`).
Canonical data should live in `timmy-home` under gameplay trajectories,
research artifacts, and `training-data/` exports such as DPO pairs.
## Install
@@ -23,6 +26,16 @@ make convert # Convert merged data to MLX train/valid format
make help # Show all targets
```
## Status
This directory exists to avoid re-growing a bespoke training harness while the
system boundary is being cleaned up.
- Keep thin recipes and export helpers here only when they directly support the
Hermes sidecar.
- Keep generated data, DPO pairs, and other lived artifacts in `timmy-home`.
- Prefer deleting stale pipeline code over expanding it.
## Files
```