Content journal and release hardening - #533
Merged
Merged
Conversation
Skidamek
force-pushed
the
gen/release-hardening
branch
from
August 20, 2026 11:12
178452e to
ad76958
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 178452ed9c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Skidamek
force-pushed
the
gen/release-hardening
branch
from
August 20, 2026 13:47
6db55da to
b29a50a
Compare
Skidamek
force-pushed
the
gen/release-hardening
branch
3 times, most recently
from
August 20, 2026 14:10
e77f036 to
5ec1999
Compare
Skidamek
force-pushed
the
gen/release-hardening
branch
from
August 20, 2026 14:14
5ec1999 to
a42815b
Compare
Skidamek
force-pushed
the
gen/release-hardening
branch
from
August 20, 2026 18:57
d662faf to
ffaa099
Compare
Skidamek
force-pushed
the
gen/release-hardening
branch
2 times, most recently
from
August 21, 2026 10:49
6c91fb3 to
3a62cf9
Compare
Skidamek
force-pushed
the
gen/release-hardening
branch
4 times, most recently
from
August 24, 2026 14:20
1cc50f5 to
e165873
Compare
Skidamek
force-pushed
the
gen/release-hardening
branch
9 times, most recently
from
August 31, 2026 10:09
107e61b to
30929b1
Compare
The storage screen already arms on the first click and only deletes on the second. The fake bridge still compacted immediately, so the release-gate flow timed out looking for Click again to clean.
The file now lives at automodpack/host-modpack/patch-notes.md, is created empty on generate, and is truncated in place after a successful publish instead of being deleted.
Preload and the preview used the same boolean, so a projection-only mod change could say no restart and then prompt. RestartPolicy answers REQUIRED, OFFERED, or NONE for both questions; preload never offers. The preview line and the post-apply screen now tell the same story, and in-game Back returns to the multiplayer hub.
Declining the offer already left the login; completing UPDATE_REQUIRED made the server log a missing-pack failure. JOIN_CANCELLED names the player's answer, logs that they cancelled during the offer, and disconnects without the install kick.
Applied-but-unloaded content used to toast on every DisconnectedScreen. The flag now clears when Join Game finishes, so an in-world kick stays quiet. The autotester can see toast titles and treats a refused connect as the pre-world disconnect that should show the nudge.
Server config declarations move under a 'modpack' section keyed by category name -> group id -> declaration, and the shipped content json mirrors that shape under 'categories'. The category is now where a group lives, not a field on it: the client can no longer invent a bucket, the category key doubles as the player-facing section label, and a typo cannot point a group at a category that does not exist. Category name guards (blank, padding, control characters, 64-char cap matching the group ID cap, case-insensitive duplicates), empty categories and duplicate group ids are loud validation errors. Group ids, requires and breaksWith semantics are unchanged; the group id remains the slug used for requires/breaksWith, saved selections and the host-modpack folder name, while displayName stays the free-form player-facing label. The manifest stays flat and its record shape is unchanged; declared category and group order is now preserved end to end instead of being force-sorted alphabetically.
The screen is now Group Selection, and Modpack details is Modpack Settings; the multiplayer button that opens the installed-pack list becomes Modpacks. Every group sits in a declared category, so the list is always sectioned: headers render in declared order with their groups beneath them, and the invented General bucket, its caption row and the title-casing of category ids are gone - category names are display strings and render verbatim. Conflicts no longer ask: the resolver's deterministic replacement applies immediately and a yellow receipt line names what was turned off and which choice caused it; when even the replacement cannot resolve, the failure line explains why and the selection stays unchanged. The conflict modal and its strings are deleted. The selection summary line gains the estimated download size, deduplicated by SHA-1 so shared files count once. Pack-level change effects in the browser name the pack instead of borrowing the old General label. Locales drop the renamed and deleted keys and fall back to en_us for the rest.
The server config docs now describe the modpack section (category name, then group id, then declaration), the category-name guards, and the declared-order guarantee. An identities note spells out the rename cost tiers: group ids anchor requires/breaksWith, saved selections and the host-modpack folder name; category names anchor saved category choices; displayName is the safe rename. Screen mentions follow the renames to Group Selection and Modpacks.
Scenario server configs declare their groups under the modpack section with real category names, the projection/policy parsers walk categories, and the metadata assertion treats 'category' as the name of the containing category. The fake bridge models the General header row and writes nested active manifests. The first gate run failed here, not in the product: servers silently ran on the default config because the old flat 'groups' key was ignored, which is exactly the stale-fixture shape this fixes.
…ction rows The group selection header compared description line count against a pixel budget, so its last wrapped line drew straight over the platform summary; the description is now capped at the rows actually above it. Below 26.1 the tooltip panel sprite was addressed by its texture path, but blitSprite on 1.21.2+ resolves atlas sprite ids, so 1.21.x drew the missing-texture checkerboard behind every tooltip; those targets now use the sprite id while pre-1.21.2 keeps the path for its hand nine- slice blit. The review screen's History and Customize actions share one auxiliary row instead of two full-width ones.
Skidamek
force-pushed
the
gen/release-hardening
branch
from
September 16, 2026 19:41
9b8a6ee to
b5e6268
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The largest layer of the stack. It replaces the generation store with a content journal, hardens the finished workflow, dissolves loader-core, and supersedes parts of every layer below.
The content journal
The store built in #524..#531 persisted every generation several times over, as a record, a catalogue entry, a commit, a delta, and a checkpoint, and validated each artifact by re-deriving a canonical digest of its content. Every change to that canonicalization made previously written state unreadable. It changed twice in one week, and either change could silently leave hosting dead while clients were still told to sync.
Storage is now a content-addressed object store plus one append-only journal. An entry records the resulting content token, which hashes only what is served (path, sha1, size), the exact policy document bytes kept in the CAS, the patch notes, and the path-level change list. Policy churn therefore never mints a new content identity, and a publish without content changes appends nothing and leaves its patch notes pending for the next real one. Rollback appends a restore entry that hosts exactly the target generation's content and policy.
Everything else is a view rebuilt from the journal head when it mismatches: the served projection document carrying policy, ownership ledger, and head sequence, plus the hosting map. A future canonicalization change is absorbed by the rebuild instead of stranding state. This retires the record, checkpoint, commit, and catalogue machinery, the historical-catalogue wire endpoint, and #531's
GenerationHistoryIndex.Hardening
Shared CAS state publishes with create-only semantics and durable ownership receipts (
SharedObjectOwnership), and objects are verified before cache insertion. Collection keeps objects that other game instances still reference, rejects changed hard-linked cache entries, handles read-only objects, and tells corrupt and missing apart.The update transaction rechecks live files and requests a replan when they changed after planning. Deferred updates run through the detached recovery helper, which now also owns the Windows projection locks; stale settings are invalidated and reviewed consequences survive pending-update recovery. Platform detection yields nothing when it cannot detect anything, so an unknown system resolves platform-agnostic groups only instead of guessing Linux.
Connection lifetimes are bounded. Transfers close cleanly on mid-stream errors and abort on cancel, so a retry gets a fresh connection. Cancelled Modrinth and CurseForge lookups abort instead of returning zero hits, and undownloadable or unlisted hits are dropped.
Restructuring
loader/coreis gone; its client engine and screen seam moved into core and the Minecraft mod. The screen layer is rebuilt around a typedScreenManager/ScreenServiceseam, the group list lives on the shared panel, and dropdowns stop growing past the screen edge.Preloadis replaced byClientLaunchandBootRecovery, and an unreachable modpack server boots the installed pack. Windows natives gained a lock probe that names lock holders and a single native stat for reparse and identity questions. Config gains file groups (synced, excluded, edit-allowed) and admin-declared platforms beyond the auto-detected trio, with docs for both plus client storage maintenance.Loaders and autotester
Loader integration tracks the consolidated mcholepunch artifacts, carries TLS records inside vanilla-shaped frames with encrypted headers (
TlsRecordCamouflage), and requires stable MixinExtras hooks for the critical mixins. Locale catalogues are refreshed.Autotester scenarios gain cache deduplication, a detached supervisor for cleanup, fail-fast interruption handling, retry handling, cold-cache and legacy-target cases, and controlled concurrency.
Known limits
The journal is the history and nothing compacts it. A changed file costs roughly two hundred bytes of line, the server parses the file once per boot, and a client re-fetches the whole journal whenever its mirror is stale. Client-side storage maintenance reclaims old content objects but keeps the mirror whole. If a long-lived server ever feels that, the fix is serving entries after the client's head sequence, since compaction would renumber rollback targets and every client mirror.
Tests
Core coverage for shared CAS publication, ownership receipts, object collection, and projection swaps. Windows native tests. Autotester coverage for cache deduplication, interruption, deferred updates, cold-cache, and legacy targets.