gooseworks 0.3.10 → 0.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -93,6 +93,31 @@ npx gooseworks logout
93
93
 
94
94
  Deletes `~/.gooseworks/credentials.json`.
95
95
 
96
+ ### `whoami`
97
+
98
+ Show which account you're currently signed in as. Handy when you have multiple
99
+ GooseWorks accounts and need to confirm the active one before running a command.
100
+ Reads local credentials only — no network call.
101
+
102
+ ```bash
103
+ npx gooseworks whoami
104
+ npx gooseworks whoami --json # machine-readable output
105
+ ```
106
+
107
+ Output:
108
+ ```
109
+ Signed in as you@example.com
110
+ Scope: user
111
+ Agent: 2e32bd49-…
112
+ API base: https://api.gooseworks.ai
113
+ ```
114
+
115
+ To switch accounts, run `gooseworks logout` then `gooseworks login`.
116
+
117
+ > The GooseWorks MCP server exposes the same identity check as the `whoami` tool
118
+ > (email + the exact agent/org the token is pinned to), separate from
119
+ > `list_accessible_scopes`, which lists *every* workspace you can reach.
120
+
96
121
  ### `search`
97
122
 
98
123
  Search the GooseWorks skill catalog.
@@ -0,0 +1,11 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * `gooseworks whoami` — show which account the CLI is currently signed in as.
4
+ *
5
+ * Reads `~/.gooseworks/credentials.json` only (no network call), so it works
6
+ * offline and reflects exactly what the CLI + its MCP registration will
7
+ * authenticate as. This is the answer to "I have multiple accounts — which one
8
+ * is active right now?"; use `gooseworks login` / `logout` to switch.
9
+ */
10
+ export declare const whoamiCommand: Command;
11
+ //# sourceMappingURL=whoami.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,SA0CtB,CAAC"}
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.whoamiCommand = void 0;
37
+ const commander_1 = require("commander");
38
+ const credentials_1 = require("../auth/credentials");
39
+ const logger = __importStar(require("../utils/logger"));
40
+ /**
41
+ * `gooseworks whoami` — show which account the CLI is currently signed in as.
42
+ *
43
+ * Reads `~/.gooseworks/credentials.json` only (no network call), so it works
44
+ * offline and reflects exactly what the CLI + its MCP registration will
45
+ * authenticate as. This is the answer to "I have multiple accounts — which one
46
+ * is active right now?"; use `gooseworks login` / `logout` to switch.
47
+ */
48
+ exports.whoamiCommand = new commander_1.Command('whoami')
49
+ .description('Show which GooseWorks account you are signed in as')
50
+ .option('--json', 'Output raw JSON')
51
+ .action((opts) => {
52
+ const creds = (0, credentials_1.getCredentials)();
53
+ if (!creds) {
54
+ if (opts.json) {
55
+ console.log(JSON.stringify({ logged_in: false }, null, 2));
56
+ }
57
+ else {
58
+ logger.error('Not logged in. Run "gooseworks login" first.');
59
+ }
60
+ process.exit(1);
61
+ }
62
+ const scope = creds.scope_type ?? 'agent';
63
+ if (opts.json) {
64
+ console.log(JSON.stringify({
65
+ logged_in: true,
66
+ email: creds.email,
67
+ scope_type: scope,
68
+ agent_id: creds.agent_id,
69
+ default_agent_id: creds.default_agent_id ?? creds.agent_id,
70
+ api_base: creds.api_base,
71
+ mcp_server_url: creds.mcp_server_url ?? null,
72
+ }, null, 2));
73
+ return;
74
+ }
75
+ logger.success(`Signed in as ${creds.email}`);
76
+ logger.info(`Scope: ${scope}`);
77
+ logger.info(`Agent: ${creds.agent_id}`);
78
+ logger.info(`API base: ${creds.api_base}`);
79
+ if (creds.mcp_server_url) {
80
+ logger.info(`MCP server: ${creds.mcp_server_url}`);
81
+ }
82
+ logger.info('Run "gooseworks logout" then "gooseworks login" to switch accounts.');
83
+ });
84
+ //# sourceMappingURL=whoami.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yCAAoC;AACpC,qDAAqD;AACrD,wDAA0C;AAE1C;;;;;;;GAOG;AACU,QAAA,aAAa,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,oDAAoD,CAAC;KACjE,MAAM,CAAC,QAAQ,EAAE,iBAAiB,CAAC;KACnC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;IACf,MAAM,KAAK,GAAG,IAAA,4BAAc,GAAE,CAAC;IAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7D,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,IAAI,OAAO,CAAC;IAC1C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ;YACE,SAAS,EAAE,IAAI;YACf,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC,QAAQ;YAC1D,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,IAAI;SAC7C,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,gBAAgB,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IAC9C,MAAM,CAAC,IAAI,CAAC,eAAe,KAAK,EAAE,CAAC,CAAC;IACpC,MAAM,CAAC,IAAI,CAAC,eAAe,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC7C,MAAM,CAAC,IAAI,CAAC,eAAe,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,eAAe,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,qEAAqE,CAAC,CAAC;AACrF,CAAC,CAAC,CAAC"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ const commander_1 = require("commander");
5
5
  const install_1 = require("./commands/install");
6
6
  const login_1 = require("./commands/login");
7
7
  const logout_1 = require("./commands/logout");
8
+ const whoami_1 = require("./commands/whoami");
8
9
  const update_1 = require("./commands/update");
9
10
  const credits_1 = require("./commands/credits");
10
11
  const search_1 = require("./commands/search");
@@ -24,6 +25,7 @@ program
24
25
  program.addCommand(install_1.installCommand);
25
26
  program.addCommand(login_1.loginCommand);
26
27
  program.addCommand(logout_1.logoutCommand);
28
+ program.addCommand(whoami_1.whoamiCommand);
27
29
  program.addCommand(update_1.updateCommand);
28
30
  program.addCommand(credits_1.creditsCommand);
29
31
  program.addCommand(search_1.searchCommand);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,yCAAoC;AACpC,gDAAoD;AACpD,4CAAgD;AAChD,8CAAkD;AAClD,8CAAkD;AAClD,gDAAoD;AACpD,8CAAkD;AAClD,4CAAgD;AAChD,wCAA4C;AAC5C,0CAA8C;AAC9C,sDAA0D;AAC1D,8CAAkD;AAClD,gDAAoD;AACpD,8CAAkD;AAClD,uCAAuC;AAEvC,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAC9B,OAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,yDAAyD,CAAC;KACtE,OAAO,CAAC,IAAA,oBAAU,GAAE,CAAC,CAAC;AAEzB,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,oBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,oBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,gBAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,kBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,8BAAiB,CAAC,CAAC;AACtC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAElC,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,yCAAoC;AACpC,gDAAoD;AACpD,4CAAgD;AAChD,8CAAkD;AAClD,8CAAkD;AAClD,8CAAkD;AAClD,gDAAoD;AACpD,8CAAkD;AAClD,4CAAgD;AAChD,wCAA4C;AAC5C,0CAA8C;AAC9C,sDAA0D;AAC1D,8CAAkD;AAClD,gDAAoD;AACpD,8CAAkD;AAClD,uCAAuC;AAEvC,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAC9B,OAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,yDAAyD,CAAC;KACtE,OAAO,CAAC,IAAA,oBAAU,GAAE,CAAC,CAAC;AAEzB,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,oBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,oBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,gBAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,kBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,8BAAiB,CAAC,CAAC;AACtC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAElC,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"master-skill.d.ts","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,oDAAoD;AACpD,wBAAgB,cAAc,IAAI,UAAU,EAAE,CAM7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CA0L9C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAsUhD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAsSlD"}
1
+ {"version":3,"file":"master-skill.d.ts","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,oDAAoD;AACpD,wBAAgB,cAAc,IAAI,UAAU,EAAE,CAM7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CA0L9C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAsUhD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CA8WlD"}
@@ -583,8 +583,17 @@ tags: [gooseworks, ads, video, remix, imessage, local-render, byoa]
583
583
 
584
584
  You produce **video** ad creative on the user's OWN machine and sync the result back to the
585
585
  GooseWorks app over MCP. This document is the **runtime contract** (auth, credits, the media
586
- proxies, data I/O, the review gate). A separate **recipe skill** — fetched per format — tells
587
- you *what to make*; read both, and this doc wins on any conflict about the environment.
586
+ proxies, data I/O, the review gate). A separate **recipe skill** — fetched per format — tells you
587
+ *what to make* (the pieces, prompts, models, order of assembly).
588
+
589
+ **Division of authority — read both, but when they disagree THIS doc wins on the environment AND the
590
+ review/approval flow.** The recipe governs WHAT to make; this doc governs WHEN you pause, generate,
591
+ and spend. In particular: a recipe may spell out a **multi-phase, multi-gate** flow — "generate the
592
+ still [GATE] → approve → author the prompt [GATE] → approve → render [GATE] → approve", several
593
+ separate pauses. **Do NOT run it that way.** Collapse every one of those gates into the single
594
+ **review-once** flow below: one review set, one approval (Step 3). Take the recipe's pieces, prompts
595
+ and models; ignore its intermediate pauses. This is the exact contradiction that confused past runs
596
+ (GOOSE-2542) — there is no ambiguity: review-once wins.
588
597
 
589
598
  You run inside the user's own Claude Code session (they pasted an instruction with a project
590
599
  id). The app NEVER runs you — it is the viewer + review surface; you are the renderer.
@@ -653,14 +662,18 @@ says to shell out, use the MCP equivalent:
653
662
  stays the project-relative \`...render-file?path=working/final.mp4\` — the route re-prepends the
654
663
  prefix itself. Always verify with \`get_download_url\` on the FULL \`agent-config/...\` path (must
655
664
  be non-empty; curl it for HTTP 200) BEFORE marking the render complete.
656
- - Media generation (FAL / ElevenLabs) is billed to the agent through the GooseWorks proxies.
657
- \`submit_render { kind: "full" }\` debits **1 ad credit at row creation** — so sequence it LAST
658
- (render + verify a good MP4 first), and never re-submit on a guess (that double-bills). Call
659
- \`get_ad_credits\` first; the user can check \`gooseworks credits\`.
665
+ - Media generation (FAL / ElevenLabs) through the GooseWorks proxies is the **REAL spend** billed
666
+ to the agent per call as you generate (Step 4). \`submit_render { kind: "full" }\` additionally
667
+ debits **1 nominal ad credit when the render ROW is opened** (a bookkeeping fee, NOT the render's
668
+ true cost) so open it only once you actually have a rendered master (Step 4.1/4.2), and never
669
+ re-submit on a guess (that double-bills). The final-video QC gate (Step 4.3) then sits between
670
+ that master and PINNING it. Call \`get_ad_credits\` first; the user can check \`gooseworks credits\`.
660
671
 
661
672
  ## Step 1 — resolve the project, source, brand
662
673
 
663
- 1. \`get_ad_project { project_id }\` → keep \`brand_id\`, \`source_sample_id\`, \`name\`, \`status\`.
674
+ 1. \`get_ad_project { project_id }\` → keep \`brand_id\`, \`source_sample_id\`, \`name\`, \`status\`, and
675
+ the **top-level** \`app_url\` + \`brand_url\` (returned alongside \`project\`, NOT inside it) — these
676
+ are the links you hand the user for the in-app review (Step 3) and the final delivery (Step 5).
664
677
  2. \`get_ad_template { template_id: source_sample_id }\` → the source video: \`media_url\`,
665
678
  \`recipe\`, \`format\` (e.g. "imessage"), \`extracted_script\`, \`how_to\`, \`remix_spec\`.
666
679
  3. Brand gate: \`get_brand_kit { brand_id }\`. If \`researchStatus\` is \`complete\`, REUSE it —
@@ -696,22 +709,55 @@ Playwright resolve, and point the recorder's \`NODE_PATH\` at it.
696
709
  > recipe does not yet carry \`atoms\` / \`instructions\` still hold the legacy \`recipe.thread\` payload;
697
710
  > migrate them to this shape (capabilities + instructions in the DB) — do not reintroduce a CLI map.
698
711
 
699
- ## Step 3 — prepare ALL the ingredients, then review ONCE (always, before any paid render)
700
-
701
- This is a **review-once** flow: prepare every ingredient the video needs, show the whole set to
702
- the user in the app, get ONE approval, then render. Never render before approval, and don't drip
703
- ingredients out one at a time.
704
-
705
- 1. **Generate every ingredient the format needs not just the script.** For an iMessage video
706
- that's typically: the **script** (the bubble thread), the **image(s)** shown in the conversation
707
- (one or more), and the **end card**. Richer templates add more (hook frame, background, product
708
- shots, music bed…). Read the recipe for the exact ingredient list. Generate the visuals NOW
709
- (media proxies / recipe), and \`get_upload_url\` each preview asset to the project folder
710
- \`agent-config/brands/<brand_slug>/projects/<project_id>/working/review/<name>\` (the same
711
- path-prefix rule as final publish a bare \`working/review/<name>\` won't render in the panel).
712
- In \`script_drafts\`, set each ingredient's \`path\` to the project-relative \`working/review/<name>\`.
713
- You may ask the user a couple of clarifying questions about the generation first if the recipe
714
- calls for it (angle, which product, offer/code) batch them, then prepare everything.
712
+ ## Step 3 — assemble the review set, then get ONE approval in the app (before the expensive render)
713
+
714
+ This is a **review-once** flow: put the whole review set in the app, get ONE approval, then run the
715
+ expensive render + any remaining paid work end-to-end. Never spend on the expensive render before
716
+ approval, and don't drip pieces out one at a time and re-pause.
717
+
718
+ **What goes in the reviewshow the REAL cheap pieces, PROMPT only the expensive render.** Split
719
+ every piece three ways by cost, NOT just "free vs paid":
720
+ - **FREE** (an iMessage / Apple-Notes HTML mockup, a text/CTA line rendered locally, no proxy
721
+ call) generate NOW and mirror the real asset.
722
+ - **CHEAP paid** a single still/image, the creator/avatar frame, the end card, a short voiceover
723
+ or music bed (each costs cents → roughly **≤ 100 credits**) → **generate these NOW too** and
724
+ mirror the real asset. The few credits buy a real review: the user SEES the actual creator face
725
+ and end card and HEARS the VO, instead of judging a prompt. **This OVERRIDES any recipe rule that
726
+ says to gate ALL paid calls** only the expensive render below is gated.
727
+ - **EXPENSIVE paid** the video take / final AI render (hundreds of credits) → do NOT generate.
728
+ Put its **exact prompt/spec** (+ ref image URLs) in the tile. This is the ONE thing approved as a
729
+ prompt (you can't preview a hundreds-of-credits video for free); it's generated only in Step 4.
730
+
731
+ **The expensive render's exact prompt must be in the panel BEFORE you ask for approval** — so a
732
+ single "go" runs it (plus any remaining paid work) without re-pausing mid-run.
733
+
734
+ **Show every cost in CREDITS, never dollars.** 1 credit = $0.01 and media generations bill at
735
+ provider-cost × 1.2, so **credits ≈ round-up(provider-$ × 120)** per generation, plus a flat
736
+ **200-credit base per video**. Convert any $ figures to credits and show ONLY credits to the user —
737
+ never print a "$…" amount.
738
+
739
+ **Never assemble/stitch the finished video for review.** The review is of the individual pieces (or
740
+ their prompts) — never a "full cascade" / "approved cut" clip. Building the whole video before
741
+ approval defeats the gate (the user opens the review to an already-finished video) and wastes the
742
+ render (GOOSE-2542). The full video is assembled ONLY in Step 4, after approval. A \`video\`
743
+ ingredient here is only a genuinely separate SOURCE clip the format needs (e.g. supplied b-roll).
744
+
745
+ 1. **Assemble every piece the format needs — not just the script.** Read the recipe for the exact
746
+ list. For an iMessage video that's the **script** (bubble thread), the **conversation image(s)**,
747
+ and the **end card**; richer templates add a hook frame, background, product shots, music bed, a
748
+ creator/avatar, a voiceover… For each piece, decide FREE / CHEAP-paid / EXPENSIVE-paid (above):
749
+ - **FREE or CHEAP paid** (≤ ~100 credits — HTML mockups, a still, the creator frame, the end
750
+ card, a short VO/music bed) → generate it now and \`get_upload_url\` the asset to the project
751
+ folder \`agent-config/brands/<brand_slug>/projects/<project_id>/working/review/<name>\` (same
752
+ path-prefix rule as final publish — a bare \`working/review/<name>\` won't render in the panel);
753
+ set that piece's \`path\` in \`script_drafts\` to the project-relative \`working/review/<name>\`.
754
+ - **EXPENSIVE paid** (the video take / final render, hundreds of credits) → do NOT generate. Put
755
+ the **exact prompt/spec** (and any ref image URLs) in the tile's \`text\` / \`subtitle\` so the
756
+ user reviews what will be spent on. No \`path\` yet — it's generated in Step 4.
757
+ Include the **estimated cost in CREDITS** (never dollars) of the cheap pieces already generated +
758
+ the pending render, so the user approves knowing the total spend. You may batch a couple of
759
+ clarifying questions first if the recipe calls for it (angle, which product, offer/code), then
760
+ assemble everything.
715
761
  2. **Mirror the whole ingredient set for review** — \`update_ad_project_script { project_id,
716
762
  script_drafts, script }\`. \`script_drafts\` is a structured payload of **container-tagged
717
763
  ingredients** so the app renders each piece the right way:
@@ -726,21 +772,34 @@ ingredients out one at a time.
726
772
  for the podcast shape, or pass the readable \`script\` string).
727
773
  \`path\` = \`working/review/<name>\` (upload the preview asset first via \`get_upload_url\`); \`url\`
728
774
  works too. **Label every ingredient** ("Hook image", "End card", "Voiceover", "Background
729
- music", "HER"). This writes NO render and costs NO credits — it populates the review panel.
730
- 3. **STOP and ask the user to approve the ingredients in THIS Claude Code session.** Do not render
731
- until they say go. If they want changes, regenerate the affected ingredient, call
732
- \`update_ad_project_script\` again, and re-ask. Only AFTER approval do Step 4.
775
+ music", "HER"). The \`update_ad_project_script\` call itself writes no render and costs no credits
776
+ (the cheap pieces you already generated above have their own small cost) it just populates the
777
+ review panel.
778
+ 3. **STOP — the review happens in the APP's review panel, NOT in this chat.** You've mirrored the
779
+ ingredients (3.2); now hand the user the project's \`app_url\` (from \`get_ad_project\`) and tell
780
+ them to review the pieces there and hit **"Approve & render"**. That button gives them a short
781
+ message to paste back into this session — THAT is your go-ahead. Do NOT paste the
782
+ script/ingredients into the chat for a thumbs-up, and do NOT render until that approval comes
783
+ back from the app. If they want changes (via the app's comments or here), regenerate the
784
+ affected ingredient, call \`update_ad_project_script\` again, tell them it's refreshed in the
785
+ app, and wait for a fresh approval. Only AFTER the app approval do Step 4. A single approval
786
+ authorises the WHOLE remaining chain — generate every paid piece, render, self-QC, publish —
787
+ with NO further pauses (that is exactly why every paid prompt must already be in the panel).
733
788
 
734
789
  ## Step 4 — render locally, report stages, publish
735
790
 
736
- 1. Render per the recipe (Playwright record ffmpeg stitch \`mix-master\` audio). Generate any
737
- hook / background / end-card assets through the media proxies (below).
791
+ 1. Now generate every PAID piece you showed as a prompt in Step 3 — the AI stills/video, voice,
792
+ music, the end-card render through the media proxies (below), each from its approved prompt.
793
+ Then assemble per the recipe (Playwright record where needed → ffmpeg stitch → \`mix-master\`
794
+ audio).
738
795
  2. Open the row LAST: \`submit_render { project_id, kind: "full" }\` → keep \`render_id\`, then
739
796
  \`update_render_status { render_id, status: "running" }\`. The render row tracks status only
740
797
  (queued / running / complete / failed) — narrate fine-grained progress with
741
798
  \`append_project_message\` instead.
742
- 3. **MANDATORY final-video review gate — review EVERY finished master before \`set_final_render\`,
743
- whatever the format (UGC or not).** The render credit is already spent (\`submit_render\` in 4.2);
799
+ 3. **MANDATORY final-video QC gate — YOU review EVERY finished master before \`set_final_render\`,
800
+ whatever the format (UGC or not).** This is your own automated quality check, separate from the
801
+ user's Step-3 approval — it does not go back to the user. The render row is already open (its
802
+ nominal credit spent, \`submit_render\` in 4.2);
744
803
  this gate stands between a rendered master and PINNING/publishing it, so a bad render never gets
745
804
  set as final. A master that looks fine on a still can still have a mis-voiced word, a caption
746
805
  drifting off its line, a beat out of order, or a deformation — review the actual VIDEO, not
@@ -844,10 +903,23 @@ path. (\`fal-storage-proxy\` may 404 depending on the install; don't block on it
844
903
 
845
904
  - **MCP + ffmpeg + Playwright required** — run \`gooseworks doctor\` in Phase 0; stop with the
846
905
  exact fix it prints if anything is ✗.
847
- - **Prepare ALL ingredients first** (script + every visual: image(s) + end card + whatever else
848
- the template needs), mirror the whole set with \`update_ad_project_script\`, and get the user's
849
- approval in-session BEFORE rendering always (review-once).
850
- - **submit_render LAST**; \`output_url\` = the durable render-file URL, never a CDN URL.
906
+ - **Assemble the whole review set first**, mirror it with \`update_ad_project_script\`, and get the
907
+ user's approval **in the app's review panel** (the "Approve & render" button) BEFORE the expensive
908
+ render — never ask for a thumbs-up in this chat (review-once, in-app).
909
+ - **Show the REAL cheap pieces; PROMPT only the expensive render.** Generate the FREE + CHEAP-paid
910
+ pieces (≤ ~100 credits — stills, creator frame, end card, short VO/music) and mirror the real
911
+ assets; put ONLY the expensive video take/render in the panel as its exact prompt. That prompt
912
+ must be in the panel before you ask to approve, so a single "go" runs the render + any remaining
913
+ paid work (→ QC → publish) with no re-pausing.
914
+ - **Costs in CREDITS, never dollars.** credits ≈ round-up(provider-$ × 120) per generation + a flat
915
+ 200-credit base per video; never show a "$…" figure to the user.
916
+ - **Never assemble the full video before approval.** The review shows the
917
+ individual PIECES, never the finished cut (or their prompts) — not a
918
+ stitched/composited cut; do not add a "full cascade" / finished-video clip
919
+ as a review ingredient (GOOSE-2542). The assembled video is produced only in
920
+ Step 4.
921
+ - **submit_render only after the master is rendered** (Step 4.2), never on a guess; \`output_url\` =
922
+ the durable render-file URL, never a CDN URL.
851
923
  - **Always pass \`project_id\` on media-proxy calls** (fal / ElevenLabs) so the credits attribute
852
924
  to this ad project — that's what lets the user see per-project spend in the app.
853
925
  - **Verify a real, non-empty MP4** (watch it) before marking the render complete.
@@ -1 +1 @@
1
- {"version":3,"file":"master-skill.js","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":";;AA4BA,wCAMC;AAWD,sDA0LC;AAiBD,0DAsUC;AAcD,8DAsSC;AAv1BD,oDAAoD;AACpD,SAAgB,cAAc;IAC5B,OAAO;QACL,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,qBAAqB,EAAE,EAAE;QACxD,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE;QACzD,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,yBAAyB,EAAE,EAAE;KAC9D,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,qBAAqB;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwLR,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,uBAAuB;IACrC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoUR,CAAC;AACF,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAgB,yBAAyB;IACvC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoSR,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"master-skill.js","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":";;AA4BA,wCAMC;AAWD,sDA0LC;AAiBD,0DAsUC;AAcD,8DA8WC;AA/5BD,oDAAoD;AACpD,SAAgB,cAAc;IAC5B,OAAO;QACL,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,qBAAqB,EAAE,EAAE;QACxD,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE;QACzD,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,yBAAyB,EAAE,EAAE;KAC9D,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,qBAAqB;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwLR,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,uBAAuB;IACrC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoUR,CAAC;AACF,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAgB,yBAAyB;IACvC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4WR,CAAC;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gooseworks",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "description": "GooseWorks CLI — give your coding agent real data tools",
5
5
  "bin": {
6
6
  "gooseworks": "./dist/index.js"
@@ -18,8 +18,17 @@ tags: [gooseworks, ads, video, remix, imessage, local-render, byoa]
18
18
 
19
19
  You produce **video** ad creative on the user's OWN machine and sync the result back to the
20
20
  GooseWorks app over MCP. This document is the **runtime contract** (auth, credits, the media
21
- proxies, data I/O, the review gate). A separate **recipe skill** — fetched per format — tells
22
- you *what to make*; read both, and this doc wins on any conflict about the environment.
21
+ proxies, data I/O, the review gate). A separate **recipe skill** — fetched per format — tells you
22
+ *what to make* (the pieces, prompts, models, order of assembly).
23
+
24
+ **Division of authority — read both, but when they disagree THIS doc wins on the environment AND the
25
+ review/approval flow.** The recipe governs WHAT to make; this doc governs WHEN you pause, generate,
26
+ and spend. In particular: a recipe may spell out a **multi-phase, multi-gate** flow — "generate the
27
+ still [GATE] → approve → author the prompt [GATE] → approve → render [GATE] → approve", several
28
+ separate pauses. **Do NOT run it that way.** Collapse every one of those gates into the single
29
+ **review-once** flow below: one review set, one approval (Step 3). Take the recipe's pieces, prompts
30
+ and models; ignore its intermediate pauses. This is the exact contradiction that confused past runs
31
+ (GOOSE-2542) — there is no ambiguity: review-once wins.
23
32
 
24
33
  You run inside the user's own Claude Code session (they pasted an instruction with a project
25
34
  id). The app NEVER runs you — it is the viewer + review surface; you are the renderer.
@@ -88,14 +97,18 @@ says to shell out, use the MCP equivalent:
88
97
  stays the project-relative `...render-file?path=working/final.mp4` — the route re-prepends the
89
98
  prefix itself. Always verify with `get_download_url` on the FULL `agent-config/...` path (must
90
99
  be non-empty; curl it for HTTP 200) BEFORE marking the render complete.
91
- - Media generation (FAL / ElevenLabs) is billed to the agent through the GooseWorks proxies.
92
- `submit_render { kind: "full" }` debits **1 ad credit at row creation** — so sequence it LAST
93
- (render + verify a good MP4 first), and never re-submit on a guess (that double-bills). Call
94
- `get_ad_credits` first; the user can check `gooseworks credits`.
100
+ - Media generation (FAL / ElevenLabs) through the GooseWorks proxies is the **REAL spend** billed
101
+ to the agent per call as you generate (Step 4). `submit_render { kind: "full" }` additionally
102
+ debits **1 nominal ad credit when the render ROW is opened** (a bookkeeping fee, NOT the render's
103
+ true cost) so open it only once you actually have a rendered master (Step 4.1/4.2), and never
104
+ re-submit on a guess (that double-bills). The final-video QC gate (Step 4.3) then sits between
105
+ that master and PINNING it. Call `get_ad_credits` first; the user can check `gooseworks credits`.
95
106
 
96
107
  ## Step 1 — resolve the project, source, brand
97
108
 
98
- 1. `get_ad_project { project_id }` → keep `brand_id`, `source_sample_id`, `name`, `status`.
109
+ 1. `get_ad_project { project_id }` → keep `brand_id`, `source_sample_id`, `name`, `status`, and
110
+ the **top-level** `app_url` + `brand_url` (returned alongside `project`, NOT inside it) — these
111
+ are the links you hand the user for the in-app review (Step 3) and the final delivery (Step 5).
99
112
  2. `get_ad_template { template_id: source_sample_id }` → the source video: `media_url`,
100
113
  `recipe`, `format` (e.g. "imessage"), `extracted_script`, `how_to`, `remix_spec`.
101
114
  3. Brand gate: `get_brand_kit { brand_id }`. If `researchStatus` is `complete`, REUSE it —
@@ -131,22 +144,55 @@ Playwright resolve, and point the recorder's `NODE_PATH` at it.
131
144
  > recipe does not yet carry `atoms` / `instructions` still hold the legacy `recipe.thread` payload;
132
145
  > migrate them to this shape (capabilities + instructions in the DB) — do not reintroduce a CLI map.
133
146
 
134
- ## Step 3 — prepare ALL the ingredients, then review ONCE (always, before any paid render)
135
-
136
- This is a **review-once** flow: prepare every ingredient the video needs, show the whole set to
137
- the user in the app, get ONE approval, then render. Never render before approval, and don't drip
138
- ingredients out one at a time.
139
-
140
- 1. **Generate every ingredient the format needs not just the script.** For an iMessage video
141
- that's typically: the **script** (the bubble thread), the **image(s)** shown in the conversation
142
- (one or more), and the **end card**. Richer templates add more (hook frame, background, product
143
- shots, music bed…). Read the recipe for the exact ingredient list. Generate the visuals NOW
144
- (media proxies / recipe), and `get_upload_url` each preview asset to the project folder
145
- `agent-config/brands/<brand_slug>/projects/<project_id>/working/review/<name>` (the same
146
- path-prefix rule as final publish a bare `working/review/<name>` won't render in the panel).
147
- In `script_drafts`, set each ingredient's `path` to the project-relative `working/review/<name>`.
148
- You may ask the user a couple of clarifying questions about the generation first if the recipe
149
- calls for it (angle, which product, offer/code) batch them, then prepare everything.
147
+ ## Step 3 — assemble the review set, then get ONE approval in the app (before the expensive render)
148
+
149
+ This is a **review-once** flow: put the whole review set in the app, get ONE approval, then run the
150
+ expensive render + any remaining paid work end-to-end. Never spend on the expensive render before
151
+ approval, and don't drip pieces out one at a time and re-pause.
152
+
153
+ **What goes in the reviewshow the REAL cheap pieces, PROMPT only the expensive render.** Split
154
+ every piece three ways by cost, NOT just "free vs paid":
155
+ - **FREE** (an iMessage / Apple-Notes HTML mockup, a text/CTA line rendered locally, no proxy
156
+ call) generate NOW and mirror the real asset.
157
+ - **CHEAP paid** a single still/image, the creator/avatar frame, the end card, a short voiceover
158
+ or music bed (each costs cents → roughly **≤ 100 credits**) → **generate these NOW too** and
159
+ mirror the real asset. The few credits buy a real review: the user SEES the actual creator face
160
+ and end card and HEARS the VO, instead of judging a prompt. **This OVERRIDES any recipe rule that
161
+ says to gate ALL paid calls** only the expensive render below is gated.
162
+ - **EXPENSIVE paid** the video take / final AI render (hundreds of credits) → do NOT generate.
163
+ Put its **exact prompt/spec** (+ ref image URLs) in the tile. This is the ONE thing approved as a
164
+ prompt (you can't preview a hundreds-of-credits video for free); it's generated only in Step 4.
165
+
166
+ **The expensive render's exact prompt must be in the panel BEFORE you ask for approval** — so a
167
+ single "go" runs it (plus any remaining paid work) without re-pausing mid-run.
168
+
169
+ **Show every cost in CREDITS, never dollars.** 1 credit = $0.01 and media generations bill at
170
+ provider-cost × 1.2, so **credits ≈ round-up(provider-$ × 120)** per generation, plus a flat
171
+ **200-credit base per video**. Convert any $ figures to credits and show ONLY credits to the user —
172
+ never print a "$…" amount.
173
+
174
+ **Never assemble/stitch the finished video for review.** The review is of the individual pieces (or
175
+ their prompts) — never a "full cascade" / "approved cut" clip. Building the whole video before
176
+ approval defeats the gate (the user opens the review to an already-finished video) and wastes the
177
+ render (GOOSE-2542). The full video is assembled ONLY in Step 4, after approval. A `video`
178
+ ingredient here is only a genuinely separate SOURCE clip the format needs (e.g. supplied b-roll).
179
+
180
+ 1. **Assemble every piece the format needs — not just the script.** Read the recipe for the exact
181
+ list. For an iMessage video that's the **script** (bubble thread), the **conversation image(s)**,
182
+ and the **end card**; richer templates add a hook frame, background, product shots, music bed, a
183
+ creator/avatar, a voiceover… For each piece, decide FREE / CHEAP-paid / EXPENSIVE-paid (above):
184
+ - **FREE or CHEAP paid** (≤ ~100 credits — HTML mockups, a still, the creator frame, the end
185
+ card, a short VO/music bed) → generate it now and `get_upload_url` the asset to the project
186
+ folder `agent-config/brands/<brand_slug>/projects/<project_id>/working/review/<name>` (same
187
+ path-prefix rule as final publish — a bare `working/review/<name>` won't render in the panel);
188
+ set that piece's `path` in `script_drafts` to the project-relative `working/review/<name>`.
189
+ - **EXPENSIVE paid** (the video take / final render, hundreds of credits) → do NOT generate. Put
190
+ the **exact prompt/spec** (and any ref image URLs) in the tile's `text` / `subtitle` so the
191
+ user reviews what will be spent on. No `path` yet — it's generated in Step 4.
192
+ Include the **estimated cost in CREDITS** (never dollars) of the cheap pieces already generated +
193
+ the pending render, so the user approves knowing the total spend. You may batch a couple of
194
+ clarifying questions first if the recipe calls for it (angle, which product, offer/code), then
195
+ assemble everything.
150
196
  2. **Mirror the whole ingredient set for review** — `update_ad_project_script { project_id,
151
197
  script_drafts, script }`. `script_drafts` is a structured payload of **container-tagged
152
198
  ingredients** so the app renders each piece the right way:
@@ -161,21 +207,34 @@ ingredients out one at a time.
161
207
  for the podcast shape, or pass the readable `script` string).
162
208
  `path` = `working/review/<name>` (upload the preview asset first via `get_upload_url`); `url`
163
209
  works too. **Label every ingredient** ("Hook image", "End card", "Voiceover", "Background
164
- music", "HER"). This writes NO render and costs NO credits — it populates the review panel.
165
- 3. **STOP and ask the user to approve the ingredients in THIS Claude Code session.** Do not render
166
- until they say go. If they want changes, regenerate the affected ingredient, call
167
- `update_ad_project_script` again, and re-ask. Only AFTER approval do Step 4.
210
+ music", "HER"). The `update_ad_project_script` call itself writes no render and costs no credits
211
+ (the cheap pieces you already generated above have their own small cost) it just populates the
212
+ review panel.
213
+ 3. **STOP — the review happens in the APP's review panel, NOT in this chat.** You've mirrored the
214
+ ingredients (3.2); now hand the user the project's `app_url` (from `get_ad_project`) and tell
215
+ them to review the pieces there and hit **"Approve & render"**. That button gives them a short
216
+ message to paste back into this session — THAT is your go-ahead. Do NOT paste the
217
+ script/ingredients into the chat for a thumbs-up, and do NOT render until that approval comes
218
+ back from the app. If they want changes (via the app's comments or here), regenerate the
219
+ affected ingredient, call `update_ad_project_script` again, tell them it's refreshed in the
220
+ app, and wait for a fresh approval. Only AFTER the app approval do Step 4. A single approval
221
+ authorises the WHOLE remaining chain — generate every paid piece, render, self-QC, publish —
222
+ with NO further pauses (that is exactly why every paid prompt must already be in the panel).
168
223
 
169
224
  ## Step 4 — render locally, report stages, publish
170
225
 
171
- 1. Render per the recipe (Playwright record ffmpeg stitch `mix-master` audio). Generate any
172
- hook / background / end-card assets through the media proxies (below).
226
+ 1. Now generate every PAID piece you showed as a prompt in Step 3 — the AI stills/video, voice,
227
+ music, the end-card render through the media proxies (below), each from its approved prompt.
228
+ Then assemble per the recipe (Playwright record where needed → ffmpeg stitch → `mix-master`
229
+ audio).
173
230
  2. Open the row LAST: `submit_render { project_id, kind: "full" }` → keep `render_id`, then
174
231
  `update_render_status { render_id, status: "running" }`. The render row tracks status only
175
232
  (queued / running / complete / failed) — narrate fine-grained progress with
176
233
  `append_project_message` instead.
177
- 3. **MANDATORY final-video review gate — review EVERY finished master before `set_final_render`,
178
- whatever the format (UGC or not).** The render credit is already spent (`submit_render` in 4.2);
234
+ 3. **MANDATORY final-video QC gate — YOU review EVERY finished master before `set_final_render`,
235
+ whatever the format (UGC or not).** This is your own automated quality check, separate from the
236
+ user's Step-3 approval — it does not go back to the user. The render row is already open (its
237
+ nominal credit spent, `submit_render` in 4.2);
179
238
  this gate stands between a rendered master and PINNING/publishing it, so a bad render never gets
180
239
  set as final. A master that looks fine on a still can still have a mis-voiced word, a caption
181
240
  drifting off its line, a beat out of order, or a deformation — review the actual VIDEO, not
@@ -279,10 +338,23 @@ path. (`fal-storage-proxy` may 404 depending on the install; don't block on it
279
338
 
280
339
  - **MCP + ffmpeg + Playwright required** — run `gooseworks doctor` in Phase 0; stop with the
281
340
  exact fix it prints if anything is ✗.
282
- - **Prepare ALL ingredients first** (script + every visual: image(s) + end card + whatever else
283
- the template needs), mirror the whole set with `update_ad_project_script`, and get the user's
284
- approval in-session BEFORE rendering always (review-once).
285
- - **submit_render LAST**; `output_url` = the durable render-file URL, never a CDN URL.
341
+ - **Assemble the whole review set first**, mirror it with `update_ad_project_script`, and get the
342
+ user's approval **in the app's review panel** (the "Approve & render" button) BEFORE the expensive
343
+ render — never ask for a thumbs-up in this chat (review-once, in-app).
344
+ - **Show the REAL cheap pieces; PROMPT only the expensive render.** Generate the FREE + CHEAP-paid
345
+ pieces (≤ ~100 credits — stills, creator frame, end card, short VO/music) and mirror the real
346
+ assets; put ONLY the expensive video take/render in the panel as its exact prompt. That prompt
347
+ must be in the panel before you ask to approve, so a single "go" runs the render + any remaining
348
+ paid work (→ QC → publish) with no re-pausing.
349
+ - **Costs in CREDITS, never dollars.** credits ≈ round-up(provider-$ × 120) per generation + a flat
350
+ 200-credit base per video; never show a "$…" figure to the user.
351
+ - **Never assemble the full video before approval.** The review shows the
352
+ individual PIECES, never the finished cut (or their prompts) — not a
353
+ stitched/composited cut; do not add a "full cascade" / finished-video clip
354
+ as a review ingredient (GOOSE-2542). The assembled video is produced only in
355
+ Step 4.
356
+ - **submit_render only after the master is rendered** (Step 4.2), never on a guess; `output_url` =
357
+ the durable render-file URL, never a CDN URL.
286
358
  - **Always pass `project_id` on media-proxy calls** (fal / ElevenLabs) so the credits attribute
287
359
  to this ad project — that's what lets the user see per-project spend in the app.
288
360
  - **Verify a real, non-empty MP4** (watch it) before marking the render complete.