@vue-skuilder/db 0.2.10 → 0.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.2.10",
7
+ "version": "0.2.11",
8
8
  "description": "Database layer for vue-skuilder",
9
9
  "main": "dist/index.js",
10
10
  "module": "dist/index.mjs",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@nilock2/pouchdb-authentication": "^1.0.2",
51
- "@vue-skuilder/common": "0.2.10",
51
+ "@vue-skuilder/common": "0.2.11",
52
52
  "cross-fetch": "^4.1.0",
53
53
  "moment": "^2.29.4",
54
54
  "pouchdb": "^9.0.0",
@@ -62,5 +62,5 @@
62
62
  "vite": "^8.0.0",
63
63
  "vitest": "^4.1.0"
64
64
  },
65
- "stableVersion": "0.2.10"
65
+ "stableVersion": "0.2.11"
66
66
  }
@@ -189,7 +189,9 @@ export class ResponseProcessor {
189
189
  const nullTags = tagKeys.filter((k) => taggedPerformance[k] === null);
190
190
  const scoredTags = tagKeys.filter((k) => taggedPerformance[k] !== null);
191
191
  logger.info(
192
- `[ResponseProcessor] per-tag ELO update for ${cardId}: scored=[${scoredTags.join(', ')}] count-only=[${nullTags.join(', ')}]`
192
+ `[FirstContactElo] correct first-attempt per-tag ELO update for ${cardId} ` +
193
+ `(historyLen=${history.records.length}, priorAttemps=${cardRecord.priorAttemps}): ` +
194
+ `scored=[${scoredTags.join(', ')}] count-only=[${nullTags.join(', ')}]`
193
195
  );
194
196
 
195
197
  void this.eloService.updateUserAndCardEloPerTag(
@@ -225,7 +227,8 @@ export class ResponseProcessor {
225
227
  );
226
228
  }
227
229
  logger.info(
228
- '[ResponseProcessor] Processed correct response with SRS scheduling and ELO update'
230
+ `[FirstContactElo] correct first-attempt ELO update (score=${userScore.toFixed(3)}) ` +
231
+ `for ${cardId} (historyLen=${history.records.length}, priorAttemps=${cardRecord.priorAttemps})`
229
232
  );
230
233
  }
231
234
 
@@ -270,8 +273,17 @@ export class ResponseProcessor {
270
273
  // Parse performance (may be numeric or structured)
271
274
  const { taggedPerformance } = this.parsePerformance(cardRecord.performance);
272
275
 
273
- // Update ELO for first-time failures (not subsequent attempts on same card)
274
- if (history.records.length !== 1 && cardRecord.priorAttemps === 0) {
276
+ // Tracks whether this response already produced an ELO update, so the
277
+ // dismiss-failed branch below doesn't double-penalize the same record (the
278
+ // two coincide when maxAttemptsPerView === 1: first contact and final
279
+ // failure land on one response).
280
+ let eloUpdated = false;
281
+
282
+ // Update ELO on the first attempt of a presentation — symmetric with the
283
+ // correct path. Previously this was gated on `history.records.length !== 1`,
284
+ // so first-EVER failures were skipped while first-ever successes updated,
285
+ // biasing cold-start ELO upward. See WORKING-first-contact-elo-asymmetry.md.
286
+ if (cardRecord.priorAttemps === 0) {
275
287
  if (taggedPerformance) {
276
288
  // Per-tag ELO update for incorrect response
277
289
  void this.eloService.updateUserAndCardEloPerTag(
@@ -282,7 +294,9 @@ export class ResponseProcessor {
282
294
  currentCard
283
295
  );
284
296
  logger.info(
285
- `[ResponseProcessor] Processed incorrect response with per-tag ELO update (${Object.keys(taggedPerformance).length - 1} tags)`
297
+ `[FirstContactElo] incorrect first-attempt per-tag ELO update for ${cardId} ` +
298
+ `(historyLen=${history.records.length}, priorAttemps=${cardRecord.priorAttemps}, ` +
299
+ `tags=${Object.keys(taggedPerformance).length - 1})`
286
300
  );
287
301
  } else {
288
302
  // Standard single-score ELO update
@@ -293,32 +307,52 @@ export class ResponseProcessor {
293
307
  courseRegistrationDoc,
294
308
  currentCard
295
309
  );
296
- logger.info('[ResponseProcessor] Processed incorrect response with ELO update');
310
+ logger.info(
311
+ `[FirstContactElo] incorrect first-attempt ELO update (score=0) for ${cardId} ` +
312
+ `(historyLen=${history.records.length}, priorAttemps=${cardRecord.priorAttemps})`
313
+ );
297
314
  }
315
+ eloUpdated = true;
298
316
  } else {
299
- logger.info('[ResponseProcessor] Processed incorrect response (no ELO update needed)');
317
+ logger.info(
318
+ `[FirstContactElo] incorrect retry — no ELO update for ${cardId} ` +
319
+ `(historyLen=${history.records.length}, priorAttemps=${cardRecord.priorAttemps})`
320
+ );
300
321
  }
301
322
 
302
323
  // Determine navigation based on attempt limits
303
324
  if (currentCard.records.length >= maxAttemptsPerView) {
304
325
  if (sessionViews >= maxSessionViews) {
305
- // Too many session views - dismiss completely with ELO penalty
306
- if (taggedPerformance) {
307
- // Use tagged performance for final failure
308
- void this.eloService.updateUserAndCardEloPerTag(
309
- taggedPerformance,
310
- courseId,
311
- cardId,
312
- courseRegistrationDoc,
313
- currentCard
326
+ // Too many session views - dismiss completely with ELO penalty.
327
+ // Skip if this response already updated ELO above, to avoid a double
328
+ // penalty on the same record (happens when maxAttemptsPerView === 1).
329
+ if (!eloUpdated) {
330
+ if (taggedPerformance) {
331
+ // Use tagged performance for final failure
332
+ void this.eloService.updateUserAndCardEloPerTag(
333
+ taggedPerformance,
334
+ courseId,
335
+ cardId,
336
+ courseRegistrationDoc,
337
+ currentCard
338
+ );
339
+ } else {
340
+ void this.eloService.updateUserAndCardElo(
341
+ 0,
342
+ courseId,
343
+ cardId,
344
+ courseRegistrationDoc,
345
+ currentCard
346
+ );
347
+ }
348
+ logger.info(
349
+ `[FirstContactElo] dismiss-failed final ELO penalty for ${cardId} ` +
350
+ `(historyLen=${history.records.length}, sessionViews=${sessionViews})`
314
351
  );
315
352
  } else {
316
- void this.eloService.updateUserAndCardElo(
317
- 0,
318
- courseId,
319
- cardId,
320
- courseRegistrationDoc,
321
- currentCard
353
+ logger.info(
354
+ `[FirstContactElo] dismiss-failed — ELO already updated this response, ` +
355
+ `skipping double penalty for ${cardId}`
322
356
  );
323
357
  }
324
358
  return {