86 lines
3.9 KiB
Markdown
86 lines
3.9 KiB
Markdown
|
|
# 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.)
|