Watch
0
0
Fork
You've already forked hyperhive
0

docs: scheduled prompts are swarm-level; the hive page is a read-only list

Drops docs/tools/scheduling.md and every description of hive-level
schedule creation, the worker, the removed MCP tools, the scheduling tool
group and the removed endpoints/events, pointing at docs/swarm/schedules.md.
This commit is contained in:
atlas 2026-10-04 21:23:18 +02:00
commit b4a2aeb5ba
18 changed files with 90 additions and 346 deletions

View file

@ -21,7 +21,7 @@ declarations.
go deeper into implementation.
- **What tools does an agent (or the operator) have available?** →
[`tools/`](tools/README.md) — `hivectl` (yours) plus every agent's MCP
tool surface (bash, forge, lifecycle, matrix, scheduling).
tool surface (bash, forge, lifecycle, matrix).
## Agent lifecycle

View file

@ -94,12 +94,8 @@ apply.
approval step — every other agent is created at swarm level
(`swarmctl agent create`).
Roster-wise, `ruth` is just another entry.
- **Wire-protocol** — the `*(privileged)*` `Request` variants in
`hive-core-agent-sock`'s unified enum are the scheduling ops
(`RequestSchedulePrompt`, `CancelSchedule`, `FireScheduleNow`,
`EditSchedule`), reachable only from the manager's socket flavour,
matching the `scheduling` tool group
([`docs/turn-loop/mcp.md`](../turn-loop/mcp.md)). Container lifecycle
- **Wire-protocol** — `hive-core-agent-sock`'s unified `Request` enum
is the same on both socket flavours. Container lifecycle
ops (kill/start/restart/rebuild) go through the separate host-admin
socket `hivectl` speaks. `Wake` (inject a `from: <X>` message straight
into the caller's own inbox) is on this socket too, unprivileged on

View file

@ -1,7 +1,8 @@
# Approvals + helper events
The approval queue is where an agent asks the operator for something it
can't do itself: a scheduled prompt, or a meta-input bump. Config
can't do itself. Nothing submits a new approval today: the one kind
left, a meta-input bump, only reads back from existing rows. Config
changes don't go through this queue: they're pull requests
on the agent's config repo, which an operator merges on the forge, and
that merge deploys them. The submitting agent — any agent with the
@ -16,13 +17,6 @@ it stays informed about what happens after a decision lands.
config (or its own, via a sub-manager) as a pull request on
`agent-configs/<agent>`. Review it on the forge like any other PR, and
merge it there to deploy it. See [Config changes](#config-changes).
- **Scheduled prompt** (`SchedulePrompt`) — an agent asked to schedule
a message to one or more inboxes at a future time. It lands on your
dashboard's Y3R C4LL tab (or `hivectl approvals pending` /
`approve <id>` from the CLI). You can also add schedules yourself
directly from the SCH3DUL3S tab, which skips this approval step
entirely — the gate here is specifically for an *agent* asking to
schedule something, not for you doing it.
- **Meta/flake update** (`UpdateMetaInputs`) — an existing row reads
back and you can approve it. Approving runs the update and commits
the lock change; it doesn't rebuild anything by itself.
@ -113,9 +107,8 @@ merged by an operator on the forge (see [Config changes](#config-changes)).
### Approval kinds (wire shapes)
`ApprovalKind` carries two variants; each maps to a different
`commit_ref` encoding because `ApprovalKind` overloads that field as
the kind-specific payload carrier.
`ApprovalKind` carries one variant, and it overloads `commit_ref` as
its payload carrier.
<!-- vale write-good.Passive = NO -->
- `UpdateMetaInputs` — `commit_ref` stores the JSON-encoded inputs
@ -123,64 +116,8 @@ the kind-specific payload carrier.
etc.). hive-c0re sets the `agent` field to the requesting root agent.
On approve hive-c0re runs `nix flake update [inputs...]` on the
meta flake and commits the resulting lock changes.
- `SchedulePrompt` — `commit_ref` stores the JSON-encoded
`SchedulePromptPayload` (target list, body, schedule) so the
approval row carries the full submission verbatim. On approve
hive-c0re inserts a row into `scheduled_prompts` with
`source = approval:<id>`; the worker fans the body out as
inbox messages to each target at the scheduled time, recurring
when `interval_seconds` is set.
<!-- vale write-good.Passive = YES -->
### Scheduled prompts (submit paths)
Two ways a row lands in `scheduled_prompts`:
- **Operator-direct** (`source = "operator"`): the operator adds a schedule through the dashboard form. Lands in the table immediately, no approval gate — operator action is already the trust boundary.
- **Agent-requested** (`source = "approval:<id>"`): an agent submits a `RequestSchedulePrompt` through its MCP socket (the `request_schedule_prompt` tool, `scheduling` group). hive-c0re queues an `ApprovalKind::SchedulePrompt` row; on approve, it inserts the schedule row with `source = approval:<id>` so the audit trail points back at the operator decision (above).
No self-target shortcut: even agent-self schedules need approval. The existing `remind` MCP tool stays the quick self-wake path (no approval, lands directly in the agent's own inbox); this module is the bigger, multi-recipient, operator-visible thing.
### Scheduled prompt worker (catch-up clamp)
When hive-c0re comes back from being down, the worker sees rows whose `next_fire_at_unix` is well in the past. For recurring rows that would mean firing N delayed pulses in a row — spammy and useless. Instead the worker fires **once** per row and bumps `next_fire_at_unix` to the next interval slot ≥ `now`, recording how many cycles it skipped in `last_result` (per-target). Operators see "fired late, caught up from 17 skipped" instead of 17 wake-up storms.
The worker fires one-shot rows once (if past due, on the next worker pass) and deletes them; recurring rows survive until cancelled.
`targets` is its own table (`scheduled_prompt_targets`) so partial cancellation flips a single row and the dashboard can show last-fired / last-result per recipient. Cancelling every target reaps the parent row on the next worker pass.
### Scheduled prompt delivery: todo, not a broker message
An agent target's delivery is `push_todo` (`Coordinator::push_todo`,
`docs/scheduler/coordinator.md` covers the mechanism generally), not a broker
`Message` — a scheduled prompt wakes its target with a todo instead of
driving an immediate turn, by design. `key = "schedule:<id>"` per
target drives `push_todo`'s own upsert-by-key dedup: a re-fire of the
*same schedule* against a target that hasn't reviewed the last one
collapses into that one todo instead of stacking up.
### Missing-target failure
When a target name doesn't resolve to a known agent (container
destroyed, typo, etc.) the worker:
1. Records `last_result = "no such agent: <name>"` on the
per-target row.
2. Sends a single advisory `Message` from `system` to `operator`
naming the schedule, target, and reason. This one stays a `Message`
regardless of target type — it's a to-operator advisory about a
broken schedule, not the schedule's own delivery.
3. Continues fanning out to the other live targets.
Transient broker errors (sqlite lock contention, etc.) get the same
`last_result` annotation plus a `tracing::warn`, and then:
- **Recurring rows** re-arm to the next interval slot — the retry
self-heals on the next worker pass.
- **One-shot rows**: the worker deletes them unconditionally after their single
fan-out pass; a broker error on a one-shot isn't retried (the
operator advisory and `last_result` are the only audit trail).
### Reminder delivery: file-path semantics
A reminder may carry a `file_path` (the agent-visible path inside its
@ -324,7 +261,6 @@ by the approval handler rather than run inline:
| `ApprovalKind` | DAG submitted | source |
|---|---|---|
| `UpdateMetaInputs` | `meta_update` (`MetaLock` + rebuild fan-out) | `approval` |
| `SchedulePrompt` | — runs inline (single sqlite insert) | — |
The DAG carries the originating `approval_id`. **Every** queued kind
resolves through `actions::resolve_approval_dag` when its DAG settles
@ -508,10 +444,8 @@ the `submitter` column on the `approvals` table). The harness delivers it
as a regular `system` inbox message so it drives a normal claude turn.
`finish_approval` fires an `ApprovalResolved` HelperEvent this way for
**every** approval kind's terminal state. A
"FYI, check when convenient" event doesn't need a message — those go
through `Coordinator::push_todo` instead, a direct live dial of the target
agent's in-container todo socket (same `UpsertTodo` request in-container
producers use). Legacy
"FYI, check when convenient" event doesn't need a message — hive-c0re publishes those as swarm
lifecycle notices instead (`swarm_notices::notify`). Legacy
approval rows that predate the submitter column fall back to the
root agent. Variants (`hive_sh4re::manager::HelperEvent`):
@ -533,9 +467,8 @@ root agent. Variants (`hive_sh4re::manager::HelperEvent`):
The remaining lower-urgency lifecycle notices — `Rebuilt`, `Killed`,
`Destroyed`, `NeedsLogin`, `LoggedIn` — are "FYI, check
when convenient" events with no reason to drive an immediate turn, so
they deliver via `push_todo` (see above) instead
of `HelperEvent`: an `agent_todo_socket` push instead of a broker
message, `subsystem = "core"`, `key = "<event>:<agent>"` for dedup,
they go out via `swarm_notices::notify` (see above) instead
of a `HelperEvent`: `subsystem = "core"`, `key = "<event>:<agent>"`,
and a single free-text `summary` (`rebuilt_todo_summary` renders
`Rebuilt`'s `ok`/`note`/`sha`/`tag` fields into that string).
@ -543,7 +476,7 @@ To add a new lifecycle notice: if it needs to drive an immediate turn
(something genuinely urgent, like `ContainerCrash`), add a
`HelperEvent` variant + call sites + update `prompts/system.md`'s
message-event list. If it's "FYI, check when convenient," call
`push_todo` directly instead — no new wire type
`swarm_notices::notify` instead — no new wire type
needed.
## Autoupdate on startup

View file

@ -40,8 +40,8 @@ schemas, file layouts, and internal migration mechanics.
### `/var/lib/hyperhive/db/broker.sqlite` (host)
Seven tables, all in one file — three queues, a small key/value
table, the schedule header/targets split, and the per-agent
Six tables, all in one file — two queues, a small key/value table,
the read-only schedule header/targets split, and the per-agent
power-intent registry:
- `messages` — every inter-agent / operator-bound message.
@ -56,21 +56,19 @@ power-intent registry:
per-agent store — see [`/harness/` contents
below](#state-dirs-per-agent) for where reminders (and todos)
live.
- `approvals` — the queue. `agent / kind (update_meta_inputs |
schedule_prompt) /
- `approvals` — the queue. `agent / kind (update_meta_inputs) /
commit_ref / requested_at / status / resolved_at / note`.
- `scheduled_prompts` — recurring + one-shot prompt queue.
- `scheduled_prompts` — the scheduled prompts left from before they
moved to swarm level ([`swarm/schedules.md`](../swarm/schedules.md)).
`owner / body / interval_seconds (NULL = one-shot) /
next_fire_at_unix / created_at_unix / source ("operator" or
"approval:<id>") / cancelled_at_unix / description`. `owner`
drives cancel-permission checks (operator vs the submitting
agent). The worker tombstones and reaps cancelled rows
on its next pass.
"approval:<id>") / cancelled_at_unix / description / paused_at_unix`.
Read-only: the dashboard's SCH3DUL3S tab lists them so the operator
can recreate them with `swarmctl schedule`; nothing writes or fires
them.
- `scheduled_prompt_targets` — per-target state for each schedule.
`schedule_id / target / cancelled_at_unix /
last_fired_at_unix / last_result`. `ON DELETE CASCADE` from
`scheduled_prompts(id)` — requires `PRAGMA foreign_keys = ON`
per connection (set at open).
last_fired_at_unix / last_result`.
- `agent_power` — one row per agent: `agent PK / wanted (up |
offline) / updated_at`, owned by `hive-c0re/src/stores/power.rs`.
This is the durable power *intent* the job queue reconciles the
@ -90,11 +88,7 @@ Retention:
an unacked row on a crash.
- hive-c0re keeps approvals indefinitely — an audit trail. `actions::destroy`
rows stay visible to anything that queries by id.
- Scheduled prompts: the worker deletes one-shot rows on fire;
recurring rows live until the operator cancels them
(`cancel_schedule` MCP / dashboard ✗) which tombstones via
`cancelled_at_unix`, then `reap_cancelled` drops the row on
the next worker pass.
- Scheduled prompts: nothing deletes them; the rows stay as they are.
- `agent_power` rows live until the operator destroys the agent (one row per
agent — nothing to vacuum).

View file

@ -254,8 +254,8 @@ the wire structs in hive-sh4re) serialize as **RFC 3339 UTC strings**
fields as `i64` unix seconds internally, only the JSON representation
changes, and deserialization accepts both the string form and a bare
integer (rolling-deploy skew, persisted blobs).
**Input-direction** fields agents compute as epoch (`first_fire_at_unix`,
schedule-edit `next_fire_at_unix`, `Wakeup::At`) stay integers. The
**Input-direction** fields agents compute as epoch (`remind`'s
`at_unix_timestamp`) stay integers. The
`*_unix` field *names* stay for now — renaming is the wire-types
refactor's concern. The dashboard frontend parses via
`util.js::epochSec` wherever it needs arithmetic and feeds the string
@ -297,7 +297,6 @@ binary flavor.
| `execution` | vestigial — `mcp__bash__run` / `mcp__bash__status` are always available unconditionally via `extraMcpServers`; this group's entries expand to non-existent `mcp__hyperhive__run` / `mcp__hyperhive__status` and have no effect. See `docs/tools/bash.md`. |
| `lifecycle` | none |
| `approvals` | none — gates `cancel_loose_end`'s approval-cancel arm server-side. |
| `scheduling` | `request_schedule_prompt`, `fire_schedule_now`, `cancel_schedule`, `edit_schedule`, `list_schedules` *(privileged)* |
| `forge` | none |
| `web_tools` | none (gates the Claude built-ins `WebFetch`/`WebSearch`, not an MCP tool) |
@ -375,8 +374,7 @@ bypass the operator approval gate.
`hive-sh4re/src/permissions.rs` + an arm to `as_str`. Add it to `Capability::ALL`
(the source of truth for the permissions UI columns). Implement the access
check in the relevant handler (`hive-c0re/src/socket_server/mod.rs`,
`hive-c0re/src/socket_server/schedules.rs`, `coordinator.rs`, or a
handler under `hive-c0re/src/dashboard/`).
`coordinator.rs`, or a handler under `hive-c0re/src/dashboard/`).
## Async forms

View file

@ -521,7 +521,7 @@ which is almost never desired. Leave off on non-x86 hosts.
## See also
- `docs/agent-lifecycle/approvals.md` — approval flow + scheduled prompts
- `docs/agent-lifecycle/approvals.md` — approval flow
- `docs/agent-lifecycle/persistence.md` — SQLite schema, state-dir layout
- `docs/process/conventions.md` — wire protocol, recipient sentinels
- `docs/agent-lifecycle/agent-roster.md` — the agent roster and privilege

View file

@ -64,11 +64,11 @@ works them out from the prompt itself, so:
watches the paused prompt; if the agent was down for the whole pause, it
fires the latest time it missed once, as above.
A fire reaches the agent as a `schedule` loose end with the prompt's text,
the same as a hive schedule's. The agent's turn loop wakes for it like any
other loose end. If the agent hasn't dealt with the previous fire yet, the
new fire replaces it rather than adding a second one; once the agent
dismissed the previous one, the next fire opens a new one.
A fire reaches the agent as a `schedule` loose end with the prompt's text.
The agent's turn loop wakes for it like any other loose end. If the agent
hasn't dealt with the previous fire yet, the new fire replaces it rather
than adding a second one; once the agent dismissed the previous one, the
next fire opens a new one.
Each agent reads its prompts every 10 seconds, so a fire can land up to 10
seconds after its time. A change reaches each agent within the same window,

View file

@ -43,5 +43,3 @@ debug agent behavior.
- **[matrix](matrix.md)** — the matrix MCP tool surface
(`mcp__matrix__*`) for agents with a matrix account, multiple
accounts per agent, and declaring extra MCP servers generally.
- **[scheduling](scheduling.md)** — scheduled prompts (operator
approval required).

View file

@ -1,74 +0,0 @@
# Scheduling and diagnostics tools
## `scheduling` tool group
Scheduled prompts fan a message body out to one or more agent inboxes
at a future time, optionally recurring.
**Creating** one goes through the operator approval queue, even when it
targets only yourself — use `remind` for an unapproved self-wake. The
other four verbs need no approval: holding the `scheduling` tool group
is the whole gate.
Authorization is one rule for all four verbs: you reach every schedule in
the hive, whoever owns it. `list_schedules` shows the same set, so the
snapshot only ever shows schedules you could also cancel.
### `request_schedule_prompt(targets, body, first_fire_at_unix, interval_seconds?, description?)`
<!-- vale write-good.Passive = NO -->
Queue an operator-approval for a scheduled prompt. On approve,
hive-c0re fans `body` out to each agent in `targets` at
`first_fire_at_unix` (Unix timestamp). Recurring when `interval_seconds`
is set, one-shot otherwise.
<!-- vale write-good.Passive = YES -->
Catch-up clamp: if hive-c0re is down across multiple intervals, only
ONE delayed fire happens on resume (per recurring schedule). The
skipped-cycle count surfaces in the per-target `last_result` for
audit.
### `edit_schedule(id, body?, description?, interval_seconds?, next_fire_at_unix?, targets_add?, targets_remove?)`
Partial-update a schedule. Pass only the fields to change; absent
fields stay untouched. `targets_add` / `targets_remove` mutate the
recipient list in the same transaction — re-adding a previously
cancelled target drops its tombstone and starts fresh.
`interval_seconds` accepts positive values only via this tool (omit
to keep the existing cadence; pass a new positive value to change
it). Toggling recurring → one-shot (clearing the interval) is
operator-only via the dashboard PATCH endpoint. Refuses cancelled
rows (terminal state).
### `cancel_schedule(id, targets?)`
<!-- vale write-good.Passive = NO -->
Cancel a schedule. Omit `targets` / pass empty to cancel the whole
schedule; pass a list to cancel just those recipients (the schedule
autocancels once every target is gone).
<!-- vale write-good.Passive = YES -->
### `fire_schedule_now(id)`
Fire a scheduled prompt out of band immediately. Recurring schedules
keep their cadence — the manual fire is additive. The manual fire
consumes one-shot schedules and cancels them afterwards.
### `list_schedules()`
Snapshot every schedule in the hive (active, and cancelled but not yet
reaped) — same read scope as the rest of this group. Returns id, owner,
body, per-target `last_fired_at` and `last_result`,
`next_fire_at_unix`, `interval_seconds`. Use to look up an id before
cancelling, or to audit upcoming wake-ups across the hive.
## See also
- `remind` (no-approval self-wake path) — documented in
[`docs/turn-loop/`](../turn-loop/README.md).
- [`docs/agent-lifecycle/approvals.md`](../agent-lifecycle/approvals.md) — approval flow for
`request_schedule_prompt`.

View file

@ -131,10 +131,10 @@ checkpoints**, not about sandboxing the agent from its own tools:
settings nor merge its own PR. It's **not** set up for external VCS (GitHub
etc.), though — there, operator-merge is process + accepted risk, not a
technical control.
- **Approvals** — schedule additions and other blast-radius-y operations
route through the operator approval queue; config changes are config PRs
an operator merges on the forge
(see [`approvals.md`](../agent-lifecycle/approvals.md)).
- **Approvals** — config changes are config PRs an operator merges on the
forge (see [`approvals.md`](../agent-lifecycle/approvals.md)), and
scheduled prompts are operator-only, set at swarm level
(see [`schedules.md`](../swarm/schedules.md)).
### Capability = accepted risk
@ -327,6 +327,6 @@ writes to only the paths `hive-priv` legitimately needs.
| Component | Runs as | Privilege needed for |
| ---------------------------- | -------------- | ------------------------------------------------------------ |
| `hive-c0re` | `hive-core` | broker, HTTP dashboard, scheduling, approvals |
| `hive-c0re` | `hive-core` | broker, HTTP dashboard, approvals |
| `hive-priv` | `root` | container lifecycle, journal reads, bind mounts, cred writes |
| `hive-ag3nt` (per-container) | per-agent user | turn execution, MCP serving |

View file

@ -24,7 +24,7 @@ for what reaches a subagent's own `--mcp-config` and what doesn't.
Tool groups (`HIVE_TOOL_GROUPS`) gate tool access. The default
preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and
`execution`. Privileged groups (`lifecycle`, `approvals`, `scheduling`)
`execution`. Privileged groups (`lifecycle`, `approvals`)
are opt-in via the P3RM1SS10NS tab.
## Core tools (always available)
@ -149,8 +149,6 @@ hive_name?, swarm_name?, matrix_accounts? }`. `matrix_accounts` is a
`lifecycle` gates nothing.
Config changes go through a forge PR on `agent-configs/<name>` — see
[`docs/agent-lifecycle/approvals.md`](../agent-lifecycle/approvals.md).
- **Scheduling** (`scheduling`) — scheduled prompts. See
[`docs/tools/scheduling.md`](../tools/scheduling.md).
- **Forge repos** (`forge`) — gates nothing.
- **Web egress** (`web_tools`) — enables Claude's built-in `WebFetch`
and `WebSearch` tools (not MCP tools; added directly to the

View file

@ -23,7 +23,8 @@ of your time. It's a single page with exactly four tabs:
- **Y3R C4LL** — anything waiting on _you_: pending approvals. If an
agent needs a decision from you, it's here.
- **P3RM1SS10NS** — what tools and system-level access each agent has.
- **SCH3DUL3S** — scheduled prompts and agent self-reminders.
- **SCH3DUL3S** — a read-only list of the scheduled prompts left in the
hive, due for removal; operators set schedules at swarm level now.
Everything else lives on its own page instead, all reachable from the
H0M3 hub: **Flow** (`/flow.html`, the raw live message stream across
@ -48,9 +49,8 @@ and quick links (stats, screen, forge profile). Select the name to open
its terminal and watch it work in real time.
**Approve something an agent is waiting on.** Y3R C4LL is the one tab
worth checking regularly — it's everything that needs _you_: an agent's
request to schedule a prompt. The tab's count pill tells you at a glance if
anything's pending.
worth checking regularly — it's everything that needs _you_. The tab's
count pill tells you at a glance if anything's pending.
**Review a config change.** Agent config changes (new packages, env
vars, MCP servers) are pull requests on the agent's `agent-configs`
@ -76,9 +76,10 @@ save queues a rebuild for whichever agents actually changed.
journald view for any agent + service; the per-agent `⋮` menu's
**journal logs →** link jumps straight there, pre-filtered.
**Set up a schedule or check on a reminder.** SCH3DUL3S covers both —
recurring or one-shot prompts you schedule for one or more agents, and
reminders agents have set for themselves.
**Set up a schedule.** Scheduled prompts are swarm-level: set them with
`swarmctl schedule` ([`swarm/schedules.md`](../swarm/schedules.md)).
SCH3DUL3S only lists the ones still in the hive, so you can copy them
over.
## More depth

View file

@ -25,7 +25,7 @@ from the dashboard tab strip.
live on their own pages too, all reachable from the
H0M3 hub (not the tab strip). Count pills on SW4RM
(container count), Y3R C4LL (pending approvals + unread
operator messages), and SCH3DUL3S (active schedules).
operator messages), and SCH3DUL3S (active schedules left to copy).
- **Banner-thin** (`░▒▓█▓▒░ HYPERHIVE / HIVE-C0RE / WE ARE THE WIRED ░▒▓█▓▒░`)
— sits below the tab strip.
- **Server-warnings banner** — a generic, sticky top-of-page strip shown
@ -50,7 +50,7 @@ from the dashboard tab strip.
page prepends a `(N)` prefix — `(3) pr1ma // h1ve-c0re` — so the
operator can see the call count in an unfocused browser tab without
opening the dashboard. The page sets the prefix on the initial
`/api/state` cold-load and updates it live via `approval_added` /
`/api/state` cold-load and updates it live via
`approval_resolved` SSE events; it's preserved when `hive_name` /
`swarm_name` later replace the raw title.
@ -332,7 +332,7 @@ named buckets of MCP tools; each agent starts with a role default
(sub-agents: `messaging`, `meta`, `inbox`, `execution` —
`ToolGroup::AGENT_DEFAULT`; hive-c0re seeds the root agent to
`ToolGroup::MANAGER_DEFAULT` — `messaging`, `meta`, `inbox`,
`lifecycle`, `approvals`, `scheduling`, `execution`,
`lifecycle`, `approvals`, `execution`,
that is, every group except `forge` and `web_tools`). Checking /
unchecking stages which groups are active for the agent; the
page-level **save all** button (below) commits it. Columns come from
@ -341,7 +341,7 @@ takes effect.
<!-- vale write-good.Passive = YES -->
The current tool groups are: `messaging`, `meta`, `inbox`, `lifecycle`,
`approvals`, `scheduling`, `forge`, `execution`,
`approvals`, `forge`, `execution`,
`web_tools`. All listed in `ToolGroup::ALL` in `hive-sh4re`. The
`web_tools` group is special: it carries no MCP tools; instead it adds
Claude's built-in `WebFetch` and `WebSearch` to `--tools` /
@ -374,83 +374,31 @@ isn't clobbered.
## SCH3DUL3S tab
Anything that fires at a future time. The operator creates schedules
inline in the table (last row); agent self-paced
reminders surface at the bottom as a sibling list — they
share enough conceptual ground to live together.
A read-only list of the scheduled prompts still in the hive's store.
The hive no longer fires them: scheduled prompts live at swarm level
([`swarm/schedules.md`](../swarm/schedules.md)), and a warning banner at
the top of the tab says the page is due for removal and points at
`swarmctl schedule`. The list is there so the operator can recreate each
row with `swarmctl schedule add`.
**N3W SCH3DUL3 / QU3U3D SCH3DUL3S** — operator-managed
scheduled prompts. **Single-table layout**: each schedule is
one `<tr>`; columns are
`# | src | next | every | owner | body | …agents… | actions`.
Agent columns are dynamic — `operator` + `root` + every
live container + any extra name that appears as a target on
some schedule but isn't a current container (same
`buildTargetChips` membership rule the new/edit forms use,
so table and forms agree on what's addressable). Column
headers tilt –45° via CSS so each column reads as a narrow
~28px strip; per-agent cells render as:
**Single-table layout**: each schedule is one `<tr>`; columns are
`# | src | next | every | owner | body | …agents…`. Agent columns are
dynamic — every live container plus any extra name that appears as a
target on some schedule but isn't a current container (struck through
as gone). Column headers render vertically so each column reads as a
narrow ~28px strip; per-agent cells show `✓` for an active target, a
muted `✕` for a cancelled one, and nothing when the agent isn't a
target. Hovering a target cell shows its last fire and result.
- **active target** → `<button>✓</button>` that cancels just
that one target on selection
- **cancelled target** → muted `✕` glyph (no button —
re-adding goes through the edit form's targets multi-select)
- **not a target** → empty cell
The `next` column shows the stored next fire time as an absolute UTC
timestamp, the value to pass as `--at`; paused and cancelled rows say
so instead, with the time in the cell's tooltip. The body cell
truncates; hovering shows the full body and description.
Per-schedule action column:
- **`↯ fire now`** — sends an out-of-band manual pulse to every
active target (recurring schedules keep their cadence; the manual
fire consumes one-shots). Disabled for paused schedules.
- **`⏸` / `▶` pause/resume toggle** — only on recurring schedules
(the operator may also pause one-shots to delay a one-time fire, so
the button always appears). Paused rows show a `⏸ paused` badge in
the `next` column; the table sorts them to the middle bucket (after
active, before cancelled). A paused schedule doesn't fire regardless of
`next_fire_at`; resuming (`▶`) clears the pause without adjusting
the next-fire time. POSTs to `/api/schedules/{id}/pause` or
`/api/schedules/{id}/resume`.
- **`✎ edit`** — expands an inline edit form as a colspan'd row
directly under the schedule's row (body / description / interval /
next-fire / targets all editable; targets are a multi-select diff'd
against the original active set so unchecked-was-active =
`targets_remove`, checked-not-originally-active = `targets_add`;
submit PATCHes `/api/schedules/{id}`).
- **`✕`** — cancels the whole schedule
(`POST /api/schedules/{id}/cancel`).
The `next` column cell (`.sched-due`) carries a `data-due-at`
Unix timestamp attribute; a shared 1s ticker rewrites it
in-place showing `fmtDuration` while in the future and
`overdue X ago` once the fire time has passed — same
zero-re-render pattern as the reminder due-at labels.
The table's last row is a permanent inline creation row:
inputs live directly in table cells (targets as checkboxes,
body textarea that expands on focus, datetime-local pre-filled
to 5 minutes from now, mini d/h/m/s number inputs (blank or
all-zero = one-shot), description). Select `+` to POST to
`/api/schedules` as JSON (or `⌫` to clear the half-filled row);
carry-state preserves partially typed inputs across re-renders. The tab pill shows the count of active
schedules (at least one live target not yet cancelled).
Refreshed on tab activation and after each submit/cancel. Backed by
`GET /api/schedules`. No hive-c0re changes for the table layout
— it renders entirely from existing `schedulesState` +
`containersState`.
**QU3U3D R3M1ND3RS** — reminders agents have scheduled for
themselves (via the `remind` tool) but not yet delivered.
Each row shows the owner, due time, and message; a `CANC3L`
button hard-deletes (`POST /api/cancel-reminder/{id}`) and a
`R3TRY` button re-arms one whose delivery failed
(`POST /api/retry-reminder/{id}`). Backed by `GET /api/reminders`.
Lives in the SCH3DUL3S tab alongside operator schedules so the
operator has one place for everything time-fired. The due-time
label (`.reminder-due`) carries a `data-due-at` Unix timestamp
attribute; a shared 1s ticker rewrites it in-place — showing
`in Xm Ys` while the reminder is in the future and
`overdue X ago` once the deadline passes — without triggering a
full re-render of the list.
The tab has no controls: nothing creates, edits, pauses, fires or
cancels a row. The tab pill shows the count of active schedules (at least one
live target not yet cancelled). Fetched from `GET /api/schedules` on
tab activation and on each dashboard state load.
## ST4TS page (`/stats.html`)
@ -880,7 +828,6 @@ renderApprovals`) with three stacked sections:
| kind | glyph | chip |
|---|---|---|
| `update_meta_inputs` | `↻` | `meta-update` |
| `schedule_prompt` | `⏱` | `schedule` |
<!-- vale write-good.Passive = NO -->
The chip ticks live every second via a `data-requested-at`
@ -889,8 +836,7 @@ renderApprovals`) with three stacked sections:
the `.stale` class flips precisely at the 3600s boundary rather
than at the next `renderApprovals` call.
- **what-changed body** — the submitting agent's description, then the
kind's payload: the inputs to bump (`update_meta_inputs`) or the prompt to
schedule (`schedule_prompt`).
kind's payload: the inputs to bump (`update_meta_inputs`).
- **decision actions** — `◆ APPR0VE` and `DENY`. Deny pops a
`prompt()` for an optional reason carried to the submitting agent as
`HelperEvent::ApprovalResolved.note`.
@ -1075,42 +1021,9 @@ below — some endpoints aren't in it yet.
rows with both fields omitted rather than erroring on them.
Returns `200 "ok"` on
success.
- `GET /api/schedules` — list all schedules (active and
recently cancelled) for the SCH3DUL3S scheduled-prompts panel.
- `POST /api/schedules` — operator-direct schedule create:
`{ targets, body, first_fire_at_unix, interval_seconds?, description? }`.
Agent-initiated schedules go through the approval queue instead
(via the `request_schedule_prompt` MCP tool).
- `PATCH /api/schedules/{id}` — partial edit. JSON body
`{ body?, description?, interval_seconds?, next_fire_at_unix?,
targets_add?, targets_remove? }`.
Missing key = "leave alone"; explicit `null` on
`description` / `interval_seconds` clears the field (so a
recurring schedule flips to one-shot when the caller sends
`interval_seconds` as `null`). `targets_add` is replace-on-conflict:
re-adding a previously cancelled target drops the tombstone
and the target starts fresh (operator intent on re-add =
"this target is active again"). `targets_remove` delegates
to the same path as `cancel_targets` — tombstones preserve
audit, parent schedule autocancels when no active targets
remain. Refuses cancelled rows; returns the updated
`WireSchedule` on success.
- `POST /api/schedules/{id}/cancel` — cancel a schedule. Body
`{ targets?: ["name", …] }` cancels just those recipients;
absent or empty body cancels the whole schedule.
- `POST /api/schedules/{id}/fire-now` — out-of-band manual
pulse. Fires the schedule body once immediately to
every active target. Recurring schedules: `next_fire_at_unix`
is untouched; the regular cadence continues. One-shots: the
manual fan-out consumes the schedule (cancels it).
The endpoint annotates per-target `last_result` as a manual fire so
the audit trail distinguishes scheduled fires from operator-
triggered ones.
- `POST /api/schedules/{id}/pause` — pause a recurring or
one-shot schedule. A paused schedule doesn't fire regardless
of `next_fire_at`; the row gains a `⏸ paused` badge.
- `POST /api/schedules/{id}/resume` — resume a paused schedule.
Clears the pause flag without adjusting `next_fire_at`.
- `GET /api/schedules` — list every schedule row left in the hive's
store (cancelled ones included) for the read-only SCH3DUL3S tab.
Targets naming an agent that no longer exists are left out.
- `POST /api/meta-update` — `nix flake update` the selected
`meta/flake.lock` inputs, then rebuild the affected agents.
- `GET /api/dashboard/stream` — unified live event channel:
@ -1133,10 +1046,9 @@ payload):
rows with a `↳ reply` tag that scroll-highlights the parent
row on selection. Used by the message-flow terminal renderer and
the operator-inbox derived state.
- `approval_added` (id, agent, approval_kind, sha_short, diff,
description) / `approval_resolved` (id, agent, approval_kind,
sha_short, status, resolved_at, note, description) — pending
queue + history mutations. Client mutates a derived store and
- `approval_resolved` (id, agent, approval_kind, status,
resolved_at, note, description) — moves a row from the pending
queue to history. Client mutates a derived store and
re-renders only the approvals section.
- `transient_set` (name, transient_kind, since_unix) /
`transient_cleared` (name) — lifecycle action spinners. The
@ -1158,16 +1070,6 @@ payload):
terminal-history trim). Unlike the snapshot events below it ships
no state at all: the client re-fetches `GET /api/jobq/graph`, which
is where it reads the queue from cold too.
- `schedules_changed` (seq, schedules: `Vec<WireSchedule>`) —
full snapshot of all scheduled prompts. Emitted after every
operator mutation via the `/api/schedules` surface (new /
edit / cancel / fire-now) and after the worker fires or
rearms a row. Same snapshot-shape rationale as
`tombstones_changed` / `meta_inputs_changed` (small list, no
add/remove races). The SCH3DUL3S tab subscribes and
re-renders `schedulesState` on receipt; tab activation still
re-fetches as a safety net for approval-path inserts and
disconnect windows.
- `reminders_changed` (seq, reminders: `Vec<PendingReminder>`) —
full snapshot of all pending reminders. Emitted after every
reminder mutation: agent `remind` calls (`agent_server`),

View file

@ -50,7 +50,7 @@
(per-agent) → `text/event-stream` SSE for live updates. The
dashboard stream carries broker `Sent` / `Delivered` (mirrored
by a forwarder task from the broker's intra-process channel)
plus mutation events (`approval_added` / `approval_resolved`,
plus mutation events (`approval_resolved`,
`transient_set` / `transient_cleared`). Each frame carries a `seq`. The
matching backfill endpoint is `GET /api/dashboard/history` (last
~200 broker messages wrapped in `{ seq, events }`) on the