fix: address the final whole-branch review's findings
The branch review approved the slice with no blocking findings. These
are the substantive non-blocking ones.
N1, the one no per-task review could see: every seeded monster rolls
silverMin/silverMax = 0 (R7), so the victory screen showed "Silber +0"
after every fight in the shipped game. Three tasks were each correct in
isolation -- the mechanism stays, the values are zero, the field still
exists -- and the composite was wrong. Now conditional, with a test.
This does not contradict R16: R16 deleted the XP block because the
field ceased to exist, leaving nothing to hide. Silver still exists and
can be non-zero, so a conditional is the right tool.
N2: world.store.ts still said a combat "granted XP and silver". Same
false-fact-in-a-comment defect fixed in b5bcd50, one file over.
N4: resolveReputationRank threw a TypeError on negative input, since
findIndex returns -1 and REPUTATION_RANKS[-1] is undefined. Unreachable
today, but grantReputation is public and accepts any number.
N5/N6: grantReputation resolved factions without the enabled filter the
read path applies, so a disabled faction could accumulate invisible
reputation -- "disabled" was not actually a kill switch. The dense read
also had no ORDER BY, so the list could reorder between requests.
N8: design 13 requires silver stay 0 for every seeded monster; only two
of four were pinned. Re-adding silver to the others would have shipped
silently.
N9: spec 36's "a normal kill grants no Renown" had no test. It was
structurally guaranteed but unasserted -- now locked down against a
later slice wiring renown into combat.
N10: an impossible renown: 0 fixture, and RENOWN_MIN exported but never
used to clamp the floor.
API 268/268, web 230/230, API build zero errors.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,8 +21,11 @@ const REPUTATION_RANKS: ReadonlyArray<{
|
||||
];
|
||||
|
||||
export function resolveReputationRank(reputation: number): ReputationRankInfo {
|
||||
const index = REPUTATION_RANKS.findIndex(
|
||||
(rank) => reputation >= rank.threshold,
|
||||
// Clamp to the lowest rank: findIndex returns -1 for a negative total, and
|
||||
// REPUTATION_RANKS[-1] would throw rather than return a rank.
|
||||
const index = Math.max(
|
||||
0,
|
||||
REPUTATION_RANKS.findIndex((rank) => reputation >= rank.threshold),
|
||||
);
|
||||
const rank = REPUTATION_RANKS[index];
|
||||
const nextRank = index > 0 ? REPUTATION_RANKS[index - 1] : null;
|
||||
|
||||
@@ -61,7 +61,7 @@ export class ReputationService {
|
||||
throw characterNotFound();
|
||||
}
|
||||
|
||||
const faction = await factions.findOneBy({ key: factionKey });
|
||||
const faction = await factions.findOneBy({ key: factionKey, enabled: true });
|
||||
if (!faction) {
|
||||
throw reputationFactionNotFound();
|
||||
}
|
||||
@@ -114,7 +114,7 @@ export class ReputationService {
|
||||
const scope: RepositoryScope = this.dataSource;
|
||||
const factions = await scope
|
||||
.getRepository(ReputationFaction)
|
||||
.find({ where: { enabled: true } });
|
||||
.find({ where: { enabled: true }, order: { key: 'ASC' } });
|
||||
const reputations = await scope
|
||||
.getRepository(CharacterReputation)
|
||||
.find({ where: { characterId } });
|
||||
|
||||
Reference in New Issue
Block a user