DC-132: Plan plugin + agent metrics report scaffold - #5
Conversation
Ingests architectural review (Claude Opus 4.7, max effort) of the
DaemonCraft visual + infra upgrade. Establishes the work breakdown
for evolving the dev server into a kid-friendly production setup.
Epic DC-124 with 8 child tasks:
- DC-125 Phase 0 stabilize (image SHA pin, rolemaster.yaml model fix,
plugin version inventory)
- DC-126 Phase 1a hardening (Docker mem/cpu limits, mc-backup sidecar,
CoreProtect, LuckPerms)
- DC-127 Phase 1b server visual (SkinsRestorer, DecentHolograms,
Better Leaves, Clean Glass, TAB)
- DC-128 Phase 1c Java client .mrpack (Modrinth App, shaders opt-in)
- DC-129 Phase 1d Bedrock client .mcpack (Geyser/packs/)
- DC-130 Phase 2 SOUL-rolemaster stage-tools cheatsheet (docs only)
- DC-131 safety whitelist + chat moderation
- DC-132 observability Plan plugin + agent metrics JSONL
Blocks on DC-123 (open dashboard/TTS regression) before visual work
can sequence. Multi-server mesh, Velocity, Terraform deferred per plan.
Branch strategy: overhaul/server-setup is the tracking branch.
Five stacked PRs land independently for upstream review:
PR-A=DC-125, PR-B=DC-126, PR-C=DC-127, PR-D=DC-128+DC-129,
PR-E=DC-131+DC-132.
Vault pages added under ~/REPOS/vault/ (separate commit, not in
this repo): projects/DaemonCraft/index.md,
projects/DaemonCraft/overhaul-plan.md,
concepts/minecraft-resource-pack-distribution.md,
concepts/minecraft-server-hardening.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lty=easy Phase 0 stabilization for the Server Setup Overhaul (epic DC-124). Locks the moving pieces so subsequent phases land on a deterministic base. PR-A of the 5-PR overhaul series. Changes: - docker-compose.yml: pin itzg/minecraft-server to digest sha256:629762aaf864e109a35e00b11d701cfc6b2bddca4331944aefde0a352ebb9fd4 (captured 2026-05-03 from a known-good running container). No more silent forward-rolls; bumps are deliberate after smoke test. - docker-compose.yml: DIFFICULTY peaceful -> easy. Permits hostile mob spawns for rolemaster narrative tension while keeping kid-friendly damage scaling. Pamplinas can override per-scene via /difficulty through mc_command. - agents/casts/rolemaster.yaml: model field corrected from kimi-k2.6 / kimi-coding to MiniMax-M2.7 / minimax to match the runtime (other casts already use this). - docs/server-overhaul.md (new): operator runbook with the pinned versions table and the upgrade procedure. Companion to plans/DC-124.md and the verbatim plan in vault. Geyser version pin deferred to a follow-up. The MODRINTH_PROJECTS line lives on feat/bedrock-geyser-support (upstream PR nicoechaniz#2 to nicoechaniz). After that PR merges, a small follow-up replaces "geyser" with "geyser:R7DKgZlt" (Geyser-Spigot 2.9.5-b1130). Verified locally: container restarts cleanly with the pinned digest; rcon-cli confirms difficulty=Easy applied to the live world (existing worlds need a one-shot /difficulty to overwrite level.dat — documented in the runbook). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ct, LuckPerms
Phase 1a of the Server Setup Overhaul (epic DC-124). PR-B of the
5-PR series. Brings the server to operational table-stakes before
visual or client-pack work.
Changes:
docker-compose.yml
- mem_limit: 14g, cpus: 6.0 on the minecraft service so a runaway
plugin cannot OOM the host.
- mc-backup sidecar (pinned digest) — daily snapshots via RCON
save-off/flush/save-on, prune after 30 days. Destination is a local
bind-mount placeholder (./server/backups); final cloud destination
deferred to follow-up once Hetzner vs B2 is decided (DC-126 open q).
- RCON_PASSWORD + ENABLE_RCON env vars pinned so the sidecar can
always authenticate (Purpur generates a random password on first
start if not set explicitly).
- MODRINTH_PROJECTS: LuckPerms v5.5.17-bukkit (OrIs0S6b) +
CoreProtect v23.1 (HD2IvrxS), both release-pinned.
server/plugins/luckperms/groups.json
LuckPerms group export (authoritative source for group definitions):
- default basic gameplay commands only (/help, /msg, /me, /reply)
- pamplina-team narrative operator scope (time, weather, gamemode,
tp, give, effect, difficulty, say, title, summon, kill)
- op wildcard (*), human admin only; inherits pamplina-team
server/plugins/luckperms/README.md
Apply and update procedure (import cmd, add-user cmd, re-export cmd).
.env.example backup config keys documented (BACKUP_DEST, BACKUP_INTERVAL, RCON_PASSWORD).
.gitignore ./server/backups/ and LuckPerms H2 DB excluded.
docs/server-overhaul.md backup restore procedure, CoreProtect rollback
command, LuckPerms hierarchy documented.
Verified locally:
- docker stats shows mem_limit applied (container capped at 14 GB).
- /usr/bin/backup now succeeds: RCON auth + save flush + tar.gz written
to ./server/backups/.
- LuckPerms 5.5.17 and CoreProtect 23.1 both load and enable on
Purpur 1.21.11 (CE edition loads cleanly despite 1.21.11 not yet
appearing in Modrinth's game-version tag list for CP 23.1).
- lp listgroups shows default / pamplina-team / op; groups.json
exported and readable.
Open items (tracked in plans/DC-126.md):
- Final backup destination (Hetzner Storage Box vs B2).
- op.json migration once a real admin user is on-boarded.
- pamplina-team permission set should be audited against actual
mc_command traces from a rolemaster session.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2 of the Server Setup Overhaul (epic DC-124). DC-130 reduces to docs-only because mc_command already gives Pamplinas operator-level world access — no new primitives needed. Adds a "## Stage Tools — Quick Reference" section to SOUL-rolemaster.md covering all common scene-staging operations Pamplinas can invoke via mc_command, with copy-paste examples and anti-pattern warnings: Sections: - DecentHolograms (/dh create/addline/setline/teleport/delete) - SkinsRestorer (/skin set/url/clear) with disguise-mechanic pattern - Time and weather (with scene-transition combos) - Titles and subtitles (with timing parameters) - Sounds (cheat sheet of useful rolemaster sounds by use-case) - Particles (common stage particles with colour/size guidance) - Targeting syntax (@A, @p, /execute as ... at @s patterns) - Anti-patterns table (what NOT to do: /op, /stop, skin-without-restore, fill-without-perceive, holograms without cleanup logging) Note: DecentHolograms and SkinsRestorer activate when DC-127 lands. The cheatsheet is present now so cast sessions after DC-127 merges have the reference immediately without a second commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mine-photo is dead code — the screenshot pipeline was migrated to prismarine-viewer + puppeteer (server.js:253) but the import was left behind. The package crashes at module load on Node 22 because it calls fs.globSync at top level on a path that doesn't resolve cleanly, and older pinned commits are even worse (broken texture loader). - Remove mine-photo from package.json dependencies - Stub the Camera class inline; the throw guards against future code that accidentally calls `new Camera(bot)` without realising the prismarine path is the only one wired up. This unblocks the bot for everyone running Node 22 (now the system default after the recent NodeSource upgrade from 20.x to 22.x). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds three Modrinth-pinned visual plugins on top of the DC-126 baseline (LuckPerms + CoreProtect): - skinsrestorer:2PjHGlwd (15.12.0) — real Mojang skins under offline mode - decentholograms:t9gURTWO (2.9.10) — stage holograms (DC-130 cheatsheet) - tab-was-taken:FewsxQmS (6.0.2) — header/footer + group-coloured nametags LuckPerms group prefixes (default &f, pamplina-team &d[★], op &c[OP]) set via console; TAB consumes them through %luckperms-prefix% so no TAB groups override is needed. Updated groups.json export captures the new prefix nodes. Plugin data files (TAB config, DH holograms) live under the gitignored server/data/ tree; the runbook for reproducing them is in plans/DC-127.md under "Progress". The DH file format gotcha (canonical pages-lines-content structure required, hand-written shorthand NPEs in loadHolograms) is documented there. Resource pack delivery (Better Leaves + Clean Glass) deferred — needs a stable host URL. Tracked as the open item in plans/DC-127.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mitigates the safety risk inherent to ONLINE_MODE=false on a VPN-reachable server frequented by minors. - ENFORCE_WHITELIST=true in compose. Connection now requires an explicit whitelist entry; manage with `whitelist add|remove` over rcon. Verified by watching a non-whitelisted bot get rejected with the standard "You are not whitelisted on this server!" message. - ChatFilter installed via Modrinth pin (chatfilter-zepsizola:UlCRrLx8). Loads cleanly on Purpur 1.21.11 even though Modrinth metadata only lists up to 1.21.8 — same forward-compat pattern as CoreProtect (DC-126). ES wordlist is a TODO once the plugin generates its config tree. - docs/privacy.md captures the data inventory (server logs, CoreProtect, backups, agent traces), retention table, and a parent-deletion runbook covering whitelist removal, CoreProtect rollback, LuckPerms clear, and usercache/playerdata cleanup. Whitelist seeds are runtime data (server/data/ is gitignored), so this PR ships the gate but not the entries — operators add their own. Pamplinas (rolemaster bot) and Hermes-Clio (cross-project bot) are pre-seeded on the dev box. Plan task nicoechaniz#3 (chat history rotation) is a no-op: Purpur already rotates server logs daily under server/data/logs/<date>.log.gz. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Closes the "player analytics" half of DC-132 immediately and scaffolds the "agent operational metrics" half. - Plan plugin (Modrinth pin plan:egk2fxRL, 5.7+build.3306). Webserver bound to 127.0.0.1:8804 — not exposed publicly. SQLite backend (default). LuckPerms extension auto-registers. GeoLite2 deliberately not enabled (city geolocation isn't useful at current scale and adds an EULA + external dependency). - scripts/agent-metrics-report.py reads ~/.hermes/metrics/<cast>/<date>.jsonl and aggregates four metric families: turns/tokens, tool distribution (top 5), heartbeats with per-minute rate, tool failures. JSONL schema documented in the script docstring; emitters are the open item. The schema lives in the script (single source of truth). Once the emission hooks land in agent_loop.py + gateway/platforms/daemoncraft.py the report will populate without further changes here. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Closes the daemoncraft side of DC-132's emission hook. Pairs with the gateway-side emitter in hermes-agent (separate PR). - agent_loop.py: _emit_metric() helper, gated on MC_METRICS_CAST env var. Best-effort writes to ~/.hermes/metrics/<cast>/<date>.jsonl, wrapped in bare except so a failed write can never break the heartbeat loop. Heartbeat events fire alongside the existing send_heartbeat_context. - daemoncraft.py launcher passes MC_METRICS_CAST=<cast_name> in the agent env, activating the emitter. No env var → emitter no-ops, so this is safe for ad-hoc invocations of agent_loop.py. Smoke-tested: synthetic _emit_metric calls produce well-formed JSONL that scripts/agent-metrics-report.py aggregates correctly. Schema lives in scripts/agent-metrics-report.py docstring (single source of truth). Once the gateway emitter PR (hermes-agent) lands, the report script populates fully without further changes here. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Review — DC-132 Plan plugin + agent metrics report scaffoldSolid scaffold. Two risks and one cross-repo dependency note. Risk — gateway-side metric events missingThe report script (
This PR only wires the heartbeat emitter in Until those emitters land, the daily report will show heartbeats but zero turn/tool data. This is documented in Risk — JSONL append is not atomic
If the agent_loop process is killed mid-write, the file may contain a truncated line. The report script skips Fix (optional, low priority): write to a temp file in the same directory, then Risk — Plan plugin default bind addressSame note as in PR #3 review: please confirm that Plan binds to Nits
LGTM once the cross-repo dependency is tracked. |
Audit found: Plan/config.yml lives under the gitignored server/data/ tree, so the 127.0.0.1 bind I set live in DC-132 doesn't survive a fresh clone or compose recreate. Plan defaults to 0.0.0.0, which exposes the dashboard publicly on host-network mode — including any auth bypass it ships with. Mirror the LuckPerms groups.json pattern: don't track the whole 100-line generated config (would force a rebase on every Plan version bump), track just the security-critical setting and a documented apply procedure. server/plugins/plan/README.md captures: - Why we override (default is publicly bound under host-network). - The one-line sed + plan reload to apply. - Verification commands (curl localhost vs public-ip). - Why the snippet pattern beats committing the whole config.yml. Plan updated to reference this with a⚠️ note. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Audit suggested temp+rename or O_APPEND. POSIX guarantees writes shorter than PIPE_BUF (typically 4 KB on Linux) are atomic when O_APPEND is set, which is enough for our line lengths. Even concurrent emitters or a process kill mid-write can no longer leave a half-written line. Functional impact is small — the report script tolerates truncated lines via JSONDecodeError continue — but eliminating them at the source is cheap and removes one silent-data-loss path. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Thanks for the review — all three points addressed.
Live validation: with Pamplinas running for ~2 min on the integration branch, |
Re-review — PR #5 (DC-132 Plan plugin + metrics)All audit findings from the first review are addressed.
LGTM. No blockers. |
…te, interoception Implements all 4 priority fixes from the Claude+Grok forum review (2026-05-31): 1. GAP #5 — L4 judge verdict feedback into next heartbeat - agent_loop.py: extract l4_verdict from /judge/pending, compact format - daemoncraft.py: inject [L4 last] line into heartbeat prompt - server.js: judgeAction sets initiator='l4_agent' + ts for seconds_ago 2. GAP #1+#2 — Registry normalization + real cancellation - action-registry.js: REG_KEY alias helper + direct aliases for dig/place/collect/place_fill - mutex.js: stopDigging() + ON_ABORT dispatch with 750ms timeout + performance.now() - server.js: REG_KEY in /action wrapper, ON_ABORT in /action/stop finally block 3. GAP #3+#4 — Auto-eat gated behind ENABLE_AUTO_EAT_PLUGIN env var - server.js: plugin load + config wrapped in feature flag (default: disabled) - runner/thread.py: ensure eat path works with plugin disabled 4. Interoception enrichment — body_activity block in heartbeat - agent_loop.py: compact body_activity string from runner + action data - daemoncraft.py: [Body] line in prompt Tests: registry-normalization.test.js (10 tests), test_l4_verdict_injection.py Co-authored-by: Grok Build <grok@x.ai>
Implements DC-132 (Observability) from the DC-124 epic. Stacks on top of #4 (DC-131 Safety) which stacks on #3 (Server Setup Overhaul). Each PR's
MODRINTH_PROJECTSline extends the previous one — merge in order or rebase will be needed.What's in
plan:egk2fxRL, 5.7+build.3306). Webserver bound to127.0.0.1:8804— not exposed publicly. SQLite backend (default). LuckPerms extension auto-registers. GeoLite2 deliberately not enabled (city geolocation isn't useful at single-server scale and adds an EULA + external dependency).scripts/agent-metrics-report.pyreads~/.hermes/metrics/<cast>/<YYYY-MM-DD>.jsonland aggregates four metric families: turns/tokens, tool distribution (top 5), heartbeats with per-minute rate, tool failures. JSONL schema documented in the script docstring (single source of truth).What's deliberately deferred
agent_loop.pyandgateway/platforms/daemoncraft.pycross repo boundaries (daemoncraft + hermes-agent). These will land alongside the gateway DC-123 fix so the schema between emitter and reader stays consistent. The report script gracefully prints "no events" until the emitters land.Test plan
docker compose up -d minecraft— Plan loads cleanly on Purpur 1.21.11.curl http://127.0.0.1:8804/returns 302 (dashboard live).scripts/agent-metrics-report.pywith synthetic JSONL aggregates turns, tokens, tools, heartbeats, failures (verified against a 7-event fixture).🤖 Generated with Claude Code