2.0.1
v2.0.1 — The Ecosystem Era: Polish Pass
v2.0.0 opened the Ecosystem Era (Manager Core pricing + Structure Manager threat alerts). v2.0.1 is the Polish Pass on the same era: faster director workflows, cross-plugin event publishing, Manager Core pricing centralization, and per-surface quality lifts.
Mental model: Same era, deeper craft. Every item is additive. No breaking changes. No new ESI scopes.
Headline features
Discord role picker
Inline "Pick from Discord" button next to every per-type Discord Role ID input on the Notifications settings page. Detects all installed Discord role providers (SeAT Broadcast discord_roles, warlof seat-connector seat_connector_sets, legacy warlof tables); merged + deduped role list, single AJAX fetch per page load. Falls back to plain text input on installs with no Discord plugin detected. No more Developer Mode → right-click → Copy ID → paste, 17 times over.
Moon extraction EventBus publishing
Three new events fire exactly once per extraction per lifecycle stage:
mining.extraction_ready— chunk has fractured, 48h fleet-able window opensmining.extraction_unstable— final 2h capital-safety window before expirymining.extraction_expired— window closed
Rich payload per event: extraction_id, moon/structure context, corporation_id for visibility scoping, lifecycle timestamps, auto_fractured + is_jackpot flags, estimated_value, plus a url field deeplinking to MM's per-extraction detail page. New cron mining-manager:scan-extraction-events runs every 5 minutes with per-stage latches in the new moon_extraction_event_log table. Standalone-safe via class_exists guard on \ManagerCore\Topics — without Manager Core the scanner is a friendly no-op.
Notification Routing Map
New read-only Settings tab (Settings → Routing Map) showing every notification type, the webhooks it fires through, the corp scope, and the resolved Discord role that will actually be pinged at send time. Resolution uses the same precedence the dispatcher uses (L1 per-type override / L2 webhook legacy role; tax_invoice hard-blocked from role pings). Summary chips: total / globally enabled / delivering / "enabled but firing nowhere" (warning). Flags extraction_at_risk / extraction_lost as dormant when Manager Core or Structure Manager is missing.
Metenox Drill Cargo readout
New director-only sidebar page Mining Manager → Metenox Cargo (also as a tab on every moon page). One card per Metenox Moon Drill showing:
| Surface | What it shows |
|---|---|
| Ore composition | Per-stack quantity, m³ volume, ISK valuation at current market prices, percent-of-cargo bars |
| Bay fill indicator | Color-graded progress bar showing X / 500,000 m³ · YZ% full |
| Header chips | Drill count · ISK in cargo · avg bay fill % (with critical-bay warning) · oldest data sample |
| Structure context | Solar-system name (with numeric id as muted suffix), structure state pill, last-polled timestamp (yellow if stale > 2h) |
Bay capacity sourced from dgmTypeAttributes attribute 5693 (Metenox-only) and cross-verified against EVE Ref's published value at https://everef.net/types/81826.
Scope model. Directors are locked to the Moon Owner Corporation (matches the Past Extractions table convention — keeps the page and the cargo-full notification aligned, so what they see is what gets alerted). Operators with mining-manager.admin land on the same Moon Owner Corp default but get a corp-picker dropdown in the page header — every metenox-owning corp plus an "All corps" aggregate option, plus a one-click "Back to Moon Owner" shortcut. The picker is read-side only; notifications still scope to Moon Owner Corp.
Cross-plugin contract. PluginBridge capability mining.metenox.cargoSnapshot($structureId) returns [type_id => quantity] for any Metenox so Structure Manager (or future consumers) can render the bay contents on their own structure detail surfaces.
Metenox Cargo Bay Full notification
New metenox_cargo_full notification type fires when a drill owned by the Moon Owner Corporation crosses the configurable fill threshold (default 85%, configurable 50-99% from Settings → Notifications). Yield-stopping warning specifically — drilling stops when the bay caps out but the structure stays online and safe (different from extraction_at_risk, which is structure-safety). Dedup-latched against repeats while still over threshold; resets implicitly when cargo is pulled (fill drops back below threshold). Cron mining-manager:scan-metenox-cargo-fill runs every 5 minutes. Includes ISK valuation of the cargo and a deeplink to the Metenox Cargo page. Standalone — no Manager Core or Structure Manager required.
At typical Metenox production rates (~1,500-2,000 m³/hour), 500,000 m³ fills in ~10-14 days. The default 85% threshold = ~425,000 m³ = ~2 days of operator lead time before the bay caps and drilling stops.
Local time auto-conversion and live countdowns
New eve-time.js wraps every server-rendered EVE timestamp across the events and moon pages. Hover any timestamp → tooltip with full local time formatted via Intl.DateTimeFormat against the browser-detected IANA timezone (same mechanism Discord, Google Calendar, and GitHub use; DST handled automatically). High-priority surfaces (active extractions, upcoming events, calendar, my-events) opt into an inline " · HH:MM local" pill via data-show-local for at-a-glance reading.
New eve-countdown.js replaces Carbon::diffForHumans() text with a 1-second-tick loop. Color-graded: green (>1d) → yellow (1d-1h) → red+bold (<1h) → muted grey (past target).
Event create / edit forms gained an Enter time in: EVE/UTC · My local toggle with live confirmation box. DST-safe via the browser's IANA timezone; server still always stores UTC regardless of input mode.
Manager Core pricing centralization
The pricing fields that used to live in MM (market choice, variant, MC subscribe toggle) all moved to MC's single source of truth. MM's Pricing settings tab rewritten as a read-only status panel plus deep-link to MC's centralized Pricing Preferences page.
Live cache invalidation: when an operator changes MM's pricing preference in MC's UI, MC publishes the new pricing.preference_changed EventBus topic, MM's PricingPreferenceChangedHandler flushes the local price cache so the next read picks up the new provider immediately (no wait for the 4h refresh cron).
Per-plugin provider override. MM passes its plugin key as the 4th arg to pricing.getPrices, so Manager Core can route MM via Janice while Structure Manager stays on Fuzzwork (or any combination) from a single MC UI. When MM's preference has provider_override='janice', MC live-fetches via Janice instead of reading the cached Fuzzwork-Jita data. One batch upstream call per 4h refresh cycle — acceptable bandwidth even with strict-rate-limit providers like Janice.
Jita fallback now works uniformly on the MC path (previously skipped). The dead bridge.requireMinimumVersion call was dropped from MM — Manager Core v1.0.0 is MC's first stable release, so there is no older MC to compare against; the class_exists guard on \ManagerCore\Topics is the actual "is MC available?" gate.
Operator workflow. MC Settings → set Janice API key → MC Pricing Preferences → find Mining Manager row → change Provider Override from "Use market's provider (Fuzzwork)" to "Janice" → Save → MC publishes pricing.preference_changed → MM flushes cache → next refresh hits Janice live.
Visual and UX polish
Jackpot rendering hardened against custom SeAT themes. 18 inline-styled jackpot elements across 6 blades (Report Jackpot button, JACKPOT banners, every "2x multiplier" indicator badge) converted to override-resistant .mm-jackpot / .mm-jackpot-badge / .mm-jackpot-alert CSS classes with !important on background/color/border plus nested icon color. Custom SeAT theme installs (custom-layout.css) that use !important on .btn-warning no longer wash the black text out into invisible yellow-on-yellow renderings.
Help & Documentation reorganised to match the canonical Structure Manager layout: Plugin Information → new Version Status card (installed vs latest Packagist with Up-to-date / Update available / Pre-release / Dev branch / Unknown pill via the ported VersionChecker service) → Welcome → green .whats-new-box What's New → What is Mining Manager? → Core Features. VersionChecker reads Composer's InstalledVersions API with config fallback, Packagist call cached 6h with 3s timeout and graceful failure (Help page never errors on Packagist hiccup). Plugin Information version badge switched from the Shields.io GitHub endpoint to the Packagist endpoint so the badge text no longer leaks rate-limit error strings when Shields.io's GitHub token pool runs dry.
Diagnostic page aligned to the suite-wide standard. Default landing tab is now Health Checks (renamed from "System Status"). Nav reordered to Tier 1 universal tabs first (Health Checks → Master Test → System Validation → Settings Health → Data Integrity → Tax Trace), then Notification Testing, then plugin-specific traces, then DEV-only Test Data with red DEV badge. Every Tier 1 tab opens with a "What this tab does / When to use / Heads up" intro paragraph.
Metenox cargo subsystem is covered in every Tier 1 tab:
- Health Checks: scanner cron under a new "metenox" category and drill / MoonMaterialBay / alert-latch counts
- System Validation: dedicated "Metenox Cargo Subsystem" card with seven server-side health probes (type 81826 in invTypes, migration 000017 schema bits, solar_systems populated, threshold setting in range, scanner cron registered, Moon Owner Corp set) plus overall Healthy / Warnings / Critical pill
- Settings Health: Notifications group iterated so the new threshold setting surfaces with source attribution
- Data Integrity: stale latches, orphan latches, orphan MoonMaterialBay asset rows
- Notification Testing:
metenox_cargo_fullentry with realistic test data so operators can smoke-test the new alert end-to-end
New artifacts (forward-only, backward-compat)
| Category | Files |
|---|---|
| Services | DiscordRoleResolver, VersionChecker, Moon/MetenoxCargoService, Pricing/PricingPreferenceChangedHandler |
| Console commands | ScanMoonExtractionEventsCommand, ScanMetenoxCargoFillCommand |
| Migrations (additive only) | 000016_create_moon_extraction_event_log_table, 000017_add_metenox_cargo_full_notification, 000018_drop_unused_mc_pricing_settings |
| JS assets | eve-time.js, eve-countdown.js |
CSS cache buster bumped to ?v=4 for the new .whats-new-box, .mm-jackpot*, .eve-countdown-*, and .diag-tab-intro primitives.
Compatibility
- Fully additive. No existing schema changes (migration 000018 only deletes two dead MM-owned settings rows from the v2.0.0 MC integration). No public API change. No setting schema breaks.
- Without Manager Core installed: the MC pricing path is a no-op and MM falls back to direct Fuzzwork / Janice fetches; the event publisher is a no-op; nothing changes from v2.0.0 behaviour.
- With Manager Core: requires Manager Core v1.0.0 (MC's first stable release) with the new pricing capabilities —
pricing.getPreferenceForPlugin,pricing.preferencesUrl, and thepricing.preference_changedtopic. Defensive try/catch on every bridge call so an isolated capability failure falls back to a safe default instead of erroring the page. - Without Structure Manager installed:
extraction_at_riskandextraction_lostnotifications stay dormant (unchanged from v2.0.0). - Metenox Cargo page is gated by
mining-manager.director. Members and accountants don't see the sidebar entry or have route access. Data is read from SeAT's existingcorporation_assetsmirror — no backfill required. - Subscribers of the new extraction events honour visibility scoping via the
corporation_idfield on each payload.
Upgrade
SeAT Docker stack down/up. Migrations and seeders run automatically on container boot. New schedule rows are seeded via firstOrCreate so any operator cron customisations are preserved.
docker compose -f docker-compose.yml -f docker-compose.mariadb.yml -f docker-compose.traefik.yml down
docker compose -f docker-compose.yml -f docker-compose.mariadb.yml -f docker-compose.traefik.yml up -d