Commit graph

1,063 commits

Author SHA1 Message Date
damocles
02d458cdf5 agent_sockets: address argus 🟡 on #832 (doc attribution + drop unused arg)
argus on PR #832:
- 🟡 spawn_poll was inserted BETWEEN write's closing doc and the
  pub fn write line; rust treated the consecutive /// as one block,
  so spawn_poll inherited write's tail and write ended up with no
  closing doc. moved spawn_poll AFTER write to fix attribution.
- 🟡 spawn_poll(coord) took Arc<Coordinator> just to drop it
  immediately. dropped the param; main.rs call site now just
  agent_sockets::spawn_poll().

no functional change. 10 tests still pass.
2026-05-31 16:28:36 +02:00
damocles
90c72d9131 docs/gateway.md: section on the #784 unix-socket upstream chain (mara on #832) 2026-05-31 16:28:36 +02:00
damocles
5e0cb5e0f5 hive-c0re + harness: filter agent-sockets.json by .bound marker (#784, atlas concern)
closes the gate atlas raised on PR #813: without per-agent opt-in
signal, agent-sockets.json listed every sub-agent, and any agent
that hadn't flipped hyperhive.web.useUnixSocket would 502 the
gateway (its harness still binds TCP, no socket at the published
path).

harness side (web_ui::bind_unix):
- after successful bind + chmod, drop a `.bound` marker in the
  per-agent dir as a stable 'this agent has a unix socket here'
  signal. best-effort: a failed marker write logs at WARN but
  doesn't abort serve (the socket still binds fine; gateway just
  keeps using TCP for one more poll).

c0re side (agent_sockets):
- new READY_MARKER const + ready_marker_for(name) helper
- build_map filters by ready_marker_for(name).exists() — only agents
  whose harness has bound the socket appear in the JSON map
- new build_map_with<F> internal extracts the predicate so tests
  pass a controlled is_ready closure (no real fs access)
- new spawn_poll() background task: re-fires agent_sockets::write
  every 10s so the JSON catches up to fresh markers without
  needing a container-start hook. write() idempotency means
  steady-state cost is one stat per agent per tick.

10 tests: 6 prior + new build_map_filters_by_ready_predicate +
ready_marker_path_is_sibling_of_socket. existing tests adjusted to
call build_map_with(_, |_| true) since the default path now hits
the fs.

once this lands + #822 lands, atlas's gateway-side step 3 can drop
its eval-time `pathExists` fallback — c0re only publishes opted-in
agents, so the gateway can trust the JSON unconditionally.
2026-05-31 16:28:36 +02:00
iris
777d26812a hive-sh4re + docs: extract AgentMeta wire-shape prose (#717 batch 8) 2026-05-31 16:25:10 +02:00
iris
29fda64866 docs/approvals.md: split sha vs tag scoping per argus on #833 2026-05-31 16:25:03 +02:00
iris
f3fefe7f7e hive-sh4re + docs: extract HelperEvent prose (#717 batch 7) 2026-05-31 16:25:03 +02:00
damocles
d59bfb899f hive-forge: drop boilerplate # Errors from pure-GET verbs (mara on #827, option A)
mara: 'those comments seem very redundant'. true — the 16 pure-GET
verbs all got the same 'transport error + stdout I/O' boilerplate,
which just restates the Result<()> contract that's trivially
derivable from the type.

dropped # Errors from: assign, branches, close, comment_show,
comments, diff, issue, labels, lint, list, milestone, pr,
pr_reviews, subscription, timeline, tree_sha, view (17 files).

kept on the 7 verbs that have a non-Forgejo failure surface worth
documenting:
- comment, comment_edit, issue_create, issue_edit — body input I/O
  via --body-file / stdin
- pr_create — body input + --push shellout to git
- attach::run_issue, attach::run_comment — explicit bail! on
  missing file

net: 23 verbs touched in the original PR → 17 trimmed back to
no-doc, 6 kept (with the 7th call being attach::run_comment in the
same file). 38 tests still pass.
2026-05-31 16:22:05 +02:00
damocles
1195bfbe11 hive-forge: add # Errors docs on every verb's pub fn run (closes #816)
systemic gap argus flagged on PR #798 (timeline verb). every `pub fn
run` in hive-forge/src/verbs/*.rs lacked a `# Errors` block —
violates Rust API guidelines + obscures the failure surface for
operators reading the source.

uniform doc per verb category:
- pure GET + print verbs: "transport error from the Forgejo REST call
  + I/O error from stdout"
- body-from-file verbs (comment/comment_edit/issue_create/issue_edit/
  pr_create): adds 'I/O error from --body-file/stdin input'
- file-upload verbs (attach-issue, attach-comment): adds 'file
  read/exist check'
- pr_create: also mentions the --push shellout

23 `pub fn run` signatures touched. no behaviour change; pure
documentation sweep. cargo test green (38 tests).
2026-05-31 16:22:05 +02:00
iris
a12c097044 hive-sh4re + docs: extract Ask/Answer routing prose (#717 batch 2) 2026-05-31 16:18:01 +02:00
atlas
3958079a8e harness-base: pin hive-dashboard-links doc ref to section anchor (argus 🟡 on #823) 2026-05-31 16:14:38 +02:00
atlas
a043c61828 docs: extract best-effort oneshot service contract + matrix-avatar (#718 batch 6)
Two new sections housing rationale that was inline in
`harness-base.nix`:

- `docs/conventions.md::Best-effort oneshot services` — shape
  contract shared by `tea-login`, `forge-avatar-sync`, and
  `matrix-avatar-sync`: always exit 0, no `set -e`, skip silently
  on missing prerequisites, wired to multi-user.target,
  re-runnable. Plus the artefact-under-agent-home + service-stays-
  root posture and the operator-visible-via-journalctl trade-off.

- `docs/persistence.md::matrix-avatar-sync` — the two-step `media
  upload` → `set avatar_url` matrix-spec dance + why
  `RemainAfterExit = false` (so the `.path` watcher's re-fire on
  token appearance actually re-executes the unit).

In-code comments trim to one-liner purpose + pointers; the script
bodies stay (they're the actual implementation) but their inline
`#` rationale comments collapse where the docs cover them.

`description = ''…''` blocks (operator-facing options docs)
preserved per iris #718.

`nix flake check` clean; `nix fmt` clean.
2026-05-31 16:14:38 +02:00
iris
fe6f96397e hive-sh4re: trim HostRequest::SetParent rustdoc (#717 batch 6) 2026-05-31 16:14:18 +02:00
iris
18d0fbfecb hive-sh4re + docs: extract Wake-injection prose (#717 batch 5) 2026-05-31 16:14:07 +02:00
atlas
e1bc9e42f1 hive-gateway: tmpfiles rule for /run/hive-agent (argus 🟡 on #829)
Defensive against the fresh-boot-before-any-agent-spawn window where
the bind-mount source wouldn't exist from c0re's per-agent
`set_nspawn_flags` mkdir chain yet. nspawn auto-creates missing
sources, so this is intent-explicit rather than functional fix.
2026-05-31 16:14:02 +02:00
atlas
3a29aee001 nix/hive-gateway: UDS upstream for /agent/<name>/ (#784 phase 2 step 3)
Switch per-agent gateway upstreams from TCP loopback to unix-domain
socket when the agent has opted in via `hyperhive.web.useUnixSocket`
(#822). Coexists with the TCP path during rollout.

Changes:

- New `agentSocketsFile` option (default
  `/var/lib/hyperhive/agent-sockets.json`) — c0re writes the map
  there via `hive_c0re::agent_sockets::write` (#809).
- `agentSocketsTable = lib.importJSON ...` (graceful empty when
  file missing).
- `agentUpstreamFor name port` picks `http://unix:<path>:/` when the
  socket has a JSON entry AND the file exists at eval time; else
  `http://127.0.0.1:<port>/`. Path-exists gate guards against
  c0re's blanket-emit shape during the canary window (agents in
  `agent-sockets.json` who haven't actually flipped have no
  bound socket on disk → fall back to TCP). Damocles will ship a
  `.bound` marker filter on the c0re side (#784 step 2d
  follow-up); once that's in, the path-exists check is redundant
  but harmless. Step 4 drops it entirely along with the TCP
  fallback.
- `containers.hive-gateway.bindMounts."/run/hive-agent"` —
  read-only, unconditional. Inert when no agents have opted in.
  Required so nginx inside the gateway container can `connect(2)`
  to the per-agent sockets damocles's #813 bind-mounts into agent
  containers at the same paths.

Docs:

- `docs/gateway.md::Per-agent UDS upstream (#784)` — full rollout
  flow, subdir-bind rationale (damocles #813), eval-time gate
  explainer, step 4 drop plan.

`nix flake check` clean; `nix fmt` clean.

Canary plan: once #822 (`useUnixSocket` option) lands + this PR
merges, manager flips atlas's agent.nix to `useUnixSocket = true`
via the config-update flow. End-to-end validation against atlas
before broader rollout.
2026-05-31 16:14:02 +02:00
iris
22b3e542c5 hive-sh4re + docs: extract LooseEnd wire-shape prose (#717 batch 4) 2026-05-31 16:13:24 +02:00
iris
a0b15ed6a4 hive-sh4re + docs: extract Approval lifecycle prose (#717 batch 3) 2026-05-31 16:12:36 +02:00
lexis
eacfa9bdb3 docs(CLAUDE.md): add agent_sockets.rs to hive-c0re file map (follow-up to #809) 2026-05-31 16:12:17 +02:00
damocles
e04b6075e5 harness-base: hyperhive.web.useUnixSocket option to flip HIVE_WEB_SOCKET (#815)
closes #815. last harness-side piece of the #784 phase 2 rollout.

new option `hyperhive.web.useUnixSocket` (default false). When true,
the harness service env gets HIVE_WEB_SOCKET set to the canonical
/run/hive-agent/${userName}/web.sock path — making web_ui::serve
bind a UnixListener (PR #800) instead of TCP. Path matches
hive_c0re::agent_sockets::socket_path_for(name) so the c0re
bind-mount (#813) and the gateway's eventual upstream config all
derive from the same canonical shape (no triangulation drift).

Default false so an agent's web UI keeps binding TCP until the
per-agent flip is explicit. Rollout shape:
1. flip one canary agent (atlas volunteered) to true via agent.nix
2. validate atlas's gateway-side step 3 against that canary
3. flip remaining agents per-agent as the gateway side soaks
4. eventually drop this option once everyone's on unix (step 4 of #784)

Sub-agent-only by design — manager UI serves at / via the c0re
dashboard upstream, never via /agent/<name>/, so the per-agent
unix path is irrelevant for it (the env var is set unconditionally
for code simplicity; the manager's bind socket would just sit
unused).
2026-05-31 16:09:31 +02:00
iris
a443108be5 hive-sh4re + docs: extract broker delivery/ack/requeue prose (#717 batch 1) 2026-05-31 15:53:00 +02:00
iris
33683f2de2 shared/terminal.css: drop stale hive-fr0nt ref; point at docs (#714) 2026-05-31 15:49:53 +02:00
lexis
68c6a5ae19 docs(CLAUDE.md): add matrix.md to file index + reading paths (follow-up to #811) 2026-05-31 15:49:42 +02:00
iris
7bb55bca43 docs/agent-hierarchy.md: scrub self-ref cookies (#719 batch 5) 2026-05-31 15:49:37 +02:00
iris
bd6b48a883 docs/persistence.md: scrub self-ref cookies (#719 batch 4) 2026-05-31 15:49:32 +02:00
iris
680d13b9e9 docs/gotchas.md: scrub self-ref cookies (#719 batch 3) 2026-05-31 15:46:02 +02:00
atlas
8a50f36c0c docs/agent-hierarchy.md: extract harness systemd unit shape (#718 batch 4)
Move the harness systemd unit rationale (role-driven unit name,
manager-only forge defaults, PATH wrapper-dir trick, env vars,
standalone-eval fallbacks, RuntimeDirectory + User= reasoning)
from `nix/templates/harness-base.nix` to a new
`docs/agent-hierarchy.md::Harness systemd unit shape (per-role)`
section.

In-code comments trim to short purpose statements + pointers; the
PATH /bin auto-append behaviour is already documented in
docs/gotchas.md, so the harness file just cross-refs both.

`description = ''…''` blocks (operator-facing options docs)
preserved per iris #718.

`nix flake check` clean; `nix fmt` clean (after formatter pass).
2026-05-31 15:44:41 +02:00
damocles
35a7ff03b7 hive-c0re: bind-mount /run/hive-agent/<name>/ per sub-agent (#784 phase 2 step 2b)
builds on step 2a (#809). lifecycle::set_nspawn_flags now adds a
--bind={socket_dir}:{socket_dir} flag per sub-agent so the harness's
HIVE_WEB_SOCKET bind (PR #800) lives in a dir both the agent
container and the host can see.

design (matches #809's a1a601d explanation):
- bind the SUBDIR, not the socket file. file bind-mounts drop on
  unlink; the harness's bind_unix unlinks any stale socket before
  binding, so a file bind would land the new socket in the agent's
  private namespace, invisible to the gateway. dir bind keeps both
  sides on the same dir inode.
- per-agent dir (one /run/hive-agent/<name>/ per agent, not a shared
  /run/hive-agent/ mount). The agent's container only sees its own
  subdir — never siblings' (mara on #800).
- manager skipped — the manager's UI serves at / via the c0re
  dashboard upstream, not via /agent/<name>/, so it never needs the
  per-agent socket dir.

mkdir source defensively before bind: nspawn refuses to start when
the bind source is missing, and /run/hive-agent/ doesn't exist on
fresh hosts.

remaining work in this phase:
- step 3 (atlas): gateway proxy_pass http://unix:/run/hive-agent/<name>/web.sock:/
- per-agent: flip HIVE_WEB_SOCKET in agent.nix to opt in (separate PRs)
- step 4 (later): drop TCP fallback once everyone's flipped
2026-05-31 15:44:26 +02:00
iris
5ca96b8c85 docs: scrub self-ref cookies across 5 files (#719 batch 2)
All five files scrubbed to 0 issue-ref cookies. Most refs were
'(closes #NNN)' style attribution after closed work landed.

- docs/turn-loop.md: 6 → 0 (#598 unified binary, #419 auth-fail,
  #542 mtime resumption, #519 role markers, #442 spawn path,
  #474/#478 partial edit)
- docs/approvals.md: 5 → 0 (#317 flake validation, #250 withdraw,
  #441 rebuild-queue dispatch, #753 manager port hash, #425
  crash-watch grace)
- docs/terminal-rendering.md: 2 → 0 (#666 ask-operator inline)
- docs/conventions.md: 3 → 0 (#753 port hash, #692 parent
  sentinel, #486 reparenting)
- docs/security.md: 4 → 0 (#240 + #658 ×2 + #673/#678 attribution)

Combined with PR #810 (docs/web-ui.md 14→0), this PR closes the
small / medium files. Remaining: agent-hierarchy.md (13),
gotchas.md (13), persistence.md (13). gateway.md is atlas's lane.
2026-05-31 15:39:09 +02:00
atlas
a8d8159038 docs/matrix.md: extract hive-matrix prose (#718 batch 5)
New top-level matrix doc covering everything that doesn't fit
elsewhere:

- Container shape (nixos-container, shared host netns, name choice,
  state persistence) — sibling to gateway.md::hive-forge container shape.
- Identity vs API listener: serverName vs gatewayHost split with the
  #660 breaking change.
- Default-closed firewall + federation port 8448 caveat.
- Provisioning flow: registration token activation, bind-mount,
  LoadCredential, hive-c0re's per-agent register + access_token
  persistence. Captures #565 first-boot race + #644 / iris 8043
  ownership shape.
- Assertion rationale (serverName, gatewayHost == "").
- fluffychat-web build fixes (#685): Imaging.{js,wasm} emscripten
  derivation + dart compile worker fixup, build-CWD path lesson
  from #685 / #733.
- Sequencing history.

In-code # comments trim to short purpose statements + docs pointers.
description = '' blocks (operator-facing options docs) preserved per
iris #718. ~140 lines removed from hive-matrix.nix.

`nix flake check` clean; `nix fmt` clean.
2026-05-31 15:38:43 +02:00
iris
ac83404f1c docs/web-ui.md: scrub self-ref cookies (#719 batch 1)
14 issue-ref cookies removed from docs/web-ui.md. Most were
attribution refs to closed issues that landed the feature being
described — the prose around them already describes the current
behaviour, the cookie was just a 'this was issue NNN' breadcrumb.

Scrubbed:
- #753 (manager port hash 8100-8999)
- #233 (linkify XSS)
- #448 (SSE multiplexing SharedWorker)
- #515 (worker-death self-heal)
- #451 (side-panel drag-to-resize)
- #635 (matrix.gui.enable defaults)
- #609 + #15 (gateway re-root) → 'lives in docs/gateway.md'
- #66 (ctx badge thresholds)
- #541 (journald panel-body column-flex)
- #447 (rebuild-queue cancel)
- #559 (mark all read)
- #474 (schedules PATCH partial edit)
- #467 (schedules fire-now)
- #589 (hive-qualified label phase A)

docs/web-ui.md: 14 → 0 issue-ref cookies. Self-contained read.
2026-05-31 15:38:28 +02:00
damocles
91f5588134 hive-c0re: per-agent subdir socket layout (#809 design fix)
was: /run/hive-agent/<name>.sock (flat single-file bind-mount).
issue: file bind-mounts don't survive the harness's 'unlink stale
socket then bind(2) a new one' cycle. The unlink drops the bind
inside the container; the rebind happens in private container
namespace; host never sees the new inode → gateway can't connect.

now: /run/hive-agent/<name>/web.sock (per-agent SUBDIR + fixed
filename). Lifecycle bind-mounts the parent dir per agent (step 2b)
so both sides see the same dir inode; the socket appears on the
host the moment the harness binds it.

new helpers:
- AGENT_SOCKET_DIR const (parent, gateway binds this whole tree)
- SOCKET_FILENAME const ("web.sock")
- agent_dir_for(name) (per-agent subdir, lifecycle bind-mounts this)
- socket_path_for(name) (= agent_dir_for(name).join(SOCKET_FILENAME))

per-agent dir isolation also satisfies mara on #800 directly:
agent's container only sees its own subdir + socket, never siblings'.

8 tests now (added agent_dir_for_is_socket_parent invariant).
2026-05-31 15:38:04 +02:00
damocles
c7a8cec2b5 hive-c0re: agent_sockets.json writer (#784 phase 2, step 2a)
sibling to agent_ports.rs (#748 / #15). Writes
/var/lib/hyperhive/agent-sockets.json mapping agent name → unix
socket path under /run/hive-agent/<name>.sock — the source of truth
for the gateway's eventual unix-socket upstreams once it flips off
TCP per #784's transition plan.

shape mirrors agent_ports.rs:
- BTreeMap for deterministic JSON output
- atomic <path>.tmp + rename for partial-write safety
- idempotent (skip rewrite when content unchanged)
- MANAGER_NAME filter (gateway routes manager via dashboard upstream,
  not /agent/<n>/)

socket_path_for(name) is pure-deterministic so a name alone resolves
to the same path the c0re writes + the harness binds via
HIVE_WEB_SOCKET (post-PR #800).

hooked into meta::sync_agents alongside the existing
agent_ports::write call (same best-effort + non-fatal shape).

next: step 2b (lifecycle bind-mount /run/hive-agent/ into agent +
gateway containers, with per-agent isolation per mara's #800 nudge
'agents can only access their own sockets'). then atlas's step 3
(gateway proxy_pass http://unix:...). then step 4 drops TCP.
2026-05-31 15:38:04 +02:00
iris
dc8c71c687 dashboard: fix rebuild spinner shape (#804)
Mara reported the rebuild spinner rendered as a rotating L-corner
('_|' / '⌐') instead of a smooth orbiting arc. Two issues:

1. The previous version coloured TWO adjacent border sides amber
   (border-top + border-right). With the rest of the border
   transparent, this paints a sharp L-shape at the icon corner —
   which when rotated reads as a spinning border-corner, not a
   loading spinner.

2. @keyframes had only `to` defined. Safer to include explicit
   `from { transform: rotate(0deg) }` so any browser that doesn't
   default cleanly still picks up the rotation.

Fix is the classic CSS spinner shape: faint amber ring around the
full icon perimeter (`border: 2px solid rgba(250, 179, 135, 0.2)`)
with one brighter top arc (`border-top-color: var(--amber)`) that
rotates. Reads unambiguously as a loading indicator.

Closes #804.
2026-05-31 15:36:38 +02:00
iris
4cee50a3be agent/app.js: final cookie scrub — 0 cookies (#713 batch 3 final)
All 19 remaining cookies in app.js were attribution refs to
closed issues / PRs / past mara/argus reviews. Substance is
already in docs from earlier batches (PR #780). This batch
collapses the cookie attributions to brief inline notes pointing
at docs where appropriate.

- #360 side-panel duplication note → drop closed-issue framing
- #394 vibec0re header redesign (×5) → drop cookies, prose stays
- #589 hive-qualified label glyphic-title note → drop cookie
- #576/#542/#584 logout chain rationale → drop cookies, prose
  preserved (the WHY + .credentials.json + mcp-needs-auth-
  cache.json detail stays inline)
- #14 relative paths convention → docs/web-ui.md::Per-agent
  relative paths pointer
- #233 marked autolinks new-tab → drop cookie
- #559/#566 mark-all-read flyout (×3) → docs/web-ui.md::Per-agent
  page (Loose-ends flyout) pointer + drop attribution
- #666 ask→operator inline-answer slot (×2) → docs/web-ui.md::
  Per-agent page (Ask → operator inline-answer binding) pointer

app.js: 19 → 0 cookies (100% reduction). Combined with PR #780
+ batch 1 + 2, app.js is fully migrated for #713 scope.

Closes #713 — all agent UI files (app.js, agent.css, screen.html,
index.html, stats.js, stats.html) at 0 issue-ref cookies.
2026-05-31 15:36:23 +02:00
atlas
db2a48cde6 docs/gotchas.md: extract nix/{assets,docs,templates/weston-vnc} prose (#718 batch 3)
- assets.nix: cargo-cache-invalidation rationale → "Split asset
  derivations away from the rust workspace" section.
- templates/weston-vnc.nix: port allocation, weston bind-address
  quirk, PAM service name, Type=simple choice, idle-time=0 →
  "Weston VNC compositor (per-agent hyperhive.gui.enable)" section.
- docs/default.nix: rendering pipeline + subtree-pick + output-tree
  history → "Nix options reference" section.

In-code comments trimmed to short purpose statements + docs pointers.
description = '' blocks (operator-facing options docs) preserved per
iris #718.

`nix flake check` + `nix build .#docs` clean.
2026-05-31 15:24:39 +02:00
iris
f60a90d752 shared terminal + docs: migrate sticky-scroll + backfill prose (#714 batch 1)
Substantial prose migration from @hive/shared/terminal.js (the
shared HiveTerminal factory backing #msgflow + #live across both
dashboard and per-agent UIs) into a new docs subsection.

Added to docs/web-ui.md as a new ### Shared terminal pane
subsection under ## Shape (shared by both) — ~78 lines of new
substantive prose:

- **api shape**: row / details / detailsDiff factory contract
- **Sticky-bottom + snap animation**: stickToBottom semantics +
  140ms ease-out vs 500ms browser default + 24px short-circuit;
  per-frame target re-eval extends destination through
  mid-animation mutations
- **Mid-animation scroll-event guard**: smoothScrollingUntil
  timestamp swallows the rAF-driven scroll events so the eased
  positions don't flip stickToBottom false partway
- **Post-append MutationObserver**: catches renderer mutations
  after api.row returns (badges, multi-line bodies, tool panes)
  + why programmatic scrollTop writes don't feedback-loop
- **Backfill + SSE**: history/stream envelope shape (seq, events),
  kind-aware seq dedupe at the boundary, .no-anim during replay,
  optional streamFactory for SharedWorker integration
- **linkify**: text-node-only autolink, XSS-safe by construction,
  trailing-punctuation strip

Collapsed in terminal.js (cookies en passant):
- #400 (snap animation timing — closed) × 3 → docs pointers
- #393 (post-append MutationObserver — closed) × 1 → docs pointer
- #375 (pre-append nearBottom snapshot — closed) × 1 → docs pointer
- #448 (streamFactory SharedWorker hook — closed) × 1 → docs pointer
- #163 (seq dedupe + onStreamOpen resync — closed) × 2 → drop
  cookies; substance lives in docs
- #233 (linkify) × 1 → docs pointer + terminal.css cookie scrub

terminal.js: 8 → 0 #NNN cookies (100% reduction).
terminal.css: 1 → 0 issue-ref cookies (remaining 1 match is a
hex color literal).
Net effect: ~50 lines of substantive WHY-prose moved out of
shared frontend into docs/web-ui.md, where it documents the
factory's contract for both consumer pages.
2026-05-31 15:18:31 +02:00
iris
b15c534e67 tabs.js: final cookie scrub — 0 issue refs (#712 batch 14)
Final pass on tabs.js. All 6 remaining cookies were either
attribution refs to closed issues / past reviews or pure-history
mile-markers:

- #259 disabled meta-update state → drop attribution
- dashboard.rs#2170 backend handler line ref → drop the line
  number (handler still lives in dashboard.rs, line drifts)
- mara on #695 M0V3 picker any-selection → drop attribution
  (substance already captured in docs/web-ui.md::Selection bar)
- #541 journald scroll surface → drop the migration history
  framing, keep present-state behaviour
- argus on #471 textContent vs innerHTML review → drop the review
  attribution, keep the safer-pattern rationale inline
- #369 tab routing section header → drop the cookie, keep the
  section divider

tabs.js: 6 → 0 issue-ref cookies (100% reduction). Across
batches 1-14 (#712): 73 → 0 issue-ref cookies, with substantive
prose (Topology tree CSS geometry, Container row icon layout,
Pending-state derivation, Selection bar, R3BU1LD QU3U3 cancel +
step annotations, Approval card requested-at, SSE multiplexing
worker-death self-heal, FL0W page MESS4GE FL0W body layout)
migrated to docs/web-ui.md across the batches.

dashboard SPA files all at 0 cookies now (PR #795 covers the
other dashboard files; this PR covers the tabs.js residual).
2026-05-31 15:14:53 +02:00
damocles
0ef79b8032 harness: opt-in UnixListener bind via HIVE_WEB_SOCKET (#784 phase 1)
phase 1 of #784 (unix-domain agent web UI for #14 prerequisite).
backwards-compatible: when HIVE_WEB_SOCKET is set + non-empty, bind a
UnixListener at that path instead of the legacy TCP bind on HIVE_PORT.
empty env var treated as unset so a stray HIVE_WEB_SOCKET= doesn't
trap an un-bindable empty path.

bind_unix helper:
- mkdir -p the socket parent (covers first-boot fresh /run/hive-agent/
  bind-mount target)
- best-effort unlink of stale socket (clean exit removes it, crash
  leaves it; bind(2) refuses to overwrite)
- mode 0o660 so gateway peers in the same unix group can connect (the
  bind-mount source dir ACL is the real gate; perms are defence in
  depth)

axum 0.8's Listener trait covers tokio::net::UnixListener directly
(no extra feature/dep required).

next phases (separate PRs):
- step 2: c0re bind-mounts /run/hive-agent/ + writes agent-sockets.json
  alongside agent-ports.json
- step 3: gateway proxy_pass http://unix:… (atlas)
- step 4: drop TCP bind once gateway no longer needs it
2026-05-31 15:14:46 +02:00
damocles
224385af37 hive-forge: extract format_event helper; tests call it directly (argus 🟡 on #798)
was: `print_event` did the format + println inline; tests had a
parallel `captured` helper that re-implemented the dispatch and only
covered 4 of 14 event types. brittle — a new arm in `print_event`
silently went uncovered.

now: pure `format_event(ev: &Value) -> String` builds the line;
`print_event` is a thin wrapper that adds the trailing newline.
tests assert on `format_event` output directly so every new arm
gets test coverage by construction.

bonus: added 3 more test cases (assignees add/remove, pull_push
commit count + force-push, commit_ref sha truncation) since the
helper extraction made them cheap. 9 tests total, all green.
2026-05-31 15:14:08 +02:00
damocles
8f9866f5e1 hive-forge: timeline verb — issue/PR audit trail (closes #783)
last unstarted piece of the original #694 epic. agents kept falling
back to curl for 'who closed this?' / 'when was this labelled?'
archaeology because view + comments only surface the body + comments,
not the structured timeline events (label adds, assignments, closes,
reopens, pushes, commit refs, review submissions, milestone changes).

separate verb rather than view --timeline because:
- composes naturally with view <n> / comments <n>
- keeps existing verb output shapes stable (no script breakage)
- argus on #770 already noted view's output is busy

human-readable by default ('**actor @ ts**: <summary>'), --json for
raw piping. unknown event types fall through to a '[<type>]'
placeholder so a forge schema bump doesn't panic the verb.

--tail N is a follow-up: timeline endpoint doesn't expose a total-count
field so the count-then-page strategy from #770 doesn't apply
directly.

6 tests cover comment / label add/remove / close / unknown-type
placeholder / missing-user fallback.
2026-05-31 15:14:08 +02:00
damocles
7cc59c4338 hive-forge: update --tail field doc to match count-then-page impl (argus on #770) 2026-05-31 15:12:41 +02:00
damocles
f57cf916a1 hive-forge: count-then-page strategy for comments --tail (mara feedback #770)
read the issue's `comments` count first, compute which page contains the
tail, fetch only `ceil(n/50) + 1` pages. drops the TAIL_MAX_PAGES cap
entirely — it was paging from the WRONG end (first 1000 comments instead
of the last n) on long threads, defeating the whole purpose of --tail.
work is now bounded by n, not by thread length.
2026-05-31 15:12:41 +02:00
damocles
7d31b7def4 hive-forge: warn when --tail saturates the pagination cap (argus 🟡 on #770) 2026-05-31 15:12:41 +02:00
damocles
5f4494c239 hive-forge: comments --tail N flag (#694 part 3) — last N comments in chronological order 2026-05-31 15:12:41 +02:00
iris
f7e38c0b42 common.js + flow.js + index.html + flow.html: cookie scrub + doc pointers (#712 batch 13 cont.)
Final dashboard frontend cleanup. Four files at 0 cookies each.

common.js (11 → 0):
- #406 module-split history → drop, present-state intro
- #448 SharedWorker SSE pipe (×3) → docs/web-ui.md SSE multiplexing
  paragraph pointer
- #515 worker-death self-heal (×3) → docs pointer + brief inline
- #453 bfcache argus-nit (×2) → drop framing, keep inline why
- #451 drag-to-resize → docs/web-ui.md::Side panel pointer
- #233 marked autolinks new-tab → drop cookie

flow.js (8 → 0):
- #406 module-split history → drop
- #389 banner-in-footer → drop framing
- #375 stacking context → docs/web-ui.md::Per-agent page Terminal-wrap
- #408 server-side filter, #499 backend allow-list → drop cookies,
  keep inline why
- #448 SharedWorker per-URL keying → docs pointer
- #163 SSE catchup drift → drop cookie, keep inline why

index.html (13 → 0):
- #389 chrome history → drop
- #459 schedules, #607 matrix, #609 nginx-front, #15 → drop cookies,
  keep functional comments + docs/web-ui.md::Tab strip + gateway.md
  pointers for matrix
- #385 dropped C0NTAINERS heading → drop framing
- #460 reminders-moved-here → drop framing
- #444 SchedulesChanged SSE absence → drop cookie, keep inline why
- #443 selection bar → docs/web-ui.md::Selection bar pointer
- #564 fold-create-into-table → drop framing
- #369#issuecomment-3437 → drop attribution
- #406 step 3 bundle rename → drop history

flow.html (3 → 0):
- #389 slug + #362 pill pattern + #406 step 2 bundle → drop cookies,
  preserve functional descriptions

Total this batch (across all 4 files): **35 cookies scrubbed**.
Net effect: the dashboard SPA's HTML + JS comments all point at
docs/web-ui.md for substantive design context now, with inline
comments only retaining present-state operational descriptions.
2026-05-31 15:09:54 +02:00
iris
6c9b28903f dashboard.css + docs: migrate MESS4GE FL0W body layout + final cookie scrub (#712 batch 13)
Second dashboard.css batch — scrubs all remaining #NNN cookies
(post-batch-12 dashboard.css went from 51 → 36 raw matches, mostly
hex colors). This batch zeroes out the actual issue refs.

Moved to docs/web-ui.md::FL0W page:
- **MESS4GE FL0W (extended)**: msgrow flex-wrap row + body
  flex-basis: 100% + min-width: 0 rationale (~10 lines new prose).
  Without flex-basis: 100% the body sat inline eating whatever the
  chips left, starting ~30ch in on long timestamps + agent names
  and wrapping awkwardly. Pushing it to its own line lets the body
  use the full row width.

Collapsed in dashboard.css (en passant cookie scrubs across many
small comments):
- #485 msgrow body flex (→ FL0W docs pointer)
- #450 inbox max-height (→ inline rationale only, cookie dropped)
- #451 side-panel drag-to-resize × 2 (→ Side panel docs pointer)
- #369 dashboard chrome (→ Tab strip pointer)
- #416 full-width layout (→ inline rationale, cookie dropped)
- #389 slug banner footer (→ inline rationale, cookie dropped)
- #369#issuecomment-3437 + #362 + #383 + #383 + #362 flow.html
  section (→ FL0W page pointer + per-agent header pills pointer)
- #375 tail pill stacking context (→ Per-agent page Terminal-wrap
  pointer to existing docs)
- #66 ctx badge thresholds (→ Container row Line 1 pointer)
- #541 panel-body column flex × 2 (→ inline rationale, cookie dropped)
- #188 / #192 image preview (→ Side panel pointer)
- #275 bulk select × 2 (→ inline, cookies dropped)
- #259 background meta-update (→ inline, cookie dropped)
- #474 inline edit button + form (→ inline, cookies dropped)
- #466 interval composer (→ inline, cookie dropped)
- #535 schedules-as-table × 2 (→ SCH3DUL3S tab pointer + inline)
- #564 inline create row (→ inline, cookie dropped)
- #443 selection bar (→ Selection bar pointer)
- #486 M0V3 → <pick> (→ Selection bar pointer)
- #459 scheduled prompts tab section (→ SCH3DUL3S tab pointer)

dashboard.css: ~20 issue refs scrubbed in this batch + ~10 lines
substantive prose migrated to MESS4GE FL0W. Total dashboard.css
across batches 12 + 13: 51 raw matches → 0 issue-ref cookies
(only hex colors remain).
2026-05-31 15:09:54 +02:00
lexis
eccceeaadf docs(CLAUDE.md): add docs/gateway.md to file index + reading paths (follow-up to #775 #793) 2026-05-31 15:07:06 +02:00
atlas
24775845a3 nix/hive-gateway: static not-found + unreachable pages for /agent/<name>/ (#755)
mara on #755: "e.g. /agent/name should show an error page stating
that the agent could not be found if missing in json or that it is
not reachable if we get a connection error. we dont want a fully
generic fallback, only for routes already special cased in the
nginx config."

Adds two static HTML pages built at deploy time via
`pkgs.runCommand "hyperhive-agent-error-pages"`:

- **not-found.html** — served when `/agent/<unknown>/...` hits the
  bare `/agent/` catch-all. The catch-all `return 404`s, and
  `error_page 404 = /__hive_agent_not_found` rewrites to the static
  page.
- **unreachable.html** — served when `/agent/<known>/...` proxy_pass
  to the harness returns 502 / 503 / 504. `proxy_intercept_errors
  on` + `error_page 502 503 504 = /__hive_agent_unreachable` on each
  per-agent location block rewrites to the static page.

Mechanics:

- `agentErrorPagesDir` (in the `let` block) is a `runCommand` that
  emits two HTML files using a `<<EOF` heredoc — no template engine
  needed.
- Two `internal` nginx locations (`= /__hive_agent_not_found`,
  `= /__hive_agent_unreachable`) `alias` the exact files. `internal`
  keeps the URIs unreachable from direct operator request — only
  nginx's own error-handling can hit them.
- Per-agent location blocks pick up the `error_page` directive
  through the existing `lib.mapAttrs'` over `agentPortsTable`. No
  per-agent generated content; same static page for all.
- `/agent/` catch-all generates from a tiny optionalAttrs alongside
  the per-agent block — both are no-op when the agent table is
  empty (matches the pre-#15 shape).

Pages: minimal inline CSS, catppuccin palette matching the
dashboard (`#1e1e2e` bg, `#cdd6f4` text, `#cba6f7` not-found heading,
`#f9e2af` unreachable heading). No frontend-dist dependency — render
even when hive-c0re is down. Both link back to `/`.

Per mara's "only for routes already special cased" — scope stays
narrow. Forge / matrix / fluffychat keep nginx defaults; extending
the custom-error pattern to other vhosts is a separate follow-up
if/when needed.

Verified:
- nginx location attrset has `["/", "/agent/", "= /__hive_agent_not_found", "= /__hive_agent_unreachable"]`
- container toplevel builds clean (`nixos-system-hive-gateway-26.05pre-git`)
- `docs/gateway.md::Per-agent error pages` section captures the
  design + rationale + intentional narrowness

Closes #755.
2026-05-31 15:04:54 +02:00
atlas
ba5a6181fc docs/gateway.md: add host-side-services.forgejo conflict-avoidance to container-shape section (argus #793 🟡) 2026-05-31 15:04:02 +02:00
damocles
2ada0e22ca harness: collapse Surface::DEFAULT_LABEL → single 'hive' fallback (#788)
closes #788. drops the per-role HIVE_LABEL fallback const ('hive-ag3nt'
on AgentSurface, 'hm1nd' on ManagerSurface) and replaces both with a
single literal 'hive' at the env-var unwrap site. real deploys set
HIVE_LABEL unconditionally via the meta-flake envelope; the fallback
is unreachable in production and there's no semantic reason for it to
differ per role.

nix-side standalone-eval fallback (HIVE_LABEL = 'hm1nd' in the
manager systemd unit) stays — that's wired so 'nixos-rebuild against
nixosConfigurations.manager' produces a sensibly-labelled container
even without the meta-flake wrapper.

Surface trait now: FLAVOR + FORGE_IS_MANAGER + 7 async wire methods.
next steps per #778 roadmap: #691#786#789.
2026-05-31 15:02:28 +02:00