gooseworks 0.4.2 → 0.5.0-dev.3.8661ef81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -21
- package/dist/agents/staging.d.ts +38 -0
- package/dist/agents/staging.d.ts.map +1 -0
- package/dist/agents/staging.js +309 -0
- package/dist/agents/staging.js.map +1 -0
- package/dist/auth/credentials.d.ts +1 -0
- package/dist/auth/credentials.d.ts.map +1 -1
- package/dist/auth/credentials.js +26 -8
- package/dist/auth/credentials.js.map +1 -1
- package/dist/auth/oauth-server.d.ts.map +1 -1
- package/dist/auth/oauth-server.js +18 -2
- package/dist/auth/oauth-server.js.map +1 -1
- package/dist/commands/doctor.d.ts +29 -5
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +117 -63
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/fetch.d.ts +1 -0
- package/dist/commands/fetch.d.ts.map +1 -1
- package/dist/commands/fetch.js +64 -29
- package/dist/commands/fetch.js.map +1 -1
- package/dist/commands/formats.js +2 -2
- package/dist/commands/formats.js.map +1 -1
- package/dist/commands/graphics/publish-flow.js +1 -1
- package/dist/commands/graphics/publish-flow.js.map +1 -1
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +57 -4
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/launch.d.ts +3 -0
- package/dist/commands/launch.d.ts.map +1 -0
- package/dist/commands/launch.js +35 -0
- package/dist/commands/launch.js.map +1 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +15 -3
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +3 -1
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/skills.d.ts +50 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +160 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/styles.js +2 -2
- package/dist/commands/styles.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +37 -6
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/video-local.d.ts +9 -0
- package/dist/commands/video-local.d.ts.map +1 -0
- package/dist/commands/video-local.js +83 -0
- package/dist/commands/video-local.js.map +1 -0
- package/dist/commands/video-save.d.ts +3 -0
- package/dist/commands/video-save.d.ts.map +1 -0
- package/dist/commands/video-save.js +83 -0
- package/dist/commands/video-save.js.map +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +11 -3
- package/dist/config.js.map +1 -1
- package/dist/environment.d.ts +9 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +103 -0
- package/dist/environment.js.map +1 -0
- package/dist/index.js +12 -38
- package/dist/index.js.map +1 -1
- package/dist/lib/graphics-cache.d.ts.map +1 -1
- package/dist/lib/graphics-cache.js +2 -2
- package/dist/lib/graphics-cache.js.map +1 -1
- package/dist/lib/video-local-captions.d.ts +83 -0
- package/dist/lib/video-local-captions.d.ts.map +1 -0
- package/dist/lib/video-local-captions.js +261 -0
- package/dist/lib/video-local-captions.js.map +1 -0
- package/dist/lib/video-local-runtime.d.ts +8 -0
- package/dist/lib/video-local-runtime.d.ts.map +1 -0
- package/dist/lib/video-local-runtime.js +201 -0
- package/dist/lib/video-local-runtime.js.map +1 -0
- package/dist/lib/video-save-mcp.d.ts +9 -0
- package/dist/lib/video-save-mcp.d.ts.map +1 -0
- package/dist/lib/video-save-mcp.js +162 -0
- package/dist/lib/video-save-mcp.js.map +1 -0
- package/dist/lib/video-save.d.ts +77 -0
- package/dist/lib/video-save.d.ts.map +1 -0
- package/dist/lib/video-save.js +440 -0
- package/dist/lib/video-save.js.map +1 -0
- package/dist/program.d.ts +2 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +54 -0
- package/dist/program.js.map +1 -0
- package/dist/skills/custom-video-skill.d.ts +3 -0
- package/dist/skills/custom-video-skill.d.ts.map +1 -0
- package/dist/skills/custom-video-skill.js +6 -0
- package/dist/skills/custom-video-skill.js.map +1 -0
- package/dist/skills/installer.d.ts +12 -6
- package/dist/skills/installer.d.ts.map +1 -1
- package/dist/skills/installer.js +82 -38
- package/dist/skills/installer.js.map +1 -1
- package/dist/skills/master-skill.d.ts +34 -28
- package/dist/skills/master-skill.d.ts.map +1 -1
- package/dist/skills/master-skill.js +2181 -858
- package/dist/skills/master-skill.js.map +1 -1
- package/dist/skills/package-freshness.d.ts +16 -0
- package/dist/skills/package-freshness.d.ts.map +1 -0
- package/dist/skills/package-freshness.js +36 -0
- package/dist/skills/package-freshness.js.map +1 -0
- package/dist/skills/releases.d.ts +12 -0
- package/dist/skills/releases.d.ts.map +1 -0
- package/dist/skills/releases.js +155 -0
- package/dist/skills/releases.js.map +1 -0
- package/dist/skills/routes.d.ts.map +1 -1
- package/dist/skills/routes.js +12 -6
- package/dist/skills/routes.js.map +1 -1
- package/dist/skills/staging-content.d.ts +3 -0
- package/dist/skills/staging-content.d.ts.map +1 -0
- package/dist/skills/staging-content.js +12 -0
- package/dist/skills/staging-content.js.map +1 -0
- package/dist/utils/browser-preflight.d.ts +29 -0
- package/dist/utils/browser-preflight.d.ts.map +1 -0
- package/dist/utils/browser-preflight.js +338 -0
- package/dist/utils/browser-preflight.js.map +1 -0
- package/package.json +11 -3
- package/skills/goose-ads/SKILL.md +486 -239
- package/skills/goose-product-photos/SKILL.md +138 -60
- package/skills/goose-video/SKILL.md +537 -259
- package/skills/goose-video-local/SKILL.md +1372 -236
- package/skills/gooseworks/SKILL.md +300 -32
- package/skills/make-custom-video/SKILL.md +697 -0
- package/skills/manifest.json +12 -0
- package/skills/routes.json +13 -6
- package/dist/skills/fetcher.d.ts +0 -18
- package/dist/skills/fetcher.d.ts.map +0 -1
- package/dist/skills/fetcher.js +0 -118
- package/dist/skills/fetcher.js.map +0 -1
|
@@ -11,7 +11,7 @@ description: >
|
|
|
11
11
|
references a product to photograph. Unlike goose-ads (ad creative) this produces clean PRODUCT
|
|
12
12
|
photos that can then feed the ad workflow.
|
|
13
13
|
category: ads
|
|
14
|
-
version: 0.
|
|
14
|
+
version: 0.3.0
|
|
15
15
|
author: GooseWorks
|
|
16
16
|
tags: [gooseworks, ads, product-photos, photoshoot, product, ecommerce, studio, lifestyle, on-model]
|
|
17
17
|
---
|
|
@@ -19,26 +19,93 @@ tags: [gooseworks, ads, product-photos, photoshoot, product, ecommerce, studio,
|
|
|
19
19
|
# GooseWorks Product Photos — branded product photography
|
|
20
20
|
|
|
21
21
|
The GooseWorks Product Photos skill. You **pick a brand + product and submit one generation**;
|
|
22
|
-
the **backend** runs the whole pipeline (compose the shot prompt → generate
|
|
23
|
-
|
|
22
|
+
the **backend** runs the whole pipeline (compose the shot prompt → generate → judge for product
|
|
23
|
+
fidelity → auto-retry a few times for free) and stores the results. You do NOT
|
|
24
24
|
generate images, call a model, or manage files — this is the exact same workflow the Product
|
|
25
25
|
Photos studio uses, so the skill and the app can never drift. The point is to **enrich a brand's
|
|
26
26
|
usable product imagery** — approved photos join the brand kit and can then feed the ad workflow
|
|
27
27
|
(`goose-ads`).
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
It shoots a **physical catalog product** (apparel, beauty, consumer goods). A software
|
|
30
|
+
screenshot or an app mockup is not a product photo: that is an image edit, not this skill.
|
|
31
|
+
|
|
32
|
+
## How to talk to the customer (applies to every message you send them)
|
|
33
|
+
|
|
34
|
+
The customer is a marketer or founder, not an engineer. Everything in this skill about tools,
|
|
35
|
+
fields, files, polling, models and pipeline steps is for YOU. Never pass it on to them.
|
|
36
|
+
|
|
37
|
+
- **Say what they get and what they need to decide, never how it's made.**
|
|
38
|
+
Bad: "Checking the media-proxy helpers for voiceover timestamps and lipsync." / "Generated with
|
|
39
|
+
gpt-image-2 at high quality, mouth closed, passes QC."
|
|
40
|
+
Good: "Recording the voices now." / "Here are your two hosts."
|
|
41
|
+
- **Never mention** tool, file or field names, ids, JSON, commands, scripts, installed software,
|
|
42
|
+
uploads, model or vendor names, timings in seconds or frames, retries, polling, internal statuses
|
|
43
|
+
or your own quality checks. Fix what you can quietly. If they ask how something works, explain it
|
|
44
|
+
simply.
|
|
45
|
+
- **Update them only at milestones they care about**: choices confirmed, script and voices done,
|
|
46
|
+
visuals done, ready to review, finished. One short line each, then stay quiet until the next
|
|
47
|
+
milestone. No update for setup, downloads, uploads or saving.
|
|
48
|
+
- **Money is in credits, with a number**: "the full video uses about N credits". Never "the cheap
|
|
49
|
+
pieces" or "the expensive render".
|
|
50
|
+
- **Problems:** say what it means for them and what they can do, in one or two sentences. No error
|
|
51
|
+
codes or stack traces. Only raise a problem that changes something for them.
|
|
52
|
+
- **Use their words**: script, voice, hook, scene, host, ending, logo, the product shot.
|
|
53
|
+
- **No filler**: no "Great question!", "Certainly!", "I hope this helps!", no restating what they
|
|
54
|
+
just said at length.
|
|
55
|
+
|
|
56
|
+
Example. Instead of a dozen lines about render tools, scripts, uploads and portraits, send:
|
|
57
|
+
"Got it: warm tone, home podcast studio, Brielle as the skeptic and Mark as the believer." then
|
|
58
|
+
"Script and voices are done (about 34 seconds)." then "Ready for you to review: the script, voices,
|
|
59
|
+
both hosts and the ending. You can also review your recipe ingredients in the app: <link>. Say go
|
|
60
|
+
here and I'll make the full video (about N credits)."
|
|
61
|
+
|
|
62
|
+
## Use current instructions for new work
|
|
63
|
+
|
|
64
|
+
Before a new task in a terminal host, run `gooseworks skills status` once. It compares the
|
|
65
|
+
installed entry files, running CLI and published npm release. An older CLI needs a package
|
|
66
|
+
upgrade before `gooseworks update`; update alone only uses that CLI's bundled instructions.
|
|
67
|
+
Preserve local edits or unknown install provenance. Review/back up before explicitly replacing
|
|
68
|
+
modified files; never quietly reinstall over them. If the release check is unavailable, report
|
|
69
|
+
that freshness is unknown rather than claiming the local copy is latest.
|
|
70
|
+
|
|
71
|
+
For a new recipe run, fetch its package from the connected catalog. Retain the returned
|
|
72
|
+
`version` / `contentHash` and every dependency's hash with the saved package. When reusing a
|
|
73
|
+
saved fetch JSON, `gooseworks fetch <slug> --saved-package <file>` returns the current package
|
|
74
|
+
and a hash comparison without changing that file. With MCP, if the advertised `catalog_fetch`
|
|
75
|
+
schema accepts them, send `saved_content_hash` and `saved_dependency_hashes`; otherwise fetch
|
|
76
|
+
normally and compare the returned hashes yourself. Missing hashes mean unknown, not current.
|
|
77
|
+
The server cannot inspect a client's saved files; hash metadata does not certify later edits.
|
|
78
|
+
Fetch current packages into a new run directory and report stale saved instructions. Preserve
|
|
79
|
+
an existing approved run's recorded package; changing that harness requires a reviewed change
|
|
80
|
+
and approval before spending. Hosted installed snapshots use the existing Skills Update action.
|
|
81
|
+
Skill content and a host's cached MCP tool schemas are separate: refreshing one does not refresh
|
|
82
|
+
the other. Check the actual advertised tools before using new fields.
|
|
83
|
+
|
|
84
|
+
## Prerequisite — the GooseWorks connector tools
|
|
85
|
+
|
|
86
|
+
Everything below runs through the GooseWorks connector's tools, by the names this skill uses.
|
|
87
|
+
Match on the tool name: a coding agent may show a server prefix (for example
|
|
88
|
+
`mcp__gooseworks__photos_generate`), a chat app may not.
|
|
89
|
+
|
|
90
|
+
If a tool named here is missing, the GooseWorks connection or its tool list is stale: ask the
|
|
91
|
+
user to reconnect or refresh GooseWorks in their app's connector settings. Installing or
|
|
92
|
+
updating the `gooseworks` CLI never fixes a missing connector tool, so never send a chat-app
|
|
93
|
+
user to a terminal for it. Only a terminal coding agent (Claude Code, Codex, Cursor) that has no
|
|
94
|
+
GooseWorks tools at all connects them, in that terminal, with `gooseworks install --mcp` plus
|
|
95
|
+
`--claude`, `--codex` or `--cursor`, then a restart.
|
|
96
|
+
|
|
97
|
+
Older notes or skill copies may name tools the connector no longer lists. Use the tool this skill
|
|
98
|
+
names instead; `catalog_fetch { type: "skill", slug: "gooseworks-guide" }` maps every old name.
|
|
34
99
|
|
|
35
100
|
## Start from the brand context — don't re-ask what it already answers
|
|
36
101
|
|
|
37
102
|
If the `gooseworks` router handed you brand context, USE IT. If you were invoked directly, call
|
|
38
|
-
`
|
|
39
|
-
the user for
|
|
103
|
+
`brand_read { brand_id, sections: ["summary","kit","products","learnings"] }` yourself first, then
|
|
104
|
+
`knowledge_search { brand_id, query: "<the shoot, in the user's words>" }` for saved rules and past
|
|
105
|
+
feedback (an archived photo's reason is saved as a `dont` rule). They answer most of the setup
|
|
106
|
+
questions below, so **do not ask the user for them**:
|
|
40
107
|
|
|
41
|
-
- **Which product?** — the context's `products
|
|
108
|
+
- **Which product?** — the context's `products` are the real catalog entries. Offer them; never
|
|
42
109
|
invent a product or ask the user to describe one you can already see.
|
|
43
110
|
- **What does it look like / what is it made of?** — grounded in the product's stored images and
|
|
44
111
|
description. Never guess a material, colorway, or silhouette.
|
|
@@ -50,79 +117,90 @@ the user for them**:
|
|
|
50
117
|
Ask only for the genuinely open choices: the shot `category`, how many photos, quality, and
|
|
51
118
|
whether a human model is wanted (which needs explicit consent — see the rules).
|
|
52
119
|
|
|
53
|
-
##
|
|
120
|
+
## Credits — state the total, then get a yes
|
|
54
121
|
|
|
55
|
-
- One
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
122
|
+
- One token authenticates the tools and resolves your org; never print it. Omit `target`.
|
|
123
|
+
- **Quote first.** Call `photos_generate` with the exact arguments you will submit plus
|
|
124
|
+
`dry_run: true`. It reserves nothing and returns `creditsPerOutput` and `totalCredits`. Pass
|
|
125
|
+
`count` and `quality` explicitly in both calls so the quote matches the run.
|
|
126
|
+
- **Nothing paid runs without the user's explicit yes in this chat, given after you state that
|
|
127
|
+
credit total.** Then submit the same call without `dry_run`.
|
|
128
|
+
- The submit reserves the quoted credits and bills only the photos that pass the judge:
|
|
129
|
+
**automatic retries are free**, and a photo the judge can't get right (`flagged`) is shown but
|
|
130
|
+
**never billed**. The balance is `credits.available_credits` from `account_whoami`. If the
|
|
131
|
+
wallet is short, say so plainly with the total and the balance, and stop.
|
|
62
132
|
|
|
63
133
|
## The tools
|
|
64
134
|
|
|
65
135
|
**Pick the brand + product**
|
|
66
|
-
- `
|
|
67
|
-
- `
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
136
|
+
- `brand_read` with no `brand_id` — the user's brands (each row's `id` is the `brand_id`).
|
|
137
|
+
- `brand_read { brand_id, sections: ["products"], products_query: "<name>" }` — the brand's
|
|
138
|
+
imported products in `products.items`; a product's `id` is the `product_id` to shoot.
|
|
139
|
+
`products_query` matches name / type / variant / SKU / description.
|
|
140
|
+
- Import a product that isn't in the catalog yet (free):
|
|
141
|
+
`brand_update { brand_id, patch: { products: [{ import_url, import_kind, name? }] } }`.
|
|
142
|
+
`import_kind` is `product_url` (a single product page), `shopify_store` (a store URL → imports
|
|
143
|
+
the catalog), or `image_url` (a direct image; also needs `name`). It returns `jobs[]`: poll
|
|
144
|
+
`job_get { job_id, kind: "product_import" }` until it finishes, then read the products again.
|
|
145
|
+
To add a photo the user attached to an existing product:
|
|
146
|
+
`media_upload { brand_id, scope: "product", scope_id: <product_id>, kind: "reference", source: { type: "bytes", filename, content_base64 } }`.
|
|
75
147
|
|
|
76
148
|
**Generate**
|
|
77
|
-
- `
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
`get_product_photo_generation` until done, then read each `outputs[].final_image_url`.
|
|
149
|
+
- `photos_generate { brand_id, product_id, variant_id?, category, controls?, prompt?, count,
|
|
150
|
+
quality, reference_image_urls?, attestation_accepted?, dry_run? }` — **the one call that makes
|
|
151
|
+
photos** (and, with `dry_run: true`, its free quote). `category` is `apparel` | `beauty` |
|
|
152
|
+
`cpg` (seeds sensible scene/framing defaults). `count` is 1, 2, 4, or 8; `quality` is
|
|
153
|
+
`low` | `medium` | `high`. Omit `controls` to use the category preset; pass `prompt` as
|
|
154
|
+
free-text steering **added on top of** the settings (it doesn't replace them). Returns the
|
|
155
|
+
generation with its `id` **immediately**.
|
|
85
156
|
**If you request a human model** (`controls.model.presence` is not `none`) you MUST pass
|
|
86
157
|
`attestation_accepted: true` to confirm the user has the rights for model imagery.
|
|
87
|
-
- `
|
|
88
|
-
`partial_failure`, or `failed
|
|
158
|
+
- `photos_read { brand_id, generation_id }` — poll until `status` is `complete`,
|
|
159
|
+
`partial_failure`, or `failed` (`job_get { job_id: <generation id>, kind: "photo_generation" }`
|
|
160
|
+
works too). Each `outputs[]` entry has its own `id`, `status` and, once ready, a
|
|
89
161
|
`final_image_url`. A `flagged` output is the best attempt but wasn't billed.
|
|
90
162
|
|
|
91
163
|
**Use the results**
|
|
92
|
-
- `
|
|
93
|
-
= active, `true` = archived).
|
|
94
|
-
- `
|
|
95
|
-
available in the **brand kit**, so `goose-ads` can use it. **Photos are
|
|
96
|
-
approved.**
|
|
97
|
-
- `
|
|
98
|
-
from ad generation.
|
|
164
|
+
- `photos_read { brand_id, archived?, product_id?, status? }` — the brand's generated photos
|
|
165
|
+
(`archived: false` = active, `true` = archived).
|
|
166
|
+
- `photos_update { brand_id, output_id, action: "approve" }` — approve a photo: links it to the
|
|
167
|
+
product and makes it available in the **brand kit**, so `goose-ads` can use it. **Photos are
|
|
168
|
+
not used anywhere until approved.**
|
|
169
|
+
- `photos_update { brand_id, output_id, action: "archive", reason? }` — archive a photo; archived
|
|
170
|
+
photos are **excluded** from ad generation, and the reason is saved as a `dont` rule.
|
|
99
171
|
|
|
100
172
|
## Workflow — shoot a product
|
|
101
173
|
|
|
102
|
-
1. **Load the brand context** (`
|
|
103
|
-
**resolve the brand + product.** `
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
174
|
+
1. **Load the brand context** (`brand_read` + `knowledge_search`, or reuse what the router passed
|
|
175
|
+
you) and **resolve the brand + product.** Pick a `product_id` from the catalog you already
|
|
176
|
+
know about. If the product genuinely isn't there, import it and poll the import.
|
|
177
|
+
2. **Quote the cost.** `photos_generate` with `dry_run: true` and the exact `brand_id`,
|
|
178
|
+
`product_id`, `category`, `count` and `quality` → tell the user the credit total and wait
|
|
179
|
+
for their explicit yes.
|
|
180
|
+
3. **Generate.** The same `photos_generate` call without `dry_run` (add `prompt` built from the
|
|
181
|
+
brand's voice/positioning you already have — don't interview the user for it). Returns a
|
|
182
|
+
generation `id` right away.
|
|
183
|
+
4. **Poll.** `photos_read { brand_id, generation_id }` every ~20-30s until terminal; show each
|
|
111
184
|
`final_image_url`.
|
|
112
|
-
5. **Approve the keepers.** Show the results and let the user pick; `
|
|
113
|
-
they'd publish (that's what puts them in the brand kit for ads),
|
|
185
|
+
5. **Approve the keepers.** Show the results and let the user pick; `photos_update` with
|
|
186
|
+
`action: "approve"` the ones they'd publish (that's what puts them in the brand kit for ads),
|
|
187
|
+
`action: "archive"` the rest.
|
|
114
188
|
|
|
115
189
|
## Rules
|
|
116
190
|
|
|
191
|
+
- **Connector tools only** — a missing tool means the GooseWorks connection is stale: ask the
|
|
192
|
+
user to reconnect or refresh GooseWorks. Never send a chat-app user to a terminal or a CLI
|
|
193
|
+
install.
|
|
117
194
|
- **Never invent product facts.** The backend grounds the shot on the product's real images; don't
|
|
118
195
|
describe a product you can't see.
|
|
119
196
|
- **Use the brand context instead of interviewing the user.** Product, audience, voice, positioning,
|
|
120
|
-
logo/colors/fonts all come from `
|
|
197
|
+
logo/colors/fonts all come from `brand_read` / the brand kit. Ask only for the shot
|
|
121
198
|
category, count, quality, and model consent.
|
|
122
|
-
- **Ask before spending.**
|
|
123
|
-
`
|
|
199
|
+
- **Ask before spending.** State the dry-run credit total and get the user's explicit yes before
|
|
200
|
+
the real `photos_generate` — it reserves credits.
|
|
124
201
|
- **Poll, don't re-submit.** A generation that's still `running` is not stuck; re-submitting
|
|
125
|
-
double-bills.
|
|
202
|
+
double-bills. A `failed` generation may still hold `flagged` photos (shown, never billed):
|
|
203
|
+
show those first, and run again only after a new quote and the user's yes.
|
|
126
204
|
- **Model imagery needs consent.** Only set a human model when the user asks, and pass
|
|
127
205
|
`attestation_accepted: true`.
|
|
128
206
|
- **Approval is the hand-off to ads.** Remind the user that only **approved** photos reach the brand
|