feat(supervision): add choice watcher daemon, HTTPS spec docs, and test suites
- bin/muse_choice_watcher.py + systemd/muse-choices-reconcile.*: automatic choice answering and timer reconciliation - bin/digest.py: fleet log and health summarization - docs/BOX-*-HTTPS.md: comprehensive HTTPS execution contracts and API documentation - docs/MUSE-CHOICES-POLICY.md & docs/SUPERVISION-SPEC.md: autonomous execution specs - tests/test_*.py: unit test suites for HTTPS API, choice watcher, fleet heal, and swarm pruning
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# Box Loop + Strategy Writes over HTTPS (No SSH)
|
||||
|
||||
> **Box is the main surface.** All operator work goes through Box (box.muse-dev.online). The web UI, `box` CLI, and agents share the same API endpoints. No UI-only powers.
|
||||
|
||||
**Date:** 2026-10-06
|
||||
**Status:** implemented on bl (`exec-constrained.py` + `box-relay.sh`;
|
||||
`box-ctl.py` verbs pre-existed, plus a vars-name validator fix)
|
||||
**Scope:** safe loop/strategy/variable mutations. Reads (`loop-status`,
|
||||
`loop-health`, `loop-breaks`, `strat-get`, `vars-get`, ...) already ride
|
||||
`box.exec` or typed read ops and are unchanged here.
|
||||
|
||||
## 1. Why
|
||||
|
||||
Agents watching loop health could see breaks but needed SSH to remediate
|
||||
them, resolve stale followups, tune strategy overrides, or undo variable
|
||||
changes. All of this now rides the agent HTTPS path
|
||||
(`https://exec.muse-dev.online/exec`, signature or bearer auth, named-op
|
||||
allowlist, audit log).
|
||||
|
||||
## 2. New ops (all known-identities-only, none in the read-only subset)
|
||||
|
||||
| Op | Args | Backend |
|
||||
|---|---|---|
|
||||
| `loop.remediate` | `{dry_run?}` (default false) | `box-ctl.py loop-remediate [--dry-run]` |
|
||||
| `loop.resolve` | `{dm_id, note?}` | `box-ctl.py loop-resolve` |
|
||||
| `strat.set` | `{type!, subtype?, agent?, track?, priority?, timeout_s?, nudges?, escalate?}` | `box-ctl.py strat-set` (payload as argv JSON) |
|
||||
| `strat.reset` | `{type!, subtype?, agent?}` | `box-ctl.py strat-reset` |
|
||||
| `vars.reset` | `{name}` | `box-ctl.py vars-reset` (restore default) |
|
||||
| `vars.rollback` | `{name, revision?}` (int step or timestamp) | `box-ctl.py vars-rollback` |
|
||||
|
||||
New `box-relay.sh` client commands:
|
||||
|
||||
```bash
|
||||
box loop remediate [--dry-run]
|
||||
box loop resolve <dm_id> [note...]
|
||||
box strat set <type> [--subtype S] [--agent A] [--track true|false] [--priority p] [--timeout N] [--nudges N] [--escalate E]
|
||||
box strat reset <type> [subtype] [--agent <agent>]
|
||||
box vars reset <name>
|
||||
box vars rollback <name> [revision]
|
||||
```
|
||||
|
||||
Raw op call (signature auth, no token):
|
||||
|
||||
```bash
|
||||
exec-sign.sh loop.remediate '{"dry_run": true}'
|
||||
exec-sign.sh strat.set '{"type": "job", "priority": "important", "nudges": 3}'
|
||||
```
|
||||
|
||||
## 3. What remediate does (non-dry)
|
||||
|
||||
`loop.remediate` delegates to `gravity.remediate_breaks`: resolves
|
||||
followups already answered in logs, re-arms expired followups with nudges
|
||||
remaining (runs the followup sweeper once), auto-allows TRUSTED (non-key)
|
||||
browser approvals, and on hard breaks appends a `hard_break_alert` to
|
||||
job-log plus one DM to opm. Use `{"dry_run": true}` first to preview the
|
||||
`remediated` / `escalated` lists with zero side effects.
|
||||
|
||||
## 4. Safety notes (same posture as existing ops)
|
||||
|
||||
- **Fixed argv, validated values.** Strategy types are a strict enum
|
||||
(`wake|job|siphon|manual|health|heartbeat`) — the backend silently maps
|
||||
typos to MANUAL, so the op rejects them instead. Priorities are a
|
||||
strict enum; timeouts/nudges must be integers (backend clamps);
|
||||
dm ids must be 6–64 hex; variable names mirror the engine identifier
|
||||
rule. All checks run before any side effect.
|
||||
- **Stdin-free.** Unlike `job.put`, `strat.set` passes its JSON payload
|
||||
as an argv token (`strat-set <type> [JSON]`), so no new stdin plumbing
|
||||
was needed.
|
||||
- **Vars-name validator fix.** `quality-validate` for all five vars
|
||||
verbs used the job-name regex (`^[a-z0-9-]{1,64}$`), rejecting every
|
||||
real variable name (`max_nudge_count`, ...). They now share
|
||||
`qv_var_name` (`^[A-Za-z0-9_.-]{1,64}$`), mirroring the
|
||||
`exec-constrained.py` rule.
|
||||
- **Audited.** Every execution records identity + op + args hash;
|
||||
`box-ctl.py` additionally audits each mutation with its target.
|
||||
|
||||
## 5. Rollout notes
|
||||
|
||||
- Restart `exec-constrained.py` after deploy for the new ops to appear in
|
||||
`GET /ops`.
|
||||
- `box-relay.sh` is served from bl (`GET /box`); agents re-fetch to get
|
||||
the `loop` / `strat` groups and `vars reset|rollback`.
|
||||
- Non-goals: approvals, deletes, `main-loop` enable/disable — future
|
||||
expansions, same pattern. (md drive files shipped separately; see
|
||||
BOX-MD-HTTPS.md.)
|
||||
Reference in New Issue
Block a user