Screenshot guardrails: name every screenshot not COMPLETE on 409 ASSET_IN_POST_PROCESSING (submit, validate; stuck after 5 min in UPLOAD_COMPLETE), apple screenshots --repair-stuck replaces only those in place, part uploads without Expect: 100-continue (failed part deletes the reservation, one retry, never committed), storebound api --confirm <operationId> for agents' plain writes (never owner-only)

This commit is contained in:
CRTkafa 2026-10-21 12:40:26 +07:00
parent 164182012f
commit 00756e6908
19 changed files with 475 additions and 45 deletions

View file

@ -25,6 +25,7 @@ import { type BuildStep, formatRows, labelOf, planIapSubmission, REJECTED_APP, t
import { allSubscriptions } from '../lib/subscriptions';
import { ascClient, type J } from './asc';
import { readSubmitState, submitPlanned } from './iap-submit';
import { explain409, isScreenshotProcessing409, pendingScreenshots, updateLedger, versionScreenshotRows } from './screenshot-health';
import { openSubmissions } from './submission';
import { ensureGcSwitch, gameCenterInUse, gcSwitchPlan, readGcSwitch } from './game-center';
import { allVersions, parsePlatform, pickPlatform, platformLabel, platformOf, versionsPath } from './platform';
@ -118,5 +119,13 @@ export async function submitVersion(ctx: Ctx, mode: 'submit' | 'resubmit'): Prom
log(TAG, `build ${number} attached to ${versionString}`);
}
if (gc && gcPlan.length) await ensureGcSwitch(c, gc, TAG);
await submitPlanned(ctx, c, { appId, ids, state, tag: TAG, withVersion: true, appVersionId: v.id, platform, quiet: true, resolve });
try {
await submitPlanned(ctx, c, { appId, ids, state, tag: TAG, withVersion: true, appVersionId: v.id, platform, quiet: true, resolve });
} catch (e) {
if (!isScreenshotProcessing409(e)) throw e;
// 409 STATE_ERROR.ASSET_IN_POST_PROCESSING.APP_SCREENSHOT: name every screenshot that is not COMPLETE
const rows = await versionScreenshotRows(c, v.id);
const pending = pendingScreenshots(rows, updateLedger(root, rows), Date.now());
throw new Error(`${explain409(pending)}\nLast answer: ${(e as Error).message}`);
}
}