TrackingV2Translator copies v1 integers to the v2 body verbatim. Several of those columns hold a wildcard sentinel that PoracleNG writes itself, emits nothing for on read, and — once jfberry/PoracleNG#217 lands — refuses on write.
The sentinel is coherent on their side: omit the field and the write default stores the sentinel; read the sentinel back and you get null. Omission round-trips. Echoing does not, and echoing is what we do.
PoracleTrackingProxy.PutV2Async maps 422 to AlarmValidationException with no v1 fallback, so this surfaces as a user-facing validation error on edit, not a silent downgrade.
Affected fields
Taken from #217's head — read wildcard (ptrUnless) against the bound the same field now carries.
| Type |
Field |
Stored wildcard |
New bound |
When it bites |
| pokemon |
size |
-1 |
minimum: 1 |
any rule not filtering on size — the default |
| pokemon |
rarity |
-1 |
minimum: 1 |
any rule not filtering on rarity — the default |
| pokemon |
min_iv |
-1 |
minimum: 0 |
any rule with no IV floor |
| raid |
level |
9000 |
1–90 |
every rule with a specific pokemon_id — PoracleNG forces level to 9000 there |
| maxbattle |
level |
9000 |
1–90 |
same |
| nest |
pokemon_id |
0 |
minimum: 1 |
a nest rule tracking any species |
Raid and max battle are the worst of these: the value we echo is one PoracleNG stamped on the row itself, so it isn't a rule anyone typed wrong. level: 90 ("any tier") sits inside the bound and is fine; only the 9000 placeholder fails.
Egg level is already handled — PositiveIntegers in the type spec, added when 5.2.1 shipped minimum: 1 on that one field. This is the same defect, and the other nine types were never checked. Second time on this shape.
Fix
Add a per-type wildcard table to TrackingV2Translator and drop a field from the outbound body when its value equals the sentinel. Dropping is correct rather than merely convenient: v2 PUT is full-replace, and every one of these fields' write default is the sentinel, so omitting stores exactly what was there.
null may work as an explicit clear — the fields are nullable: true and huma should skip the bound on null — but that is unconfirmed upstream, and omission needs no confirmation. Use omission.
Tests
TrackingV2TypeTranslationTests asserts our field tables against 5.2.1's openapi.golden.json, which is what should have caught this and didn't — it checks that every schema field is classified, not that every value we can emit is acceptable. Add a case per row of the table above: a v1 row holding the sentinel must produce a body with that key absent. Watch each one fail first — a test written next to the fix passes whether or not the fix is right.
Then extend the golden assertion to compare our sentinels against the schema's minimum/maximum, so the next bound added upstream fails the build instead of production.
Timing
Not live. The bounds arrive with #217, which is open. We should land the fix before adopting a build that carries it, and pin Poracle:TrackingApiVersion=v1 as the escape hatch if the ordering slips.
TrackingV2Translatorcopies v1 integers to the v2 body verbatim. Several of those columns hold a wildcard sentinel that PoracleNG writes itself, emits nothing for on read, and — once jfberry/PoracleNG#217 lands — refuses on write.The sentinel is coherent on their side: omit the field and the write default stores the sentinel; read the sentinel back and you get
null. Omission round-trips. Echoing does not, and echoing is what we do.PoracleTrackingProxy.PutV2Asyncmaps 422 toAlarmValidationExceptionwith no v1 fallback, so this surfaces as a user-facing validation error on edit, not a silent downgrade.Affected fields
Taken from #217's head — read wildcard (
ptrUnless) against the bound the same field now carries.size-1minimum: 1rarity-1minimum: 1min_iv-1minimum: 0level90001–90pokemon_id— PoracleNG forceslevelto 9000 therelevel90001–90pokemon_id0minimum: 1Raid and max battle are the worst of these: the value we echo is one PoracleNG stamped on the row itself, so it isn't a rule anyone typed wrong.
level: 90("any tier") sits inside the bound and is fine; only the 9000 placeholder fails.Egg
levelis already handled —PositiveIntegersin the type spec, added when 5.2.1 shippedminimum: 1on that one field. This is the same defect, and the other nine types were never checked. Second time on this shape.Fix
Add a per-type wildcard table to
TrackingV2Translatorand drop a field from the outbound body when its value equals the sentinel. Dropping is correct rather than merely convenient: v2 PUT is full-replace, and every one of these fields' write default is the sentinel, so omitting stores exactly what was there.nullmay work as an explicit clear — the fields arenullable: trueand huma should skip the bound on null — but that is unconfirmed upstream, and omission needs no confirmation. Use omission.Tests
TrackingV2TypeTranslationTestsasserts our field tables against 5.2.1'sopenapi.golden.json, which is what should have caught this and didn't — it checks that every schema field is classified, not that every value we can emit is acceptable. Add a case per row of the table above: a v1 row holding the sentinel must produce a body with that key absent. Watch each one fail first — a test written next to the fix passes whether or not the fix is right.Then extend the golden assertion to compare our sentinels against the schema's
minimum/maximum, so the next bound added upstream fails the build instead of production.Timing
Not live. The bounds arrive with #217, which is open. We should land the fix before adopting a build that carries it, and pin
Poracle:TrackingApiVersion=v1as the escape hatch if the ordering slips.