Das Token sagt, wer schickt - nicht, wer den Inhalt geschrieben hat. Felder
mit festem Typ (Kennung, Zahl, Ganzzahl, Datum, Datum+Uhrzeit, ja/nein,
Auswahl, Mailadresse) koennen keine Anweisung tragen und gehen als geprueft
an den Agenten, auch als {feld} im Auftrag. Freitext bleibt Fremdinhalt.
Streng (Standard): unpassende Lieferung -> 400 mit Grund, im Audit.
Nachsichtig (fuer Absender, die sich nicht an die Felder halten): nichts
wird abgewiesen, Passendes gilt als geprueft, der Rest bleibt Fremdinhalt;
der Trigger ist dann 'unsicher' und wird so angezeigt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rueckmeldung aus der Testinstanz: Nach dem Anlegen war nicht zu erkennen,
wofuer ein Trigger da ist, der Agent erfuhr nur 'ein Ereignis kam', und
bearbeiten ging nicht.
- Slug aus dem Namen in der Adresse: /hooks/<instanz>/trigger/<slug>;
ein Token an fremder Adresse oeffnet nichts. Die alte Adresse ohne
Slug bleibt gueltig.
- Auftrag vom Menschen steht vor den gelieferten Daten (die weiter als
Fremdinhalt gerahmt sind); ohne Auftrag wird der Agent darauf hingewiesen.
- Prioritaet der erzeugten Aufgaben.
- PUT /api/triggers/{id} (Token bleibt), POST /api/triggers/{id}/token.
- WebUI: Formular fuer Anlegen und Bearbeiten, Adresse und Auftrag je Zeile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An instance without n8n has no use for workflow proposals. New instance
setting automation.enabled ("this instance works with n8n"), off by default:
- off: automation_find/automation_propose refuse, no candidates are put to
agents, and the escalation tool does not mention proposals at all;
- the switch sits in the settings card for external MCP servers; the
approvals view says proposals are off instead of standing empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Core/Automation/WorkflowReview: free check of an n8n export - every node
with its effect and target, code/command/SSH nodes only with a stated
reason, embedded secrets refused (by name or by look), a short summary of
what the workflow does.
- AutomationProposals: measured saving from the replaced tasks' receipts
(28 days), at most 3 open per agent, the same purpose not twice, a
rejected proposal must be answered; the human gets a task and decides in
the approvals view. Accepting records it - importing stays a human's step.
- Candidates: recurring tasks whose runs repeat the same calls (>= 75 % of
>= 8 runs in 14 days) are put to their agent once a month.
- Escalation tool: automation_find, automation_propose.
- Approvals view: proposals with nodes, targets, warnings, saving, JSON
download, accept/reject.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Core/Mcp/McpClient: Streamable HTTP (MCP 2025-06-18) - initialize, tools/list
with paging, tools/call; answers as JSON or event stream; session kept and
opened again when the server forgets it; answers capped at 4 MB.
- McpServerTool: one server is one tool (mcp_<id>); its remote tools are the
operations, so rights, preflight, staging, lead approvals, votes, audit and
budget apply. On approval by default unless the human trusts an operation;
reads foreign content; the server's descriptions are cut short and marked.
- McpConnections: registers, refreshes every 15 minutes, follows changes.
- InstanceConfig.mcpServers with protected headers; /api/mcp-servers;
settings card "External MCP servers"; tool catalog includes the servers.
- stdio servers are not part of this (sandbox runner, separate step).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Something outside hands an agent a task - an n8n workflow on a mail, a
schedule, a form, or any system that sends an HTTP request.
- Core/Triggers/InboundTriggers: one token per trigger and agent, kept as a
hash and shown once; a rate per hour; deliveries up to 64 KB. Each
delivery becomes a task whose content is framed as foreign content with its
origin; every delivery, accepted or refused, is in the audit (source
"trigger").
- POST /hooks/trigger on the instance (MCP socket, own token, no browsers);
the manager forwards /hooks/{instance}/trigger. /api/triggers to issue,
list and revoke.
- Agent view: a triggers card with the address and an n8n/curl example.
- Nothing writes back: no right to create or change workflows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A unit holds credentials for its members, by tool and setting - needed once,
not once per agent, and other units never get them.
- UnitConfig.credentials, protected by ConfigSecrets like every secret.
- OrgChart.ToolSettingsFor: an agent's own settings, completed by the nearest
unit up that holds the value; own non-empty values win; no tool is granted.
Used for runs, tool jobs and preflight probes.
- OrgAdministration.SetUnitCredential, PUT /organisation/units/{id}/credentials;
the view lists held settings, never values.
- Templates never carry unit credentials; they are named instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Core/Organisation/OrgTemplate: format, export, parse, structural checks.
Export empties every credential (nested, name variants) and names it;
a template that carries one is refused.
- OrgAdministration: ExportTemplate, PreviewTemplate (problems, warnings,
every text in full), ImportTemplateAsync - only with confirmation, units
before agents, own workspace per unit, doctrines, model and budget factor
overridable, templateOrigin on every unit and agent.
- Endpoints GET /organisation/template, POST .../preview, POST .../import;
templates card in the organisation view.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A unit can put operations to a blind vote instead of its lead. Not opinions
are counted but independent checks enforced.
- UnitConfig.votes: operations, voters, approveAt, rejectAt, quorum, hours,
perDay - set only by a human (OrgAdministration.SetVotes).
- Core/Staging/Votes: routes a covered staged call before the lead; one task
per voter with the frozen call and nothing else; cast_vote needs reasoning
and evidence. Counted as soon as the result is settled; in between and
without the quorum a human decides. The proposer does not vote, voters that
read foreign content get no ballot, changes to agents never go to a vote.
- Escalation tool: list_ballots, cast_vote.
- Approvals view shows each vote with every ballot and the voters' costs;
organisation view sets the rule and lists the votes held.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A lead looks at the runs of the agents it leads - held against the task and
against what the engine stamped, never by asking the agent.
- RunInspector (Core/Inspection): the mechanical check and first profile of
the inspector foundation (#204). Flags failed runs, tool errors, refusals,
unfinished calls, tasks answered with bookkeeping alone, and operations the
agent names but never called in the run (the 2026-09-14 pattern).
- SpotChecks: picks runs per unit (rate, denser for new agents, new models and
after a failure, daily cap), deterministic by run id; one task per lead.
- Escalation tool: spot_checks, inspect_run, check_done, propose_measure.
Measures go to a human; nothing is carried out automatically. A security
suspicion is its own case, not a personnel matter.
- Organisation view: settings per unit, recent checks, measures to decide.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- TeamMember: Laeufe und Kosten heute, offene und blockierte Aufgaben je Agent
(eine Abfrage fuer alle, nicht eine je Agent)
- TeamUnit: dieselben Werte samt Untereinheiten, offene Vorgaenge aus der Einheit,
heutige Kostenmeldung (#699)
- team.js: Lagezeile auf jeder Karte und in der Seitenleiste, Instanz summiert;
Spruenge zu Kosten je Einheit und zu den Vorgaengen; i18n en/de
- Tests: TeamOverviewTests (+2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- SqliteDoctrineStore: je Einheit versioniert (Tabelle Doctrines: Text, Autor, Lauf, Grund),
hoechstens 2000 Zeichen, leer entfernt; im Speicher gehalten fuer jeden Lauf
- OrgBriefing: die Doctrine der eigenen Einheit, nur eine Ebene tief, mit dem Satz,
dass ein Widerspruch zur Aufgabe eskaliert wird; Leitungen erfahren doctrine_set
- Escalation: doctrine_read, doctrine_set (nur die Leitung, Grund Pflicht)
- IAgentTool.Vorgabe(operation, context): Einstufung mit dem Aufrufkontext; eine Leitung
der Fremdinhalt-Stufe legt doctrine_set zur Freigabe vor
- API GET/PUT /organisation/units/{id}/doctrine; WebUI-Karte mit Verlauf und Markierung
haeufiger Aenderungen; i18n en/de
- Tests: DoctrineTests (5)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- IUsageRepository.GetDailyByAgentAsync: Tagessummen je Agent, Abo-Tokens getrennt
- UnitCosts: je Einheit samt Untereinheiten heute/7/30 Tage, Monat, Vormonat,
Durchschnittstag (28 Tage, ab 7 Tagen Verlauf), Maximum = Summe der Agentengrenzen
- Meldungen: Abweichung vom eigenen Verlauf, Naehe zur Grenzsumme, Monatsschwellen
der Instanz; CostWatch alle 15 min, je Einheit/Tag einmal (Tabelle CostAlerts),
als Aufgabe an den Menschen; schaltet nichts ab
- API /usage/units, /usage/cost-alerts; Zeitreihe gruppiert nach Einheit
- WebUI: Karte "Kosten je Einheit" mit Meldungen in der Verbrauchsansicht; i18n en/de
- Einstellungen instance.json costWatch
- Tests: UnitCostsTests (8)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- UnitConfig.Approvals: Operationen, Hoechstzahl je Tag, Stunden je Freigabe; nur der
Mensch setzt sie (OrgAdministration.SetApprovals mit Audit, Teilmengenregel nach oben,
Senken oben senkt unten mit; Agentenaenderungen/Sonderbudget nie freigebbar)
- LeadApprovals: Vorlage geht an die naechste Leitung, deren Einheit die Operation
erlaubt; nie eigene Aufrufe, nie eine Leitung mit Fremdinhalt, Tagesdeckel, Frist.
Entscheidung ueber Escalation approve_call/reject_call, im Audit als lead:<id>
- Der Mensch sieht alles und kann zuerst entscheiden; danach wird die Leitungsaufgabe
geschlossen. Nach der Frist nur noch der Mensch.
- StagedCalls: Approver, ApproverUntil, ApproverTaskId (Nachruestung)
- WebUI: Hinweis "bei Leitung" in der Freigabestelle, Zaehler ohne diese, Quote je
Leitung; Karte in der Organisationsansicht; i18n en/de
- Tests: LeadApprovalsTests (13), OrgAdministrationTests (+2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- cases.top in instance.json: Human | Agent | HumanFirst, agentId, humanFirstHours
- TopRole: der entscheidende Agent steht in keiner Einheit, liest keine Fremdinhalte
und hat nur Taskboard, Escalation, Memory, FileRW, AgentComm; geprueft beim Setzen,
bei jeder Zustellung und vor jeder Entscheidung
- CaseDesk: was bisher an der Spitze beim Menschen landete, geht nach Einstellung an
den Agenten; Tagesdeckel und Frist gehen weiter an den Menschen. handle verwehrt,
forward geht an den Menschen. Mensch zuerst: Uebergabe nach Ablauf, eigene Frist.
Der Mensch kann einen Fall des Agenten selbst entscheiden.
- Beschluesse des Agenten: ByTopAgent, zum Lesen vorgelegt; jeder Beschluss laesst
sich mit Begruendung zuruecknehmen und bindet dann niemanden mehr
- Briefing fuer den Agenten an der Spitze, auch ohne Einheiten
- OrgAdministration.SetTop mit Audit; Werkzeugsperre und Verschiebesperre
- WebUI Vorgaenge: Reiter Spitze, Lesen/Zuruecknehmen, Zaehler; i18n en/de
- Tests: TopRoleTests (15), OrgAdministrationTests (+2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Aufgabentyp meeting mit Teilnehmerliste (Frontmatter, SQLite, Taskboard-Werkzeug,
WebUI). MeetingRunner: jeder Teilnehmer einmal in Reihenfolge, Beitraege
gerahmt als Daten des Verfassers, danach schliesst die Leitung mit Ergebnis und
Aufgaben. Protokoll schreibt die Instanz in den Bereich der Einheit der Leitung.
Teilnehmer nur aus dem Adressbuch der Leitung, hoechstens 12.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eigene inhaltsadressierte Ablage (Agents/.history/objects, SHA-256) statt Git,
Journal FileHistory in state.db. Jeder Schreibweg - FileRW write/append/copy/
delete/stock_add, FTP- und RocketChat-Downloads - legt die alte Fassung ab und
verbucht die Aenderung mit RunId. Externe Aenderungen findet ein Abgleich beim
Start und stuendlich. Aufbewahrung nach Tagen und Fassungen, verwaiste Objekte
werden entfernt. WebUI: Dateiverlauf mit Filter, Diff und Wiederherstellen nur
durch den Menschen mit Grund.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FileLedger merkt je Agent die gelesene Fassung einer Datei in gemeinsamen
Bereichen. write, copy und Downloads (FTP, RocketChat) ersetzen eine Datei nur,
wenn sie seit dem Lesen unveraendert ist; eine fremde Datei nicht ungelesen.
append bleibt ungeprueft, macht aeltere Lesestaende aber ungueltig.
Abgelehnte Schreibvorgaenge: eigener Audit-Status Conflict, Tabelle
FileConflicts, Hotspots (>= 3 in 7 Tagen) in der Teamuebersicht.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eine fremde Laufzeit oder der eigene Claude Code des Menschen spricht per MCP
mit der Instanz. Der Server bietet genau die Werkzeuge des Agenten hinter dem
Token an; jeder Aufruf laeuft durch die Engine wie ein Aufruf des eigenen
Modells (Rechtetor, Vorpruefung, Werkzeugrechte, Staging, Audit mit Quelle
mcp, Rahmung), und der Aufrufer gilt als Leser fremder Inhalte (#715).
Eigener Socket mcp.sock statt manager.sock: ueber den Instanz-Socket antwortet
die Verwaltung ohne Token, ueber mcp.sock gibt es nur /mcp und nur mit
MCP-Token. Lauf-Tokens nur im Speicher, enden mit dem Lauf; persoenliche Tokens
(hoechstens 90 Tage, widerrufbar, nur als SHA-256) in der Agentenansicht.
Transporte: HTTP auf mcp.sock, Weboberflaeche und Manager (/mcp/{instanz}),
stdio ueber ClawdDotNet --mcp-bridge (Token nur aus Umgebung oder 0600-Datei).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eine Aufgabe, deren eigene Schaetzung (gemessen, Geschwister oder Autor)
ueber der Haelfte der Tageskostengrenze liegt, bekommt zuerst einen
Planungslauf: teilen (Teile unter der Elternaufgabe) oder Sonderbudget
beantragen. Danach wartet sie auf ihre Teile bzw. die Freigabe und laeuft
einmal zum Zusammenfuehren.
Sonderbudget ueber Escalation.request_budget, ausnahmslos freigabepflichtig
(auch gegen Werkzeugkonfiguration und Dauerfreigabe). Laeufe im Baum ziehen
den Topf statt der Tagesgrenzen, tragen BudgetPot in Verbrauch und Beleg und
sind in der Kostenanalyse (nach Budget) und auf der Aufgabenkarte gesondert
sichtbar. Aufgebraucht zaehlen sie wieder gegen den Tag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eine einmalige Aufgabe mit fill_by (spaetestens bis) statt Termin laeuft,
wenn der Tag Budget uebrig laesst: nach regulaerer und nachgeholter Arbeit,
nur wenn nichts vom Agenten wartet, bevorzugt ab 18 Uhr, und nur wenn ihre
eigene Schaetzung in den Spielraum aus #716 passt. Kurz vor der Frist wird
einmal ein Mensch gefragt; nach der Frist laeuft sie als regulaere Arbeit.
Ehrliche Schaetzung (TaskCostEstimator): eigene Laeufe, dann Geschwister
unter derselben Elternaufgabe, dann die Angabe des Autors (als Schaetzung
gekennzeichnet), zuletzt das Mittel des Agenten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- /api/cases (offen/alle), /api/cases/{id}/decide, /api/cases/{id}/statement, /api/decisions
- Ansicht "Vorgaenge": offene Faelle mit Verlauf je Ebene (Lauf-Links), Frist und
"Mensch gerufen"; Reiter Beschluesse mit Reichweite; Zaehler in der Seitenleiste
- Konzept Abschnitt 5: Umsetzung
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Werkzeugkontext kennt Lauf und Aufgabe des Laufs (RunId, RunTaskId); die
Delegationskette traegt den Lauf des Aufrufers weiter (RunId, CallerRunId)
- Aufgaben tragen origin_run / origin_task (Datei, DB, Taskboard "Arose from"),
gestempelt, nie vom Agenten behauptet
- Kommentare nennen den Lauf, in dem sie geschrieben wurden
- Laufbeleg: CallerRunId; Laufansicht fuehrt zu "Delegiert von" und "Delegiert an"
- #677: task_update ersetzt den Rumpf nicht mehr, Abnahmekriterien aendert nur der
Ersteller; Kommentare der Fremdinhalt-Stufe auf fremden Aufgaben kommen gerahmt an
- WebUI: Herkunft auf der Aufgabenkarte mit Link zum Lauf
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- IAgentTool.ReadsForeignContent: Werkzeuge mit Aussenkontakt erklaeren sich selbst
(WebFetch, WebMonitor, DirectAPI, Mail, RocketChat, Telegram, TelegramClient,
SocialMediaManager, FTP, Database); AgentConfig.TreatAsForeignReader kann nur verschaerfen
- ReportingLine: Stufe aus den Werkzeugen, "wer steht darueber", Pruefung des Wegs nach oben
(nie Aenderungen an Aufgaben darueber, nur eine Ebene hoch, Fremdinhalt-Stufe gar kein
Freitext) und Rahmung als untrusted_content
- Engine: Nachricht und Spawn nach oben geprueft, Text der Fremdinhalt-Stufe kommt gerahmt an
- Taskboard: task_create/update/comment, reopen und die Dubletten-Notiz folgen dem Berichtsweg
- Dispatcher: Aufgaben von Agenten der Fremdinhalt-Stufe gehen gerahmt in den Lauf
- TaskReports: Erledigt-Signal und Systemmeldung (in Pruefung, blockiert, abgebrochen) an den
Auftraggeber, je Zustand einmal, gesammelt in einer offenen Aufgabe je Empfaenger
- OrgBriefing nennt den Berichtsweg; WebUI zeigt Stufe und Schalter am Agenten
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- TaskItem.ParentId, in der Datei als parent, in der DB als ParentId. Die
Elternaufgabe ist der Zweck ("wofuer"), blocked_by die Voraussetzung
("wann").
- GoalChain: Eltern bis zu acht Stufen, Zyklen brechen den Gang ab und
werden beim Setzen abgelehnt. Oben steht das Ziel der Einheit - der
obersten Aufgabe, die zu einer Einheit gehoert (ein Ziel, das ein Mensch
auf der obersten Ebene setzt, gehoert keiner), sonst der naechsten Einheit
darueber mit Ziel.
- Der Lauf einer Aufgabe bekommt den Absatz "Why this task exists" und die
Bitte, zu melden, wenn die Aufgabe ihrem Zweck nicht mehr dient.
- Taskboard-Tool: parent bei task_create/task_update, geprueft auf Existenz,
Sichtbarkeit (#684) und Zyklen; task_list zeigt "Serves".
- WebUI: Feld im Aufgabendialog, Anzeige auf der Karte; die Verwaltung
prueft wie das Tool.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- TaskBoard: das Brett einer Aufgabe wird nicht gespeichert, es folgt aus
der Organisation - Einheit des Zustaendigen, sonst des Anlegers. Offene
Aufgaben ziehen damit beim Umzug mit. Sichtbar: das eigene Brett, fuer eine
Leitung alle darunter, und was man selbst angelegt hat (nach oben
Gereichtes bleibt im Blick).
- TaskItem.CreatedBy, in der Datei als created_by und in der DB. Ein Import
aus einer Datei ohne das Feld behaelt, was die DB weiss.
- Taskboard-Tool: task_list, task_find und die Dublettenpruefung sehen nur
die eigenen Bretter - eine aehnliche Buchhaltungsaufgabe haelt keine
Marketingaufgabe auf und bekommt keinen Kommentar von dort. task_update und
task_comment antworten auf eine fremde Aufgabe wie auf eine unbekannte.
- TaskboardService.ListAsync mit Sichtfilter: erst filtern, dann kappen.
- WebUI: jede Aufgabe nennt ihr Brett, ein Filter waehlt eines aus.
- Konzept (Abschnitt 3) und Einrichtungshandbuch nachgetragen.
Ohne Einheiten aendert sich nichts - ein Brett fuer alle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- OrgAdministration: eine neue Einheit bekommt ihren Bereich (Mitglieder:
die Einheit samt Untereinheiten, schreiben); reservierte Namen und belegte
Bereichs-Ids werden als Einheiten-Id abgelehnt. Uebergreifende Bereiche
anlegen (mit Eigentuemer), Name/Zweck/geschuetzte Ordner aendern,
Mitgliederliste ersetzen, nur leere Bereiche loeschen, nie den eigenen
einer Einheit. Eine geloeschte Einheit verlaesst alle Mitgliederlisten;
ihr Bereich geht nur, wenn er leer ist, sonst bleibt er ohne Eigentuemer.
Nachruesten des eigenen Bereichs fuer Einheiten von vor #684. Alles im
AuditLog.
- API unter /api/organisation/workspaces; die Uebersicht liefert je Bereich
Mitglieder, Dateizahl und - von der Instanz berechnet - wer hineinkommt.
- Organisationsseite: Bereiche unter der Struktur, Bearbeitung mit
Mitglieder-Editor, "Wer hineinkommt", Bereichskarte an der Einheit,
Bereiche am Agenten. Sprachschluessel en/de.
- Die Nachschau nach einem Absturz bekommt die Organisation mit, sonst
fände sie einen Aufruf in einem benannten Bereich nicht.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- TeamOverview.RollUp: je Einheit die Zustaende aller Agenten darin und
darunter, offene Freigaben und der dringlichste Zustand (Urgency:
haengt > Problem > Freigabe > Budget > arbeitet > frei). Gezaehlt wird in
der Instanz, die Seite zeichnet nur.
- /api/team liefert Einheiten und je Agent Einheit und Leitung.
- team.js: Einheiten als Container, zugeklappt als Kasten mit
Zusammenfassung ("6 agents · ⚠ 1 · ✋ 1 · ▶ 2") in der Farbe des
dringlichsten Zustands. Kanten laufen zum sichtbaren Stellvertreter und
werden gebuendelt (×n); was in einer zugeklappten Einheit bleibt, wird
nicht gezeichnet. Bis 15 Agenten alles offen, darueber nur die oberste
Ebene; der Zustand je Instanz im localStorage.
- Seitenleiste fuer Einheiten: Pfad, Ziel, Mitglieder (Leitung zuerst),
Untereinheiten, Auf-/Zuklappen, Link zur Organisation.
- Layout: rectpacking trifft das Seitenverhaeltnis nur grob; es werden
bis zu vier Vorgaben probiert und die zur Flaeche passendste behalten.
- Sprachschluessel en/de.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- /api/organisation: Baum (Einheiten mit Pfad, Tiefe, eigener und
wirksamer Obergrenze, Vorgaben; Agenten mit Einheit, Leitung, effektiver
Leitung, Werkzeugen, Budget), Probleme der Struktur, bekannte Werkzeuge.
Anlegen, Aendern, Loeschen, Leitung, Umzug von Agent und Einheit,
Obergrenze, Sammelwerte mit Vorschau. Umzug und Obergrenze kennen
preview=true; die Entscheidung trifft allein OrgAdministration.
- Neue Agenten koennen ueber die API gleich in eine Einheit gelegt werden.
- WebUI #/organisation: links der Baum (oberste Ebene, Einheiten, Leitungen
mit Stern, Mitglieder), rechts die Bearbeitung. Eine Begruendung fuer das
Audit gilt fuer jede Aenderung. Wo etwas verloren gehen kann, zuerst die
Vorschau: laufende Agenten und Probleme halten an, wegfallende Werkzeuge
muessen in einem Dialog bestaetigt werden. Sammelwerte als Tabelle mit
Ausreissern markiert und abgewaehlt. Bei aktivierter Begrenzung ist
vorausgewaehlt, was von oben ohnehin erlaubt ist. Sprachpakete en/de.
Geprueft im Browser mit einem Pruefgestell (API durch Attrappe ersetzt):
Baum, Auswahl, Obergrenze mit Bestaetigungsdialog, Sammelwert-Vorschau
mit Ausreisser und das Schreiben nur der gewaehlten Agenten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wer arbeitet, wer hat in den letzten Sekunden wen erreicht, und wo klemmt es,
auf einen Blick. Stufe 0 kommt ohne Einheiten aus; mit #683 werden die Agenten
zu aufklappbaren Containern, und die Ansicht faellt mit #688 zusammen.
Kern:
- AgentActivity: fuehrt die laufenden Laeufe (Beginn, Quelle, Aufgabe,
Aufrufer) und die letzten Verbindungen (begrenzt auf 500), meldet Start,
Ende und Verbindung als Ereignis. Vorher war nur ein Lauf ueber ChatAsync
"laufend"; ein frischer Lauf (@new) war unsichtbar, und niemand hielt fest,
wer wen gerufen hatte.
- Die Engine meldet beide Einstiege an; "arbeitet" beginnt hinter dem
Agententor.
Anwendung:
- TeamOverview stellt den Lagebericht zusammen. Die Zustaende entscheidet der
Code aus erfassten Tatsachen: laufender Lauf, offene Freigabe, Status des
letzten Belegs, Laufzeit gegen den Median des Agenten (haengt ab dem
Dreifachen, fruehestens nach 5 Minuten, ohne Verlauf nach 15). Rangfolge:
haengt, wartet auf Freigabe, arbeitet; Problem und Budget nur, solange
nichts Neues laeuft.
Web:
- GET /api/team, Ereignis "activity" (started, finished, link); das Ereignis
"run" traegt jetzt die Agenten-Id.
- Ansicht #/team mit Cytoscape.js und ELK (bei Bedarf geladen): Layout aus
der Struktur statt von Hand, Farbe und Symbol je Zustand, Kanten verblassen
nach 30 s, Klick oeffnet eine Seitenleiste (laufende Laeufe, Freigaben,
letzter Lauf im Wortlaut) mit "Freigaben oeffnen" vorne, danach Chat und
Laeufe. Sprachpakete en/de.
- Ein Layout, das lief, solange die Flaeche noch 0 px breit war, wird neu
gerechnet, sobald sie eine Breite hat (im Pruefgestell gefunden).
Geprueft im Browser mit einem Pruefgestell (API und Ereignisstrom durch
Attrappen ersetzt): sieben Agenten in allen Zustaenden, Seitenleiste,
Live-Kante und Verblassen.
Tests: AgentActivityTests (8), TeamOverviewTests (14). 1.283 gruen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bisher teilte sich eine Budgetablehnung den Ausgang NichtGelaufen mit dem
Herunterfahren. Einmalige Aufgaben wurden den Rest des Tages alle 30 Minuten
erneut abgelehnt (jedes Mal ein Beleg) und liefen nach Mitternacht still zu
Lasten des neuen Tages. Wiederkehrende verfielen still.
- TaskLauf.KeinBudget als eigener Ausgang; der Dispatcher bildet
BudgetExceeded darauf ab.
- Vier Felder an der Aufgabe: BudgetMissCount, LastBudgetMissAt,
DeferredUntil, CatchUpPending (Spalten werden fuer bestehende Datenbanken
nachgeruestet).
- Einmalige Aufgaben werden mitgenommen: Der Termin bleibt erhalten, faellig
ist die Aufgabe wieder ab der naechsten lokalen Mitternacht, nicht vorher.
- Wiederkehrende Aufgaben folgen der Regel des Agenten
(budget.whenExhausted): expire vermerkt den Ausfall und laesst den Termin
verfallen; catchUpOnce fasst alle verpassten Termine zu einem Nachholauf am
naechsten Tag zusammen. Ein regulaer faelliger Termin deckt das Nachholen
mit ab, es bleibt bei einem Lauf.
- Der neue Tag hat Vorfahrt: Mitgenommenes und Nachholaeufe kommen nach allem
regulaer Faelligen, unabhaengig von der Prioritaet.
- Beim ersten Takt eines neuen Tages eine Zusammenfassung je Agent im Log.
- Sichtbar: Die Aufgabenkarte der WebUI zeigt Ausfaelle, Mitnahme und
Nachholen; die Regel ist im Desktop je Agent einstellbar. Nebenbei sind
"Next:"/"Last:" auf der Karte ins Sprachpaket gewandert.
Stufen 2 bis 4 (Reservierung, Spielraum statt pauschalem Anteil, Vorbeugen)
folgen.
Tests: BudgetMissTests (9 neu). 1.238 gruen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Prompt history: every run records its transcript (task, each model step with
tokens and cost, tool calls and results, engine follow-ups) - bounded in size,
scrubbed of recognisable secrets, system prompts stored once per text.
Retention in days per instance (default 14) and per agent (0 = never, -1 =
forever); an hourly janitor deletes what is past it, "never" also purges.
Usage analysis over run receipts: filter by period, agent, model, event, status,
cost and free text; sort by every column; totals over the whole filter; hourly
or daily series grouped by agent, model or event; markers for budget denials and
model changes; run detail with receipt, transcript, tool calls and approvals.
WebUI: Analysis > Token usage > Runs / Timeline, run detail, Prompt History with
retention settings. New code, comments and texts in English; texts come from
language packs (i18n/en.json, de.json) via modules/i18n.js. CLAUDE.md records
English as the main language.
Version 0.1.12.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#643: Entscheidungen wecken den Agenten nur noch bei Ablehnung oder gescheiterter
Ausfuehrung, gesammelt in einer Aufgabe je Agent (5 min Ruhefenster), frisch per
@new:agent statt im fortgefuehrten Chatverlauf. Eine einmalige Aufgabe, deren Lauf
wegen Budget oder Herunterfahren gar nicht stattfand, bleibt faellig (Wiedervorlage
nach 30 min) statt mit gesetztem Marker liegenzubleiben.
#641: Rechte stehen in der Werkzeugkonfiguration des Agenten (erlaubt/verboten).
Erlaubtes laeuft, Nicht-Erlaubtes wird einzeln vorgelegt statt verweigert, Verbotenes
gar nicht erst. "Gleichartiges kuenftig erlauben" erteilt eine widerrufbare
Dauerfreigabe je Agent/Werkzeug/Operation; nach drei gleichartigen Freigaben schlaegt
die Oberflaeche sie vor. Aenderungen an Agenten bleiben ausnahmslos freigabepflichtig.
Database: Operation ist das SQL-Schluesselwort; die eigene Zugriffspruefung greift nur
noch ohne Freigabestelle (AgentToolContext.FreigabeGeprueft). WebUI: Rechte-Tabelle je
Werkzeug, Dauerfreigaben mit Widerruf.
Version 0.1.11.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#608: Ein wegen erschoepften Budgets abgelehnter Lauf hinterlaesst jetzt einen Beleg
(Status BudgetExceeded) an Aufgabe und Verlauf; die Log-Warnung kommt einmal je Agent,
Grenze und Tag. Die Kostenprognose zaehlt Ablehnungen nicht als Lauf und warnt, wenn die
geplanten Aufgaben eines Agenten sein Tagesbudget uebersteigen (API + Verbrauchsansicht).
#607: Passt schon der erste Treffer nicht in den Abruf, wird er gekuerzt (Anfang und Ende
bleiben) statt ganz zu fehlen. remember weist auf gleichnamige Eintraege ohne Schluessel und
auf Eintraege nahe der Abrufgrenze hin.
Version 0.1.9.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Engine: leere Antworten nachfassen, Aufgaben enden mit Ergebniszeile (EmptyResponse/Incomplete),
gleiche Fehlschlaege erkennen (RepeatedFailure), Zeitzeile vor Chatnachrichten,
Werkzeugergebnisse gerahmt, Vorpruefung vor dem Staging, Belegkosten wie RunUsage
- Staging: gescheiterte Freigaben als Failed; AgentEditor-Aenderungen freigabepflichtig,
Selbstbearbeitung gesperrt (allowSelfEdit)
- Taskboard: Termine beim Anlegen pruefen, naechster/letzter Lauf, pausieren, Sofortlauf,
Kostenprognose, Hinweis auf wachsenden Kontext; Jobs-Reiter und /api/jobs entfernt
- Release: Instanzmanager samt Weboberflaeche im Paket, setup.json fragt manager.json ab,
Token nicht mehr ins systemd-Journal
Version 0.1.6.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- #578: Abgebrochene Laeufe (Schrittgrenze, Zeitueberschreitung, Abbruch,
Ausnahme) tragen ihre bezahlten Token - vorher 0 in RunUsage, fuer das
Budget unsichtbar.
- #580: WebFetch prueft die aufgeloeste Zieladresse beim Verbinden; ein
erlaubter Name, der auf 127.0.0.1 aufloest, wird gesperrt.
- #575: Dateien entstehen unter Unix mit 0600 (AtomicFile, Protokolle,
FileRW-Anhaengen, state.db), Protokollverzeichnisse mit 0700.
- #576: SIGTERM/SIGINT beenden auch mit Fenster geordnet.
- #593: OpenRouter-Statusdienst wird gestartet.
- #589: Auftragsnachricht nennt aktuelle Zeit, Zeitzone und Termin.
- #581: Unvollstaendiges Argument-JSON erreicht das Werkzeug nicht mehr;
das Modell bekommt eine verstaendliche Rueckmeldung statt Parsertext.
- #591: Weboberflaeche liest die Felder, die API und Ereignisstrom liefern
(Freigaben, Protokoll, Taskboard mit Termin, Werkzeug-Einstellungen,
Tokennutzung); Ereignisstrom in camelCase.
- #592: Chat zeigt Nachrichten nicht mehr doppelt.
- #536 (teilweise): A2A-Antworten tragen Absender und Empfaenger, Parser
wirft nicht mehr; Datums- und Volltextfilter in Agenten-Chats.
- deploy/publish.py: Packager-Pfad net10.0, Schalter --agent-file.
- Version 0.1.4.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Weboberflaeche (#531): X-Instanz an der einen fetch-Stelle, Instanzauswahl in der
Kopfzeile, Verwaltungsansicht mit Anlegen/Starten/Anhalten/Fenster, Herkunft der
Ereignisse, Sicherungs- und Einstellungsansicht. 402/409/502 werden eigen behandelt.
Sicherungen im Manager (#530): Auflisten, Anlegen, Wiederherstellen, Loeschen. Das
Zurueckspielen haelt die Instanz vorher an und startet sie danach wieder.
Geordnetes Ende (Konzept 9.8). Zwei Befunde waren der Anlass:
Der Manager konnte eine Instanz MIT FENSTER nie geordnet beenden.
Control.ShutdownRequested war nur in HeadlessRun verdrahtet, nicht in der
Avalonia-Anwendung - und ein Fenster ist die Vorgabe. Die Bitte wurde mit 202
beantwortet, niemand hoerte zu, und der Manager schoss den Prozess nach Ablauf
seiner Frist jedes Mal ab.
InstanceSupervisor.StopAsync hielt Socket-Schweigen fuer einen Todesnachweis und
meldete "beendet", ohne den Prozess anzufassen. Bei einer verstummten Instanz ist
der Socket von vornherein still - der Neustart haette einen zweiten Prozess auf
dieselbe state.db gesetzt. Genau der Schaden, gegen den #528 gebaut ist.
- ShutdownCoordinator: erst die Tuer schliessen, dann auf offene Arbeiten warten,
Frist zwei Minuten. Vorher brach TaskScanner.DisposeAsync mitten in ProcessAsync
ab, sodass die Aufgabe bis zum Lease-Ablauf auf "running" stehenblieb.
- GET /api/instanz/puls hinter dem Socket-Gate: Phase und offene Arbeiten. Weiter
gefragt statt gemeldet - ein Puls, den die Instanz von sich aus schickt, fehlt
genau dann, wenn sie haengt.
- Eskalationsleiter: drei Durchgaenge ohne Antwort, einmal um ein Ende bitten,
nach 30s beenden und den Vollzug abwarten, gemeldet als Absturz.
- EnsureDeadAsync vor jedem Start.
Weiteres aus dem Review:
- .gitignore verankert: "Instances/" passte auch auf src/ClawdDotNet.Core/Instances/.
InstanceEndpoint.cs und InstanceLayout.cs waren dadurch nie eingecheckt.
- Das WebUI-Token stand im Protokoll des Managers. Konsole ja, Datei nein.
- Ein Klick auf "Instanz anlegen" loeste requestSubmit() zweimal aus.
- [Obsolete] auf WebServerPort wieder entfernt: sechs Warnungen, die niemand
abstellen konnte, solange E-M5 nicht fertig ist.
- Die Wiederherstellung bricht nicht mehr ab, wenn der Browser weggeht.
- Entwicklungspfade zum Instanzmanager aus Segmenten statt mit Rueckstrichen, und
der Port kommt aus manager.json statt fest aus der Zeile.
Offen und gemeldet: --managed hebelt die Lizenzpruefung aus (#535).
806 Tests gruen, 0 Warnungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Vorher hiess "Instanz wechseln": Der laufende Prozess faehrt sich herunter
und startet sich mit einer anderen Instanz neu. Wer wechselte, beendete
zwangslaeufig die Instanz, aus der heraus er es tat - und genau dabei hat
sich die Anwendung am 2026-09-08 aufgehaengt (#529). Jetzt laufen alle, und
es kommt eine nach vorn.
Neu: POST /api/instanz/fenster, wieder ausschliesslich ueber den Socket.
Die Instanz holt ihr Fenster SELBST nach vorn; der Manager kennt kein
Fenstersystem und soll keines kennen. Kopflos laeuft die Bitte ins Leere,
und das ist die richtige Antwort. Das letzte Wort hat der Fenstermanager -
Windows laesst einen Hintergrundprozess den Vordergrund nicht einfach an
sich reissen; der kurze Ausflug ueber Topmost half im Versuch, unter Linux
entscheidet der Fenstermanager. Mehr als bitten kann eine Anwendung nicht.
ShutdownSignal ist zu InstanceControl geworden: beenden und Fenster sind
zwei Bitten desselben Absenders ueber denselben Weg. Beenden gilt einmal,
das Fenster beliebig oft - jeder Wechsel auf diese Instanz ist einer.
--managed schaltet den Wechsel im Instanzfenster ab. Der Manager gibt es
jedem Instanzprozess mit; das Fenster graut seinen Instanz-Schalter aus und
erklaert auf Nachfrage, wo gewechselt wird. Zwei Schloesser an derselben
Tuer: Der Knopf ist aus, und der Weg dahinter prueft es noch einmal - denn
hinter ihm wartet ein Selbst-Neustart.
instanceWindows in manager.json, vorbelegt auf true: Jede Instanz behaelt
ihr Fenster. Auf einer Maschine ohne Bildschirmsitzung gehoert false hin.
ABNAHME an echten Fenstern: Instanz ueber den Manager gestartet, PID 18128,
Fenstertitel "ClawdDotNet", Befehlszeile mit --managed. Fenster von aussen
minimiert (IsIconic True), dann POST /instanzen/Instance-Test/fenster:
IsIconic False, im Vordergrund - und PID unveraendert 18128. Nichts neu
gestartet. Das ist die Abnahmebedingung dieser Stufe.
Build ohne Warnungen, 780 Tests gruen, 8 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Managerprozess. ClawdDotNet.Instanzmanager ist eine Konsolenanwendung:
Lizenz pruefen, uebernehmen was laeuft, Weboberflaeche aufhaengen, warten.
Beim Herunterfahren beendet er keine Instanz.
Zwei neue Projekte. ClawdDotNet.Manager.Web traegt die Weboberflaeche -
ohne jeden Verweis auf ClawdDotNet.Web, denn dort haengt App und daran
sechzehn Werkzeugprojekte. ClawdDotNet.ManagerApp ist der Einstiegspunkt.
wwwroot ist aus der Instanz in den Manager gezogen.
DER SCHNITT DER HTTP-FLAECHE: Unter /api/verwaltung steht, was den Manager
angeht - auflisten, anlegen, starten, anhalten. Alles andere unter /api
geht unveraendert an eine Instanz. Er leitet weiter, er implementiert
nicht. Welche Instanz gemeint ist, sagt die Kopfzeile X-Instanz; laeuft
genau eine, ist sie gemeint, und laufen mehrere, wird nachgefragt statt
geraten - eine Anfrage an die falsche Instanz ist schlimmer als eine
abgelehnte.
Der zusammengefuehrte Ereignisstrom haelt je laufender Instanz eine
Zuleitung und schreibt an jedes Ereignis, von wem es kommt. Ergaenzt statt
verpackt: Ein Umschlag waere einfacher gewesen, haette aber jede Ansicht
der Weboberflaeche zum Umschreiben gezwungen.
ABNAHME gegen laufende Prozesse: 401 ohne Token, Oberflaeche ohne Token
200, Instanz ueber den Manager gestartet und angehalten, /api/info und
/api/status unverfaelscht durchgereicht, /events/logs mit
"instanz":"Instance-Test" an jeder Zeile. Manager abgeschossen: Instanz
laeuft weiter. Manager neu gestartet: "1 Instanzen bekannt, 1 laufen
bereits" - uebernommen. E-M6 damit in beide Richtungen belegt.
DREI DEFEKTE, die erst der Lauf gezeigt hat:
1. Der Manager hing an einer Konsolenabfrage nach dem Lizenzschluessel. Ein
Test auf Console.IsInputRedirected reicht dafuer NICHT: Er beantwortet
"ist die Eingabe umgeleitet", nicht "kann jemand antworten" - ein
Prozess ganz ohne Konsole, der gefaehrlichste Fall, ist nicht
umgeleitet. Der Manager fragt jetzt gar nicht.
2. Eine unlesbare manager.json wurde stillschweigend durch Vorgaben
ersetzt: falsches Instanzverzeichnis, anderer Port, Lizenzfrage - und
nichts sagte, woran es lag. LoadProblem meldet den Grund jetzt.
3. Der Instanzprozess erbte CLAWD_CONFIG_DIR vom Manager und legte sich in
dessen Konfigurationsverzeichnis eine eigene Settings.json an. Eine
Umgebungsvariable vererbt sich, ein Argument nicht - der Manager nimmt
sein Verzeichnis jetzt ueber --config.
SharedLoggerProvider ist nach Core gezogen: Es gibt jetzt zwei Webhosts,
die dasselbe brauchen, und sie duerfen einander nicht kennen.
NOCH OFFEN in dieser Stufe: Die Weboberflaeche kennt noch keine Instanzen -
Instanzauswahl, X-Instanz und die Verwaltungsansicht sind Arbeit an
wwwroot. Und webServerPort steht noch in der Instanz.
Build ohne Warnungen, 775 Tests gruen, 8 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Anlass des ganzen Vorhabens: Am 2026-09-08 hat sich eine Instanz
aufgehaengt und wurde von Windows beendet. Zu diesem Vorgang steht keine
einzige Zeile in den Instanzprotokollen, kein Bugtracker-Eintrag, kein
Watchdog-Alarm - weil Meldeweg und Watchdog in genau dem Prozess lebten,
ueber den sie haetten berichten sollen. Ab jetzt sieht jemand anders hin.
Neu ist ClawdDotNet.Manager mit genau EINEM Projektverweis: Core. Das ist
keine Sparsamkeit, sondern die halbe Architektur - App schleppt sechzehn
Werkzeugprojekte hinter sich her, und "keine Werkzeuge im Manager" soll
eine Eigenschaft des Bauplans sein und keine Absichtserklaerung.
DIE ENTSCHEIDUNG, AN DER ALLES HAENGT: Die Aufsicht fragt den Socket, nicht
die Prozessliste. Die Prozessliste sagt, dass etwas da ist. Sie sagt nicht,
ob es noch arbeitet - und genau das war der Zustand, den niemand bemerkt
hat. Ein Durchgang kennt drei Antworten: Socket antwortet, sie laeuft.
Socket schweigt und der Prozess ist weg, Absturz mit Abschiedscode. Socket
schweigt und der Prozess lebt, verstummt - aber erst nach mehreren
Fehlversuchen in Folge, denn ein Alarm bei jedem Aussetzer ist nach einer
Woche der Alarm, den niemand mehr liest.
Ein Start gilt erst, wenn die Instanz antwortet. Zwischen "der Prozess
laeuft" und "die Instanz laeuft" liegen Datenbank, Agenten und Kestrel; wer
nur auf Process.Start wartet, haelt eine Instanz fuer gestartet, die gleich
an einer kaputten Konfiguration scheitert.
Angehalten wird durch Bitten. Neu auf der Instanzseite:
POST /api/instanz/beenden, ausschliesslich ueber den Socket - ueber TCP
gibt es den Endpunkt nicht, auch mit gueltigem Token nicht, und er
antwortet dort 404 statt 403. Die Lebensdauer einer Instanz gehoert dem
Manager; eine Browsersitzung soll den Prozess, der sie bedient, nicht
beenden koennen. Ausgeloest wird der Wunsch erst nach dem Absenden der
Antwort (Response.OnCompleted), sonst bekaeme der Manager statt einer
Zusage einen Verbindungsabbruch und hielte ihn fuer einen Fehlschlag.
Wiederfinden nach einem Managerausfall (E-M6): Die Zustandsdatei
instance-state.json ist der Hinweis, der Socket ist der Beweis. Erst wird
gefragt, wer da antwortet; nur bei gleicher Instanzkennung wird die
Prozesskennung aus der Datei angefasst. Andersherum fuehrt eine
wiederverwendete Nummer nach einem Neustart der Maschine geradewegs dazu,
ein fremdes Programm zu beaufsichtigen und beim Anhalten zu beenden.
Selbsttaetiger Neustart ist bewusst NICHT gebaut. Die Abnahmebedingung
lautet "merkt es und meldet es", und zum Neustarten trifft das Konzept
keine Entscheidung; eine Instanz in einer Schleife wieder hochzufahren,
die jedes Mal abstuerzt, waere die falsche Vorgabe fuer etwas, das niemand
entschieden hat. Als offene Frage aufgeschrieben.
ABNAHME, mit echten Prozessen gemessen: Manager startet Instanz Test in
0,7 s, identifiziert sie ueber die Named Pipe, schreibt die Zustandsdatei
(pid 54476, id d68af6fd). Prozess von aussen abgeschossen. Naechster
Durchgang: Crashed, Code -1, meldenswert, Zustandsdatei weggeraeumt. Dazu
20 Tests gegen gesteuerte Prozesse - ein Absturz zum gewuenschten Zeitpunkt
und eine wiederverwendete Prozesskennung sind mit einem echten Programm
nicht herzustellen.
Noch offen in dieser Stufe: Instanzen anlegen, Lizenz im Manager,
Sub-Token je Instanz, und der Managerprozess selbst.
Build ohne Warnungen, 737 Tests gruen, 8 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eine Instanz ist jetzt ohne Port ansprechbar. InstanceEndpoint liefert die
Adresse: unter Linux <Instanz>/manager.sock, unter Windows eine Named Pipe,
deren Name aus einem SHA-256-Fingerabdruck des Instanzpfades entsteht. Zwei
Instanzen bekommen damit zwangslaeufig zwei Adressen, ohne dass jemand
etwas vergeben muss - genau der Unterschied zur Vorbelegung 7001, um die
sich zwei Instanzen stillschweigend streiten.
Der Socket ist immer da, auch ohne Weboberflaeche. Er gehoert nicht zu
#494, sondern ist die Steuerflaeche der Instanz: webUiEnabled schaltet den
TCP-Zugang, nicht den Socket. WebUiHost heisst deshalb jetzt
InstanceHttpHost - ein Kestrel, zwei Zugaenge.
Ueber TCP gilt Token-Pflicht, ueber den Socket nicht: Dort steht die Grenze
am Dateisystem (0600 unter Linux, CurrentUserOnly bei der Named Pipe,
explizit gesetzt statt auf die Vorgabe vertraut). Wer die Socketdatei
oeffnen darf, sieht ohnehin in das Verzeichnis, in dem alles liegt, was ein
Token schuetzen wuerde.
Woran die beiden Zugaenge auseinandergehalten werden, ist der Punkt, an dem
ein Irrtum teuer waere. Bei Socket-Verbindungen stehen in
HttpContext.Connection keine IP-Adressen - daraus zu schliessen "keine IP,
also Socket" waere eine Vermutung an der Stelle, an der ein Torwaechter
steht, und sie irrt in die falsche Richtung: Im Zweifel liesse sie eine
TCP-Anfrage ohne Token durch. Der Socket-Zuhoerer haengt deshalb jeder
Verbindung selbst eine Marke an (LocalTransport, Verbindungs-
Zwischenschicht ueber ListenOptions.Use); wo sie fehlt, gilt Token-Pflicht.
Ein belegter Port kostet nur noch die Weboberflaeche. Vorher gab TryStart
in dem Fall null zurueck und die ganze HTTP-Flaeche war weg. Seit dem
Socket ist das die falsche Rangfolge: Ihn zu verlieren, weil eine zweite
Instanz laeuft, hiesse die Instanz unerreichbar zu machen - in genau dem
Fall, fuer den es den Manager gibt.
Kopfloser Betrieb ueber --headless --instance <Pfad>. Die Verzweigung sitzt
in Program.Main vor BuildAvaloniaApp, damit auf einem Server ohne
Bildschirmsitzung kein Fenstersystem angefasst wird. Gefragt wird nichts:
Instanz aus dem Argument, Lizenz ueber das bereits vorhandene
NonInteractiveLicensePrompt, Telegram-Anmeldung aus.
Nachgemessen, nicht angenommen: Kopflose Instanz auf Instance-Test
gestartet, kein Fenster, und eine rohe HTTP/1.1-Anfrage aus einem fremden
Prozess (PowerShell, direkt auf die Pipe) bekam 200 OK auf /api/info, ohne
Token. Das ist die Abnahmebedingung fuer Stufe 2.
Korrektur zur eigenen Ankuendigung: webServerPort entfaellt nicht mit
Stufe 2, sondern mit Stufe 4. Solange kein Manager die Weboberflaeche
traegt, hiesse Wegnehmen, sie ersatzlos abzuschalten.
Zwei Luecken bleiben offen und gehoeren auf die Linux-Testmaschine: Die
Socketdatei-Faelle sind unter Windows gegenstandslos und werden dort mit
sichtbarer Begruendung uebersprungen - der Unix-Domain-Socket selbst ist
auf der Entwicklungsmaschine also ungeprueft, obwohl er die Zielplattform
ist. Und das geordnete Ende auf SIGTERM liess sich hier nicht ausloesen:
Windows kennt das Signal fuer einen fensterlosen Prozess nicht.
Build ohne Warnungen, 714 Tests gruen, 8 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mehrere Instanzen sollen unabhaengig voneinander laufen. Heute tun sie das
nicht: Sie teilen sich Protokollverzeichnis, Sicherungsordner und
Sicherungszeitplan. In einer gemeinsamen Startup.log standen abwechselnd
Zeilen zweier Instanzen, ohne Kennzeichnung, welche gerade spricht -- im
Mehrinstanzbetrieb ist damit jede Fehlersuche Ratearbeit. Genau in dieser
Lage ist der Aufhaenger aus #529 entstanden.
Fuenf Schluessel wandern aus AppSettings nach InstanceSettings.json:
logDirectory, backupDirectory, backupKeepCount, autoBackupEnabled,
autoBackupTime. Leer heisst jetzt <Instanz>/Logs beziehungsweise
<Instanz>/Backups; ein relativer Wert zaehlt weiterhin gegen das
Instanzverzeichnis, nicht gegen das Arbeitsverzeichnis des Prozesses
(#375 bleibt gewahrt).
autoBackupEnabled und autoBackupTime standen in der Planung nicht, sind
aber mitgegangen: Ein Schalter, der die taegliche Sicherung fuer eine
Instanz einschaltet und sie damit fuer alle einschaltet, ist derselbe
Fehler wie ein Ordner fuer alle -- und zwei von vier Sicherungsfeldern zu
verschieben hinterliesse eine Einstellungsseite, auf der die Haelfte
anwendungsweit gilt.
Keine Uebernahme der Altwerte, aber eine Ansage: SettingsManager.
AbandonedKeys meldet die aufgegebenen Schluessel, solange sie noch in
Settings.json stehen, und der Start protokolliert einmal, was nicht mehr
gilt und wohin diese Instanz stattdessen schreibt. Ein anwendungsweiter
Pfad, in alle Instanzen kopiert, waere wieder ein Ordner fuer alle.
webServerPort bleibt vorerst stehen. Es entfaellt mit Stufe 2, sobald
Kestrel auf dem Socket hoert; vorher wegzunehmen hiesse, die
Weboberflaeche ohne Ersatz abzuschalten.
Korrektur an der eigenen Befundlage: "Instanz A wirft die Sicherungen von
Instanz B weg" war zu breit. Beide Rotationen filtern bereits nach dem
Praefix backup_<Instanzname>_. Der reale Defekt ist enger, und ein Praefix
ist keine Grenze: backup_Test_2_*.zip faengt mit backup_Test_ an, also
zaehlt Test im gemeinsamen Ordner die Archive von Test_2 mit. Belegt in
BackupSchedulerTests, das den alten Zustand eigens wiederherstellt.
Nachgeprueft an der laufenden Installation, nicht nur im Test: Nach dem
Umbau schreibt Instance-Test nach Instances/Instance-Test/Logs, das alte
gemeinsame Verzeichnis bekommt keine Zeile mehr, und die Warnung zu den
aufgegebenen Schluesseln erscheint genau einmal.
Enthaelt ausserdem, was seit der Weboberflaeche liegengeblieben war:
- #526: Instanzgeheimnisse wurden beim Speichern verschluesselt und beim
Laden nie entschluesselt. LoadInstanceConfig hatte kein
ConfigSecrets.Unprotect; ConfigLoader.LoadAsync macht es richtig, wird
zur Laufzeit aber gar nicht aufgerufen. Der OpenRouter-Schluessel ging
so verschluesselt an die API.
- Ein offener Ereignisstrom hielt das Beenden fuenf Sekunden auf. Die
Stroeme hingen nur an RequestAborted, das beim geordneten Herunterfahren
nicht feuert; jetzt zusaetzlich an ApplicationStopping. Der Fang in
WebUiHost gilt dazu ohne Bedingung auf RequestAborted -- mit ihr waere
jeder offene Strom beim Beenden als Fehler im Bugtracker gelandet.
- Nach der Anmeldung blieb die Weboberflaeche leer: #anmeldung
{ display:flex } schlaegt als ID-Selektor das hidden-Attribut.
- docs/Instanzmanager-Konzept.md: Befund, die Entscheidungen E-M1 bis
E-M6, der Schnitt zwischen Manager und Instanz und die sechs Stufen.
Build ohne Warnungen, 709 Tests gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- ClawdDotNet.Web: Kestrel im Prozess der Anwendung, Token-Absicherung (TokenGate mit FixedTimeEquals), Endpunkte unter /api, Ereignisstrom unter /events (Server-Sent Events) und fortlaufendes Log-Streaming.
- Weboberflaeche (wwwroot/): Vollstaendiges Single-Page-Frontend mit nativem HTML5, CSS3 und ES-Modulen ohne Buildschritt (strikt textContent, #-Routing, sofortiger Login-Sprung bei fehlendem Token, Light/Dark-Theme, lokale Barlow-Schriften).
- ClawdDotNet.App: Fachlogik aus den Avalonia-ViewModels in Dienste herausgeloest (AgentAdministration, JobAdministration, ServiceAdministration, RunCoordinator) und ueber InstanceChange synchronisiert.
- Tests: ClawdDotNet.Web.Tests und ClawdDotNet.App.Tests hinzugefuegt, alle 684 Tests erfolgreich.