Nothing in the gate read doc-comments: clippy doesn't check intra-doc
links, cargo test doesn't, and no check built docs. So a [`Foo`] pointing
at a renamed, moved or deleted item rendered as plain text and had no
discoverer but a human happening to read the comment.
That matters here more than in most repos, because the convention is to
put a thing's authoritative description in one doc-comment and point at
it from everywhere else -- the design leans on the pointers being real,
and a dangling link is worse than no link since it names something and
sends the reader looking.
Adds `docs-rustdoc` to nix/checks.nix: craneLib.cargoDoc over
--workspace --no-deps --document-private-items, denying six rustdoc
lints. Listed explicitly rather than -D warnings so a new lint appearing
upstream cannot red the build on a class nobody has triaged.
--document-private-items is load-bearing rather than thoroughness for
its own sake: most of this workspace's doc-comments live on private
items and //! module headers, so without it rustdoc checks a small
fraction of the links and the gate sits green while the rot continues.
Then fixes every error it reports, 40 to 0 across nine crates. The
classes differ and so do the fixes:
- public item, wrong scope -> qualify. Node and Node::parent are both
public; the link failed only because scheduler.rs does not import
Node. Six sites become [`crate::Node::parent`].
- private item -> downgrade to backticks. Nothing was made public to
satisfy a lint; changing API surface to appease a doc check would be
the tail wagging the dog.
- genuinely dead -> [`JobBuilder::insert_into`] names a method that does
not exist. Insertion is Scheduler::insert_job.
- prose that looks like markup -> argv[0] parsed as a link, and
<args>/<hex>/<name> parsed as HTML tags.
Note for future fixes: pub(crate) resolves in an intra-doc link, a plain
private fn in a binary crate does not (wait_for_nodes resolved,
connect_hint did not, same crate, same shape).
The check does not ride the clippy/test artifact cache. It takes
cargoArtifacts, but rustdoc needs its own flavour of dependency
metadata, which cargo build does not produce, so a --no-deps docs build
still compiles dependencies it never documents. Measured at 6m47s cold;
that reasoning is recorded in the check's own comment so the next reader
does not re-derive it.
Verified by running the check's exact command against the pre-cleanup
tree first: 40 errors, build failed. A gate that cannot fail is not
evidence, and building it before the cleanup makes that proof free.
Per mara: just show the task name, don't show any command — if an
agent doesn't name a task itself it still gets the id back in the
run tool's own result, so the todo doesn't need to repeat it. Drops
short_cmd_label (and its tests) added in the previous commit; the
summary is now just "bash task `<id>` running".
The todo pushed at task start embedded the entire raw shell command
(task.cmd) as its summary — for a multi-line heredoc script (a common
agent pattern), that balloons every UI that renders todo summaries to
the command's full line count (hyperhive#3248).
short_cmd_label keeps only the first non-blank line, char-truncated to
100 chars, with a trailing ellipsis whenever either the line itself
was cut or more lines follow — so a short-looking first line ahead of
a long heredoc body still reads as truncated, not as the complete
command. The full command is still on disk in the task file for
status/view; this only shortens the todo label.
done_summary previously only pointed at .out/.err without any visual
distinction, so a completed task with clean-looking stdout and a
nonzero exit still read as routine bookkeeping in the todo queue.
Rather than gating a flag on the exit code, key it on has_stderr - a
failed command mid-chain (cd bad-path && rm ...) can exit 0 while the
real evidence sits in stderr, so an exit-code trigger would filter out
precisely the cases where nothing looks wrong. .err's presence is
already the scarce, meaningful signal the Read() pointer is built on;
keying the flag on the same condition costs nothing on the common
quiet-success path (no stderr, no pointer, unchanged) and fires on
every case where something was written to stderr, including the ones
the exit code can't be trusted to reveal.
When stderr is present: header reads as a flag instead of neutral
bookkeeping, and the .err pointer is listed before .out so it's not
the last thing skimmed past on a long completion.
Six places in the tree hand-rolled the same connect / write one JSON
line / read one JSON line back. Two of them — the harness serve loop's
client and the MCP server's — were byte-identical apart from a six-line
wrapper, ~145 lines of literal copy-paste. The other four each
reimplemented a subset, and the subsets had drifted: some named the
socket path in their errors and some did not, one classified transient
against fatal failures and the rest retried nothing at all, two drained
the response and two decoded it.
That duplication was defended when the daemons were split out, on the
grounds that a daemon's socket etiquette should stay visible in the
crate that depends on it. The etiquette genuinely does differ. The code
does not, and five copies is where "each daemon documents its own
etiquette" stops paying for itself.
`hive-sock-client` now owns the transport once, generic over the
request and response types so it is protocol-agnostic: the host-served
control socket and the harness's in-agent socket both use it with their
own wire-type crates. The two real differences become values instead of
forks. Retry is `Retry::RideOutRestart` (2/4/8/16/30s, sized to ride out
a service restart) for callers with no natural retry of their own, or
`Retry::None` for callers already inside a poll loop where the poll
interval is the retry — and the reason each caller picked one is a
comment at the call site rather than a reimplementation. The response is
either decoded (`request`) or half-closed and drained (`notify`, where
the drain exists so the server's write-back doesn't land on a closed
socket). Whether a failure propagates or is logged and swallowed stays
at the call site, because that is the caller's choice and not a property
of the transport.
Errors always name the socket path now, everywhere. That detail is
load-bearing: a permission problem on a socket that reads as "is the
daemon running?" sends the operator to fix the wrong thing.
The transient-against-fatal enum is gone rather than moved. Serialising
happens before the retry loop and deserialising after it, so only
connect, I/O and short-read failures can reach the loop at all — a
deterministic failure is now unretryable by construction instead of by
classification.
It is deliberately a new crate and not part of `hive-agent-sock`. The
`*-sock` crates are pure wire types by convention — `hive-agent-sock`
depends on serde and nothing else — and the two largest copies talk to
the host socket, whose types live in a different crate entirely. A
transport in either wire-type crate would drag tokio into it and point
the wrong way besides.
No wire-format change: same JSON line in, same line out.
Argus flagged the doc comment overclaiming no risk of a stale
suppression leaking onto a reused task id — narrow the wording to
match the acknowledged race in run_task's completion handler.
A `status` or `run` call whose inline `wait_seconds` poll observes a
terminal task hands the caller the full result in that same tool
response. The completion wake fired unconditionally regardless,
producing a redundant `bash-task-<id>` inbox message for information
the agent already has.
Add a one-shot, in-memory wake-suppression registry in hive-bash-mcp's
runner: `wait_for_task` (shared by both BashRun's and BashStatus's
inline-wait paths) marks a task's wake suppressed the moment it
observes a terminal state; `run_task`'s completion handler consumes
that flag before calling `send_wake` and skips the wake if set.
In-memory only (daemon restart wipes it) — fine, since a task still
running across a restart is separately marked `interrupted` on boot
and gets its own fresh wake. Narrow best-effort race window between
the terminal write and the wake send; acceptable given this daemon's
existing best-effort delivery tolerance elsewhere.
docs/tools/bash.md updated to describe the new suppression behavior.
- subvol upgrade waits for the queued stop DAG before migrating (was
snapshotting + swapping state under a live bind mount) and for the
restart job after
- history trim gets a 5-min grace for fresh terminals so broad
stop/start waits can't miss a failed DAG evicted by the per-template
cap (cap still applies past the grace)
- restart-all returns its DAG ids so hivectl actually waits
- hard stops await their agent DAGs (bounded) before infra goes down,
restoring the agents-before-infra invariant
- hivectl wait uses node-level terminality so the after-any recovery
reconcile is watched to completion; infra render errors no longer
skip watching already-queued agent DAGs
- fold hive-bash-mcp's last local now_unix into wire_time
'Mirrors the logic previously embedded in hive-ag3nt' is pure provenance
(where the code moved from) — the #2077 rubric says drop impl-history. The
rest of my slice (nix modules + hive-priv/matrix-mcp/bash-mcp + forge.rs) is
already lean: surveyed it, the long comment blocks are all legit why/invariant/
contract docs under the 30-line threshold, not bloat.
Move the bash-task on-disk schema (TaskFile + TaskStatus) into hive-sh4re,
the shared wire-types crate, and re-export them from hive-bash-mcp::protocol
so existing in-crate imports keep compiling. This gives hive-ag3nt's agent
web UI a canonical type to deserialize when reading the bash-tasks dir for a
running-tasks panel, instead of a parallel struct that would silently drift
from the daemon's persisted format. Both crates already depend on hive-sh4re,
so no new dependency edges.
clippy::unnecessary_wraps — the helper always returns Ok. Build the
DaemonResponse directly and wrap with Ok() at the call sites. Fixes the
CI clippy failure on this PR.
When an agent parks on a long-poll that expires with nothing to show,
nudge it to spend the idle time on other useful work instead of
immediately re-blocking on the same call.
- recv: when wait_seconds > 0 and the inbox is empty at timeout, the
'(empty)' result now carries IDLE_WAIT_HINT. Immediate peeks
(no/zero wait) are unchanged.
- bash_status: when wait_seconds > 0 and the task is still
pending/running at timeout, append BASH_IDLE_WAIT_HINT. Finished
tasks and no-wait calls are unchanged.
Both thread a 'waited' flag into the formatter so the hint only fires
on an actual wait-timeout. Unit tests cover both. Implements #1411.
The nixpkgs bump to clippy 0.1.95 / cargo 1.95.0 added + strengthened a
large batch of lints. CI denied ALL warnings (`-D warnings`) against the
`pedantic = warn` workspace lint, so the bump hard-failed `nix flake
check` workspace-wide with zero code changes — and would recur on every
future clippy bump.
Posture fix (the durable part): CI now runs
`-D warnings -A clippy::pedantic`, so the default/correctness/style lints
stay a hard gate while the "extra, opinionated" pedantic group is
advisory only (still `warn` for local `cargo clippy` via the workspace
lints table, just non-blocking in CI). `-A` rather than `-W` so the
group drop doesn't re-enable the specific pedantic lints the workspace
allows (e.g. `must_use_candidate`).
Also fixes the genuine DEFAULT/STYLE lints the bump surfaced across the
workspace (doc_lazy_continuation, collapsible_if, ptr_arg,
match_like_matches_macro, …) via `cargo clippy --fix` + manual stragglers
(`too_many_arguments` #[allow] on the host-config constructors), and
three tests that had rotted while the CI runner was offline (#1221):
- topology::top_level_agents_in_multi_root — hardcoded unsorted expected
- rebuild_queue::depends_on_evicted_dep_counts_as_resolved — needs
MAX_HISTORY_PER_KIND newer terminals to evict, not one
- coordinator::agent_paths doctest — illustrative pseudo-code, now `ignore`
Validated: clippy + formatting + cargo-test checks all pass.
- restore count in get_loose_ends: 'N local task(s):' instead of bare 'local task(s):'
- add cross-crate coupling comment to both mcp_loose_ends_dir() copies
- add comment in hive-bash-daemon service env explaining HYPERHIVE_HARNESS_DIR
is already injected via systemd.globalEnvironment by the meta flake
- hive-ag3nt: remove bash_tasks.rs entirely; add mcp_loose_ends.rs that
scans harness/mcp-loose-ends/*.json generically (no bash knowledge)
- hive-bash-mcp: daemon writes mcp-loose-ends/bash.json on every task
state transition (pending/running/done/interrupted/timed_out)
- get_loose_ends: reads mcp_loose_ends::collect() instead of bash-specific code
- implements the generic mechanism from #1162