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

@ -5,7 +5,7 @@ import { appleRows, generate, playRows, scanSource, templateRegex, wrappedOps }
import { APPLE_OPS } from '../cli/api/apple.gen';
import { PLAY_APIS, PLAY_OPS } from '../cli/api/play.gen';
import { allOps, appleApi, backoffMs, buildRequest, getOp, mergePages, nextRequest, type Op, playApi, resolveOp, send, sendAll } from '../cli/api/client';
import { decide, gateOf, type GateInput, OWNER_ONLY, ownerReason } from '../cli/api/gate';
import { decide, gateOf, type GateInput, OWNER_NO_CONFIRM, OWNER_ONLY, ownerReason } from '../cli/api/gate';
import { coverage, describeRequest, parseParams, rows, search } from '../cli/api';
import { parseArgs } from '../cli/lib/args';
import { TOOLS, toArgv, validateInput } from '../cli/mcp-tools';
@ -180,6 +180,20 @@ describe('write gating', () => {
expect(decide({ ...base, getOnly: true, method: 'POST' }).action).toBe('refuse');
expect(decide({ ...base, getOnly: true })).toEqual({ action: 'send' });
});
test('--confirm <operationId>: an agent without a terminal sends a plain write, never an owner-only one', () => {
const w = { ...base, kind: 'write' as const, method: 'PATCH', yes: true, tty: false, opId: 'appStoreVersions_updateInstance' };
expect(decide({ ...w, confirm: 'appStoreVersions_updateInstance' })).toEqual({ action: 'send' });
expect(decide({ ...w, confirm: 'appInfos_updateInstance' })).toMatchObject({ action: 'refuse', why: expect.stringContaining('does not equal') });
expect(decide({ ...w, confirm: 'x', opId: undefined }).action).toBe('refuse');
// still dry without --yes, still nothing under MCP / desktop
expect(decide({ ...w, yes: false, confirm: w.opId }).action).toBe('dry');
expect(decide({ ...w, agent: true, confirm: w.opId }).action).toBe('refuse');
// owner-only: --confirm never replaces the owner's typed id, with or without a terminal
const o = { ...w, kind: 'owner' as const, method: 'DELETE', opId: 'appScreenshots_deleteInstance' };
expect(decide({ ...o, confirm: o.opId })).toMatchObject({ action: 'refuse', why: OWNER_NO_CONFIRM });
expect(decide({ ...o, tty: true, confirm: o.opId })).toMatchObject({ action: 'refuse', why: OWNER_NO_CONFIRM });
expect(decide({ ...o, tty: true })).toEqual({ action: 'confirm', typed: true });
});
test('the dry run shows the exact request without the credential', () => {
const r = buildRequest(op('apple', 'appStoreVersions_updateInstance'), { id: '1' }, { data: { type: 'appStoreVersions', id: '1' } });
const text = describeRequest(r);