Initial commit: a0_software_orchestrator v1.0

- Auto-Registration-Bug behoben (register_project/get_project_id/resolve_project Trennung)
- 25 Tests gruen (Pytest)
- block_compactor-Tool refactored (Option B: Soft-Check statt Hard-Block)
- 4 Restbaustellen gefixt
- DB-Schema: plugin_settings-Tabelle hinzugefuegt
- 3 Schattenprojekte aus DB geloescht
- Plan v3 + Refactor-Plan + Worklog dokumentiert
This commit is contained in:
Software Orchestrator
2026-06-16 22:13:06 +00:00
commit 5769c1cd22
236 changed files with 17825 additions and 0 deletions
+145
View File
@@ -0,0 +1,145 @@
"""Briefing helpers for specialist handover DB-backed.
Vorher: persistierte als .md-Datei in <project_root>/.a0proj/handover/.
Jetzt: append-only in der Tabelle `orch_briefings` (patterns.db).
Kompatible Rückgabe-Schemata, aber statt "path" jetzt "briefing_id".
"""
from __future__ import annotations
import os
from typing import Any, Dict, List, Optional
from usr.plugins.a0_software_orchestrator.helpers.db_state_store import (
get_project_id,
append_briefing,
get_briefing,
list_briefings,
)
from usr.plugins.a0_software_orchestrator.helpers.handover_protocol import is_known_specialist
def ensure_handover_dir(project_root: str = "", project_name: str = "") -> Dict[str, Any]:
"""No-op in DB-mode. Wird für Rückwärtskompatibilität gehalten und
gibt statt eines Pfads einfach einen Marker-Dict zurück.
"""
return {"kind": "db", "project_name": project_name or None}
def write_initial_briefing(
*,
project_name: str = "",
project_root: str = "",
specialist: str,
topic: str,
summary: str,
decisions: List[str],
artifacts: List[str],
recent_turns: List[str],
return_condition: str,
handover_reason: str,
) -> Dict[str, Any]:
"""Hängt eine neue Briefing-Section an `orch_briefings` an.
Returns dict mit:
- briefing_id (DB-PK)
- project_id
- project_name
- specialist
- topic
- created_at
- newly_created (immer True es gibt keine "vorhanden" Files mehr)
- existed_before (deprecated, immer False für neue Einträge)
- size_bytes (Größe des section_md-Texts in Bytes)
"""
if not is_known_specialist(specialist):
raise ValueError(f"Unknown specialist profile: {specialist!r}")
# Projekt-Name ableiten: project_name ist Pflicht (basename-Fallback
# entfernt würde ungültige/Phantom-Namen erzeugen).
if not project_name or not project_name.strip():
raise ValueError(
"project_name is required (basename-fallback removed; "
"pass explicit project_name or registered name)"
)
name = project_name.strip()
pid = get_project_id(name)
if pid is None:
raise LookupError(f"project '{name}' is not registered")
# Section-Markdown aufbauen (aus handover_protocol)
from .handover_protocol import render_briefing_section
section_md = render_briefing_section(
specialist=specialist,
topic=topic,
summary=summary,
decisions=decisions,
artifacts=artifacts,
recent_turns=recent_turns,
return_condition=return_condition,
handover_reason=handover_reason,
)
briefing_id = append_briefing(
project_id=pid,
specialist=specialist,
topic=topic,
section_md=section_md,
summary=summary,
decisions=decisions,
artifacts=artifacts,
recent_turns=recent_turns,
return_condition=return_condition,
handover_reason=handover_reason,
)
return {
"briefing_id": briefing_id,
"project_id": pid,
"project_name": name,
"specialist": specialist,
"topic": topic,
"newly_created": True,
"existed_before": False,
"size_bytes": len(section_md.encode("utf-8")),
}
def read_briefing(
briefing_id: int,
max_bytes: int = 32_000,
) -> str:
"""Liest den Markdown-Text eines Briefings aus der DB.
Returns "" wenn nicht gefunden. Truncated mit Marker wenn zu lang.
"""
full = get_briefing(int(briefing_id))
if not full:
return ""
content = full.get("section_md") or ""
if len(content.encode("utf-8")) <= max_bytes:
return content
truncated = content.encode("utf-8")[:max_bytes].decode("utf-8", errors="ignore")
return truncated + "\n\n_…[truncated, use a smaller max_bytes or split topic]…_\n"
def list_briefings_for(
project_name: str = "",
project_root: str = "",
specialist: Optional[str] = None,
limit: int = 50,
) -> List[Dict[str, Any]]:
"""Listet alle Briefings (latest first) eines Projekts.
Komfort-Wrapper um db_state_store.list_briefings mit
project_name/project_root-Auflösung.
"""
# Bug-Fix: basename-Fallback entfernt würde ungültige/Phantom-Namen
# erzeugen. project_name ist Pflicht; ohne Name → leere Liste.
if not project_name or not project_name.strip():
return []
name = project_name.strip()
pid = get_project_id(name)
if pid is None:
return []
return list_briefings(pid, specialist=specialist, limit=limit)