compat: audit Productive Metalworks fail-closed boundary - #46
Conversation
There was a problem hiding this comment.
Code Review
This pull request integrates compatibility support for the "Productive Metalworks" mod (version 1.21.1-1.15.0) using the Compat Kit. Following an audit of 23 recipe-class candidates, a fail-closed boundary is established with zero registered production families due to their reliance on live multiblock or block-entity states. The changes include adding the audit, contract, compat module, and an isolated GameTest fixture with eight tests, alongside updating the compatibility matrix to lock the new 14-mod workload baseline of 12,232 recipes. The review feedback recommends improving the robustness of the registry checks in both the isolated fixture and the compatibility matrix by verifying the namespace in addition to the path prefix, and correcting a misleading error message regarding the unique recipe type count.
| if (!ModList.get().isLoaded("productivemetalworks") | ||
| || AutoStorage.MACHINE_DESCRIPTOR_REGISTRY.keySet().stream() | ||
| .anyMatch(id -> id.getPath().startsWith("productivemetalworks_")) | ||
| || AutoStorage.RECIPE_FAMILY_REGISTRY.keySet().stream() | ||
| .anyMatch(id -> id.getPath().startsWith("productivemetalworks_"))) { | ||
| helper.fail("Productive Metalworks unsafe foundry contract was registered"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Failing with a misleading message when the mod is not loaded makes debugging difficult. Additionally, checking only the path prefix can miss registrations under the productivemetalworks namespace. Separating the mod-loaded check and expanding the registry search to include the namespace improves test robustness.
if (!ModList.get().isLoaded("productivemetalworks")) {
helper.fail("Productive Metalworks mod is not loaded");
return;
}
if (AutoStorage.MACHINE_DESCRIPTOR_REGISTRY.keySet().stream()
.anyMatch(id -> id.getNamespace().equals("productivemetalworks") || id.getPath().startsWith("productivemetalworks_"))
|| AutoStorage.RECIPE_FAMILY_REGISTRY.keySet().stream()
.anyMatch(id -> id.getNamespace().equals("productivemetalworks") || id.getPath().startsWith("productivemetalworks_"))) {
helper.fail("Productive Metalworks unsafe foundry contract was registered");
return;
}| if (types.size() != 4) { | ||
| helper.fail("Audited Productive Metalworks recipe type is empty"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
The error message is misleading when the unique recipe type count is not equal to 4, as it incorrectly claims the recipe type is empty. Updating the message to reflect the actual mismatch improves clarity.
| if (types.size() != 4) { | |
| helper.fail("Audited Productive Metalworks recipe type is empty"); | |
| return; | |
| } | |
| if (types.size() != 4) { | |
| helper.fail("Expected 4 unique audited Productive Metalworks recipe types, but found " + types.size()); | |
| return; | |
| } |
| if (AutoStorage.MACHINE_DESCRIPTOR_REGISTRY.keySet().stream() | ||
| .anyMatch(id -> id.getPath().startsWith("productivemetalworks_")) | ||
| || AutoStorage.RECIPE_FAMILY_REGISTRY.keySet().stream() | ||
| .anyMatch(id -> id.getPath().startsWith("productivemetalworks_"))) { | ||
| helper.fail("Productive Metalworks fail-closed boundary changed"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Checking only the path prefix can miss descriptors or families registered under the productivemetalworks namespace. Including a namespace check prevents potential bypasses of the fail-closed boundary.
| if (AutoStorage.MACHINE_DESCRIPTOR_REGISTRY.keySet().stream() | |
| .anyMatch(id -> id.getPath().startsWith("productivemetalworks_")) | |
| || AutoStorage.RECIPE_FAMILY_REGISTRY.keySet().stream() | |
| .anyMatch(id -> id.getPath().startsWith("productivemetalworks_"))) { | |
| helper.fail("Productive Metalworks fail-closed boundary changed"); | |
| return; | |
| } | |
| if (AutoStorage.MACHINE_DESCRIPTOR_REGISTRY.keySet().stream() | |
| .anyMatch(id -> id.getNamespace().equals("productivemetalworks") || id.getPath().startsWith("productivemetalworks_")) | |
| || AutoStorage.RECIPE_FAMILY_REGISTRY.keySet().stream() | |
| .anyMatch(id -> id.getNamespace().equals("productivemetalworks") || id.getPath().startsWith("productivemetalworks_"))) { | |
| helper.fail("Productive Metalworks fail-closed boundary changed"); | |
| return; | |
| } |
Follow-up evidence (
|
| Check | Result |
|---|---|
| Static RED→GREEN focused unittest | pass |
./gradlew runProductivemetalworksGameTestServer via heavy_gradle.py |
All 8 required tests passed |
./gradlew runCompatibilityMatrixGameTestServer via heavy_gradle.py |
coexistence + classification passed ([++X]); craftable latency gate failed twice on unrelated craftable_prepare_ms (75.940 then 59.326; limit < 50) after one allowed retry — not caused by these assertion changes; threshold not weakened |
| Docs | checked; no documentation update required |
No merge and no review-bot trigger from this worker.
0d966e6 to
40cf828
Compare
Audit 1.15.0 Foundry/casting candidates as unsafe without a multiblock transaction contract (outcome C), register an empty present-mod module, and lock the boundary with fixture, matrix, and CI gates. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Reject namespace-owned registrations as well as path-prefixed ones, separate the missing-mod failure message, and report the actual unique recipe-type count. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Productive Metalworks still contributes Foundry/casting datapack recipes to RecipeManager while remaining fail-closed for Auto Storage families, so the combined Theurgy + PMW coexistence gate must lock 13,311 rather than 12,736. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Preserve Outcome C fail-closed PMW on current main (Bees/Draconic/CAA/AdvAE), move coexistence to descriptor-owned matrix digests, and lock cross-mod recipe inventories measured with the full optional pack. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Matrix recipe-inventory SHA updates must keep committed manifest contract_sha256 values in sync with the reviewed contracts. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Updating AE2's coexistence recipe-inventory digest changes the contract SHA, so committed generation/conformance/resource plans must follow. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0b21257 to
f724e1c
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f724e1c41d
ℹ️ 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".
via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. Delightful! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Rebase onto #78/#46 keeps peer descriptors untouched; the isolated fixture now verifies this module's own digest evidence. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
Closes #42.
Evidence-backed Outcome C (fail-closed) for Productive Metalworks
1.21.1-1.15.0: the current Compat Kit audit rejects all five actual Foundry/casting/alloying/entityRecipeclasses because no complete multiblock/live-state simulate-then-commit contract exists. The present-mod module loads and registers zero stations, resource kinds, and recipe families through the public modular SDK. Vanilla-class recipes under the PMW namespace remain covered by Auto Storage's built-in exact families.The GitHub issue #42 body was initially mis-templated as Oritech; the authoritative scope of this branch is Productive Metalworks per the compatibility coordinator and umbrella #33.
Rebased onto current
origin/main61aaf32f4faa263d8c3c6c6281f5ad81adc2e8b7(Railcraft PR #58). The descriptor-owned coexistence matrix now includes Productive Metalworks without central Java/workflow lists.Reproducible evidence
1.21.1-1.15.07884786curse.maven:productivemetalworks-1184570:78847861dcf9e10fc457c92d9ed466336104927169817cd509ca9ca69dec734f994d124JDKDigital/productivemetalworks@7c6483c51e1a9def633a939ea75e0018dd079ffaMigration from the legacy scanner removed 18 name-shaped false positives (datagen builders, JEI categories, serializers,
RecipeHelper,CastingRecipeEvent, andICastingRecipe). The five actual candidates are:BlockCastingRecipeEntityMeltingRecipeFluidAlloyingRecipeItemCastingRecipeItemMeltingRecipeAll five remain explicitly rejected. No typed resources or approximate Foundry fuel/cooling behavior were introduced.
Matrix evidence
8519ac6300242a3bb29d02a53598c0b2ab979a05336ede8dec816faef443b8cb29eae13b02c34d2f44c4dd2bbc5aefe44e1dbf054c19c9c6e099f61e6b2367b9387d86fe1576cf7b0f887dea819ced43dcd650f9934d89c6003160b53c010895The 10k matrix passed all three tests. Current local measurements stayed within the existing gates: first switch
1.209 ms, switch p951.312 ms, warm switch p950.499 ms, shared retained index9,202,456bytes, and per-menu retained heap114,675bytes.TDD and verification
Two current-head regressions were observed before correction:
Both now have exact regression coverage in
scripts/test_modular_compat_sdk.py.fcf52ce8e08af1e3b503c4e666bdb28b50049322./gradlew build1.1.24+1.21.1)./gradlew stageAe2CompatAuditAncestryPYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover scripts./gradlew runDatagit diff --checkEvery Gradle invocation in the shared local workspace ran through
heavy_gradle.py.Docs
docs/productivemetalworks-compatibility.mddocs/productivebees-compatibility.mddocs/notes.mddocs/plan.mddocs/roadmap.mdOutcome and remaining risk
C — audited, present-mod tested, fail-closed, and zero custom production families.
Melting, alloying, and casting remain unsupported until a generic contract can represent Foundry multiblock composition, live fuel temperature/speed/consumption/upgrades, casting cooling independent of mutable block-entity/config state, and optional event-injected recipes without approximating those conditions. The representative artifact is CI evidence only and is not a player-facing exact dependency pin.
Test plan
Made with Cursor