Fix infinite loop when saving 0-point estimate
All checks were successful
Build & Push Container Image / build (push) Successful in 10s

Saving an estimate of 0 caused advanceToNext to re-poker the same
issue repeatedly: !0 is true and 0 === 0 matches the "unestimated"
check. Track pokered issues with an `estimated` flag so they are
skipped regardless of estimate value.

Also guard against empty session index entries in createScopedSession
(NATS KV tombstones after delete can return empty values, producing
an invalid trailing-dot key that crashes Bucket.get).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jan Willem Mannaerts 2026-03-03 11:20:22 +01:00
parent ae3569e897
commit 4f5c71e811
2 changed files with 15 additions and 10 deletions

View file

@ -105,14 +105,19 @@ export async function createScopedSession({ issueKey, issueId, issueTitle, roomI
const indexEntry = await kvSessionIndex.get(issueIndexKey(tenantCloudId, issueKey));
if (indexEntry) {
const existingId = indexEntry.string();
const existing = await getSession(tenantCloudId, existingId);
if (existing && existing.tenantCloudId === tenantCloudId) {
if (existing.state === 'VOTING') {
return getSnapshot(existing);
}
// Clean up stale revealed/saved sessions
await kvSessions.delete(sessionKey(tenantCloudId, existingId));
if (!existingId) {
// Corrupted index entry (empty value) — clean up and continue
await kvSessionIndex.delete(issueIndexKey(tenantCloudId, issueKey));
} else {
const existing = await getSession(tenantCloudId, existingId);
if (existing && existing.tenantCloudId === tenantCloudId) {
if (existing.state === 'VOTING') {
return getSnapshot(existing);
}
// Clean up stale revealed/saved sessions
await kvSessions.delete(sessionKey(tenantCloudId, existingId));
await kvSessionIndex.delete(issueIndexKey(tenantCloudId, issueKey));
}
}
}

View file

@ -75,13 +75,13 @@ export default function Room({ room, user, dark, toggleDark, onBack }) {
const currentKey = activeSessionRef.current.issue.key;
const updated = issuesRef.current.map((i) =>
i.key === currentKey ? { ...i, estimate } : i
i.key === currentKey ? { ...i, estimate, estimated: true } : i
);
issuesRef.current = updated;
setIssues(updated);
setActiveSession(null);
const next = updated.find((i) => !i.estimate || i.estimate === 0);
const next = updated.find((i) => !i.estimated && (!i.estimate || i.estimate === 0));
if (!next) {
finishSession();
return;
@ -90,7 +90,7 @@ export default function Room({ room, user, dark, toggleDark, onBack }) {
setTimeout(() => startSessionForIssue(next), 300);
}, []);
const estimatedCount = issues.filter((i) => i.estimate && i.estimate > 0).length;
const estimatedCount = issues.filter((i) => i.estimated || (i.estimate && i.estimate > 0)).length;
const totalCount = issues.length;
if (loading) {