@nextcommerce/campaigns-os 1.43.1 → 1.46.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 (92) hide show
  1. package/AGENTS.md +9 -2
  2. package/CHANGELOG.md +1099 -5103
  3. package/README.md +34 -13
  4. package/agents/claude/CLAUDE.md +1 -1
  5. package/agents/codex/AGENTS.md +1 -1
  6. package/agents/copilot/copilot-instructions.md +1 -1
  7. package/agents/cursor/campaigns-os.mdc +1 -1
  8. package/campaign-spec/dist/rules/analytics-contract-shape.d.ts +2 -2
  9. package/campaign-spec/dist/rules/analytics-contract-shape.js +2 -2
  10. package/campaign-spec/dist/rules/store-profile-shape.d.ts +5 -1
  11. package/campaign-spec/dist/rules/store-profile-shape.js +8 -10
  12. package/campaign-spec/dist/types.d.ts +2 -2
  13. package/contracts/agent-relevant-change-policy.v1.json +5 -0
  14. package/contracts/archive/CHANGELOG.2026-09-30.md +5111 -0
  15. package/contracts/archive/release-ledger.2026-09-30.json +5068 -0
  16. package/contracts/effects.v1.json +1184 -121
  17. package/contracts/orientation-reason-codes.v1.json +7 -0
  18. package/contracts/release-ledger.json +2190 -5260
  19. package/contracts/supported-surface.json +7 -4
  20. package/contracts/template-slot-manifest.shared-content-core.v0.json +403 -0
  21. package/docs/brand-theme-bridge.md +81 -0
  22. package/docs/build-packet.md +222 -23
  23. package/docs/campaigns-os-build-flow.md +4 -3
  24. package/docs/design-source-package.md +162 -15
  25. package/docs/effects.md +66 -12
  26. package/docs/gateway-login.md +3 -0
  27. package/docs/local-setup.md +1 -1
  28. package/docs/orientation-contract-reference.md +42 -2
  29. package/docs/polish-evidence.md +74 -0
  30. package/docs/progress-snapshots.md +10 -6
  31. package/docs/qa-and-test-orders.md +230 -20
  32. package/docs/release-ledger-authoring-guide.md +70 -8
  33. package/docs/runtime-readiness.md +1 -1
  34. package/docs/sdk-storage-compatibility.md +1 -1
  35. package/docs/skills-revision.md +10 -10
  36. package/docs/supported-surface.md +2 -2
  37. package/docs/versioning.md +4 -1
  38. package/package.json +1 -1
  39. package/schemas/campaigns-os-release-ledger.v1.schema.json +32 -2
  40. package/schemas/campaigns-os-tooling-orientation.v1.schema.json +1 -0
  41. package/skills/campaign-lifecycle-orientation/SKILL.md +16 -5
  42. package/skills/campaign-readback-classification/SKILL.md +3 -3
  43. package/skills/campaign-run-evidence/SKILL.md +7 -6
  44. package/skills/contribution-intake/SKILL.md +3 -3
  45. package/skills/next-campaigns-build/SKILL.md +7 -6
  46. package/skills/next-campaigns-os/SKILL.md +7 -7
  47. package/skills/next-campaigns-os/references/session-intake.md +9 -3
  48. package/skills/next-campaigns-os-setup/SKILL.md +5 -5
  49. package/skills/next-campaigns-polish/SKILL.md +28 -9
  50. package/skills/next-campaigns-qa/SKILL.md +7 -4
  51. package/skills.json +10 -10
  52. package/src/brand-theme.mjs +320 -20
  53. package/src/build-brief.mjs +6 -4
  54. package/src/built-script-syntax.mjs +480 -0
  55. package/src/built-site-scope.mjs +16 -4
  56. package/src/campaigns-api-key.mjs +99 -0
  57. package/src/cli-helpers.mjs +118 -0
  58. package/src/cli.mjs +1530 -7580
  59. package/src/commercial-parity.mjs +48 -2
  60. package/src/design-source-package.mjs +1 -1
  61. package/src/design-source-publication.mjs +898 -0
  62. package/src/deviation.mjs +13 -1
  63. package/src/diagnostic.mjs +6 -2
  64. package/src/directory-lock.mjs +270 -0
  65. package/src/doctor/checks.mjs +4654 -0
  66. package/src/doctor/inspect.mjs +678 -0
  67. package/src/doctor/next-step.mjs +731 -0
  68. package/src/doctor/source-provenance.mjs +184 -0
  69. package/src/install-invocation.mjs +29 -0
  70. package/src/invocation.mjs +183 -0
  71. package/src/live-campaign-refs.mjs +466 -0
  72. package/src/login.mjs +2 -2
  73. package/src/page-kit-store-profile.mjs +69 -12
  74. package/src/page-kit-sync.mjs +31 -12
  75. package/src/private-template-source.mjs +1 -1
  76. package/src/progress-node.mjs +9 -36
  77. package/src/proof-policy.mjs +1 -1
  78. package/src/qa-analytics-correctness.mjs +3 -0
  79. package/src/qa-binding-evidence.mjs +76 -11
  80. package/src/qa-browser.mjs +1316 -105
  81. package/src/qa-build-scope.mjs +47 -0
  82. package/src/qa-commercial-parity.mjs +48 -5
  83. package/src/qa-node.mjs +339 -19
  84. package/src/qa-test-order-topology.mjs +148 -0
  85. package/src/sdk-markup.mjs +72 -8
  86. package/src/source-html-intake.mjs +117 -1
  87. package/src/source-html-manifest.mjs +9 -2
  88. package/src/stage-ledger.mjs +28 -0
  89. package/src/stage-record.mjs +551 -0
  90. package/src/target-lock.mjs +54 -0
  91. package/src/template-brand-contract.mjs +17 -1
  92. package/src/upsell-selector-scope.mjs +112 -2
@@ -652,8 +652,8 @@ function recurrenceEquivalent(claim, truth) {
652
652
  if (moneyToCents(claim.amount) !== moneyToCents(truth.amount)) return false;
653
653
  if (claim.interval_count === truth.interval_count && claim.interval === truth.interval) return true;
654
654
 
655
- // Uzzle's accepted monthly disclosure is the one narrow proven equivalence:
656
- // its API cadence is expressed as 30 days. Do not generalize rate math.
655
+ // One merchant's accepted monthly disclosure is the one narrow proven
656
+ // equivalence: its API cadence is expressed as 30 days. Do not generalize rate math.
657
657
  return (claim.interval_count === 1 && claim.interval === "month"
658
658
  && truth.interval_count === 30 && truth.interval === "day")
659
659
  || (truth.interval_count === 1 && truth.interval === "month"
@@ -872,6 +872,52 @@ export function serializeCommercialFindings(findingsValue, options = {}) {
872
872
  return { assertions, omittedFindingCount };
873
873
  }
874
874
 
875
+ export const RECURRING_CLAIM_ABSENT_CODE = "commercial_parity.recurring_claim_absent";
876
+
877
+ /**
878
+ * A subscription package a page renders with no recurring claim extracted for
879
+ * it (#533). The diff above is silent when there is nothing to compare, which
880
+ * is exactly how a page whose rebill copy could not be read used to pass. This
881
+ * is incomplete evidence, not a mismatch: it names the package, never a price.
882
+ * `subscriptionRefsByPage` maps a page id to the refs the spec says recur;
883
+ * a capture carries the package refs its page renders as
884
+ * `rendered_package_refs`. A capture that failed extraction is skipped — that
885
+ * failure is already its own issue — and so is one with no page id: there is
886
+ * no page to hold its subscriptions against, and it is never keyed as "null".
887
+ */
888
+ export function recurringClaimAbsences(capturesValue, subscriptionRefsByPage) {
889
+ const absences = [];
890
+ const seen = new Set();
891
+ partitionCaptures(capturesValue).valid.forEach((capture) => {
892
+ if (!present(capture.page_id)) return;
893
+ const pageId = String(capture.page_id);
894
+ const subscriptions = subscriptionRefsByPage?.get?.(pageId);
895
+ if (!subscriptions?.size) return;
896
+ const claimed = new Set(capture.recurrence_claims.map((claim) => String(claim.package_id)));
897
+ array(capture.rendered_package_refs).map(String).forEach((ref) => {
898
+ const key = `${pageId}\u0000${ref}`;
899
+ if (!subscriptions.has(ref) || claimed.has(ref) || seen.has(key)) return;
900
+ seen.add(key);
901
+ absences.push({ type: "recurring-claim-absent", page_id: pageId, ...(present(capture.url) ? { url: String(capture.url) } : {}), package_id: ref });
902
+ });
903
+ });
904
+ return absences;
905
+ }
906
+
907
+ export function recurringClaimAbsentAssertions(absences, { maxAssertions = Number.POSITIVE_INFINITY } = {}) {
908
+ return array(absences).slice(0, Math.max(0, maxAssertions)).map((absence) => ({
909
+ id: `${RECURRING_CLAIM_ABSENT_CODE}:${component(absence.page_id)}:package-${component(absence.package_id)}`,
910
+ family: "pricing",
911
+ page: absence.page_id || "campaign",
912
+ ...(absence.url ? { url: absence.url } : {}),
913
+ status: "warn",
914
+ severity: "warn",
915
+ expected: `a readable recurring (rebill) claim for subscription package ${absence.package_id}`,
916
+ actual: `no recurring claim extracted for package ${absence.package_id}`,
917
+ evidence: { type: absence.type, code: RECURRING_CLAIM_ABSENT_CODE, package_id: absence.package_id },
918
+ }));
919
+ }
920
+
875
921
  /**
876
922
  * Build the runner-facing commercial evidence section and flat QA assertions.
877
923
  * `countsOnly` retains matched array lengths without walking claim elements.
@@ -612,7 +612,7 @@ export function synthesizeHtmlFunnelDesignSourcePackage({
612
612
  kind: "coverage_absence",
613
613
  scope: "primary_design_coverage",
614
614
  applies_to: [surface.id],
615
- reason: `Page surface "${surface.id}" is template stock from the ${template.family} family: it has no design source of its own, and the build stage materialises it from that family's stock page.`,
615
+ reason: `Page surface "${surface.id}" is template stock from the ${template.family} family: it has no design source of its own; keep it unbuilt unless the operator opts in to materialising that family's stock page.`,
616
616
  status: "accepted",
617
617
  attributed_by: "prepare-build",
618
618
  attributed_at: null,