@nextcommerce/campaigns-os 1.48.0 → 1.50.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 (38) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/agents/claude/CLAUDE.md +5 -4
  3. package/agents/codex/AGENTS.md +6 -5
  4. package/agents/copilot/copilot-instructions.md +3 -3
  5. package/agents/cursor/campaigns-os.mdc +3 -3
  6. package/compatibility.json +1 -1
  7. package/contracts/effects.v1.json +173 -0
  8. package/contracts/release-ledger.json +333 -0
  9. package/contracts/supported-surface.json +4 -4
  10. package/docs/brand-theme-bridge.md +12 -6
  11. package/docs/build-packet.md +8 -3
  12. package/docs/local-setup.md +1 -1
  13. package/docs/orientation-contract-reference.md +1 -1
  14. package/docs/qa-and-test-orders.md +21 -7
  15. package/docs/runtime-readiness.md +1 -1
  16. package/docs/skills-revision.md +10 -10
  17. package/package.json +1 -1
  18. package/schemas/campaign-runtime-build-packet.v0.schema.json +4 -0
  19. package/schemas/campaigns-os-qa-verdict.v0.schema.json +8 -3
  20. package/skills/campaign-lifecycle-orientation/SKILL.md +3 -3
  21. package/skills/campaign-readback-classification/SKILL.md +3 -3
  22. package/skills/campaign-run-evidence/SKILL.md +3 -3
  23. package/skills/contribution-intake/SKILL.md +3 -3
  24. package/skills/next-campaigns-build/SKILL.md +4 -4
  25. package/skills/next-campaigns-os/SKILL.md +3 -3
  26. package/skills/next-campaigns-os-setup/SKILL.md +3 -3
  27. package/skills/next-campaigns-polish/SKILL.md +5 -4
  28. package/skills/next-campaigns-qa/SKILL.md +3 -3
  29. package/skills.json +10 -10
  30. package/src/brand-theme.mjs +13 -2
  31. package/src/cli.mjs +11 -9
  32. package/src/install-mode.mjs +0 -8
  33. package/src/invocation.mjs +3 -1
  34. package/src/qa-binding-evidence.mjs +21 -0
  35. package/src/qa-browser.mjs +30 -1
  36. package/src/qa-node.mjs +10 -2
  37. package/src/stage-record.mjs +303 -22
  38. package/src/theme-gate.mjs +3 -3
@@ -4631,6 +4631,339 @@
4631
4631
  }
4632
4632
  ],
4633
4633
  "entry_sha256": "abc8749936f439d33b92b3bfdc1c954d0b38df24e5a80a2e3862123494b47f07"
4634
+ },
4635
+ {
4636
+ "id": "RL-0208",
4637
+ "sequence": 200,
4638
+ "date": "2026-10-02",
4639
+ "kind": "release",
4640
+ "surface_version": "1.49.0",
4641
+ "changelog_section": "1.49.0",
4642
+ "changelog_sha256": "89c063cc2229b29d161820612d3724a7daecc03c3d1531c0046b503941bd0667",
4643
+ "agent_impact": "Surface 1.49.0. Record an applied brand layer with campaigns-os record theme --packet <p> after record build, instead of editing report.theme by hand: it reads each built commerce page's stylesheet links, requires brand-theme.css (or checkout-brand.css) after next-core.css on every page that loads next-core.css, and writes report.theme (status applied, load_order after-next-core, css_path, commerce_pages, evidence), clearing any earlier theme waiver. Record a local preview with campaigns-os record deploy --packet <p> --base-url <url> after record polish, instead of editing deploy.preview_url and stages.deploy by hand: the URL must be a loopback origin naming the campaign's route root, every built page under it must answer 2xx, and it writes the packet's deploy.preview_url and stages.deploy completed. Both are refused, writing nothing, otherwise. The Build Packet schema now accepts the template families apollo, apollo-mv-single-step, arjuna and karna, so a packet on those families validates. Bundled skills carry revision 1.49.0+skills.1: an agent that loaded 1.48.0+skills.1 should start a fresh session. Install the 1.49.0 package with the documented local setup command.",
4644
+ "compatibility": "additive",
4645
+ "migration": "none",
4646
+ "changes": [
4647
+ {
4648
+ "class": "compatibility_policy",
4649
+ "path": "compatibility.json",
4650
+ "surface_entry": null,
4651
+ "summary": "Name the package version (1.49.0)."
4652
+ },
4653
+ {
4654
+ "class": "compatibility_policy",
4655
+ "path": "contracts/effects.v1.json",
4656
+ "surface_entry": "contracts/effects.v1.json",
4657
+ "summary": "Declare record theme (writes the Assembly Report) and record deploy (writes the Build Packet and the Assembly Report, and requests each built page on the loopback --base-url), each with its --dry-run form, and the doctor sidecar stamp when one is retained."
4658
+ },
4659
+ {
4660
+ "class": "compatibility_policy",
4661
+ "path": "contracts/supported-surface.json",
4662
+ "surface_entry": "contracts/supported-surface.json",
4663
+ "summary": "Advance the supported surface to 1.49.0 and record the effects contract and Build Packet schema hashes."
4664
+ },
4665
+ {
4666
+ "class": "named_surface",
4667
+ "path": "docs/brand-theme-bridge.md",
4668
+ "surface_entry": "docs/brand-theme-bridge.md",
4669
+ "summary": "Record an applied brand layer with record theme, which writes commerce_pages from the built pages that load next-core.css."
4670
+ },
4671
+ {
4672
+ "class": "named_surface",
4673
+ "path": "docs/build-packet.md",
4674
+ "surface_entry": "docs/build-packet.md",
4675
+ "summary": "Add record theme and record deploy to the record command table, name record theme in the starter-palette lane, and name record deploy where the local-serve URL is recorded."
4676
+ },
4677
+ {
4678
+ "class": "named_surface",
4679
+ "path": "docs/qa-and-test-orders.md",
4680
+ "surface_entry": "docs/qa-and-test-orders.md",
4681
+ "summary": "In the local proof sequence, record the served URL with record deploy once polish is recorded."
4682
+ },
4683
+ {
4684
+ "class": "named_surface",
4685
+ "path": "docs/local-setup.md",
4686
+ "surface_entry": "docs/local-setup.md",
4687
+ "summary": "Pin the local setup install command to the 1.49.0 package."
4688
+ },
4689
+ {
4690
+ "class": "documentation",
4691
+ "path": "docs/orientation-contract-reference.md",
4692
+ "surface_entry": "docs/orientation-contract-reference.md",
4693
+ "summary": "Regenerate the orientation reference for surface 1.49.0."
4694
+ },
4695
+ {
4696
+ "class": "named_surface",
4697
+ "path": "docs/runtime-readiness.md",
4698
+ "surface_entry": "docs/runtime-readiness.md",
4699
+ "summary": "Regenerate the runtime readiness reference for surface 1.49.0."
4700
+ },
4701
+ {
4702
+ "class": "named_surface",
4703
+ "path": "docs/skills-revision.md",
4704
+ "surface_entry": "docs/skills-revision.md",
4705
+ "summary": "Use the current 1.49.0+skills.1 bundle in executable examples."
4706
+ },
4707
+ {
4708
+ "class": "generated_runtime",
4709
+ "path": "package-lock.json",
4710
+ "surface_entry": null,
4711
+ "summary": "Update release metadata for 1.49.0."
4712
+ },
4713
+ {
4714
+ "class": "package_export",
4715
+ "path": "package.json",
4716
+ "surface_entry": null,
4717
+ "summary": "Release 1.49.0."
4718
+ },
4719
+ {
4720
+ "class": "schema",
4721
+ "path": "schemas/campaign-runtime-build-packet.v0.schema.json",
4722
+ "surface_entry": "schemas/campaign-runtime-build-packet.v0.schema.json",
4723
+ "summary": "assembly.template_family accepts apollo, apollo-mv-single-step, arjuna and karna, every family the catalog and the private template sources name; nothing is removed."
4724
+ },
4725
+ {
4726
+ "class": "skill",
4727
+ "path": "skills.json",
4728
+ "surface_entry": "skills.json",
4729
+ "summary": "Advance the bundled skill revision to 1.49.0+skills.1 and each skill version one patch."
4730
+ },
4731
+ {
4732
+ "class": "skill",
4733
+ "path": "skills/campaign-lifecycle-orientation/SKILL.md",
4734
+ "surface_entry": null,
4735
+ "summary": "Publish the 1.49.0+skills.1 bundle revision."
4736
+ },
4737
+ {
4738
+ "class": "skill",
4739
+ "path": "skills/campaign-readback-classification/SKILL.md",
4740
+ "surface_entry": null,
4741
+ "summary": "Publish the 1.49.0+skills.1 bundle revision."
4742
+ },
4743
+ {
4744
+ "class": "skill",
4745
+ "path": "skills/campaign-run-evidence/SKILL.md",
4746
+ "surface_entry": null,
4747
+ "summary": "Publish the 1.49.0+skills.1 bundle revision."
4748
+ },
4749
+ {
4750
+ "class": "skill",
4751
+ "path": "skills/contribution-intake/SKILL.md",
4752
+ "surface_entry": null,
4753
+ "summary": "Publish the 1.49.0+skills.1 bundle revision."
4754
+ },
4755
+ {
4756
+ "class": "skill",
4757
+ "path": "skills/next-campaigns-build/SKILL.md",
4758
+ "surface_entry": null,
4759
+ "summary": "Run record theme after record build when a brand theme is applied, instead of editing report.theme; publish the 1.49.0+skills.1 bundle revision."
4760
+ },
4761
+ {
4762
+ "class": "skill",
4763
+ "path": "skills/next-campaigns-os-setup/SKILL.md",
4764
+ "surface_entry": null,
4765
+ "summary": "Publish the 1.49.0+skills.1 bundle revision."
4766
+ },
4767
+ {
4768
+ "class": "skill",
4769
+ "path": "skills/next-campaigns-os/SKILL.md",
4770
+ "surface_entry": null,
4771
+ "summary": "Publish the 1.49.0+skills.1 bundle revision."
4772
+ },
4773
+ {
4774
+ "class": "skill",
4775
+ "path": "skills/next-campaigns-polish/SKILL.md",
4776
+ "surface_entry": null,
4777
+ "summary": "Name record build and record theme in the theme gate's apply lane; publish the 1.49.0+skills.1 bundle revision."
4778
+ },
4779
+ {
4780
+ "class": "skill",
4781
+ "path": "skills/next-campaigns-qa/SKILL.md",
4782
+ "surface_entry": null,
4783
+ "summary": "Publish the 1.49.0+skills.1 bundle revision."
4784
+ },
4785
+ {
4786
+ "class": "cli_surface",
4787
+ "path": "src/cli.mjs",
4788
+ "surface_entry": "record",
4789
+ "summary": "Add record theme and record deploy to the usage text; name record theme in the build prompt and starter-palette notice, and record deploy in next's local-serve deploy action and the deploy prompt."
4790
+ }
4791
+ ],
4792
+ "entry_sha256": "044cecbdb3e7b2bf182ec907320d8beb45361095df4a1df1ec6a119639f1b28d"
4793
+ },
4794
+ {
4795
+ "id": "RL-0209",
4796
+ "sequence": 201,
4797
+ "date": "2026-10-02",
4798
+ "kind": "release",
4799
+ "surface_version": null,
4800
+ "changelog_section": "1.49.0+agent.1",
4801
+ "changelog_sha256": "b2b72f1759ab5a42e177b386aa70d7fbd56a1e2730d5143cf91b14d0f3e3c0ab",
4802
+ "agent_impact": "Run toolkit commands from the campaign folder as npx --no-install campaigns-os …, as the agent context now spells them; a bare campaigns-os resolves through PATH to another copy or to nothing. Reproduce the source design's own proof and urgency elements as designed; do not remove, soften or flag them. An installed agent context keeps the old text until install-agent-context refreshes it.",
4803
+ "compatibility": "compatible",
4804
+ "migration": "none",
4805
+ "changes": [
4806
+ {
4807
+ "class": "documentation",
4808
+ "path": "agents/claude/CLAUDE.md",
4809
+ "surface_entry": "agents/claude/CLAUDE.md",
4810
+ "summary": "Spell every toolkit command npx --no-install campaigns-os, and carry the build skill's rule to reproduce the source design's proof and urgency elements without removing, softening or flagging them."
4811
+ },
4812
+ {
4813
+ "class": "documentation",
4814
+ "path": "agents/codex/AGENTS.md",
4815
+ "surface_entry": "agents/codex/AGENTS.md",
4816
+ "summary": "Spell every toolkit command npx --no-install campaigns-os, and carry the build skill's rule to reproduce the source design's proof and urgency elements without removing, softening or flagging them."
4817
+ },
4818
+ {
4819
+ "class": "documentation",
4820
+ "path": "agents/copilot/copilot-instructions.md",
4821
+ "surface_entry": "agents/copilot/copilot-instructions.md",
4822
+ "summary": "Spell every toolkit command npx --no-install campaigns-os, and carry the build skill's rule to reproduce the source design's proof and urgency elements without removing, softening or flagging them."
4823
+ },
4824
+ {
4825
+ "class": "documentation",
4826
+ "path": "agents/cursor/campaigns-os.mdc",
4827
+ "surface_entry": "agents/cursor/campaigns-os.mdc",
4828
+ "summary": "Spell every toolkit command npx --no-install campaigns-os, and carry the build skill's rule to reproduce the source design's proof and urgency elements without removing, softening or flagging them."
4829
+ }
4830
+ ],
4831
+ "entry_sha256": "11f7c4b1771dc24155cc03b92f942a115b834de9528e12da51bd9d7b73fb3572"
4832
+ },
4833
+ {
4834
+ "id": "RL-0210",
4835
+ "sequence": 202,
4836
+ "date": "2026-10-02",
4837
+ "kind": "release",
4838
+ "surface_version": "1.50.0",
4839
+ "changelog_section": "1.50.0",
4840
+ "changelog_sha256": "dd65c5d7f65efddf1366ea3f1220660d98918bdd558dd18757b61a5152553b23",
4841
+ "agent_impact": "Surface 1.50.0. With qa run --browser, page-binding:<page_id> compares the key the Campaign Cart SDK sent on its Campaigns API requests with the expected key (evidence observation sdk_request, source kind sdk_request): match passes and mismatch is a blocker; the key is never recorded. A page that sends no Campaigns API request, a run with no single expected key, and qa run without --browser keep the static declaration read. A verdict consumer must accept observation sdk_request and source kind sdk_request in page-binding evidence. Ships 1.49.0+agent.1 (the agent context spells npx --no-install campaigns-os and carries the build skill's proof rule) and 1.49.0+agent.2 (a comment-only change). Bundled skills carry revision 1.50.0+skills.1 with unchanged text: an agent that loaded an earlier revision should start a fresh session. Install the 1.50.0 package with the documented local setup command.",
4842
+ "compatibility": "additive",
4843
+ "migration": "none",
4844
+ "changes": [
4845
+ {
4846
+ "class": "compatibility_policy",
4847
+ "path": "compatibility.json",
4848
+ "surface_entry": null,
4849
+ "summary": "Advance the declared version to 1.50.0."
4850
+ },
4851
+ {
4852
+ "class": "compatibility_policy",
4853
+ "path": "contracts/supported-surface.json",
4854
+ "surface_entry": "contracts/supported-surface.json",
4855
+ "summary": "Advance surface_version to 1.50.0 and record the QA verdict schema hash."
4856
+ },
4857
+ {
4858
+ "class": "named_surface",
4859
+ "path": "docs/local-setup.md",
4860
+ "surface_entry": "docs/local-setup.md",
4861
+ "summary": "Pin the 1.50.0 package in the local setup install command."
4862
+ },
4863
+ {
4864
+ "class": "documentation",
4865
+ "path": "docs/orientation-contract-reference.md",
4866
+ "surface_entry": "docs/orientation-contract-reference.md",
4867
+ "summary": "Regenerate at surface 1.50.0."
4868
+ },
4869
+ {
4870
+ "class": "named_surface",
4871
+ "path": "docs/qa-and-test-orders.md",
4872
+ "surface_entry": "docs/qa-and-test-orders.md",
4873
+ "summary": "Describe the browser pass's sdk_request page-binding observation and when the static read still applies."
4874
+ },
4875
+ {
4876
+ "class": "named_surface",
4877
+ "path": "docs/runtime-readiness.md",
4878
+ "surface_entry": "docs/runtime-readiness.md",
4879
+ "summary": "Regenerate at surface 1.50.0."
4880
+ },
4881
+ {
4882
+ "class": "named_surface",
4883
+ "path": "docs/skills-revision.md",
4884
+ "surface_entry": "docs/skills-revision.md",
4885
+ "summary": "Use the current 1.50.0+skills.1 bundle in executable examples."
4886
+ },
4887
+ {
4888
+ "class": "generated_runtime",
4889
+ "path": "package-lock.json",
4890
+ "surface_entry": null,
4891
+ "summary": "Advance the package version to 1.50.0."
4892
+ },
4893
+ {
4894
+ "class": "package_export",
4895
+ "path": "package.json",
4896
+ "surface_entry": null,
4897
+ "summary": "Advance the package version to 1.50.0."
4898
+ },
4899
+ {
4900
+ "class": "schema",
4901
+ "path": "schemas/campaigns-os-qa-verdict.v0.schema.json",
4902
+ "surface_entry": "schemas/campaigns-os-qa-verdict.v0.schema.json",
4903
+ "summary": "Accept observation sdk_request and the sdk_request source kind in page-binding evidence."
4904
+ },
4905
+ {
4906
+ "class": "skill",
4907
+ "path": "skills.json",
4908
+ "surface_entry": "skills.json",
4909
+ "summary": "Advance the bundled skill revision to 1.50.0+skills.1 and each skill version one patch."
4910
+ },
4911
+ {
4912
+ "class": "skill",
4913
+ "path": "skills/next-campaigns-os/SKILL.md",
4914
+ "surface_entry": null,
4915
+ "summary": "Publish the 1.50.0+skills.1 bundle revision."
4916
+ },
4917
+ {
4918
+ "class": "skill",
4919
+ "path": "skills/next-campaigns-os-setup/SKILL.md",
4920
+ "surface_entry": null,
4921
+ "summary": "Publish the 1.50.0+skills.1 bundle revision."
4922
+ },
4923
+ {
4924
+ "class": "skill",
4925
+ "path": "skills/next-campaigns-build/SKILL.md",
4926
+ "surface_entry": null,
4927
+ "summary": "Publish the 1.50.0+skills.1 bundle revision."
4928
+ },
4929
+ {
4930
+ "class": "skill",
4931
+ "path": "skills/next-campaigns-polish/SKILL.md",
4932
+ "surface_entry": null,
4933
+ "summary": "Publish the 1.50.0+skills.1 bundle revision."
4934
+ },
4935
+ {
4936
+ "class": "skill",
4937
+ "path": "skills/next-campaigns-qa/SKILL.md",
4938
+ "surface_entry": null,
4939
+ "summary": "Publish the 1.50.0+skills.1 bundle revision."
4940
+ },
4941
+ {
4942
+ "class": "skill",
4943
+ "path": "skills/campaign-lifecycle-orientation/SKILL.md",
4944
+ "surface_entry": null,
4945
+ "summary": "Publish the 1.50.0+skills.1 bundle revision."
4946
+ },
4947
+ {
4948
+ "class": "skill",
4949
+ "path": "skills/campaign-run-evidence/SKILL.md",
4950
+ "surface_entry": null,
4951
+ "summary": "Publish the 1.50.0+skills.1 bundle revision."
4952
+ },
4953
+ {
4954
+ "class": "skill",
4955
+ "path": "skills/campaign-readback-classification/SKILL.md",
4956
+ "surface_entry": null,
4957
+ "summary": "Publish the 1.50.0+skills.1 bundle revision."
4958
+ },
4959
+ {
4960
+ "class": "skill",
4961
+ "path": "skills/contribution-intake/SKILL.md",
4962
+ "surface_entry": null,
4963
+ "summary": "Publish the 1.50.0+skills.1 bundle revision."
4964
+ }
4965
+ ],
4966
+ "entry_sha256": "3fed96311daa5f153704a1ec7ded952e1e00065ce9f81fd4d3350f13beba964a"
4634
4967
  }
4635
4968
  ]
4636
4969
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "_note": "The downstream contract manifest. Everything listed here is SUPPORTED SURFACE: consumers (campaigns-agent, campaign-builder, the private ops repo, page-kit campaign repos) may depend on it, and changing it is a deliberate act — hashed entries require a surface_version bump in the same change (check-supported-surface.mjs --base, mirroring the skills.json bump gate), named entries must keep existing at their path, cli_commands must keep resolving in the CLI dispatch, package_exports must stay exported, and every entry must ship in the npm pack (files[] coverage). Anything NOT listed here — src/** internals, scripts/** checkers, examples/**, prompts/**, contracts/** other than this file and the entries named[] below (the orientation contract, the release ledger, and the consumer-facing orientation fixtures) — is implementation: consumers may read it for context but must not build on it, and it can change without notice. Rationale and the compatibility promise: docs/supported-surface.md.",
3
- "surface_version": "1.48.0",
3
+ "surface_version": "1.50.0",
4
4
  "package_exports": [
5
5
  "./commercial-journey",
6
6
  "./commercial-parity",
@@ -66,7 +66,7 @@
66
66
  "sha256": "f310c1b7844d9b1994864d15858ff0c6f17efc1e5285ba193d181bdb2dd4be2f"
67
67
  },
68
68
  "schemas/campaign-runtime-build-packet.v0.schema.json": {
69
- "sha256": "47c2899f409ed5927cf410bddd63e7bb8a9d154eabf7a07455777aed118212e9"
69
+ "sha256": "5002529c5979c4d8fef7e298de82ebdf625ffbd11f5284ba684d96d173788d62"
70
70
  },
71
71
  "schemas/campaign-spec.v4.schema.json": {
72
72
  "sha256": "af56c3de638d3c4e1fd5df8c2d733754d9ad16caa23b5bec92ce3a6f71bd0271"
@@ -84,7 +84,7 @@
84
84
  "sha256": "3c40fd79f218d1b95f0424f9a2b67db140a306329e2ca02793e2740d3afded59"
85
85
  },
86
86
  "schemas/campaigns-os-qa-verdict.v0.schema.json": {
87
- "sha256": "ec4514e81a791a63bc0b358d7977aa823f3c5524e0f5caaba2712e66f262e18a"
87
+ "sha256": "8c689d29850b2b894c17438ee4411363033bae6f805820af53b7a04a5693d0e0"
88
88
  },
89
89
  "schemas/campaigns-os-doctor-output.v0.schema.json": {
90
90
  "sha256": "87866e619d2a18f01a208a1682cb14a2079f61df51c642f4e1902b6e6bce0ce5"
@@ -120,7 +120,7 @@
120
120
  "sha256": "dfc9abed38d456969e47a21f606d308a03bdf036f3466f7d6e47a602747dacf4"
121
121
  },
122
122
  "contracts/effects.v1.json": {
123
- "sha256": "5f8c6f2f88fc9e0c11c09e265640071d3c1d81fd96c6e05283ed5cfed21d1d59"
123
+ "sha256": "75cdd0bc33788d8d6af10c995f362987725f622bf42700f1b70b39bbd87e6878"
124
124
  },
125
125
  "schemas/campaigns-os-effects.v1.schema.json": {
126
126
  "sha256": "3eadd22169ab98bc7c2f682af2751267605170581182158d96be035b0cfe44dc"
@@ -151,8 +151,9 @@ When `theme inspect` reports `can_generate: true` and the campaign ships
151
151
  commerce pages (checkout/upsell/downsell/receipt), the gate **blocks**
152
152
  `next polish`, `next deploy`, `next qa`, and `qa run` until one of:
153
153
 
154
- - the brand layer is generated and recorded as applied
155
- (`report.theme.status: applied`, `load_order: after-next-core`), or
154
+ - the brand layer is generated, linked and recorded as applied with
155
+ `campaigns-os record theme --packet <p>` (`report.theme.status: applied`,
156
+ `load_order: after-next-core`), or
156
157
  - an explicit waiver is recorded:
157
158
  `campaigns-os theme waive --packet <p> --reason "<why>" --waived-by "<named human>"`
158
159
  (optionally `--expires-at <ISO>`; placeholders such as "operator" are refused), or
@@ -197,8 +198,12 @@ If a fresh `brand-theme.css` exists:
197
198
  3. Preserve SDK-owned runtime surfaces: `data-next-*`, package selectors,
198
199
  payment fields, totals, submit controls, receipt templates, route meta tags,
199
200
  and SDK JavaScript.
200
- 4. Record `report.theme.status`, `css_path`, `commerce_pages`, `load_order`,
201
- and evidence.
201
+ 4. Rebuild, run `campaigns-os record build --packet <p>`, then
202
+ `campaigns-os record theme --packet <p>`. It reads each built commerce
203
+ page's stylesheet links and records `report.theme.status`, `css_path`,
204
+ `commerce_pages`, `load_order` and evidence. It refuses, writing nothing,
205
+ when a page that loads `next-core.css` does not load the brand layer after
206
+ it, or when no built commerce page loads `next-core.css`.
202
207
 
203
208
  ### Where next-core.css belongs
204
209
 
@@ -212,8 +217,9 @@ stylesheets off those pages and list them only in the frontmatter styles of
212
217
  the pages that render family components, often just checkout.
213
218
 
214
219
  `report.theme.commerce_pages` is the list of pages where the brand layer was
215
- applied, recorded by the build. Record the pages you actually scoped, for
216
- example `commerce_pages: ["checkout"]`. The theme gate does not compare this
220
+ applied. `record theme` writes it from the built pages: every commerce page
221
+ that loads `next-core.css`, for example `commerce_pages: ["checkout"]`. A page
222
+ that loads neither stylesheet is left out and noted in the evidence. The theme gate does not compare this
217
223
  list with the funnel. It passes on `report.theme.status: applied` with
218
224
  `load_order: after-next-core`. The gate's own `commerce_pages` output is a
219
225
  different field: every checkout, upsell, downsell, receipt or thank-you page
@@ -970,8 +970,9 @@ starter palette is acceptable>" --waived-by "<named human>"`, optionally
970
970
  (status and severity — never `fail`) and keeps the shipped palette visible in
971
971
  the verdict; or hand-author the brand
972
972
  layer — write `brand-theme.css`, list it after `next-core.css` in commerce-page
973
- frontmatter styles, rebuild, and record `report.theme.status: applied` with
974
- `load_order: after-next-core`. Nothing waives the gate on the operator's
973
+ frontmatter styles, rebuild, `record build`, and `record theme`, which records
974
+ `report.theme.status: applied` with `load_order: after-next-core` from the
975
+ built pages. Nothing waives the gate on the operator's
975
976
  behalf. See [Brand Theme Bridge](./brand-theme-bridge.md) for both lanes in
976
977
  full.
977
978
 
@@ -1237,7 +1238,9 @@ loopback host (`127.0.0.1`, `[::1]`) with a ready line naming the
1237
1238
  `http://localhost:<port>/` fallback, and warns (`deploy.local_serve_url`) when
1238
1239
  the recorded URL is neither.
1239
1240
  `next` at the deploy stage then hands off a serve-locally prompt and action
1240
- instead of a ship-to-host one. The directory to serve is `_site/`; for a
1241
+ instead of a ship-to-host one, and the served URL is recorded with
1242
+ `campaigns-os record deploy --packet <p> --base-url <url>` (the record command
1243
+ table below). The directory to serve is `_site/`; for a
1241
1244
  root-served campaign (`campaign.route_root: "/"`) the handoff adds that pages
1242
1245
  are served at site-root paths while assets keep the `/<public_route_slug>/`
1243
1246
  prefix, so `_site/` needs the same rewrite of root-level page routes onto
@@ -1609,6 +1612,8 @@ hand-editing `.campaign-runtime/` JSON:
1609
1612
  | `campaigns-os record setup --packet <p>` | Build Context `scaffold.required=false` (`handoff_skill` next-campaigns-build) and `stages.setup` completed | the campaign output directory (`assembly.output_dir`) does not exist, or there is no Build Context or Assembly Report |
1610
1613
  | `campaigns-os record build --packet <p>` | `stages.assembly` completed with `build_fingerprint` = doctor's `derived.build_output_fingerprint.value`, `source_package_material_fingerprint` = the report's Design Source Package material fingerprint when present, and `stages.polish` reset to `required` (`required_by` build, `required_for` qa) unless its evidence is bound to this exact output | doctor cannot compute the fingerprint (no `_site/<public_route_slug>/`), setup is still required, or `stages.setup` is not terminal |
1611
1614
  | `campaigns-os record polish --packet <p> --evidence <file>` | `stages.polish` from the file (`docs/polish-evidence.md` §7: completed, blocked or skipped), bound to doctor's current fingerprint; `report.theme.repair_loop_defect` when the file sets it | build is not recorded for the current output, the file has a shape error (named by field), or, for a completed status, the polish gate doctor evaluates would not pass on the result |
1615
+ | `campaigns-os record theme --packet <p>` | `report.theme`: status `applied`, `load_order` `after-next-core`, `css_path`, `commerce_pages` and per-page evidence read from each built commerce page's stylesheet links; any earlier theme waiver is cleared | build is not recorded for the current output, the campaign ships no commerce pages, a built commerce page that loads `next-core.css` does not load `brand-theme.css` (or `checkout-brand.css`) after it or links one missing from the built output, or no built commerce page loads `next-core.css` |
1616
+ | `campaigns-os record deploy --packet <p> --base-url <url>` | the packet's `deploy.preview_url` and `stages.deploy` completed with the URL in `outputs` and one evidence line per built page that answered (each page is requested under the URL first) | the packet is not `local-serve`, the URL is not a loopback origin naming the campaign's route root, a built page does not answer 2xx, polish is not recorded, the built output changed since build was recorded, or the theme gate is blocked |
1612
1617
 
1613
1618
  Each command also refuses a stage `next` has not reached: while doctor's
1614
1619
  prepare-build gate is set (`next` answers prepare-build) or while an earlier
@@ -3,7 +3,7 @@
3
3
  For a new campaign, create an empty working folder and run this from it:
4
4
 
5
5
  ```sh
6
- npm init -y && npm install --save-exact next-campaign-page-kit@0.2.0 && npm install --save-dev --save-exact @nextcommerce/campaigns-os@1.48.0 && npx --no-install campaigns-os tooling setup --target . --platform claude
6
+ npm init -y && npm install --save-exact next-campaign-page-kit@0.2.0 && npm install --save-dev --save-exact @nextcommerce/campaigns-os@1.50.0 && npx --no-install campaigns-os tooling setup --target . --platform claude
7
7
  ```
8
8
 
9
9
  `npm init -y` gives the folder its own `package.json`. Without one, npm
@@ -26,7 +26,7 @@ Ledger schema id: `campaigns-os-release-ledger/v1`
26
26
  Change policy version: `1.0.0`
27
27
  Reason-code vocabulary version: `1.0.0`
28
28
  Limits version: `1.0.0`
29
- Supported surface at generation time: `1.48.0`
29
+ Supported surface at generation time: `1.50.0`
30
30
 
31
31
  ## Forward compatibility
32
32
 
@@ -55,9 +55,10 @@ the toolkit runs that loop in a fixed order:
55
55
  with it locally (the parity step renders it to a temp dir, and the deploy
56
56
  host renders it from the committed source).
57
57
  2. **Serve and prove.** Serve `_site/` on localhost (the `next deploy` handoff
58
- names the directory and any root-route rewrite), record the URL on
59
- `deploy.preview_url`, then run `polish capture`, `qa run --browser`, and the
60
- typed-card order paths against it.
58
+ names the directory and any root-route rewrite) and run `polish capture`
59
+ against it. Once polish is recorded, `record deploy --packet <p> --base-url
60
+ <url>` records the URL on `deploy.preview_url` and the deploy stage; then run
61
+ `qa run --browser` and the typed-card order paths against it.
61
62
  3. **Prove the pin on the production output.** Before committing, run
62
63
 
63
64
  ```bash
@@ -1927,19 +1928,32 @@ expectations, and commerce-ref validation. A doctor-clean non-packet run means
1927
1928
  matches a spec". Treat it as a residue/visual gate, not equivalent to a
1928
1929
  packet-driven QA pass.
1929
1930
 
1930
- ### Per-page credential declarations
1931
+ ### Per-page credential binding
1931
1932
 
1932
1933
  Canonical `qa run` emits `page-binding:<page_id>` in the existing `api-metadata`
1933
1934
  family, with `campaigns-os-page-binding/v0` evidence (typed in the verdict schema).
1934
- `match` means the statically declared credential equals the expected credential;
1935
+ `match` means the page's credential equals the expected credential;
1935
1936
  `mismatch` is a blocker. `unknown` requires manual review. All three carry
1936
1937
  `identity: not_verified`: credential equality never proves a unique Campaign App
1937
1938
  ID, and no App ID is inferred from `campaignId` or `next-campaign-id`.
1938
1939
 
1939
1940
  Expected data reuses the commercial QA resolver (packet, then spec, then an
1940
1941
  explicit supported environment source). Conflicting authored values are unknown.
1941
- The SDK loads `window.nextConfig.apiKey` before `next-api-key` at boot, so meta
1942
- wins at runtime; this check deliberately reports differing declarations as a
1942
+
1943
+ With `--browser`, the row comes from the key the Campaign Cart SDK actually
1944
+ sent. The SDK sends it as `Authorization` on every Campaigns API request, and
1945
+ the browser pass reads that header on each page it loads. The values are
1946
+ compared in memory and dropped: the evidence records
1947
+ `observation: sdk_request`, `source_kinds: ["sdk_request"]`, and `match` when
1948
+ every request carried the expected credential or `mismatch` when any carried
1949
+ another. This works whatever the
1950
+ page's scripts look like. A page that sent no Campaigns API request, or a run
1951
+ with no single expected credential, keeps the static row described below.
1952
+
1953
+ Without `--browser`, or for such a page, the row is a static read of what the
1954
+ page declares (`observation: static_declaration`). The SDK loads
1955
+ `window.nextConfig.apiKey` before `next-api-key` at boot, so meta wins at
1956
+ runtime; the static read deliberately reports differing declarations as a
1943
1957
  conflict rather than certifying one. It does not observe SDK execution.
1944
1958
 
1945
1959
  The bounded HTML loader is reused. HTML is parsed without execution; JavaScript
@@ -8,7 +8,7 @@
8
8
 
9
9
  How a checkout of this repository at one commit becomes a usable installed runtime, and how a consumer decides whether a prepared one is still trustworthy. Everything below is generated from `contracts/runtime-recipe.campaigns-os-node-v1.json`, which is the only authority for these values.
10
10
 
11
- Recipe kind `campaigns-os-node-v1`, revision `1.0.2`, validated by `schemas/campaigns-os-runtime-recipe.v1.schema.json` (`Campaigns OS Runtime Recipe v1`). Supported surface at generation time: `1.48.0`.
11
+ Recipe kind `campaigns-os-node-v1`, revision `1.0.2`, validated by `schemas/campaigns-os-runtime-recipe.v1.schema.json` (`Campaigns OS Runtime Recipe v1`). Supported surface at generation time: `1.50.0`.
12
12
 
13
13
  ## What this is
14
14
 
@@ -16,7 +16,7 @@ that the copy on disk moved.
16
16
  `skills.json` carries one top-level field:
17
17
 
18
18
  ```json
19
- "bundle_revision": "1.48.0+skills.1"
19
+ "bundle_revision": "1.50.0+skills.1"
20
20
  ```
21
21
 
22
22
  The spelling is `<package version>+skills.<n>`:
@@ -25,7 +25,7 @@ The spelling is `<package version>+skills.<n>`:
25
25
  skills ship with (`check-skill-versions.mjs` fails if the two disagree);
26
26
  - `<n>` is a plain counter, not a semver component. It says "this is the *n*th
27
27
  skill-text revision published against that package version" and it **resets
28
- with the prefix**. `1.48.0+skills.1` is therefore ahead of `1.40.0+skills.7`.
28
+ with the prefix**. `1.50.0+skills.1` is therefore ahead of `1.40.0+skills.7`.
29
29
 
30
30
  It is one identity for the bundle as a whole, on purpose. Per-skill versions
31
31
  still exist and still gate per-skill changes, but an agent that loaded one skill
@@ -37,7 +37,7 @@ The first body line of every bundled `SKILL.md`, immediately after the
37
37
  frontmatter, is exactly:
38
38
 
39
39
  ```
40
- Bundle revision: 1.48.0+skills.1
40
+ Bundle revision: 1.50.0+skills.1
41
41
  ```
42
42
 
43
43
  followed by a short paragraph telling the agent to run the check below at the
@@ -48,7 +48,7 @@ text the agent is actually reading, not from a file it would have to go and open
48
48
  ## The check
49
49
 
50
50
  ```bash
51
- npx --no-install campaigns-os tooling status --skills-revision 1.48.0+skills.1
51
+ npx --no-install campaigns-os tooling status --skills-revision 1.50.0+skills.1
52
52
  ```
53
53
 
54
54
  The value is compared against the bundle revision of the **CLI the command runs
@@ -90,20 +90,20 @@ reports the choice as `skills.scope` (`requested`, `installed_platforms`, or
90
90
  "revision_check": "match",
91
91
  "skills_revision": {
92
92
  "status": "match",
93
- "requested": "1.48.0+skills.1",
93
+ "requested": "1.50.0+skills.1",
94
94
  "spelling": "bundle",
95
- "on_disk": "1.48.0+skills.1",
95
+ "on_disk": "1.50.0+skills.1",
96
96
  "on_disk_skill": null,
97
- "message": "match (1.48.0+skills.1)"
97
+ "message": "match (1.50.0+skills.1)"
98
98
  }
99
99
  ```
100
100
 
101
101
  The text view prints one named line, as a header above the rest of the status:
102
102
 
103
103
  ```
104
- Skills revision: match (1.48.0+skills.1)
105
- Skills revision: mismatch: loaded 1.39.0+skills.1, on disk 1.48.0+skills.1 — start a fresh session
106
- Skills revision: unchecked (on disk 1.48.0+skills.1)
104
+ Skills revision: match (1.50.0+skills.1)
105
+ Skills revision: mismatch: loaded 1.39.0+skills.1, on disk 1.50.0+skills.1 — start a fresh session
106
+ Skills revision: unchecked (on disk 1.50.0+skills.1)
107
107
  ```
108
108
 
109
109
  `unchecked` is the state when the flag is absent. It is not an error — an
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextcommerce/campaigns-os",
3
- "version": "1.48.0",
3
+ "version": "1.50.0",
4
4
  "description": "Toolkit for agent-assisted NEXT campaign builds.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -336,6 +336,8 @@
336
336
  "template_family": {
337
337
  "enum": [
338
338
  "undecided",
339
+ "apollo",
340
+ "apollo-mv-single-step",
339
341
  "olympus",
340
342
  "limos",
341
343
  "demeter",
@@ -343,6 +345,8 @@
343
345
  "olympus-mv-two-step",
344
346
  "shop-single-step",
345
347
  "shop-three-step",
348
+ "arjuna",
349
+ "karna",
346
350
  "custom"
347
351
  ]
348
352
  },