Commit graph

815 commits

Author SHA1 Message Date
damocles
dfb2595a50 hive-matrix: fix tuwunel option types (address/port lists, max_request_size int) 2026-05-29 02:48:50 +02:00
lexis
c580b7ce6d docs: fix tuwunel upstream URL + clarify domain/serverName requirement 2026-05-29 02:24:13 +02:00
lexis
249635430d docs: add hyperhive.domain + matrix module to README host config (follow-up to #552) 2026-05-29 02:22:16 +02:00
damocles
e3c9715220 cargo: regenerate Cargo.lock to add transitive deps surfaced by resolver=3 (closes #556) 2026-05-29 02:03:38 +02:00
lexis
b0e35687ae nix: fix stale useSubdomain reference in hyperhive.domain description 2026-05-29 01:55:16 +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
iris
4b6c733afb flake: replace naersk with crane (#538)
Framework swap, no public API change.

- naersk input → crane (`github:ipetkov/crane`); crane is stateless, no
  nixpkgs.follows needed.
- `forAllSystems` exposes `craneLib = crane.mkLib pkgs`,
  `cargoArtifacts = craneLib.buildDepsOnly` (built once, reused), and
  a shared `nativeBuildInputs = [ pkgs.librsvg pkgs.git ]` consumed by
  buildDepsOnly + buildPackage + cargoClippy so the three derivations
  see the same toolchain shape.
- `packages.default = craneLib.buildPackage` (was naersk-lib.buildPackage)
  with explicit `pname = "hyperhive-workspace"; version = "0.1.0";` —
  the virtual workspace Cargo.toml has no [package].name so crane
  needs the hint.
- `checks.clippy = craneLib.cargoClippy` (was naersk + overrideAttrs
  hack). Crane parses `cargoClippyExtraArgs = "--workspace --all-targets
  -- -D warnings"` correctly; naersk's `mode = "clippy"` used to mangle
  the `--` separator which is why the old wiring went through
  overrideAttrs. The whole hack — including `doCheck = false`,
  `copyTarget = false`, and the swapped buildPhase/installPhase — is
  now gone.
- librsvg native dep (#424) preserved on all three derivations. Added
  pkgs.git too — naersk auto-included it; crane is more minimal, so
  hive-c0re's `lifecycle::tests::setup_proposed_*` (which shell out to
  `git init`+commit) need it explicit to pass under `cargo test` in
  the sandbox.
- build.rs + hive-c0re/Cargo.toml comments updated from "naersk
  derivation" to "crane derivation".
- 3 doc-list-indentation lints in hive-sh4re/src/lib.rs cleaned up
  (replaced `+`-at-line-start with `and`/`/` so doc continuations
  don't trigger `clippy::doc_lazy_continuation`).

Validated locally: `nix build .#default --fallback` succeeds, all
117 tests pass, all four bins in `result/bin/`.
2026-05-29 01:45:48 +02:00
damocles
b652e6a6b0 hive-matrix: always use matrix.<domain> subdomain (mara on #552) 2026-05-29 01:31:40 +02:00
damocles
5a4eb3e053 hive-matrix: stateVersion 26.05 + drop premature federationPort firewall (argus nits #552) 2026-05-29 01:28:46 +02:00
damocles
2b1c1b54ac nix: add hive-matrix module + hyperhive.domain option (#548 part 1) 2026-05-29 01:25:29 +02:00
damocles
b43438d1a6 hive-forge: collapse autogenerated diffs to a single one-line summary (#222) 2026-05-29 01:16:51 +02:00
lexis
c9f537c2e3 docs: forge_notify.rs file map + wait_for_login mtime resumption (follow-up to #544 #545) 2026-05-29 00:34:11 +02:00
lexis
4a2b2fca4f docs(web-ui): logs flyout fills side-panel height (follow-up to #543) 2026-05-29 00:22:19 +02:00
damocles
994166b20c turn: snapshot file_count alongside mtime to survive unreadable meta.modified() 2026-05-28 20:08:27 +02:00
damocles
6e833b22d6 turn: wait_for_login resumes only on credentials mtime bump (closes #542) 2026-05-28 20:08:27 +02:00
damocles
b5be139fb7 forge_notify: diff mentions against raw excerpt to avoid heading-mention duplication 2026-05-28 20:08:05 +02:00
damocles
4b30f20dcf forge_notify: embed issue/pr body excerpt + surface truncated mention lines (closes #539) 2026-05-28 20:08:05 +02:00
iris
8bbb3e4905 tabs: logs flyout fills the side-panel height (closes #541)
The journald viewer opens in the side panel, but the <pre> only takes
the height of its content — so a short log fetch leaves the bottom 80%
of the panel empty, and a long one pushes the controls past the
viewport. Wrap the body in a column-flex container that fills the
side-panel-body, keep controls fixed at the top, and let the <pre>
flex-grow into the rest of the panel with its own overflow:

  .journal-body { display: flex; flex-direction: column; height: 100%; }
  .journal-controls { flex: 0 0 auto; }
  .journal-output { flex: 1 1 0; min-height: 0; overflow: auto; }

`min-height: 0` is the canonical "let me actually flex-shrink for
overflow" escape hatch on flex children. Also moved the auto-scroll
target from side-panel-body onto the <pre> itself — the panel-body no
longer overflows now that journal-body fills it; the <pre> is the new
scroll surface.

Pure CSS + one JS line; no DOM shape change.
2026-05-28 18:54:02 +02:00
damocles
2d9109d1bd docs(web-ui): describe new single-table schedules layout (#124#535) 2026-05-28 15:25:32 +02:00
iris
42a2503834 tabs: render schedules as a single table with tilted agent columns (closes #535)
Replaces the per-schedule card layout with one table:

  | # | src | next | every | owner | body | …agents… | actions |
  |---|-----|------|-------|-------|------|----------|---------|
  | 5 | op  | 5m   | 10m   | mara  | "…"  | ✓ ✓ . ✓ |  ↯ ✎ ✕  |

Each schedule is one <tr> in the tbody. Agent columns are dynamic —
operator + manager + live containers + any "extra" name that appears
as a target on some schedule but isn't a current container (same
membership rule buildTargetChips uses, so the table + new/edit forms
agree on what's addressable). Per-agent cells:

  - active target  → <button>✓</button> that cancels just that target
    on click (replaces the per-row ✕ from the old targets sub-table)
  - cancelled target → muted ✕ glyph (no button; re-add flows through
    the edit form's targets multi-select)
  - not a target → empty cell

Agent column headers tilt -45° via CSS so each column reads as 28px
of horizontal real estate instead of the full word width. Standard
rotated-header pattern: 95px-tall <th> with position:relative, inner
<div> positioned absolute at bottom-left, transform rotates about
left-bottom.

Body cell truncates with ellipsis + full-text title. Description used
to be a separate visible block on the card layout; the table folds
it into the body cell's title to keep row height tight. If mara wants
description visible in-table it's a small follow-up — easier to
iterate on a rejection.

Edit form expands inline into a colspan'd row underneath the schedule
row it edits (instead of inside the card). Wrapper drops the form's
background so it reads as a row extension.

No backend changes; everything renders from existing schedulesState
+ containersState.
2026-05-28 14:22:39 +02:00
iris
78a503d8b9 nix/frontend: refresh stale dist-layout comment
The output-layout block was written before:
- the dashboard's app.js → tabs.js rename (#495)
- the flow.html / flow.js page (#406 / #485)
- the SharedWorker stream-worker.js (#448)
- the build.mjs split-into-static/ subdir convention

Brings the comment in line with what `find dist -type f` actually
prints for both packages. Pure documentation refresh; install phase
and build hashes untouched.
2026-05-28 14:17:52 +02:00
iris
bc1c910044 tabs: extract scheduleField() helper — collapse 8 label/caption sites
Every field on the new- and edit-schedule forms (plus buildIntervalComposer
and buildTargetChips) builds the same wrapper shape:

    el('label', { class: 'schedule-field' },
      el('span', { class: 'schedule-field-label' }, '…'))

then appends an input. Eight identical-modulo-text sites. Extracting one
small helper:

    function scheduleField(labelText, ...children) {
      return el('label', { class: 'schedule-field' },
        el('span', { class: 'schedule-field-label' }, labelText),
        ...children);
    }

drops the per-site cost to a single `form_.append(scheduleField('caption',
input));` line. -10 LOC net; no behaviour change.
2026-05-28 14:13:35 +02:00
damocles
1d7feeb029 hive-forge: pr-create --push opts into branch push + hint suppression (#222) 2026-05-28 01:08:43 +02:00
damocles
def06fb388 docs(web-ui): SharedWorker heartbeat + watchdog (#124 follow-up to #515) 2026-05-28 00:57:15 +02:00
damocles
800d9925a5 prompt: matched-close check + drop dead-code guard (argus #527 nits) 2026-05-28 00:33:43 +02:00
damocles
1c8e72c81f docs(CLAUDE.md): file map reflects unified prompts/system.md + src/prompt.rs 2026-05-28 00:33:43 +02:00
damocles
01af5003d1 harness: unify agent + manager prompts into single template (closes #519) 2026-05-28 00:33:43 +02:00
iris
88476976b7 stream-worker: honest comment on heartbeat catch (argus nit on #529)
argus pointed out the inline catch comment claimed dead ports get cleaned up on
next subscribe, but nothing actually prunes the allPorts Set on subscribe — the
honest answer is the one already at the bottom of onconnect: dead entries are
left in the Set, the bound cost is acceptable, and postMessage's throw is the
ambient signal we use. Point at that comment instead of repeating a wrong
description.
2026-05-27 23:40:38 +02:00
iris
b9df538940 frontend/dashboard: heartbeat + watchdog so a dead SharedWorker self-heals (#515)
Firefox kills "idle" SharedWorkers under memory pressure with no native
signal to the client. The page's port silently becomes a no-op and
events stop flowing — observable symptom: mara's "dashboard never
refreshes; F5 fixes it" (because F5 creates a fresh page that creates
a fresh worker).

The worker now pings every connected port every 30s. The client tracks
last-activity-from-worker on every message arrival (incl. pings, since
those carry no URL — bumped before the URL filter in the route handler).
A visibility-gated watchdog polls every 15s; if the page is visible AND
has active subs AND hasn't heard from the worker in >90s, it presumes
the worker dead, logs a console warning, and re-subscribes on a fresh
port. Three pings missed before we act, so a normal tab-throttle blip
doesn't false-positive.

The fresh-port re-subscribe re-uses the bfcache-restore code path
(same shape: drop stale listeners, getSharedPort → new SharedWorker,
re-attach each route + repost subscribe). Recovery is per-tab — when
one tab's watchdog fires and brings up a new worker, other tabs that
share the named worker pick it up on their own watchdog cycle.

Falls back gracefully on environments without SharedWorker (the
existing direct-EventSource path is untouched) and is invisible on the
healthy path — pings are 30s apart, no UI surface.
2026-05-27 23:37:21 +02:00
damocles
c786b9ec37 docs(persistence): broker.sqlite is six tables now (#124 follow-up) 2026-05-27 23:07:15 +02:00
lexis
29d8c264ca docs: update README flake example to nixos-26.05 2026-05-27 22:12:32 +02:00
damocles
308b2b4773 docs: point upstream URL at public mirror at forge.darkest.space (closes #523) 2026-05-27 22:09:42 +02:00
damocles
7d4811504a forge_notify: strict ATX detection + preserve trailing newline (argus nits on #518) 2026-05-27 19:39:39 +02:00
iris
e8c86bef3b tabs: dedup target-chips + interval-parse between new/edit schedule forms
mara: \"look through the code for dedups, structural improvements
and so on\". Two near-identical blocks across the new-schedule and
edit-schedule forms folded into shared helpers.

## `buildTargetChips({ idPrefix, fieldName, checked, extraNames })`

Was inlined twice in 18-line blocks that built the same
`<label class="schedule-field">` + `<div class="schedule-targets">`
+ candidate-list logic (containers + operator + manager). Now one
function, two callers; `extraNames` lets the edit form keep
showing already-active targets that have vanished from the live
container list so the operator can still uncheck them
intentionally.

## `intervalSecondsFromFormData(fd, namePrefix)`

Both submit handlers had the same ~13-line d/h/m/s → total-seconds
parser (with `NaN` propagation on bad input). Pulled into one
helper next to `buildIntervalComposer`; call sites become 3 lines:

  const intervalTotal = intervalSecondsFromFormData(fd, 'interval_');
  if (Number.isNaN(intervalTotal)) { alert(...); return; }
  const interval_seconds = intervalTotal > 0 ? intervalTotal : null;

Net -13 lines, but the bigger win is shape — when (not if) a new
schedule field surfaces, there's one chip-render path + one
interval-parser to thread it through instead of two.

Zero behaviour change. Built clean.
2026-05-27 19:22:59 +02:00
damocles
12f2c8311e forge_notify: escape ATX headings in embedded body so they don't blow into wrapper h1/h2 (closes #455) 2026-05-27 19:22:39 +02:00
damocles
0460bbe9ec docs: ContainerCrash mentions recent-transient grace (#124 followup to #425) 2026-05-27 19:19:08 +02:00
damocles
dca62fda40 harness: remove deprecated manager-side request_spawn surface (closes #442) 2026-05-27 18:58:04 +02:00
damocles
03d39788d9 prompts: cancel_loose_end approval kind + lint verb in hive-forge list (#124) 2026-05-27 17:06:40 +02:00
damocles
dfc944d5fa flake: expose agent-base + manager toplevels as packages, opt-in pre-build via hive-c0re module (closes #97) 2026-05-27 14:37:50 +02:00
damocles
c62c73e546 harness: anchor 'update allow-list' reminder on both #[tool_router] impls (argus nit on #511) 2026-05-27 14:26:04 +02:00
damocles
e02b279785 harness: add 6 missing manager tools to allow-list (unblocks #509) 2026-05-27 14:26:04 +02:00
damocles
aba1d3153e crash_watch: suppress crash event on recently-cleared transient (closes #425) 2026-05-27 14:24:13 +02:00
lexis
8dca741f09 docs: add lint verb and approval cancel_loose_end (follow-up to #507 #508) 2026-05-27 12:53:46 +02:00
damocles
402ee95d0c questions: unit-test the manager-only guard on approval cancel (argus nit on #508) 2026-05-27 12:48:30 +02:00
damocles
c1f27e3b7b approvals: manager can withdraw pending approvals (closes #250) 2026-05-27 12:48:30 +02:00
damocles
f866f1822e hive-forge: require --reviewer explicitly for lint no-reviewer (mara nit on #507) 2026-05-27 12:01:54 +02:00
damocles
8f4cb3ed29 hive-forge: paginate no-reviewer comment fetch (argus nit on #507) 2026-05-27 12:01:54 +02:00
damocles
703018106a hive-forge: add lint verb for triage queries (closes #505) 2026-05-27 12:01:54 +02:00
lexis
c11424dd9a docs: add step field to QueueEntry in CLAUDE.md file map (follow-up to #501) 2026-05-27 10:17:24 +02:00
iris
1dfcc8953c rqe-step: drop tabular-nums (no digits in step names)
argus 🟡 on #504 — `font-variant-numeric: tabular-nums` has no
effect on `.rqe-step` since step strings are text-only
("nixos-container update" / "meta prepare_deploy" / etc.),
no digit columns to align. Harmless but cleaner without it.
2026-05-27 02:00:42 +02:00