fix(report): accept section: job progress phases on read-back
Independent Staging Quality Gate / validate (push) Successful in 8m41s
Independent Staging Quality Gate / publish (push) Successful in 1m53s

Heartbeat and GET were parsing progress_phase with a pattern that rejects the colon prefix the writer already persists, which aborted generation after the first chapter.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-04 20:09:11 +08:00
parent a793edb1ad
commit e27d5dc5a7
5 changed files with 57 additions and 4 deletions
@@ -352,7 +352,7 @@ function recordFromRow(value: unknown): PersonalReportJobRecord {
}
const status = statusValue as PersonalReportJobStatus;
if (attemptCount > maxAttempts) storageInvalid("invalid personal report job retry budget");
if (!operationalCodePattern.test(progressPhase)) storageInvalid("invalid progress phase");
if (!isValidPersonalReportJobProgressPhase(progressPhase)) storageInvalid("invalid progress phase");
if (lastErrorCode !== null && !operationalCodePattern.test(lastErrorCode)) {
storageInvalid("invalid last error code");
}
@@ -479,6 +479,23 @@ test("claim and heartbeat require the exact live lease token", async () => {
});
assert.equal(progressed.progressPhase, "building_evidence");
assert.equal(progressed.progressPercent, 40);
const sectionProgress = await service.updateProgress({
jobId: claimed.id,
leaseToken: claimed.leaseToken,
phase: "section:theme-health_pressure",
percent: 52,
});
assert.equal(sectionProgress.progressPhase, "section:theme-health_pressure");
backend.now = new Date("2026-08-14T03:00:20.000Z");
const afterSection = await service.heartbeatLease({
jobId: claimed.id,
leaseToken: claimed.leaseToken,
leaseSeconds: 60,
});
assert.equal(afterSection.progressPhase, "section:theme-health_pressure");
const owned = await service.getOwnedByRequestId(USER_A, REQUEST_A);
assert.equal(owned?.progressPhase, "section:theme-health_pressure");
});
test("atomic completion binds the canonical v2 document to the exact live lease", async () => {