fix: address audit low-hanging fruit — docs accuracy, auction timing, stubs, tests

- Docs: "No Cloud" → "No Cloud AI" (frontend uses CDN for Bootstrap/HTMX/fonts)
- Docs: "600+" → "640+" tests, "20+" → "58" endpoints (actual counts)
- Docs: LND described as "scaffolded" not "gRPC-ready"; remove "agents earn sats"
- Fix auction timing: coordinator sleep(0) → sleep(AUCTION_DURATION_SECONDS)
- agent_core: implement remember() with dedup/eviction, communicate() via swarm comms
- Tests: add CLI tests for chat, think, and backend/model-size forwarding (647 passing)

https://claude.ai/code/session_01SZTwAkTg6v4ybv8g9NLxqN
This commit is contained in:
Claude
2026-02-24 18:29:21 +00:00
parent 0367fe3649
commit 96c9f1b02f
8 changed files with 102 additions and 36 deletions

View File

@@ -2,7 +2,7 @@
[![Tests](https://github.com/AlexanderWhitestone/Timmy-time-dashboard/actions/workflows/tests.yml/badge.svg)](https://github.com/AlexanderWhitestone/Timmy-time-dashboard/actions/workflows/tests.yml)
A local-first, sovereign AI agent system. Talk to Timmy, watch his swarm, gate API access with Bitcoin Lightning — all from a browser, no cloud required.
A local-first, sovereign AI agent system. Talk to Timmy, watch his swarm, gate API access with Bitcoin Lightning — all from a browser, no cloud AI required.
**[Live Docs →](https://alexanderwhitestone.github.io/Timmy-time-dashboard/)**
@@ -15,7 +15,7 @@ A local-first, sovereign AI agent system. Talk to Timmy, watch his swarm, gate
| **Timmy Agent** | Agno-powered agent (Ollama default, AirLLM optional for 70B/405B) |
| **Mission Control** | FastAPI + HTMX dashboard — chat, health, swarm, marketplace |
| **Swarm** | Multi-agent coordinator — spawn agents, post tasks, run Lightning auctions |
| **L402 / Lightning** | Bitcoin Lightning payment gating for API access |
| **L402 / Lightning** | Bitcoin Lightning payment gating for API access (mock backend; LND scaffolded) |
| **Spark Intelligence** | Event capture, predictions, memory consolidation, advisory engine |
| **Creative Studio** | Multi-persona creative pipeline — image, music, video generation |
| **Tools** | Git, image, music, and video tools accessible by persona agents |
@@ -25,7 +25,7 @@ A local-first, sovereign AI agent system. Talk to Timmy, watch his swarm, gate
| **Telegram** | Bridge Telegram messages to Timmy |
| **CLI** | `timmy`, `timmy-serve`, `self-tdd` entry points |
**600+ tests, 100% passing.**
**Full test suite, 100% passing.**
---
@@ -161,7 +161,7 @@ cp .env.example .env
| `AIRLLM_MODEL_SIZE` | `70b` | `8b` \| `70b` \| `405b` |
| `L402_HMAC_SECRET` | *(default — change in prod)* | HMAC signing key for macaroons |
| `L402_MACAROON_SECRET` | *(default — change in prod)* | Macaroon secret |
| `LIGHTNING_BACKEND` | `mock` | `mock` \| `lnd` |
| `LIGHTNING_BACKEND` | `mock` | `mock` (production-ready) \| `lnd` (scaffolded, not yet functional) |
---
@@ -217,7 +217,7 @@ src/
shortcuts/ # Siri Shortcuts endpoints
telegram_bot/ # Telegram bridge
self_tdd/ # Continuous test watchdog
tests/ # 600+ tests — one file per module, all mocked
tests/ # one test file per module, all mocked
static/style.css # Dark mission-control theme (JetBrains Mono)
docs/ # GitHub Pages landing page
AGENTS.md # AI agent development standards ← read this

View File

@@ -563,13 +563,13 @@
<h1>Your agents.<br><em>Your hardware.</em><br>Your sats.</h1>
<p class="hero-sub">
A local-first AI command center. Talk to Timmy, coordinate your swarm,
gate API access with Bitcoin Lightning — no cloud, no telemetry, no compromise.
gate API access with Bitcoin Lightning — no cloud AI, no telemetry, no compromise.
</p>
<div class="hero-badges">
<span class="badge green">600+ Tests Passing</span>
<span class="badge green">Full Test Suite Passing</span>
<span class="badge blue">FastAPI + HTMX</span>
<span class="badge amber">Lightning L402</span>
<span class="badge">No Cloud</span>
<span class="badge">No Cloud AI</span>
<span class="badge purple">Multi-Agent Swarm</span>
<span class="badge">MIT License</span>
</div>
@@ -582,11 +582,11 @@
<!-- ── Stats ────────────────────────────────────────────────────────────── -->
<div class="stats">
<div class="stat">
<div class="stat-number">600+</div>
<div class="stat-number">640+</div>
<div class="stat-label">Tests Passing</div>
</div>
<div class="stat">
<div class="stat-number">20+</div>
<div class="stat-number">58</div>
<div class="stat-label">API Endpoints</div>
</div>
<div class="stat">
@@ -595,7 +595,7 @@
</div>
<div class="stat">
<div class="stat-number">0</div>
<div class="stat-label">Cloud Calls</div>
<div class="stat-label">Cloud AI Calls</div>
</div>
</div>
@@ -639,7 +639,7 @@
<span class="feature-icon"></span>
<h3>L402 Lightning Payments</h3>
<p>Bitcoin Lightning payment gating via HMAC macaroons. Mock backend for dev,
LND gRPC-ready for production. Agents earn and spend sats autonomously.</p>
LND backend scaffolded for production. Auction bids priced in sats.</p>
<span class="feature-tag">L402 · Macaroon · BOLT11</span>
</div>
@@ -780,7 +780,7 @@ External: Ollama :11434 · optional Redis · optional LND gRPC</span>
<div class="step-num">5</div>
<div>
<h4>Test</h4>
<pre class="codeblock"><span class="cmd">make test</span> <span class="cmt"># 600+ tests — no Ollama needed</span>
<pre class="codeblock"><span class="cmd">make test</span> <span class="cmt"># full test suite — no Ollama needed</span>
<span class="cmd">make test-cov</span> <span class="cmt"># + coverage report</span>
<span class="cmd">make watch</span> <span class="cmt"># self-TDD watchdog in background</span></pre>
</div>
@@ -912,7 +912,7 @@ External: Ollama :11434 · optional Redis · optional LND gRPC</span>
<footer>
<div class="footer-copy">
TIMMY TIME // MISSION CONTROL &nbsp;·&nbsp; MIT License &nbsp;·&nbsp;
No cloud. No telemetry. Sats are sovereignty.
No cloud AI. No telemetry. Sats are sovereignty.
</div>
<div class="footer-links">
<a href="https://github.com/AlexanderWhitestone/Timmy-time-dashboard">GitHub</a>

View File

@@ -172,18 +172,19 @@ Respond naturally and helpfully."""
return result
def remember(self, memory: Memory) -> None:
"""Store memory persistently.
For now, working memory is sufficient. In the future,
this would write to SQLite or vector DB for long-term
memory across sessions.
"""Store memory in working memory.
Adds the memory to the sliding window and bumps its importance.
"""
# Mark as accessed to update importance
memory.touch()
# TODO: Persist to SQLite for long-term memory
# This would integrate with the existing briefing system
pass
# Deduplicate by id
self._working_memory = [m for m in self._working_memory if m.id != memory.id]
self._working_memory.append(memory)
# Evict oldest if over capacity
if len(self._working_memory) > self._max_working_memory:
self._working_memory.pop(0)
def recall(self, query: str, limit: int = 5) -> list[Memory]:
"""Retrieve relevant memories.
@@ -215,13 +216,22 @@ Respond naturally and helpfully."""
return [m for _, m in scored[:limit]]
def communicate(self, message: Communication) -> bool:
"""Send message to another agent.
This would use the swarm comms layer for inter-agent
messaging. For now, it's a stub.
"""
# TODO: Integrate with swarm.comms
return True
"""Send message to another agent via swarm comms."""
try:
from swarm.comms import SwarmComms
comms = SwarmComms()
comms.publish(
"agent:messages",
"agent_message",
{
"from": self._identity.name,
"to": message.recipient,
"content": message.content,
},
)
return True
except Exception:
return False
def _extract_tags(self, perception: Perception) -> list[str]:
"""Extract searchable tags from perception."""

View File

@@ -11,7 +11,7 @@ import logging
from datetime import datetime, timezone
from typing import Optional
from swarm.bidder import AuctionManager, Bid
from swarm.bidder import AUCTION_DURATION_SECONDS, AuctionManager, Bid
from swarm.comms import SwarmComms
from swarm import learner as swarm_learner
from swarm.manager import SwarmManager
@@ -227,7 +227,7 @@ class SwarmCoordinator:
All bids are recorded in the learner so agents accumulate outcome
history that later feeds back into adaptive bidding.
"""
await asyncio.sleep(0) # yield to let any pending callbacks fire
await asyncio.sleep(AUCTION_DURATION_SECONDS)
# Snapshot the auction bids before closing (for learner recording)
auction = self.auctions.get_auction(task_id)

View File

@@ -27,3 +27,45 @@ def test_status_does_not_use_inline_string():
call_args = mock_timmy.print_response.call_args
assert call_args[0][0] != "Brief status report — one sentence."
def test_chat_sends_message_to_agent():
"""chat command must pass the user message to the agent with streaming."""
mock_timmy = MagicMock()
with patch("timmy.cli.create_timmy", return_value=mock_timmy):
runner.invoke(app, ["chat", "Hello Timmy"])
mock_timmy.print_response.assert_called_once_with("Hello Timmy", stream=True)
def test_think_sends_topic_to_agent():
"""think command must pass the topic wrapped in a prompt with streaming."""
mock_timmy = MagicMock()
with patch("timmy.cli.create_timmy", return_value=mock_timmy):
runner.invoke(app, ["think", "Bitcoin self-custody"])
mock_timmy.print_response.assert_called_once_with(
"Think carefully about: Bitcoin self-custody", stream=True
)
def test_chat_passes_backend_option():
"""chat --backend airllm must forward the backend to create_timmy."""
mock_timmy = MagicMock()
with patch("timmy.cli.create_timmy", return_value=mock_timmy) as mock_create:
runner.invoke(app, ["chat", "test", "--backend", "airllm"])
mock_create.assert_called_once_with(backend="airllm", model_size=None)
def test_think_passes_model_size_option():
"""think --model-size 70b must forward the model size to create_timmy."""
mock_timmy = MagicMock()
with patch("timmy.cli.create_timmy", return_value=mock_timmy) as mock_create:
runner.invoke(app, ["think", "topic", "--model-size", "70b"])
mock_create.assert_called_once_with(backend=None, model_size="70b")

View File

@@ -162,8 +162,8 @@ async def test_coordinator_run_auction_no_bids():
coord = SwarmCoordinator()
task = coord.post_task("No bids task")
# Patch sleep to avoid 15-second wait
with patch("swarm.bidder.asyncio.sleep", new_callable=AsyncMock):
# Patch sleep to avoid 15-second wait in tests
with patch("swarm.coordinator.asyncio.sleep", new_callable=AsyncMock):
winner = await coord.run_auction_and_assign(task.id)
assert winner is None

View File

@@ -8,7 +8,7 @@ These tests verify that:
"""
import asyncio
from unittest.mock import patch
from unittest.mock import AsyncMock, patch
import pytest
@@ -16,6 +16,13 @@ from swarm.coordinator import SwarmCoordinator
from swarm.tasks import TaskStatus
@pytest.fixture(autouse=True)
def _fast_auction():
"""Skip the 15-second auction wait in tests."""
with patch("swarm.coordinator.AUCTION_DURATION_SECONDS", 0):
yield
class TestSwarmInProcessAgents:
"""Test the in-process agent spawning and bidding flow."""

View File

@@ -8,6 +8,13 @@ import pytest
from unittest.mock import AsyncMock, MagicMock, patch
@pytest.fixture(autouse=True)
def _fast_auction():
"""Skip the 15-second auction wait in tests."""
with patch("swarm.coordinator.AUCTION_DURATION_SECONDS", 0):
yield
class TestFullSwarmLifecycle:
"""Integration tests for end-to-end swarm task lifecycle."""