hzhang bc1ab7b6ea fix: snake_case SlotStatus + scheduler debug logs
Two issues found while end-to-end testing against a running
harborforge-backend:

  - SlotStatus enum values: backend stores snake_case
    ("not_started" / "ongoing" / …), not the camelCase the
    OpenClaw plugin's TypeScript types.ts misled the initial
    drop into using. Heartbeat responses came back with
    Slot.Status="not_started" which the scheduler never matched
    against SlotStatus("NotStarted"), so dispatchSlot never
    fired. Aligned with backend's actual enum string values
    (verified via heartbeat response shape).

  - Added info-level logs at slot selection + dispatchSlot
    entry + WakeAgent fire/result so operators can see the
    plugin's decision chain in production without enabling
    debug. Cheap (~one tick per agent per heartbeat interval).

E2E in sim: backend returns slots=1 → selection chosen=true →
dispatch enter → WakeAgent enqueued ok → backend slot ongoing
→ next heartbeat returns slots=0.
2026-06-03 11:42:18 +01:00

HarborForge.PlexumPlugin

Plexum-side equivalent of HarborForge.OpenclawPlugin: exposes Plexum-side telemetry to the HarborForge Monitor bridge, drives the HarborForge Calendar scheduler, and gives agents a tool surface for the same calendar lifecycle actions OpenClaw agents had.

Part of the HarborForge platform; tracked as a git submodule of the HarborForge umbrella repo.

  • Plugin id: harbor-forge (matches the OpenClaw counterpart so the backend's per-plugin schemas don't fork)
  • Plugin version: 0.1.0
  • Activation: eager — Monitor bridge + Calendar scheduler must be running before any agent turn fires
  • Plexum SDK version: requires Plexum-sdk-go with HostAPI.WakeAgent (commit 216cf21 or later)

What it does

  • Monitor bridge — HTTP server on 127.0.0.1:<monitor_port> that responds to /telemetry with a Snapshot the HarborForge.Monitor binary expects (system metrics + every Plexum agent's sm-state)
  • Calendar scheduler — heartbeats <backendUrl>/calendar/agent/ heartbeat every interval, receives any TimeSlots due to fire, and dispatches them through HostAPI.WakeAgent (state-aware queue with depth-1 replace-newest)
  • 9 harborforge_ tools* mirroring the OpenClaw plugin's surface
Tool Use
harborforge_status resolved config + Monitor bridge health + Calendar status + telemetry snapshot
harborforge_telemetry fresh system + agent metrics
harborforge_monitor_telemetry last bridge query timing + last snapshot served
harborforge_calendar_status active slot(s) + history + heartbeat clock
harborforge_calendar_complete mark active slot completed (+optional summary)
harborforge_calendar_abort mark active slot aborted (+optional reason)
harborforge_calendar_pause pause active slot (non-terminal)
harborforge_calendar_resume resume a paused slot
harborforge_restart_status backend restart-pending flag + last poll time

Install

git clone --recurse-submodules https://git.hangman-lab.top/zhi/HarborForge.PlexumPlugin
cd HarborForge.PlexumPlugin
bash scripts/install.sh   # or:  make install

Then in ~/.plexum/plexum.json:

{
  "plugins": {
    "allow": [
      ".",
      "harbor-forge"
    ]
  }
}

And configure at ~/.plexum/plugins/harbor-forge/config.json:

{
  "backendUrl": "https://monitor.hangman-lab.top",
  "identifier": "server-t3",
  "apiKey": "g1_xxx",
  "monitor_port": 9100,
  "calendar_enabled": true,
  "calendar_heartbeat_interval_seconds": 30
}

Restart the host (systemctl --user restart plexum) and verify:

plexum plugin-list | grep harbor
curl -s http://127.0.0.1:9100/health
curl -s http://127.0.0.1:9100/telemetry | jq .agents

How calendar wake works

When the backend returns a slot_to_fire in a heartbeat response:

  1. Scheduler builds the message from slot.wake_options.override_message or falls back to slot.prompt
  2. host.WakeAgent({agent_id, message, source: "calendar:slot-<id>"})
  3. Plexum host-side wake.Manager:
    • if agent's sm-state is idle → runs the turn synchronously in a goroutine against the agent's wake session
    • else → enqueues (depth 1; new wake replaces any pending one)
    • drains automatically when the running turn returns
  4. The source tag lands on the turn's faithful event so retros can tell which slot caused which turn

The agent uses harborforge_calendar_complete / _abort / _pause / _resume mid-turn to push status back to the backend.

Layout

HarborForge.PlexumPlugin/
├── manifest.json                    # plugin manifest (eager, 9 tools)
├── go.mod                           # → Plexum-sdk-go (replace ../)
├── cmd/plexum-harborforge-plugin/   # main entry (Serve + Init)
├── internal/config/                 # config.json schema + Resolve
├── internal/telemetry/              # /proc-based snapshot collector
├── internal/monitor/                # HTTP bridge for HF.Monitor
├── internal/calendar/               # types + backend client + scheduler
├── internal/tools/                  # 9 tool implementations
└── scripts/install.sh               # build + drop into ~/.plexum/plugins

Differences vs OpenClaw equivalent

OpenClaw plugin Plexum plugin
api.registerTool(factory) runtime ToolPlugin.CallTool + manifest contract
api.spawn({agentId, task}) HostAPI.WakeAgent({agent_id, message, source}) (state-aware queue)
api.getAgentStatus() HostAPI.ReadAgentState(ctx, agent_id)
--install-monitor / --install-cli flags n/a — Monitor + hf CLI deploy separately (e.g. via HangmanLab.Server.T3 docker compose)
TS source compiled by tsc static Go binary built per-platform
Description
Plexum-side equivalent of HarborForge.OpenclawPlugin — telemetry, monitor bridge, calendar scheduler, harborforge_* tools
Readme 98 KiB
Languages
Go 96%
Shell 2.5%
Makefile 1.5%