diff --git a/bruno/neon-sprawl-server/contracts/scripts/reset-contract-instances-helper.js b/bruno/neon-sprawl-server/contracts/scripts/reset-contract-instances-helper.js index 18cda66..e3b68ee 100644 --- a/bruno/neon-sprawl-server/contracts/scripts/reset-contract-instances-helper.js +++ b/bruno/neon-sprawl-server/contracts/scripts/reset-contract-instances-helper.js @@ -4,26 +4,35 @@ async function resetAllContractInstances(bru) { const playerId = bru.getEnvVar("playerId") || bru.getVar("playerId"); const jsonHeaders = { headers: { "Content-Type": "application/json" } }; - const listResponse = await axios.get( - `${baseUrl}/game/players/${playerId}/contracts`, - jsonHeaders, - ); - if (listResponse.status !== 200) { - throw new Error(`contracts GET failed: ${listResponse.status}`); + // GET is capped (active + 10 completed); loop until no rows remain so older + // completed instances outside the first page are cleared too. + const maxPasses = 100; + for (let pass = 0; pass < maxPasses; pass += 1) { + const listResponse = await axios.get( + `${baseUrl}/game/players/${playerId}/contracts`, + jsonHeaders, + ); + if (listResponse.status !== 200) { + throw new Error(`contracts GET failed: ${listResponse.status}`); + } + + const ids = (listResponse.data.contracts || []).map((row) => row.contractInstanceId); + if (ids.length === 0) { + return; + } + + await axios.post( + `${baseUrl}/game/players/${playerId}/__dev/quest-fixture`, + { + schemaVersion: 1, + resetContractInstanceIds: ids, + }, + jsonHeaders, + ); } - const ids = (listResponse.data.contracts || []).map((row) => row.contractInstanceId); - if (ids.length === 0) { - return; - } - - await axios.post( - `${baseUrl}/game/players/${playerId}/__dev/quest-fixture`, - { - schemaVersion: 1, - resetContractInstanceIds: ids, - }, - jsonHeaders, + throw new Error( + `resetAllContractInstances exceeded ${maxPasses} passes; contract list may still be non-empty`, ); } diff --git a/docs/reviews/2026-06-28-NEO-151.md b/docs/reviews/2026-06-28-NEO-151.md index 74fed77..244a192 100644 --- a/docs/reviews/2026-06-28-NEO-151.md +++ b/docs/reviews/2026-06-28-NEO-151.md @@ -47,6 +47,12 @@ None. - ~~Nit: **`GetContracts_ShouldReturnCompletionRewardSummary_WhenContractCompletedAfterEncounterClear`** invokes **`TryCompleteOnEncounterClear`** under **`// Act`** before the GET — consider moving completion into **`// Arrange`** so Act is HTTP-only (AAA clarity).~~ **Done.** - ~~Nit: Plan §1 Postgres snippet mentions **`completed_at DESC`** but kickoff + README + implementation use **`issuedAt` desc** — update the plan technical section on merge to remove the stale SQL note (implementation is correct).~~ **Done.** +## Bugbot (PR #193) + +| Severity | Location | Finding | Status | +|----------|----------|---------|--------| +| Low | `reset-contract-instances-helper.js:27` | Reset helper only cleared one capped GET page (active + 10 completed); older rows could survive pre-request cleanup | **Done.** — loop GET → reset until list empty (max 100 passes) | + ## Verification ```bash