返回 ppt-master
image-searcher.md
根目录 / skills / ppt-master / references / image-searcher.md
1 > See [`image-base.md`](./image-base.md) for the common framework. Technical SVG/PPT constraints are in [`shared-standards-core.md`](./shared-standards-core.md).
2
3 # Image_Searcher Reference Manual
4
5 Role definition for the **web image acquisition path**: translate the active resource owner's intent into keyword queries, search openly-licensed providers, download a license-cleared image into `project/images/`, and record provenance + license metadata into `image_sources.json`.
6
7 **Trigger**: the Default Generate resource list contains `Acquire Via: web`, or Quick Generate has resolved a required web image in active context. Load only when at least one such resource exists.
8
9 ---
10
11 ## 1. License Tier Discipline
12
13 Every **provider-sourced** image is classified into one of two tiers; anything else is rejected outright. A third tier, `manual`, exists **only** for a directly selected replacement from [`--from-url`](#5-running-image_searchpy) or an adopted-page source package — it is never the result of a provider search accepting an unknown license.
14
15 | Tier | Licenses | On-slide attribution |
16 |---|---|---|
17 | `no-attribution` | CC0, Public Domain, Pexels License, Pixabay Content License | None |
18 | `attribution-required` | CC BY, CC BY-SA | Inline credit `<text>` on the slide |
19 | `manual` | Directly selected URL or adopted-page package image (license unverified) | None — verifying rights / any credit is the user's responsibility |
20
21 **Forbidden — auto-rejected licenses**:
22
23 - CC BY-NC, CC BY-NC-SA (non-commercial)
24 - CC BY-ND, CC BY-NC-ND (no derivatives)
25 - All Rights Reserved
26 - Unknown / missing license
27
28 > `license_tier` is the central abstraction. Downstream consumers (Executor) read this single field and never interpret raw license strings.
29
30 ---
31
32 ## 2. Search Strategy
33
34 Default: quality-first across all allowed license tiers. Do not prefer CC0 / Public Domain over a better CC BY / CC BY-SA image; rely on the manifest's `license_tier` so Executor can add attribution only when needed.
35
36 ```
37 Multimodal Generate: explicit query variants × provider chain + allowed licenses
38 → aggregate/deduplicate/rank → first 8 thumbnails → visually select
39 → download one original; if none passes, inspect the next 8 first.
40 Non-visual / standalone best-only: explicit query variants × provider chain
41 → strict metadata gate → first downloadable ranked original wins.
42 Strict: provider chain, license filter = cc0,pdm,pexels,pixabay
43 → apply the same selected execution mode without CC BY / CC BY-SA.
44 ```
45
46 `--strict-no-attribution` is opt-in. Use it only when the deck cannot tolerate any on-slide credit (corporate template, full-bleed hero).
47
48 ---
49
50 ## 3. Providers
51
52 | Provider | Config | Strength |
53 |---|---|---|
54 | Pexels | recommended: `PEXELS_API_KEY` (free, [signup](https://www.pexels.com/api/)) | modern stock photography, people, workplace, lifestyle |
55 | Pixabay | recommended: `PIXABAY_API_KEY` (free, [signup](https://pixabay.com/api/docs/)) | broad type coverage including photos and illustrations |
56 | Openverse | zero-config | fallback aggregator: Wikimedia + Flickr + museums + rawpixel |
57 | Wikimedia Commons | zero-config | educational, scientific, geographic, historical |
58
59 Default chain (when `--provider` is unset):
60
61 `pexels` (when keyed) → `pixabay` (when keyed) → `openverse` → `wikimedia`.
62
63 Keyed providers without an API key are silently skipped — not an error.
64
65 **Default — keyed providers for broader stock coverage (may override when zero-config sources fit)**: Configure Pexels or Pixabay when their stock-photo coverage serves the brief. Their absence is not a validation failure; Openverse and Wikimedia remain valid zero-config acquisition paths.
66
67 ---
68
69 ## 4. Intent → Query Translation
70
71 Keep two layers distinct:
72
73 | Layer | Owner and grammar |
74 |---|---|
75 | Default Generate `design_spec.md §VIII Reference` | Strategist's complete visual intent: exact subject, desired view/mood, focal or quiet region, and crop-safety constraints. Positive quality cues are valid here. |
76 | Quick Generate active `Reference` | Current main agent's active-context intent after honoring explicit user assets, URLs, subjects, and constraints; unspecified choices are resolved automatically without confirmation. |
77 | `image_queries.json.items[].query` / positional query | Image_Searcher's concrete entity/identity keyword string. Start with the shortest phrase that preserves identity; keep exact multi-word names and necessary disambiguators even when they exceed four words. Omit mood, quality, composition, HEX, and negative wording. |
78
79 Web APIs match metadata, not semantic intent. Providers try each explicit query first, then progressively simplified four/three/two/one-word variants. A pipeline manifest should therefore use a concise primary `query` without pre-truncating exact names, plus `query_variants` for materially different official translations, spellings, aliases, or Chinese names. The tool aggregates and deduplicates their results; do not use variants for cosmetic word-order changes. For Chinese landmarks, pair the precise Chinese name used by Wikimedia with compact English identity terms used by stock providers.
80
81 Image_Searcher consumes the active Reference and never rewrites its owner. In Default Generate, that means no rewrite of `design_spec.md` or `spec_lock.md`; in Quick Generate, the active-context Reference remains fixed for the run. A candidate either satisfies that existing subject/focal/crop intent, or the role tries materially different query/provider/permitted-license strategies until no untried strategy remains, then marks `Needs-Manual`. Never loosen `required_terms`, the license policy, or the active intent to manufacture a match.
82
83 When the subject is an exact entity (landmark / person / company / product / venue), write `required_terms` at the same time you write the row's `query` and `query_variants`. Use one required group per identity anchor and `|` for aliases / translations, e.g. `["Chongqing|重庆", "Jiefangbei|解放碑|Liberation Monument"]`. This keeps provider queries short while preventing metadata-ranked wrong entities from being accepted automatically.
84
85 Do **not** loosen `required_terms` to generic category words just to improve coverage. Terms like `canyon`, `grand canyon`, `stone pillar`, `ground fissure`, `ancient town`, `bridge`, `temple`, or `village` belong in the search query, not as the only identity gate. For small / Chinese-local attractions, the correct failure mode is `Needs-Manual` or a user-provided `--from-url`, not a visually plausible image of the wrong place.
86
87 **Forbidden — web negative prompts**: `not tourist snapshot`, `no amateur photo`, `avoid low quality`.
88
89 > Note: Keyword APIs search negative words literally.
90
91 | §VIII Reference (intent) | Provider query |
92 |---|---|
93 | "Offshore wind farm at dusk, aerial view, quiet sky on the left for safe crop" | `offshore wind farm` |
94 | "Diverse engineering team around a laptop, modern office, natural light" | `engineering team laptop` |
95 | "Chongqing Jiefangbei monument, full structure visible, landscape frame" | `Chongqing Jiefangbei monument` |
96
97 ---
98
99 ## 5. Running `image_search.py`
100
101 ```bash
102 python3 scripts/image_search.py "<query>" \
103 --filename <name>.jpg \
104 --slide <slide_id> \
105 --orientation landscape \
106 --purpose background \
107 -o <project_path>/images
108 ```
109
110 | Parameter | Required | Default | Description |
111 |---|---|---|---|
112 | `query` | yes | — | Positional. Pre-simplification not necessary; CLI runs `simplify_query` internally. |
113 | `--query-variant` | no | — | Repeatable official translation, spelling, alias, or materially different entity phrase; results are aggregated and deduplicated. Batch rows use `query_variants`. |
114 | `--filename` | yes | — | Output filename matching the resource list |
115 | `-o / --output` | no | `.` | Output directory; manifest defaults to `<output>/image_sources.json` |
116 | `--slide` | no | `""` | Slide ID from resource list (recorded in manifest) |
117 | `--purpose` | no | `""` | `background` / `hero` / `side` / `accent` |
118 | `--orientation` | no | `any` | `any` / `landscape` / `portrait` / `square` |
119 | `--min-width / --min-height` | no | `1200 / 800` | Actual downloaded-pixel floors; `--from-url` honors explicit lower overrides |
120 | `--provider` | no | (chain) | Pin one provider |
121 | `--strict-no-attribution` | no | off | Restrict to no-attribution licenses; refuse CC BY / CC BY-SA |
122 | `--require-terms` | no | — | Entity-safety gate for exact subjects. Repeatable; comma separates required groups; `A|B` means aliases within one group. Example: `--require-terms Chongqing --require-terms "Jiefangbei|Liberation Monument"` |
123 | `--manifest` | no | (default) | Override manifest path |
124 | `--save-candidates` | no | off | Thumbnail-selection mode: save one ranked page of review-eligible previews and `review_sheet.jpg`, but no original or provenance record. Multimodal Generate enables this; standalone CLI remains best-only by default |
125 | `--max-candidates` | no | `8` | Thumbnail page size. `0` explicitly requests the complete pool and is reserved for debugging / exceptional review, not normal Generate |
126 | `--candidate-page` | no | `1` | Ranked thumbnail page to fetch. Page 2 starts at rank 9 with the default page size. Batch rows may override with `candidate_page` |
127 | `--promote` | no | — | Download exactly one selected candidate original, enforce the request's size/readability gates, and write provenance |
128 | `--from-url` | no | — | Manual replace: download a directly selected image URL into `--filename` (recorded `license_tier: manual`); works without a multimodal model |
129
130 ### Batch mode (≥ 2 web rows) — preferred
131
132 When more than one row is `Acquire Via: web`, do **not** call the CLI once per row. Write all rows into one `image_queries.json` and run a single concurrent batch — the web sister of `image_gen.py --manifest`:
133
134 ```bash
135 python3 scripts/image_search.py --batch <project_path>/images/image_queries.json \
136 -o <project_path>/images \
137 --save-candidates
138 ```
139
140 The candidate flag above is the normal Generate invocation when the current
141 agent can inspect images. It downloads previews only and moves each successful
142 row to `Needs-Selection`; no target image or `image_sources.json` entry exists
143 yet. A non-multimodal agent omits it and follows the handoff rules under
144 Suitability review below: only strict metadata-verified candidates may download
145 automatically. Standalone CLI use remains best-only unless the caller explicitly
146 requests thumbnail selection.
147
148 `image_queries.json` schema (one item per web row):
149
150 ```json
151 {
152 "items": [
153 {
154 "filename": "jiefangbei.jpg",
155 "query": "Jiefangbei Chongqing downtown monument",
156 "query_variants": ["Chongqing Liberation Monument", "重庆 解放碑"],
157 "slide": "03_landmark",
158 "purpose": "exact landmark photo",
159 "orientation": "landscape",
160 "required_terms": ["Chongqing", "Jiefangbei|Liberation Monument"],
161 "status": "Pending"
162 }
163 ]
164 }
165 ```
166
167 Required per item: `filename`, `query`, `status` (`Pending`). Optional per-item overrides: `query_variants`, `candidate_page`, `slide`, `purpose`, `orientation`, `provider`, `strict_no_attribution`, `min_width`, `min_height`, `required_terms`.
168
169 Use `required_terms` for **exact-entity images**: landmarks, people, companies, products, venues, named artworks, and named institutions. Each list item is required; alternatives inside one item use `|`. Example for a Chongqing landmark: `["Chongqing|重庆", "Jiefangbei|解放碑|Liberation Monument"]`. In best-only mode, candidates whose title / author / source URL do not satisfy every group are rejected before ranking, so a visually polished but wrong Rome / Hoi An image cannot win. Thumbnail mode may show a separately labeled near match only for visual identity verification; it never promotes automatically. Do **not** use `required_terms` for generic mood / background rows such as "modern city skyline" or "team collaboration".
170
171 For less-covered local attractions, keep the strict identity gate rather than progressively deleting location anchors or replacing proper names with category words. If strict metadata cannot prove the entity, mark the row `Needs-Manual` and use the manual URL path when the user supplies a confirmed source.
172
173 The runner first revalidates every `Sourced` row against its readable file, requested dimensions, and `image_sources.json` entry; drift returns that row to `Failed`. It then searches all `Pending` / `Failed` rows concurrently. Thumbnail mode writes `Needs-Selection`, `candidate_page`, `candidate_count`, `candidate_total`, `has_more_candidates`, `next_candidate_page`, and the relative `review_sheet` path without creating a target image or provenance. To inspect the next page for one row, set its `candidate_page` to `next_candidate_page`, reset only that row to `Pending`, and rerun the batch. Promoting one candidate with the same `--batch` manifest changes that row to `Sourced`. Provider failures remain retryable `Failed`, while clean provider/stage exhaustion becomes terminal `Needs-Manual`. Status is saved after each completion. A single `web` row may still use single-query mode above.
174
175 **Pacing**: free providers (Wikimedia/Openverse) are rate-sensitive, so batch concurrency defaults to a modest **3** (`--concurrency N`, or `IMAGE_SEARCH_CONCURRENCY` env). Use `--concurrency 1` to restore strict one-at-a-time pacing. Single-query mode is one request at a time by nature.
176
177 ### Ranking model
178
179 `image_search.py` ranks provider metadata, not pixels. The order is deliberately conservative:
180
181 1. common hard reject: invalid license or zero query relevance;
182 2. strict automatic gate: best-only mode rejects every candidate missing any `required_terms`; this is the only pool available without visual review;
183 3. visual-review widening: thumbnail mode keeps strict matches first, then may admit a near match only when exactly one required group is absent and the explicit query that found it still has strong metadata relevance. The sidecar marks it `identity_evidence: visual-verification-required`; visual inspection must establish the missing identity before promotion;
184 4. identity priority: metadata-verified candidates whose title contains the required entity terms outrank candidates that match only via URL;
185 5. query relevance: concrete query tokens match whole ASCII metadata tokens and dominate generic visual words like "photo", "high quality", "background"; substrings such as `office` inside `officer` do not count;
186 6. layout fit: requested orientation helps; mismatched orientation is a small penalty, not a hard reject;
187 7. license / size tie-breakers: no-attribution is a small bonus; pixel count is capped so a huge but weakly relevant image cannot outrank a smaller accurate image.
188
189 Do not tune this into a visual taste engine. The scorer removes obvious metadata failures and orders the thumbnail sheet; visual review still decides whether any candidate fits the slide.
190
191 ### Suitability review — with or without a multimodal model
192
193 A metadata-ranked top hit is *downloadable and token-relevant*, not necessarily *visually suitable* — `score_candidate` never sees pixels. Review it against the active Reference and Crop Policy before it is trusted:
194
195 - **Multimodal review available**: run `--save-candidates`. The tool aggregates explicit query variants, deduplicates them, and saves at most the first **8** ranked previews under `candidates/<stem>/review/`; `review_sheet.jpg` contains only that page and no original is downloaded. Run [`web-image-review.md`](../workflows/stages/web-image-review.md): use one isolated vision reviewer for the current batch when available, otherwise review locally. Only the active image owner may use the returned candidate filename with `--promote`. If `has_more_candidates` is true and none passes, fetch `--candidate-page 2` before changing the query.
196 - **Non-multimodal model (no vision)**: omit `--save-candidates`; the tool excludes every `visual-verification-required` near match, downloads only the first candidate that passes all strict metadata / license / dimension gates, and records `selection_method: metadata-ranked`. Do **not** describe this as visual confirmation. If no strict candidate exists, or the active Reference requires a viewpoint, crop, expression, or fine identity detail that metadata cannot establish, mark the row `Needs-Manual`; Quick does not open an acquisition-time interaction.
197
198 The review stage owns pixel-inspection gates, bounded detail reads, and the compact decision receipt. It receives only the locked row intent plus candidate sidecars/sheets; it never receives the full planning or acquisition context.
199
200 If no thumbnail on the current page passes, download no original. When
201 `has_more_candidates` is true, advance to `next_candidate_page` first. Only
202 after the ranked pool is exhausted should you change the query materially —
203 identity wording, translation, alias, viewpoint, or necessary disambiguator —
204 reset that row to `Pending`, and generate a fresh pool. Do not promote the
205 least-bad candidate.
206
207 For exact-entity rows, suitability has two gates: `required_terms` first enforces metadata identity, then the `.review` image confirms the pixels actually show the right subject and satisfy the active focal/crop intent. Passing metadata never authorizes changing that intent downstream.
208
209 Never treat a generic `required_terms` pass as acceptance. For example, matching `Ground Fissure` can return an unrelated transit station named Yunlong, and matching `stone pillar` can return a different scenic area. If the proper name / geography cannot be retained, stop at `Needs-Manual`.
210
211 **Replacement ladder when the first round is not right**:
212
213 1. With vision, promote the one passing thumbnail selected under the review-stage contract; this is the first original-image request.
214 2. If none passes and `has_more_candidates` is true, fetch the next ranked page (8 by default). Candidate numbers continue globally, so page 2 starts at `candidate_09`; do not repeat page 1 or download an original.
215 3. After the current pool is exhausted, add materially different query variants for identity wording, official translation, alias, viewpoint, or disambiguation and generate a fresh pool; do not repeat a semantically exhausted query.
216 4. With vision only, if normal search is exhausted, select one relevant adopted `source_url` and follow [`topic-research`](../workflows/stages/topic-research.md) § Hand-off to fetch its Markdown + companion-image source package. Review that package, copy only one passing image into `<project>/images/`, and reconcile the query row plus `image_sources.json` from the selected `image_manifest.json` entry with `license_tier: manual`. Fetch another page only when the current package has no passing image; never auto-expand facts URLs or promote the whole package.
217 5. **manual URL replace (universal, model-agnostic)** — use a directly selected URL and swap it in:
218 ```bash
219 python3 scripts/image_search.py --from-url <image-url> --filename <name>.jpg -o <project_path>/images
220 ```
221 Recorded with `license_tier: manual` — verifying usage rights is the user's
222 call. In Quick Generate, use this step only when the URL was already
223 supplied; never pause to request one. The command updates the image and
224 `image_sources.json` but does **not** rewrite `image_queries.json`. Validate
225 the downloaded file and matching manual-provenance entry, then reconcile
226 that query row and the active roster to `Sourced` before export; a stale
227 `Needs-Manual` status remains blocking
228 ([`executor-web-image.md`](./executor-web-image.md) §1);
229 6. When the query variants, ranked pages, configured provider chain, permitted license stages, and eligible adopted-page package fallback are exhausted, mark the row `Needs-Manual`.
230
231 **This review never opens an acquisition-time interaction** ([`image-base.md`](./image-base.md) §6). Default Generate may build a placeholder and continue to Step 6. Quick Generate finishes all permitted automated strategies, records `Needs-Manual`, and blocks direct export when the unresolved image is required.
232
233 ### Visual selection candidates (multimodal Generate; standalone opt-in)
234
235 Candidate-thumbnail saving stays **off by default for standalone CLI use**.
236 Generate enables it for every web row when the current agent can inspect images,
237 so the first pass sees a bounded ranked page rather than trusting metadata rank
238 1 or flooding the reviewer with the complete pool.
239
240 ```bash
241 python3 scripts/image_search.py "<query>" --filename <name>.jpg -o <project_path>/images \
242 --save-candidates
243 ```
244
245 Saves provider previews to `images/candidates/<stem>/review/` with a
246 thumbnail-only `candidates.json` manifest and an automatically generated
247 `candidates/<stem>/review_sheet.jpg` containing only the current round. The
248 default first round is ranks 1–8. The sidecar records `candidate_page`,
249 `page_size`, `candidate_total`, `has_more_candidates`, each candidate's matched
250 query, and whether identity is metadata-verified or requires visual
251 verification. The target filename and `image_sources.json` remain untouched.
252 Inspect the sheet first, open only plausible individual previews when needed,
253 then promote the best fit — only that full-resolution original is downloaded
254 to the target:
255
256 ```bash
257 python3 scripts/image_search.py --promote candidate_03.jpg --filename <name>.jpg -o <project_path>/images
258
259 # No pass on page 1, but candidates.json says has_more_candidates: true
260 python3 scripts/image_search.py "<same query>" --filename <name>.jpg \
261 -o <project_path>/images --save-candidates --candidate-page 2
262
263 # Batch flow: also reconcile image_queries.json from Needs-Selection to Sourced
264 python3 scripts/image_search.py --promote candidate_03.jpg --filename <name>.jpg \
265 --batch <project_path>/images/image_queries.json -o <project_path>/images
266 ```
267
268 For batch continuation, set only the no-pass row's `candidate_page` to its
269 `next_candidate_page`, reset that row to `Pending`, and rerun. Use
270 `--max-candidates 0` only when a complete-pool dump is explicitly useful for
271 debugging; it is not the Generate default.
272
273 ---
274
275 ## 6. Manifest Format (`image_sources.json`)
276
277 Every successful download appends or replaces one entry keyed on `filename`:
278
279 ```json
280 {
281 "license_verification": "provider metadata used; manual review recommended for external delivery",
282 "generated_at": "2026-05-01T12:17:59.856275Z",
283 "items": [
284 {
285 "filename": "team.jpg",
286 "slide": "03_team",
287 "purpose": "Leadership photo",
288 "search_query": "executive boardroom meeting",
289 "matched_query": "leadership team boardroom",
290 "selection_method": "metadata-ranked",
291 "orientation": "landscape",
292 "provider": "openverse",
293 "stage": "all",
294 "title": "Untitled",
295 "author": "",
296 "source_page_url": "https://www.rawpixel.com/...",
297 "download_url": "https://...",
298 "license_name": "CC0",
299 "license_url": "https://creativecommons.org/publicdomain/zero/1.0/",
300 "license_tier": "no-attribution",
301 "attribution_required": false,
302 "width": 1024,
303 "height": 683,
304 "metadata_dimensions": {
305 "width": 4800,
306 "height": 3200,
307 "note": "upstream-reported size; actual downloaded file is smaller (likely a preview)"
308 },
309 "attribution_text": "team.jpg — \"Untitled\" via Openverse — license: CC0 (...)",
310 "status": "sourced"
311 }
312 ]
313 }
314 ```
315
316 | Field | Notes |
317 |---|---|
318 | `matched_query` | Explicit primary query or query variant that discovered the selected asset. |
319 | `selection_method` | `visual-thumbnail` after promotion from a reviewed preview, or `metadata-ranked` for the strict no-vision / best-only path. It never claims a visual check that did not occur. |
320 | `width` / `height` | Measured from the file actually saved to disk. Use these for layout. |
321 | `metadata_dimensions` | Present only when upstream-claimed size differs from the saved file (preview vs original). Informational only. |
322 | `license_tier` | Drives Executor's attribution decision: `no-attribution` / `attribution-required` for provider-sourced images, or `manual` for a directly selected URL/source-package replacement (embed only; rights/credit are the user's responsibility). |
323 | `attribution_required` | Boolean alias of `license_tier == "attribution-required"`. |
324 | `attribution_text` | Canonical credit source. Preserve its author/provider/license facts; compress only through §7's visual grammar rather than inventing or dropping identity. |
325 | `stage` | `all` by default, or `no-attribution-only` when strict mode is used. |
326
327 > Manifest is **idempotent on `filename`** and written atomically. Rerunning replaces that entry while preserving all others. An existing unreadable/non-object manifest blocks the write instead of being overwritten as fresh state.
328
329 ---
330
331 ## 7. On-Slide Attribution Contract
332
333 Applied by Executor when an image's `license_tier == "attribution-required"`.
334
335 **Hard rule — legal content and binding**: Every slide that uses the asset carries a visible, readable credit bound unambiguously to that asset. Preserve its author, source/provider, and CC BY / CC BY-SA license facts from `attribution_text`; do not invent, merge away, or drop identity.
336
337 **Reference — visual treatment is not a constraint**: Position, size, color, line structure, per-image versus combined credits, labels, and contrast treatment belong to the page composition. Use any treatment that stays readable and preserves the asset-to-credit binding; a scrim or gradient is optional, not required.
338
339 **Reference — attribution treatments, not constraints**:
340
341 | Page situation | Possible treatment |
342 |---|---|
343 | One credited image | Place a compact credit near the image edge or in a page footnote area |
344 | Several credited images | Use per-image credits or one combined source line with labels when needed for unambiguous mapping |
345 | Hero / full-bleed image | Place the credit in an available quiet region; add a scrim or gradient only when contrast otherwise fails |
346
347 Use `attribution_text` from the manifest as the **starting point**. Compress when the chosen page treatment needs a shorter line, without dropping the required facts:
348
349 | Manifest | Slide credit |
350 |---|---|
351 | `team.jpg — "Untitled" via Openverse — license: CC0 (...)` | `via Openverse / CC0` |
352 | `team.jpg — "Sunset" by Jane Doe via Wikimedia Commons — license: CC BY-SA 4.0 (...)` | `© Jane Doe / Wikimedia / CC BY-SA 4.0` |
353
354 ---
355
356 ## 8. Failure Handling (web-specific)
357
358 Extends [`image-base.md`](./image-base.md) §6.
359
360 | Situation | Behavior |
361 |---|---|
362 | No candidates from any provider in either stage | Mark row `Needs-Manual`. Suggest a more precise query or another configured provider; rerun without `--strict-no-attribution` only when the confirmed page may carry visible credit. |
363 | Current thumbnail page has no acceptable image and `has_more_candidates` is true | Fetch `next_candidate_page`; do not change the query or download an original yet. |
364 | Requested thumbnail page is past `candidate_total` | Treat the current pool as exhausted; add a materially different query variant or move to the manual boundary. |
365 | One or more previews fail while another qualified preview succeeds | Keep the successful thumbnail set; no original has been requested. |
366 | Every qualified preview fails | Mark row `Failed`; a later batch run retries it. |
367 | Selected original fails its download/readability/dimension gate | Leave `Needs-Selection`; select another passing thumbnail or materially change the query. Do not commit provenance. |
368 | Best-only candidate fails to download (HTTP 403/404) | Dispatcher auto-falls through to the next ranked candidate. |
369 | Provider/network failure remains after dispatch | Mark row `Failed`; a later batch run retries it. |
370 | Keyed provider has no API key | Silently skipped. Not an error. |
371
372 CLI exit: a successfully prepared `Needs-Selection` thumbnail set returns `0`
373 as an intermediate success; `Failed` or `Needs-Manual` returns `1`.
374
375 ---
376
377 ## 9. Handoff with the Intent Owner
378
379 Reference field is **intent description**, not a query. See [`image-base.md`](./image-base.md) §8 for the rule.
380
381 Keep it intact as the acceptance contract. In Default Generate the owner is Strategist; in Quick Generate it is the current main agent's active-context resource decision. Derive a separate concise provider query that preserves exact names and necessary disambiguation; do not pass the Reference verbatim or rewrite it after search.
382
383 ---
384
385 ## 10. Handoff with Executor
386
387 Executor reads `image_sources.json` per slide that uses a Sourced image. For each entry:
388
389 | `license_tier` | Slide-level action |
390 |---|---|
391 | `no-attribution` | Embed `<image>` only |
392 | `attribution-required` | Embed `<image>` **and** an inline credit element per §7 |
393 | `manual` | Embed `<image>` only — directly selected URL or adopted-page package image; verifying usage rights / any required credit is the user's responsibility |
394
395 Executor does not interpret raw license strings — `license_tier` is sufficient.
396
397 `svg_quality_checker.py` verifies this handoff before post-processing: a referenced attribution-required image needs its own visible author + CC BY / CC BY-SA credit; one generic deck-level CC token cannot satisfy several images.
398
399 ---
400
401 ## 11. Task Completion Checkpoint
402
403 In addition to the shared checkpoint in [`image-base.md`](./image-base.md) §10:
404
405 - [ ] Every required web row is `Sourced` with a downloaded original at `project/images/<filename>` OR is marked `Needs-Manual`; `Needs-Selection` remains incomplete
406 - [ ] Each multimodal `Sourced` web image was selected from a bounded ranked thumbnail page and only its winner original was downloaded; a no-pass page advanced through remaining pages before query replacement. Without vision, only strict metadata candidates may become `Sourced`, with `selection_method: metadata-ranked`; unresolved or visually unprovable intent becomes `Needs-Manual` without pretending a visual check occurred
407 - [ ] Each `Sourced` row has a manifest entry with valid `license_tier` and non-empty `attribution_text` (except `manual` directly selected rows, which carry no `attribution_text`)
408 - [ ] Any `attribution-required` image has visible author + license credit in every SVG that references it
409 - [ ] `metadata_dimensions` warnings surfaced when downloaded preview is much smaller than upstream-claimed size
410 - [ ] `Needs-Manual` rows include the failure reason
411
411 lines MARKDOWN