Der Push-Test mit den echten CLIs war NEGATIV: codex meldete "durch
agenthub_work erfahren", kimi ausdruecklich "kein MCP-Push". Der Kanal
funktioniert (mit eigenem Testclient verifiziert), die Hosts reichen ihn
aber offenbar nicht ans Modell durch.
Naheliegender Verdacht, statt zu spekulieren gemessen: `sendLoggingMessage`
nuetzt nichts, wenn der CLIENT `logging` nicht deklariert — dann verwirft
seine SDK-Seite die Notification, bevor das Modell sie je sieht. Mein
Testclient deklarierte sie ausdruecklich und empfing deshalb.
Die Bridge schreibt die deklarierten Client-Capabilities jetzt nach
~/.cache/agenthub/client-capabilities-<agent>.json.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bisher band die Bridge den Push-Kanal erst, wenn der Agent seinen Namen nannte
(hello/work). Genau davor — direkt nach dem Session-Start — war er also
weiterhin taub, und das ist der Zustand, den wir beheben wollen. Mit
`--agent <name>` bindet die Bridge ab der ersten Sekunde.
Codex- und Kimi-Konfiguration entsprechend gesetzt (args ["mcp","--agent",…]).
Briefing umgeschrieben: Es sagte "du bist waehrend der Arbeit taub" — das
stimmt nicht mehr. Jetzt: Push-Benachrichtigungen sind verbindlich, sofort
darauf reagieren; der pending-Block im task_log-Ergebnis bleibt als zweiter
Empfangsweg, Loggen bleibt Pflicht.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
DIE WURZEL, endlich an der Wurzel gefasst. Bisher konnte ein Agent nur
empfangen, WÄHREND er in `agenthub_work` blockierte. Kehrte der Aufruf leer
zurück und das Modell startete ihn nicht neu, war der Agent unerreichbar —
ein Mensch musste ihn anstoßen. Wir haben einen ganzen Tag um diese Tatsache
herum gebaut (Watchdog, Check-in-Kanal, längere Timeouts, notListening) statt
sie aufzulösen. Der CEO hat zu Recht darauf bestanden, das Protokoll selbst
zu nutzen.
MCP kann Server→Client jederzeit `notifications/message` schicken. Die Bridge
ist ohnehin ein langlebiger Prozess pro Agent: sie hängt sich jetzt dauerhaft
an den SSE-Stream des Hubs (`pushChannel.ts`) und reicht relevante Ereignisse
als Notification hoch — neue/zurückgegebene Task, Nachricht, beantwortete
Frage, Abbruch. Bindung erfolgt, sobald der Agent seinen Namen nennt
(hello/work), inklusive Alias-Auflösung; Reconnect mit Backoff.
⚠️ Die entscheidende Falle: `sendLoggingMessage` prüft `_capabilities.logging`
und verwirft die Notification sonst STILL. Der Server deklarierte gar keine
Capabilities — daran wäre dieser Weg unbemerkt gescheitert. Jetzt deklariert.
Live verifiziert mit einem echten MCP-Client: Nachricht am Hub angelegt →
Notification kam beim Client an, ohne dass dieser in einem Tool-Call wartete.
OFFEN und bewusst nicht behauptet: ob die CLI-Hosts (Codex/Kimi) die
Notification dem Modell zeigen — das ist client-abhängig und empirisch zu
prüfen. Deshalb bleiben Check-in-Kanal und Work-Loop als Netz bestehen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Work-Loop blockierte fuer ALLE Agenten 50s. Der Wert stammt von Kimi-Code,
dessen MCP-Client Requests nach gut einer Minute abbricht (-32001). Codex
vertraegt Minuten — musste aber mit demselben Minimum leben und kehrte dadurch
sechsmal so oft leer zurueck wie noetig. Jede Rueckkehr ist eine Gelegenheit,
den Turn zu beenden und aus dem Loop zu fallen; genau daran unterschied sich
codex' Zuverlaessigkeit von kimis.
Ein globaler Default zwingt alle auf die Grenze des schwaechsten Clients. Die
Grenze gehoert aber zum Agenten, nicht zum Hub — deshalb jetzt
`agents[].workTimeoutSec` im Roster, ausgeliefert ueber /agents/:agent/identity.
Aufloesung: explizites Argument > Roster > globaler Default.
Konfiguriert: codex 240s, kimi 50s.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Fall, der den Menschen zum Postman macht: eine Task ist an einen Agenten
adressiert, aber er sitzt nicht in agenthub_work. Er ist dann NICHT dormant
(er lebt womöglich und hat gerade erst mit dem Hub gesprochen), hört aber
nicht zu — die Arbeit bleibt liegen, ohne dass irgendwo ein Fehler erscheint.
Real erlebt: TSK-0288 lag offen an codex adressiert, codex war `active`
(75 s zuvor gesehen) mit inLoop=false. Der Architekt hat es nicht bemerkt und
dem CEO stattdessen erzählt, die Task werde "automatisch" geclaimt. Genau
diese Erwartungs-statt-Prüfung soll das Signal künftig unmöglich machen.
`/architect/pulse` liefert dazu `notListening` mit Agent, Anzahl wartender
Tasks und einem Klartext-Hinweis.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Architekten-Message-Notifier (`watch --await-message claude`) feuerte bei
JEDER Nachricht — auch bei den Watchdog-Erinnerungen, die alle paar Minuten
eintreffen ("review waiting 5m/10m/15m…"). Real erlebt: dreimal hintereinander
geweckt worden, ohne dass etwas Neues passiert war. Ein Signal, das im Takt
seiner eigenen Erinnerungen feuert, entwertet sich selbst.
`--ignore-from <agents>` blendet Absender aus; message-Events tragen dafür
jetzt `from` (bisher nur im title). Armung des Architekten künftig mit
`--ignore-from agenthub` — echte Agenten-Post weckt weiterhin sofort, die
Watchdog-Erinnerungen sieht man beim Pollen ohnehin.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`dormantAgents` filterte auf `!inLoop`. `inLoop` ist aber immer false, solange
ein Agent einen Task AUSFÜHRT — dadurch meldete der Pulse jeden hart
arbeitenden Agenten als dormant. Live gesehen: codex mit 17 s altem Check-in
stand als dormant im Pulse. Genau das Signal, auf das sich der Architekt
verlassen soll, hätte ihn dazu gebracht, funktionierende Agenten anzustoßen.
Jetzt zwei getrennte Fälle:
- `loopExitReason` gesetzt → der Agent hat den Loop ausdrücklich verlassen
und hört nicht mehr zu: sofort melden (unverändert).
- kein Exit-Grund, aber Task in Arbeit → er führt aus; erst nach
DORMANT_AFTER_MS (5 min) ohne Check-in ist das mein Fall.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1) hookContext druckte einen eigenen, aelteren Text. Fuer Codex/Kimi ist der
Hook aber oft der EINZIGE Einstieg (sie starten automatisch) — dadurch
kannten auto-gestartete Agenten weder den Check-in-Kanal noch DEC-0035.
Aufgefallen, als codex ohne `agenthub start` losgelaufen ist. Beide Wege
nutzen jetzt agentBriefing().
2) `server start` prueft jetzt VOR dem Binden, ob hier ueberhaupt ein Projekt
liegt. Vorher band der Server erst den Port und starb dann beim Laden der
Config — der alte Hub war da schon gekillt, die Agenten liefen ins Leere.
Klassiker: aus dem Quellrepo statt aus dem Projekt gestartet (an einem Tag
dreimal passiert). Jetzt sofortiger, verstaendlicher Fehler.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Check-in prüfte nur `assignedTo`, `findAddressedOpenTask` dagegen
Titel-Präfix `<name>:`, Handoff-Empfänger UND `assignedTo`. Folge: `agenthub
start codex` meldete "AKTUELL: nichts offen", während TSK-0268 mit
`codex:`-Präfix offen lag — der Work-Loop hätte sie unmittelbar danach
geclaimt. Zwei Wahrheiten über dieselbe Frage.
Jetzt nutzt der Check-in dieselbe Adressierungslogik.
Tests: 293 → 295.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`delivered` heißt nur "einmal ausgeliefert", nicht "gelesen" — abgeschlossen
ist eine Nachricht erst mit read/acked. Der Check-in filterte aber auf
`unread` und ließ damit jede Nachricht fallen, die irgendwo schon einmal
aufgetaucht war (etwa beim Session-Start), ohne dass der Agent sie je
beantwortet hätte.
Real aufgefallen an einer Architekten-Korrektur an kimi: Status `delivered`,
im pending-Block unsichtbar — die Korrektur wäre still verlorengegangen.
Genau der Fehlermodus, gegen den der Kanal gebaut wurde.
Tests: 292 → 294.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Realer Fall heute: kimi stellte ASK-0004 um 16:43 und blockierte in
`agenthub_ask(wait:true)`. Der Wait läuft nach 300 s aus. Der Architekt
brauchte für die Architekturentscheidung 18 Minuten — danach war kimi
dormant, das korrekt emittierte `ask`-Event lief ins Leere, und ein Mensch
musste den Agenten wecken.
Dieselbe Taubheit wie in TSK-0274, nur an einer anderen Stelle: dormant =
unerreichbar. Deshalb wandert die Antwort jetzt in den pending-Block —
`resolvePending` liefert beantwortete eigene Fragen mit, sodass sie beim
nächsten Hub-Kontakt (task_log, checkin, start) ankommt, egal wie lange der
Architekt gebraucht hat.
Tests: 290 → 292.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Architekt kann prinzipiell nicht in agenthub_work blockieren (er ist die
interaktive Gegenstelle des Menschen). Sein Sessionstart ist deshalb ein
Lagebericht: Hub-Status, Board, wer woran arbeitet inkl. "kein Check-in seit",
wer auf Übernahme wartet, und eine ausdrückliche Warnung, wenn etwas auf sein
Review wartet. Dazu das rollenspezifische Briefing (pollen statt blockieren,
Approve ist sein Gate, Push gehört dem Menschen, "still" ≠ "tot").
BUGFIX dabei: Die Rolle wurde aus der LOKALEN Config gelesen und hing damit am
Startverzeichnis — das Quellrepo hat ein .agenthub OHNE Config, wodurch
loadConfig warf und die Auflösung stumm auf "implementer" zurückfiel. Der
Architekt bekam so das Implementer-Briefing und hätte sich einen Task
geclaimt. Jetzt kommt die Rolle vom Hub-Roster, wenn ein Hub erreichbar ist;
der lokale Weg greift nur noch ohne Server.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Mensch soll pro Session eine Zeile tippen, nicht einen Textblock
einfügen (CEO: „die Prompts sind mega lang"). `agenthub start codex` druckt
jetzt das verbindliche Briefing selbst — Loop-Pflicht, Taubheit während der
Arbeit, pending-Block als einziger Empfangsweg, DEC-0035 — und zeigt
anschließend, was gerade auf den Agenten wartet.
Kein zweites Kommando: das bestehende `start` (Onboarding: announce, Task
claimen, Handoff drucken) nimmt jetzt ein positionales Agent-Argument und
stellt das Briefing voran. `--agent` bleibt für Hooks/Skripte, `--no-briefing`
gibt das alte Verhalten.
Die Rolle kommt aus dem Roster (Architekt wird als solcher erkannt), sodass
`agenthub start <name>` für jeden Agenten ohne weitere Flags reicht.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
WURZEL: Ein Agent empfängt SSE-Events ausschließlich, solange er in
agenthub_work blockiert. Während er einen Task AUSFÜHRT, ist er vollständig
taub — kein Reopen, kein Cancel, keine Nachricht erreicht ihn. Ein echter
Interrupt in einen laufenden Agenten-Turn existiert nicht. Heartbeat,
Watchdog, Polling-Fallback und Presence-Tracking waren allesamt Umgehungen
dieser einen Tatsache; deshalb kam das Problem jede Session zurück.
LÖSUNG — der einzige reale Kanal: die Momente, in denen der Agent von sich
aus mit dem Hub spricht.
- checkinService.resolvePending(): zustandslos aus Tasks + Messages
abgeleitet, nichts, was ein Neustart verliert. Erkennt, dass dem Agenten
die Arbeit entzogen wurde (reopen/cancel/fremder Claim), und liefert
ungelesene Nachrichten + offene Zuweisungen mit.
- agenthub_task_log gibt den pending-Block zurück; der Tool-Text macht ihn
verbindlich (pending.interrupted ⇒ sofort aufhören, nicht einreichen).
- Neu: agenthub_checkin(agent, taskId) für lange Strecken ohne Log-Zeile,
plus GET /agents/:agent/pending.
- resolvePending degradiert auf reinen Namensvergleich, wenn keine Config da
ist — ein Check-in darf nie an Konfiguration scheitern.
DEC-0035: `review` bindet den Agenten wie `in_progress`. Vorher galt er in
der Sekunde des Einreichens als frei, griff sich die nächste Task, und ein
Reopen prallte danach am Ein-Task-Guard ab (so ging der Reopen von TSK-0218
verloren). Der Loop bleibt aktiv — wach warten, nichts Neues anfangen.
Außerdem: in_progress → open erlaubt, damit der Architekt eine festhängende
Arbeit überhaupt entziehen kann (vorher 400, Agent blieb dauerhaft blockiert).
SICHTBARKEIT: /health und /agent-health zeigen pro Agent pendingCount
(wartende Tasks) und deafForSec (wie lange ohne Check-in bei laufender
Arbeit). Ungelesene Nachrichten stehen separat — der dreistellige
Altbestand einzelner Agenten hätte das Signal sonst erschlagen.
Tests: 279 → 290. tests/singleClaim (b) auf den neuen Vertrag umgeschrieben
(+ (b2): ein Reopen gewinnt gegen die nächste Task).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drei vom Architekten abgenommene Tasks, gebündelt als Checkpoint:
- TSK-0242: Agent-Alias-Mapping (kimi-ah → kimi kanonisiert, Rollen → preferredAgent),
reopenTask räumt claimedBy ab, fsWatch reindiziert direkte Datei-Edits,
work-Default 300s → 50s, task_list mit Limit.
- TSK-0245: zwei Agent-Klassen (dispatch loop|architect). Watchdog mahnt
architekt-getriebene Agenten nur noch EINMAL statt im Minutentakt;
`task dispatch` startet sie explizit, `task record` trägt extern
erledigte Arbeit mit origin=external nach.
- TSK-0249: Lifecycle wird serverseitig erzwungen (open→review scheitert mit
klarer Meldung), claimedBy/doneBy überleben bis done, Presence pro Agent,
Review-Watchdog, GET /architect/pulse (1.4 kB statt 34 kB, since-Cursor,
omitted statt stillem Abschneiden), unbekannter Agent → 400 statt 500.
Alle Punkte live am laufenden Hub nachgemessen, nicht aus Agenten-Logs übernommen.
Tests: 242 → 279 grün, tsc sauber.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- probe configured server URL (~1s) before remote commands; on failure
re-discover via LAN discovery, persist and use the resolved URL
- poll tryClaim/trySurfaceMessages every ~4s during the SSE wait in CLI
work and MCP waitForTask — a lost SSE frame costs seconds, never sleep
- work/MCP no longer auto-mark messages read: surfacing sets delivered,
explicit ack/read required; loops still wake only on unread (no spin)
- parseSSEBuffer accepts CRLF frame separators and joins multi data: lines
- bump version to 0.10.1 (package.json, CLI --version, MCP server)
Root cause of twitch + overlap: source.onmessage ran an unconditional full
board rerender (cardsEl.innerHTML=) on EVERY change event, and the enter
animation was on a global `.card` selector so it replayed on all cards each
rerender.
1+2) SSE onmessage now parses event.type and only rerenders the board on
type==='task' (message/ask/decision/memory/handoff/agent no longer
trigger a board rerender; budget still refreshes — it has its own
diff/throttle). renderBoard() rebuilt as a per-card diff-patch
(patchColumn/cardSig/cardNode): untouched cards are kept + reordered,
only new/changed cards are (re)built. Enter animation scoped to a
one-shot .card-new class. min-height added to the .card transition list
so the console-open growth is smooth. Open live consoles preserved
(reapplyConsoles unchanged; kept nodes retain their console DOM).
3) setMetric() pulses the .metric-card (.metric-flash + @keyframes metricFlash)
only when the value actually changes from a previously-seen value.
4) Donut center readout moved from a CSS top:66.7% div to SVG-native <text>
in viewBox coords (x=110, value y=102, label y=120) so it scales and sits
in the arc opening. .donut-value/.donut-label restyled with fill.
TSK-0154 politur: .cc-line.level-bridge .cc-text color.
tsc clean; vitest 36 files / 184 tests green. Visual points are Screenshot/
manual (no board.test.ts). No console regression.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- core/index.ts: the FTS5 'search' table has no UNIQUE on id, so INSERT OR
REPLACE appended a new row every update and the search JOIN returned an id
N times. Now delete-then-insert by id -> exactly one FTS row per entity.
- taskService.claimTask: race-guarded — only an OPEN task can be claimed;
same-agent re-claim is an idempotent no-op; a task claimed by another agent or
past open is refused. getTask+updateTask are sync, so within the single-thread
event loop the read-check-write is effectively atomic (first claim wins).
- routes.ts (spec 'nicht brechen'): PATCH in_progress surfaces a lost/non-open
claim as a clean 400 instead of 500, so the board drag reverts gracefully and
a second agent can't clobber the first's claim.
- tests: +coreCorrectness.test.ts (no FTS dup after updates; concurrent claims
-> one wins; idempotent re-claim; refuse non-open) + server.test.ts route guard
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- work.ts: the CLI work loop now wakes on architect follow-up MESSAGES, not just
tasks. drainAgentMessages() surfaces + marks-read the agent's inbox; wired into
waitAndClaim (SSE message events) and as an upfront check in workAgent. reopen +
next-assign already wake via task events; a plain message no longer leaves the
re-armed loop dormant.
- mcp/server.ts: implementer agenthub_work LOOP reminder now explicitly instructs
re-arming after agenthub_task_review so the architect's approval/reopen/message
wakes it (waitForTask already wakes on task+message).
- tests: work.test.ts +message-wake +reopen-wake
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- routes PATCH /tasks/🆔 uniformly appendTaskLog + publishLog for every status
transition (claim/review/done/cancel/reopen) and on assign; best-effort, never
fails the mutation. No double-emission (log rides the 'log' channel; fsWatch
only watches .md so the .log append is not re-emitted).
- MCP agenthub_task_log tool + remoteClient.appendTaskLog + CLI 'task log <id>
--text [--agent][--level]'
- agenthub_work LOOP reminder: 'Report meaningful progress with agenthub_task_log'
- taskDetail: Live Console panel — historic lines via readTaskLog + live tail via
the named task-log SSE event filtered to this task id
- tests: +taskLog-live.test.ts (PATCH->one log event, no double change, historic
render, POST /log, remoteClient)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Board-v3 (TSK-0099, codex, architect-reviewed 7/7):
- kanban columns fill viewport height, scroll inside the column (no page scroll)
- padding/margin between task cards
- bar-chart HTML-escape bug fixed (est marker inserted raw, not esc())
- half-donut center text centered
- task detail opens in a modal (title/description/activity, empty sections hidden)
instead of navigating to /tasks
- header-jitter on page switch fixed via scrollbar-gutter: stable
- SSE connection-leak/freeze fixed (EventSource closed on pagehide/beforeunload)
Animated KPI icons (architect):
- vendored lottie-web player + Lottie card icons under assets/, served via a
path-traversal-guarded /card-assets/* route (offline-safe, no CDN)
- state-driven: loop-while-active (open/in_progress/review), pulse-on-change
(active/done/agents); light KPI card bg so the black-line icons read clearly
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QMN96FH9rJDY2bZnEBdPfA
- The pulse is now one dash with an effectively infinite gap, animated
origin→target→off with fill:forwards — no wrap-around second bar and no
snap-back to origin when it arrives (fixes the ghost bar). Slower, ease-in-out
so it eases out of the origin card and into the target's last branch.
- Real delegation/review pulses now originate at the ARCHITECT (the root's
single child), not the CEO above it — so a task assigned to an agent no longer
visibly starts from the CEO. Cuts a 3-edge cascade to 2 for a PO-level agent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an org service + CLI: parse a fenced ```agenthub-org YAML block from
CLAUDE.md (or AGENTS.md), validate it (one root, resolvable parents, unique
ids) and write it to .agenthub/agenthub.config.json. So the human-readable doc
drives /team: edit the block, run `agenthub org sync`, the chart adapts. Can be
wired into a post-commit hook to sync automatically when CLAUDE.md changes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reflects the org move (CI/CD + Strategist now report to the architect, not the
CEO): architect coordinates them directly, and the demo flow adds a closing
'architect → CEO final report' step. The CEO is only touched at the very end.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Move the arrival/completion glow onto a dedicated ::after overlay animated by
opacity only, so it no longer collides with the node's box-shadow transition
— the card stays put on done (transform verified identity throughout).
- A pulse now lights only its FINAL destination; waypoint nodes it merely passes
through (e.g. the CEO on an architect→strategist peer handoff) are no longer
flashed as 'involved'.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The badge chip already says Active/Review, so the status line now shows just
the task id + relative time (e.g. 'TSK-0059 · 2h') instead of repeating it.
- Active card: a green highlight sweeps along the border left→right while the
task runs (masked gradient ::before), signalling live work.
- Status chip: white background (green/amber text), Paperclip-style.
- Slower, more readable animation (edge 1500ms, stagger 680ms, longer flow gaps).
- Arrival and task-completion are now soft glow fades with NO transform, so the
card no longer twitches on done; a dedicated doneFade plays when a task the
agent held goes done/cancelled.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 'Active'/'Review' badge is now a floating pill straddling the card's top-right
edge (light green / amber) instead of an inline tag — keeps the card uncluttered.
- Move info + rename buttons out of the text row into an absolute top-right
cluster so short names (Chahine, backyard, CI/CD) fit without truncating;
smaller name font (12px); slightly wider node (182px) + matching grid.
- Rename is discoverable: a pencil appears on node hover (still double-click too).
Role labels for org-only / function nodes come from config.org 'role' (CEO,
Product Owner, CI/CD, Strategist, Release) so they no longer show 'Implementer'.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>