@nextcommerce/campaigns-os 1.43.2 → 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.
- package/AGENTS.md +5 -0
- package/CHANGELOG.md +648 -5103
- package/README.md +32 -11
- package/agents/claude/CLAUDE.md +1 -1
- package/agents/codex/AGENTS.md +1 -1
- package/agents/copilot/copilot-instructions.md +1 -1
- package/agents/cursor/campaigns-os.mdc +1 -1
- package/campaign-spec/dist/rules/analytics-contract-shape.d.ts +2 -2
- package/campaign-spec/dist/rules/analytics-contract-shape.js +2 -2
- package/campaign-spec/dist/rules/store-profile-shape.d.ts +5 -1
- package/campaign-spec/dist/rules/store-profile-shape.js +8 -10
- package/campaign-spec/dist/types.d.ts +2 -2
- package/contracts/archive/CHANGELOG.2026-09-30.md +5111 -0
- package/contracts/archive/release-ledger.2026-09-30.json +5068 -0
- package/contracts/effects.v1.json +1176 -113
- package/contracts/orientation-reason-codes.v1.json +7 -0
- package/contracts/release-ledger.json +2345 -6087
- package/contracts/supported-surface.json +7 -4
- package/contracts/template-slot-manifest.shared-content-core.v0.json +403 -0
- package/docs/brand-theme-bridge.md +81 -0
- package/docs/build-packet.md +158 -21
- package/docs/campaigns-os-build-flow.md +3 -3
- package/docs/design-source-package.md +73 -0
- package/docs/effects.md +50 -8
- package/docs/gateway-login.md +3 -0
- package/docs/local-setup.md +1 -1
- package/docs/orientation-contract-reference.md +42 -2
- package/docs/polish-evidence.md +74 -0
- package/docs/qa-and-test-orders.md +99 -13
- package/docs/release-ledger-authoring-guide.md +64 -4
- package/docs/runtime-readiness.md +1 -1
- package/docs/sdk-storage-compatibility.md +1 -1
- package/docs/skills-revision.md +10 -10
- package/docs/supported-surface.md +2 -2
- package/docs/versioning.md +4 -1
- package/package.json +1 -1
- package/schemas/campaigns-os-release-ledger.v1.schema.json +32 -2
- package/schemas/campaigns-os-tooling-orientation.v1.schema.json +1 -0
- package/skills/campaign-lifecycle-orientation/SKILL.md +16 -5
- package/skills/campaign-readback-classification/SKILL.md +3 -3
- package/skills/campaign-run-evidence/SKILL.md +7 -6
- package/skills/contribution-intake/SKILL.md +3 -3
- package/skills/next-campaigns-build/SKILL.md +7 -6
- package/skills/next-campaigns-os/SKILL.md +7 -7
- package/skills/next-campaigns-os/references/session-intake.md +9 -3
- package/skills/next-campaigns-os-setup/SKILL.md +5 -5
- package/skills/next-campaigns-polish/SKILL.md +28 -9
- package/skills/next-campaigns-qa/SKILL.md +7 -4
- package/skills.json +10 -10
- package/src/brand-theme.mjs +320 -20
- package/src/built-site-scope.mjs +16 -4
- package/src/cli.mjs +280 -46
- package/src/commercial-parity.mjs +48 -2
- package/src/deviation.mjs +13 -1
- package/src/diagnostic.mjs +5 -2
- package/src/doctor/checks.mjs +320 -81
- package/src/doctor/inspect.mjs +55 -13
- package/src/doctor/source-provenance.mjs +184 -0
- package/src/invocation.mjs +4 -0
- package/src/live-campaign-refs.mjs +466 -0
- package/src/login.mjs +2 -2
- package/src/page-kit-store-profile.mjs +69 -12
- package/src/page-kit-sync.mjs +31 -12
- package/src/progress-node.mjs +3 -1
- package/src/qa-browser.mjs +538 -28
- package/src/qa-commercial-parity.mjs +48 -5
- package/src/qa-node.mjs +122 -7
- package/src/qa-test-order-topology.mjs +148 -0
- package/src/sdk-markup.mjs +72 -8
- package/src/source-html-intake.mjs +116 -0
- package/src/stage-record.mjs +551 -0
- 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
|
-
//
|
|
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.
|
package/src/deviation.mjs
CHANGED
|
@@ -17,6 +17,17 @@ export const DEVIATION_JOURNAL_REL_PATH = ".campaign-runtime/agent-deviations.js
|
|
|
17
17
|
// (doctor, next, findings, telemetry, run, validate-*) never deviate.
|
|
18
18
|
export const TRACKED_STAGE_COMMANDS = Object.freeze(new Set(["start", "prepare-build", "theme", "polish", "qa", "run-record"]));
|
|
19
19
|
|
|
20
|
+
// Setup and metadata subcommands of a tracked command. They produce no stage
|
|
21
|
+
// output, so running one outside the recommendation is not a detour:
|
|
22
|
+
// qa install-browser — installs the package-owned QA browser binary;
|
|
23
|
+
// qa policy set — edits the packet's QA policy fields (and the
|
|
24
|
+
// assembly report's mirror of them);
|
|
25
|
+
// qa resolve — read-only diagnostic: prints the derived QA targets
|
|
26
|
+
// and, unless --no-probe, probes their reachability.
|
|
27
|
+
// Every other subcommand of a tracked command (`qa run`, `polish capture`,
|
|
28
|
+
// `theme generate`, ...) is still compared against the recommendation.
|
|
29
|
+
export const UNTRACKED_SUBCOMMANDS = Object.freeze(new Set(["qa install-browser", "qa policy", "qa resolve"]));
|
|
30
|
+
|
|
20
31
|
// Commands every recommendation implicitly allows for its stage. Stage work is
|
|
21
32
|
// agent/skill work, so the expected command set is small and explicit.
|
|
22
33
|
const EXPECTED_COMMANDS_BY_STAGE = Object.freeze({
|
|
@@ -65,8 +76,9 @@ export function buildRecommendation({ stage, status, expectedCommands, now = new
|
|
|
65
76
|
* Compare a pipeline-advancing command against the session's last
|
|
66
77
|
* recommendation. Returns a deviation entry or null.
|
|
67
78
|
*/
|
|
68
|
-
export function detectDeviation({ lastRecommendation, command, argvShape = [], runId = null, deviationReason = null, now = new Date() }) {
|
|
79
|
+
export function detectDeviation({ lastRecommendation, command, subcommand = null, argvShape = [], runId = null, deviationReason = null, now = new Date() }) {
|
|
69
80
|
if (!TRACKED_STAGE_COMMANDS.has(command)) return null;
|
|
81
|
+
if (subcommand && UNTRACKED_SUBCOMMANDS.has(`${command} ${subcommand}`)) return null;
|
|
70
82
|
if (!lastRecommendation || !Array.isArray(lastRecommendation.expected_commands)) return null;
|
|
71
83
|
if (lastRecommendation.expected_commands.includes(command)) return null;
|
|
72
84
|
return {
|
package/src/diagnostic.mjs
CHANGED
|
@@ -20,7 +20,10 @@ const REASONS = new Set([
|
|
|
20
20
|
"built_output.upsell_selector_scope", "built_output.sdk_markup.swap_with_add_to_cart",
|
|
21
21
|
"built_output.sdk_markup.checkout_not_form", "built_output.sdk_markup.wrong_field_name",
|
|
22
22
|
"built_output.sdk_markup.missing_selector_id_match", "built_output.script_syntax.parse_failure",
|
|
23
|
-
"built_output.script_syntax.missing_script",
|
|
23
|
+
"built_output.script_syntax.missing_script", "source_html.producer_provenance",
|
|
24
|
+
"source_html.producer_provenance.source_type", "source_html.producer_provenance.screenshot_fallback_used",
|
|
25
|
+
"source_html.producer_provenance.semantic_section_count", "source_html.producer_provenance.material_fingerprint",
|
|
26
|
+
"source_html.producer_provenance.section_exports", "source_html.producer_provenance.waiver_inert",
|
|
24
27
|
]);
|
|
25
28
|
const ACTIONS = new Set([
|
|
26
29
|
"repair_target", "align_store_profile", "align_sdk_version", "repair_waiver", "waive_checkpoint",
|
|
@@ -31,7 +34,7 @@ const ACTIONS = new Set([
|
|
|
31
34
|
]);
|
|
32
35
|
|
|
33
36
|
const RECOVERY = Object.freeze({
|
|
34
|
-
skills: { owner: "operator", action_id: "install-skills", input_needed: "Selected agent profile", instruction: "Refresh the bundled skills for that profile
|
|
37
|
+
skills: { owner: "operator", action_id: "install-skills", input_needed: "Selected agent profile", instruction: "Refresh the bundled skills for that profile, then read the SKILL.md files install-skills lists under Read now in the running session; restart the agent only if it cannot read them." },
|
|
35
38
|
pin: { owner: "operator", action_id: "tooling.status", input_needed: "Reviewed toolkit version and lockfile", instruction: "Compare the installed package with the reviewed version; registry currency is not checked." },
|
|
36
39
|
update: { owner: "operator", action_id: "tooling.status", input_needed: "Reviewed upstream revision", instruction: "Review and update the toolkit checkout using the existing worktree workflow." },
|
|
37
40
|
doctor: { owner: "workflow_owner", action_id: "doctor", input_needed: "Local packet inputs and detailed doctor findings", instruction: "Inspect doctor locally and follow its existing owner, required inputs, and recovery actions." },
|