Commit graph hyperhive/hive-c0re/src/actions.rs
Author SHA1 Message Date
damocles
b191858366 defer start-after-rebuild to the fast lane so the build lane frees up (closes #2094) 2026-07-01 23:37:13 +02:00
damocles
3618399d94 fix(#1953): route approval helper-events to the submitter, not the root agent 2026-06-23 22:04:56 +02:00
damocles
e593547063 feat: make the bootstrap/root container destroyable (drop stale destroy guard + redundant hive-priv manager allows) 2026-06-23 14:50:03 +02:00
damocles
0b86295776 feat(#1838): run_merge_config_pr handler (verify pr head, ff forge main, deploy) 2026-06-23 13:12:12 +02:00
damocles
5f5626456a wip(#1838): extract deploy_applied_target shared deploy tail + dedup approve dispatch 2026-06-23 13:12:12 +02:00
damocles
449e3fcb7b wip(#1838): add MergeConfigPr approvalkind + match-site arms 2026-06-23 13:12:12 +02:00
damocles
4beaeea7b3 meta: non-mutating verify_commit + pre-flight eval before apply 2026-06-22 19:48:59 +02:00
damocles
1caf978004 hive-c0re: create the agent subvolume before seeding dirs in init_config 2026-06-22 01:10:58 +02:00
damocles
b3d002e4a7 hive-c0re: let request_init_config spawn a brand-new sub-agent under its requester 2026-06-21 22:36:35 +02:00
atlas
eb103a5660 btrfs subvols: scrub tracker tags from comments; harden subvol chown
- Replace the #-number tracker references in code comments with prose
  (tracker-tag lint; hive convention is prose in source).
- ensure_agent_subvolume now treats a chown failure on the freshly
  created subvolume as fatal: it rolls the subvolume back (deletes it)
  and returns an error, instead of warning and leaving a root-owned
  subvol that hive-c0re can't write into (which would also make the
  c0re-side exists-check skip the retry, wedging the agent).
2026-06-19 13:46:39 +02:00
atlas
1f602d5fda hive-c0re: back agent state dirs with btrfs subvolumes
Progressive enhancement: a brand-new agent's state root under
/var/lib/hyperhive/agents is created as a btrfs subvolume when the host
filesystem is btrfs, otherwise it falls back to a plain directory. No
existing agent is auto-migrated — the new path only fires when the root
does not yet exist, so plain-dir agents are left untouched until an
explicit opt-in upgrade.

Two new privileged ops (subvolume create/delete are root-only):
EnsureAgentSubvolume statfs-gates on btrfs, creates the subvolume, and
chowns it to the hive-core user so the normal state/claude/harness
mkdirs succeed inside it; DeleteAgentSubvolume btrfs-subvolume-deletes
the root iff it is actually a subvolume. hive-c0re calls Ensure before
the per-agent dirs are created (spawn/rebuild/InitConfig) and Delete on
the purge path only — destroy keeps the subvolume for revival, matching
plain-dir semantics. btrfs-progs added to the hive-priv unit PATH.

Per-subvolume usage accounting + optional quota is a separate
follow-up.
2026-06-19 13:46:39 +02:00
damocles
e08122a206 fix(#1643): filter destroyed-agent targets from dashboard schedule view 2026-06-13 15:58:44 +02:00
damocles
3130e56cfb refactor(#1474): replace too_many_arguments allows on pub fns with param structs 2026-06-09 11:25:38 +02:00
damocles
7c9954ceec chore(#1474): add reason= to remaining bare clippy allows outside dashboard 2026-06-09 11:25:38 +02:00
atlas
734fe88858 fix(ci): unblock nix flake check after clippy 0.1.95 bump (#1368)
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.
2026-06-05 15:32:07 +02:00
iris
68108fe5f8 fix: emit SchedulesChanged from manager-server + approval paths
Agent-triggered schedule mutations (cancel_schedule, fire_schedule_now,
edit_schedule MCP tools) go through manager_server.rs, not the HTTP API
handlers. Approval-resolved SchedulePrompt inserts go through actions.rs.
Neither was emitting SchedulesChanged.

- manager_server.rs: add emit_schedules_snapshot() on Ok in
  handle_cancel_schedule, handle_fire_schedule_now, handle_edit_schedule
- actions.rs: emit_schedules_snapshot() after successful
  run_approval_schedule_prompt (covers request_schedule_prompt approval
  resolving)

Coverage is now complete: every path that writes a scheduled_prompts row
emits the SSE snapshot.
2026-06-05 12:06:32 +02:00
damocles
7118c5efdd feat(queue): link rebuild queue entries to build log rows for live streaming 2026-06-04 17:39:31 +02:00
damocles
d31a723daf feat(#550): add depends_on to queue entries for explicit dep tracking 2026-06-04 17:39:31 +02:00
damocles
41eb3f806c refactor: remove hyperhive.role option — there is only one role: agent 2026-06-04 14:31:44 +02:00
damocles
29c7f64bd3 refactor(#1202): introduce HiveEnv + AgentPaths to reduce arg repetition 2026-06-03 22:09:08 +02:00
damocles
d1fbb4aef8 feat(#1184): make agent CPU quota and memory limit configurable 2026-06-03 21:39:14 +02:00
damocles
eae0e875cf feat(#1086): serialize perm changes through rebuild queue
add QueueKind::PermChange — dashboard tool-group and capability
handlers no longer write the shared JSON files inline. instead they
enqueue a PermChange entry; the FIFO worker applies the file write
then calls rebuild_agent so the updated env var takes effect.

concurrent batch-apply actions for different agents previously raced
on tool-groups.json / capabilities.json (last write wins, earlier
change silently dropped). serialising through the queue prevents this.

dedup check extended with perm-type discriminant so tool-groups and
capabilities changes for the same agent are kept as distinct entries
and never collapse into one slot.
2026-06-02 16:42:22 +02:00
atlas
db50da570a refactor(#1003): nixpkgs + nixpkgs-unstable as top-level meta inputs
Per mara's direction: both nixpkgs and nixpkgs-unstable are now
top-level meta flake inputs with explicit store-path URLs.  Hyperhive
follows them rather than the other way around:

  inputs.nixpkgs.url            = "path:${pkgs.path}";
  inputs.nixpkgs-unstable.url   = "path:${nixpkgs-unstable}";
  inputs.hyperhive.url          = "...";
  inputs.hyperhive.inputs.nixpkgs.follows            = "nixpkgs";
  inputs.hyperhive.inputs.nixpkgs-unstable.follows   = "nixpkgs-unstable";

New NixOS host options (auto-set at build time, overridable):
  services.hyperhive.c0re.nixpkgsFlake
    default: "path:${pkgs.path}" — host's evaluated nixpkgs.
  services.hyperhive.c0re.nixpkgsUnstableFlake
    default: "path:${nixpkgs-unstable}" from hyperhive's flake.nix —
    the channel that carries claude-code.  Operators can override to
    track a different unstable snapshot.

Legacy fallback (both args empty) preserved for backward compat.
Two new Rust tests cover the full-URL and fallback paths.
2026-06-01 23:30:04 +02:00
atlas
fe5a41288d feat(#1003): inject pkgs.path into meta flake as explicit nixpkgs.url
meta flake was using `nixpkgs.follows = "hyperhive/nixpkgs"` but
`hyperhive` is a store-path input, so nix resolves hyperhive's own
pinned lock rather than the host's follows-substituted version.
When an operator sets `inputs.hyperhive.inputs.nixpkgs.follows =
"nixpkgs"` in their host flake, the meta flake was silently ignoring
it and using hyperhive's pinned nixpkgs instead.

Fix: hive-c0re.nix injects `--nixpkgs-flake path:${pkgs.path}` into
the daemon's ExecStart. `pkgs` IS the host's nixpkgs when follows is
set; otherwise it's hyperhive's own pin — so the meta flake gets the
right nixpkgs in both cases. render_flake emits `nixpkgs.url = "..."`
(explicit) when nixpkgs_flake is non-empty, falling back to the old
`follows` form when empty for backward compat.
2026-06-01 23:29:54 +02:00
atlas
4bff450343 feat(gateway): hivectl gateway user management + fix htpasswdFile assertion
Add `hivectl gateway {create-user,delete-user,list-users}` subcommands for
managing htpasswd files used by gateway Basic auth. Pure Rust bcrypt
(cost 12, $2y$ prefix nginx accepts). No external htpasswd binary required.

Also fix the NixOS module assertion: `cfg.auth ? htpasswdFile` is always
true in the module system (declared options always exist as keys); switch
to `nullOr path; default = null` + `!= null` check so the assertion
actually fires with a useful error when enable=true but no file is set.
Guard bind-mount and nginx config against null to prevent eval errors.

Update docs/gateway.md to show hivectl commands instead of raw htpasswd.
2026-06-01 23:25:28 +02:00
damocles
68fd7e08a5 fix(#948): pre-enqueue topology-sorted cascades on meta-update approval 2026-06-01 17:55:01 +02:00
iris
188f3ea5ec chore: scrub #NNN issue references from code comments and nix descriptions
Part of the docs-migration chore (issue #708). Remove GitHub issue
numbers from inline comments, option descriptions, and rustdoc —
these are contextless noise for anyone reading the code without
access to the original discussions. Replace with prose that captures
the same rationale directly.

No functional change. Build still clean (cargo check passes).
2026-06-01 13:30:52 +02:00
damocles
d14df8b9a0 lifecycle: thread on_step callback through rebuild for accurate queue step labels 2026-06-01 00:05:09 +02:00
iris
9ed58ab96d clippy: fix lints that crane's cargoClippy properly enforces (#538)
The naersk → crane swap in the parent commit flips clippy from
silently passing to actually failing on `-D warnings` (naersk's
`mode = "clippy"` mangled the `--` separator so the deny never took
effect). This commit clears the surfaced lints so the workspace
builds clean under the new enforcement — every fix is mechanical and
preserves behaviour. Tests still pass (160 across the workspace).

Auto-fixes via `cargo clippy --fix`:
- `doc_markdown` (19 sites): bare identifiers in doc comments
  wrapped in backticks
- `format_in_format_args`, `explicit_into_iter_loop`,
  `redundant_closure_for_method_calls`, `useless_conversion`, and
  a few more — mechanical rewrites of the kind cargo can apply
  safely.

Hand-fixed:
- `match_same_arms` (forge_notify::is_atx_heading): two arms returning
  `true` collapsed into a single `matches!` pattern.
- `cast_sign_loss` + `format_push_string` (mcp.rs status formatter):
  guarded `i64 → u64` through `u64::try_from(…).unwrap_or(0)` (status
  timestamps are always positive in practice; clamp the skew edge to
  0) and swapped `out.push_str(&format!(…))` for `write!` into the
  buffer with an infallible-writer `let _ =`.
- `doc_lazy_continuation` in turn.rs + manager_server.rs + sh4re/lib.rs:
  doc paragraphs that the markdown parser was treating as list-item
  continuations got either a separating blank line or a `/`-for-`+`
  word swap so the parser stops seeing a list.
- `unused_async` (manager_server::handle_request_schedule_prompt):
  function has no `.await`; dropped the `async` and its `.await` call
  site.
- `needless_pass_by_value` (scheduled_prompts::submit): take
  `&NewSchedule` instead of moving the struct in; updated two prod
  callers and eight test sites to pass references.
- `type_complexity` (approvals::mark_cancelled): hoisted the
  7-tuple SELECT row shape into a `type CancelLookupRow = (…);` alias.

Allow-with-reason for intentional patterns:
- `option_option` (6 sites across dashboard / scheduled_prompts /
  manager_server): `Option<Option<T>>` carries three-state PATCH
  semantics (missing key = leave alone, `Some(None)` = clear,
  `Some(Some(v))` = set). Collapsing to `Option<T>` loses the
  "clear" state.
- `dead_code` (rebuild_queue::QueueKind::Destroy /
  QueueSource::CrashRecover; topology::parent_of / default_seed):
  wire-shape variants + API surfaces kept for the upcoming features
  (#361 follow-ups, future `Destroy` queue routing, crash-recovery
  path). Allowed at the variant / function level with the rationale
  in `reason = "…"`.
- `too_many_lines` on three specific call-sites: a 117-line
  exhaustive-variant test (dashboard_events::kind_tag_matches_…),
  the meta-flake string template renderer
  (meta::render_flake_with_lookup), and the notification poll loop
  (forge_notify::poll_once) — splitting any of them would just hide
  the contiguous shape they exist to keep visible.

`nix flake check` formatting target is still broken on main itself
(pre-existing nixfmt drift across ~28 files unrelated to this PR);
left alone here so the scope stays "crane port + lints the port
exposed" and the operator's review doesn't have to triage drive-by
nixfmt churn.
2026-05-29 01:45:48 +02:00
damocles
a286ae777c rebuild_queue: per-entry step label + worker phase annotations (#437) 2026-05-27 01:58:34 +02:00
damocles
aa7d8d9c9a c0re: schedule_prompt approval kind + worker + manager surface (#444 step 2) 2026-05-26 01:27:25 +02:00
damocles
a4789760ed c0re: route approval execution through rebuild_queue (closes #436) 2026-05-26 00:29:42 +02:00
damocles
748536203b refactor: split long functions per review feedback; remove all #[allow] attributes 2026-05-22 19:24:44 +02:00
damocles
484cea62c7 fix #265: resolve all remaining clippy warnings (cast, too_many_lines, doc nits) 2026-05-22 19:11:40 +02:00
damocles
30d82148e0 clippy: apply auto-fixable warnings across workspace (closes #265 partial) 2026-05-22 18:55:57 +02:00
damocles
bbe4cdb872 add meta read access and remote for agents on forge 2026-05-22 15:28:06 +02:00
damocles
7426654a74 crash_watch: track prev_sub_agents to fix needs_login for newly spawned agents 2026-05-22 12:31:18 +02:00
damocles
55fe2856b9 actions: emit needs_login after first spawn, fix update_meta_inputs arity 2026-05-22 12:31:18 +02:00
damocles
66f1568e8f apply_commit handles first-time spawns, request_spawn deprecated 2026-05-22 10:52:02 +02:00
damocles
3e098c56ff update_meta_inputs: require operator approval, rename to request_update_meta_inputs 2026-05-22 10:49:20 +02:00
damocles
d3d52349c3 model/context: move context window config to host-level hive-c0re.nix 2026-05-20 15:49:03 +02:00
damocles
80dd5bb69e two-step agent spawn: request_init_config + request_spawn 2026-05-20 14:40:15 +02:00
müde
5aad2d67e1 forge: mirror applied config repos to a private agent-configs org
on startup (and after every applied-repo ref mutation) core pushes
each agent's hive-c0re-owned applied repo — main plus every
proposal/approved/building/deployed/failed/denied tag — to
agent-configs/<name> on the local forge. the org is private and
agents are not members, so core is the only principal that can read
it.

the tokenised push url is passed inline, never stored as a named
remote: the applied repo is bind-mounted read-only into the manager,
so a token in .git/config would leak the core admin credential to an
agent.

push_config is best-effort at every site (ensure_all, spawn,
approve, deny, submit) — a missing or down forge never blocks a
deploy.
2026-05-20 10:24:50 +02:00
damocles
f9f1346eae clippy: zero pedantic warnings across the tree 2026-05-18 22:09:34 +02:00
müde
aed43ce4df dashboard: tombstones + meta_inputs events — last /api/state refetches drop
new DashboardEvent::TombstonesChanged + MetaInputsChanged carry
full snapshots (lists are tiny; snapshot beats diff for race
avoidance). Coordinator-side helpers
emit_tombstones_snapshot + emit_meta_inputs_snapshot fire from
every mutation site: actions::destroy + post_purge_tombstone +
actions::approve (spawn finalise consumes tombstone) +
run_meta_update + auto_update::rebuild_agent (lock bumps).

client adds derived stores + apply* handlers + drops the
post-submit refetch on PURG3 (container row + tombstone row)
and meta-update.

after this commit /api/state is fetched exactly once per page
session (cold load); every other change rides the SSE channel.
2026-05-17 23:52:12 +02:00
müde
e7ce35c503 phase 6: container events + drop the 5s /api/state poll
new DashboardEvent::ContainerStateChanged + ContainerRemoved
close the last refetch loop on the dashboard. Coordinator's
rescan_containers_and_emit diffs a fresh container_view::build_all
against a cached last_containers map and fires per-row events.
called from actions::approve (post-spawn), actions::destroy,
the lifecycle_action wrapper, auto_update::rebuild_agent, and
the existing 10s crash_watch poll.

ContainerView extracted to its own module so coordinator and
dashboard can both build it. dashboard endpoints flip to 200;
container-lifecycle forms carry data-no-refresh. client drops
the periodic poll entirely — initial cold load + SSE for
everything afterwards. pending overlay reads from the existing
transientsState since the new event payload doesn't carry it.

PURG3 + meta-update keep the post-submit refetch since
tombstones + meta_inputs aren't event-derived yet; tracked in
TODO.md.
2026-05-17 22:01:15 +02:00
müde
56d615b51f dashboard: approval_added / approval_resolved mutation events + client derived state 2026-05-17 13:30:25 +02:00
müde
411cf86632 nix fmt + rustfmt sweep 2026-05-17 01:40:28 +02:00
müde
480d646f69 forge: auto-create a user + token per agent on spawn / startup
new forge module probes the hive-forge nixos-container (no-op when
absent), and ensures every agent + the manager has a forgejo user
named after them with an access token at `<state>/forge-token`
(visible inside the container as `/state/forge-token`).

idempotent: skips user creation when forgejo reports 'already
exists', skips token issuance when the file is present, scopes the
token to read:user,write:repository,write:issue. token-name suffixed
with a clock so re-issuing doesn't collide with a stale name. shells
out via `nixos-container run hive-forge -- runuser -u forgejo --
forgejo admin` (runuser instead of sudo since sudo isn't in the
container by default).

hooks: ensure_all sweeps existing containers at hive-c0re startup
(backgrounded), and the actions.rs spawn task calls ensure_user_for
the new agent right after lifecycle::spawn succeeds. failures log a
warning but don't abort spawn — a missing token is recoverable from
the next startup sweep.
2026-05-16 20:55:13 +02:00
müde
313121a6e9 fix: transient state leak via RAII guard
bare set_transient/clear_transient pairs leak the in-memory transient
on task cancellation, panics, or any early return between the two
calls — dashboard then shows the agent stuck in 'rebuilding…'
forever (coder hit this today). add Coordinator::transient_guard
returning a TransientGuard whose Drop clears, and convert every
caller (dashboard lifecycle_action, auto_update::rebuild_agent,
manager_server Update, actions::destroy, actions Spawn task,
migrate phase 4). destroy() now takes &Arc<Coordinator> so it can
hold a guard. existing stuck transients clear on next hive-c0re
restart since transient state is in-memory only.
2026-05-16 19:47:52 +02:00