gooseworks 0.4.4 → 0.4.5-dev.15.facf60fb

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.
Files changed (120) hide show
  1. package/README.md +42 -11
  2. package/dist/agents/staging.d.ts +38 -0
  3. package/dist/agents/staging.d.ts.map +1 -0
  4. package/dist/agents/staging.js +309 -0
  5. package/dist/agents/staging.js.map +1 -0
  6. package/dist/auth/credentials.d.ts +1 -0
  7. package/dist/auth/credentials.d.ts.map +1 -1
  8. package/dist/auth/credentials.js +26 -8
  9. package/dist/auth/credentials.js.map +1 -1
  10. package/dist/auth/device-flow.d.ts +79 -0
  11. package/dist/auth/device-flow.d.ts.map +1 -0
  12. package/dist/auth/device-flow.js +411 -0
  13. package/dist/auth/device-flow.js.map +1 -0
  14. package/dist/auth/login-mode.d.ts +25 -0
  15. package/dist/auth/login-mode.d.ts.map +1 -0
  16. package/dist/auth/login-mode.js +87 -0
  17. package/dist/auth/login-mode.js.map +1 -0
  18. package/dist/auth/oauth-server.d.ts +2 -2
  19. package/dist/auth/oauth-server.d.ts.map +1 -1
  20. package/dist/auth/oauth-server.js +28 -5
  21. package/dist/auth/oauth-server.js.map +1 -1
  22. package/dist/auth/paste-login.d.ts +27 -0
  23. package/dist/auth/paste-login.d.ts.map +1 -0
  24. package/dist/auth/paste-login.js +203 -0
  25. package/dist/auth/paste-login.js.map +1 -0
  26. package/dist/commands/doctor.d.ts.map +1 -1
  27. package/dist/commands/doctor.js +17 -8
  28. package/dist/commands/doctor.js.map +1 -1
  29. package/dist/commands/formats.js +2 -2
  30. package/dist/commands/formats.js.map +1 -1
  31. package/dist/commands/graphics/publish-flow.js +1 -1
  32. package/dist/commands/graphics/publish-flow.js.map +1 -1
  33. package/dist/commands/install.d.ts.map +1 -1
  34. package/dist/commands/install.js +101 -34
  35. package/dist/commands/install.js.map +1 -1
  36. package/dist/commands/launch.d.ts +3 -0
  37. package/dist/commands/launch.d.ts.map +1 -0
  38. package/dist/commands/launch.js +35 -0
  39. package/dist/commands/launch.js.map +1 -0
  40. package/dist/commands/login.d.ts +21 -1
  41. package/dist/commands/login.d.ts.map +1 -1
  42. package/dist/commands/login.js +106 -29
  43. package/dist/commands/login.js.map +1 -1
  44. package/dist/commands/logout.d.ts.map +1 -1
  45. package/dist/commands/logout.js +7 -1
  46. package/dist/commands/logout.js.map +1 -1
  47. package/dist/commands/skills.d.ts +16 -0
  48. package/dist/commands/skills.d.ts.map +1 -1
  49. package/dist/commands/skills.js +52 -1
  50. package/dist/commands/skills.js.map +1 -1
  51. package/dist/commands/styles.js +2 -2
  52. package/dist/commands/styles.js.map +1 -1
  53. package/dist/commands/tool.d.ts +17 -0
  54. package/dist/commands/tool.d.ts.map +1 -0
  55. package/dist/commands/tool.js +239 -0
  56. package/dist/commands/tool.js.map +1 -0
  57. package/dist/commands/update.d.ts.map +1 -1
  58. package/dist/commands/update.js +20 -0
  59. package/dist/commands/update.js.map +1 -1
  60. package/dist/commands/video-local.d.ts.map +1 -1
  61. package/dist/commands/video-local.js +2 -2
  62. package/dist/commands/video-local.js.map +1 -1
  63. package/dist/config.d.ts.map +1 -1
  64. package/dist/config.js +14 -3
  65. package/dist/config.js.map +1 -1
  66. package/dist/environment.d.ts +9 -0
  67. package/dist/environment.d.ts.map +1 -0
  68. package/dist/environment.js +105 -0
  69. package/dist/environment.js.map +1 -0
  70. package/dist/index.js +12 -45
  71. package/dist/index.js.map +1 -1
  72. package/dist/lib/graphics-cache.d.ts.map +1 -1
  73. package/dist/lib/graphics-cache.js +2 -2
  74. package/dist/lib/graphics-cache.js.map +1 -1
  75. package/dist/lib/mcp-client.d.ts +53 -0
  76. package/dist/lib/mcp-client.d.ts.map +1 -0
  77. package/dist/lib/mcp-client.js +196 -0
  78. package/dist/lib/mcp-client.js.map +1 -0
  79. package/dist/program.d.ts +2 -0
  80. package/dist/program.d.ts.map +1 -0
  81. package/dist/program.js +56 -0
  82. package/dist/program.js.map +1 -0
  83. package/dist/skills/custom-video-skill.d.ts +1 -1
  84. package/dist/skills/custom-video-skill.d.ts.map +1 -1
  85. package/dist/skills/custom-video-skill.js +1 -1
  86. package/dist/skills/custom-video-skill.js.map +1 -1
  87. package/dist/skills/installer.d.ts.map +1 -1
  88. package/dist/skills/installer.js +42 -24
  89. package/dist/skills/installer.js.map +1 -1
  90. package/dist/skills/master-skill.d.ts +12 -9
  91. package/dist/skills/master-skill.d.ts.map +1 -1
  92. package/dist/skills/master-skill.js +857 -372
  93. package/dist/skills/master-skill.js.map +1 -1
  94. package/dist/skills/releases.d.ts +1 -1
  95. package/dist/skills/releases.d.ts.map +1 -1
  96. package/dist/skills/releases.js +8 -5
  97. package/dist/skills/releases.js.map +1 -1
  98. package/dist/skills/routes.js +8 -8
  99. package/dist/skills/routes.js.map +1 -1
  100. package/dist/skills/staging-content.d.ts +3 -0
  101. package/dist/skills/staging-content.d.ts.map +1 -0
  102. package/dist/skills/staging-content.js +12 -0
  103. package/dist/skills/staging-content.js.map +1 -0
  104. package/dist/utils/http.d.ts +7 -1
  105. package/dist/utils/http.d.ts.map +1 -1
  106. package/dist/utils/http.js +5 -1
  107. package/dist/utils/http.js.map +1 -1
  108. package/package.json +12 -4
  109. package/skills/goose-ads/SKILL.md +386 -224
  110. package/skills/goose-product-photos/SKILL.md +107 -58
  111. package/skills/goose-video/SKILL.md +300 -36
  112. package/skills/goose-video-local/SKILL.md +239 -31
  113. package/skills/gooseworks/SKILL.md +133 -26
  114. package/skills/make-custom-video/SKILL.md +256 -11
  115. package/skills/manifest.json +7 -7
  116. package/skills/routes.json +8 -8
  117. package/dist/skills/fetcher.d.ts +0 -18
  118. package/dist/skills/fetcher.d.ts.map +0 -1
  119. package/dist/skills/fetcher.js +0 -118
  120. package/dist/skills/fetcher.js.map +0 -1
@@ -65,6 +65,158 @@ an existing approved run's recorded package; changing that harness requires a re
65
65
  and approval before spending. Hosted installed snapshots use the existing Skills Update action.
66
66
  Skill content and a host's cached MCP tool schemas are separate: refreshing one does not refresh
67
67
  the other. Check the actual advertised tools before using new fields.`;
68
+ const VIDEO_GUIDE_COMPATIBILITY = `**Follow the connector's full-guide requirement when available.** Fetch
69
+ \`catalog_fetch { type: "skill", slug: "gooseworks-guide" }\` when the connector requires it.
70
+ The only older-server exception is below; it never removes brand preparation or approvals.
71
+
72
+ | Returned guide/workflow state on the selected connection | Required action |
73
+ | --- | --- |
74
+ | Guide returns \`not_found\`, and this same connection already returned the complete current matching video entry with its required dependencies | Continue with that authoritative entry's workflow, full brand preparation and existing approvals. |
75
+ | Guide has another error, no response, or incomplete content; or the matching entry/dependencies are missing, incomplete or from another connection | Stop before creative work, project writes or paid calls; resolve a compatible connection on this same environment. |
76
+
77
+ A missing custom entry or required custom tool/schema remains unavailable: never substitute
78
+ a template or an import to bypass it. Never hide other fetch errors or continue from cached
79
+ instructions merely because the guide was not found.`;
80
+ /**
81
+ * The Brain-first entry contract, shared by the router, goose-ads and every video
82
+ * entry. A fresh agent read the Kit, then asked the user for angles and proof
83
+ * the Brand Brain already held: no entry made the search a step, and several
84
+ * told it to ask. Searching is free and read-only, so it needs no approval.
85
+ */
86
+ const BRAIN_FIRST_ENTRY = `## Search the Brand Brain, then propose — before any creative choice or question
87
+
88
+ The Brand Kit is a summary. The brand's saved knowledge (its Brain) holds what the Kit does not:
89
+ rules from past feedback, approved and rejected creatives, customer evidence, approved claims,
90
+ reports and documents. **After \`brand_read\`, and before you choose an angle, claim, hook,
91
+ product emphasis, format or source ad — and before you ask the user for any brand fact — call
92
+ \`knowledge_search { brand_id, query }\`** when it is registered. It is free and read-only: no
93
+ approval, no announcement to the customer and no questionnaire.
94
+
95
+ 1. **Search for this task, not the whole Brain.** Run one short query (under 500 characters) in
96
+ the user's own words plus the product (for example "ads for <product>: what worked, what to
97
+ avoid") and one with
98
+ \`source_types: ["evidence", "claim", "learning", "creative", "document"]\` for proof and past
99
+ creative results. Add a query only for a specific open question. Reuse results from this run.
100
+ 2. **Keep these states distinct** and record which one each query returned:
101
+
102
+ | Result | Means | Do |
103
+ | --- | --- | --- |
104
+ | \`status: "ok"\` with matches | Saved knowledge exists | Use it; keep each fact's citation in your working brief |
105
+ | \`status: "empty"\` | Nothing saved matches this query | Say "no saved evidence for <topic>", never "the brand has no proof" |
106
+ | \`refresh_required\` | The results shown are current; some changed sources were left out | Use them; search again shortly for anything missing |
107
+ | \`building\` | The index is not ready | Retry once shortly, then continue with the gap stated |
108
+ | An error, or the tool is not registered | Retrieval failed or is unavailable | Retry an error once, then continue from \`brand_read\` and treat evidence as unchecked |
109
+ | An empty Kit field | Only that field is blank | Not a search result: still search before asking |
110
+
111
+ 3. **Let the findings change the plan.** A \`dont\`/\`must\` learning or a rejected creative rules
112
+ options out; an approved or well-rated past creative is a proven angle to lead with; a report
113
+ shows what worked. Claim-grade proof is a result with \`approved_ad_claim: true\` or the kit's
114
+ \`approvedClaims\` within their stated applicability; the kit's \`proofPoints\` are what the
115
+ backend allows an ad to state, used as written. Other Kit text, documents and performance
116
+ numbers are context, never public claims. Never invent an offer, price or result. Judge
117
+ relevance and skip results about another product or business.
118
+ 4. **Propose; don't interview.** Lead with one recommended direction (angle, product, source or
119
+ format) and a one-line reason naming what you found, with up to two alternatives. When several
120
+ directions fit (two audiences, products or campaigns), pick the one the evidence favours, such
121
+ as an active campaign or approved past creatives, and name the other as an alternative instead
122
+ of asking. Ask only for a decision the brand read and the search cannot settle, or for spend
123
+ approval. Never ask the user for a fact the Brain already answered.
124
+ Keep directions already chosen (the user's words, a project's creative brief, handed-off
125
+ defaults, approved plans): search only to fill empty fields and to apply saved rules. When a
126
+ returned card or picker presents the choice, your recommendation is its one acknowledgement
127
+ line and the saved plan's defaults, not a separate list.
128
+ 5. **Carry an evidence brief** into the routed skill, writer or plan: findings with citations,
129
+ the state of each query, what it ruled out and the open gaps. Tell the customer the findings
130
+ in plain words; the citations stay in the brief.
131
+
132
+ On an approved resume, keep the saved brief and evidence; search again only for a new decision.`;
133
+ /** One prerequisite for template, custom and delegated video entry. */
134
+ function videoEntryPreparation(slug) {
135
+ return `## Prepare the video workflow and brand before creative work
136
+
137
+ **Installed entry files are bootstrap instructions.** For new work started from an installed
138
+ file or an old chat, fetch \`catalog_fetch { type: "skill", slug: "${slug}" }\` on the selected
139
+ connection and read its returned content and dependencies before continuing. Use the already
140
+ fetched body when this entry came from that connection in this run; do not recursively fetch
141
+ the same entry. A CLI freshness warning does not block using the current connected package.
142
+ Keep the fetched package and hashes in a new run folder; do not replace edited installed files.
143
+ If current instructions cannot be loaded, resolve the connection before format suggestions,
144
+ script writing or production. Do not continue from an old installed workflow or session notes.
145
+
146
+ ${VIDEO_GUIDE_COMPATIBILITY}
147
+
148
+ **For an existing project or batch, read \`video_project_read\` first.** Determine its actual
149
+ route and saved review state before fetching a recipe or doing local work. Resume an approved
150
+ run with its recorded packages, brand inputs, script and ingredients; do not silently replace
151
+ them with today's release or brand rules. Follow returned preparation requirements and use
152
+ the existing affected review/approval flow for an intentional change. Reads, free drafts,
153
+ imports and existing-job retrieval do not grant permission for new paid production.
154
+
155
+ **For every new plan, resolve the brand and call**
156
+ \`brand_read { brand_id, sections: ["summary", "kit", "products", "learnings"] }\` **before**
157
+ suggesting formats, mining video angles, writing any script, creating a production plan or
158
+ handing the work to another agent. Use \`brand_get_context\` with those same four sections only
159
+ when advertised. Reuse a complete read from this run on the same connection and brand.
160
+ Onboarding completion, a summary-only response and earlier session notes do not supply the
161
+ kit, selected product facts or saved rules. If the full read is unavailable, resolve it before
162
+ creative work. Use known kit facts while research is incomplete; do not restart research
163
+ solely because its status is pending. Never invent a product fact or accept a campaign to
164
+ bypass preparation. Then search the Brand Brain (next section) before suggesting formats,
165
+ choosing an angle or asking the user for a brand fact.
166
+
167
+ Carry that context through custom/template/idea routing and delegation. Before writing, make
168
+ the workflow's brand-rules file from those sources: pronunciations, required spoken copy,
169
+ prohibited claims, real logo, font, palette and the selected product's supported facts.
170
+ Entries prefixed \`Video preference:\` and visual/pacing instructions are creative direction,
171
+ not spoken lines. Use the kit's audience, offer and voice as defaults without an interview.
172
+ The rules file mirrors context; it is not proof of a read or permission to spend. Keep the
173
+ route's existing script, ingredient and budget approvals, including its allowed previews.
174
+
175
+ **Use the server's brand bundle when the selected API returns it.** Project create/read may
176
+ return top-level
177
+ \`brand_context\`: \`{ version: 1, brand_id, digest, loaded_at, sections, brand, kit, products,
178
+ learnings }\`. Its sections cover summary, kit, products and learnings.
179
+
180
+ | Returned brand/project response on the selected connection | Required action |
181
+ | --- | --- |
182
+ | A complete authoritative version-1 \`brand_context\` is returned | Binding is required: use its contents for the plan/rules and include \`script_drafts.brand_context_digest = brand_context.digest\` with each new/changed script save. |
183
+ | An older API returns the actual four brand sections but no \`brand_context\` in either the brand read or project response | Prepare from those full sections, write the brand rules and follow the selected connection's existing approval flow and advertised fields. Do not fabricate a receipt or send nonexistent bundle/digest fields. |
184
+
185
+ An incomplete/malformed bundle or missing brand section is a preparation failure, never the
186
+ older-API exception. Once this connection returns an authoritative bundle, use its binding
187
+ contract. \`script_drafts.video_brand_context\` is server-owned: do not author, replace or forge
188
+ it, and do not invent a read receipt or hash local brand facts into one. Keep the full bundle
189
+ out of client review payloads. If managed generation returns HTTP 409
190
+ \`video_brand_context_required\`, reload the project and bind the plan to its returned bundle
191
+ through the normal review flow before retrying. That refusal never permits the older-API
192
+ fallback, skipping sections or retrying paid calls blindly. Unchanged approved legacy resumes
193
+ retain their package, context and approvals; an upgrade alone needs no extra approval.
194
+
195
+ **To apply a deliberate brand correction to a new or revised plan**, make a fresh, unfiltered
196
+ \`brand_read\` of those four sections. When it returns the authoritative bundle, use its
197
+ returned \`brand_context.digest\` in the complete revised \`script_drafts\` through the existing
198
+ \`video_project_upsert\` \`patch.script\` save; the server verifies the current brand and replaces
199
+ its snapshot with the saved plan. On an older API with no bundle contract, apply the actual
200
+ four-section correction and save the revised draft through its existing flow without inventing
201
+ digest fields. If a connection that already returned a bundle cannot supply the fresh bundle,
202
+ resolve that failure before rebinding. Review and approve the affected script, ingredients and
203
+ budget through the normal flow. Never refresh an ongoing approved run automatically; its
204
+ recorded package and brand inputs stay pinned until an intentional change.
205
+
206
+ ${BRAIN_FIRST_ENTRY}`;
207
+ }
208
+ /**
209
+ * Who makes an agent-made video: one rule, in the same words as the server's
210
+ * (gooseworks-app lib/who-makes-the-video.ts). The agent decides; the server
211
+ * never guesses it from the host.
212
+ */
213
+ const WHO_MAKES_THE_VIDEO = `**Who makes the video (one rule).** If you can run shell commands on the customer's own
214
+ computer, you make it there with \`goose-video-local\`: check the toolchain first and, if it
215
+ can't be fixed, hand it off. If you can't, hand it to the GooseWorks coworker with
216
+ \`goose_run_task { brand_id, project_id, message: the customer's words }\`; the one approval,
217
+ Stop and fixes stay in this chat. Inside the GooseWorks coworker's own sandbox you are the
218
+ coworker: make it yourself and never hand off. A hosted code sandbox is not the customer's
219
+ computer. Decide this yourself, never from the host's name; the server needs no flag for it.`;
68
220
  const CUSTOMER_TALK = `## How to talk to the customer (applies to every message you send them)
69
221
 
70
222
  The customer is a marketer or founder, not an engineer. Everything in this skill about tools,
@@ -96,6 +248,31 @@ both hosts and the ending. You can also review your recipe ingredients in the ap
96
248
  here and I'll make the full video (about N credits)."
97
249
 
98
250
  ${SKILL_FRESHNESS}`;
251
+ // GOOSE-3937: a cloud agent's sandbox (ChatGPT agent, Meta AI, Grok) has a
252
+ // shell but no GooseWorks connector, because those apps load tools from their
253
+ // own settings. `gooseworks tool` calls the same MCP tools with the CLI login.
254
+ const CLI_TOOL_BRIDGE = `## No GooseWorks tools in this session? Call them through the CLI
255
+
256
+ The GooseWorks tools named here (for example \`account_whoami\`, \`brand_onboarding\`,
257
+ \`brand_read\`) normally come from the GooseWorks connector. If they are not registered in this
258
+ session but you have a shell with the \`gooseworks\` CLI signed in (a cloud sandbox such as
259
+ ChatGPT agent, Meta AI or Grok), call the same tool from the shell:
260
+
261
+ \`\`\`bash
262
+ npx gooseworks tool <name> '<arguments as a JSON object>'
263
+ \`\`\`
264
+
265
+ - Run \`npx gooseworks tool --list\` once first. It prints the server's rules for using the tools
266
+ and every tool name; \`npx gooseworks tool <name> --schema\` shows one tool's arguments.
267
+ - The result prints as JSON. When a tool answers with a one-line summary, the fields you act on
268
+ (such as \`next_step\` and ids) follow under "Data:". It exits 1 when the tool reports an error.
269
+ - Nothing renders as a widget here, so tell the user the result in plain words.
270
+ - The same rules apply as over the connector: state the credit total and get the user's yes
271
+ before paid work.
272
+ - If it says you are not logged in, run \`npx gooseworks login --device --no-wait\`, show the
273
+ user the link and code it prints, and run \`npx gooseworks login --device\` once they approve.
274
+
275
+ When the tools are registered in this session, call them directly instead.`;
99
276
  const ENVIRONMENT_IDENTITY = `## Keep the selected connection for the whole run
100
277
 
101
278
  Call \`account_whoami\` on the connection that owns the brand/project before the first write.
@@ -135,8 +312,9 @@ review set; do not add a separate approval round.`;
135
312
  const DURABLE_BRAND_CAPTURE = `## Save durable brand answers, then verify them
136
313
 
137
314
  Read the selected brand with \`brand_read { brand_id, sections: ["summary", "kit", "products", "learnings", "onboarding"] }\`
138
- (fallback: \`brand_get_context\` with the same sections). Keep founder answers, user corrections,
139
- research and your own hypotheses distinct. Reuse matching saved answers; ask only about gaps.
315
+ (older clients: \`brand_get_context\` with the same sections, only when that tool is advertised).
316
+ Keep founder answers, user corrections, research and your own hypotheses distinct. Reuse
317
+ matching saved answers; ask only about gaps.
140
318
 
141
319
  When the user asks to remember a rule, answers a brand interview, or explicitly corrects a
142
320
  standing fact, save that answer in the same turn. The capture request authorizes those answers;
@@ -271,7 +449,7 @@ description: >
271
449
  Capture founder answers, brand rules, audience depth, and video taste in the existing brand.
272
450
  Use it as the single GooseWorks entry point for brand growth, B2B, sales, research, and GTM work.
273
451
  category: general
274
- version: 1.0.0
452
+ version: 1.1.2
275
453
  author: GooseWorks
276
454
  tags: [gooseworks, data, scraping, search, reddit, twitter, linkedin, email, people, research, gtm, leads, prospecting]
277
455
  ---
@@ -284,9 +462,13 @@ This skill is also the **parent router** for the GooseWorks family. Data/GTM wor
284
462
 
285
463
  ${CUSTOMER_TALK}
286
464
 
465
+ ${CLI_TOOL_BRIDGE}
466
+
287
467
  ## Route to the right skill FIRST
288
468
 
289
- First apply the **Common company onboarding** gate below. Preserve the user's original request while onboarding, then continue with it as soon as onboarding is complete. Then load the brand context (**"Load the brand context FIRST"**, immediately below). After that, check whether the request belongs to a specialized domain. If so, **switch to that skill** instead of the data flow below:
469
+ First apply the **Common company onboarding** gate below. Preserve the user's original request while onboarding, then continue with it as soon as onboarding is complete. For video work, load the current matching workflow from the selected connection first: \`goose-video\` for a new request, \`make-custom-video\` for an explicit original/reference brief, or \`goose-video-local\` for an existing template project/batch. Read an existing project first to determine its actual route and retain its approved packages. Fetch with the advertised \`catalog_fetch { type: "skill", slug }\`; an installed copy or old chat is only a bootstrap. Then load the brand context (**"Load the brand context FIRST"**, immediately below), search the Brand Brain for the task (**"Search the Brand Brain, then propose"**), and follow the matching workflow with both. For other specialized work, **switch to that skill** after loading the brand instead of the data flow below:
470
+
471
+ ${VIDEO_GUIDE_COMPATIBILITY}
290
472
 
291
473
  For "interview me about the brand", "save our brand rules", "refine our audience", or "remember
292
474
  our video taste", stay here and follow **Guided brand capture** below. This extends the current
@@ -303,27 +485,38 @@ Examples — all of these route to \`goose-ads\`, not the data flow: "remix this
303
485
 
304
486
  **Call \`brand_read { brand_id, sections: ["summary", "kit", "products", "learnings", "onboarding"] }\` before the first substantive step of ANY task**, and before you route to a specialist skill. Older clients can use \`brand_get_context\` with the same sections only when that tool is advertised. It is a read-only call that returns the brand's canonical facts and saved rules:
305
487
 
488
+ For videos, read the current workflow first as described above, then load all four creative
489
+ sections (summary, kit, products and learnings) before suggesting formats, choosing angles or
490
+ writing a script. Onboarding facts alone are insufficient. Carry saved rules and kit assets
491
+ into the specialist's brand preparation. \`Video preference:\` rules describe the look, voice
492
+ and pacing; they are not lines to read aloud. On an approved resume, preserve the saved brand
493
+ inputs and packages; an intentional change uses the existing affected review/approval gates.
494
+
306
495
  | It returns | Use it for |
307
496
  | --- | --- |
308
497
  | **voice** — tone, style, banned phrasing | Any copy, script, caption, hook, or headline. Don't ask "what tone?" |
309
498
  | **products** — names, descriptions, pricing, links, imagery | Picking the product to feature. Don't ask "which product?" — offer the list. |
310
499
  | **audience** — segments, demographics, jobs-to-be-done | Targeting, angles, creator fit. Don't ask "who is this for?" |
311
- | **positioning** — category, value props, proof points, tagline | Angles, offers, competitive framing. Don't ask "what makes you different?" |
500
+ | **positioning** — category, value props, proof points, tagline | Angles, offers, competitive framing. Don't ask "what makes you different?" Kit proof points are proof an ad may state as written; other positioning text is context. |
312
501
  | **research status** — whether the brand's research pass has completed | Whether the facts are trustworthy yet, or still being filled in. |
313
502
 
314
503
  Then:
315
504
 
316
- 1. **Pass what it returned INTO the routed skill.** When you hand off to \`goose-ads\`, \`goose-video\`, \`goose-product-photos\`, \`goose-graphics\`, or a fetched Brand Growth recipe, carry the voice / products / audience / positioning with you. Do **not** make the routed skill re-derive them, and do **not** re-run brand research when the context is already there.
317
- 2. **Never re-ask the user for something the brand context already answers.** If a routed skill's own prose asks a question the context answers, the context wins — answer it yourself and move on. Ask only for what is genuinely missing or ambiguous.
505
+ 1. **Pass what it returned INTO the routed skill.** When you hand off to \`goose-ads\`, \`goose-video\`, \`goose-product-photos\`, \`goose-graphics\`, or a fetched Brand Growth recipe, carry the voice / products / audience / positioning with you, plus the evidence brief from the Brain search below. Do **not** make the routed skill re-derive them, and do **not** re-run brand research when the context is already there.
506
+ 2. **Never re-ask the user for something the brand context already answers.** If a routed skill's own prose asks a question the context or the Brain search answers, they win — answer it yourself and move on. Ask only for what is genuinely missing after both, or a decision that is the user's to make.
318
507
  3. **If research status is not complete**, say so in one line, use what you have, and continue. Only run brand research when the context comes back empty or the user asks for it.
319
508
  4. **If \`brand_read\` is unavailable**, refresh the GooseWorks connection or tool list. An older connection may expose \`brand_get_context\` / \`get_brand_kit\`; use those only when actually advertised. Never require a legacy tool name or guess brand facts.
320
509
  5. **A read grants no write permission.** Save explicit durable answers/corrections with the capture policy below. Propose agent-derived changes for review; never overwrite confirmed knowledge with research or a guess.
321
510
 
322
- Never invent a brand fact. If it isn't in the brand context and the user hasn't said it, ask.
511
+ Never invent a brand fact. If it isn't in the brand context and the user hasn't said it, search
512
+ the Brand Brain next. Ask the user only when that search cannot answer it and the answer is
513
+ theirs to give.
514
+
515
+ ${BRAIN_FIRST_ENTRY}
323
516
 
324
517
  ## Setup
325
518
 
326
- All commands below auto-load credentials from \`~/.gooseworks/credentials.json\`. If a command exits with "Not logged in", tell the user to run: \`npx gooseworks login\`. To log out: \`npx gooseworks logout\`.
519
+ All commands below auto-load credentials from \`~/.gooseworks/credentials.json\`. If a command exits with "Not logged in", tell the user to run: \`npx gooseworks login\`. In a cloud sandbox or over SSH, sign in with a code instead: run \`npx gooseworks login --device --no-wait\` yourself, show the user the link and code it prints, and run \`npx gooseworks login --device\` once they have approved. To log out: \`npx gooseworks logout\`.
327
520
 
328
521
  ### Choose the available runtime — MCP first, then CLI
329
522
 
@@ -331,20 +524,33 @@ Skills may describe a managed provider request as an environment-neutral operati
331
524
  \`provider\`, \`method\`, \`path\`, and optional \`query\` or \`body\`. Execute the operation through
332
525
  the first available runtime:
333
526
 
334
- 1. If the matching GooseWorks MCP tool is registered, use it. For ScrapeCreators, pass the
335
- operation directly to \`call_data_provider\`. This is the preferred path in ChatGPT, Cowork,
336
- and other terminal-free clients. Do not shell out and do not ask for a separate provider key.
527
+ 1. If the matching GooseWorks MCP tool is registered, use it. For ScrapeCreators, pass a GET
528
+ operation directly to \`data_call_provider\` (\`provider\`, \`path\`, \`query\`) and a POST
529
+ operation to \`data_post_provider\` (\`provider\`, \`path\`, \`body\`, optional \`query\`).
530
+ This is the preferred path in ChatGPT, Cowork, and other terminal-free clients.
531
+ Do not shell out and do not ask for a separate provider key.
337
532
  2. Otherwise, if a local terminal and the \`gooseworks\` CLI are available, translate the same
338
533
  operation into \`gooseworks call <provider> <path>\` with its method, query, and body options.
339
534
  3. Otherwise, follow the provider dependency's direct-key path only when the user has supplied
340
535
  their own key. If no runtime is available, explain what connection is missing; never pretend
341
536
  the provider call ran.
342
537
 
538
+ Managed provider calls are paid on the MCP and CLI runtimes alike (a ScrapeCreators call costs 1
539
+ credit today, and each result reports what it charged). Before a skill's first paid call, tell the
540
+ user roughly how many calls it will make and the credit total, and get their yes.
541
+
343
542
  The same selection applies to catalog and account operations. When the CLI is unavailable but the
344
- \`mcp__gooseworks__*\` tools are connected, use these equivalents:
345
- - \`gooseworks search <q>\` → the **\`search_skills\`** MCP tool.
346
- - \`gooseworks fetch <slug>\` → the **\`fetch_skill\`** MCP tool (same content/scripts/files/deps).
347
- - \`gooseworks credits\` → the **\`get_ad_credits\`** MCP tool.
543
+ GooseWorks MCP tools are connected, use these equivalents. Match on the tool name: a coding agent
544
+ may show a server prefix (for example \`mcp__gooseworks__catalog_search\`), a chat app may not.
545
+ - \`gooseworks search <q>\` → **\`catalog_search { type: "skill", query: "<q>" }\`**.
546
+ - \`gooseworks fetch <slug>\` → **\`catalog_fetch { type: "skill", slug: "<slug>" }\`** (same content/scripts/files/deps).
547
+ - \`gooseworks credits\` → **\`account_whoami\`** (the balance is \`credits.available_credits\`).
548
+
549
+ If one of these tools is missing, the GooseWorks connection or its tool list is stale: ask the
550
+ user to reconnect or refresh GooseWorks. Installing or updating the \`gooseworks\` CLI never fixes
551
+ a missing connector tool, so never send a chat-app user to a terminal for it. An agent that has
552
+ its own shell and a signed-in CLI can call the tool with \`npx gooseworks tool\` instead (see
553
+ "No GooseWorks tools in this session?" above).
348
554
 
349
555
  Discovery, skill fetching, and ScrapeCreators-backed Brand Growth workflows work fully CLI-free
350
556
  this way. Task skills own the endpoint and analysis workflow; this runtime rule owns how the same
@@ -394,8 +600,8 @@ already volunteered in this chat, and **save known information first** using the
394
600
  mapping below. Do not run a long questionnaire as a prerequisite for making an ad.
395
601
 
396
602
  For facts needed by the task but absent from the read, call \`knowledge_search\` first if it is
397
- registered. Use returned citations and states honestly: an empty, building or failed index is
398
- not proof that the brand has no answer. Do not re-scrape or ask the founder for a fact already
603
+ registered, as in **"Search the Brand Brain, then propose"**. Use returned citations and states
604
+ honestly: an empty, building or failed index is not proof that the brand has no answer. Do not re-scrape or ask the founder for a fact already
399
605
  answered by trustworthy saved knowledge.
400
606
 
401
607
  Ask one short group of missing human-only facts at a time, in plain language, with the relevant
@@ -561,28 +767,50 @@ The \`gooseworks\` CLI sends authenticated requests (Bearer \`GOOSEWORKS_API_KEY
561
767
 
562
768
  0. **Read the canonical brand context before substantive work**, pass what it returns into whatever skill you route to, and never re-ask the user for a fact it already answers (see "Load the brand context FIRST").
563
769
  1. **Consider a GooseWorks skill when it fits the task** — scraping, research, lead gen, enrichment, especially at scale, behind auth, or from a specific source. For a quick lookup your built-in tools are fine; use your judgement and pick the best tool for the user.
564
- 2. **Before paid operations**, tell the user the estimated credit cost
565
- 3. **If a \`gooseworks\` command exits with "Not logged in"**: tell the user to run \`npx gooseworks login\`
770
+ 2. **Before paid operations**, tell the user the estimated credit cost and get their yes
771
+ 3. **If a \`gooseworks\` command exits with "Not logged in"**: tell the user to run \`npx gooseworks login\` (in a cloud sandbox or over SSH, use the code sign-in described in Setup)
566
772
  4. **Parse JSON responses** and present data in a readable format to the user
567
773
  5. **When running scripts**: save to \`/tmp/gooseworks-scripts/\`, install pip deps, then execute. NEVER pollute the user's project directory
568
774
  6. **Output files default to \`~/Gooseworks/\`** — always confirm with the user before saving
569
775
  7. **Prefer \`gooseworks call\` over raw curl** — if it returns an error, first fix the parameters (check types, required fields, format) and retry. Only fall back to raw curl if you have strong reason to believe it is a CLI bug, not a parameter issue.
570
776
  `;
571
777
  }
778
+ /**
779
+ * How an entry skill that runs on the GooseWorks connector says what to do when
780
+ * a tool is missing (QA-26 / VWR14). A chat agent that could not find a tool
781
+ * used to tell the customer to run `gooseworks install` in a terminal they do
782
+ * not have; a missing connector tool is fixed by reconnecting, never by a CLI.
783
+ */
784
+ function connectorPrerequisite(exampleTool, extra = '') {
785
+ return `## Prerequisite — the GooseWorks connector tools
786
+
787
+ Everything below runs through the GooseWorks connector's tools, by the names this skill uses.
788
+ Match on the tool name: a coding agent may show a server prefix (for example
789
+ \`mcp__gooseworks__${exampleTool}\`), a chat app may not.${extra}
790
+
791
+ If a tool named here is missing, the GooseWorks connection or its tool list is stale: ask the
792
+ user to reconnect or refresh GooseWorks in their app's connector settings. Installing or
793
+ updating the \`gooseworks\` CLI never fixes a missing connector tool, so never send a chat-app
794
+ user to a terminal for it. Only a terminal coding agent (Claude Code, Codex, Cursor) that has no
795
+ GooseWorks tools at all connects them, in that terminal, with \`gooseworks install --mcp\` plus
796
+ \`--claude\`, \`--codex\` or \`--cursor\`, then a restart.
797
+
798
+ Older notes or skill copies may name tools the connector no longer lists. Use the tool this skill
799
+ names instead; \`catalog_fetch { type: "skill", slug: "gooseworks-guide" }\` maps every old name.`;
800
+ }
572
801
  /**
573
802
  * Returns the goose-ads entry SKILL.md content (the `goose-ads` entry skill,
574
803
  * formerly `ads-remix`).
575
804
  *
576
805
  * This is the ads domain skill and a THIN WRAPPER over the backend's single ad
577
- * generation workflow (adRemixBatchesService, exposed via the new
578
- * mcp__gooseworks__*_remix_batch / regenerate_creative tools — the SAME workflow
579
- * the ads frontend uses). The skill no longer generates images, manages renders,
580
- * or uploads files itself; the backend reserves credits, runs the cloud pipeline,
581
- * and bills. The skill ALSO routes ad analytics/intelligence to recipe skills
582
- * fetched on demand from goose-skills. It is SEPARATE from the `gooseworks` GTM
583
- * skill — different domain, different tools — and Claude loads it (or the
584
- * `gooseworks` parent router hands off to it) when the user wants to make/edit an
585
- * ad, research a brand for ads, or analyze ad performance.
806
+ * generation workflow (adRemixBatchesService, exposed on the connector as
807
+ * `ads_generate` / `ads_creative_edit` — the SAME workflow the ads frontend
808
+ * uses). The skill never generates images, manages renders, or uploads files
809
+ * itself; the backend reserves credits, runs the cloud pipeline, and bills. The
810
+ * skill ALSO routes ad analytics/intelligence to recipe skills fetched on demand
811
+ * from goose-skills. Chat apps reach it through `catalog_fetch`, so every tool it
812
+ * names must be one the connector lists (QA-26 / VWR14; guarded by
813
+ * tests/skills/master-skill.test.ts against tests/fixtures/connector-tool-names.json).
586
814
  */
587
815
  function getGooseAdsSkillContent() {
588
816
  return `---
@@ -599,7 +827,7 @@ description: >
599
827
  app uses) — credits are reserved and billed server-side. Analytics recipes are fetched from
600
828
  goose-skills on demand.
601
829
  category: ads
602
- version: 2.4.0
830
+ version: 2.6.1
603
831
  author: GooseWorks
604
832
  tags: [gooseworks, ads, remix, static-ad, brand, creative, image, analytics, meta-ads, performance]
605
833
  ---
@@ -617,41 +845,70 @@ The GooseWorks ads skill. Two jobs:
617
845
  2. **Analyze ad performance** — fetch ad-analytics recipes from goose-skills on demand
618
846
  (these are unrelated to generation; see "Analyze / intelligence" below).
619
847
 
848
+ It works the same in a chat app (ChatGPT, claude.ai, Cowork) and in a terminal coding agent:
849
+ everything except the few steps labelled terminal-only goes through the connector's tools.
850
+
620
851
  ${CUSTOMER_TALK}
621
852
 
622
- ## Prerequisite — the GooseWorks MCP server is REQUIRED
853
+ ${CLI_TOOL_BRIDGE}
623
854
 
624
- Everything goes through the \`mcp__gooseworks__*\` tools. If they are not available, **stop and
625
- tell the user to run \`gooseworks install --claude --mcp\`** (and restart Claude Code). There is
626
- no HTTP/file fallback — the REST ad endpoints are session-cookie-only and reject your token.
855
+ ${connectorPrerequisite('ads_generate', ' There is no HTTP or file fallback: the REST ad\nendpoints are session-cookie-only and reject your token.')}
627
856
 
628
857
  ## Start from the brand context — don't re-ask what it already answers
629
858
 
630
- If the \`gooseworks\` router handed you brand context, USE IT. If you were invoked directly, call
631
- \`brand_read { brand_id, sections: ["summary","kit","products","learnings"] }\` first. It already
632
- answers most of what the flows below would otherwise ask the user:
859
+ If the \`gooseworks\` router handed you brand context and an evidence brief, USE THEM. If you were
860
+ invoked directly, call \`brand_read { brand_id, sections: ["summary","kit","products","learnings"] }\`
861
+ first, then search the Brand Brain (next section). Together they answer most of what the flows
862
+ below would otherwise ask the user:
633
863
 
634
- - **Which product to feature** → \`products[]\`. Offer the real catalog entries; never guess a
864
+ - **Which product to feature** → \`products.items\`. Recommend one real catalog entry; never guess a
635
865
  product name and never ask the user to list their products.
636
866
  - **The vibe / tone of the copy** → the brand's **voice**. Use it; don't ask "what tone?".
637
867
  - **Who the ad is for** → the brand's **audience**. Don't ask "who's the target?".
638
- - **The angle, offer framing, and what to claim** → **positioning**, value props, proof points.
868
+ - **The angle and offer framing** → **positioning** and value props, sharpened by the Brain:
869
+ lead with an angle past approved creatives proved, and drop anything a saved rule forbids.
870
+ - **What to claim** → only claim-grade proof: a search result with \`approved_ad_claim: true\`, the
871
+ kit's approved claims, or its proof points as written. Other Kit text, documents and
872
+ performance numbers are context.
639
873
  - **Logo, colors, fonts** → owned by the backend research pass. **Never re-derive them.**
640
874
  - **Whether the facts are trustworthy yet** → **research status**. If it isn't complete, say so in
641
875
  one line and continue; the batch queues and runs when research finishes.
642
876
 
643
- Ask only for what the context genuinely doesn't answer: the specific campaign intent (season,
644
- promo, which of several angles), the source ad, and anything the user must consent to.
877
+ Don't ask for the angle, product or tone: recommend them with a one-line reason. Ask only for a
878
+ decision the brand read and the Brain search cannot settle (for example an offer or season the
879
+ user hasn't mentioned) and for anything the user must consent to (rights, spend).
880
+
881
+ ${BRAIN_FIRST_ENTRY}
882
+
883
+ ## Credits — state the total, then get a yes
884
+
885
+ - One token authenticates the GooseWorks tools and resolves your org; never print it. The
886
+ generation tools need no \`target\`.
887
+ - **Nothing paid runs without the user's explicit yes in this chat, given after you state the
888
+ credit total.** The paid calls are \`ads_generate\` (without \`dry_run\`), every
889
+ \`ads_creative_edit\` action, \`ads_approval_decide\` with \`decision: "approve"\`, and every
890
+ \`data_call_provider\` / \`data_post_provider\` call (the data calls an analysis recipe or brand
891
+ research makes; \`gooseworks call\` in a terminal). Reads, dry runs, \`ads_creative_update\` and
892
+ \`request_campaign_generation\` (it only composes plans) are free.
893
+ - The balance is \`credits.available_credits\` from \`account_whoami\`; a dry-run \`estimate\` also
894
+ carries \`available_credits\`.
895
+ - The backend reserves the quoted credits when a paid call starts and bills only the images
896
+ that complete. A rejection with \`insufficient_credits\` means the wallet is short: tell the
897
+ user the total and their balance in plain words, offer fewer images or a top-up, and stop.
898
+ Never retry blindly.
645
899
 
646
- ## Identity & credits
900
+ How to get the total for each paid call:
647
901
 
648
- - One agent-scoped token authenticates the \`gooseworks\` MCP tools. Never print it. The tools
649
- resolve your org automatically — you do NOT resolve an "Ads agent" or pass \`target\` for the
650
- generation tools.
651
- - **Credits are handled entirely by the backend.** \`submit_remix_batch\` reserves the estimated
652
- cost up front (it errors with \`insufficient_credits\` if the wallet is short — relay the
653
- message and stop) and bills only the images that actually complete. Call
654
- \`estimate_remix_batch\` first to tell the user the cost; \`gooseworks credits\` shows balance.
902
+ | Paid call | Credit total to state |
903
+ | --- | --- |
904
+ | \`ads_generate\` with \`source.template_ids\` | The same call with \`dry_run: true\` returns \`estimate\`: \`total_credits\` for \`images\` images (plus \`credits_per_image\`, \`rates\` and \`unknown_template_ids\`). |
905
+ | \`ads_generate\` with \`source.community_ad_ids\` or \`source.creative_ids\` | No dry run (it returns \`not_available\`; these are priced when submitted). Pass \`quality\` explicitly (the user's choice, else \`high\`, the app default) and quote the images (each source's \`variants\` × its \`ratios\`) × \`estimate.rates.<quality>\` as the most it will cost (an engine without quality tiers costs the lower \`rates.low\`). \`rates\` comes with any template dry run; with no template at hand, dry-run one Surprise-me pick. |
906
+ | \`ads_creative_edit\` \`animate\` | The same call with \`dry_run: true\` returns its \`estimate\`. |
907
+ | \`ads_creative_edit\` \`regenerate\` / \`precision_edit\` | No dry run. Pass \`quality\` in the action's payload (without it \`precision_edit\` copies the source render's tier) and quote the images × \`estimate.rates.<quality>\` as the most it will cost: \`regenerate\` with \`mode: "variation"\` makes one per ratio (omitted \`ratios\` make three); \`edit\`, \`exact\` and \`precision_edit\` make one. |
908
+ | \`ads_creative_edit\` \`resize\` | No dry run and no quality setting: it renders at the server's default tier (\`high\` today). Quote one image per placement × the larger of \`estimate.rates.high\` and \`credits_per_image\` from a template dry run without \`quality\` (that dry run uses the server's default tier) as the most it will cost. \`platforms\` expands to every placement: meta 4, google 4, tiktok 2, linkedin 3, reddit 3, x 2; pass \`targets\` for fewer. |
909
+ | \`ads_creative_edit\` \`layerize\` | No dry run. It holds about 80 credits while it runs and charges the actual cost of the split (usually less). Say so and get the yes before sending \`layerize: { confirmed: true }\`. |
910
+ | \`ads_approval_decide\` approve | The \`credits\` that \`request_campaign_generation\` and \`ads_creative_read { brand_id, view: "approvals", batch_id }\` return for those plans. |
911
+ | \`data_call_provider\` / \`data_post_provider\` | The calls a recipe will make × their price: a ScrapeCreators call costs 1 credit today, and each result reports what it charged. A fal or ElevenLabs POST quotes free with the same call plus \`query: { quote_only: true }\`. State the rough total for the whole recipe once, before its first paid call. |
655
912
 
656
913
  ## Live MCP contract — inspect it before asking
657
914
 
@@ -664,77 +921,82 @@ Before each tool call:
664
921
  2. Fill required inputs already known from the Brand Kit, selected source, or conversation.
665
922
  3. Ask the user only for required inputs that cannot be inferred and for choices that materially
666
923
  change the result. Do not turn every optional field into a questionnaire.
667
- 4. Omit unspecified optional settings so the backend applies its current app defaults.
924
+ 4. Omit unspecified optional settings so the backend applies its current app defaults. Two
925
+ exceptions: always pass \`ratios\` on every source (omitted ratios make three images per
926
+ variant), and keep the same \`quality\` setting in the quote and the paid call (both omitted
927
+ for a template dry run, or the explicit \`quality\` the credits table asks for).
668
928
  5. If the live schema conflicts with this workflow, follow the live schema and report the drift
669
- with \`log_cli_event\`.
670
-
671
- ## The generation tools (the new, single-workflow surface)
672
-
673
- - \`submit_remix_batch\` — **the one call that makes ads.** Inspect its live schema and supply
674
- the required brand/source inputs plus any choices the user explicitly made.
675
- Returns the batch with a \`links\` block (\`brand_url\` + per-creative \`app_url\`). If the brand's
676
- research isn't finished yet the batch comes back \`status: "queued"\` — it auto-runs the moment
677
- research completes; tell the user it'll appear shortly, don't error.
678
- - \`estimate_remix_batch\` — cost preview. Reserves nothing. Use it to quote the cost first and
679
- check whether every selected source resolved before submitting.
680
- - \`get_remix_batch\` — poll status. Returns each creative with its renders and
681
- \`completed\`/\`failed\`/\`pending\` counts, plus \`links\`. A creative is done when its \`pending\` is 0
682
- — NOT when \`current_render_url\` is set (during a regenerate that field still points at the prior
683
- image). Each render carries \`age_seconds\` (since queued) and \`elapsed_seconds\` (time generating):
684
- use them to tell a slow-but-healthy render from a stuck one. A render only failed when its
685
- \`status\` is \`"failed"\` — never assume a stall and re-submit, that double-bills.
686
- - \`list_brand_creatives\` — the brand's gallery feed (newest
687
- first) + \`brand_url\`. Alternative poll target; also use to show everything made for a brand.
688
- - \`surprise_me_templates\` — the **"Surprise me" recommender**. Picks
689
- remixable Community creations (SAME logic as the web /create "Surprise me" button), shuffled
690
- so picks stay fresh. It does not use the retired curated third-party catalog.
691
- Returns the picked templates (id, slug, title, image, ratio) AND a ready-to-open \`create_url\`
692
- (the /create page with \`cli=true\` and the picks pre-selected). This is how you recommend
693
- templates — do NOT hand-pick from the raw catalog yourself (see "Picking templates" below).
694
- - \`regenerate_creative\` — edit or re-roll one existing creative through the same pipeline.
695
- Inspect the live schema to select the supported mode and required source inputs. Returns a
696
- single-item batch; poll it with \`get_remix_batch\`.
697
- - \`set_creative_feedback\` — record the user's reaction to a generated image. Use it whenever
698
- the user reacts; inspect the schema for the current rating and reason choices.
699
-
700
- ### Plan mode — review the plan BEFORE generating (optional)
701
-
702
- For users who want to approve each ad's plan before spending credits (the app's "Plan it" flow):
703
-
704
- - Use the approval option exposed by \`submit_remix_batch\` — it composes each creative's plan and PAUSES.
705
- **No credits are reserved and no image renders** until you approve.
706
- - \`list_ad_approvals\` — poll this. While a creative is
707
- \`composing\`, wait; once \`awaiting_approval\`, show its \`plan\` (composed prompt + refs + quality)
708
- to the user.
709
- - \`revise_ad_plan\` — recompose from a chat steer, still
710
- free. Poll \`list_ad_approvals\` until it's \`awaiting_approval\` again.
711
- - \`approve_ad_plan\` — approve one creative or the whole batch using the live schema.
712
- **This is the step that reserves credits and renders.** Then poll
713
- \`get_remix_batch\` and hand back links as usual.
714
-
715
- Only offer plan mode when the user asks to review/approve first — the default path generates
716
- immediately.
717
-
718
- ## Reading the brand & picking inputs (still MCP, read-only)
719
-
720
- - \`get_brand_kit\` — read the canonical brand context and available products/assets.
721
- - \`list_ad_brands\` / \`get_ad_brand\` — find and fetch the active brand.
722
- - \`list_user_ad_templates\` — list the org's own uploads and
723
- imported ads. Prefer \`relationship: "self"\` when the user wants to reuse their own ads;
724
- \`relationship: "competitor"\` is research/inspiration, never proof that the user owns the ad.
725
- - \`search_ad_templates\` — search remixable Community generations. The
726
- retired curated third-party catalog is not returned.
727
- - \`get_static_ad_template\` — resolve a source already owned by
728
- the org, including an own upload or a snapshotted Community creative. It does not resolve the
729
- retired curated third-party catalog.
730
- - \`remix_community_ad\` — turn a selected Community creative into a private remix source before
731
- submitting it. A Community ad id is an \`ad_project\` id, not a
732
- template id. Call this FIRST to snapshot it into a private template, then use the returned
733
- template \`id\` in \`items\`.
734
- - \`create_user_ad_template\` — upload a source image as a private template. Answer any
735
- ownership/rights input only from the user's explicit confirmation. Never claim rights for a
736
- competitor ad or an image found online.
737
- - \`get_ad_project\` / \`append_project_message\` — inspect a creative / leave a note on its thread.
929
+ (see "Report problems" in the rules).
930
+
931
+ ## The ad tools
932
+
933
+ - \`ads_generate\` — **the one call that makes ads.** Needs \`brand_id\` and exactly one \`source\`:
934
+ \`template_ids: [{ template_id, variants?, ratios }]\` (the brand's own templates, Surprise-me
935
+ picks, Community rows whose \`item_type\` is \`template\`), \`community_ad_ids: [{ community_id,
936
+ variants?, ratios }]\` (Community rows whose \`item_type\` is \`creative\`; the backend snapshots
937
+ them), or \`creative_ids: [{ project_id, render_id?, variants?, ratios }]\` (remix the user's own
938
+ finished ads). Optional: \`product_name\` (a real product), \`prompt\` (a short steering note),
939
+ \`reference_image_urls\`, \`quality\`. \`dry_run: true\` quotes template sources and reserves
940
+ nothing. A real call GENERATES at once and returns \`{ job_id }\` (\`kind: "ads_batch"\`) and the
941
+ \`batch\`, which already carries its \`links\` (below). If the
942
+ brand's research is still running the batch is \`queued\` and starts on its own when research
943
+ finishes: tell the user it'll appear shortly, don't error.
944
+ - \`job_get { job_id, kind: "ads_batch" }\` — poll a batch. \`status\` is \`queued\`, \`running\`,
945
+ \`complete\`, \`partial_failure\` or \`failed\`; \`progress\` counts \`completed\` / \`failed\` /
946
+ \`pending\` images. \`result.creatives[]\` carry each creative's \`renders[]\` (\`id\`, \`status\`,
947
+ \`ratio\`, \`output_url\`, \`age_seconds\` since queued, \`elapsed_seconds\` generating). A creative
948
+ is done when its \`pending\` is 0, NOT when \`current_render_url\` is set (during a regenerate it
949
+ still points at the prior image). A render only failed when its \`status\` is \`"failed"\`: a slow
950
+ render is healthy, and re-submitting it double-bills. \`result.links\` holds the app links you end
951
+ the run with: \`brand_url\` (the brand's page, with all its ads) and \`creative_links: [{
952
+ project_id, app_url }]\` (each creative's page). \`brand_url\` is \`null\` and the list empty only
953
+ if the link lookup failed; the ads are still made. A server older than this skill returns no
954
+ \`links\` (and no \`app_url\` / \`brand_url\` on creative reads): then hand back the images
955
+ only and don't mention links.
956
+ - \`ads_creative_read { brand_id }\` — the brand's generated creatives, newest first (filter with
957
+ \`batch_id\`, \`tags\`, \`approved_only\`); each row has its \`app_url\` and the list has the
958
+ \`brand_url\`. \`creative_id\` reads one with its \`renders\`, plus \`creative.app_url\` and
959
+ \`creative.brand_url\`.
960
+ - \`ads_template_read\` — find or inspect a source (see "Picking source ads"). \`template_id\` reads one.
961
+ - \`ads_creative_edit { brand_id, creative_id, action }\` — every paid edit of one creative, one
962
+ \`action\` per call: \`regenerate\` (\`regenerate: { mode: "variation" }\` for another take;
963
+ \`mode: "edit"\` or \`"exact"\` with \`source_render_id\` and \`prompt\`), \`precision_edit\` (\`source_render_id\`
964
+ plus a \`note\` or region \`annotations\`), \`resize\` (\`source_render_id\` plus \`platforms\` or
965
+ \`targets\`), \`layerize\` (\`confirmed: true\`), \`animate\` (\`source_image_url\`; only when
966
+ \`account_whoami\` shows the Animate Images feature; for the full flow fetch the \`animate-image\`
967
+ skill). Returns \`{ job_id }\`: poll \`job_get\` with \`kind: "ads_batch"\` (regenerate, resize,
968
+ precision_edit) or \`kind: "animate"\`; for layerize read
969
+ \`ads_creative_read { brand_id, creative_id, include: ["layers"] }\`. While the brand's research is
970
+ still running, \`regenerate\`, \`resize\` and \`precision_edit\` refuse with
971
+ \`brand_research_in_progress\`: tell the user and try again when it finishes.
972
+ - \`ads_creative_update { brand_id, creative_id, patch }\` — free. \`patch.feedback: { render_id, rating:
973
+ "happy" | "neutral" | "sad", comment?, reasons? }\` records the user's reaction (it feeds the
974
+ quality loop); \`patch.tags\` replaces the creative's tags.
975
+ - \`ads_template_create { brand_id, source, rights_attested? }\` — register the user's own image as a
976
+ private source template (see "Upload" below).
977
+
978
+ ### Campaigns — the only plan-and-approve path
979
+
980
+ \`ads_generate\` has no plan step: the dry-run quote and the user's yes are the checkpoint, and a
981
+ submit generates at once. Never promise a review step before the images render. Plans exist only
982
+ for a campaign's concepts (planned with \`campaign_read\`, \`campaign_upsert\` and
983
+ \`add_campaign_concept\`; follow the connector guide for those):
984
+
985
+ 1. \`request_campaign_generation { campaign_id }\` composes the plans for the concepts with no
986
+ creatives yet (\`concept_ids\` re-runs chosen ones; \`count\` only for a number the user named). It
987
+ spends nothing and returns \`batch_ids\` and \`credits\`. Never say generation has started.
988
+ 2. Tell the user what will be made and the credit total, and wait for their explicit yes in this
989
+ chat. Never send them to a button in the app; a campaign link is only a place to look.
990
+ 3. Read \`ads_creative_read { brand_id, view: "approvals", batch_id }\` until that batch's plans are
991
+ \`awaiting_approval\` and none is \`composing\`. If its credit total differs from what they
992
+ agreed to, state the new total and ask again.
993
+ 4. \`ads_approval_decide { brand_id, decision: "approve", batch_id, user_quote: "<their exact words>" }\`
994
+ for each batch, then poll the batch ids it returns with \`job_get\` (\`kind: "ads_batch"\`) and
995
+ end with each batch's \`result.links\`, as in step 7 of the workflow below.
996
+
997
+ Before approving, a steer is free: \`ads_approval_decide { brand_id, creative_id, decision: "revise",
998
+ revise: { message } }\` recomposes that plan (read the approvals view again), and
999
+ \`decision: "edit_plan"\` patches it directly. Editing the campaign in between discards these plans.
738
1000
 
739
1001
  ## Keep the brand kit in sync — reconcile, then save or propose
740
1002
 
@@ -767,159 +1029,206 @@ ${DURABLE_BRAND_CAPTURE}
767
1029
  ## Picking source ads — use approved sources, not the retired catalog
768
1030
 
769
1031
  When the user wants to make ads but has NOT named a specific template (id/slug/Community
770
- ad/upload), do NOT silently browse the raw catalog and hand-pick for them. Instead run this
771
- short ask flow — it mirrors the web app and keeps the human in the loop:
772
-
773
- 1. **Ask what kind of ads they want** — the angle/offer/theme/season. **The brand context already
774
- gives you the vibe (voice), the audience, and the product catalog — do NOT ask for those.**
775
- Offer the real \`products[]\` to pick from rather than asking "which product?", and derive the
776
- tone from the brand's voice. This shapes both the source choice and your steering \`prompt\`.
777
- Keep it to one quick question about campaign intent.
778
- 2. **Ask how to pick a source: their own ads, Community, upload, or "Surprise me".**
779
- - **Their own ads** → use \`list_user_ad_templates\` to load the active brand's own sources and
780
- let them choose from the results.
781
- - **Community** → \`search_ad_templates\`, let them choose, then call \`remix_community_ad\`
782
- before submitting.
783
- - **Upload** → upload through the workspace and call \`create_user_ad_template\`. If its live
784
- schema requires an ownership or permission answer, only supply it after explicit confirmation.
785
- - **Surprise me** (they want you/the app to pick) → call \`surprise_me_templates\` for the active
786
- brand and hand the user the returned \`create_url\`.
787
- It opens /create in **CLI mode** with the picks pre-selected, a preview modal, and the
788
- **copyable remix prompt at the bottom** (in place of the Generate input). They can swap
789
- picks and copy that prompt. If they'd rather you "just make them" without reviewing in the
790
- app, you MAY submit the \`surprise_me_templates\` picks directly (skip to submit).
791
- - **Browse in the app** → hand the user this URL, with the
792
- active brand's slug filled in:
793
- \`https://make.gooseworks.ai/create?brand=<brand-slug>&cli=true\`
794
- In CLI mode the app shows the copyable remix prompt at the bottom (dismissable / switchable
795
- back to the UI composer). They browse the available own/Community sources and copy the prompt.
1032
+ ad/upload), do NOT silently browse the raw catalog and hand-pick for them. Instead send **one
1033
+ proposal** — it mirrors the web app and keeps the human in the loop without an interview:
1034
+
1035
+ 1. **Propose the direction yourself.** From the brand read and the evidence brief, recommend the
1036
+ product, angle/offer and tone, with a one-line reason naming what the Brain showed (a past
1037
+ approved angle, a rule it respects). **Do NOT ask what kind of ads they want, which product,
1038
+ or the vibe.** Keep any direction the user already gave. This shapes both the source choice
1039
+ and your steering \`prompt\`.
1040
+ 2. **Recommend a source in the same message: their own ads, Community, upload, or "Surprise me".**
1041
+ Default to their own approved ads when suitable ones exist, otherwise Surprise-me picks for
1042
+ the brand; list the other paths as one-line alternatives. You may resolve the picks and run the
1043
+ free dry run first so the proposal already carries the credit total. Show a list of sources as
1044
+ one table with every row and its image link, and mark your one suggestion.
1045
+ - **Their own ads** → \`ads_template_read { brand_id, mode: "mine", filters: { relationship: "self" } }\`
1046
+ and let them choose. \`mode: "competitor"\` rows are research and inspiration, never proof that
1047
+ the user owns the ad.
1048
+ - **Community** → \`ads_template_read { brand_id, mode: "query", query: "<the angle and look>" }\`
1049
+ ranks Community ads by meaning; each row's \`sourceId\` (with \`title\` and \`thumbnailUrl\`) goes
1050
+ in \`source.community_ad_ids[].community_id\`. To browse the feed instead, use \`mode:
1051
+ "community"\`: its rows carry \`item_type\`, and a \`template\` id goes in \`source.template_ids\`,
1052
+ a \`creative\` id in \`source.community_ad_ids\`. The backend snapshots a Community creative
1053
+ itself; there is no separate step. Let the user choose.
1054
+ - **Upload** → the user's own image becomes a private template: \`media_upload\` it (\`scope:
1055
+ "brand"\`, \`scope_id: <brand_id>\`, \`kind: "image"\`; in a chat app \`source.type: "bytes"\`), then
1056
+ \`ads_template_create { brand_id, source: { type: "media", media_id } }\` (or \`source: { type:
1057
+ "url", url }\` for a public image). Set \`rights_attested: true\` only after the user explicitly
1058
+ confirms they own it; this is the ownership/rights input. Never claim rights for a competitor
1059
+ ad or an image found online.
1060
+ - **Surprise me** (they want you/the app to pick) → \`ads_template_read { brand_id, mode:
1061
+ "surprise", count: 4 }\` picks remixable Community ads for the brand, shuffled so picks stay
1062
+ fresh. Every pick is a template: show them, and generate
1063
+ from the ones the user keeps (or from all of them if they said "just make them").
1064
+ - **Browse in the app** → the one link you build yourself: \`<app>/create?brand=<brand-slug>&cli=true\`,
1065
+ with the active brand's slug, where \`<app>\` is the origin of a \`brand_url\` a tool returned
1066
+ (the part before \`/?brand=\`: \`https://ads-staging.gooseworks.ai/?brand=acme\` gives
1067
+ \`https://ads-staging.gooseworks.ai\`), so it opens on the user's own environment. Reuse a
1068
+ \`brand_url\` you already have from this run, or read one cheaply with
1069
+ \`ads_creative_read { brand_id, limit: 1 }\`; if none comes back, use \`https://make.gooseworks.ai\`.
1070
+ In this mode the app shows a copyable remix prompt at the bottom (dismissable / switchable
1071
+ back to the UI composer). They browse their own and Community sources and copy the prompt.
796
1072
  3. **Close the loop.** When the user **pastes back the copyable remix prompt** from the app
797
1073
  (it names the brand + the templates they chose), THAT is your cue to generate: resolve the
798
- named source(s), inspect \`submit_remix_batch\`, and collect only its unresolved required inputs.
1074
+ named source(s) with \`ads_template_read\`, quote, and collect only the unresolved required inputs.
799
1075
 
800
1076
  If the user already named an owned source (id/slug), a Community ad, or an upload, skip the source
801
1077
  choice. Competitor ads may inform the angle or structure, but describe them as inspiration, never
802
- claim ownership, and never attest rights for the user.
1078
+ claim ownership, and never attest rights for the user. Never use the retired curated third-party
1079
+ catalog.
803
1080
 
804
1081
  ## Workflow — make ads from a template
805
1082
 
806
- 1. **Resolve the brand.** Use \`list_ad_brands\` by name/site, then call \`get_brand_kit\` for the
807
- selected brand. If the
808
- kit's \`researchStatus\` isn't \`complete\`, you can still submit (the batch queues and runs when
809
- research finishes) — just tell the user. Use the kit to pick \`product_name\` (a real entry from
810
- \`products[]\`, not a guess) and, if the user supplied product photos, \`reference_image_urls\`.
811
- 2. **Pick the source ad(s) via the ask flow above.** Once you have concrete ids:
812
- call \`get_static_ad_template\` for each.
813
- For a Community ad, \`remix_community_ad\` first; for an uploaded image, \`create_user_ad_template\`
814
- first.
1083
+ 1. **Resolve the brand and its evidence.** List brands with \`brand_read\` (no \`brand_id\`), then
1084
+ read the selected brand's summary, kit, products and
1085
+ learnings and search the Brand Brain as above. If research isn't \`complete\`, you can still
1086
+ submit (the batch queues and runs when research finishes) — just tell the user. Use the read to
1087
+ pick \`product_name\` (a real entry from \`products.items\`, not a guess) and, if the user supplied
1088
+ product photos, \`reference_image_urls\` (public URLs; \`media_upload\` a local file first).
1089
+ 2. **Pick the source ad(s) via the proposal above.** Read each chosen template with
1090
+ \`ads_template_read { brand_id, template_id }\`. Community creatives (a query row's \`sourceId\`,
1091
+ a feed row with \`item_type: "creative"\`) and the user's own finished ads go straight into
1092
+ \`source.community_ad_ids\` / \`source.creative_ids\`.
815
1093
  3. **(Optional) Craft the steering prompt.** The \`prompt\` is OPTIONAL — this is where the skill
816
1094
  adds value: turn the user's intent (from step 1) into a concise steering note (e.g. tone,
817
1095
  season, emphasis). Don't over-specify; the backend pipeline + brand kit handle palette, fonts,
818
1096
  product swap.
819
- 4. **Quote the cost.** Inspect and call \`estimate_remix_batch\`, then tell the user.
820
- 5. **Submit ONE batch.** Inspect the current \`submit_remix_batch\` schema, fill known required
821
- inputs, ask only for unresolved user decisions, and omit unspecified optional settings. Keep
822
- the returned \`batch_id\` and \`links\`.
823
- 6. **Poll until done.** Call \`get_remix_batch\` for the returned batch (or use
824
- \`list_brand_creatives\`) every ~20-30s
825
- until every creative's \`pending\` is 0. Most images finish in a few minutes; text-heavy templates
826
- and \`quality: high\` take longer. Read each render's \`elapsed_seconds\` rather than guessing — a
827
- render that's still \`running\` is healthy; do NOT re-submit thinking it stalled (that double-bills).
828
- 7. **Hand back the links** from the batch's \`links\` block — \`brand_url\` (gallery) and each
829
- creative's \`app_url\` — copied verbatim. Never end on just "done" or a file path.
1097
+ 4. **Quote the cost.** Call \`ads_generate\` with the exact arguments you will submit plus
1098
+ \`dry_run: true\` (template sources), or work out the total as in the credits table. Drop or
1099
+ replace anything in \`unknown_template_ids\`. State the image count and credit total in one line
1100
+ and wait for the user's explicit yes.
1101
+ 5. **Submit ONE batch** after that yes: the same \`ads_generate\` call without \`dry_run\`. Keep the
1102
+ returned \`job_id\`.
1103
+ 6. **Poll until done.** \`job_get { job_id, kind: "ads_batch" }\` every ~20-30s until every creative's
1104
+ \`pending\` is 0. Most images finish in a few minutes; text-heavy templates and \`quality: high\`
1105
+ take longer. Read each render's \`elapsed_seconds\` rather than guessing; do NOT re-submit
1106
+ thinking it stalled (that double-bills). If \`ads_generate\` refused with
1107
+ \`brand_research_required\`, the brand was never researched: run "Brand research" below, then
1108
+ quote and submit again.
1109
+ 7. **Hand back the ads with their links.** Show every finished image (\`renders[].output_url\` of
1110
+ completed renders) and say in one line what failed, if anything. Then give the links from the
1111
+ last \`job_get\`'s \`result.links\`, copied verbatim: the \`brand_url\` where all the brand's
1112
+ ads are, and the \`app_url\` of each creative with a finished image (match
1113
+ \`creative_links[].project_id\` to \`result.creatives[].id\`; skip a creative whose renders all
1114
+ failed). For a big batch (more than about 8 creatives) give the \`brand_url\` and offer the
1115
+ rest. Skip a missing or \`null\` link; never build an app URL yourself. Never end on just
1116
+ "done" or a file path.
830
1117
 
831
1118
  ## Workflow — edit an existing ad
832
1119
 
833
- User wants to tweak a creative they already made → use \`regenerate_creative\`. Infer whether they
834
- want another take, a targeted edit, or an exact instructed change from their request. Then inspect
835
- the live schema, ask only for any required source or instruction that is still missing, submit,
836
- poll with \`get_remix_batch\`, and hand back the links.
1120
+ User wants to tweak a creative they already made → \`ads_creative_edit\`. Read the creative first
1121
+ (\`ads_creative_read { brand_id, creative_id }\`) to get the render they mean. Infer the action
1122
+ from their request: another take (\`regenerate\`, \`mode: "variation"\`), a targeted change
1123
+ (\`regenerate\` with \`mode: "edit"\`, or \`precision_edit\` when they point at a region), an exact
1124
+ instructed change (\`mode: "exact"\`), new placements (\`resize\`), editable layers (\`layerize\`) or a
1125
+ short video (\`animate\`). Ask only for a required source or instruction that is still missing,
1126
+ state the credit total from the credits table, get the yes, submit, poll as above, and hand back
1127
+ the new images with their links: \`result.links\` from \`job_get\` for \`regenerate\`, \`resize\` and
1128
+ \`precision_edit\`; \`creative.app_url\` and \`creative.brand_url\` from \`ads_creative_read { brand_id,
1129
+ creative_id }\` for \`animate\` and \`layerize\`.
837
1130
 
838
1131
  ## Brand research
839
1132
 
840
- Prefer the backend's result: call \`get_brand_kit\` for the selected brand. If \`researchStatus\` is
841
- \`complete\`, REUSE it — never re-research.
1133
+ Prefer the backend's result. \`brand_read { brand_id, sections: ["summary"] }\`: if
1134
+ \`research_status\` is \`complete\`, REUSE it — never re-research. Proof points and the full product
1135
+ catalog keep arriving until \`enrichment.settled\` is true.
842
1136
 
843
1137
  **The split — backend owns visuals, you own the qualitative depth:**
844
1138
 
845
- - **Backend LIGHT pass (automatic).** \`create_ad_brand\` with a \`website_url\` kicks off the same
846
- backend research the web app uses, in \`mode: "light"\`: it resolves the **authoritative logo,
847
- colors, and fonts** (Brandfetch + context.dev) plus a baseline kit, then flips
848
- \`research_status\` to \`complete\` — usually under a minute. You can't reproduce those visual
849
- signals locally, so **never re-derive logo/colors/fonts.** (Web onboarding via \`/api/ads/onboard\`
850
- runs the full thing; nothing to do but read it.)
851
- - **Your DEEP pass (local, agentic).** You add the qualitative depth the light pass leaves thin —
852
- positioning, audience segments, voice, brandType, value props, proof points, products — grounded
853
- on the actual site.
854
-
855
- **CLI brand-research flow:**
856
-
857
- 1. Inspect and call \`create_ad_brand\` with the known brand identity and website, then keep its id
858
- and slug. The brand comes back with
859
- \`research_status: "pending"\` (light pass in flight).
860
- 2. **Wait for the backend light pass:** poll \`get_brand_kit\` for that brand until \`researchStatus\`
861
- is \`complete\` (usually <60s). Now the kit has authoritative logo/colors/fonts + a baseline.
862
- At this point generation is already unblocked — but do the deep pass to make it good.
863
- 3. **Deep research locally:** \`gooseworks fetch brand-research\` and follow its phases. **Ground
864
- every fact on the fetched site** — if the site can't be read, say so and ask the user; never
1139
+ - **Backend LIGHT pass (automatic).** A new brand comes from the connector's setup flow
1140
+ (\`brand_onboarding { action: "status" }\`, then its \`next_step\`). For an additional brand, check
1141
+ it is absent (\`brand_read\` with no \`brand_id\`), then \`brand_create { name, website_url }\` (free).
1142
+ The website starts the same backend research the web app uses: it resolves the
1143
+ **authoritative logo, colors, and fonts** plus a baseline kit and returns \`research_job_id\`; poll
1144
+ \`job_get { job_id: research_job_id, kind: "brand_research" }\` (or \`brand_read\`) until
1145
+ \`research_status\` is \`complete\` — usually under a minute. If it returns \`reused_existing: true\`,
1146
+ tell the user and use that brand. You can't reproduce those visual signals, so **never
1147
+ re-derive logo/colors/fonts.**
1148
+ - **Your DEEP pass (optional).** You add the qualitative depth the light pass leaves thin —
1149
+ positioning, audience, voice, brand type, value props, proof points, products — grounded on
1150
+ the actual site.
1151
+
1152
+ **Deep research flow:**
1153
+
1154
+ 1. Fetch the \`brand-research\` skill (\`catalog_fetch { type: "skill", slug: "brand-research" }\` in a
1155
+ chat app; \`gooseworks fetch brand-research\` in a terminal) and follow its phases. Its data
1156
+ calls are paid: state the credit total (credits table) and get the yes first. **Ground every
1157
+ fact on the brand's own site** — if the site can't be read, say so and ask the user; never
865
1158
  guess a category from the brand name alone.
866
- 4. **Write the pack** with \`write_file\` under \`agent-config/brands/<slug>/\`:
867
- - the \`brand-research/*.md\` docs + \`brand-assets/manifest.json\` (human-readable pack), AND
868
- - \`brand-research/kit-patch.json\` — the STRUCTURED fields the web UI renders. Field-for-field
869
- contract; only what you put here reaches the kit. Shape:
870
- \`{ positioning?: string, audience?: string, voice?: string, brandType?: string, tagline?: string, valueProps?: string[], proofPoints?: string[], products?: [{ name, description?, link?, pricing?, imageUrls?: string[] }] }\`
871
- (\`brandType\` ∈ product | saas | service | agency | restaurant | fashion | beauty | fitness |
872
- finance | education | health). Only URLs already in our storage for product images.
873
- - **Do NOT set logo / colors / fonts here** — the backend light pass already owns those.
874
- 5. **Persist it:** call \`finalize_brand_research\` for the brand. It merges \`kit-patch.json\` into the kit
875
- NON-CLOBBERINGLY (it will NOT overwrite the backend's visuals or any user edit), then re-confirms
876
- \`research_status: complete\`.
877
- 6. **Verify:** call \`get_brand_kit\` again and confirm the qualitative fields you wrote are present
878
- before generating.
879
-
880
- **If the brand has NO website**, the backend light pass can't run (nothing to fetch) — do the whole
881
- thing locally (steps 3–6) and finalize; an un-finalized brand has no kit for generation and leaves
882
- no artifact to debug a wrong run (this is how a bad local classification, e.g. mislabelling a SaaS
883
- as a "drink company", used to vanish without a trace).
1159
+ 2. **Save what you improved.**
1160
+ - **As proposals (the default):** \`brand_update { brand_id,
1161
+ knowledge_intent: "agent_proposal", rationale, patch: { knowledge: { positioning?, audience?,
1162
+ voice?, brandType?, tagline?, valueProps? } } }\`. They stay pending until the user accepts
1163
+ them in the app.
1164
+ - **As the full research pack (only when you wrote all of it; in a chat app, only for a brand
1165
+ with no website, see below):** with \`file_write\`, under
1166
+ \`agent-config/brands/<slug>/brand-research/\`, write all four docs \`brand-summary.md\`,
1167
+ \`visual-identity.md\`, \`audience.md\` and \`competitors.md\` (real content, at least a few
1168
+ sentences each) plus \`kit-patch.json\`: \`{ positioning?, audience?, voice?, brandType?,
1169
+ tagline?, valueProps?: string[], proofPoints?: [{ text, source_url }], products?: [{ name,
1170
+ description?, link?, pricing?, imageUrls?: string[] }] }\` with only the fields you improved;
1171
+ every proof point copied word for word from the brand's own page at \`source_url\`; product
1172
+ images only from URLs already in GooseWorks storage; **no logo / colors / fonts**. Then
1173
+ \`brand_update { brand_id, patch: { finalize_research: true } }\` merges it NON-CLOBBERINGLY
1174
+ (never over the backend's visuals or a user edit). **If any of the four docs is missing or
1175
+ nearly empty, finalize fails and marks the brand's research as failed**, so never finalize a
1176
+ partial pack.
1177
+ 3. **Verify:** \`brand_read\` again (\`summary\`, \`kit\`) and confirm what you saved before generating.
1178
+
1179
+ **If the brand has NO website**, the backend light pass can't run and generation goes ahead with
1180
+ an empty kit, so ads come out off-brand. Do the deep pass from what the user tells you and save the
1181
+ full research pack, so the brand has a kit and a record to debug a wrong run (this is how a bad
1182
+ classification, e.g. mislabelling a SaaS as a "drink company", used to vanish).
884
1183
 
885
1184
  ## Analyze / intelligence (fetched recipes — NOT generation)
886
1185
 
887
- These are analysis recipes you fetch from goose-skills with \`gooseworks fetch <slug>\` and
888
- follow; they do NOT touch the generation tools or credits-for-images. Pick the closest match;
889
- if unsure, \`gooseworks search "<what the user wants>"\` first:
1186
+ These are analysis recipes you fetch and follow; they don't use the generation tools, but their
1187
+ data calls are paid (credits table: state the total and get the yes before the first one). Fetch with \`catalog_fetch { type: "skill", slug: "<slug>" }\` in a chat app or
1188
+ \`gooseworks fetch <slug>\` in a terminal. Pick the closest match; if unsure, search first with
1189
+ \`catalog_search { type: "skill", query: "<what the user wants>" }\` (terminal:
1190
+ \`gooseworks search "<what the user wants>"\`):
890
1191
  - **Campaign performance diagnosis** ("why is my Meta/Google campaign underperforming",
891
- creative fatigue, learning phase, pacing, auction overlap) → \`gooseworks fetch meta-ads-analyzer\`
1192
+ creative fatigue, learning phase, pacing, auction overlap) → \`meta-ads-analyzer\`
892
1193
  (or \`ad-campaign-analyzer\` for cross-platform).
893
1194
  - **Lead/CAC quality** ("are these ads driving qualified leads", true CAC vs vanity CPA,
894
- Scale/Keep/Investigate/Cut) → \`gooseworks fetch ad-lead-quality-analyzer\`.
1195
+ Scale/Keep/Investigate/Cut) → \`ad-lead-quality-analyzer\`.
895
1196
  - **Competitor ad intelligence** ("what ads are competitors running") →
896
- \`gooseworks fetch competitor-ad-intelligence\` (Meta Ad Library: \`meta-ad-scraper\`;
897
- Google: \`google-ad-scraper\`).
898
- - **Creative ideation** (ad angles, winning hooks) → \`gooseworks fetch ad-angle-miner\` /
899
- \`gooseworks fetch trending-ad-hook-spotter\`.
900
- - **Policy / landing-page checks** → \`gooseworks fetch meta-ad-policy-checker\` /
901
- \`gooseworks fetch ad-to-landing-page-auditor\`.
1197
+ \`competitor-ad-intelligence\` (Meta Ad Library: \`meta-ad-scraper\`; Google: \`google-ad-scraper\`).
1198
+ - **Creative ideation** (ad angles, winning hooks) → \`ad-angle-miner\` / \`trending-ad-hook-spotter\`.
1199
+ - **Policy / landing-page checks** → \`meta-ad-policy-checker\` / \`ad-to-landing-page-auditor\`.
902
1200
 
903
- Save their scripts to \`/tmp/gooseworks-scripts/<slug>/\` and follow their instructions. These
904
- run through the \`gooseworks\` CLI (\`gooseworks fetch\` / \`gooseworks call\`), like the GTM skills.
1201
+ A recipe's provider calls go through the connector in a chat app (\`data_call_provider\` for GET,
1202
+ \`data_post_provider\` for POST) and through \`gooseworks call\` in a terminal; both are billed. Saving and running
1203
+ its scripts (under \`/tmp/gooseworks-scripts/<slug>/\`) is terminal-only: without a terminal, do
1204
+ that step's analysis yourself from the data the tools return, and never ask the user to run a
1205
+ command.
905
1206
 
906
1207
  ## Rules
907
1208
 
908
- - **MCP required** — if \`mcp__gooseworks__*\` is unavailable, stop and tell the user to run
909
- \`gooseworks install --claude --mcp\`.
910
- - **One backend workflow** — generation is \`submit_remix_batch\` / \`regenerate_creative\` ONLY.
1209
+ - **Connector tools only** — every step uses the tool names above. A missing tool means the
1210
+ GooseWorks connection is stale: ask the user to reconnect or refresh GooseWorks. Never send a
1211
+ chat-app user to a terminal or a CLI install.
1212
+ - **One backend workflow** — generation is \`ads_generate\` / \`ads_creative_edit\` ONLY.
911
1213
  Do NOT call FAL, the media proxy, \`submit_render\`, \`update_render_status\`, or upload render
912
- files yourself; do NOT \`gooseworks fetch\` a local remix recipe to generate. The backend owns it.
913
- - **Always end a successful run with the links** from the batch's \`links\` block (\`brand_url\` +
914
- each creative's \`app_url\`), copied verbatim. Never end on just "done" or a file path.
915
- - **Quote cost before generating** when it's non-trivial (use \`estimate_remix_batch\`), and
916
- relay \`insufficient_credits\` plainly if the submit is rejected — don't retry blindly.
917
- - **Use approved source paths.** If the user didn't name a source, run the ask flow (own ads,
918
- Community, upload, Surprise me, or browse in the app). "Surprise me" goes through
919
- \`surprise_me_templates\`; browsing uses \`/create?brand=<slug>&cli=true\`. Never use the retired
920
- curated third-party catalog.
921
- Generate when they paste the app's copyable remix prompt back (or submit the surprise picks
922
- directly if they'd rather not review).
1214
+ files yourself; do NOT fetch a local remix recipe to generate. The backend owns it.
1215
+ - **State the credit total and get an explicit yes before every paid call** (credits table above).
1216
+ Relay \`insufficient_credits\` plainly if the submit is rejected — don't retry blindly.
1217
+ - **No plan step for one-off ads.** Only a campaign's plans wait for approval
1218
+ (\`request_campaign_generation\` → \`ads_approval_decide\` with the user's words as \`user_quote\`).
1219
+ - **Always end a successful run with the finished images and their links**, copied verbatim:
1220
+ each creative's \`app_url\` and the \`brand_url\` from \`result.links\`, or from the creative
1221
+ read (\`creative.app_url\`, \`creative.brand_url\`) after \`animate\` or \`layerize\`. When the
1222
+ server returns no links, end with the images only. Never end on just "done" or a file path, and
1223
+ never build an app URL yourself (the one exception is the browse link below).
1224
+ - **Search the Brain before proposing.** After the brand read and before choosing an angle,
1225
+ claim or source — or asking for a brand fact — run the task's \`knowledge_search\` and carry
1226
+ its evidence brief. A failed or empty search is stated as such, never as "no evidence exists".
1227
+ - **Use approved source paths.** If the user didn't name a source, recommend one in the single
1228
+ proposal (own ads, Community, upload, Surprise me, or browse in the app). Surprise me is
1229
+ \`ads_template_read\` with \`mode: "surprise"\`; browsing uses \`/create?brand=<slug>&cli=true\`.
1230
+ Never use the retired curated third-party catalog. Generate when they paste the app's copyable
1231
+ remix prompt back, or when they accept the surprise picks.
923
1232
  - **Treat competitor ads as inspiration** — never attest rights, imply ownership, or promise to
924
1233
  copy a competitor's distinctive expression.
925
1234
  - **Reconcile brand facts into the kit** — when the user states or changes something brand-level
@@ -927,17 +1236,15 @@ run through the \`gooseworks\` CLI (\`gooseworks fetch\` / \`gooseworks call\`),
927
1236
  \`brand_update\` with correction intent and the user's exact statement, or the user's own
928
1237
  images through \`media_upload\`; read back before saying saved. Proposed improvements stay
929
1238
  pending. Ask only when it is unclear whether a one-ad direction should apply to future ads.
930
- - **Record feedback** — when the user reacts to a generated image, inspect and call
931
- \`set_creative_feedback\` so the quality loop learns.
932
- - **Plan mode is opt-in** — only use the live approval option, then \`list_ad_approvals\` and
933
- \`approve_ad_plan\`, when the user wants to review before spending credits; otherwise generate
934
- immediately.
935
- - **Don't busy-loop** — poll \`get_remix_batch\` on a sensible interval (~20-30s); a \`queued\`
936
- batch is waiting on research and will start on its own.
937
- - **Report problems so we can fix them** — when a batch fails/is rejected and you can't resolve it,
938
- a required brand input/asset is missing, or a recipe/instruction is ambiguous or contradictory,
939
- call the **\`log_cli_event\`** MCP tool (\`event_type\`: \`error\`/\`blocker\`/\`missing_input\`/\`confusion\`,
940
- with the real error + step in \`details\`) so the team gets visibility. Still tell the user too.
1239
+ - **Record feedback** — when the user reacts to a generated image, call \`ads_creative_update\`
1240
+ with \`patch.feedback\` so the quality loop learns.
1241
+ - **Don't busy-loop** — poll \`job_get\` on a sensible interval (~20-30s); a \`queued\` batch is
1242
+ waiting on research and will start on its own.
1243
+ - **Report problems so we can fix them** — when a batch fails or is rejected and you can't resolve
1244
+ it, a required brand input/asset is missing, or an instruction is ambiguous or contradictory:
1245
+ in a terminal run \`gooseworks log "<what happened>" --event-type <error|blocker|missing_input|confusion> --details '<json with the real error and step>'\`,
1246
+ or call the \`log_cli_event\` tool if your connection lists it. A chat app without either skips
1247
+ this step. Always tell the user too.
941
1248
  `;
942
1249
  }
943
1250
  /** Card-aware video entry. Shell execution and widget display are independent. */
@@ -947,11 +1254,11 @@ name: goose-video
947
1254
  slug: goose-video
948
1255
  description: >
949
1256
  Start a video ad in the same chat. Resolve the brand and suggest supported formats with a
950
- picker or the returned text choices. An agent with a verified shell makes the video with
951
- goose-video-local; a chat host hands the same project to the GooseWorks coworker. Review
952
- one complete template plan and total credits before production. Custom videos retain two authenticated review gates in this chat.
1257
+ picker or the returned text choices. An agent that can run shell commands on the customer's
1258
+ computer makes the video with goose-video-local; otherwise the GooseWorks coworker makes it.
1259
+ Review one complete template plan and total credits before production. Custom videos retain two authenticated review gates in this chat.
953
1260
  category: ads
954
- version: 3.0.0
1261
+ version: 3.1.0
955
1262
  author: GooseWorks
956
1263
  tags: [gooseworks, ads, video, local-render, coworker, chat]
957
1264
  ---
@@ -960,21 +1267,114 @@ tags: [gooseworks, ads, video, local-render, coworker, chat]
960
1267
 
961
1268
  ${CUSTOMER_TALK}
962
1269
 
1270
+ ${CLI_TOOL_BRIDGE}
1271
+
963
1272
  ## Purpose
964
1273
 
965
- Resolve the brand, show supported formats and create one saved project. An agent with a verified
966
- shell follows **\`goose-video-local\`**. A chat host delegates that same project to the GooseWorks
967
- coworker, which renders in its sandbox. This is agent execution, not a recipe server-order API.
1274
+ Resolve the brand, show supported formats and create one saved project. This is agent
1275
+ execution, not a recipe server-order API.
1276
+
1277
+ ${WHO_MAKES_THE_VIDEO}
968
1278
 
969
- **Cards and execution are separate capabilities.** A terminal may render without drawing a
970
- picker; a chat host may draw a picker while the coworker renders. Follow \`card.display_hint\`
971
- and \`available_here\`, never a guess from the host's name. Template-remix review stays in this
1279
+ **Cards and execution are separate.** A terminal may render without drawing a picker; a chat
1280
+ app may draw a picker while the coworker renders. Follow \`card.display_hint\` for how to show
1281
+ things; who makes the video is the rule above. Template-remix review stays in this
972
1282
  chat. Custom videos use separate authenticated script and ingredient approvals in this same chat.
973
1283
  Studio is an optional review surface.
974
1284
 
975
- ## Custom videos: route before formats
976
-
977
- For an original brief without a reference template, an Instagram reel/post URL or a direct video URL to study, fetch \`catalog_fetch { type: "skill", slug: "make-custom-video" }\` and follow it in this same session. It creates format:"custom", custom_mode:"generate" with the brief and optional reference_url. Growth executes in its managed sandbox; connected agents use their shell. Script and actual ingredients are reviewed and separately approved in the same chat before paid production; Studio is optional. Do not force a template choice or import the reference as a finished video.
1285
+ ${videoEntryPreparation('goose-video')}
1286
+
1287
+ ## Custom videos: check formats first
1288
+
1289
+ First check the catalog: when the brief names or implies a listed format (for example a street interview, testimonial, podcast or chat video), show that format and its fit through the format flow below, including its support status. Go custom only when no format fits and the customer chooses custom after hearing why; custom keeps that format's hard constraints. For an original brief without a reference template, an Instagram reel/post URL or a direct video URL to study, fetch \`catalog_fetch { type: "skill", slug: "make-custom-video" }\` and follow it in this same session. It creates format:"custom", custom_mode:"generate" with the brief and optional reference_url. Growth executes in its managed sandbox; connected agents use their shell. Script and actual ingredients are reviewed and separately approved in the same chat before paid production; Studio is optional. Do not force a template that does not fit or that the customer declined, and do not import the reference as a finished video.
1290
+
1291
+ ## Run the format's route check before proposing it
1292
+
1293
+ A catalog row says what a format is best for and ranks it for the brand. It does not say whether
1294
+ the format can make the setup the customer asked for (how many people, mic only, no product).
1295
+ Some renderers declare a route selector that decides that; today that is the street interview
1296
+ (\`render-street-interview\`). Before you propose, script or create a street-interview project,
1297
+ template or custom, check the request against its routes:
1298
+
1299
+ | Street route | On screen | Needs | Makes |
1300
+ | --- | --- | --- | --- |
1301
+ | Guessing (\`product-guess\`) | Interviewer and up to four people | A physical product to hand over, photographed on its own | A finished video |
1302
+ | Conversation (\`mic-only\`, \`product-sample\`, \`concept-challenge\`) | Interviewer and one person | No product photo, phone, screen or UI | A script and prompt preview only, no finished video yet |
1303
+ | Street testimonial (its own format) | One person talking to camera, no interviewer | The creator still its recipe prescribes | A finished video, when the catalog lists it |
1304
+
1305
+ Neither interview route takes a photo of a person: people are described in text, so never
1306
+ propose putting a founder's or creator's face in one. Keep the customer's named setup: never
1307
+ change the interaction, mode or number of people just to make a check pass.
1308
+
1309
+ **Never offer guessing to software or a service.** The guessing route needs a physical product
1310
+ to hand over. When what the brand sells is software, an app or a service (nothing a person can
1311
+ hold), never offer or script guessing, not even with a laptop, phone or other device showing the
1312
+ app as the prop: the device is a prop, so \`offering_type\` stays \`digital\` or \`service\` and the
1313
+ selector says unsupported. A brand that sells a physical product, including a device or
1314
+ hardware that comes with an app, can still use guessing with that product. Offer the table's
1315
+ real alternatives instead: a one-person conversation (a script and prompt preview only, no
1316
+ finished video yet), the street testimonial if the catalog lists it and does not rule it out,
1317
+ or custom. Asking again never unlocks a route the table or the selector rules out.
1318
+
1319
+ When they asked for a street interview, the catalog may mark (newer servers do) the
1320
+ street-interview rows \`fit.ok: false\` for such a brand: they can't make a finished video for it.
1321
+ That verdict is about the finished video, so still offer the one-person conversation, saying
1322
+ plainly that it is a script and prompt preview only. If the brand read shows the brand sells the
1323
+ device itself but the catalog rules guessing out, tell the customer the catalog flagged it and
1324
+ confirm what they sell before offering guessing with that device.
1325
+
1326
+ - **Without a shell on the customer's computer**, decide from this table. Do not fetch the renderer: its
1327
+ package is far too large for a chat.
1328
+ - **With a shell** (the customer's computer or the coworker sandbox), also run its free
1329
+ selector, which makes no paid call. If the fetch or the run fails, decide from the table.
1330
+ 1. Fetch the renderer to a file, not into the conversation: its inline package is over a
1331
+ million characters. Either save the JSON from \`gooseworks fetch render-street-interview > <file.json>\`,
1332
+ then write each \`scripts\` entry as
1333
+ \`/tmp/gooseworks-scripts/render-street-interview/scripts/<name>\` and each \`files\` entry at
1334
+ \`/tmp/gooseworks-scripts/render-street-interview/<path>\`. Or call
1335
+ \`catalog_fetch { type: "skill", slug: "render-street-interview", delivery: "archive" }\`,
1336
+ extract the ZIP, check each file against its \`manifest.json\` hashes and copy the contents of its
1337
+ \`agent-config/skills/render-street-interview/\` folder into
1338
+ \`/tmp/gooseworks-scripts/render-street-interview/\`. Either way the selector reads
1339
+ \`references/street-reference-library.json\` from that folder.
1340
+ 2. Write a brief JSON. \`mode\` and \`interaction_type\` go in pairs: \`product-guess\` with
1341
+ \`product-guess\`, or \`conversation\` with \`mic-only\`, \`product-sample\` or \`concept-challenge\`.
1342
+ \`offering_type\` is exactly \`physical\`, \`service\` or \`digital\` (software, SaaS and apps are
1343
+ \`digital\`). \`participants\` is a whole number: people interviewed on screen, not counting
1344
+ the interviewer. Leave it out when the customer named no count; the selector then uses the
1345
+ route's usual cast. When the customer named no interaction, run each mode that could fit.
1346
+ 3. Run \`python3 /tmp/gooseworks-scripts/render-street-interview/scripts/prepare_script_context.py --brief <brief.json> --out <context.json>\`.
1347
+ Read \`status\` and \`brief_gaps\` in the output file, not the exit code: it exits 2 for every
1348
+ status except \`ready-for-writing\`. A Python traceback is not a verdict: the brief or the
1349
+ saved files are wrong (for example the reference library in the wrong folder). Fix them and
1350
+ run it again.
1351
+ 4. A run with any \`brief_gaps\` has not checked the route. Fix every gap and run it again.
1352
+
1353
+ **An unsupported setup is a stop.** \`unsupported-route\`, or a request the table rules out,
1354
+ means this setup can't be made. Tell the customer plainly, in one line, for example: "A
1355
+ three-person mic-only street interview isn't something we can make yet: that version takes one
1356
+ person, and the three-person version needs a physical product to hand over." Then offer the
1357
+ closest supported options in plain words, each with how it differs. With a shell, these are the
1358
+ selector's \`alternatives\`. Without one, take them from the table:
1359
+
1360
+ - when only the number of people was the problem, the same setup within its limit: guessing
1361
+ (a physical product only) with up to four people, a finished video; or a conversation with
1362
+ one person, a script preview only;
1363
+ - for something people can't hold (software, a service), a one-person conversation instead
1364
+ of guessing;
1365
+ - one person talking to camera with no interviewer, if the catalog lists that format;
1366
+ - a custom video, untested, that keeps the street format's limits.
1367
+
1368
+ Never use route names or the script's wording. Do not create the project or write a script for
1369
+ it. Go custom only if the customer picks it; a custom video keeps the street format's hard
1370
+ constraints, which make-custom-video lists.
1371
+
1372
+ **A supported setup.** With empty \`brief_gaps\`, any other status means the route exists.
1373
+ \`needs-reference\` means no observed reference ad matched yet: it does not block proposing the
1374
+ format; carry it into the handoff so the script step resolves or reports it before script
1375
+ approval. Carry the route too: which one, how many people, finished video or preview only, and
1376
+ no person photos. A preview-only route makes a script and prompt preview, not a finished video:
1377
+ say so before the customer chooses it.
978
1378
 
979
1379
  ## Route first: is this a new video?
980
1380
 
@@ -984,9 +1384,10 @@ Hand off to **\`goose-video-local\`** now, and stop following this skill, for:
984
1384
  - the app's copy-for-Claude command (it names \`goose-video-local\`);
985
1385
  - "remix this video ad template" for a specific app template.
986
1386
 
987
- Use \`goose-video-local\` if it is installed; otherwise load it with
1387
+ Load the current \`goose-video-local\` entry with
988
1388
  \`catalog_fetch { type: "skill", slug: "goose-video-local" }\` on the GooseWorks MCP (older clients:
989
- \`fetch_skill("goose-video-local")\`). It reads the project first and says what to do with it.
1389
+ \`fetch_skill("goose-video-local")\`), unless already fetched on this connection in this run.
1390
+ It reads the project first and retains an approved run's recorded recipe packages.
990
1391
 
991
1392
  When they ask **what** to make ("give me video ad ideas", "what angles should I use?", "what's
992
1393
  working for my competitors?"), fetch **\`ad-angle-miner\`** (\`catalog_fetch { type: "skill", slug: "ad-angle-miner" }\`,
@@ -1042,16 +1443,15 @@ bypass setup. Keep the original request.
1042
1443
  ### 2. Keep the goal and propose defaults
1043
1444
 
1044
1445
  Never ask what the ad is for before showing formats. Keep the customer's exact direction.
1045
- Otherwise propose defaults from the verified kit's products, audience, offer and voice. Show
1046
- those defaults in the saved plan so any can be changed. Never invent proof or product facts.
1446
+ Otherwise propose defaults from the verified kit's products, audience, offer and voice and the
1447
+ Brain search's evidence brief (proven angles, saved rules, approved claims). Show those defaults
1448
+ in the saved plan so any can be changed. Never invent proof or product facts.
1047
1449
  Idea requests still follow ad-angle-miner with the video output.
1048
1450
 
1049
1451
  ### 3. Show the picker or its text fallback
1050
1452
 
1051
- Call \`video_catalog_list { kind: "formats", brand_id }\`. On the customer's computer, when you
1052
- can actually execute shell commands, include \`client: { shell: true }\`; in a chat host omit
1053
- that claim. Inside a coworker sandbox follow its reported capabilities, never claim to be the
1054
- customer's computer. Respect requested limit and next_cursor for more item pages; the card may
1453
+ Call \`video_catalog_list { kind: "formats", brand_id }\`. Every caller gets every format.
1454
+ Respect requested limit and next_cursor for more item pages; the card may
1055
1455
  contain a full picker independently of the item page.
1056
1456
 
1057
1457
  - **card.display_hint:"widget"**: the picker replaces a format table. Write at most one short
@@ -1059,8 +1459,18 @@ contain a full picker independently of the item page.
1059
1459
  - **Otherwise**: print card.text_summary as returned. Without a card, show returned rows with
1060
1460
  names, faithful descriptions, needs and demo links, respecting the requested page size.
1061
1461
  Write "no demo yet" when absent. Never put links only in a question control.
1062
- - A format whose card contradicts what they asked for is never Suggested. Never offer
1063
- available_here:false as an executable choice. Relay not_available_here in one short line.
1462
+ - A format whose card contradicts what they asked for is never Suggested. A \`needs_browser\`
1463
+ format is made only on the customer's computer (the coworker has no browser): if you have no
1464
+ shell there and the customer picks one, say it needs GooseWorks running on their computer and
1465
+ offer another.
1466
+ - A row with \`fit.ok: false\` is ruled out for this brand. Never propose it unless the customer,
1467
+ after hearing why, still asks for it. For a street interview, insisting never unlocks a route
1468
+ the route check rules out; when they asked for one, the one-person conversation preview is the
1469
+ one exception (see the route check). When they ask about a ruled-out row, tell them why in
1470
+ plain words (its \`fit.reason\`), never the field name.
1471
+ - \`suggested\` ranks a row for the brand; it never means the row makes the setup they named.
1472
+ Never tell the customer a row is "exactly" what they asked for beyond what its card says. A
1473
+ street interview still needs the route check above first.
1064
1474
  - Missing required assets are missing; unknown suitability is “needs review.” Inspect the
1065
1475
  selected format's candidates before spending. Never promise an unverified asset is ready.
1066
1476
  - Template formats use **one plan, one approval with the total in credits**, not approval for
@@ -1068,11 +1478,11 @@ contain a full picker independently of the item page.
1068
1478
 
1069
1479
  ### 4. Check this machine can render it
1070
1480
 
1071
- - **Chat host without a shell:** skip local checks and delegate an available format in Step 5.
1072
- Never tell a chat host it needs Claude Code to start. Browser-only formats remain unavailable
1073
- here; give computer setup guidance only if asked how to make one.
1481
+ - **No shell on the customer's computer:** skip local checks and hand the project off in Step 5.
1482
+ Never tell the customer they need Claude Code to start. Give computer setup guidance only if
1483
+ asked how to make a \`needs_browser\` format.
1074
1484
  - **Local shell:** run \`gooseworks doctor --no-browser\` for common setup (auth/MCP, Node 18+,
1075
- ffmpeg with libx264 + libass, ffprobe). Then fetch the selected template and its capabilities,
1485
+ ffmpeg with libx264 (libass only for an ASS caption route), ffprobe). Then fetch the selected template and its capabilities,
1076
1486
  and install documented dependencies in its fetched folder. Do not guess a renderer from a
1077
1487
  format name. For each Node renderer using default Playwright Chromium, run
1078
1488
  \`gooseworks doctor --renderer-script "/absolute/path/to/the/fetched/scripts/record.js"\` with
@@ -1080,31 +1490,36 @@ contain a full picker independently of the item page.
1080
1490
  Use the actual script path. Custom browser launch settings need their equivalent exact-runtime
1081
1491
  check. Non-browser capabilities need only their documented runtime checks.
1082
1492
  Never create paid ingredients before the selected renderer passes. Repair and recheck under
1083
- existing setup permissions. If it cannot be fixed, re-list with client:{shell:false} and
1084
- delegate only a format available to the coworker; never silently change the selected format.
1493
+ existing setup permissions. If it cannot be fixed, hand the project off in Step 5 (a
1494
+ \`needs_browser\` format can't be: offer another); never silently change the selected format.
1085
1495
  - **Coworker sandbox:** you are the renderer. Follow goose-video-local's sandbox checks; never
1086
1496
  hand off recursively or claim browser capability that the sandbox lacks.
1087
1497
 
1088
1498
  ### 5. Create the project and hand it off, in this session
1089
1499
 
1090
1500
  1. \`video_project_upsert { brand_id, name, format: <template_id> }\` with no brief (a brief creates
1091
- a concept batch). Include client:{shell:true} only for real local execution. Default
1501
+ a concept batch). A street interview is created only after its route check found a supported
1502
+ setup. Default
1092
1503
  creation_intent:"format" keeps the style with this brand's content; source_remix requires an
1093
1504
  explicit choice to use source content.
1094
1505
  If they already chose a campaign/concept, include its verified campaign_id and optional
1095
1506
  campaign_concept_id. Read the IDs from that brand's saved campaign; the concept must belong
1096
1507
  to it. Omit unknown IDs and never infer a link or create a campaign solely to file a video.
1097
- 2. **Verified local shell or coworker sandbox:** load
1508
+ 2. **You make it** (a shell on the customer's computer, or the coworker sandbox): load
1098
1509
  \`catalog_fetch { type: "skill", slug: "goose-video-local" }\` and follow it on the same project_id.
1099
- Carry the customer's words, verified defaults, campaign and selected angle into its brief.
1100
- 3. **Chat host:** \`goose_run_task { brand_id, project_id, message }\` with the request, selected
1101
- format and defaults. Keep task_id. Continue questions/edits with the same task and project.
1510
+ Carry the customer's words, verified defaults, campaign, selected angle and a street
1511
+ interview's route check result into its brief.
1512
+ 3. **No shell there:** \`goose_run_task { brand_id, project_id, message }\` with the request, selected
1513
+ format, defaults and a street interview's route check result. Keep task_id. Continue questions/edits with the same task and project.
1102
1514
  A saved free draft is not a started worker or a complete plan. Follow actual saved state.
1103
1515
  4. When follow.card_follows is true, the card follows progress: do not re-read it in a loop.
1104
1516
  Read again on a customer reply, card action or requested update. A failure or missing saved
1105
1517
  plan needs the returned recovery action; never claim completion or approve an empty plan.
1106
1518
  Approval requires the current complete plan and total. Insufficient balance: offer a shorter
1107
1519
  video or top-up. “Not now” keeps the saved plan.
1520
+ 5. On the customer's yes, record it with \`video_project_upsert { brand_id, project_id, patch:
1521
+ { approve: { user_quote, total_credits } } }\`. Its reply says what comes next: the coworker
1522
+ gets the approval when it is making the video; otherwise you render it.
1108
1523
 
1109
1524
  Do not hand the customer a command to paste somewhere else.
1110
1525
 
@@ -1133,7 +1548,7 @@ and its usable delivery link.
1133
1548
  | Symptom | Cause | Fix |
1134
1549
  | --- | --- | --- |
1135
1550
  | Picker plus table | Ignored display_hint | Widget: one line; otherwise text_summary |
1136
- | Terminal loses formats | Confused cards with execution | Declare real shell capability and check renderer |
1551
+ | Agent with a shell hands off | Decided from the host, not the rule | Make it here; hand off only without a shell or a fixable toolchain |
1137
1552
  | Chat told it cannot start | Only local execution considered | Create once and delegate to coworker |
1138
1553
  | Worker ends without a plan | Assumed completion means ready | Follow actual failure/recovery state |
1139
1554
  | Two projects for one video | Recreated instead of resuming | Continue same project and task |
@@ -1174,10 +1589,10 @@ description: >
1174
1589
  gooseworks CLI) OR inside a GooseWorks workspace sandbox (canonical MCP tools + Bash, no CLI).
1175
1590
  Use for a client-side format project (created by goose-video), a template-remix project or a
1176
1591
  video batch. A copy-for-Claude command or project id must first be checked with
1177
- video_project_read. A hosted connector with no shell hands this same project to the GooseWorks coworker. To start a NEW video ad in chat,
1592
+ video_project_read. An agent that can't run shell commands on the customer's computer hands this same project to the GooseWorks coworker. To start a NEW video ad in chat,
1178
1593
  use goose-video first.
1179
1594
  category: ads
1180
- version: 0.6.1
1595
+ version: 0.7.0
1181
1596
  author: GooseWorks
1182
1597
  tags: [gooseworks, ads, video, remix, imessage, podcast, ugc, local-render, sandbox, byoa]
1183
1598
  ---
@@ -1186,9 +1601,16 @@ tags: [gooseworks, ads, video, remix, imessage, podcast, ugc, local-render, sand
1186
1601
 
1187
1602
  ${CUSTOMER_TALK}
1188
1603
 
1604
+ ${CLI_TOOL_BRIDGE}
1605
+
1606
+ ${videoEntryPreparation('goose-video-local')}
1607
+
1189
1608
  ## Chat hosts and cards
1190
1609
 
1191
- First perform the mandatory route check below. Then, without a shell, hand a verified template
1610
+ ${WHO_MAKES_THE_VIDEO}
1611
+
1612
+ First perform the mandatory route check below. Then, if you can't run shell commands on the
1613
+ customer's computer, hand a verified template
1192
1614
  project to \`goose_run_task { brand_id, project_id, message }\`, or a verified template batch to
1193
1615
  \`goose_run_task { brand_id, batch_id, message }\` (never send both ids). Keep task_id and the
1194
1616
  same project or batch. Generated custom children keep their separate make-custom-video flow.
@@ -1243,7 +1665,8 @@ ${ASSET_READINESS}
1243
1665
 
1244
1666
  ## Where am I running? (decide once, first)
1245
1667
 
1246
- Check in Bash, without printing any secret value:
1668
+ With no shell on the customer's computer at all, you don't render here: hand the project off
1669
+ (Chat hosts and cards above). Otherwise check in Bash, without printing any secret value:
1247
1670
 
1248
1671
  \`\`\`bash
1249
1672
  [ -n "$GW_MEDIA_PROXY_TOKEN" ] && echo sandbox || echo local
@@ -1406,7 +1829,7 @@ the skill. It's fire-and-forget, never counts against you, and never blocks your
1406
1829
  1. **Sandbox →** see "Running in a GooseWorks sandbox": ffmpeg + ffprobe (+ PIL on demand); no
1407
1830
  Chromium, so browser formats stop there.
1408
1831
  2. **CLI present →** run \`gooseworks doctor --no-browser\` for login, MCP, Node 18+, ffmpeg
1409
- with libx264 + libass, and ffprobe. This is common setup only. After fetching the selected
1832
+ with libx264, and ffprobe. Require libass only for a selected ASS caption route; caption-burn uses Pillow overlays and does not need it. This is common setup only. After fetching the selected
1410
1833
  capabilities in Step 2, check each browser renderer's actual launch before ANY paid
1411
1834
  ingredient. An unscoped \`gooseworks doctor\` checks only the calling folder's browser;
1412
1835
  it cannot certify a different fetched renderer.
@@ -1421,7 +1844,10 @@ the skill. It's fire-and-forget, never counts against you, and never blocks your
1421
1844
  **\`ghcr.io/gooseworks-ai/goose-video-render\`** (ffmpeg + ffprobe + Playwright Chromium baked
1422
1845
  in), mounting the project working directory. (Nested Docker is usually disabled inside
1423
1846
  managed sandboxes — treat this as an option, not a guarantee.)
1424
- 5. **None of the above works →** STOP and tell the user plainly, e.g.: *"Video rendering needs
1847
+ 5. **None of the above works →** on the customer's computer, hand the project to the GooseWorks
1848
+ coworker with \`goose_run_task\` (a \`needs_browser\` format can't be: offer another). Inside the
1849
+ coworker sandbox, or when the customer wants it made on their own machine, STOP and tell the
1850
+ user plainly, e.g.: *"Video rendering needs
1425
1851
  ffmpeg (and, for this format, a Playwright Chromium) on the machine running this agent. This
1426
1852
  environment doesn't have them and I can't install them here. Options: (a) enable Docker so I
1427
1853
  can use the goose-video-render image, (b) install ffmpeg + \`npx playwright install chromium\`,
@@ -1643,7 +2069,7 @@ the app's "N concepts" flow: one composer submission fans out into **N independe
1643
2069
  **Loop shape (ONE approval for the batch, isolated work per concept):**
1644
2070
  1. Run **Step 1 + Step 1.5 + Step 2 + Step 2.5 + Step 3-assemble** for EACH concept project (each
1645
2071
  has its own \`project_id\`, brief, \`GW_PROJECT_ID\` and \`working/\` folder — never cross-write
1646
- between concepts). The brand read (Step 1 item 3) and \`brand-rules.json\` (Step 1.7) are per
2072
+ between concepts). The brand read (Step 1 item 2) and \`brand-rules.json\` (Step 1.7) are per
1647
2073
  BRAND: do them once for the batch and copy the file into each concept's \`working/\`. The read is
1648
2074
  ~90K characters. Step 2.5's angle bank is shared per brand AND product: load or build it once, and
1649
2075
  give every concept whose angle is \`auto\` a DIFFERENT angle from that list, so the batch is N
@@ -1686,8 +2112,8 @@ written per-project; a batch just runs it N times with the shared approval gate
1686
2112
 
1687
2113
  The composer already collected the user's creative direction onto the project. **Read it and treat
1688
2114
  it as ground truth — it OVERRIDES the template recipe's defaults, and it REPLACES the clarifying
1689
- questions you would otherwise ask.** Only fall back to the recipe default (then, last, to asking)
1690
- for a field the brief leaves empty. Map the fields you WILL honor:
2115
+ questions you would otherwise ask.** For a field the brief leaves empty, use the Brain search's
2116
+ evidence brief first, then the recipe default, then (last) asking. Map the fields you WILL honor:
1691
2117
 
1692
2118
  - \`creative_brief.productName\` / \`.offer\` / \`.angle\` → the product, offer/code, and angle. Do
1693
2119
  **not** ask "which product / what offer / what angle" if these are set.
@@ -1705,20 +2131,24 @@ for a field the brief leaves empty. Map the fields you WILL honor:
1705
2131
  "also 1:1" option included).
1706
2132
  - \`polish_policy\` (\`standard\` | \`extra\`) → \`extra\` means spend the extra pass on QC/polish.
1707
2133
 
1708
- 2. \`catalog_fetch { type: "template", slug: <source_sample_id> }\` → the source video: \`media_url\`,
1709
- \`recipe\`, \`format\` (e.g. "podcast-skit", "imessage"), \`extracted_script\`, \`how_to\`, \`remix_spec\`.
1710
- 3. Brand gate: \`brand_read { brand_id, sections: ["summary", "kit", "products", "learnings"] }\`
2134
+ 2. Brand gate: \`brand_read { brand_id, sections: ["summary", "kit", "products", "learnings"] }\`
1711
2135
  (older clients: \`brand_get_context\` with the same sections). Ask for all four: the default
1712
2136
  leaves out the kit and the brand's saved rules, and a video made without them is off-brand.
1713
2137
  If the kit's \`researchStatus\` (or the brand's \`research_status\`) is \`complete\`, REUSE it —
1714
- never re-research. If not, run brand research first (\`catalog_fetch { type: "skill", slug:
1715
- "brand-research" }\`) and follow its stored-pack workflow. Only when that verified pack is
2138
+ never re-research. A pending status alone does not require research; use verified stored
2139
+ facts and ask only for an actual gap. If the context is empty or the customer requests
2140
+ research, load \`catalog_fetch { type: "skill", slug:
2141
+ "brand-research" }\` and follow its stored-pack workflow. Only when that verified pack is
1716
2142
  saved in the supported research workspace, finalize with
1717
2143
  \`brand_update { brand_id, patch: { finalize_research: true } }\`, then read the brand back.
1718
2144
  Never send raw research JSON through \`kit_patch\`: the public tool accepts only the existing
1719
2145
  \`video_lab\` asset slot there. If there is no verified stored pack, submit researched facts
1720
2146
  through typed \`patch.knowledge\` / \`patch.kit\` as pending agent proposals; do not pretend
1721
2147
  research is finalized or its proposals are approved. Then do Step 1.7 with verified facts.
2148
+ 3. For a new plan, \`catalog_fetch { type: "template", slug: <source_sample_id> }\` → the source
2149
+ video: \`media_url\`, \`recipe\`, \`format\` (e.g. "podcast-skit", "imessage"),
2150
+ \`extracted_script\`, \`how_to\`, \`remix_spec\`. For an approved resume, restore the recorded
2151
+ package and dependencies instead of fetching today's recipe over the saved plan.
1722
2152
 
1723
2153
  ### Step 1.6 — a remix of a FINISHED video (the project read has a \`remix\` block)
1724
2154
 
@@ -1747,8 +2177,9 @@ Its \`video_project_read\` returns a top-level \`remix\` block
1747
2177
 
1748
2178
  ### Step 1.7 — the brand rules file and the brand assets (every run, before any writing)
1749
2179
 
1750
- Write \`working/brand-rules.json\` from the Step 1 brand read. Every later step reads THIS file,
1751
- not your memory of the chat:
2180
+ Before new writing, write \`working/brand-rules.json\` from the Step 1 brand read. Preserve an
2181
+ approved run's saved rules on resume; reconcile an intentional rule change through the existing
2182
+ review gate. Every later step reads THIS file, not your memory of the chat:
1752
2183
 
1753
2184
  \`\`\`json
1754
2185
  {
@@ -1902,7 +2333,16 @@ of ad performance. The existing Step 3 review and approval remain unchanged.
1902
2333
  evidence and proof plan, not only its hook. Reuse the shared video-angle-bank.v1 from
1903
2334
  the brand's video-scripts workspace or this run. Prepare angle-context.json with the
1904
2335
  writer's preparation script with \`--brief working/script/creative-brief.json\` and use
1905
- its strict rule check before review. Save the sourced brief in the writer's documented
2336
+ its strict rule check before review. First run the fetched writer's free
2337
+ \`python3 <saved-writer-package>/scripts/verify_handoff.py --package-dir <saved-writer-package> --out working/script/writer-handoff-check.json\` check against those
2338
+ actual saved prepare/lint scripts. Retain the result and script hashes with the run.
2339
+ A provided writer package with a missing checker, failed check or unsupported flag
2340
+ is incompatible: stop this handoff and refresh the package on the same connection.
2341
+ Never use the provisional agent path after a provided package fails validation.
2342
+ Only an actual \`not_found\` for an optional writer permits the explicit provisional
2343
+ agent check below; a required writer remains blocked. Never silently remove the brief,
2344
+ strict check or new shape requirement to run an older parser.
2345
+ Save the sourced brief in the writer's documented
1906
2346
  shape: exact product/variant, buyer situation, supported mechanism, offer/CTA, constraints,
1907
2347
  delivery intent, source references, locked copy, applicable prior decisions and unknowns.
1908
2348
  New custom/template shapes set \`requires_creative_brief: true\`; both writer and critic
@@ -1917,16 +2357,20 @@ of ad performance. The existing Step 3 review and approval remain unchanged.
1917
2357
  a catalogue card alone cannot establish fit. An impossible essential visual blocks
1918
2358
  the proposal until repaired. Do not force a testimonial into every format.
1919
2359
  - **Research only what is missing.** Fetching the miner does not spend or rerun research.
1920
- Reuse current facts and prior evidence. If paid collection is needed, its permission
2360
+ Reuse current facts and prior evidence. The Brain search's evidence brief (citations, the
2361
+ state of each query and open gaps) is the first source for the brief's source references and
2362
+ prior decisions. If paid collection is needed, its permission
1921
2363
  rides in the existing choices round. A new product with no reviews can use verified
1922
2364
  facts and a feasible demo; do not require paid research merely to fill a quote quota.
1923
2365
  - **Choose eligible concepts only.** Take the strongest supported concept and validated
1924
2366
  hook into review, with up to two viable alternatives when the direction is open.
1925
2367
  Do not pad three concepts or select the highest-ranked rejected one. If all fail,
1926
2368
  repair within the brief and recipe before Step 3.
1927
- - **Unavailable writer or research.** Complete an explicit agent check of claim support,
1928
- recipe limits, visual feasibility and hook payoff. Record missing provenance and say
1929
- in the review when research is provisional. Never report a failed check as a pass.
2369
+ - **Optional writer not found or unavailable research.** Only when the optional writer
2370
+ returns \`not_found\`, complete an explicit agent check of claim support, recipe limits,
2371
+ visual feasibility and hook payoff. Record missing provenance and say in the review when
2372
+ research is provisional. A provided incompatible writer or another fetch error stops
2373
+ the handoff. Never report a failed check as a pass.
1930
2374
  - **The user's exact lines** remain verbatim; use report-only checks and raise material
1931
2375
  timing, claim or format conflicts without silently rewriting them.
1932
2376
  - **No spoken words.** Still fit the visual promise, reveal, cards and CTA to the silent
@@ -1938,9 +2382,14 @@ of ad performance. The existing Step 3 review and approval remain unchanged.
1938
2382
 
1939
2383
  Fetch \`video-production-harness\` and read its \`references/editorial-review.md\`,
1940
2384
  \`references/specialist-handoff.md\` and \`references/hook-compatibility.md\`, plus the
1941
- review/edit/polish/promote/wrap steps when used. Save the fetched version/content hash with
1942
- this run's capability records. These supply craft and evidence rules inside the template
1943
- flow; this entry's existing review, paid approval, storage and two-repair limit still apply.
2385
+ review/edit/polish/promote/wrap steps when used. Verify every named file is returned as
2386
+ nonempty text before relying on that package. A version/content hash alone cannot establish
2387
+ that the required guides were published. Missing guides block new template creative work
2388
+ before project writes or paid previews; resolve the package on this same connection. Keep
2389
+ an unchanged approved resume on its recorded package and approvals.
2390
+ Save the fetched version/content hash with this run's capability records. These supply
2391
+ craft and evidence rules inside the template flow; this entry's existing review, paid
2392
+ approval, storage and two-repair limit still apply.
1944
2393
  Do not turn a template into a custom project or import the custom host's extra gates.
1945
2394
 
1946
2395
  Before executing a fetched recipe atom, pass the exact brief/script revision, scene/beat IDs,
@@ -2049,8 +2498,8 @@ ingredient here is only a genuinely separate SOURCE clip the format needs (e.g.
2049
2498
  “Not now” keeps the plan. In a coworker sandbox end the turn after saving the review set;
2050
2499
  the customer's approval arrives as the next message.
2051
2500
  Record the explicit yes with \`video_project_upsert { brand_id, project_id, patch: { approve:
2052
- { user_quote: "<their exact words>", total_credits: <the saved total> } } }\` before rendering.
2053
- A single project requires recorded approval too; never treat an absent approval as permission.
2501
+ { user_quote: "<their exact words>", total_credits: <the saved total> } } }\` before rendering;
2502
+ its reply says what comes next. A single project requires recorded approval too; never treat an absent approval as permission.
2054
2503
  Changes require an updated saved review, clearing prior approval, and approval of the new
2055
2504
  total. One yes authorizes the remaining approved chain. On insufficient_balance start
2056
2505
  nothing; offer a shorter video or top-up. Remove batch concepts with patch.concepts remove:true,
@@ -2060,7 +2509,8 @@ ingredient here is only a genuinely separate SOURCE clip the format needs (e.g.
2060
2509
 
2061
2510
  1. **Open the render row FIRST** — right after recording the Step 3 approval, before any paid
2062
2511
  generation (if this returns \`approval_required\`, the approval is missing or was cleared: go
2063
- back to Step 3 and ask in this chat, don't retry):
2512
+ back to Step 3 and ask in this chat, don't retry; if it returns \`coworker_makes_it\`, the video
2513
+ was handed to the GooseWorks coworker, so don't render it: the coworker is making it):
2064
2514
  \`${exports.RENDER_ROW_TOOL} { brand_id, project_id, ${exports.RENDER_OPEN_ARGS} }\` (no \`dry_run\`; returns
2065
2515
  \`render_id\`) → keep \`render_id\`, then mark it running:
2066
2516
  \`${exports.RENDER_ROW_TOOL} { brand_id, project_id, ${exports.RENDER_UPDATE_KEY}: { render_id, status: "running",
@@ -2069,8 +2519,25 @@ ingredient here is only a genuinely separate SOURCE clip the format needs (e.g.
2069
2519
  about start / blocked / complete.
2070
2520
  After EVERY progress callback inspect stop. If true, start no new paid step; record
2071
2521
  status:"stopped" with a plain note and report what is kept and credits used. SPEND_CAP_REACHED
2072
- stops the same way; raising the cap requires patch.approve scope:"raise_cap" and the customer's
2073
- words. Send render.steps with the same neutral names each time and a live count only in the
2522
+ stops the same way. Never raise a video's budget on your own judgment. Raise it only when a paid
2523
+ step was refused with SPEND_CAP_REACHED or the progress card offers "Finish it" or a choice past the budget.
2524
+ Then read the project and say cost.raise_quote in one line: "Finishing needs up to <by_credits>
2525
+ more credits, so your budget goes from <from_credits> to <to_credits>. OK?" Only after the
2526
+ customer's yes to that number: patch.approve { scope:"raise_cap", total_credits: <to_credits>,
2527
+ user_quote: "<their exact words>" }. total_credits is the new budget itself, never credits to
2528
+ add; do not send raise_cap_credits for one video. If they pick a smaller choice past the
2529
+ budget, send that choice's new budget as total_credits (never above to_credits). Sending the same
2530
+ total_credits again changes nothing (already_raised: carry on). An earlier yes, or "I don't care
2531
+ about the cost", is not approval of a new budget. If cost.raise_quote is null because the plan was saved
2532
+ again since its approval (plan_status is not "approved"), show the plan with its total and
2533
+ approve it again; otherwise the approved budget already covers the work: carry on, do not ask.
2534
+ After a raise, open a new render that reuses the saved pieces. A refused raise changes nothing:
2535
+ cap_raise_not_needed means carry on within the budget, or, when it says the plan was saved again,
2536
+ show the plan with its total and approve the plan again; cap_raise_total_required means read the
2537
+ project again and send total_credits equal to cost.raise_quote.to_credits after the customer
2538
+ approves that number; cap_raise_changed or cap_raise_too_large mean read the project again and
2539
+ show the current cost.raise_quote. Custom videos never use raise_cap.
2540
+ Send render.steps with the same neutral names each time and a live count only in the
2074
2541
  current detail; use render.choices when blocked.
2075
2542
  2. Now generate every PAID piece you showed as a prompt in Step 3 — the AI stills/video, lipsync
2076
2543
  clips, voice, music — through the media proxies (below), each from its approved prompt, with
@@ -2398,6 +2865,10 @@ ${STORED_FOOTAGE_GUIDANCE}`;
2398
2865
  * never regenerated it and `install` / `update` / login-refresh never wrote or
2399
2866
  * refreshed it on a user's machine. Moving the body here makes the registry the
2400
2867
  * one source: one command emits all four entry skills.
2868
+ *
2869
+ * QA-26 / VWR14: chat apps fetch it with `catalog_fetch`, so it names only the
2870
+ * tools the connector lists (`photos_generate` / `photos_read` / `photos_update`,
2871
+ * `brand_read`, `brand_update`, `job_get`).
2401
2872
  */
2402
2873
  function getGooseProductPhotosSkillContent() {
2403
2874
  return `---
@@ -2413,7 +2884,7 @@ description: >
2413
2884
  references a product to photograph. Unlike goose-ads (ad creative) this produces clean PRODUCT
2414
2885
  photos that can then feed the ad workflow.
2415
2886
  category: ads
2416
- version: 0.2.0
2887
+ version: 0.3.0
2417
2888
  author: GooseWorks
2418
2889
  tags: [gooseworks, ads, product-photos, photoshoot, product, ecommerce, studio, lifestyle, on-model]
2419
2890
  ---
@@ -2421,28 +2892,31 @@ tags: [gooseworks, ads, product-photos, photoshoot, product, ecommerce, studio,
2421
2892
  # GooseWorks Product Photos — branded product photography
2422
2893
 
2423
2894
  The GooseWorks Product Photos skill. You **pick a brand + product and submit one generation**;
2424
- the **backend** runs the whole pipeline (compose the shot prompt → generate on \`gpt_image_2\` →
2425
- judge for product fidelity → auto-retry a few times for free) and stores the results. You do NOT
2895
+ the **backend** runs the whole pipeline (compose the shot prompt → generate → judge for product
2896
+ fidelity → auto-retry a few times for free) and stores the results. You do NOT
2426
2897
  generate images, call a model, or manage files — this is the exact same workflow the Product
2427
2898
  Photos studio uses, so the skill and the app can never drift. The point is to **enrich a brand's
2428
2899
  usable product imagery** — approved photos join the brand kit and can then feed the ad workflow
2429
2900
  (\`goose-ads\`).
2430
2901
 
2902
+ It shoots a **physical catalog product** (apparel, beauty, consumer goods). A software
2903
+ screenshot or an app mockup is not a product photo: that is an image edit, not this skill.
2904
+
2431
2905
  ${CUSTOMER_TALK}
2432
2906
 
2433
- ## Prerequisite — the GooseWorks MCP server is REQUIRED
2907
+ ${CLI_TOOL_BRIDGE}
2434
2908
 
2435
- Everything goes through the \`mcp__gooseworks__*\` tools. If they are not available, **stop and
2436
- tell the user to run \`gooseworks install --claude --mcp\`** (and restart Claude Code). There is no
2437
- HTTP/file fallback.
2909
+ ${connectorPrerequisite('photos_generate')}
2438
2910
 
2439
2911
  ## Start from the brand context — don't re-ask what it already answers
2440
2912
 
2441
2913
  If the \`gooseworks\` router handed you brand context, USE IT. If you were invoked directly, call
2442
- \`brand_read\` yourself first. It answers most of the setup questions below, so **do not ask
2443
- the user for them**:
2914
+ \`brand_read { brand_id, sections: ["summary","kit","products","learnings"] }\` yourself first, then
2915
+ \`knowledge_search { brand_id, query: "<the shoot, in the user's words>" }\` for saved rules and past
2916
+ feedback (an archived photo's reason is saved as a \`dont\` rule). They answer most of the setup
2917
+ questions below, so **do not ask the user for them**:
2444
2918
 
2445
- - **Which product?** — the context's \`products[]\` are the real catalog entries. Offer them; never
2919
+ - **Which product?** — the context's \`products\` are the real catalog entries. Offer them; never
2446
2920
  invent a product or ask the user to describe one you can already see.
2447
2921
  - **What does it look like / what is it made of?** — grounded in the product's stored images and
2448
2922
  description. Never guess a material, colorway, or silhouette.
@@ -2454,79 +2928,90 @@ the user for them**:
2454
2928
  Ask only for the genuinely open choices: the shot \`category\`, how many photos, quality, and
2455
2929
  whether a human model is wanted (which needs explicit consent — see the rules).
2456
2930
 
2457
- ## Identity & credits
2931
+ ## Credits — state the total, then get a yes
2458
2932
 
2459
- - One agent-scoped token authenticates the tools; they resolve your org automatically. Never
2460
- print the token. (You may pass an optional \`target\` to operate on a specific agent/org, exactly
2461
- as the other GooseWorks tools; omit it to use your pinned scope.)
2462
- - **Credits are handled by the backend.** \`generate_product_photos\` reserves the estimated cost up
2463
- front and bills only the photos that pass the judge — **automatic retries are free**, and a photo
2464
- the judge can't get right (\`flagged\`) is shown but **never billed**. Call
2465
- \`estimate_product_photos\` first to quote the cost; \`get_ad_credits\` shows the balance.
2933
+ - One token authenticates the tools and resolves your org; never print it. Omit \`target\`.
2934
+ - **Quote first.** Call \`photos_generate\` with the exact arguments you will submit plus
2935
+ \`dry_run: true\`. It reserves nothing and returns \`creditsPerOutput\` and \`totalCredits\`. Pass
2936
+ \`count\` and \`quality\` explicitly in both calls so the quote matches the run.
2937
+ - **Nothing paid runs without the user's explicit yes in this chat, given after you state that
2938
+ credit total.** Then submit the same call without \`dry_run\`.
2939
+ - The submit reserves the quoted credits and bills only the photos that pass the judge:
2940
+ **automatic retries are free**, and a photo the judge can't get right (\`flagged\`) is shown but
2941
+ **never billed**. The balance is \`credits.available_credits\` from \`account_whoami\`. If the
2942
+ wallet is short, say so plainly with the total and the balance, and stop.
2466
2943
 
2467
2944
  ## The tools
2468
2945
 
2469
2946
  **Pick the brand + product**
2470
- - \`list_ad_brands\` — the user's ad brands (get a \`brand_id\`; also carries \`slug\`).
2471
- - \`list_brand_products { brand_id, search?, page?, page_size? }\` — the brand's imported products.
2472
- Pick a \`product_id\` to shoot. \`search\` matches name / type / variant / SKU.
2473
- - \`import_product { brand_id, kind, url, product_name? }\` — import a product if it isn't in the
2474
- catalog yet. \`kind\` is \`product_url\` (a single product page), \`shopify_store\` (a store URL →
2475
- imports the catalog), or \`image_url\` (a direct image; requires \`product_name\`). Returns an import
2476
- row with an \`id\`; if its \`status\` isn't \`complete\`, poll \`get_product_import\` until it is, then
2477
- \`list_brand_products\` to find the new product. (File uploads aren't available over MCP — use a URL.)
2478
- - \`get_product_import { import_id }\` — poll an import until \`status\` is \`complete\` or \`failed\`.
2947
+ - \`brand_read\` with no \`brand_id\` — the user's brands (each row's \`id\` is the \`brand_id\`).
2948
+ - \`brand_read { brand_id, sections: ["products"], products_query: "<name>" }\` — the brand's
2949
+ imported products in \`products.items\`; a product's \`id\` is the \`product_id\` to shoot.
2950
+ \`products_query\` matches name / type / variant / SKU / description.
2951
+ - Import a product that isn't in the catalog yet (free):
2952
+ \`brand_update { brand_id, patch: { products: [{ import_url, import_kind, name? }] } }\`.
2953
+ \`import_kind\` is \`product_url\` (a single product page), \`shopify_store\` (a store URL → imports
2954
+ the catalog), or \`image_url\` (a direct image; also needs \`name\`). It returns \`jobs[]\`: poll
2955
+ \`job_get { job_id, kind: "product_import" }\` until it finishes, then read the products again.
2956
+ To add a photo the user attached to an existing product:
2957
+ \`media_upload { brand_id, scope: "product", scope_id: <product_id>, kind: "reference", source: { type: "bytes", filename, content_base64 } }\`.
2479
2958
 
2480
2959
  **Generate**
2481
- - \`estimate_product_photos { count, quality? }\` — cost preview (per-photo + total credits). \`count\`
2482
- is 1, 2, 4, or 8; \`quality\` is \`low\` | \`medium\` | \`high\` (default \`medium\`). Reserves nothing.
2483
- - \`generate_product_photos { brand_id, product_id, variant_id?, category, controls?, prompt?,
2484
- count?, quality?, reference_image_urls?, attestation_accepted? }\` — **the one call that makes
2485
- photos.** \`category\` is \`apparel\` | \`beauty\` | \`cpg\` (seeds sensible scene/framing defaults).
2486
- Omit \`controls\` to use the category preset; pass \`prompt\` as free-text steering **added on top of**
2487
- the settings (it doesn't replace them). Returns a generation with an \`id\` **immediately** — poll
2488
- \`get_product_photo_generation\` until done, then read each \`outputs[].final_image_url\`.
2960
+ - \`photos_generate { brand_id, product_id, variant_id?, category, controls?, prompt?, count,
2961
+ quality, reference_image_urls?, attestation_accepted?, dry_run? }\` — **the one call that makes
2962
+ photos** (and, with \`dry_run: true\`, its free quote). \`category\` is \`apparel\` | \`beauty\` |
2963
+ \`cpg\` (seeds sensible scene/framing defaults). \`count\` is 1, 2, 4, or 8; \`quality\` is
2964
+ \`low\` | \`medium\` | \`high\`. Omit \`controls\` to use the category preset; pass \`prompt\` as
2965
+ free-text steering **added on top of** the settings (it doesn't replace them). Returns the
2966
+ generation with its \`id\` **immediately**.
2489
2967
  **If you request a human model** (\`controls.model.presence\` is not \`none\`) you MUST pass
2490
2968
  \`attestation_accepted: true\` to confirm the user has the rights for model imagery.
2491
- - \`get_product_photo_generation { generation_id }\` — poll until \`status\` is \`complete\`,
2492
- \`partial_failure\`, or \`failed\`. Each \`outputs[]\` entry has its own \`status\` and, once ready, a
2969
+ - \`photos_read { brand_id, generation_id }\` — poll until \`status\` is \`complete\`,
2970
+ \`partial_failure\`, or \`failed\` (\`job_get { job_id: <generation id>, kind: "photo_generation" }\`
2971
+ works too). Each \`outputs[]\` entry has its own \`id\`, \`status\` and, once ready, a
2493
2972
  \`final_image_url\`. A \`flagged\` output is the best attempt but wasn't billed.
2494
2973
 
2495
2974
  **Use the results**
2496
- - \`list_product_photos { brand_id, archived? }\` — the brand's generated photos (\`archived: false\`
2497
- = active, \`true\` = archived).
2498
- - \`approve_product_photo { output_id }\` — approve a photo: links it to the product and makes it
2499
- available in the **brand kit**, so \`goose-ads\` can use it. **Photos are not used anywhere until
2500
- approved.**
2501
- - \`archive_product_photo { output_id, reason? }\` — archive a photo; archived photos are **excluded**
2502
- from ad generation.
2975
+ - \`photos_read { brand_id, archived?, product_id?, status? }\` — the brand's generated photos
2976
+ (\`archived: false\` = active, \`true\` = archived).
2977
+ - \`photos_update { brand_id, output_id, action: "approve" }\` — approve a photo: links it to the
2978
+ product and makes it available in the **brand kit**, so \`goose-ads\` can use it. **Photos are
2979
+ not used anywhere until approved.**
2980
+ - \`photos_update { brand_id, output_id, action: "archive", reason? }\` — archive a photo; archived
2981
+ photos are **excluded** from ad generation, and the reason is saved as a \`dont\` rule.
2503
2982
 
2504
2983
  ## Workflow — shoot a product
2505
2984
 
2506
- 1. **Load the brand context** (\`brand_read\`, or reuse what the router passed you) and
2507
- **resolve the brand + product.** \`list_ad_brands\` → \`brand_id\`. \`list_brand_products\` → pick a
2508
- \`product_id\` from the catalog you already know about. If the product genuinely isn't there,
2509
- \`import_product\` (poll \`get_product_import\`).
2510
- 2. **Quote the cost.** \`estimate_product_photos { count, quality }\` → tell the user credits.
2511
- 3. **Generate.** \`generate_product_photos { brand_id, product_id, category, count, quality, prompt? }\`.
2512
- Build \`prompt\` from the brand's voice/positioning you already have — don't interview the user for it.
2513
- Returns a generation \`id\` right away.
2514
- 4. **Poll.** \`get_product_photo_generation { generation_id }\` until terminal; hand back each
2985
+ 1. **Load the brand context** (\`brand_read\` + \`knowledge_search\`, or reuse what the router passed
2986
+ you) and **resolve the brand + product.** Pick a \`product_id\` from the catalog you already
2987
+ know about. If the product genuinely isn't there, import it and poll the import.
2988
+ 2. **Quote the cost.** \`photos_generate\` with \`dry_run: true\` and the exact \`brand_id\`,
2989
+ \`product_id\`, \`category\`, \`count\` and \`quality\` → tell the user the credit total and wait
2990
+ for their explicit yes.
2991
+ 3. **Generate.** The same \`photos_generate\` call without \`dry_run\` (add \`prompt\` built from the
2992
+ brand's voice/positioning you already have — don't interview the user for it). Returns a
2993
+ generation \`id\` right away.
2994
+ 4. **Poll.** \`photos_read { brand_id, generation_id }\` every ~20-30s until terminal; show each
2515
2995
  \`final_image_url\`.
2516
- 5. **Approve the keepers.** Show the results and let the user pick; \`approve_product_photo\` the ones
2517
- they'd publish (that's what puts them in the brand kit for ads), \`archive_product_photo\` the rest.
2996
+ 5. **Approve the keepers.** Show the results and let the user pick; \`photos_update\` with
2997
+ \`action: "approve"\` the ones they'd publish (that's what puts them in the brand kit for ads),
2998
+ \`action: "archive"\` the rest.
2518
2999
 
2519
3000
  ## Rules
2520
3001
 
3002
+ - **Connector tools only** — a missing tool means the GooseWorks connection is stale: ask the
3003
+ user to reconnect or refresh GooseWorks. Never send a chat-app user to a terminal or a CLI
3004
+ install.
2521
3005
  - **Never invent product facts.** The backend grounds the shot on the product's real images; don't
2522
3006
  describe a product you can't see.
2523
3007
  - **Use the brand context instead of interviewing the user.** Product, audience, voice, positioning,
2524
3008
  logo/colors/fonts all come from \`brand_read\` / the brand kit. Ask only for the shot
2525
3009
  category, count, quality, and model consent.
2526
- - **Ask before spending.** Quote the estimate and confirm \`count\` / \`quality\` before
2527
- \`generate_product_photos\` — it reserves credits.
3010
+ - **Ask before spending.** State the dry-run credit total and get the user's explicit yes before
3011
+ the real \`photos_generate\` — it reserves credits.
2528
3012
  - **Poll, don't re-submit.** A generation that's still \`running\` is not stuck; re-submitting
2529
- double-bills. Only a \`failed\` generation should be retried.
3013
+ double-bills. A \`failed\` generation may still hold \`flagged\` photos (shown, never billed):
3014
+ show those first, and run again only after a new quote and the user's yes.
2530
3015
  - **Model imagery needs consent.** Only set a human model when the user asks, and pass
2531
3016
  \`attestation_accepted: true\`.
2532
3017
  - **Approval is the hand-off to ads.** Remind the user that only **approved** photos reach the brand
@@ -2535,6 +3020,6 @@ whether a human model is wanted (which needs explicit consent — see the rules)
2535
3020
  }
2536
3021
  /** Thin GooseWorks connection to the catalog-published production harness. */
2537
3022
  function getMakeCustomVideoSkillContent() {
2538
- return custom_video_skill_1.CUSTOM_VIDEO_ADAPTER_CONTENT.replace("\n# Agent version\n", `\n# Agent version\n\n${CUSTOMER_TALK}\n\n${ENVIRONMENT_IDENTITY}\n\n${ASSET_READINESS}\n`) + STORED_FOOTAGE_GUIDANCE;
3023
+ return custom_video_skill_1.CUSTOM_VIDEO_ADAPTER_CONTENT.replace("\n# Agent version\n", `\n# Agent version\n\n${CUSTOMER_TALK}\n\n${CLI_TOOL_BRIDGE}\n\n${videoEntryPreparation('make-custom-video')}\n\n${ENVIRONMENT_IDENTITY}\n\n${ASSET_READINESS}\n`) + STORED_FOOTAGE_GUIDANCE;
2539
3024
  }
2540
3025
  //# sourceMappingURL=master-skill.js.map