@nextcommerce/campaigns-os 1.48.0 → 1.52.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 (80) hide show
  1. package/CHANGELOG.md +539 -0
  2. package/agents/claude/CLAUDE.md +6 -5
  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/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 +254 -2
  13. package/contracts/release-ledger.json +1239 -0
  14. package/contracts/supported-surface.json +4 -4
  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 +12 -6
  18. package/docs/build-packet.md +101 -12
  19. package/docs/campaign-build-brief.md +25 -1
  20. package/docs/effects.md +6 -0
  21. package/docs/local-setup.md +1 -1
  22. package/docs/orientation-contract-reference.md +1 -1
  23. package/docs/polish-evidence.md +10 -0
  24. package/docs/qa-and-test-orders.md +66 -11
  25. package/docs/runtime-readiness.md +1 -1
  26. package/docs/sdk-storage-compatibility.md +1 -1
  27. package/docs/skills-revision.md +10 -10
  28. package/package.json +1 -1
  29. package/schemas/campaign-runtime-build-packet.v0.schema.json +4 -0
  30. package/schemas/campaigns-os-qa-verdict.v0.schema.json +8 -3
  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 +4 -4
  36. package/skills/next-campaigns-os/SKILL.md +4 -4
  37. package/skills/next-campaigns-os/references/session-intake.md +7 -3
  38. package/skills/next-campaigns-os-setup/SKILL.md +3 -3
  39. package/skills/next-campaigns-polish/SKILL.md +5 -4
  40. package/skills/next-campaigns-qa/SKILL.md +6 -5
  41. package/skills.json +10 -10
  42. package/src/adapter-decision-contract.mjs +1 -1
  43. package/src/brand-theme.mjs +25 -2
  44. package/src/build-brief.mjs +68 -21
  45. package/src/built-site-scope.mjs +39 -6
  46. package/src/built-smoke-qc.mjs +1117 -0
  47. package/src/campaign-identity.mjs +36 -2
  48. package/src/cart-placeholders.mjs +730 -0
  49. package/src/cli.mjs +320 -42
  50. package/src/commercial-journey.mjs +65 -4
  51. package/src/commercial-parity.mjs +6 -1
  52. package/src/doctor/checks.mjs +291 -24
  53. package/src/doctor/inspect.mjs +53 -2
  54. package/src/doctor/next-step.mjs +1 -1
  55. package/src/install-mode.mjs +0 -8
  56. package/src/invocation.mjs +5 -2
  57. package/src/local-preview-policy.mjs +1 -1
  58. package/src/local-proof.mjs +4 -1
  59. package/src/polish-browser.mjs +218 -1
  60. package/src/polish-capture.mjs +1 -1
  61. package/src/polish-media-weight.mjs +492 -0
  62. package/src/polish-node.mjs +96 -4
  63. package/src/progress-node.mjs +5 -1
  64. package/src/qa-binding-evidence.mjs +21 -0
  65. package/src/qa-browser.mjs +338 -97
  66. package/src/qa-content-params.mjs +889 -0
  67. package/src/qa-node.mjs +114 -14
  68. package/src/qa-order-bump.mjs +22 -1
  69. package/src/qa-policy-links.mjs +1019 -0
  70. package/src/qa-tracking-params.mjs +1389 -0
  71. package/src/qa-url-privacy.mjs +168 -0
  72. package/src/qc-accept.mjs +446 -0
  73. package/src/qc-check-registry.mjs +83 -0
  74. package/src/qc-results.mjs +1049 -0
  75. package/src/sdk-attribute-index.mjs +71 -0
  76. package/src/sdk-markup.mjs +2 -2
  77. package/src/sdk-storage-compatibility.mjs +63 -3
  78. package/src/source-prep.mjs +37 -7
  79. package/src/stage-record.mjs +356 -36
  80. package/src/theme-gate.mjs +3 -3
@@ -14,6 +14,10 @@ export const PROGRESS_ENDPOINT = '/api/progress';
14
14
  // A projection keeps the first PROGRESS_GATE_LIMIT continuation gates (the snapshot schema's gates maxItems).
15
15
  export const PROGRESS_GATE_LIMIT = 16;
16
16
  const accepted = (value,values,fallback='unknown')=>values.includes(value)?value:fallback;
17
+ // A next action that is a variant of a fixed action projects onto it: the
18
+ // order-bump QA run is a QA run. Without this, the closed v0 vocabulary would
19
+ // read it as unknown and mark a ready QA continuation blocked.
20
+ const PROGRESS_ACTION_ALIASES = new Map([['qa_run_bump','qa_run']]);
17
21
  const hash = value=>typeof value==='string'&&/^(?:sha256:)?[0-9a-f]{64}$/i.test(value)?`sha256:${value.replace(/^sha256:/i,'').toLowerCase()}`:null;
18
22
  const id = value=>typeof value==='string'&&/^[A-Za-z0-9_-]{1,64}$/.test(value)?value:null;
19
23
  const digest = value=>`sha256:${createHash('sha256').update(value).digest('hex')}`;
@@ -63,7 +67,7 @@ export function projectProgressObservation({workspace,context,report,doctor,cont
63
67
  // here (never pass).
64
68
  const gateState=status=>accepted(status==='carried_forward'?'not_applicable':status,['pass','blocked','waived','not_applicable']);
65
69
  const gates=(Array.isArray(continuation?.gates)?continuation.gates:[]).slice(0,PROGRESS_GATE_LIMIT).map(gate=>({id:accepted(gate?.id,PROGRESS_GATE_IDS),state:PROGRESS_GATE_IDS.includes(gate?.id)?gateState(gate?.status):'unknown'}));
66
- const actions=[...new Set((Array.isArray(continuation?.next_actions)?continuation.next_actions:[]).slice(0,64).map(action=>accepted(action?.id,PROGRESS_ACTION_IDS)))];
70
+ const actions=[...new Set((Array.isArray(continuation?.next_actions)?continuation.next_actions:[]).slice(0,64).map(action=>accepted(PROGRESS_ACTION_ALIASES.get(action?.id)||action?.id,PROGRESS_ACTION_IDS)))];
67
71
  const stage=accepted(continuation?.stage,PROGRESS_CONTINUATIONS);
68
72
  const preview=typeof packet?.deploy?.preview_url==='string'&&packet.deploy.preview_url?packet.deploy.preview_url:null;
69
73
  return {
@@ -188,6 +188,27 @@ export async function observeBinding({ source, page, expected, scriptLoader, par
188
188
  return result(values[0] === expected.value ? 'match' : 'mismatch', 'credential_comparison');
189
189
  }
190
190
 
191
+ // The Campaign Cart SDK sends the page's raw key, with no scheme, as
192
+ // `Authorization` on every Campaigns API call, so the browser pass can see the
193
+ // key a page actually uses, whatever its scripts look like. The value is
194
+ // compared as sent: if the SDK ever adds a scheme, every page reads mismatch
195
+ // rather than passing. The API host is matched by its shape,
196
+ // campaigns.apps.<name>.com, without naming it.
197
+ const SDK_API_HOST = /^campaigns\.apps\.[a-z0-9-]+\.com$/;
198
+ export function isSdkApiRequest(url) {
199
+ try { const parsed = new URL(url); return parsed.protocol === 'https:' && SDK_API_HOST.test(parsed.hostname) && parsed.pathname.startsWith('/api/'); } catch { return false; }
200
+ }
201
+
202
+ // The keys a page sent are compared here and dropped; only the outcome is
203
+ // kept. With nothing sent, or no single expected key, the static read stands
204
+ // (null).
205
+ export function sentBinding(sentKeys, expected) {
206
+ if (!sentKeys.length || expected?.conflict || !expected?.value) return null;
207
+ return { schema_version: BINDING_SCHEMA, observation: 'sdk_request',
208
+ outcome: sentKeys.every(value => value === expected.value) ? 'match' : 'mismatch',
209
+ reason: 'credential_comparison', source_kinds: ['sdk_request'], identity: 'not_verified' };
210
+ }
211
+
191
212
  export function bindingAssertion(page, evidence) {
192
213
  // No source URLs, values, hashes, masked fragments, or inferred App IDs.
193
214
  return { id: `page-binding:${page.page_id}`, family: 'api-metadata', page: page.page_id,