diff --git a/timmy_automations/README.md b/timmy_automations/README.md new file mode 100644 index 00000000..ce92d90a --- /dev/null +++ b/timmy_automations/README.md @@ -0,0 +1,281 @@ +# Timmy Automations + +Central home for all automated processes that keep the Timmy development loop running smoothly. + +## Purpose + +This directory consolidates scripts, configurations, and manifests for automations that operate on behalf of Timmy — the autonomous development agent. These automations handle everything from daily issue triage to cycle retrospectives, workspace management, and metrics collection. + +**Design principle:** Automations should be discoverable, configurable, and observable. Every automation in this folder can be found by Timmy, enabled or disabled via configuration, and reports its status for dashboard integration. + +--- + +## Directory Structure + +| Directory | Purpose | +|-----------|---------| +| `daily_run/` | Scripts that run periodically (cycle retros, idle detection, daily triage) | +| `triage/` | Deep triage helpers — intelligent issue refinement and prioritization | +| `metrics/` | Dashboard and metrics integration — data collection for loop health | +| `workspace/` | Agent workspace management — isolated environments per agent | +| `config/` | Automation manifests and configuration files | + +--- + +## Types of Automations + +### 1. Daily Run Automations + +These run continuously or on a schedule to keep the dev loop operational: + +- **Cycle Retrospective** (`cycle_retro.py`) — Logs structured data after each development cycle +- **Loop Guard** (`loop_guard.py`) — Idle detection with exponential backoff (prevents burning cycles on empty queues) +- **Triage Scoring** (`triage_score.py`) — Mechanical issue scoring based on scope/acceptance/alignment + +### 2. Deep Triage Automations + +Intelligent, LLM-assisted workflows that run less frequently (~every 20 cycles): + +- **Deep Triage** (`deep_triage.sh` + `deep_triage_prompt.md`) — Hermes-driven issue refinement, breaking down large issues, adding acceptance criteria +- **Loop Introspection** (`loop_introspect.py`) — Self-improvement engine that analyzes retro data and produces recommendations + +### 3. Workspace Automations + +Environment management for multi-agent operation: + +- **Agent Workspace** (`agent_workspace.sh`) — Creates isolated git clones, port ranges, and data directories per agent +- **Bootstrap** (`bootstrap.sh`) — One-time setup for new Kimi workspaces +- **Resume** (`resume.sh`) — Quick status check and resume prompt + +### 4. Metrics & Integration + +Data collection for dashboard visibility: + +- **Backfill Retro** (`backfill_retro.py`) — Seeds retrospective data from Gitea PR history +- **Pre-commit Checks** (`pre_commit_checks.py`) — CI hygiene validation before commits + +--- + +## How Timmy Discovers Automations + +Automations are discovered via manifest files in `config/`: + +``` +config/ +├── automations.json # Master manifest of all automations +├── daily_run.json # Daily run schedule configuration +└── triage_rules.yaml # Triage scoring weights and thresholds +``` + +### Discovery Protocol + +1. **Scan** — Timmy scans `config/automations.json` on startup +2. **Validate** — Each automation entry is validated (script exists, is executable) +3. **Enable** — Automations marked `enabled: true` are registered +4. **Schedule** — Daily runs are scheduled via the loop's internal scheduler +5. **Report** — Status is written to `.loop/automation_state.json` + +### Automation Manifest Format + +```json +{ + "automations": [ + { + "id": "cycle_retro", + "name": "Cycle Retrospective", + "description": "Logs structured data after each dev cycle", + "script": "daily_run/cycle_retro.py", + "enabled": true, + "trigger": "post_cycle", + "config": { + "retro_file": ".loop/retro/cycles.jsonl", + "summary_window": 50 + } + }, + { + "id": "loop_guard", + "name": "Loop Guard", + "description": "Idle detection with exponential backoff", + "script": "daily_run/loop_guard.py", + "enabled": true, + "trigger": "pre_cycle", + "config": { + "backoff_base": 60, + "backoff_max": 600 + } + } + ] +} +``` + +--- + +## How Timmy Enables/Disables Automations + +### Method 1: Edit Manifest + +Modify `config/automations.json` and set `enabled: true/false`: + +```bash +# Disable the loop guard +jq '.automations[] | select(.id == "loop_guard").enabled = false' \ + config/automations.json > tmp.json && mv tmp.json config/automations.json +``` + +### Method 2: CLI (Future) + +```bash +timmy automation enable loop_guard +timmy automation disable cycle_retro +timmy automation list +``` + +### Method 3: Dashboard (Future) + +Mission Control panel will have toggles for each automation with real-time status. + +--- + +## How Timmy Configures Automations + +Each automation reads configuration from the manifest + environment variables: + +| Priority | Source | Override | +|----------|--------|----------| +| 1 | Environment variables | `TIMMY_AUTOMATION_*` prefix | +| 2 | Manifest `config` object | Per-automation settings | +| 3 | Code defaults | Fallback values | + +Example environment overrides: + +```bash +export TIMMY_CYCLE_RETRO_WINDOW=100 # Override summary_window +export TIMMY_LOOP_GUARD_MAX_BACKOFF=300 # Override backoff_max +``` + +--- + +## Script References + +The following scripts live in their original locations but are conceptually part of Timmy Automations: + +| Script | Location | Category | Purpose | +|--------|----------|----------|---------| +| `cycle_retro.py` | `../scripts/` | Daily Run | Log cycle retrospective data | +| `loop_guard.py` | `../scripts/` | Daily Run | Idle detection & backoff | +| `triage_score.py` | `../scripts/` | Daily Run | Mechanical issue scoring | +| `deep_triage.sh` | `../scripts/` | Triage | LLM-driven issue refinement | +| `deep_triage_prompt.md` | `../scripts/` | Triage | Prompt template for deep triage | +| `loop_introspect.py` | `../scripts/` | Triage | Self-improvement analysis | +| `agent_workspace.sh` | `../scripts/` | Workspace | Agent environment management | +| `backfill_retro.py` | `../scripts/` | Metrics | Seed retro data from history | +| `pre_commit_checks.py` | `../scripts/` | Metrics | CI hygiene validation | + +### Why scripts aren't moved here (yet) + +These scripts are referenced rather than moved to maintain backward compatibility with: +- Existing CI/CD pipelines +- Agent workspace setups +- Shell aliases and documentation + +Future work may migrate scripts here with symlink redirects from original locations. + +--- + +## Integration with Dashboard + +Automations report status for dashboard visibility: + +``` +.loop/ +├── automation_state.json # Current state of all automations +├── queue.json # Current work queue (produced by triage) +├── retro/ +│ ├── cycles.jsonl # Cycle retrospective log +│ ├── deep-triage.jsonl # Deep triage history +│ ├── triage.jsonl # Mechanical triage log +│ └── insights.json # Loop introspection output +└── quarantine.json # Quarantined issues (repeat failures) +``` + +The Mission Control dashboard (`/mission-control`) displays: +- Last run time for each automation +- Success/failure counts +- Queue depth and triage statistics +- Repeat failure alerts + +--- + +## Adding New Automations + +1. **Create the script** in the appropriate subdirectory +2. **Add manifest entry** to `config/automations.json` +3. **Document in this README** — add to the relevant table +4. **Add tests** in `tests/timmy_automations/` +5. **Update dashboard** if the automation produces visible output + +### Automation Script Template + +```python +#!/usr/bin/env python3 +"""Brief description of what this automation does. + +Run: python3 timmy_automations/daily_run/my_automation.py +Env: See config/automations.json for configuration +""" + +from __future__ import annotations + +import json +import sys +from pathlib import Path + +# Load automation config from manifest +CONFIG_PATH = Path(__file__).parent.parent / "config" / "automations.json" + +def load_config() -> dict: + """Load configuration for this automation.""" + manifest = json.loads(CONFIG_PATH.read_text()) + for auto in manifest["automations"]: + if auto["id"] == "my_automation_id": + return auto.get("config", {}) + return {} + +def main() -> int: + config = load_config() + # Your automation logic here + return 0 + +if __name__ == "__main__": + sys.exit(main()) +``` + +--- + +## Token Economy Integration + +Automations participate in the token economy: + +| Action | Token Cost/Reward | Reason | +|--------|-------------------|--------| +| Run daily automation | 1 token | Resource usage | +| Successful cycle retro | +5 tokens | Data contribution | +| Find quarantine candidate | +10 tokens | Quality improvement | +| Deep triage refinement | +20 tokens | High-value work | +| Automation failure | -2 tokens | Penalty | + +See `src/lightning/` for token economy implementation. + +--- + +## See Also + +- `CLAUDE.md` — Architecture patterns and conventions +- `AGENTS.md` — Agent roster and development standards +- `.kimi/README.md` — Kimi agent workspace guide +- `.loop/` — Runtime data directory (created on first run) + +--- + +_Maintained by: Timmy Automations Subsystem_ +_Updated: 2026-03-21_ diff --git a/timmy_automations/__init__.py b/timmy_automations/__init__.py new file mode 100644 index 00000000..26d43aa4 --- /dev/null +++ b/timmy_automations/__init__.py @@ -0,0 +1,271 @@ +"""Timmy Automations — Central automation discovery and control module. + +This module provides: +- Discovery of all configured automations +- Enable/disable control +- Status reporting +- Configuration management + +Usage: + from timmy_automations import AutomationRegistry + + registry = AutomationRegistry() + for auto in registry.list_automations(): + print(f"{auto.id}: {auto.name} ({'enabled' if auto.enabled else 'disabled'})") +""" + +from __future__ import annotations + +import json +import os +from dataclasses import dataclass +from pathlib import Path +from typing import Any + + +@dataclass +class Automation: + """Represents a single automation configuration.""" + + id: str + name: str + description: str + script: str + category: str + enabled: bool + trigger: str + executable: str + config: dict[str, Any] + outputs: list[str] + depends_on: list[str] + schedule: str | None = None + + @property + def full_script_path(self) -> Path: + """Resolve the script path relative to repo root.""" + repo_root = Path(__file__).parent.parent + return repo_root / self.script + + @property + def is_executable(self) -> bool: + """Check if the script file exists and is executable.""" + path = self.full_script_path + return path.exists() and os.access(path, os.X_OK) + + @property + def is_runnable(self) -> bool: + """Check if automation can be run (enabled + executable).""" + return self.enabled and self.is_executable + + +class AutomationRegistry: + """Registry for discovering and managing Timmy automations.""" + + MANIFEST_PATH = Path(__file__).parent / "config" / "automations.json" + STATE_PATH = Path(__file__).parent.parent / ".loop" / "automation_state.json" + + def __init__(self, manifest_path: Path | None = None) -> None: + """Initialize the registry, loading the manifest. + + Args: + manifest_path: Optional override for manifest file location. + """ + self._manifest_path = manifest_path or self.MANIFEST_PATH + self._automations: dict[str, Automation] = {} + self._load_manifest() + + def _load_manifest(self) -> None: + """Load automations from the manifest file.""" + if not self._manifest_path.exists(): + self._automations = {} + return + + try: + data = json.loads(self._manifest_path.read_text()) + for auto_data in data.get("automations", []): + auto = Automation( + id=auto_data["id"], + name=auto_data["name"], + description=auto_data["description"], + script=auto_data["script"], + category=auto_data["category"], + enabled=auto_data.get("enabled", True), + trigger=auto_data["trigger"], + executable=auto_data.get("executable", "python3"), + config=auto_data.get("config", {}), + outputs=auto_data.get("outputs", []), + depends_on=auto_data.get("depends_on", []), + schedule=auto_data.get("schedule"), + ) + self._automations[auto.id] = auto + except (json.JSONDecodeError, KeyError) as e: + raise AutomationError(f"Failed to load manifest: {e}") + + def _save_manifest(self) -> None: + """Save current automation states back to manifest.""" + data = { + "version": "1.0.0", + "description": "Master manifest of all Timmy automations", + "last_updated": "2026-03-21", + "automations": [] + } + + for auto in self._automations.values(): + auto_dict = { + "id": auto.id, + "name": auto.name, + "description": auto.description, + "script": auto.script, + "category": auto.category, + "enabled": auto.enabled, + "trigger": auto.trigger, + "executable": auto.executable, + "config": auto.config, + "outputs": auto.outputs, + "depends_on": auto.depends_on, + } + if auto.schedule: + auto_dict["schedule"] = auto.schedule + data["automations"].append(auto_dict) + + self._manifest_path.write_text(json.dumps(data, indent=2) + "\n") + + def list_automations( + self, + category: str | None = None, + enabled_only: bool = False, + trigger: str | None = None, + ) -> list[Automation]: + """List automations with optional filtering. + + Args: + category: Filter by category (daily_run, triage, metrics, workspace) + enabled_only: Only return enabled automations + trigger: Filter by trigger type (pre_cycle, post_cycle, scheduled, manual) + + Returns: + List of matching Automation objects. + """ + results = [] + for auto in self._automations.values(): + if category and auto.category != category: + continue + if enabled_only and not auto.enabled: + continue + if trigger and auto.trigger != trigger: + continue + results.append(auto) + return sorted(results, key=lambda a: (a.category, a.name)) + + def get_automation(self, automation_id: str) -> Automation | None: + """Get a specific automation by ID.""" + return self._automations.get(automation_id) + + def enable(self, automation_id: str) -> bool: + """Enable an automation. + + Returns: + True if automation was found and enabled, False otherwise. + """ + if automation_id not in self._automations: + return False + self._automations[automation_id].enabled = True + self._save_manifest() + return True + + def disable(self, automation_id: str) -> bool: + """Disable an automation. + + Returns: + True if automation was found and disabled, False otherwise. + """ + if automation_id not in self._automations: + return False + self._automations[automation_id].enabled = False + self._save_manifest() + return True + + def get_by_trigger(self, trigger: str) -> list[Automation]: + """Get all automations for a specific trigger.""" + return [a for a in self._automations.values() if a.trigger == trigger] + + def get_by_schedule(self, schedule: str) -> list[Automation]: + """Get all automations for a specific schedule.""" + return [ + a for a in self._automations.values() + if a.schedule == schedule + ] + + def validate_all(self) -> list[tuple[str, str]]: + """Validate all automations and return any issues. + + Returns: + List of (automation_id, error_message) tuples. + """ + issues = [] + for auto in self._automations.values(): + if not auto.full_script_path.exists(): + issues.append((auto.id, f"Script not found: {auto.script}")) + elif auto.enabled and not auto.is_executable: + # Check if file is readable even if not executable + if not os.access(auto.full_script_path, os.R_OK): + issues.append((auto.id, f"Script not readable: {auto.script}")) + return issues + + def get_status(self) -> dict[str, Any]: + """Get overall registry status.""" + total = len(self._automations) + enabled = sum(1 for a in self._automations.values() if a.enabled) + runnable = sum(1 for a in self._automations.values() if a.is_runnable) + issues = self.validate_all() + + return { + "total_automations": total, + "enabled": enabled, + "disabled": total - enabled, + "runnable": runnable, + "validation_issues": len(issues), + "issues": [{"id": i[0], "error": i[1]} for i in issues], + "categories": sorted(set(a.category for a in self._automations.values())), + } + + def save_state(self) -> None: + """Save current automation state to .loop directory.""" + state = { + "automations": { + id: { + "enabled": auto.enabled, + "runnable": auto.is_runnable, + "script_exists": auto.full_script_path.exists(), + } + for id, auto in self._automations.items() + } + } + self.STATE_PATH.parent.mkdir(parents=True, exist_ok=True) + self.STATE_PATH.write_text(json.dumps(state, indent=2) + "\n") + + +class AutomationError(Exception): + """Raised when automation operations fail.""" + pass + + +# Convenience functions for CLI usage +def list_automations(category: str | None = None, enabled_only: bool = False) -> list[Automation]: + """List automations (convenience function).""" + return AutomationRegistry().list_automations(category, enabled_only) + + +def enable_automation(automation_id: str) -> bool: + """Enable an automation (convenience function).""" + return AutomationRegistry().enable(automation_id) + + +def disable_automation(automation_id: str) -> bool: + """Disable an automation (convenience function).""" + return AutomationRegistry().disable(automation_id) + + +def get_status() -> dict[str, Any]: + """Get registry status (convenience function).""" + return AutomationRegistry().get_status() diff --git a/timmy_automations/config/automations.json b/timmy_automations/config/automations.json new file mode 100644 index 00000000..c1f68576 --- /dev/null +++ b/timmy_automations/config/automations.json @@ -0,0 +1,197 @@ +{ + "version": "1.0.0", + "description": "Master manifest of all Timmy automations", + "last_updated": "2026-03-21", + "automations": [ + { + "id": "cycle_retro", + "name": "Cycle Retrospective", + "description": "Logs structured retrospective data after each development cycle", + "script": "scripts/cycle_retro.py", + "category": "daily_run", + "enabled": true, + "trigger": "post_cycle", + "executable": "python3", + "config": { + "retro_file": ".loop/retro/cycles.jsonl", + "summary_file": ".loop/retro/summary.json", + "summary_window": 50, + "epoch_enabled": true + }, + "outputs": [ + ".loop/retro/cycles.jsonl", + ".loop/retro/summary.json" + ] + }, + { + "id": "loop_guard", + "name": "Loop Guard", + "description": "Idle detection with exponential backoff to prevent burning cycles on empty queues", + "script": "scripts/loop_guard.py", + "category": "daily_run", + "enabled": true, + "trigger": "pre_cycle", + "executable": "python3", + "config": { + "queue_file": ".loop/queue.json", + "idle_state_file": ".loop/idle_state.json", + "backoff_base_seconds": 60, + "backoff_max_seconds": 600, + "backoff_multiplier": 2, + "cycle_duration_seconds": 300 + }, + "outputs": [ + ".loop/idle_state.json" + ] + }, + { + "id": "triage_score", + "name": "Mechanical Triage Scoring", + "description": "Pure heuristic scoring of open issues based on scope, acceptance criteria, and alignment", + "script": "scripts/triage_score.py", + "category": "daily_run", + "enabled": true, + "trigger": "scheduled", + "schedule": "every_10_cycles", + "executable": "python3", + "config": { + "ready_threshold": 5, + "quarantine_lookback": 20, + "queue_file": ".loop/queue.json", + "retro_file": ".loop/retro/triage.jsonl", + "quarantine_file": ".loop/quarantine.json" + }, + "outputs": [ + ".loop/queue.json", + ".loop/retro/triage.jsonl", + ".loop/quarantine.json" + ] + }, + { + "id": "deep_triage", + "name": "Deep Triage", + "description": "LLM-driven intelligent issue refinement, breaking down large issues, adding acceptance criteria", + "script": "scripts/deep_triage.sh", + "category": "triage", + "enabled": true, + "trigger": "scheduled", + "schedule": "every_20_cycles", + "executable": "bash", + "depends_on": ["loop_introspect"], + "config": { + "queue_file": ".loop/queue.json", + "retro_file": ".loop/retro/deep-triage.jsonl", + "prompt_file": "scripts/deep_triage_prompt.md", + "timmy_consultation": true, + "timmy_timeout_seconds": 60 + }, + "outputs": [ + ".loop/queue.json", + ".loop/retro/deep-triage.jsonl" + ] + }, + { + "id": "loop_introspect", + "name": "Loop Introspection", + "description": "Self-improvement engine that analyzes retro data and produces structured recommendations", + "script": "scripts/loop_introspect.py", + "category": "triage", + "enabled": true, + "trigger": "scheduled", + "schedule": "every_20_cycles", + "executable": "python3", + "config": { + "cycles_file": ".loop/retro/cycles.jsonl", + "deep_triage_file": ".loop/retro/deep-triage.jsonl", + "triage_file": ".loop/retro/triage.jsonl", + "quarantine_file": ".loop/quarantine.json", + "insights_file": ".loop/retro/insights.json", + "trend_window_days": 7 + }, + "outputs": [ + ".loop/retro/insights.json" + ] + }, + { + "id": "agent_workspace", + "name": "Agent Workspace Manager", + "description": "Creates and maintains isolated git clones, port ranges, and data directories per agent", + "script": "scripts/agent_workspace.sh", + "category": "workspace", + "enabled": true, + "trigger": "manual", + "executable": "bash", + "config": { + "agents_dir": "/tmp/timmy-agents", + "canonical_repo": "~/Timmy-Time-dashboard", + "gitea_remote": "http://localhost:3000/rockachopa/Timmy-time-dashboard.git", + "agents": ["hermes", "kimi-0", "kimi-1", "kimi-2", "kimi-3", "smoke"], + "port_base_dashboard": 8100, + "port_base_serve": 8200 + }, + "outputs": [] + }, + { + "id": "kimi_bootstrap", + "name": "Kimi Workspace Bootstrap", + "description": "One-time setup script for new Kimi agent workspaces", + "script": ".kimi/scripts/bootstrap.sh", + "category": "workspace", + "enabled": true, + "trigger": "manual", + "executable": "bash", + "config": {}, + "outputs": [] + }, + { + "id": "kimi_resume", + "name": "Kimi Resume", + "description": "Quick status check and resume prompt for Kimi workspaces", + "script": ".kimi/scripts/resume.sh", + "category": "workspace", + "enabled": true, + "trigger": "manual", + "executable": "bash", + "config": {}, + "outputs": [] + }, + { + "id": "backfill_retro", + "name": "Backfill Retrospective", + "description": "One-time script to seed retrospective data from Gitea PR history", + "script": "scripts/backfill_retro.py", + "category": "metrics", + "enabled": true, + "trigger": "manual", + "executable": "python3", + "config": { + "retro_file": ".loop/retro/cycles.jsonl", + "summary_file": ".loop/retro/summary.json", + "gitea_api": "http://localhost:3000/api/v1" + }, + "outputs": [ + ".loop/retro/cycles.jsonl", + ".loop/retro/summary.json" + ] + }, + { + "id": "pre_commit_checks", + "name": "Pre-commit Checks", + "description": "CI hygiene validation before commits — import checks, model config, syntax, formatting", + "script": "scripts/pre_commit_checks.py", + "category": "metrics", + "enabled": true, + "trigger": "pre_commit", + "executable": "python3", + "config": { + "check_imports": true, + "check_model_config": true, + "check_test_syntax": true, + "check_platform_paths": true, + "check_docker_tests": true, + "check_black_formatting": true + }, + "outputs": [] + } + ] +} diff --git a/timmy_automations/config/daily_run.json b/timmy_automations/config/daily_run.json new file mode 100644 index 00000000..186176af --- /dev/null +++ b/timmy_automations/config/daily_run.json @@ -0,0 +1,36 @@ +{ + "version": "1.0.0", + "description": "Daily run schedule configuration", + "schedules": { + "every_cycle": { + "description": "Run before/after every dev cycle", + "automations": ["loop_guard", "cycle_retro"] + }, + "every_10_cycles": { + "description": "Run approximately every 10 cycles", + "automations": ["triage_score"] + }, + "every_20_cycles": { + "description": "Run approximately every 20 cycles", + "automations": ["loop_introspect", "deep_triage"] + }, + "manual": { + "description": "Run on-demand only", + "automations": ["agent_workspace", "kimi_bootstrap", "kimi_resume", "backfill_retro"] + } + }, + "triggers": { + "pre_cycle": { + "description": "Run before each dev cycle begins", + "automations": ["loop_guard"] + }, + "post_cycle": { + "description": "Run after each dev cycle completes", + "automations": ["cycle_retro"] + }, + "pre_commit": { + "description": "Run before git commit", + "automations": ["pre_commit_checks"] + } + } +} diff --git a/timmy_automations/config/triage_rules.yaml b/timmy_automations/config/triage_rules.yaml new file mode 100644 index 00000000..1ee99ba5 --- /dev/null +++ b/timmy_automations/config/triage_rules.yaml @@ -0,0 +1,99 @@ +# Triage scoring weights and thresholds +# Used by: triage_score.py, deep_triage.sh + +version: "1.0.0" + +# Scoring thresholds +thresholds: + ready: 5 # Minimum score to be considered "ready" for work + excellent: 8 # Score indicating well-scoped, actionable issue + +# Scope scoring (0-3) +scope: + file_patterns: + - pattern: '(?:src/|tests/|scripts/|\.py|\.html|\.js|\.yaml|\.toml|\.sh)' + weight: 1 + description: "Mentions specific files" + function_patterns: + - pattern: '(?:def |class |function |method |`\w+\(\)`)' + weight: 1 + description: "Mentions specific functions/classes" + title_length: + max_chars: 80 + weight: 1 + description: "Short, focused title" + meta_penalty: -2 # Penalty for philosophy/meta issues + +# Acceptance criteria scoring (0-3) +acceptance: + language_patterns: + - pattern: '(?:should|must|expect|verify|assert|test.?case|acceptance|criteria|pass(?:es|ing)|fail(?:s|ing)|return(?:s)?|raise(?:s)?)' + weight: 2 + min_matches: 3 + description: "Has acceptance-related language" + test_patterns: + - pattern: '(?:tox|pytest|test_\w+|\.test\.|assert\s)' + weight: 1 + description: "Mentions specific tests" + structure_patterns: + - pattern: '##\s*(problem|solution|expected|actual|steps)' + weight: 1 + description: "Has structured sections" + +# Alignment scoring (0-3) +alignment: + bug_tags: + - bug + - broken + - crash + - error + - fix + - regression + - hotfix + bug_score: 3 # Bugs on main = highest priority + + refactor_tags: + - refactor + - cleanup + - tech-debt + - optimization + - perf + refactor_score: 2 + + feature_tags: + - feature + - feat + - enhancement + - capability + - timmy-capability + feature_score: 2 + + loop_generated_bonus: 1 # Boost for loop-generated issues + + meta_tags: + - philosophy + - soul-gap + - discussion + - question + - rfc + meta_score: 0 # Philosophy issues are valid but lowest priority + +# Quarantine rules +quarantine: + failure_threshold: 2 # Failures before quarantine + lookback_cycles: 20 # How many cycles to look back + +# Issue type classification +types: + bug: + tags: [bug, broken, crash, error, fix, regression, hotfix] + priority_bonus: 0 # Handled by alignment scoring + feature: + tags: [feature, feat, enhancement, capability, timmy-capability] + refactor: + tags: [refactor, cleanup, tech-debt, optimization, perf] + philosophy: + tags: [philosophy, soul-gap, discussion, question, rfc] + dev_actionable: false + unknown: + default: true diff --git a/timmy_automations/daily_run/README.md b/timmy_automations/daily_run/README.md new file mode 100644 index 00000000..9b6febba --- /dev/null +++ b/timmy_automations/daily_run/README.md @@ -0,0 +1,30 @@ +# Daily Run Automations + +Scripts that run periodically to keep the development loop operational. + +## Scripts + +| Script | Source | Purpose | Trigger | +|--------|--------|---------|---------| +| `cycle_retro.py` | `../../scripts/cycle_retro.py` | Log structured retrospective data | Post-cycle | +| `loop_guard.py` | `../../scripts/loop_guard.py` | Idle detection with exponential backoff | Pre-cycle | +| `triage_score.py` | `../../scripts/triage_score.py` | Mechanical issue scoring | Every 10 cycles | + +## Running + +These scripts are invoked by the dev loop orchestrator (Hermes). Manual execution: + +```bash +# After a successful cycle +python3 scripts/cycle_retro.py --cycle 42 --success --issue 123 --type bug + +# Check if queue has work (exits 0 if ready, 1 if idle) +python3 scripts/loop_guard.py + +# Score open issues +python3 scripts/triage_score.py +``` + +## Configuration + +See `../config/automations.json` for automation manifests and `../config/daily_run.json` for scheduling. diff --git a/timmy_automations/metrics/README.md b/timmy_automations/metrics/README.md new file mode 100644 index 00000000..c3448a29 --- /dev/null +++ b/timmy_automations/metrics/README.md @@ -0,0 +1,51 @@ +# Metrics & Integration Automations + +Data collection, validation, and dashboard integration. + +## Scripts + +| Script | Source | Purpose | +|--------|--------|---------| +| `backfill_retro.py` | `../../scripts/backfill_retro.py` | Seed retrospective data from Gitea history | +| `pre_commit_checks.py` | `../../scripts/pre_commit_checks.py` | CI hygiene validation | + +## Backfill Retrospective + +One-time script to populate `.loop/retro/` from Gitea merged PRs: + +```bash +python3 scripts/backfill_retro.py +``` + +This seeds the cycle retrospective log so the LOOPSTAT panel isn't empty on new setups. + +## Pre-commit Checks + +Runs automatically before commits to catch common issues: + +- ImportError regressions +- Model name assertions +- Platform-specific path issues +- Syntax errors in test files +- Black formatting + +```bash +# Run manually +python3 scripts/pre_commit_checks.py + +# Or via pre-commit hook +bash scripts/pre-commit-hook.sh +``` + +## Dashboard Integration + +Metrics automations write to: + +| File | Purpose | +|------|---------| +| `.loop/retro/cycles.jsonl` | Cycle retrospective log | +| `.loop/retro/summary.json` | Rolling statistics | +| `.loop/retro/insights.json` | Introspection recommendations | +| `.loop/automation_state.json` | Current automation states | + +These feed the Mission Control dashboard at `/mission-control`. diff --git a/timmy_automations/triage/README.md b/timmy_automations/triage/README.md new file mode 100644 index 00000000..75a23909 --- /dev/null +++ b/timmy_automations/triage/README.md @@ -0,0 +1,34 @@ +# Deep Triage Automations + +Intelligent, LLM-assisted workflows for issue refinement and prioritization. + +## Scripts + +| Script | Source | Purpose | Frequency | +|--------|--------|---------|-----------| +| `deep_triage.sh` | `../../scripts/deep_triage.sh` | LLM-driven issue refinement | Every 20 cycles | +| `deep_triage_prompt.md` | `../../scripts/deep_triage_prompt.md` | Prompt template for deep triage | — | +| `loop_introspect.py` | `../../scripts/loop_introspect.py` | Self-improvement analysis | Every 20 cycles | + +## Deep Triage Protocol + +1. **Mechanical scoring** runs first (`triage_score.py`) +2. **Introspection** analyzes trends (`loop_introspect.py`) +3. **Deep triage** consults Hermes + Timmy for refinement +4. **Queue updated** with refined, prioritized issues + +## Running + +```bash +# Full deep triage (includes introspection) +bash scripts/deep_triage.sh + +# Introspection only +python3 scripts/loop_introspect.py +``` + +## Output + +- `.loop/queue.json` — Updated work queue +- `.loop/retro/deep-triage.jsonl` — Deep triage history +- `.loop/retro/insights.json` — Introspection recommendations diff --git a/timmy_automations/workspace/README.md b/timmy_automations/workspace/README.md new file mode 100644 index 00000000..e5686120 --- /dev/null +++ b/timmy_automations/workspace/README.md @@ -0,0 +1,60 @@ +# Workspace Automations + +Agent workspace management for multi-agent operation. + +## Scripts + +| Script | Source | Purpose | +|--------|--------|---------| +| `agent_workspace.sh` | `../../scripts/agent_workspace.sh` | Manage isolated agent environments | +| `bootstrap.sh` | `../../.kimi/scripts/bootstrap.sh` | One-time Kimi workspace setup | +| `dev.sh` | `../../.kimi/scripts/dev.sh` | Development helper commands | +| `resume.sh` | `../../.kimi/scripts/resume.sh` | Quick status check | + +## Agent Workspace Layout + +``` +/tmp/timmy-agents/ +├── hermes/ # Loop orchestrator +├── kimi-0/ # Kimi pane 0 +├── kimi-1/ # Kimi pane 1 +├── kimi-2/ # Kimi pane 2 +├── kimi-3/ # Kimi pane 3 +└── smoke/ # Smoke testing environment +``` + +Each workspace gets: +- Isolated git clone (from Gitea, not local repo) +- Unique port range (8100+, 8200+) +- Separate data directory +- Own TIMMY_HOME + +## Usage + +```bash +# Initialize all workspaces +bash scripts/agent_workspace.sh init-all + +# Reset a specific workspace +bash scripts/agent_workspace.sh reset kimi-0 + +# Create branch in workspace +bash scripts/agent_workspace.sh branch kimi-0 feature/my-branch + +# Bootstrap Kimi workspace +bash .kimi/scripts/bootstrap.sh + +# Check status +bash .kimi/scripts/resume.sh +``` + +## Port Allocation + +| Agent | Dashboard | Serve | +|-------|-----------|-------| +| hermes | 8100 | 8200 | +| kimi-0 | 8101 | 8201 | +| kimi-1 | 8102 | 8202 | +| kimi-2 | 8103 | 8203 | +| kimi-3 | 8104 | 8204 | +| smoke | 8109 | 8209 |