@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
@@ -0,0 +1,91 @@
1
+ // The one map from a QC check id to the check module that re-derives its
2
+ // results. The QC readers (src/qc-results.mjs) reach every check's rules
3
+ // through here. A QA check lands by adding its module; a Polish check reads
4
+ // one package-owned visual_review record, named by `record`, and each record
5
+ // has its own reader in the Polish dispatch (POLISH_RECORDS), so a check for
6
+ // a new record also adds that reader.
7
+ //
8
+ // Each entry names a module specifier, imported lazily and relative to this
9
+ // file, and the export the reader calls:
10
+ // - QA checks: `rederiveQcResult(observation)` returns a Derived result
11
+ // ({check, subject, result, reason_code, members, accept_eligible, coverage,
12
+ // state}) or null when the observation cannot be re-derived.
13
+ // - Polish checks: the record's rules, {thresholds, vocabulary,
14
+ // evaluate(cell, thresholds) => Derived[]} (MEDIA_WEIGHT_QC_RULES for
15
+ // media_weight; READABILITY_QC_RULES for readability, which also carries
16
+ // the record's fixed producer values, limits and route set).
17
+ // - Doctor checks recompute from the built HTML on every read and are called
18
+ // by doctor itself; their entries name the evaluator for completeness.
19
+ //
20
+ // The table holds only the shipped QC checks. A stand-in check never comes
21
+ // from here: tests pass theirs in-process (qcStandIns).
22
+ export const QC_CHECK_REGISTRY = Object.freeze({
23
+ // Tracking params reach the order
24
+ "tracking.url": Object.freeze({ leg: "qa", unit: "1.1", module: "./qa-tracking-params.mjs", rederive: "rederiveQcResult" }),
25
+ "tracking.order": Object.freeze({ leg: "qa", unit: "1.1", module: "./qa-tracking-params.mjs", rederive: "rederiveQcResult" }),
26
+ "tracking.tag": Object.freeze({ leg: "qa", unit: "1.1", module: "./qa-tracking-params.mjs", rederive: "rederiveQcResult" }),
27
+ // Content params
28
+ content_param: Object.freeze({ leg: "qa", unit: "1.2", module: "./qa-content-params.mjs", rederive: "rederiveQcResult" }),
29
+ // Media weight and oversizing
30
+ "media.weight": Object.freeze({ leg: "polish", unit: "1.3", module: "./polish-media-weight.mjs", rederive: "MEDIA_WEIGHT_QC_RULES", record: "media_weight" }),
31
+ "media.oversize": Object.freeze({ leg: "polish", unit: "1.3", module: "./polish-media-weight.mjs", rederive: "MEDIA_WEIGHT_QC_RULES", record: "media_weight" }),
32
+ // Policy links
33
+ "policy.presence": Object.freeze({ leg: "qa", unit: "1.4", module: "./qa-policy-links.mjs", rederive: "rederiveQcResult" }),
34
+ "policy.availability": Object.freeze({ leg: "qa", unit: "1.4", module: "./qa-policy-links.mjs", rederive: "rederiveQcResult" }),
35
+ // Text readability
36
+ "readability.contrast": Object.freeze({ leg: "polish", unit: "2.4", module: "./polish-readability.mjs", rederive: "READABILITY_QC_RULES", record: "readability" }),
37
+ // Cart placeholders
38
+ cart_placeholders: Object.freeze({ leg: "doctor", unit: "1.5", module: "./cart-placeholders.mjs", rederive: "evaluateCartPlaceholders" }),
39
+ // Built-output smoke checks
40
+ smoke_qc: Object.freeze({ leg: "doctor", unit: "1.6", module: "./built-smoke-qc.mjs", rederive: "evaluateSmokeQc" }),
41
+ });
42
+
43
+ export const qcChecksForLeg = (leg) => Object.keys(QC_CHECK_REGISTRY).filter((check) => QC_CHECK_REGISTRY[check].leg === leg);
44
+ // The Polish checks read from one visual_review record.
45
+ export const qcChecksForRecord = (record) => Object.keys(QC_CHECK_REGISTRY).filter((check) => QC_CHECK_REGISTRY[check].leg === "polish" && QC_CHECK_REGISTRY[check].record === record);
46
+
47
+ // A module "does not exist yet" only when Node reports ERR_MODULE_NOT_FOUND
48
+ // for that exact specifier. A missing transitive import names another URL,
49
+ // and a syntax error or a throw at load has no such code: those read as a
50
+ // failed load, which the readers turn into evidence_not_reproducible, never
51
+ // not_captured_by_this_version and never pass.
52
+ function isMissingModule(error, href) {
53
+ if (error?.code !== "ERR_MODULE_NOT_FOUND") return false;
54
+ if (typeof error.url === "string") return error.url === href;
55
+ const message = String(error.message || "");
56
+ return message.startsWith(`Cannot find module '${new URL(href).pathname}' imported from `);
57
+ }
58
+
59
+ let loaded = null;
60
+
61
+ // Loads every registered module of the given legs once per process and
62
+ // returns { "<check>": {status: "loaded", rederive} | {status: "missing"} |
63
+ // {status: "failed", error} }. `importer` resolves a specifier relative to
64
+ // this file; it exists so the missing/failed distinction can be exercised.
65
+ export async function loadQcRederivers({ legs = ["qa", "polish"], registry = QC_CHECK_REGISTRY, importer = (specifier) => import(specifier) } = {}) {
66
+ const useCache = registry === QC_CHECK_REGISTRY;
67
+ const cache = useCache && loaded ? loaded : {};
68
+ const modules = new Map();
69
+ for (const [check, entry] of Object.entries(registry)) {
70
+ if (!legs.includes(entry.leg) || Object.hasOwn(cache, check)) continue;
71
+ const href = new URL(entry.module, import.meta.url).href;
72
+ if (!modules.has(href)) {
73
+ modules.set(href, importer(entry.module).then(
74
+ (module) => ({ module }),
75
+ (error) => ({ error, missing: isMissingModule(error, href) }),
76
+ ));
77
+ }
78
+ const outcome = await modules.get(href);
79
+ if (outcome.missing) cache[check] = Object.freeze({ status: "missing" });
80
+ else if (outcome.error) cache[check] = Object.freeze({ status: "failed", error: String(outcome.error?.message || outcome.error) });
81
+ else if (outcome.module?.[entry.rederive] == null) cache[check] = Object.freeze({ status: "failed", error: `${entry.module} has no export ${entry.rederive}` });
82
+ else cache[check] = Object.freeze({ status: "loaded", rederive: outcome.module[entry.rederive] });
83
+ }
84
+ if (useCache) loaded = cache;
85
+ return Object.freeze({ ...cache });
86
+ }
87
+
88
+ // The rederivers loaded so far in this process, or null before the first
89
+ // load. Synchronous readers fall back to this; a check not loaded yet reads
90
+ // as not re-derivable (evidence_not_reproducible), never as pass.
91
+ export const loadedQcRederivers = () => (loaded ? Object.freeze({ ...loaded }) : null);