@koda-sl/baker-cli 0.316.1 → 0.320.0

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
@@ -6136,7 +6136,7 @@ This CLI is designed for AI agent consumption. Key patterns:
6136
6136
 
6137
6137
  ## Internal Notes
6138
6138
 
6139
- - **0.316.0**: `baker flows show <slug>` reports **destination drift** — what the HubSpot form a Form submits to has changed under it — as `data.drift[]` plus a `DESTINATION DRIFT` hint carrying the repair as a ready-to-run command. It is the first time a Runtime can see this at all: the findings live in Baker and the HubSpot form id the repair needs is inside an encrypted config, so a Company Workspace could read neither. The classification is the one the dashboard badge reads (`classifyDrift` in `@baker/api`), so the two surfaces cannot disagree about whether a Form is healthy; a failed check answers `drift: null`, which is "unknown", never "clean". `baker flows map <slug> --side-effect <id> --hubspot-form <guid> --prune-missing [--write]` is the repair: it drops only the mappings whose destination field is absent from the form's live field list, names each in `dropped`, needs no `--map`, and refuses to run without a readable destination rather than guessing. Reach for it instead of `--replace`, which drops every mapping the call does not name.
6139
+ - **0.316.1**: `baker flows show <slug>` reports **destination drift** — what the HubSpot form a Form submits to has changed under it — as `data.drift[]` plus a `DESTINATION DRIFT` hint carrying the repair as a ready-to-run command. It is the first time a Runtime can see this at all: the findings live in Baker and the HubSpot form id the repair needs is inside an encrypted config, so a Company Workspace could read neither. The classification is the one the dashboard badge reads (`classifyDrift` in `@baker/api`), so the two surfaces cannot disagree about whether a Form is healthy; a failed check answers `drift: null`, which is "unknown", never "clean". `baker flows map <slug> --side-effect <id> --hubspot-form <guid> --prune-missing [--write]` is the repair: it drops only the mappings whose destination field is absent from the form's live field list, names each in `dropped`, needs no `--map`, and refuses to run without a readable destination rather than guessing. Reach for it instead of `--replace`, which drops every mapping the call does not name.
6140
6140
 
6141
6141
  - **0.103.0**: `baker actions ...` commands now type their `/api/actions/...` request/response payloads from the shared `@baker/api` contract package (also consumed by the backend) instead of hand-written local interfaces. No command, flag, or output-shape changes.
6142
6142
  - **0.104.0**: `baker actions tags ...` commands now type their `/api/actions/tags...` request/response payloads from the shared `@baker/api` contract package instead of hand-written local interfaces. No command, flag, or output-shape changes.
@@ -6243,6 +6243,18 @@ Pushing to `main` with changes in `packages/cli/` triggers the GitHub Actions wo
6243
6243
  - **0.306.6**: the render review moved onto OpenRouter, where every other model call on this platform already goes. It was calling Gemini directly on the stated grounds that the watch pass uploads the finished mp4 inline and OpenRouter had no route that takes one — it does: a `video_url` content part accepts a base64 data URL, verified end to end (HTTP 200, video tokens counted, the model describing the clip it was sent). That one wrong premise had cost a hand-maintained price table in the backend, token counts round-tripping through the run record to be priced there, a second credential in every Runtime, and a model id pinned to `gemini-3.7-flash` so the table's key would not drift — which is also why the review had quietly stayed off `gemini-3.8-flash`. Now the reply carries `usage.cost`, the Runtime relays the gateway's figure instead of computing one, and the models float (`~google/gemini-flash-latest`, `~google/gemini-pro-latest`). The key it reads is `OPENROUTER_API_KEY`; `GEMINI_API_KEY` is still shipped for HyperFrames' own `snapshot --describe` pass, which is a third-party tool and reads that name.
6244
6244
  - **0.306.0**: review fixes across the avatar and ad paths. **A silent clip is 5 seconds again** — the 8s speaking floor was applied to every `studio animate` call, not just ones that cast an avatar, so plain b-roll was lengthened by three seconds and billed for them. **`scaffold-ad` validates the mark it will actually draw**: the logo gates read the path the spec named rather than the one `fetchClientLogo` had just put there, so a fetched mark either skipped both checks or was refused as unreadable. **The render review is told where the ad is set, who it is for and what the script says** — read off the blueprint beside the canvas; without them the watch pass judged an accent against nothing and still graded the verdict as blocking. **A beat is "too short" by its LINE, not by the clip floor** — the old test needed a 25-word clause, so it fired on all three beats of this command's own documented example. **A voiceover stays a voiceover** — a phrase nobody is shown speaking was being folded into the presenter's clip and said on camera, words that shot was never generated for; the fold now applies only where there is a mouth for the words to contradict. `--voice-id` / `--voice-description` are gone from `baker avatars create` (0.252.0 said so; the flags were still declared and still sent), `end_card.cta` is documented as having no default, and the dead Veo/Omni content-block fallbacks are removed.
6245
6245
 
6246
+ - **0.319.0**: **A source that refuses everything it receives now says so — and can be read backwards.** A rejection is a per-event verdict, so it reached neither field a source row carries: `lastError` means "the record could not be read", `lastArrivalAt` means "something was counted", and a source refusing every delivery was indistinguishable from one nothing had ever been sent to. Its card said exactly that, in a sentence telling the reader to go and move a record in HubSpot to see the first event — while three of one client's events had been refused for two days, with six ad-platform sends hanging off conversions that were never counted. `sources` now carries `lastRefusal` and says it in words: "5 changes arrived and could not be counted: its name has a character Baker cannot count an event under. Rename it and the next change counts." Cleared by the next delivery that counts.
6247
+
6248
+ **And `--backfill --source <id> --since <date> [--write]` is the way back.** The refused events cannot be replayed from Baker — the arrival log masks every value on the way in and keeps 48 hours — but HubSpot still holds them, so this reads its property history and counts every moment the trigger *came to hold* since `--since`, each at the instant HubSpot says it happened. That instant is half the event's id, so a backfilled event and the one the journal would have delivered are the **same** event: running it twice counts nothing twice, and it cannot collide with the live read. It is not the thing a source deliberately refuses to do — the rule that holds is *never invent a moment*, and every moment here is HubSpot's own: a record that merely satisfies the trigger today is still not an event today, and one already in the value before the window is not counted at its edge. Candidates are "changed since", never "satisfies the trigger now", so a deal that entered the stage on Monday and moved on by Wednesday — the exact event a client is missing — is found. Dry unless `--write`, which is refused inside a chat: an event inside the last 72 hours is uploaded to its sends within minutes, and that is real budget. It also answers the other half of the same gap, which has nothing to do with any incident: **every** HubSpot event counts forwards only, so a client connecting on Monday has had no funnel until Monday's deals reached the end of a sales cycle.
6249
+
6250
+ - **0.318.0**: **An event arrives, and then it is populated.** A CRM posts a deal naming the person only by the CRM's own contact id — HubSpot's default mapping is `external_id: {{id}}` — and that event used to reach Meta and be **skipped by Google Ads, LinkedIn and X**, all three of which want an email or a phone before they will take it. The address book had that person's address the whole time; nothing asked. It could only be opened at an email or a phone digest, and could only hand back click ids and Meta cookies, so the three commonest shapes a server event arrives in — named by a contact id, by the click that produced it, or by the browser (`user_data.baker_id`) — resolved to nothing at all. Now ingest opens it at **every** key the event names anybody by and stamps back everything the row is missing: the other contact digests, the click ids, both Meta cookies, and an `identity_hash` when the event arrived without one, so it lands on that person's timeline rather than beside it. It crosses one pivot to get there — a contact id reaches the email directly, and the click is one hop from *the email* rather than from an id no ad platform has ever seen. Three rules hold it: the hub guard now covers the seed and the pivot as well as the returned value (a shared browser resolving to somebody's email stamps the wrong person, which is worse than stamping nobody); a click is time-guarded and a person is not, because an address first seen a week later is the same address; and a browser id is never inferred, since `anon_id` is the session join and a server event did not happen in a session. Measured on one store, 16 of 17 `deal_won` events carried `external_id` and nothing else.
6251
+
6252
+ **And an event name may be written in the language its marketer speaks.** `event_name` was `[A-Za-z0-9 ._-]` — ASCII, which is to say English — so a HubSpot source called "Matrícula Realizada" had every event it ever raised refused as `invalid_name`, for ever, with a reason whose only honest reading was "use a different alphabet". Any letter and any digit in any script is a name now; `:` and `,` stay out because the vocabulary around the name is built on them. Every door that takes a name now knows the rule — `sourceNameSchema` on the wire, `--name` in this CLI before the request leaves, the Create button on the dashboard, and the mutation itself — because the one that was missing is the one somebody types into: a source whose name the ingest door cannot accept is refused **when it is named**, rather than counting nothing behind a card that reads "Counting"; and the name HubSpot's trigger builder *suggests* goes through the same rule, so "Deal value > 1000" is offered as "Deal value is greater than 1000" instead of as a source that could never count anything.
6253
+
6254
+ - **0.317.0**: **`baker analytics feed` says what happened to a delivery.** Every upload and every Form action on the feed read **"On the way"**, for ever — the settlement carrying the platform's verdict was joined only when the caller had already asked for uploads, so the default read never brought an outcome back at all. It is joined always now. Three things ship with it. `--person <anon-id>` takes a browser id off any row and returns that whole person — resolved through the identity graph rather than filtered by cookie, so the deal their CRM posted, which carries no cookie, is on the list beside the page view that named them. `--everything` keeps the rows the feed normally folds away. And **`--sent-to meta` no longer hides the Meta rows**: a delivery our own server made is logged under the dispatcher's name (`metaCapi`, `ga4Mp`), the filter only matched the tag's name and the send's, and the three platforms that deliver *only* server-side returned nothing at all while their rows sat in the unfiltered list. All four spellings of a destination — the tag, the sender, the send's upload and the name a person is shown — now resolve through one table in `@baker/api`, held against `computeSigForTag`, `DISPATCHERS` and the store's own copy by `destinations.parity.test.ts`.
6255
+
6256
+ Underneath: **four click ids Baker had no column for** — Reddit's `rdt_cid`, Pinterest's `epik`, Snapchat's `ScCid` and Google's `dclid`. Reddit, Pinterest and Snapchat were already ad platforms here, emitting campaign macros and drawing their own logos, with nothing to attribute a click by; the scaffold had been seeding Pinterest's `_epik` cookie and offering `tracking.pinterest_click_id` to Forms the whole time, with nowhere to store it. They reach `analytics` output, `matchedBy` and the person panel like every other click id, and a URL parameter is now read case-insensitively — Snapchat spells its `ScCid`, which an exact match never saw.
6257
+
6246
6258
  ### Testing a pre-release in sandboxes
6247
6259
 
6248
6260
  ```bash