Commit graph

2,917 commits

Author SHA1 Message Date
atlas
31008c83df feat: pause an agent's turn loop without stopping its container
A paused agent keeps its container, its claude session and its
dashboard/todo servers up, but stops driving turns. Messages queue
unacked and are drained on resume.

The whole protocol is a single marker file, `<harness>/paused`. That
directory is already a bind-mount shared between host and container, so
both sides just stat the same path: the harness reads it to decide
whether to drive a turn, hive-c0re reads it to render the badge and
writes/removes it for `hivectl pause|resume`. No new wire protocol, no
container round-trip, and it is sticky across restarts by construction.

Not calling `recv_next` while paused *is* the queueing semantic, so
there is no fencing to get wrong: reminders buffer in their unbounded
channel, the todo `Notify` permit coalesces, and a `request_next_turn`
that raced the pause survives because the gate sits above
`self_continue.take()`.

Graceful stop is handled host-side rather than in the harness: a paused
agent provably has no turn in flight, so `run_signal` skips the fence
entirely instead of eating the full `GRACEFUL_STOP_TIMEOUT` waiting for
a checkpoint turn that will never run.

`paused` is reported on `ContainerView` / `AgentStatusRow` for the
dashboard, orthogonal to `running` and reported for stopped containers
too.

Closes: hyperhive/hyperhive issue 2271
2026-07-26 03:11:33 +02:00
atlas
1356a1f049 fix(#2624): make hive-forge failures unmistakable on stderr
`fn main() -> Result<()>` let anyhow's Debug impl render failures with a
bare `Error:` header. hive-forge is almost always invoked from an agent's
bash task, where the completion wake points at the task's .out file - so a
failure that only writes to .err is easy to miss entirely (mara's "had no
clue it failed" on #2624).

Wrap the dispatch in a run() and own the failure path in main():
- prefix with the binary name (`hive-forge: FAILED: ...`) so the line is
  unmistakably ours in a mixed transcript,
- render with {:#} (alternate Display), which keeps the full context chain
  inline - plain Display would have dropped every `.context()` below the
  top one,
- return ExitCode::FAILURE explicitly rather than relying on the Termination
  impl.

Half of #2624: the other half (surfacing .err in the bash-mcp completion
when a task exits non-zero) is damocles's, per the issue thread.
2026-07-26 02:44:41 +02:00
atlas
3429a8c5a6 job_queue: grow the rebuild subgraph from DeployApply (#2664)
The config-PR deploy's apply node still did the whole container rebuild
inline, through the last surviving `lifecycle::rebuild_no_meta` call. It
now merges, opens the two-phase meta deploy, and returns the ordinary
rebuild chain as a subgraph the scheduler grafts into the live DAG under
it. A new `FinalizeDeploy` node, gated on that graft, plants the deploy
tag and commits the staged lock.

Net effect: "did the agent come back up?" is answered by `Reconcile`
succeeding, the same way it is for every other rebuild, instead of by a
fused inline start — and each deploy phase is its own queue node, so the
dashboard shows which one is running.

The grafted nodes root on the apply node, so they land inside
`DeployWindow`'s subtree and re-enter the meta window and build slot it
already holds rather than deadlocking against them. The new happy-path
test runs on a one-slot queue specifically to pin that down.

`FinalizeDeploy`'s two git writes are fatal, deliberately: they are what
tells `DeployTail` a deploy confirmed good, so a node that merely warned
on them could report success while leaving the tail looking at the git
state of a failure — and the tail would then roll a good deploy back.
The trailing `meta::finalize_deploy` stays warn-only, since by then the
container already runs the new config.

The `failed/<id>` annotated tag moves into the tail, which is now the
only place holding a failed deploy. It reads the reason off the DAG via
a new `JobQueue::first_error`, and is gated on `main` having actually
moved — the rollback ref is parked *before* the merge, so its existence
alone does not mean a merge happened, and a pre-merge rejection must not
tag the previous, innocent head.

Removing the last inline rebuild orphaned a chain of now-dead code:
`rebuild_no_meta`, `container_exists`, `Coordinator::set_queue_build_log`
and `JobQueue::set_build_log_id_running`, all deleted here.
2026-07-26 02:28:03 +02:00
damocles
7b2645078a fix(#2635): close review nits on the questions mirror (inc2 pt2) 2026-07-26 02:07:33 +02:00
damocles
e5ef5a72be feat(#2635): wire harness-local questions mirror (inc2 pt2) 2026-07-26 02:07:33 +02:00
damocles
9471201698 feat(#2635): add harness-local questions mirror store (inc2 pt2, unwired) 2026-07-26 02:07:33 +02:00
atlas
ff8ada77b7 docs: describe the deploy subtree
Rewrite the approval flow's step 4 as the three phases, with the reason
the rollback state is a git ref, and refresh the coordinator's node
inventory + dispatch table. Fix four doc comments that still pointed at
the deleted `run_merge_config_pr`.
2026-07-25 22:55:02 +02:00
atlas
8499c793fe test: assert the deploy subtree's phase order and tail-on-failure
Two cases, both pinning the load-bearing property that the tail is
reached on every path: a failed apply (AfterAny dep is terminal) and a
failed verify (apply is cancel-cascaded, tail still claimable). Both
assert the DAG rolls up to Failed — an Ok tail must not launder a failed
deploy into a success.
2026-07-25 22:55:02 +02:00
atlas
816006fd48 job_queue: hang the approval link off the deploy root
Non-derivable per-node payload rides the node that owns it. Tagging all
four deploy nodes with the approval id would render the same card four
times in `dag_view`.

Also fix `set_queue_step`'s doc comment, which claimed the DAG-id lookup
was exact because approval DAGs are single-node. They are not anymore;
what actually holds is that the chain is strictly sequential with the
root parked in Finishing, so at most one node is ever Running.
2026-07-25 22:55:02 +02:00
atlas
27ecda7b13 actions: split the config-PR deploy into verify / apply / tail
`run_approval_merge_config_pr` and `run_merge_config_pr` are gone; the
three phases are `run_deploy_merge_verify` (drift gate, fetch, verify —
mutates nothing), `run_deploy_apply` (merge + build) and
`run_deploy_tail` (compensation + push).

The rollback state is a git ref in the applied repo
(`refs/hyperhive/rollback/<approval-id>`) rather than a value handed
between nodes, because hive-c0re can restart between the apply and the
tail and the tail still has to know what to undo.

Rolling `main` back on a *successful* deploy is the worst thing the tail
can do, so it is guarded twice: the apply drops the rollback ref before
it plants `deployed/<id>`, and the tail refuses to compensate at all if
`deployed/<id>` resolves. It takes two independent git failures to get
there.

`run_deploy_tail` returns nothing and warns on every error — a failing
compensation must not mask the deploy's own verdict, which the terminal
hook takes from the DAG's roll-up.
2026-07-25 22:55:02 +02:00
atlas
8899c9f355 lifecycle: add git_delete_ref
The deploy subtree parks its rollback state in a git ref and has to drop
it again on both the success and the compensated path.
2026-07-25 22:55:02 +02:00
atlas
bdf15168db job_queue: split ApprovalDeploy into a four-node deploy subtree
A config-PR deploy was one opaque node that fetched, verified, merged,
built and compensated. That shape made three things impossible: the
nix-heavy phases could not take the meta window without the cheap ones
holding it too, a crash mid-build left no node to run the rollback, and
the dashboard could only ever show "deploying" for the whole thing.

Replace it with a `DeployWindow` group root over `MergeVerify ->
DeployApply` (AfterOk) plus a `DeployTail` hanging off the apply with
AfterAny, so the tail runs whether the apply succeeded, failed, or was
cancel-cascaded by a failing verify.
2026-07-25 22:55:02 +02:00
damocles
001ea38ea4 gate stale todo wakes on an emptiness check (#2678) 2026-07-25 22:28:38 +02:00
atlas
dfadacd45f feat(job-queue): promote the meta-repo deploy window to a queue resource
The two-phase approval deploy keeps a bumped `flake.lock` staged
uncommitted for the whole container build, so no other meta mutation may
land inside that span — until now enforced by a process-global
`meta::exclusive()` mutex held inside each executor fn.

A `MutexGuard` cannot outlive the fn that takes it, which is what blocks
decomposing the opaque `ApprovalDeploy` node into scheduler-visible
sub-nodes: the window has to span them. Replace the mutex with
`Resource::MetaWindow`, a global capacity-1 queue resource declared by
every meta-mutating node kind (`NodeKind::needs_meta_window`). Resources
are held by a subtree root across its whole subtree, so a later increment
can hang the deploy's phases under one window-holding parent.

Same global serialisation as before, and the scheduler now blocks a node
from being claimed rather than parking a worker on a mutex.

Split the rebuild's meta preamble out of `Prebuild` into a new `MetaSync`
node. `Prebuild` must NOT hold the window: the old mutex was deliberately
scoped to drop before the multi-minute toplevel build, which only reads
the store, and a cap-1 global held across it would serialise every
agent's rebuild behind every other's. `MetaSync` is a sibling root that
`Prebuild` deps `AfterOk` on — not its parent, since a parent's resource
covers its whole subtree and would reintroduce exactly that problem.

Queue tests: shape assertions gain the extra node, which is the point of
the change (phases become nodes). The concurrency invariants are intact
but observed one step later — the `MetaSync` heads take turns on the
window, exactly as the runtime mutex made them, so those tests now
complete the heads before asserting that the prebuilds overlap.
2026-07-25 21:20:56 +02:00
damocles
2316287327 remove hive-agent-wake — no shipped consumer 2026-07-25 20:05:32 +02:00
atlas
65a0686297 fix(#2673): set nix fallback in agent + CI containers
Container nix invocations hard-failed whenever the remote builder
muede-pc2 was unreachable, while the identical build on the host
succeeded. Both go through the same host nix-daemon, so the difference
looked impossible.

The cause is that `fallback` is a client-side option: the nix client
transmits it to the daemon per connection (`tryFallback` in setOptions),
so the caller decides whether a failed remote dispatch may degrade to a
local build — even when the build itself runs on the host daemon under
NIX_REMOTE=daemon. Only genuinely daemon-side settings such as
`builders` are inherited from the host. The containers never set
`fallback`, so they took nix's default of false.

Set it in the agent-container base module and in the CI container, and
correct the hive-ci comment that claimed fallback was inherited from
the host daemon along with buildMachines and max-jobs.

Verified in an agent container: `nix fmt` fails outright on the remote
store's connection reset, while the same command with fallback enabled
reports the same connection error and then builds locally and succeeds.
2026-07-25 19:36:31 +02:00
damocles
6c886d3fa6 hive-agent: inline todo dispatch arms instead of a sub-match + unreachable!
per mara's review on #2679: replace the separate dispatch_todo sub-match
(with its trailing unreachable! arm) with four small handler functions
called directly from dispatch's existing match. same behavior, no
unreachable! left in the todo path.
2026-07-25 18:42:10 +02:00
damocles
2dcbb78b40 hive-agent: add debug logging around todo upsert/clear/mark-done
diagnostic instrumentation for #2678 (phantom 'you have todos' wakes
after clearing bash-task todos). logs subsystem/key/id/changed on
UpsertTodo, subsystem/key/all/count on ClearTodo, id/count on
MarkTodoDone, and a marker when the serve loop actually consumes a
todo_wake notification. no behavior change - RUST_LOG=debug only.
2026-07-25 17:40:11 +02:00
atlas
9f82dc4e7d fix(#2676): stop hive-priv flooding the journal with list output
`container_run` logs every stdout line at INFO (target `nixos-container`)
as operation progress. For the read-only `list` op — called on the hot
path (dashboard rescan, forge + boot sweeps) — that stdout is the return
value, not progress, so every call logs the full ~28-line container roster
at INFO. hive-c0re calls it several times a second, flooding the host
journal.

Gate the stdout per-line INFO logging on the op not being `list`. Mutating
ops still log their progress; stderr is still logged for every op (errors
matter regardless). No behaviour change beyond log volume.
2026-07-24 14:22:45 +02:00
damocles
a66b7ab298 feat(#2659): serve hive-matrix-mcp over persistent streamable-http, drop stdio bridge 2026-07-24 12:44:39 +02:00
iris
ae8d1aaac4 builds: drop per-entry agent grouping from DAG renderer
Removes entryAgents() and the two places it rendered agent names:
- rqe-agent code element in the entry header
- rqe-node-agent-label prefix per component chain when multi-component

The DAG structure split (WCC + fan-out) already communicates subgraph
boundaries visually via the separate .rqe-nodes rows; the agent-name
labels on top of that caused layout breakage (#2666) and duplicate
information. Closes #2666.

The live build log header still labels liveNode.agent (a single specific
node, not the whole DAG) — that .rqe-agent rule is kept.

Also removes the now-unused .rqe-node-agent-label CSS rule and its
comment.
2026-07-24 02:10:14 +02:00
atlas
03f8bc8a6a docs(#2671): trim per-verb arg help (repo-create)
Drop the API path from --org and the "Forgejo applies it to the initial
commit" mechanics from --default-branch (kept the user-facing caveat:
only takes effect with --auto-init). Swept the remaining verbs
(attachment-get, pr-reviews, attach, repo-add-collaborator, comment,
clone, pr-cmd router, …) — already user-relevant, no changes needed.
2026-07-23 22:54:15 +02:00
atlas
311599e8f2 docs(#2671): trim per-verb arg help (diff, list, lint, pr-merge)
Continue trimming clap arg help to user-relevant info: drop the
token-bounded-paging rationale (list --page), the why-it's-required
note (lint no-reviewer), the `Forgejo Do:`/`force_merge` API internals
(pr-merge), and tighten diff --full. pr-status was already clean.
2026-07-23 22:50:55 +02:00
atlas
ef9935e3d0 docs(#2671): trim per-verb arg help (pr-create, comments, ci-log, ci-rerun)
Drop implementation detail from the clap arg help (the `<verb> --help`
surface) — which API/endpoint, page-count math, persisted-vs-streamer
log-source internals, refspec shapes — keeping only what/when-to-use for
each flag. Module `//!` docs (dev-facing, not shown by `--help`) left
intact.
2026-07-23 21:55:04 +02:00
atlas
c64136d094 docs(#2671): trim hive-forge top-level + global-option help
Drop implementation mechanics from the `--help` surface, keep only
what a user needs to run the command:

- global `-r`/`-f`/`--json`: remove token-file paths, the bash-helper
  history, and the "already-JSON verbs ignore --json" aside.
- verb `about` strings (repo-create/repo-labels/repo-search/artifact-get/
  ci-log/ci-rerun/pr-commits): drop which-API / "no REST endpoint" /
  web-route / workflow-dispatch internals and cross-refs.

Per-verb arg help (verbs/*.rs) trimmed in follow-up commits.
2026-07-23 21:52:33 +02:00
atlas
c7feeb7f5b test(#2591): assert hivectl header shows source, not a derived label 2026-07-23 18:01:26 +02:00
atlas
0ee4647d1e refactor(#2591): drop the client-side DAG label map — show source + node kinds
Per mara (#2660): the client shouldn't re-derive a friendly name from
node kinds — that re-bakes the domain knowledge the raw-graph redesign
removed. The queue card / hivectl header now shows what the backend sends
(the DAG's source + the raw node kinds); the node chain conveys the
operation. Removes the DagView::label() helper (hivectl was its only
consumer after iris dropped the frontend map in #2660) and points
hivectl's header at d.source instead.
2026-07-23 18:01:26 +02:00
damocles
63b1a6fe39 docs(#2659): note bash extraMcpServers example is illustrative, tracks bashHttpPort 2026-07-23 18:01:20 +02:00
damocles
c4fcf7fbf1 feat(#2659): serve hive-bash-mcp over persistent streamable-http, drop stdio bridge 2026-07-23 18:01:20 +02:00
iris
ecc2ebe682 feat(#2637): rework builds.js for new DagView wire shape
- SSE live-log → 2s polling on GET /api/build-log/<node_id> ({stdout,stderr} JSON)
- DagView top-level started_at/finished_at used directly (isoToSecs); no client
  min/max derivation
- rollupState/entryKind/isoToSecs derive state+kind from NodeView[]; Done nodes
  absent from wire so fully-done DAGs disappear naturally
- rollupState([]) returns 'done' defensively for empty node arrays
- Log links and live-log panel gated on n.has_log (backend field, mirrors old
  build_log_id != null — excludes lock/noop/store-only nodes)
- Raw download: /api/build-log/<node_id>/raw (text/plain)
2026-07-23 18:00:17 +02:00
damocles
29f45ddd48 docs(#2627): crate READMEs for the harness column (hive-agent, hive-agent-mcp, hive-agent-wake, hive-bash-mcp) 2026-07-23 16:41:05 +02:00
atlas
0ae0780089 style(#2591): reword render_dag_line doc so clippy doesn't read it as a list 2026-07-23 16:40:46 +02:00
atlas
acffb6333b test(#2591): update queue tests for Done-nodes-vanish semantics
Two tests asserted the old behaviour where completed nodes/DAGs stayed
in the snapshot. Under the redesign, Done nodes are filtered off the wire
(a fully-Done DAG disappears; a Failed one lingers + is history-capped):
- failed_node test: the completed reconcile is Done → assert it's absent,
  not Done-present (its run is already verified by the claim).
- history-eviction test: fail the nodes so the DAGs linger (Done ones
  would vanish), then assert the grace window + per-template cap.
2026-07-23 16:40:46 +02:00
atlas
33c3fb3b34 refactor(#2591): hivectl derives DagView label + roll-up state from nodes
hivectl/dag_progress.rs was reading the now-removed DagView.kind/state.
Derive both from the node set via the shared DagView::rollup_state() +
DagView::label() helpers (added to hive-sh4re). Timestamps are DateTime<Utc>
now — elapsed calcs compare in unix seconds. Dropped the live step display
(step left the wire). Test fixtures updated to the slim shape.
2026-07-23 16:40:46 +02:00
atlas
52dd9ede23 feat(#2591): add GET /api/build-log/<node_id> {,/raw} query endpoints
The raw-graph wire drops the inline build_log_id; the client fetches a
node's captured output on demand. Two handlers resolve node id -> log-row
id (JobQueue::build_log_id_of, now keyed by the wire u64) then delegate to
the existing get_full / raw handlers: /api/build-log/<node_id> serves the
BuildLogFull JSON ({stdout, stderr} + header), /raw serves text/plain.
404 when the node has no linked log.
2026-07-23 16:40:46 +02:00
atlas
02e2bf895e refactor(#2591): DagView carries host-computed timestamps + NodeView.has_log
Reconcile with mara + argus's review on the frontend PR (#2660):

- DagView regains started_at/finished_at (DateTime<Utc>), computed
  host-side as min/max over ALL subtree nodes (including the Done ones
  filtered off the wire). The client can't derive these — the
  earliest/only-started node is often Done and absent — so the backend
  sets them, per mara's call.
- NodeView gains has_log: bool = build_log_id.is_some(), the precise
  old 'node has a captured build log' guard so the dashboard only shows
  a log link for nodes that actually produce one.
2026-07-23 16:40:46 +02:00
atlas
f3207ce9f8 refactor(#2591): drop never-constructed Template::{Destroy,Reconcile}
Now that Template is internal to hive-c0re (not a pub wire enum), the
dead-code lint flags the two variants nothing ever constructs. Removed
them + their as_str arms; terminal_hook's catch-all arm is unaffected.
2026-07-23 16:40:46 +02:00
atlas
51bcad1adb refactor(#2591): fix wire-shape consumers (server await_dags, tests)
- server::await_dags: a DAG is settled when gone from the snapshot (fully
  Done) or present with all nodes terminal; pending only with a non-terminal
  node (DagView no longer carries a rolled-up state).
- DagView::rollup_state() added to hive-sh4re — the shared node-set roll-up
  derivation every Rust consumer uses.
- JobQueue::build_log_id_of(node_id) — the node_id -> build_logs lookup the
  query endpoint will use; tests assert log-id via it now.
- tests: derive roll-up state; drop the off-wire step/build_log_id wire asserts.
2026-07-23 16:40:46 +02:00
atlas
c574948d7c refactor(#2591): read node lifecycle off the jobq Node; slim DagView build (WIP)
hive-c0re side of the raw-graph wire: dag_view now projects the slim
DagView, reading started_at/finished_at/error straight off the
hive_jobq Node (removes the node_rt double-write from #2645), excludes
Done nodes, and rides approval_id/inputs on the owning node. Template
moved into hive-c0re (model.rs) — no longer on the wire. Still WIP:
build-log endpoint + hivectl derive + compile fixes to follow.
2026-07-23 16:40:46 +02:00
atlas
54d144b647 refactor(#2591): slim the job-queue wire types to a raw-graph projection
WIP (hive-sh4re only; hive-c0re construction follows). Reshapes the
dashboard/hivectl queue wire per mara's redesign (#2637):

- DagView is now a thin projection: { id, source, reason, created_at, nodes }.
  Dropped the rolled-up kind (Template)/state/started_at/finished_at/inputs/
  approval_id — the client derives label + roll-up state + DAG timestamps
  from the node set.
- NodeView drops step + inline build_log_id; started_at/finished_at are
  chrono DateTime<Utc> (off the hive_jobq Node); non-derivable per-node
  payload rides the owning node (approval_id on the approval node, inputs
  on the meta_lock node).
- Deleted the Template enum entirely.

Done nodes are excluded from the wire (a fully-done DAG disappears; a
failed DAG lingers until the history cap). Build logs move to an on-demand
GET /api/build-log/<node_id> query (next commits).
2026-07-23 16:40:46 +02:00
damocles
a7f0f3d231 fix(#2635): drop obsolete todos_db/reminders_db path aliases 2026-07-23 15:05:19 +02:00
damocles
c316dc852d feat(#2635): consolidate todos + reminders into one hyperhive-state.sqlite 2026-07-23 15:05:19 +02:00
atlas
64a14718fe docs(#2627): clarify hive-sh4re owns payloads, not the wire envelopes
Land argus's review nit from #2654 (it was pushed to that PR's branch as
fc4230f2 but got orphaned when the branch was merged + auto-deleted before
the fix landed). The hive-sh4re README opening said 'Agent / Manager
request + response shapes', which reads as if the wire envelopes still live
here; they moved to the per-socket crates. Now says 'shared payload
vocabulary' and notes where the envelopes live.
2026-07-23 14:23:16 +02:00
iris
7176ce6322 docs(#2627): add README for hive-screen-mcp 2026-07-23 14:17:47 +02:00
atlas
bbfc578c95 docs(#2627): add READMEs for the remaining infra crates
Second increment of the per-crate README effort, covering the rest of the
infra/wire/priv column: hive-priv, hive-metric, hive-types, hive-sh4re,
hive-core-agent-sock, hive-agent-sock. Same shape as the first batch —
purpose + when-to-use, and point at the crate-root //! docs plus the
relevant docs/ pages rather than duplicating them. Wires
readme = "README.md" into each Cargo.toml [package].

Disjoint from the batch-1 crates, so the two increments compose cleanly.
2026-07-23 13:16:29 +02:00
atlas
4017a57350 docs(#2627): add READMEs for hive-jobq + the socket wire crates
Adds crate READMEs (matching the hive-claude precedent) and wires
readme = "README.md" into each Cargo.toml [package] for hive-jobq,
hive-host-sock, and hive-priv-sock — the crates squarely in the infra
lane. Each README leads with purpose + when-to-use and points at the
crate-root //! docs for depth rather than duplicating them.

First increment of the per-crate-README effort; the shape here is the
proposed template for the remaining crates (see issue discussion).
2026-07-23 12:34:22 +02:00
damocles
e9df5def02 add agent-facing compact tool gated on context usage 2026-07-23 12:34:20 +02:00
iris
5e1abe4836 fix comment: remove tracker tag from schedules.js module doc 2026-07-23 09:54:40 +02:00
iris
fab2fdcc0a fix(#2650,#2649): drop reminders tab + pending_reminders chip
hive-c0re's broker reminder store and /api/reminders endpoint were
removed in PR #2644 (reminders migrated to in-container sqlite store).
The dashboard had a QU3U3D R3M1ND3RS section on the schedules tab backed
by that endpoint, and a per-agent badge driven by pending_reminders
(always 0 after the migration).

Remove both. Per-agent reminders now surface through get_loose_ends /
the todos pill on the agent page, consistent with the todos migration.

- frontend/packages/dashboard/src/schedules.js: drop refreshReminders,
  renderReminders, applyRemindersChanged; drop appendLinkified import
  (no longer used); update module comment.
- frontend/packages/dashboard/src/tabs.js: drop applyRemindersChanged
  and refreshReminders imports; remove reminders-section from managed
  list; remove refreshReminders call site; drop reminders_changed from
  SSE dispatch; simplify countdown ticker (reminder-due gone).
- frontend/packages/dashboard/src/dashboard.html: remove QU3U3D
  R3M1ND3RS section.
- frontend/packages/dashboard/src/dashboard.css: remove reminder list +
  row CSS.
- frontend/packages/dashboard/src/common.css: remove .badge-reminder.
- frontend/packages/dashboard/src/swarm.js: remove pending_reminders
  from fingerprint key and badge render.
2026-07-23 09:54:40 +02:00
damocles
ee072149c9 fix(#2038): don't treat MCP 'pending' status as degraded
pending is the claude CLI's normal init-event race for a freshly
spawned stdio server (status reported before the handshake completes,
then flips to connected moments later in the same turn — see
anthropics/claude-agent-sdk-typescript#368). flagging it fired on
nearly every turn for all three configured servers with zero actual
impact, which is why #2038's persistent-vs-one-turn repro question
looked answered when it wasn't — that was this false positive, not
the real bug.
2026-07-23 07:47:56 +02:00