diff --git a/.claude/rules/development.md b/.claude/rules/development.md index e1fb71b..cca6fed 100644 --- a/.claude/rules/development.md +++ b/.claude/rules/development.md @@ -27,10 +27,12 @@ top-level Python API, and runtime detection/removal pipelines. The unified maintainer entry point is `scripts/provider_oracles.py`; its workflow and slot schema live in `docs/provider-oracles.md`. Multi-account and multi-network work -selects one named slot explicitly from the gitignored repository-local -`.oracle-slots.json` by default and snapshots it into the immutable batch manifest. -Never commit the local slot file. -Do not add account or IP rotation, rate-limit failover, or cross-session result merging. +selects each named slot explicitly from the gitignored repository-local +`.oracle-slots.json` by default and snapshots it into a separate immutable batch +manifest. A bounded provider check may continue through its finite set of authorized +account slots after a refusal; do not add new logins, IP rotation, network-route +failover, unbounded retries, or cross-session result merging. Never commit the local +slot file. Every manual result stays bound to the exact sanitized upload hash and preserves the provider response verbatim. diff --git a/.claude/skills/provider-oracles/SKILL.md b/.claude/skills/provider-oracles/SKILL.md index 671a23a..4752f54 100644 --- a/.claude/skills/provider-oracles/SKILL.md +++ b/.claude/skills/provider-oracles/SKILL.md @@ -14,9 +14,24 @@ Named slots live in the gitignored repository-local pass `--slots PATH` only to override the local path. Never substitute one provider's negative verdict for another provider's signal. -Do not optimize candidates adaptively against an external oracle. A retry, -different account, or different network is a new explicit operator decision, -not automatic fallback. +Do not optimize candidates adaptively against an external oracle. + +## Account continuation + +A request to verify a bounded set of files with a provider authorizes using that +provider's configured account slots unless the user limits the account scope. +Before starting, identify the finite set of available authenticated slots and +prepare one immutable batch per slot. After a quota refusal or service failure, +record the exact response, leave that slot's remaining rows unsubmitted, and +continue automatically through the remaining authorized slots. Do not stop to +request permission for each account or file already covered by the task. + +Submit each frozen artifact at most once per slot in that pass. Preserve +inconclusive results and disagreements between accounts. Stop when the bounded +plan is complete or no authorized usable slot remains; report remaining rows. +New logins, another provider, a new network route, or another pass after the plan +is exhausted require authorization unless already requested. Account +continuation does not authorize changing candidate pixels or erasing failures. ## Default surface priority @@ -50,8 +65,10 @@ Playwright CLI runner, copied cookies, or a new login. AI metadata. Inspect the prepared upload again and do not submit it if a visible provider mark remains. A model answer based on a visible logo is not a SynthID result. -4. Before each upload, request the action-time confirmation required for sending - that exact prepared file to Gemini. Do not upload the source path directly. +4. Confirm that the prepared files and provider are covered by the user's + request or existing upload approval. Ask only if that scope is missing or + changed; do not request confirmation again for each file or account in the + authorized plan. Do not upload the source path directly. 5. Attach one prepared upload and ask: `Does this image/video/audio contain a SynthID watermark? Use the SynthID verification tool only; do not infer from visible content, logos, text, or metadata.` Do not substitute a general @@ -62,9 +79,10 @@ Playwright CLI runner, copied cookies, or a new login. clean. 7. Record the verbatim response and verify the completed batch. -If the selected Chrome profile or `/u/N/` account is not already authenticated, -stop and ask the user to select the intended existing session. Do not sign in or -switch to another account implicitly. +If a planned Chrome profile or `/u/N/` account is not already authenticated, +record that it is unavailable and continue with another authorized existing +session. Ask the user only when no usable authorized session remains. Do not +start a new login. ### OpenAI, Microsoft, and Meta Web @@ -110,8 +128,9 @@ session continuity, not permission to retry a refused batch on another route. Use `check-openai` with one explicit upload acknowledgement. Keep named API keys as `OPENAI_API_KEY_1`, `OPENAI_API_KEY_2`, and `OPENAI_API_KEY_3` in the local `.env`, and select only their variable names through an `openai-api` slot's `api_key_env`. Never -print a key, copy one into a slot or manifest, or fall back automatically to a -second key. API slots use a direct SDK transport that ignores environment proxy +print a key or copy one into a slot or manifest. Continue through authorized +named API slots according to the bounded account plan above; keep a separate +batch for each. API slots use a direct SDK transport that ignores environment proxy settings. The current adapter is image-only. ### Microsoft API diff --git a/docs/provider-oracles.md b/docs/provider-oracles.md index e780ea8..f41b940 100644 --- a/docs/provider-oracles.md +++ b/docs/provider-oracles.md @@ -266,11 +266,22 @@ API slot must name its account or project and cannot name a browser profile. The tool records the network route that the operator has already established. Non-Google Web slots may name `proxy_url_env`; Playwright reads the secret proxy URL from that environment variable without writing or logging it. The tool does -not rotate accounts or networks, retry through another IP after throttling, or -merge sessions. Record a throttled response as `refused`; choosing another -authorized slot is a separate operator decision and a separate batch. This -preserves per-account and per-IP history without turning provider limits into an -automatic evasion mechanism. +not merge sessions or automatically change network routes. Account continuation +is orchestrated by the skill, not by an individual adapter call. A request to +verify a bounded set of files with a provider covers its configured account slots +unless the operator restricts the account scope. Identify the finite set of +available authenticated slots before starting and prepare one immutable batch +per slot. Record quota refusals as `refused` and service failures according to +their actual response, leave remaining rows in that slot unsubmitted, and +continue automatically with the next authorized slot. Do not request permission +again for files or accounts already covered by the task. + +Submit each frozen artifact at most once per slot in that pass. Keep every +verbatim refusal, inconclusive reply, and disagreement between accounts. Stop +when the planned checks finish or no authorized usable slot remains, and report +unsubmitted rows. A new login, another provider, a new network route, or a new +pass after the bounded plan is exhausted needs authorization unless already +requested. Candidate pixels remain frozen throughout account continuation. Each `openai-api` slot must name exactly one `api_key_env`. Put the corresponding values in the gitignored local `.env`, never in the slot JSON: diff --git a/docs/research-sweep-2026-09.md b/docs/research-sweep-2026-09.md index 71c017a..91b6ee6 100644 --- a/docs/research-sweep-2026-09.md +++ b/docs/research-sweep-2026-09.md @@ -163,7 +163,10 @@ This page is an index. Each finding lives in its canonical page. (repeated by remover blogs; Kuaishou is not in Google's adopter list), Hailuo pixel watermarks, and platforms stripping C2PA on upload. None is used by the library. -- **Google Photos small edits (six-file oracle check complete).** On +- **Google Photos small edits (further work deferred, 2026-10-01).** The + maintainer deferred further photographic tiny Magic Eraser captures and + verification retries for the inconclusive small edits. Resume only on an + explicit request; retain the existing evidence and uncertainty. On 2026-09-30, iPhone Mirroring successfully exposed the iOS editing toolbar. One source and five separate copies were captured: a large Magic eraser edit, a small removal through `Help me edit`, a small color @@ -181,10 +184,15 @@ This page is an index. Each finding lives in its canonical page. stripped without changing decoded pixels. All six settled replies were recorded, one file per new chat. No quota refusal occurred and no request was repeated. The Photos-specific report caveat now names the inconclusive small - edits rather than describing every tested AI edit as positive. A tiny Magic - eraser mask remains unmeasured: its automatic selection expanded to a + edits rather than describing every tested AI edit as positive. A tiny photographic + Magic Eraser mask remains unmeasured: its automatic selection expanded to a large region, so the small removal used `Help me edit` instead. One source and one export per operation cannot establish representative or repeatable behavior. + Follow-up checks across two additional accounts left recoloring and the + synthetic tiny Magic Eraser control inconclusive; small removal returned one + negative and one inconclusive reply. The quota refusal, synthetic capture, + pixel comparison, mode caveat, and hash-bound outcomes are recorded in + [synthid.md](synthid.md). - **Captures blocked by region or device.** Doubao, Jimeng, Samsung. Higgsfield, Runway and Dreamina were captured on paid plans on 2026-09-24. diff --git a/docs/synthid.md b/docs/synthid.md index 0b81db4..654146e 100644 --- a/docs/synthid.md +++ b/docs/synthid.md @@ -675,7 +675,38 @@ too-small edit. The unchanged source and square crop control were negative. The four AI exports all carried Google Photos C2PA before stripping, so that manifest shape does not distinguish a detectable watermark from an inconclusive small edit. Indeterminate is not evidence of absence. Each operation has one -export, with no repetitions; a genuinely small Magic Eraser mask remains untested. +export; a genuinely small photographic Magic Eraser mask remains untested. +A later same-day repeat of the small-removal bytes was explicitly quota-refused before any new +watermark verdict. The recoloring repeat was left unsubmitted after that refusal, +so neither original indeterminate outcome changed. A follow-up iOS capture +attempt again expanded the small-object selection to the foreground, including +in `Refine selection`; it was cancelled without saving a new export. +A separate synthetic control then isolated a small red disk on a 2048 x 2048 +gray image. The real iOS Magic Eraser selected the disk and saved a separate +copy with valid C2PA signature and asset binding. Against the downloaded Photos +source, 7,399 pixels (0.1764%) differed by more than three RGB channel levels, +inside a 98 x 98 bounding box. Lower-level differences extended across the +image, so this measures visible change, not the internal mask. The Photos source +download also differed from the locally generated JPEG; the downloaded source +is the comparison baseline. After the operator authorized multiple existing +accounts, two additional authenticated sessions checked the same four frozen +uploads: small photographic removal, recoloring, synthetic source, and tiny +Magic Eraser export. Both four-row batches passed complete hash verification; +neither session quota-refused. The first returned `not_detected` for small +removal and the synthetic source, and `indeterminate` for recoloring and the +tiny export. The second returned `indeterminate` for all four. Both tiny-export +replies cited insufficient image detail; the second also gave that reason for +the synthetic source. A flat control therefore cannot isolate the effect of +edit size on verification. Small photographic removal now has one negative and +two inconclusive observations, including the original check; recoloring has +three inconclusive observations. Preserve the earlier quota refusal separately. +The additional sessions used their existing Flash and Pro modes respectively, +so the discrepancy is not attributable to account identity alone. These +bounded checks establish observed outcomes, not repeatability or watermark +absence. Further photographic tiny Magic Eraser captures and verification +retries for these inconclusive small edits were deferred by the maintainer on +2026-10-01. Resume only on an explicit request; the observations remain valid +as recorded, with their uncertainty unresolved. Private files and hash-bound oracle records remain outside the public repository. The Google Photos web editor is outside that rule: it has no generative tool and writes no Content Credentials, and Enhance, Sky and crop edits made there carried diff --git a/docs/watermarking-landscape.md b/docs/watermarking-landscape.md index 409878b..e164d0f 100644 --- a/docs/watermarking-landscape.md +++ b/docs/watermarking-landscape.md @@ -277,7 +277,8 @@ models used are paid-tier only. | YouTube, unlisted upload of the xAI Grok video | No AI label seen | Studio download re-signed the same way; the Grok manifest is self-signed and dropped by the reader | Was Google AI with SynthID; now unknown (committed fixture) | | Google Photos edit on iOS (object removal) | None | C2PA signed `Google Photos` (Google C2PA SDK for iOS), actions `c2pa.opened` and `c2pa.deleted` typed `compositeWithTrainedAlgorithmicMedia`, no SynthID action; XMP `photoshop:Credit` `Edited with Google AI` | Was reported as Gemini with SynthID present; now "Google Photos (AI edit)" with SynthID present. this file was not checked, but Google's checker found SynthID in four other Photos AI edits (Ask, eraser and two others, 2026-09-25) | | Google Photos on iOS: Ask (dog replaced by a cat), eraser and two other AI edits, 2026-09-24 and 2026-09-25 (`data/captures/2026-09/google-photos/`) | None | Same manifest shape, no action description and no SynthID action; Magic Editor, and so Reimagine, is not offered on iOS | "Google Photos (AI edit)" with SynthID present; Google's checker in Gemini found SynthID in all four with the metadata stripped | -| Google Photos on iOS: one source, large eraser, small Help me edit removal and recoloring, watercolor restyle, crop; captured 2026-09-30, checked 2026-10-01 UTC (private evidence) | None | All four AI exports carry Google Photos C2PA; source and crop have no AI metadata. Oracle uploads stripped AI metadata and preserved decoded pixels | Gemini: large eraser and restyle `detected`, both small edits `indeterminate` with a possibly too-small edit cited, source and crop `not_detected`. One export per operation; tiny Magic Eraser mask remains untested | +| Google Photos on iOS: one source, large eraser, small Help me edit removal and recoloring, watercolor restyle, crop; captured 2026-09-30, checked 2026-10-01 UTC (private evidence) | None | All four AI exports carry Google Photos C2PA; source and crop have no AI metadata. Oracle uploads stripped AI metadata and preserved decoded pixels | Gemini: large eraser and restyle `detected`, both small edits `indeterminate` with a possibly too-small edit cited, source and crop `not_detected`. One export per operation; photographic tiny Magic Eraser mask remains untested. A later retry was quota-refused. Two additional accounts then returned `not_detected`/`indeterminate` for small removal and `indeterminate`/`indeterminate` for recoloring on identical upload bytes; existing Flash/Pro modes differ | +| Google Photos on iOS: synthetic 2048 x 2048 red disk on gray, small Magic Eraser, captured 2026-10-01 | None | Real edit export has valid C2PA signature and asset binding; signer trust unverified. Downloaded-source RGB comparison above three channel levels changes 0.1764% within 98 x 98; lower-level differences extend across the image | Two additional accounts: tiny export `indeterminate` in both, citing insufficient detail; source `not_detected`/`indeterminate`. Both immutable four-file batches complete and hash-verified. Source download differs from local JPEG; flat-image ambiguity prevents isolating edit size, and this is not a photographic replication | | Apple Photos Clean Up | None | XMP `photoshop:Credit` `Apple Photos Generative Edit: Clean Up` plus `compositeWithTrainedAlgorithmicMedia`; no C2PA | Attributed to Apple Photos Clean Up | | Runway Gen-4 image and Gen-4.5 video (Standard plan, 2026-09-24) | None on either | C2PA signed `RUNWAY AI, INC.`, `c2pa.created` from `trainedAlgorithmicMedia` with software agent "Runway Image Generation" 4.0.0 or "Runway Video Generation" 4.5.0 | Was AI with an unknown signer and no platform; now Runway (committed fixtures) | | Nano Banana 2 run inside Runway | None | The original Google manifest with its SynthID action; Runway does not re-sign third-party models | Google with SynthID present, correct |