- Migration: adds current_file_id, latest_file_id, is_ignored columns - ModpackApiService: all 4 platforms now return file_id in response - CheckModpackUpdates: file ID comparison (preferred) with string fallback - detectCurseForge: extracts manifest['version'] as installed_version - Cron skips is_ignored servers - Detection priority: manifest version > egg var > DB record > seed latest Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8.4 KiB
Chronicler Dispatch — v1.1.0 Full Architecture Plan (Gemini Consultation Complete)
Date: April 13, 2026 From: Chronicler #84 — The Meridian To: Code
Context
Gemini consultation complete. Michael has reviewed and approved the plan.
Full consultation: firefrost-operations-manual/docs/consultations/gemini-modpackchecker-ux-overhaul-2026-04-12.md
No time pressure — Michael wants to get this right. These are v1.1.0 priorities in order.
Priority 1: File ID Comparison (Foundation)
The problem: We're comparing messy display name strings ("ATM10-6.5" vs "ATM10-6.6"). Unreliable and ugly.
Gemini's fix: Use sequential File IDs from CurseForge/Modrinth. latest_file_id > current_file_id = update available. Clean, reliable, platform-agnostic.
Schema migration needed:
ALTER TABLE modpackchecker_servers
ADD COLUMN current_file_id VARCHAR(64) NULL,
ADD COLUMN latest_file_id VARCHAR(64) NULL;
API changes:
ModpackApiService::fetchLatestVersion()should also returnfile_id- CurseForge: use the file's
idfield - Modrinth: use the version's
idfield - FTB: use the version
id - Technic: use the build number
Version comparison logic:
// Prefer file ID comparison if available
if ($currentFileId && $latestFileId) {
$updateAvailable = $latestFileId !== $currentFileId;
} else {
// Fallback to string comparison
$updateAvailable = $currentVersion !== $latestVersion;
}
Priority 2: Date-Time Seeding Heuristic
The problem: First run seeds current_version = latest_version, which is wrong for servers that have been running old versions.
Gemini's fix: On first detection, fetch the platform's file history. Find the release closest to (but not after) modpack_installations.created_at. That's the assumed current version.
private function seedCurrentVersion(string $platform, string $modpackId, ?string $installDate): array
{
if (!$installDate) {
// No install date — fall back to latest
return $this->apiService->fetchLatestVersion($platform, $modpackId);
}
$allFiles = $this->apiService->fetchFileHistory($platform, $modpackId);
$assumedCurrent = collect($allFiles)
->filter(fn($f) => $f['releaseDate'] <= $installDate)
->sortByDesc('releaseDate')
->first();
return $assumedCurrent ?? $this->apiService->fetchLatestVersion($platform, $modpackId);
}
New method needed: ModpackApiService::fetchFileHistory(platform, modpackId)
- CurseForge:
GET /v1/mods/{modId}/files— returns all files with dates - Modrinth:
GET /project/{id}/version— returns all versions with dates - Returns: array of
['id', 'version', 'displayName', 'releaseDate']
Also: If manifest.json exists and has a version field — use that directly as current_version instead of any heuristic. We confirmed this works on 5 servers (Mythcraft, Create Plus, Beyond Depth, Beyond Ascension, Homestead). Manifest version is truth — no guessing needed.
Priority 3: Zero-Click Widget with Recalibrate
The problem: Widget requires clicking, shows only latest version string, no comparison, no context.
Gemini's redesign:
New GET endpoint needed:
GET /api/client/extensions/modpackchecker/servers/{server}/status
Returns cached DB data (NO external API calls):
{
"configured": true,
"platform": "curseforge",
"modpack_name": "MYTHCRAFT 5",
"current_version": "Update 5",
"latest_version": "Update 5",
"current_file_id": "6148845",
"latest_file_id": "6148845",
"update_available": false,
"last_checked": "2026-04-13T04:00:00Z",
"detection_method": "installer"
}
New GET endpoint for Recalibrate dropdown:
GET /api/client/extensions/modpackchecker/servers/{server}/releases
Returns last 10 releases from platform (DOES make external API call):
{
"releases": [
{"file_id": "6148845", "display_name": "MYTHCRAFT 5 | Update 5", "release_date": "2026-03-27"},
{"file_id": "6089021", "display_name": "MYTHCRAFT 5 | Update 4.1", "release_date": "2026-03-11"},
...
]
}
New POST endpoint for Recalibrate save:
POST /api/client/extensions/modpackchecker/servers/{server}/calibrate
Body: { "file_id": "6089021", "version": "Update 4.1" }
Sets current_version, current_file_id, is_user_overridden = true
Widget TSX redesign:
const ModpackVersionCard: React.FC = () => {
const uuid = ServerContext.useStoreState(state => state.server.data?.uuid);
const [data, setData] = useState<StatusData | null>(null);
const [showCalibrate, setShowCalibrate] = useState(false);
const [releases, setReleases] = useState([]);
// Zero-click: load on mount from cache
useEffect(() => {
if (!uuid) return;
http.get(`/api/client/extensions/modpackchecker/servers/${uuid}/status`)
.then(res => setData(res.data))
.catch(() => {});
}, [uuid]);
const openCalibrate = () => {
http.get(`/api/client/extensions/modpackchecker/servers/${uuid}/releases`)
.then(res => setReleases(res.data.releases));
setShowCalibrate(true);
};
// Render: platform icon | current → latest | Calibrate button
// If update_available: orange background
// If unconfigured: gray with "Not configured"
// If showCalibrate: dropdown showing last 10 releases to click
};
Priority 4: is_ignored Flag
The problem: Vanilla, FoundryVTT, Hytale servers pollute the DB and show unconfigured widgets.
Note from Michael: Nest ID filtering does NOT work — his eggs span multiple nests.
Solution:
ALTER TABLE modpackchecker_servers ADD COLUMN is_ignored BOOLEAN DEFAULT FALSE;
- Widget shows "Hide (Not a Modpack)" button for unconfigured servers
- Clicking sets
is_ignored = true, widget unmounts - Cron skips servers where
is_ignored = true - Admin panel shows ignored servers in a separate list with "Restore" option
Priority 5: BCC Log Parsing (Optional Signal)
Research findings from live testing:
latest.logIS readable viaDaemonFileRepository::getContent('logs/latest.log')BetterCompatibilityCheckermod prints:Loaded BetterCompatibilityChecker - Modpack: {name} | Version: {version}- Mythcraft 5 has BCC but it's unconfigured (
CHANGE_ME) - Most packs (ATM10 etc.) don't have BCC at all
- Log only has startup lines if server recently restarted
Recommended approach: Add as optional detection step 4 in the cron (after modpack_installations, egg vars, file detection):
// Step 4: BCC log parsing
private function detectFromLogs(Server $server): ?array
{
try {
$log = $this->fileRepository->getContent('logs/latest.log');
if (preg_match('/Loaded BetterCompatibilityChecker - Modpack: (.+?) \| Version: (.+)/', $log, $m)) {
if ($m[1] !== 'CHANGE_ME' && $m[2] !== 'CHANGE_ME') {
return ['name' => trim($m[1]), 'version' => trim($m[2])];
}
}
} catch (\Exception $e) {}
return null;
}
Document in BuiltByBit: "Servers with BetterCompatibilityChecker configured will have the most accurate version detection. Version updates on server restart."
Also: Manifest Version Audit Results
Ran Wings filesystem audit on all 22 servers for manifest.json:
| Status | Servers |
|---|---|
| ✅ Has manifest with version | Mythcraft 5 (Update 5), Create Plus (0.9.0), Beyond Depth (Ver12.3.2), Beyond Ascension (Ver2.4.1), Homestead (1.2.9.4) |
| ⚠️ Manifest but no version field | Society, All of Create NC, Otherworld, Submerged 2 |
| ❌ No manifest | 13 servers (installed via modpack installer) |
When detectCurseForge() finds a valid manifest, it should also extract manifest['version'] as installed_version and use it as current_version. This is truth — no heuristic needed for these 5 servers.
Summary: What Needs to Be Built
| Priority | Task | Complexity |
|---|---|---|
| 1 | File ID fields in DB + comparison logic | Medium |
| 2 | fetchFileHistory() + date-time seeding | Medium |
| 2b | manifest['version'] as current_version | Small |
| 3 | New status/releases/calibrate endpoints | Medium |
| 3b | Widget TSX redesign (zero-click + Recalibrate) | Large |
| 4 | is_ignored flag + Hide button | Small |
| 5 | BCC log parsing in cron | Small |
Take them in order. File ID comparison first since it's foundational.
— Chronicler #84, The Meridian Fire + Frost + Foundation = Where Love Builds Legacy 💙🔥❄️