@nextcommerce/campaigns-os 1.50.0 → 1.53.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +474 -0
  2. package/agents/claude/CLAUDE.md +4 -2
  3. package/agents/codex/AGENTS.md +3 -1
  4. package/agents/copilot/copilot-instructions.md +3 -1
  5. package/agents/cursor/campaigns-os.mdc +3 -1
  6. package/campaign-spec/dist/rules/campaign-metadata.d.ts +5 -1
  7. package/campaign-spec/dist/rules/campaign-metadata.js +9 -2
  8. package/campaign-spec/dist/rules/design-source-shape.js +13 -3
  9. package/campaign-spec/dist/rules/sdk-version.js +2 -1
  10. package/compatibility.json +1 -1
  11. package/contracts/commerce-surface-catalog.json +26 -46
  12. package/contracts/effects.v1.json +282 -14
  13. package/contracts/release-ledger.json +1233 -0
  14. package/contracts/supported-surface.json +2 -2
  15. package/contracts/template-brand-contract.shared-commerce.v0.json +2 -2
  16. package/contracts/template-slot-manifest.shared-content-core.v0.json +24 -0
  17. package/docs/brand-theme-bridge.md +2 -0
  18. package/docs/build-packet.md +148 -23
  19. package/docs/campaign-build-brief.md +57 -2
  20. package/docs/effects.md +14 -0
  21. package/docs/local-setup.md +1 -1
  22. package/docs/orientation-contract-reference.md +1 -1
  23. package/docs/polish-evidence.md +12 -0
  24. package/docs/progress-snapshots.md +2 -4
  25. package/docs/qa-and-test-orders.md +47 -4
  26. package/docs/readback.md +19 -2
  27. package/docs/runtime-readiness.md +1 -1
  28. package/docs/sdk-storage-compatibility.md +1 -1
  29. package/docs/skills-revision.md +10 -10
  30. package/package.json +1 -1
  31. package/skills/campaign-lifecycle-orientation/SKILL.md +3 -3
  32. package/skills/campaign-readback-classification/SKILL.md +3 -3
  33. package/skills/campaign-run-evidence/SKILL.md +3 -3
  34. package/skills/contribution-intake/SKILL.md +3 -3
  35. package/skills/next-campaigns-build/SKILL.md +5 -3
  36. package/skills/next-campaigns-os/SKILL.md +11 -20
  37. package/skills/next-campaigns-os/references/session-intake.md +17 -9
  38. package/skills/next-campaigns-os-setup/SKILL.md +4 -3
  39. package/skills/next-campaigns-polish/SKILL.md +7 -5
  40. package/skills/next-campaigns-qa/SKILL.md +8 -5
  41. package/skills.json +10 -10
  42. package/src/adapter-decision-contract.mjs +1 -1
  43. package/src/brand-theme.mjs +33 -20
  44. package/src/brief-summary.mjs +275 -0
  45. package/src/build-brief.mjs +229 -30
  46. package/src/built-site-scope.mjs +39 -6
  47. package/src/built-smoke-qc.mjs +1117 -0
  48. package/src/campaign-identity.mjs +36 -2
  49. package/src/cart-placeholders.mjs +730 -0
  50. package/src/cli.mjs +467 -143
  51. package/src/commercial-journey.mjs +65 -4
  52. package/src/commercial-parity.mjs +6 -1
  53. package/src/contrast.mjs +649 -0
  54. package/src/design-source-publication.mjs +110 -3
  55. package/src/doctor/checks.mjs +415 -33
  56. package/src/doctor/inspect.mjs +53 -2
  57. package/src/doctor/next-step.mjs +16 -7
  58. package/src/input-currency.mjs +394 -0
  59. package/src/input-refresh.mjs +708 -0
  60. package/src/invocation.mjs +5 -2
  61. package/src/local-preview-policy.mjs +1 -1
  62. package/src/local-proof.mjs +4 -1
  63. package/src/polish-browser.mjs +660 -141
  64. package/src/polish-capture.mjs +1 -1
  65. package/src/polish-deadline.mjs +7 -0
  66. package/src/polish-media-weight.mjs +492 -0
  67. package/src/polish-node.mjs +400 -28
  68. package/src/polish-readability.mjs +828 -0
  69. package/src/progress-node.mjs +19 -3
  70. package/src/qa-browser.mjs +439 -180
  71. package/src/qa-content-params.mjs +889 -0
  72. package/src/qa-node.mjs +116 -13
  73. package/src/qa-order-bump.mjs +22 -1
  74. package/src/qa-policy-links.mjs +1019 -0
  75. package/src/qa-tracking-params.mjs +1389 -0
  76. package/src/qa-url-privacy.mjs +168 -0
  77. package/src/qc-accept.mjs +482 -0
  78. package/src/qc-check-registry.mjs +91 -0
  79. package/src/qc-results.mjs +1341 -0
  80. package/src/readback.mjs +58 -10
  81. package/src/sdk-attribute-index.mjs +71 -0
  82. package/src/sdk-markup.mjs +2 -2
  83. package/src/sdk-storage-compatibility.mjs +63 -3
  84. package/src/source-prep.mjs +37 -7
  85. package/src/stage-ledger.mjs +235 -44
  86. package/src/stage-record.mjs +165 -33
  87. package/src/tooling-setup.mjs +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,480 @@
2
2
 
3
3
  Notable supported-surface changes are recorded here.
4
4
 
5
+ ## [1.53.0+agent.2] - 2026-10-05
6
+
7
+ ### Changed
8
+
9
+ `next` now prints a short campaign intent summary, generated from the normalized Campaign Build Brief, in every result (`intent_summary` in JSON) and at the top of the setup, build, Polish and QA prompts. Each value is marked as set in the saved brief file, taken from the source, a generated default, or not stated; a value whose origin was not recorded, or that changed since it was recorded, says so. These marks are tamper evidence, not proof of who wrote a value. Values are shown as quoted data. The summary never shows a purpose or palette source that Campaigns OS inferred from page types or page names: unless the brief file states them, they read "not stated". The summary is output only: nothing reads it, and it cannot change prices or commerce behaviour. Bundled skills and the agent context files now tell agents to read the brief and the recorded assembly decisions.
10
+
11
+ The build stage prompt that `next` returns now tells the agent to save a changed Campaign Build Brief with `record brief` or a changed CampaignSpec with `record spec` before building on the change.
12
+
13
+ Bundled skills are at revision `1.53.0+skills.2`. The lifecycle skill saves brief answers with `record brief` and returns the single compact brief defined in `references/session-intake.md`, whose work-type field is now `Work:` beside a new `Campaign:` field. The build skill passes `--deviation-reason` to `record build` only on the operator's explicit decision, the Polish skill records a skipped Polish only on the operator's decision, and the Polish and QA skills say how to report a readability warning and offer repairs inside the brand palette.
14
+
15
+ ### Migration
16
+
17
+ **Migration.** The agent context files changed, so `tooling setup` refuses until each existing campaign refreshes its installed copy. From the campaign folder:
18
+
19
+ 1. `npm install --save-dev --save-exact @nextcommerce/campaigns-os@1.53.0`
20
+ 2. `npx --no-install campaigns-os install-agent-context --target .` (overwrites all four files under `.campaign-runtime/agent-context/`; local edits there are lost)
21
+ 3. Review and commit `git diff .campaign-runtime/agent-context/`
22
+ 4. `npx --no-install campaigns-os tooling setup --target . --platform claude`
23
+ 5. Restart the agent session so it loads the new skills and context.
24
+
25
+ Codex, Cursor and Copilot users run step 2 and reinstall skills with `install-skills --platform codex` (or `agents`).
26
+
27
+ ## [1.53.0+agent.1] - 2026-10-05
28
+
29
+ ### Added
30
+
31
+ - Polish now measures text readability on every built page at desktop and mobile widths and reports warnings, review and coverage results in the QC handoff. QA's primary-CTA check and the theme generator use the same unrounded WCAG 2.x measurement. Results change on some campaigns: ratios in [4.495, 4.5), translucent text or backgrounds, and child text now warn; large bold text at 3:1 and Tailwind v4 `oklch()` colours are measured correctly; gradients, unreadable colours, and opacity, filters, blend modes or masks on surrounding elements become manual review instead of a contrast result. QA's primary-CTA check no longer skips pages by page type: every page with an expected next route is checked, every route-matching CTA is measured (not only the most readable one), each piece of text inside a CTA is compared with the requirement for its own size, and a CTA measured before its text, the SDK, stylesheets or fonts are ready is reported for manual review, never as a pass. The primary-CTA check also reports manual review, not a failure or a pass, when the page inspection itself errors (earlier releases reported a failure), when every route-matching CTA is disabled, and when a CTA's text includes text generated by CSS `::before` or `::after` (earlier releases reported a contrast pass or failure for both). `polish capture` on a campaign with no mapped pages now measures readability and exits successfully, printing the page-load message as a warning. Polish media weight and readability results now read as stale when the built output changed without a new `record build`.
32
+
33
+ ## [1.53.0] - 2026-10-05
34
+
35
+ ### Added
36
+
37
+ - Adds `record brief`, which saves Campaign Build Brief answers without re-running intake. A brief file is guided unless it sets `"brief_mode": "prepared"` (a campaign whose report already records a prepared brief stays prepared). An unchanged or reformatted save keeps all stage evidence and statuses. A material change marks build, Polish and QA (or QA alone for `qa_policy`) as owed again and keeps the last five superseded records per stage as history. `next` routes to the owed stage. Re-recording an unchanged build after an input change keeps the build owed unless the operator records a reason, including when a CampaignSpec change was never saved with `record spec`; an edit to the brief file is read only once it is saved with `record brief`. Intake now also refuses, without `--force`, to discard recorded waivers, warning accepts, stage history, an applied theme, or a recorded deploy target, preview URL, production URL, order-path depth or allowed-domains confirmation; with `--force`, it keeps stage history and archives the records it clears.
38
+ - Adds `record spec`, a non-destructive refresh after a CampaignSpec change. Superseded build, Polish and QA records move to stage history, and `next`, progress, readback and the Assembly Report status now report the owed stage instead of recommending QA. Spec material changes are detected for saved-Map and gateway packets too (against the fetched copy; remote currency is reported as unconfirmed). `spec derive` no longer rebinds report identity on its own; run `record spec` after it. Re-recording an unchanged build or replaying a Polish record after an input change no longer makes them current, whether or not the change was saved with `record spec`.
39
+
40
+ ### Changed
41
+
42
+ - `spec derive --json` no longer carries the `rebound` field, because `spec derive` no longer rebinds report identity.
43
+ - A stage status outside the recognized list (`pending`, `blocked`, `required`, `completed`, `completed_with_warnings`, `completed_partial`, `skipped`) now reads as unknown and stops `next`, where earlier releases accepted any status beginning `completed` or `skipped`.
44
+ - The effects contract declares the `record brief` and `record spec` rows (tier C) and their `--dry-run` rows (tier none, with no lifecycle entry). It corrects the intake rows: `start`, `prepare-build` and `build` are refused without `--force` when the report carries stage evidence, stage history or operator decisions, and their `--force` forms archive completed build, Polish and QA records into stage history, which is kept. It also states that `record build` writes `input_change` and makes Polish and QA owed when it detects a brief or CampaignSpec change, and that `record polish`, `record theme` and `record deploy` are refused while an earlier stage is owed again.
45
+
46
+ ### Migration
47
+
48
+ **Migration.** Build, Polish and QA records made before this release do not record which brief and CampaignSpec content they used, so they read as unconfirmed. After upgrading, `next` routes each in-flight campaign back to build once: re-record build and Polish, and run QA again for a new verdict. Hand-written brief files without `brief_mode` are now guided for new campaigns; add `"brief_mode": "prepared"` if you want open questions to block.
49
+
50
+ - Re-running `start`, `prepare-build` or `build` over a campaign with recorded waivers, warning accepts, stage history, an applied theme, or a recorded deploy target, preview URL, production URL, order-path depth or allowed-domains confirmation is now refused without `--force`. Save brief answers with `record brief` and a CampaignSpec change with `record spec` instead; pass `--force` only to clear that state on purpose.
51
+ - Scripts that read `rebound` from `spec derive --json` must stop: run `record spec` after `spec derive` to bind the new CampaignSpec content.
52
+
53
+ ## [1.52.0+agent.1] - 2026-10-05
54
+
55
+ ### Added
56
+
57
+ - Doctor now reports built-page smoke warnings: missing in-page anchor targets, missing favicon and Open Graph tags, unresolved `og:image`, the Tailwind CDN script in production builds, `cdn.29next.store` asset references, and loopback URLs.
58
+
59
+ ## [1.52.0] - 2026-10-05
60
+
61
+ ### Added
62
+
63
+ - QA now checks that configured store policy links are rendered and reachable, recording link presence and availability separately. QA now sends bounded header-only requests to the configured policy URLs; the effects contract declares them.
64
+
65
+ ### Changed
66
+
67
+ - The effects contract now states that `qa run --browser` sends the header-only GET requests to the configured store policy URLs, rather than declaring them ahead of the check that sends them.
68
+
69
+ ## [1.51.0+agent.4] - 2026-10-05
70
+
71
+ ### Added
72
+
73
+ - QA now checks at runtime that declared content parameters hide their content, comparing fresh contexts with and without `?<name>=n`.
74
+
75
+ ## [1.51.0+agent.3] - 2026-10-05
76
+
77
+ ### Added
78
+
79
+ - QA browser test orders now report URL preservation and order attribution for synthetic tracking parameters as separate results. QA order evidence no longer stores query strings in `checkout_url`, `final_url` or request URLs.
80
+
81
+ ## [1.51.0+agent.2] - 2026-10-05
82
+
83
+ ### Added
84
+
85
+ - Polish capture now records image geometry and redirect chains, and reports origin and weight warnings for video and large images served from the page's own origin, plus oversized images.
86
+
87
+ ### Changed
88
+
89
+ - `polish capture` now records media weight beside page load evidence in the Assembly Report, so `next` reads the image weight and oversize results of a fresh capture.
90
+
91
+ ## [1.51.0+agent.1] - 2026-10-04
92
+
93
+ ### Added
94
+
95
+ - Doctor now warns on SDK cart placeholders printed in live built HTML (`built_output.cart_placeholders`), ported from the public starter-template lint.
96
+
97
+ ## [1.51.0] - 2026-10-04
98
+
99
+ ### Added
100
+
101
+ - Adds `checkpoint accept`, which records an operator's accept of a measured warning next to the unchanged measurement, and a QC handoff section in `next` that lists open, review, lapsed, unexercised and excluded results. Accepts never change readiness, and `checkpoint waive` still refuses gates that are not blocked.
102
+ - The effects contract now declares bounded header-only GET requests to the store policy URLs a CampaignSpec configures, for `qa run --browser`, ahead of the check that sends them; QA does not send them yet.
103
+
104
+ ## [1.50.0+agent.20] - 2026-10-04
105
+
106
+ ### Changed
107
+
108
+ - `scripts/refresh-certified-family-fixtures.mjs` now sets `og_image` to
109
+ `https://example.com/og-image.png` on each certified family's
110
+ `_data/campaigns.json` entry before it renders, and records that in the
111
+ fixture manifest as `render_inputs`. The starters leave `og:image` out until
112
+ a campaign sets one, so without it every certified page would report a
113
+ missing `og:image` to the coming built-output smoke checks. The value is on a
114
+ reserved domain, so it is plainly synthetic, and doctor `--built` neither
115
+ maps nor fetches it. The committed fixture tree is unchanged until the next
116
+ regeneration.
117
+
118
+ ## [1.50.0+agent.19] - 2026-10-03
119
+
120
+ ### Fixed
121
+
122
+ - A campaign-identity finding (`funnel_drift`, `funnel_missing`,
123
+ `api_key_drift`, `attribution_drift`) that names a built file no active
124
+ CampaignSpec page builds to now says so, and lists the file on the finding as
125
+ `stray_files`. The message says to remove the source file if there is one,
126
+ delete the built file, and rebuild and record the build again. Before, a
127
+ design `index.html` copied under `assets/` built as its own page and blocked
128
+ as funnel drift, with the message telling the agent to retag a page it never
129
+ meant to ship. The gate still blocks, because the file is still served.
130
+
131
+ ## [1.50.0+agent.18] - 2026-10-03
132
+
133
+ ### Fixed
134
+
135
+ - Commercial parity no longer warns `price-claim-mismatch` on a page that shows
136
+ a voucher the normalized plan cannot price. An upsell priced by a live
137
+ voucher showed the voucher price, and QA compared it with the list-price
138
+ total. Those price claims now count as unresolved, as the page's voucher
139
+ claims already did, so coverage reads incomplete instead.
140
+ - `browser-order-bump-state` reads a `✓` glyph marker as checked only when its
141
+ text is painted. A tick that stays in the marker and is hidden with
142
+ `color: transparent` read checked before, so a declined bump failed as
143
+ misaligned.
144
+
145
+ ## [1.50.0+agent.17] - 2026-10-03
146
+
147
+ ### Fixed
148
+
149
+ - Three CampaignSpec validation messages that doctor and `start` print now say
150
+ which field to change:
151
+ - The missing-`payment_env_key` warning keeps "No campaign loaded — campaign
152
+ key required for spec export." and adds that `campaign.payment_env_key` is
153
+ empty, where its value comes from, and that a Campaigns API key in the spec
154
+ does not fill it. Before, it printed next to doctor's "Campaigns API key
155
+ available via the packet-local CampaignSpec" line and read as if the key
156
+ were missing.
157
+ - The missing SDK version error keeps "SDK version is required for spec
158
+ export." and names `global_config.sdk_version` and the form it takes.
159
+ - The missing `design_source.file_url` warning, for a page whose
160
+ `design_source.type` is not `figma` or `ai-generated`, and doctor's
161
+ matching no-source-mapping error, say that hand-written or template HTML
162
+ leaves `design_source` off the page. Before, a plain HTML page with a
163
+ `design_source` block was told only to add a design-tool URL.
164
+
165
+ ## [1.50.0+agent.16] - 2026-10-03
166
+
167
+ ### Fixed
168
+
169
+ - `prepare-build` (and `start`) no longer write an Assembly Report that fails
170
+ its own schema. When the brand-theme write failed, each error was copied
171
+ onto `theme.warnings[]` as `{ code, message, detail: error.detail || null }`.
172
+ The schema's `themeIssue.detail` is an object, so an error with no detail
173
+ (`theme.generate.not_ready`, `theme.generate.empty`) left `detail: null`.
174
+ `record setup` and `record build` then refused the report with
175
+ "Assembly Report theme.warnings.0.detail must be object". `detail` is now
176
+ written only when the error carries an object. The schema is unchanged.
177
+
178
+ ## [1.50.0+agent.15] - 2026-10-03
179
+
180
+ ### Fixed
181
+
182
+ - `record setup|build|polish|theme|deploy` accept `--deviation-reason`, as
183
+ every other command does. The deviation notice tells an agent that departs
184
+ from `next` to "Declare intent with --deviation-reason". `record` checks its
185
+ flags against a strict allowlist, which held only its own flags and the global
186
+ `--run-id` and `--lifecycle-journal`, so it refused the flag as unknown on
187
+ every stage. It is now a global flag there too. A bare `--deviation-reason`
188
+ with no value is refused as before.
189
+
190
+ ## [1.50.0+agent.14] - 2026-10-03
191
+
192
+ ### Fixed
193
+
194
+ - QA's placeholder text-residue gate (`template-residue:<page>:placeholder-text`,
195
+ and doctor's built-output warning) now also matches the starter templates'
196
+ own icon-grid placeholders, `Benefit one`, `Benefit two`, `Benefit three` and
197
+ `Benefit four`. The olympus checkouts ship them, and a campaign that kept
198
+ them passed QA with no failures, because the shared-commerce term list held
199
+ only `Lorem`, `lorem ipsum`, `Placeholder`, `TODO` and `Product Name`.
200
+ - The terms live in `contracts/template-brand-contract.shared-commerce.v0.json`,
201
+ so every family inherits them.
202
+ - Matching is word-bounded, so "Benefit once" does not fire.
203
+ - Doctor's next-step hint and `docs/template-family-contracts.md` list them.
204
+
205
+ ## [1.50.0+agent.13] - 2026-10-03
206
+
207
+ ### Fixed
208
+
209
+ - The source preparation check `source_html.prep.document_wrapper` reads a
210
+ page's converted page-kit file once it exists at `page_kit.output_path`, not
211
+ the design it was converted from. Wrappers are stripped during conversion,
212
+ and the converted page is what page-kit builds. The check only ever read the
213
+ mapped design, so a correctly stripped build still failed. To clear it,
214
+ agents copied built pages over the design source (breaking the Design Source
215
+ Package hashes) or recorded `preserve_document_wrappers` for pages they had
216
+ stripped. A converted page that still carries wrappers is reported under its
217
+ own path. Before conversion the design is checked as before. The frontmatter
218
+ and source-link checks are unchanged. `docs/source-adapters.md` says so.
219
+
220
+ ## [1.50.0+agent.12] - 2026-10-03
221
+
222
+ ### Fixed
223
+
224
+ - `record deploy` follows `next` past a polish carried forward on the local
225
+ preview. On a `local-serve` packet with a loopback preview, `next`'s stage
226
+ picker skips a polish that was never recorded for this build (the
227
+ local-preview policy carries it forward as a warning), so after
228
+ `record build` it answers deploy. `record deploy` checked every earlier
229
+ stage strictly and refused with "stages.polish.status is "required", so next
230
+ answers polish", which contradicted `next`. Sessions hand-wrote a polish skip
231
+ record to get past it. Both now read the same rule, from doctor's
232
+ `polish_gate`. Polish stays owed on the report and QA still reports it.
233
+ `record polish` and the waiver commands keep the strict gates.
234
+ `docs/build-packet.md` names the exception.
235
+
236
+ ## [1.50.0+agent.11] - 2026-10-03
237
+
238
+ ### Added
239
+
240
+ - Doctor warns `spec.material_stale` when a local-spec campaign's CampaignSpec
241
+ no longer has the material hash prepare-build bound on the Assembly Report
242
+ (`identity.spec_material_hash`). `next` prints the warning with the rest of
243
+ doctor's. QA already refused such a run ("Re-run prepare-build after a
244
+ material revision"). Until now it was the only command that checked, so a
245
+ spec edited after `start` was accepted by doctor, `next` and every `record`
246
+ command, and the problem surfaced only after the build, polish and deploy
247
+ stages had been recorded against the old spec. The warning names both hashes
248
+ and says to re-run prepare-build from the edited spec.
249
+ `docs/build-packet.md` says so.
250
+
251
+ ## [1.50.0+agent.10] - 2026-10-03
252
+
253
+ ### Fixed
254
+
255
+ - A checkout row marked `is_order_bump: true` is now read as an order bump.
256
+ The CampaignSpec schema and the authoring guide mark a checkout add-on with
257
+ that flag, and the certified fixtures use it alone, but the one bump
258
+ predicate the toolkit shares read only `is_upsell`. Such a row was treated
259
+ as a main package:
260
+ - the QA tier planner made it a selector tier of its own;
261
+ - `next` named no bump-cart QA command (`qa_run_bump`);
262
+ - commercial-journey priced the bump into the representative checkout and
263
+ planned no with/without-bump scenarios.
264
+
265
+ Either flag now marks a bump, and a row carrying `is_upsell: true` behaves as
266
+ before. On the certified fixtures, the five checkouts that declare a bump now
267
+ plan only their real tiers, and their bumps get the bump-cart command.
268
+ - Messages that named a bump `(is_upsell)` now say
269
+ `(is_order_bump or is_upsell)`: the tier planner's warning and refusal, the
270
+ `next` bump-command description, the `qa run --help` text and the QA
271
+ test-order notes. `docs/qa-and-test-orders.md` says the same.
272
+
273
+ ## [1.50.0+agent.9] - 2026-10-03
274
+
275
+ ### Added
276
+
277
+ - `record build --build-environment <development|production>` records the
278
+ page-kit environment the built output was rendered in, on
279
+ `stages.assembly.evidence.build_environment`. A later `record build` without
280
+ the flag keeps the recorded value. Before this, local proof mode
281
+ (`deploy.target: local-serve`) asked the agent to record the field, but no
282
+ command wrote it, so it was hand-edited into the Assembly Report.
283
+ - Under local-serve, `next` now names
284
+ `record build --packet <packet> --build-environment development` in the
285
+ build action and the build prompt.
286
+ - Doctor's `local_proof.build_environment` warning, the parity messages and
287
+ the local-proof rebuild hint name the same command.
288
+ - `docs/build-packet.md` and `docs/qa-and-test-orders.md` describe it.
289
+
290
+ ### Fixed
291
+
292
+ - A `record` refused for a value outside a schema enum now lists the values the
293
+ schema allows and the value it got. For example,
294
+ `adapter_decisions.wrapper_policy must be equal to one of the allowed values:
295
+ "strip_document_wrappers", "preserve_document_wrappers", "not_required",
296
+ "unknown" (got "strip")`; before, it stopped at "allowed values".
297
+ - Doctor's adapter-decision warning for an unknown value lists the allowed
298
+ values too.
299
+
300
+ ## [1.50.0+agent.8] - 2026-10-03
301
+
302
+ ### Changed
303
+
304
+ - The vendored commerce-surface catalog is re-synced to
305
+ campaign-cart-starter-templates `02ffc61`, from `37a8d94`. That brings in
306
+ three starter changes:
307
+ - the single-offer upsell keeps a hidden in-offer skip, so its closing-card
308
+ decline works in every family (#205);
309
+ - `payment-methods.html` takes `method_order` and `default_method` (#202);
310
+ - fresh SDK 0.4.40 verification evidence (#203).
311
+
312
+ The upstream order-bump notes were consolidated, and the vendored copy
313
+ follows them.
314
+ - `fixtures/certified-families/` is regenerated at the new pin. Every
315
+ `upsell-single` fixture now carries the in-offer skip, so QA's static
316
+ `route-link:<page>:decline` check, which fails a proxy decline with no
317
+ target, passes on all eight families instead of failing on each.
318
+ - The shared commerce payment-chrome `asset_pin` moves to the new pin. The
319
+ asset bytes are unchanged.
320
+
321
+ ## [1.50.0+agent.7] - 2026-10-03
322
+
323
+ ### Fixed
324
+
325
+ - `sdk storage-check` resolves Page Kit's `campaign_asset` script srcs. A
326
+ page under `src/<slug>/` that loads `{{ 'js/checkout.js' | campaign_asset }}`
327
+ is read as `src/<slug>/assets/js/checkout.js`, where Page Kit serves it from,
328
+ and a page's frontmatter `scripts:` entries resolve the same way, since a
329
+ layout's `{% for script in scripts %}` loop loads them through that filter.
330
+ Until now every such tag read as a relative path that was never in scope and
331
+ reported `shared-script-outside-scope`, so every Page Kit campaign came back
332
+ `unknown` even when nothing was incompatible (#582). A resolved script left
333
+ out of `--scope`, or excluded, is still reported, under its real path; a
334
+ `campaign_asset` value the scan cannot resolve stays unknown.
335
+ `docs/sdk-storage-compatibility.md` says so.
336
+
337
+ ## [1.50.0+agent.6] - 2026-10-03
338
+
339
+ ### Fixed
340
+
341
+ - QA no longer passes an upsell decline or accept that does nothing. Some
342
+ upsell pages show a `data-upsell-proxy="skip"` (or `"add"`) button that
343
+ forwards its click to the SDK's `data-next-upsell-action` inside the offer.
344
+ When the offer has no such action, the static `route-link:<page>:decline`
345
+ (or `:accept`) check used to pass on the decline URL in the page's meta tag,
346
+ and only browser QA, after placing the order, found the control missing. The
347
+ static check now fails as a blocker and says the proxy has nothing to
348
+ forward to. The starter templates' single-offer upsell shipped this way in
349
+ every family until the templates kept a hidden in-offer skip.
350
+ - Browser QA declines (and accepts) through the control a shopper sees. When
351
+ the page's `data-next-upsell-action` is hidden and a visible
352
+ `data-upsell-proxy` button forwards to it, QA clicks the proxy instead of
353
+ failing with `Element is not visible` on the hidden action. A proxy with no
354
+ in-offer action is still reported as a missing upsell control.
355
+
356
+ ## [1.50.0+agent.5] - 2026-10-03
357
+
358
+ ### Changed
359
+
360
+ - The agent context the toolkit installs (`agents/claude/CLAUDE.md`,
361
+ `agents/codex/AGENTS.md`, `agents/cursor/campaigns-os.mdc`,
362
+ `agents/copilot/copilot-instructions.md`) and the `next-campaigns-os` and
363
+ `next-campaigns-qa` skills no longer say test orders need no permission or
364
+ approval, or are safe to fire any time. They say `qa run` has no permission
365
+ flag and coverage is its only control, and that test orders still land in
366
+ the store as real orders (global test cards: no charge, no transaction) that
367
+ someone may have to cancel. Unless the operator has already said test orders
368
+ are fine for the campaign, the agent asks once, up front in its first turn
369
+ with its other setup questions, so the answer covers the whole build and QA
370
+ and neither stops for it later. The skill's session-intake reference says
371
+ the same in its test-order proof policy.
372
+ - The QA skill follows an answer the operator already gave and does not pause
373
+ QA to ask again. When nobody asked earlier, it asks once before the first
374
+ test order.
375
+ - `qa run` is unchanged: it has no permission flag and none is added. Its
376
+ help text and the docs are unchanged.
377
+ - Bundled skills carry revision `1.50.0+skills.2`, with each skill version
378
+ advanced one patch.
379
+
380
+ ## [1.50.0+agent.4] - 2026-10-02
381
+
382
+ ### Changed
383
+
384
+ - At the QA stage, `next` lists a second QA command when the CampaignSpec's
385
+ checkout declares an order bump (`is_upsell: true` rows). `qa_run_bump`,
386
+ beside `qa_run`, is the same `qa run --browser --test-order common` with
387
+ `--cart <base>:1,<bump>:1`, so its test orders carry the add-on and prove
388
+ its charge. Until now `next` named only the default run. Its test orders
389
+ never toggle a bump, because the tier planner skips bump rows by design and
390
+ bump coverage comes from `--cart`, so a verdict could read ready with the
391
+ add-on never ordered. The base is the first selector tier the checkout
392
+ declares; a checkout that declares no tier gets the bump alone, and several
393
+ declared bumps share one cart. The QA stage prompt and the human `next`
394
+ output name the same command, and `docs/qa-and-test-orders.md` says so
395
+ under its launch-grade proof list. The bump is read from the one checkout
396
+ QA's test orders run on, the first enabled checkout across funnels, so a
397
+ bump declared only on a later funnel's checkout gets no command. When the
398
+ CampaignSpec does not parse, `next` warns
399
+ (`next.order_bump_spec_unreadable`) instead of silently naming no bump
400
+ command.
401
+ - A progress snapshot records `qa_run_bump` as `qa_run`. The snapshot's
402
+ action vocabulary is unchanged, and a ready QA continuation does not read
403
+ as blocked.
404
+ - The QA tier planner reads its bump and tier helpers from the
405
+ commercial-journey module, where `next` reads them too. Tier planning is
406
+ unchanged.
407
+
408
+ ## [1.50.0+agent.3] - 2026-10-02
409
+
410
+ ### Changed
411
+
412
+ - The Campaign Build Brief question `promo_urgency_copy` now asks only about
413
+ the starter template's own promo placeholders: demo countdown timers, promo
414
+ banners, placeholder voucher codes and exit-pop offers. It asks whether to
415
+ fill them from the campaign's promo codes and offers or remove them. It no
416
+ longer asks which promo, savings and urgency language is approved, which
417
+ read as a request to approve the source design's own copy; that copy is the
418
+ merchant's content and is built as designed.
419
+ - The question is asked only when the CampaignSpec maps a surface that fills
420
+ those placeholders: a `funnels[].promo_codes` roster, or a checkout page's
421
+ enabled `exit_intent` or `promo_code_input`. It used to be asked for any CampaignSpec
422
+ key naming an offer, discount, timer or urgency, so the offer catalog,
423
+ before-discount prices and design slot names all raised it. Without such a
424
+ surface the guided draft sets `promo_urgency.header_claim_source` and
425
+ `promo_urgency.timer_label` to `"none"` (the template's promo placeholders
426
+ are removed). The draft used to set `timer_label` to "Limited-time offer".
427
+ - Doctor's `build_brief.guided_questions` warning names the brief fields that
428
+ close each open question and says how to record the answers: copy the
429
+ normalized draft to `campaign-build-brief.json` in the target repo, set the
430
+ fields, and re-run `start` or `prepare-build`. It also says the re-run needs
431
+ `--force`, which clears stage evidence, once a stage has recorded evidence.
432
+ `build_brief.questions_unanswered` names the fields too. An answer given
433
+ only in conversation was never recorded, so a later session asked again.
434
+ - `next`'s QA prompt compares the template's own promo placeholders and trust
435
+ badges with the brief and says not to flag the source design's own proof,
436
+ urgency or guarantee elements. It used to ask for promo/urgency copy and
437
+ trust/guarantee claims to be compared. The build prompt names the template's
438
+ promo placeholders where it named promo/urgency language.
439
+ - `docs/campaign-build-brief.md` rewords question 5 and adds "Answering The
440
+ Questions", with the fields that close each question.
441
+
442
+ ## [1.50.0+agent.2] - 2026-10-02
443
+
444
+ ### Changed
445
+
446
+ - `docs/build-packet.md` says how to read a campaign's package, offer and
447
+ shipping refs for a local CampaignSpec. Its new "Reading package, offer and
448
+ shipping refs" section, under "Local-spec entry", gives the read doctor and
449
+ QA already make against the live campaign: one GET of NEXT's proxy,
450
+ `https://campaign-map.nextcommerce.com/api/campaign`, with the public
451
+ Campaigns API key in the `X-Campaign-Key` header. It shows a `node` and a
452
+ `curl` form, describes the envelope, and maps the campaign retrieve body's
453
+ `id`, `packages[]`, `offers[]` and `shipping_methods[]` to
454
+ `campaign.ref_id`, `funnels[].pages[].packages[]`, root `offers[]` and root
455
+ `shipping_methods[]`. It also notes that the proxy refuses some default user
456
+ agents, Python `urllib`'s and Perl `libwww-perl`'s among them. No command
457
+ writes these refs, as before.
458
+ - With no saved gateway login, `tooling status` no longer only says to run
459
+ `login`. Its warning says login is optional and only lets `spec derive
460
+ --from-store` fill the Store Profile fields, and a second warning says
461
+ package, offer and shipping refs never come from the gateway login and
462
+ where the public-key read is documented. Both lines are under `warnings`,
463
+ and the exit code is unchanged.
464
+
465
+ ## [1.50.0+agent.1] - 2026-10-02
466
+
467
+ ### Changed
468
+
469
+ - No command behaves differently. A comment in the QA browser module that
470
+ explains how repeated package declarations become purchase multipliers
471
+ named an internal store as its example; it now gives a neutral one (a 1x
472
+ and a 2x package). Two QA test files swap the same name in a fixture SKU
473
+ and a hosted-checkout URL for neutral placeholders, and the same fixture's
474
+ product title becomes "Demo Bag". Comment and test fixtures only; every
475
+ message and every exit code is unchanged.
476
+ - The private-string check adds that store name to its hashed list, so it
477
+ cannot return.
478
+
5
479
  ## [1.50.0] - 2026-10-02
6
480
 
7
481
  ### Changed
@@ -8,6 +8,8 @@ project copy. Do not use private runtime source as the campaign's starting point
8
8
 
9
9
  To pick up an existing campaign, run `npx --no-install campaigns-os readback .` and `npx --no-install campaigns-os next --packet campaign-runtime.build.json` before reading artifacts by hand; `next` blocks polish and QA again whenever the built output changes. The committed `.campaign-runtime/qa-verdict.json` keeps no order records or URLs: its `browser-test-order:<path>` assertions are the typed-card proof, and the full verdict is under `qa-output/`.
10
10
 
11
+ A NEXT campaign is one connected commerce journey: the pages the CampaignSpec declares, in its order, through product choice, checkout, post-purchase offers and receipt. Creative intent — audience, purpose, layout, imagery, approved copy, visual style — belongs to the operator, the Campaign Build Brief and the accepted design source. Commerce behaviour — products, package IDs, prices, offers, shipping, routes, cart, checkout, payment and post-purchase — belongs to CampaignSpec/API values and the SDK. Before changing a campaign, read the normalized brief at `.campaign-runtime/input/campaign-build-brief.normalized.json` and the recorded decisions: `adapter_decisions`, `decisions` and `theme` in `.campaign-runtime/assembly-report.json`, and `assembly.template_decision_notes` in the Build Packet. The setup, build, Polish and QA prompts begin with a campaign intent summary, and every `next` result carries it (`intent_summary` in JSON); treat it as orientation, never as a source of prices or behaviour. When a revision is narrow, keep the recorded intent and ask only about missing choices that affect that revision. Ask the operator before saving brief answers.
12
+
11
13
  Core rules:
12
14
 
13
15
  - Treat CampaignSpec as campaign intent and the Campaigns API as live commerce truth.
@@ -28,12 +30,12 @@ Core rules:
28
30
  - Do not copy Olympus-style `shipping_methods` frontmatter into `shop-three-step`; it uses dynamic shipping through `window.next.getShippingMethods()`.
29
31
  - Run build/lint checks and record evidence in the assembly report, then hand off to polish. Before Polish becomes terminal or hands off to deploy/QA, install the package-owned Playwright browser with `npm run qa:install-browser`, serve the current build, and run `npx --no-install campaigns-os polish capture --packet campaign-runtime.build.json --base-url <served-current-build-url>`.
30
32
  - After that checkpoint clears, QA uses the Campaigns OS Node/npm runner: run `npx --no-install campaigns-os qa resolve --packet campaign-runtime.build.json`, then run `npx --no-install campaigns-os qa run --packet campaign-runtime.build.json --base-url <url> --browser --test-order common`.
31
- - Typed-card test-order proof uses `npx --no-install campaigns-os qa run --test-order <common|checkout|decline|accept|both|full|explicit-path>` through the deployed checkout and rendered upsell controls. `common` runs every actual terminal path when they fit under the flood cap (`--max-test-orders`, 6 by default); above the cap it runs the checkout baseline, first-offer accept and decline, and a deduplicated shortest real receipt path, then adds one decline path per offer or downsell page not yet declined, up to the cap, and names any page left out. A page counts as covered only when an order clicks its decline; the `browser-test-order:upsell-action-coverage` verdict row warns naming each page whose decline no order clicked. `full` walks every actual terminal path in the selected checkout topology; cycles, missing routes, and reachable nonterminals block exhaustive proof before browser launch. The accidental-flood cap remains `6`, and an overflow names the exact explicit `--max-test-orders` raise. Coverage is the only control — there is no permission/approval step.
33
+ - Typed-card test-order proof uses `npx --no-install campaigns-os qa run --test-order <common|checkout|decline|accept|both|full|explicit-path>` through the deployed checkout and rendered upsell controls. `common` runs every actual terminal path when they fit under the flood cap (`--max-test-orders`, 6 by default); above the cap it runs the checkout baseline, first-offer accept and decline, and a deduplicated shortest real receipt path, then adds one decline path per offer or downsell page not yet declined, up to the cap, and names any page left out. A page counts as covered only when an order clicks its decline; the `browser-test-order:upsell-action-coverage` verdict row warns naming each page whose decline no order clicked. `full` walks every actual terminal path in the selected checkout topology; cycles, missing routes, and reachable nonterminals block exhaustive proof before browser launch. The accidental-flood cap remains `6`, and an overflow names the exact explicit `--max-test-orders` raise. `qa run` has no permission flag: coverage is its only control.
32
34
  - Analytics correctness is two-phase: the campaign-root visit inventories declared providers/tags only, and the same canonical typed-card run proves Purchase only from the topology-recognized final receipt document after `--analytics-settle`. Missing/unrecognized receipts are manual-review warnings; recognized receipt no-signal and capture/settle errors block. Only the genuine recognized-receipt/no-signal failure is waivable; capture/settle errors are not. Checkout/upsell signals remain whole-journey parity evidence and cannot satisfy a silent receipt.
33
35
  - Test-order proof must use the canonical Playwright typed-card path through the tested checkout: select the rendered cart, fill customer/shipping fields, type the sandbox card into active hosted payment iframes, click the real submit button, then click rendered SDK upsell accept/decline controls and verify receipt/order evidence.
34
36
  - Do not use `next.getCartData().cartLines` as cart-populated proof; use typed-card order read-back, `cart:updated` payload `items` / `summary.lines`, and rendered bundle DOM evidence.
35
37
  - Do not use external browser skills, the SDK test-mode event, or hand-built backend API orders as launch proof. Those are diagnostic fallbacks only when explicitly requested.
36
- - Test orders are safe to fire any time: global test cards bypass the gateway, create no transactions, and need no merchant-specific routing confirmation. Localhost on any port is a Campaigns App Development domain for SDK QA with analytics suppressed; non-localhost preview/production origins must be allowlisted for the campaign API key so the SDK loads — that is about SDK initialization, not test-order permission.
38
+ - Test orders still land in the store as real orders that someone may have to cancel (global test cards bypass the gateway: no charge, no transaction, no merchant-specific routing to confirm). Unless the operator has already said test orders are fine for this campaign, ask once, up front in your first turn with your other setup questions, so the answer covers the whole build and QA and neither stops for it later. Localhost on any port is a Campaigns App Development domain for SDK QA with analytics suppressed; non-localhost preview/production origins must be allowlisted for the campaign API key so the SDK loads — that is about SDK initialization, not test-order permission.
37
39
  - Campaigns OS proof is not merchant launch readiness. Before launch, confirm production storefront URL, live payment methods, shipping markets, legal/support URLs, analytics expectations, and merchant-side configuration.
38
40
 
39
41
  Current source adapter: prepared HTML/assets (`html_funnel`).
@@ -2,6 +2,8 @@
2
2
 
3
3
  Use this context when working in a target campaign repo with Campaigns OS artifacts.
4
4
 
5
+ A NEXT campaign is one connected commerce journey: the pages the CampaignSpec declares, in its order, through product choice, checkout, post-purchase offers and receipt. Creative intent — audience, purpose, layout, imagery, approved copy, visual style — belongs to the operator, the Campaign Build Brief and the accepted design source. Commerce behaviour — products, package IDs, prices, offers, shipping, routes, cart, checkout, payment and post-purchase — belongs to CampaignSpec/API values and the SDK. Before changing a campaign, read the normalized brief at `.campaign-runtime/input/campaign-build-brief.normalized.json` and the recorded decisions: `adapter_decisions`, `decisions` and `theme` in `.campaign-runtime/assembly-report.json`, and `assembly.template_decision_notes` in the Build Packet. The setup, build, Polish and QA prompts begin with a campaign intent summary, and every `next` result carries it (`intent_summary` in JSON); treat it as orientation, never as a source of prices or behaviour. When a revision is narrow, keep the recorded intent and ask only about missing choices that affect that revision. Ask the operator before saving brief answers.
6
+
5
7
  - Read `campaign-runtime.build.json` first.
6
8
  - To pick up an existing campaign, run `npx --no-install campaigns-os readback .` and `npx --no-install campaigns-os next --packet campaign-runtime.build.json` before reading artifacts by hand; `next` blocks polish and QA again whenever the built output changes. The committed `.campaign-runtime/qa-verdict.json` keeps no order records or URLs: its `browser-test-order:<path>` assertions are the typed-card proof, and the full verdict is under `qa-output/`.
7
9
  - If `.campaign-runtime/build-context.json` or `.campaign-runtime/assembly-report.json` exists, read them before editing campaign files.
@@ -22,7 +24,7 @@ Use this context when working in a target campaign repo with Campaigns OS artifa
22
24
  - For `shop-three-step`, keep dynamic shipping via `window.next.getShippingMethods()` and do not add Olympus-style static `shipping_methods` frontmatter.
23
25
  - Build hands off to polish. Before Polish becomes terminal or hands off to deploy/QA, install the package-owned Playwright browser with `npm run qa:install-browser`, serve the current build, and run `npx --no-install campaigns-os polish capture --packet campaign-runtime.build.json --base-url <served-current-build-url>`.
24
26
  - After that checkpoint clears, QA uses the Campaigns OS Node/npm runner: run `npx --no-install campaigns-os qa resolve`, then `npx --no-install campaigns-os qa run --browser --test-order common` against the tested URL.
25
- - Typed-card test-order proof uses `npx --no-install campaigns-os qa run --test-order <common|checkout|decline|accept|both|full|explicit-path>` through the tested checkout and rendered upsell controls. Global test cards bypass the gateway and create no transactions, so no permission/approval is needed — coverage is the only control. `common` runs every actual terminal path when they fit under the flood cap (`--max-test-orders`, 6 by default); above the cap it runs the checkout baseline, first-offer accept and decline, and a deduplicated shortest real receipt path, then adds one decline path per offer or downsell page not yet declined, up to the cap, and names any page left out. A page counts as covered only when an order clicks its decline; the `browser-test-order:upsell-action-coverage` verdict row warns naming each page whose decline no order clicked. `full` walks every actual terminal path in the selected checkout topology; cycles, missing routes, and reachable nonterminals block exhaustive proof before browser launch. The accidental-flood cap remains `6`, and an overflow names the exact explicit `--max-test-orders` raise. Localhost on any port is a Campaigns App Development domain for SDK QA with analytics suppressed; non-localhost preview/production origins still need SDK origin allowlist confirmation. Do not use external browser skills, the SDK test-mode event, or hand-built backend API orders as launch proof.
27
+ - Typed-card test-order proof uses `npx --no-install campaigns-os qa run --test-order <common|checkout|decline|accept|both|full|explicit-path>` through the tested checkout and rendered upsell controls. `qa run` has no permission flag: coverage is its only control. Test orders still land in the store as real orders (global test cards: no charge, no transaction) that someone may have to cancel. Unless the operator has already said test orders are fine for this campaign, ask once, up front in your first turn with your other setup questions, so the answer covers the whole build and QA and neither stops for it later. `common` runs every actual terminal path when they fit under the flood cap (`--max-test-orders`, 6 by default); above the cap it runs the checkout baseline, first-offer accept and decline, and a deduplicated shortest real receipt path, then adds one decline path per offer or downsell page not yet declined, up to the cap, and names any page left out. A page counts as covered only when an order clicks its decline; the `browser-test-order:upsell-action-coverage` verdict row warns naming each page whose decline no order clicked. `full` walks every actual terminal path in the selected checkout topology; cycles, missing routes, and reachable nonterminals block exhaustive proof before browser launch. The accidental-flood cap remains `6`, and an overflow names the exact explicit `--max-test-orders` raise. Localhost on any port is a Campaigns App Development domain for SDK QA with analytics suppressed; non-localhost preview/production origins still need SDK origin allowlist confirmation. Do not use external browser skills, the SDK test-mode event, or hand-built backend API orders as launch proof.
26
28
  - Analytics correctness is two-phase: the campaign-root visit inventories declared providers/tags only, and the same canonical typed-card run proves Purchase only from the topology-recognized final receipt document after `--analytics-settle`. Missing/unrecognized receipts are manual-review warnings; recognized receipt no-signal and capture/settle errors block. Only the genuine recognized-receipt/no-signal failure is waivable; capture/settle errors are not. Checkout/upsell signals remain whole-journey parity evidence and cannot satisfy a silent receipt.
27
29
  - Do not use `next.getCartData().cartLines` as cart-populated proof; use typed-card order read-back, `cart:updated` payload `items` / `summary.lines`, and rendered bundle DOM evidence.
28
30
  - Campaigns OS proof is not merchant launch readiness. Before launch, confirm production storefront URL, live payment methods, shipping markets, legal/support URLs, analytics expectations, and merchant-side configuration.
@@ -8,8 +8,10 @@ When this repository contains Campaigns OS artifacts, use them as the build hand
8
8
  - To pick up an existing campaign, run `npx --no-install campaigns-os readback .` and `npx --no-install campaigns-os next --packet campaign-runtime.build.json` before reading artifacts by hand; `next` blocks polish and QA again whenever the built output changes. The committed `.campaign-runtime/qa-verdict.json` keeps no order records or URLs: its `browser-test-order:<path>` assertions are the typed-card proof, and the full verdict is under `qa-output/`.
9
9
  - `.campaign-runtime/theme/theme-report.json`, when present, is optional brand-theme evidence. Generated `brand-theme.css` must load after `next-core.css`; missing or low-confidence theme is a warning/skipped reason, not permission to edit SDK-owned runtime surfaces.
10
10
 
11
+ A NEXT campaign is one connected commerce journey: the pages the CampaignSpec declares, in its order, through product choice, checkout, post-purchase offers and receipt. Creative intent — audience, purpose, layout, imagery, approved copy, visual style — belongs to the operator, the Campaign Build Brief and the accepted design source. Commerce behaviour — products, package IDs, prices, offers, shipping, routes, cart, checkout, payment and post-purchase — belongs to CampaignSpec/API values and the SDK. Before changing a campaign, read the normalized brief at `.campaign-runtime/input/campaign-build-brief.normalized.json` and the recorded decisions: `adapter_decisions`, `decisions` and `theme` in `.campaign-runtime/assembly-report.json`, and `assembly.template_decision_notes` in the Build Packet. The setup, build, Polish and QA prompts begin with a campaign intent summary, and every `next` result carries it (`intent_summary` in JSON); treat it as orientation, never as a source of prices or behaviour. When a revision is narrow, keep the recorded intent and ask only about missing choices that affect that revision. Ask the operator before saving brief answers.
12
+
11
13
  CampaignSpec validation is owned by the public `@nextcommerce/campaigns-os/campaign-spec` rules surfaced through doctor `spec.validation` findings; use structured rule/path detail when available.
12
14
 
13
15
  Preserve Campaign Cart SDK-owned commerce surfaces. Replace starter demo refs from CampaignSpec/API. Prepared AI/exported HTML must be converted into page-kit-ready source first: keep page-owned body markup, strip document wrappers, add YAML frontmatter, move shared CSS/assets into the campaign structure, and use Liquid helpers only for page-kit links/assets/includes. Landing/presell pages can preserve source design; checkout/upsell/downsell/receipt should use starter-template commerce surfaces as SDK contract references while campaign/source owns visual chrome. Reproduce the source design's own proof and urgency elements as designed (reviews, ratings, stock counters, countdowns, guarantees). They are the merchant's content; Campaigns OS does not review them, so do not remove, soften or flag them. Copy starter template families atomically with dependent pages, `_includes/`, `_layouts/`, `assets/css/`, and `assets/js/`; do not copy only checkout/receipt pages. Do not claim launch readiness until build, polish, deploy, and QA evidence are recorded.
14
16
 
15
- Before Polish becomes terminal or hands off to deploy/QA, install the package-owned Playwright browser with `npm run qa:install-browser`, serve the current build, and run `npx --no-install campaigns-os polish capture --packet campaign-runtime.build.json --base-url <served-current-build-url>`. After that checkpoint clears, QA must use the Campaigns OS Node/npm runner: run `npx --no-install campaigns-os qa resolve`, then run `npx --no-install campaigns-os qa run --browser --test-order common` against the tested URL. Typed-card test-order proof must use `npx --no-install campaigns-os qa run --test-order <common|checkout|decline|accept|both|full|explicit-path>` through the tested checkout and rendered upsell controls; global test cards bypass the gateway and create no transactions, so no permission/approval is needed. `common` runs every actual terminal path when they fit under the flood cap (`--max-test-orders`, 6 by default); above the cap it runs the checkout baseline, first-offer accept and decline, and a deduplicated shortest real receipt path, then adds one decline path per offer or downsell page not yet declined, up to the cap, and names any page left out. A page counts as covered only when an order clicks its decline; the `browser-test-order:upsell-action-coverage` verdict row warns naming each page whose decline no order clicked. `full` walks every actual terminal path in the selected checkout topology; cycles, missing routes, and reachable nonterminals block exhaustive proof before browser launch. The accidental-flood cap remains `6`, and an overflow names the exact explicit `--max-test-orders` raise. Analytics correctness is two-phase: the campaign-root visit inventories declared providers/tags only, and the same canonical typed-card run proves Purchase only from the topology-recognized final receipt document after `--analytics-settle`. Missing/unrecognized receipts are manual-review warnings; recognized receipt no-signal and capture/settle errors block. Only the genuine recognized-receipt/no-signal failure is waivable; capture/settle errors are not. Checkout/upsell signals remain whole-journey parity evidence and cannot satisfy a silent receipt. Do not use `next.getCartData().cartLines` as cart-populated proof; use typed-card order read-back, `cart:updated` payload `items` / `summary.lines`, and rendered bundle DOM evidence. Localhost on any port is a Campaigns App Development domain for SDK QA with analytics suppressed; non-localhost preview/production origins still need SDK origin allowlist confirmation. Do not use external browser skills, SDK test-mode events, or direct backend orders as launch proof. Campaigns OS proof is not merchant launch readiness; before launch, confirm production storefront URL, live payment methods, shipping markets, legal/support URLs, analytics expectations, and merchant-side configuration.
17
+ Before Polish becomes terminal or hands off to deploy/QA, install the package-owned Playwright browser with `npm run qa:install-browser`, serve the current build, and run `npx --no-install campaigns-os polish capture --packet campaign-runtime.build.json --base-url <served-current-build-url>`. After that checkpoint clears, QA must use the Campaigns OS Node/npm runner: run `npx --no-install campaigns-os qa resolve`, then run `npx --no-install campaigns-os qa run --browser --test-order common` against the tested URL. Typed-card test-order proof must use `npx --no-install campaigns-os qa run --test-order <common|checkout|decline|accept|both|full|explicit-path>` through the tested checkout and rendered upsell controls. `qa run` has no permission flag: coverage is its only control. Test orders still land in the store as real orders (global test cards: no charge, no transaction) that someone may have to cancel. Unless the operator has already said test orders are fine for this campaign, ask once, up front in your first turn with your other setup questions, so the answer covers the whole build and QA and neither stops for it later. `common` runs every actual terminal path when they fit under the flood cap (`--max-test-orders`, 6 by default); above the cap it runs the checkout baseline, first-offer accept and decline, and a deduplicated shortest real receipt path, then adds one decline path per offer or downsell page not yet declined, up to the cap, and names any page left out. A page counts as covered only when an order clicks its decline; the `browser-test-order:upsell-action-coverage` verdict row warns naming each page whose decline no order clicked. `full` walks every actual terminal path in the selected checkout topology; cycles, missing routes, and reachable nonterminals block exhaustive proof before browser launch. The accidental-flood cap remains `6`, and an overflow names the exact explicit `--max-test-orders` raise. Analytics correctness is two-phase: the campaign-root visit inventories declared providers/tags only, and the same canonical typed-card run proves Purchase only from the topology-recognized final receipt document after `--analytics-settle`. Missing/unrecognized receipts are manual-review warnings; recognized receipt no-signal and capture/settle errors block. Only the genuine recognized-receipt/no-signal failure is waivable; capture/settle errors are not. Checkout/upsell signals remain whole-journey parity evidence and cannot satisfy a silent receipt. Do not use `next.getCartData().cartLines` as cart-populated proof; use typed-card order read-back, `cart:updated` payload `items` / `summary.lines`, and rendered bundle DOM evidence. Localhost on any port is a Campaigns App Development domain for SDK QA with analytics suppressed; non-localhost preview/production origins still need SDK origin allowlist confirmation. Do not use external browser skills, SDK test-mode events, or direct backend orders as launch proof. Campaigns OS proof is not merchant launch readiness; before launch, confirm production storefront URL, live payment methods, shipping markets, legal/support URLs, analytics expectations, and merchant-side configuration.
@@ -8,6 +8,8 @@ alwaysApply: false
8
8
 
9
9
  Read Campaigns OS artifacts before editing campaign pages. To pick up an existing campaign, run `npx --no-install campaigns-os readback .` and `npx --no-install campaigns-os next --packet campaign-runtime.build.json` before reading artifacts by hand; `next` blocks polish and QA again whenever the built output changes. The committed `.campaign-runtime/qa-verdict.json` keeps no order records or URLs: its `browser-test-order:<path>` assertions are the typed-card proof, and the full verdict is under `qa-output/`. Treat CampaignSpec/API values as live commerce truth, starter-template contracts as SDK surface truth, and designed HTML/assets as visual/content intent. Treat CampaignSpec validation as owned by the public `@nextcommerce/campaigns-os/campaign-spec` rules surfaced through doctor `spec.validation` findings; use structured rule/path detail when available. If `context.theme` or `.campaign-runtime/theme/theme-report.json` exists, use it as optional brand-theme evidence; generated `brand-theme.css` must load after `next-core.css`, and missing/low-confidence theme is a warning or skipped reason, not permission to edit SDK-owned runtime surfaces.
10
10
 
11
+ A NEXT campaign is one connected commerce journey: the pages the CampaignSpec declares, in its order, through product choice, checkout, post-purchase offers and receipt. Creative intent — audience, purpose, layout, imagery, approved copy, visual style — belongs to the operator, the Campaign Build Brief and the accepted design source. Commerce behaviour — products, package IDs, prices, offers, shipping, routes, cart, checkout, payment and post-purchase — belongs to CampaignSpec/API values and the SDK. Before changing a campaign, read the normalized brief at `.campaign-runtime/input/campaign-build-brief.normalized.json` and the recorded decisions: `adapter_decisions`, `decisions` and `theme` in `.campaign-runtime/assembly-report.json`, and `assembly.template_decision_notes` in the Build Packet. The setup, build, Polish and QA prompts begin with a campaign intent summary, and every `next` result carries it (`intent_summary` in JSON); treat it as orientation, never as a source of prices or behaviour. When a revision is narrow, keep the recorded intent and ask only about missing choices that affect that revision. Ask the operator before saving brief answers.
12
+
11
13
  Do not carry over demo package, shipping, voucher, payment, tracking, footer, or SEO values. Prepared AI/exported HTML must be converted into page-kit-ready source first: keep page-owned body markup, strip document wrappers, add YAML frontmatter, move shared CSS/assets into the campaign structure, and use Liquid helpers only for page-kit links/assets/includes. Preserve prepared landing/presell source HTML when it is a real standalone design. For checkout/upsell/downsell/receipt, use starter-template commerce surfaces as SDK contract references while campaign/source owns visual chrome. Reproduce the source design's own proof and urgency elements as designed (reviews, ratings, stock counters, countdowns, guarantees). They are the merchant's content; Campaigns OS does not review them, so do not remove, soften or flag them. Copy starter template families atomically with dependent pages, `_includes/`, `_layouts/`, `assets/css/`, and `assets/js/`; do not copy only checkout/receipt pages. Emit SDK routing meta tags as campaign-root paths such as `/campaign-slug/upsell/`. Preserve SDK-owned checkout/cart/upsell/receipt surfaces. For `shop-three-step`, shipping is dynamic via `window.next.getShippingMethods()`.
12
14
 
13
- Before Polish becomes terminal or hands off to deploy/QA, install the package-owned Playwright browser with `npm run qa:install-browser`, serve the current build, and run `npx --no-install campaigns-os polish capture --packet campaign-runtime.build.json --base-url <served-current-build-url>`. After that checkpoint clears, QA uses the Campaigns OS Node/npm runner: `npx --no-install campaigns-os qa resolve`, then `npx --no-install campaigns-os qa run --browser --test-order common` against the tested URL. Test-order proof uses `npx --no-install campaigns-os qa run --test-order <common|checkout|decline|accept|both|full|explicit-path>` through the tested checkout and rendered upsell controls; global test cards bypass the gateway and create no transactions, so no permission/approval is needed. `common` runs every actual terminal path when they fit under the flood cap (`--max-test-orders`, 6 by default); above the cap it runs the checkout baseline, first-offer accept and decline, and a deduplicated shortest real receipt path, then adds one decline path per offer or downsell page not yet declined, up to the cap, and names any page left out. A page counts as covered only when an order clicks its decline; the `browser-test-order:upsell-action-coverage` verdict row warns naming each page whose decline no order clicked. `full` walks every actual terminal path in the selected checkout topology; cycles, missing routes, and reachable nonterminals block exhaustive proof before browser launch. The accidental-flood cap remains `6`, and an overflow names the exact explicit `--max-test-orders` raise. Analytics correctness is two-phase: the campaign-root visit inventories declared providers/tags only, and the same canonical typed-card run proves Purchase only from the topology-recognized final receipt document after `--analytics-settle`. Missing/unrecognized receipts are manual-review warnings; recognized receipt no-signal and capture/settle errors block. Only the genuine recognized-receipt/no-signal failure is waivable; capture/settle errors are not. Checkout/upsell signals remain whole-journey parity evidence and cannot satisfy a silent receipt. Do not use `next.getCartData().cartLines` as cart-populated proof; use typed-card order read-back, `cart:updated` payload `items` / `summary.lines`, and rendered bundle DOM evidence. Localhost on any port is a Campaigns App Development domain for SDK QA with analytics suppressed; non-localhost preview/production origins still need SDK origin allowlist confirmation. External browser skills, SDK test-mode events, and direct backend orders are not launch proof. Campaigns OS proof is not merchant launch readiness; before launch, confirm production storefront URL, live payment methods, shipping markets, legal/support URLs, analytics expectations, and merchant-side configuration.
15
+ Before Polish becomes terminal or hands off to deploy/QA, install the package-owned Playwright browser with `npm run qa:install-browser`, serve the current build, and run `npx --no-install campaigns-os polish capture --packet campaign-runtime.build.json --base-url <served-current-build-url>`. After that checkpoint clears, QA uses the Campaigns OS Node/npm runner: `npx --no-install campaigns-os qa resolve`, then `npx --no-install campaigns-os qa run --browser --test-order common` against the tested URL. Test-order proof uses `npx --no-install campaigns-os qa run --test-order <common|checkout|decline|accept|both|full|explicit-path>` through the tested checkout and rendered upsell controls. `qa run` has no permission flag: coverage is its only control. Test orders still land in the store as real orders (global test cards: no charge, no transaction) that someone may have to cancel. Unless the operator has already said test orders are fine for this campaign, ask once, up front in your first turn with your other setup questions, so the answer covers the whole build and QA and neither stops for it later. `common` runs every actual terminal path when they fit under the flood cap (`--max-test-orders`, 6 by default); above the cap it runs the checkout baseline, first-offer accept and decline, and a deduplicated shortest real receipt path, then adds one decline path per offer or downsell page not yet declined, up to the cap, and names any page left out. A page counts as covered only when an order clicks its decline; the `browser-test-order:upsell-action-coverage` verdict row warns naming each page whose decline no order clicked. `full` walks every actual terminal path in the selected checkout topology; cycles, missing routes, and reachable nonterminals block exhaustive proof before browser launch. The accidental-flood cap remains `6`, and an overflow names the exact explicit `--max-test-orders` raise. Analytics correctness is two-phase: the campaign-root visit inventories declared providers/tags only, and the same canonical typed-card run proves Purchase only from the topology-recognized final receipt document after `--analytics-settle`. Missing/unrecognized receipts are manual-review warnings; recognized receipt no-signal and capture/settle errors block. Only the genuine recognized-receipt/no-signal failure is waivable; capture/settle errors are not. Checkout/upsell signals remain whole-journey parity evidence and cannot satisfy a silent receipt. Do not use `next.getCartData().cartLines` as cart-populated proof; use typed-card order read-back, `cart:updated` payload `items` / `summary.lines`, and rendered bundle DOM evidence. Localhost on any port is a Campaigns App Development domain for SDK QA with analytics suppressed; non-localhost preview/production origins still need SDK origin allowlist confirmation. External browser skills, SDK test-mode events, and direct backend orders are not launch proof. Campaigns OS proof is not merchant launch readiness; before launch, confirm production storefront URL, live payment methods, shipping markets, legal/support URLs, analytics expectations, and merchant-side configuration.
@@ -8,7 +8,11 @@
8
8
  * a need to subset them. If that need shows up, split into
9
9
  * CampaignPaymentKey and CampaignRefId.
10
10
  *
11
- * Message text inherited verbatim from the pre-#110 validator at migration time.
11
+ * The ref_id message is inherited verbatim from the pre-#110 validator. The
12
+ * payment_env_key message keeps that validator's first sentence, which
13
+ * consumers match, and adds which field is empty and where its value comes
14
+ * from: a Campaigns API key in the spec (campaign.campaigns_api_key) is a
15
+ * different field and does not satisfy it.
12
16
  */
13
17
  import type { Rule } from '../types.ts';
14
18
  export declare const CampaignMetadata: Rule;