@nextcommerce/campaigns-os 1.62.0 → 1.66.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/CHANGELOG.md +71 -0
- package/campaign-spec/dist/rules/campaign-currencies.d.ts +1 -0
- package/campaign-spec/dist/rules/campaign-currencies.js +1 -1
- package/campaign-spec/dist/rules/index.d.ts +1 -0
- package/campaign-spec/dist/rules/index.js +3 -0
- package/campaign-spec/dist/rules/package-currency-prices.d.ts +29 -0
- package/campaign-spec/dist/rules/package-currency-prices.js +137 -0
- package/campaign-spec/dist/types.d.ts +16 -0
- package/compatibility.json +1 -1
- package/contracts/effects.v1.json +129 -1
- package/contracts/fixtures/runtime-recipe/accept/current.json +1 -0
- package/contracts/fixtures/runtime-recipe/accept/minimal.json +1 -0
- package/contracts/fixtures/runtime-recipe/dist-states.json +1 -1
- package/contracts/fixtures/runtime-recipe/reject/advisory-enforcement.json +1 -0
- package/contracts/fixtures/runtime-recipe/reject/allowlist-without-hosts.json +1 -0
- package/contracts/fixtures/runtime-recipe/reject/committed-output-claim.json +1 -0
- package/contracts/fixtures/runtime-recipe/reject/engines-disagreement-warns.json +1 -0
- package/contracts/fixtures/runtime-recipe/reject/lifecycle-scripts-enabled.json +1 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-kind.json +1 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-network-policy.json +1 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-output-check.json +1 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-revision.json +1 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-step-id.json +1 -0
- package/contracts/fixtures/runtime-recipe/reject/unperformable-check-skipped.json +1 -0
- package/contracts/fixtures/runtime-recipe/reject/unpinned-lockfile.json +1 -0
- package/contracts/release-ledger.json +810 -0
- package/contracts/runtime-recipe.campaigns-os-node-v1.json +1 -0
- package/contracts/supported-surface.json +6 -6
- package/docs/build-packet.md +31 -5
- package/docs/campaigns-os-build-flow.md +38 -0
- package/docs/campaignspec-authoring-examples.md +31 -0
- package/docs/effects.md +8 -0
- package/docs/local-setup.md +1 -1
- package/docs/orientation-contract-reference.md +1 -1
- package/docs/qa-and-test-orders.md +44 -10
- package/docs/readback.md +8 -1
- package/docs/runtime-readiness.md +4 -3
- package/docs/skills-revision.md +10 -10
- package/docs/supported-surface.md +1 -1
- package/package.json +1 -1
- package/schemas/campaign-spec.v4.schema.json +15 -0
- package/schemas/campaign-spec.v5.schema.json +15 -0
- package/schemas/campaigns-os-qa-verdict.v0.schema.json +17 -2
- package/skills/campaign-lifecycle-orientation/SKILL.md +3 -3
- package/skills/campaign-readback-classification/SKILL.md +3 -3
- package/skills/campaign-run-evidence/SKILL.md +3 -3
- package/skills/contribution-intake/SKILL.md +3 -3
- package/skills/next-campaigns-build/SKILL.md +3 -3
- package/skills/next-campaigns-os/SKILL.md +3 -3
- package/skills/next-campaigns-os-setup/SKILL.md +3 -3
- package/skills/next-campaigns-polish/SKILL.md +3 -3
- package/skills/next-campaigns-qa/SKILL.md +4 -4
- package/skills/next-campaigns-sdk-bump/SKILL.md +3 -3
- package/skills.json +11 -11
- package/src/change-scope.mjs +25 -6
- package/src/cli.mjs +120 -7
- package/src/commercial-journey.mjs +3 -0
- package/src/commercial-parity.mjs +157 -7
- package/src/doctor/checks.mjs +28 -3
- package/src/doctor/ci.mjs +36 -1
- package/src/evidence-debt.mjs +9 -1
- package/src/input-currency.mjs +4 -2
- package/src/input-refresh.mjs +1 -1
- package/src/invocation.mjs +3 -2
- package/src/orchestration-stage-contract.mjs +3 -1
- package/src/polish-gate.mjs +10 -0
- package/src/qa-browser.mjs +46 -0
- package/src/qa-commercial-parity.mjs +173 -18
- package/src/qa-node.mjs +10 -5
- package/src/qa-verdict.mjs +3 -1
- package/src/readback.mjs +1 -0
- package/src/runtime-state-ignore.mjs +44 -19
- package/src/stage-ledger.mjs +35 -9
- package/src/stage-record.mjs +93 -14
|
@@ -131,6 +131,7 @@
|
|
|
131
131
|
"campaign-spec/rules/index.ts",
|
|
132
132
|
"campaign-spec/rules/offer-ref-integrity.ts",
|
|
133
133
|
"campaign-spec/rules/one-checkout-per-path.ts",
|
|
134
|
+
"campaign-spec/rules/package-currency-prices.ts",
|
|
134
135
|
"campaign-spec/rules/package-pricing-sanity.ts",
|
|
135
136
|
"campaign-spec/rules/page-count.ts",
|
|
136
137
|
"campaign-spec/rules/page-id-uniqueness.ts",
|
|
@@ -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.
|
|
3
|
+
"surface_version": "1.66.0",
|
|
4
4
|
"package_exports": [
|
|
5
5
|
"./commercial-journey",
|
|
6
6
|
"./commercial-parity",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"sha256": "53269453dfdaac4b286124d445b60b0c46205dfbaf8037ac61b30254a32df53a"
|
|
53
53
|
},
|
|
54
54
|
"contracts/runtime-recipe.campaigns-os-node-v1.json": {
|
|
55
|
-
"sha256": "
|
|
55
|
+
"sha256": "4755be6c700463e99b609ceaebeb92cb1df5c54805aeaea3e6fd68e87ebcd6ef"
|
|
56
56
|
},
|
|
57
57
|
"schemas/campaign-build-brief.v1.schema.json": {
|
|
58
58
|
"sha256": "d524003184131398dc15fdb84385af1be0de0c69d6c5ede7278689a5b6101b81"
|
|
@@ -70,10 +70,10 @@
|
|
|
70
70
|
"sha256": "1e309c5dad8c5eeef26ef6fd3b6b90b72440c5104319a161002b17f1359fbd76"
|
|
71
71
|
},
|
|
72
72
|
"schemas/campaign-spec.v4.schema.json": {
|
|
73
|
-
"sha256": "
|
|
73
|
+
"sha256": "4e9a092c1426429b727aaa6ef5622ae2c6c6dad59270f08636987847b63f5001"
|
|
74
74
|
},
|
|
75
75
|
"schemas/campaign-spec.v5.schema.json": {
|
|
76
|
-
"sha256": "
|
|
76
|
+
"sha256": "24d04c8d3ede920de161fadf914dc497a3f61e126f377cb01dd0aeff12b53c4f"
|
|
77
77
|
},
|
|
78
78
|
"schemas/campaigns-os-legacy-migration-inventory.v0.schema.json": {
|
|
79
79
|
"sha256": "d6edc4572900d15fdaf6b8c59916b0d885303c1d398bdadeb7c58285522c9049"
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"sha256": "3c40fd79f218d1b95f0424f9a2b67db140a306329e2ca02793e2740d3afded59"
|
|
89
89
|
},
|
|
90
90
|
"schemas/campaigns-os-qa-verdict.v0.schema.json": {
|
|
91
|
-
"sha256": "
|
|
91
|
+
"sha256": "237361d4cda53f3cd98d306bc9ab2c2a0ce1cdd4ded0b15001a3c47b5cf05988"
|
|
92
92
|
},
|
|
93
93
|
"schemas/campaigns-os-doctor-output.v0.schema.json": {
|
|
94
94
|
"sha256": "87866e619d2a18f01a208a1682cb14a2079f61df51c642f4e1902b6e6bce0ce5"
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
"sha256": "b397b417b0456cafc03346a2e882a34c19fa0500ea92594afca2f9fa1d7f2c3b"
|
|
125
125
|
},
|
|
126
126
|
"contracts/effects.v1.json": {
|
|
127
|
-
"sha256": "
|
|
127
|
+
"sha256": "4979649217470c1066f7efab722e56d4089f9df374bfbb5af03d2d7e5aba3c14"
|
|
128
128
|
},
|
|
129
129
|
"schemas/campaigns-os-effects.v1.schema.json": {
|
|
130
130
|
"sha256": "3eadd22169ab98bc7c2f682af2751267605170581182158d96be035b0cfe44dc"
|
package/docs/build-packet.md
CHANGED
|
@@ -1339,7 +1339,7 @@ than hand-adding the field. Note that a committed artifact set always reads
|
|
|
1339
1339
|
stale to the readback once HEAD moves past it — freshness proof is a
|
|
1340
1340
|
regeneration at HEAD, not a property a commit can preserve.
|
|
1341
1341
|
|
|
1342
|
-
Commit durable packet/context/report artifacts when they represent a real build handoff. The rest of `.campaign-runtime/` is machine-local and is not for the campaign repository: `run-session.json`, `command-lifecycle.jsonl`, `agent-deviations.jsonl`, `workflow-findings.jsonl`, `run-records/`, `
|
|
1342
|
+
Commit durable packet/context/report artifacts when they represent a real build handoff. The rest of `.campaign-runtime/` is machine-local and is not for the campaign repository: `run-session.json`, `command-lifecycle.jsonl`, `agent-deviations.jsonl`, `workflow-findings.jsonl`, `run-records/`, `progress/`, `polish-evidence/`, `evidence/`, and `*.log`/`*.tmp` are per-machine, append-only, or carry live URLs and absolute paths, and so are the full QA verdicts `qa run` writes under the target's `qa-output/`. `start`, `prepare-build`, `install-agent-context`, and `run start` write a managed ignore block for that set into the target's `.gitignore` (keyed on its marker line; edit the list beneath it freely). The block also lists `.campaign-runtime/funnels/*/run-records/` for the Run Records of a project's second and later funnels. An existing block gains `qa-output/`, `.campaign-runtime/progress/` and `.campaign-runtime/funnels/*/run-records/` when they are missing, since those hold live URLs, order references and remit records. An existing block also loses `.campaign-runtime/fetched-specs/`, which earlier versions listed. The readback bundle (`build-context.json`, `assembly-report.json`, `doctor-output.json`, `qa-verdict.json`), `fetched-specs/`, `input/`, `theme/`, `agent-context/`, and `setup-handoff.json` are deliberately not ignored, at the top level or under `funnels/<route>/`. The Campaigns API key is a public, browser-side, domain-allowlisted key and may already be present in the local CampaignSpec as `campaign.campaigns_api_key`; do not duplicate it into the packet unless the spec is unavailable. Do not commit raw private API responses, backend secrets, or temporary media exports.
|
|
1343
1343
|
|
|
1344
1344
|
Packet-mode `doctor` is inspection-only by default and preserves retained evidence and active run journals, even when lifecycle capture is configured. Use `--write` to intentionally record fresh evidence; `--no-write` takes precedence. Inspection still reports current blockers and keeps the same exit status.
|
|
1345
1345
|
|
|
@@ -1527,7 +1527,9 @@ root and is not hashed. Build does not type the value: after page-kit build it r
|
|
|
1527
1527
|
`campaigns-os record build --packet <packet>`, which stamps doctor's
|
|
1528
1528
|
`derived.build_output_fingerprint.value` onto `stages.assembly.build_fingerprint`
|
|
1529
1529
|
(see "Recording stage completion" below). Doctor recomputes the value on every run
|
|
1530
|
-
(`built_output.fingerprint`): a match is a ready line,
|
|
1530
|
+
(`built_output.fingerprint`): a match is a ready line, no built output after a
|
|
1531
|
+
completed build is the warning `built_output.absent` (nothing was compared with
|
|
1532
|
+
this checkout, so build it before reading the records as current), a missing record is the
|
|
1531
1533
|
warning `built_output.fingerprint_missing` carrying the value to record, and a
|
|
1532
1534
|
recorded value the output no longer matches is `built_output.fingerprint_stale`
|
|
1533
1535
|
(blocking once assembly is complete). The polish gate, QA, and `polish capture`
|
|
@@ -1580,8 +1582,11 @@ change from a commerce-affecting one
|
|
|
1580
1582
|
|
|
1581
1583
|
Readback's `evidence_debt.<stage>.change` and `doctor --ci` read these, so the
|
|
1582
1584
|
changed-page list survives `record build`. A record made before these fields
|
|
1583
|
-
existed has no signature, and its next change reads as
|
|
1584
|
-
|
|
1585
|
+
existed has no signature, and its next change reads as commerce-affecting, with
|
|
1586
|
+
the reason `commerce_signature_unavailable`, whatever the change was. `record
|
|
1587
|
+
build` keeps a signature from then on, but the QA it would extend is still
|
|
1588
|
+
anchored before that rebuild. So page-only changes start after one full QA on
|
|
1589
|
+
1.62.0 or later, or after [a baseline](campaigns-os-build-flow.md#accepting-a-funnel-from-before-1620).
|
|
1585
1590
|
|
|
1586
1591
|
A stale or missing Assembly Source Package Fingerprint is waivable only as an
|
|
1587
1592
|
exceptional Source Freshness Waiver. The waiver must be structured in
|
|
@@ -1779,7 +1784,8 @@ When `--map-id <id>` is set, the CLI fetches `GET <proxy>/api/spec/<id>` (defaul
|
|
|
1779
1784
|
|
|
1780
1785
|
Saved-Map retrieval behavior (`--map-id`):
|
|
1781
1786
|
|
|
1782
|
-
- **Re-fetch by default.** Every `start` / `prepare-build` invocation re-fetches from KV
|
|
1787
|
+
- **Re-fetch by default.** Every `start` / `prepare-build` invocation re-fetches from KV and replaces the copy, including any `spec derive` edits in it.
|
|
1788
|
+
- **Commit the copy.** The packet, the Build Context and the Assembly Report are bound to it, and `spec derive` writes into it, so it is the campaign's spec until the next intake. A checkout without it fails doctor with `spec.local_path`. [Restore it with `spec fetch`](#restoring-a-saved-map-spec-spec-fetch).
|
|
1783
1789
|
- **One writer at a time.** The fetch happens first, but the fetched spec is written to the cache file only once the run holds the per-target prepare-build lock. A second run against the same target, fetching a newer Map revision, waits for the lock before replacing the cache, so the run holding it records the hash of the revision it actually parsed.
|
|
1784
1790
|
- **`--cached-spec`** reuses the cache without a network call. Use for offline iteration or when the proxy is temporarily unreachable. The cached copy is read as it is and never rewritten.
|
|
1785
1791
|
- **`--proxy-base <url>`** overrides the default origin. Use for staging environments or local Worker dev (`wrangler dev`). Spec retrieval carries no credential, so any reachable origin works here — but the same flag also aims the credential-bearing rails (Run Telemetry remit, QA verdict publish, `telemetry list`), and those require `https:` unless the host is loopback (`localhost`, `127.0.0.1`, `[::1]`), which is allowed over plain http with a stderr warning. A plain-http remote proxy is refused before the request. See docs/workflow-findings-sidecar.md (Remit Channel).
|
|
@@ -2114,6 +2120,26 @@ records it (`campaigns-os record setup|build|polish`; deploy and QA record their
|
|
|
2114
2120
|
own stages) → calls `next` again → repeat until stage="done"
|
|
2115
2121
|
```
|
|
2116
2122
|
|
|
2123
|
+
### Restoring a saved-Map spec (`spec fetch`)
|
|
2124
|
+
|
|
2125
|
+
Before 1.64.0 the fetched copy was ignored by Git, so a fresh clone of those repositories doesn't have it. On that checkout, run:
|
|
2126
|
+
|
|
2127
|
+
```bash
|
|
2128
|
+
campaigns-os spec fetch --packet campaign-runtime.build.json [--proxy-base <url>] [--json]
|
|
2129
|
+
```
|
|
2130
|
+
|
|
2131
|
+
It fetches the Map named by the packet, writes the copy to `spec.local_path`, and takes `fetched-specs/` out of an older managed `.gitignore` block. Commit the file so the next checkout has it. A copy already on disk is never replaced and nothing is sent. The packet and the records are not written.
|
|
2132
|
+
|
|
2133
|
+
The result's `bound` compares the copy with the spec the records were made against:
|
|
2134
|
+
|
|
2135
|
+
| `bound` | Meaning |
|
|
2136
|
+
|---|---|
|
|
2137
|
+
| `matches` | The records still apply. |
|
|
2138
|
+
| `differs` | The Map changed since they were made, or the earlier copy held `spec derive` edits the Map doesn't have. Doctor shows build, Polish and QA owed until `record spec` binds it (warning `spec.fetch.differs_from_records`). |
|
|
2139
|
+
| `unknown` | The report records no spec material to compare with. |
|
|
2140
|
+
|
|
2141
|
+
The command exits 2 for a packet that names no saved Map, for one whose `spec.local_path` is not the Map's copy, when the Map returns a spec for another campaign (`spec.fetch.identity_mismatch`), and when the fetch fails.
|
|
2142
|
+
|
|
2117
2143
|
### Refreshing after a CampaignSpec change
|
|
2118
2144
|
|
|
2119
2145
|
When the CampaignSpec's material content changes, every build, Polish and QA record made against the earlier content stops counting as current, in local-spec, saved-Map and gateway packets alike. For a saved Map or gateway packet, Campaigns OS compares the copy intake fetched; it does not check whether that copy is the latest remote revision, and reports the remote as unconfirmed. Doctor warns `spec.material_stale`, and `next`, progress, readback, the Assembly Report's own status and the QA gate show the stages that are owed again; `next` routes to the first one. Run `campaigns-os record spec --packet <packet>` to bind the new content: it updates the build context and report identity, re-reads the brief file last saved (by intake or `record brief`), keeps waivers, warning accepts, the applied theme and deploy settings, and moves each superseded stage record into that stage's `history`, which is never used as current proof. `spec derive` no longer rebinds identity; run `record spec` after it. A move of page routes binds with `record spec`, and so does a page added or removed after handoff (see [Binding a page change](#binding-a-page-change)); a change to the order of active pages, or a page added or removed before handoff, still needs intake. Re-recording an unchanged build after a spec change keeps the build owed, because the change has not reached the pages, unless the operator records a reason with `--deviation-reason`; this holds even when `record spec` was not run, and that `record build` also marks Polish and QA owed again. Polish stays owed until a new `polish capture`, with one exception: when `record build --deviation-reason` records the very output a completed Polish reviewed (same build fingerprint and source package) and only the CampaignSpec changed, not the brief's presentation, that `record build` restores the Polish record the change moved to history, stamped with the current inputs. It is the same review of the same pages. QA must produce a new verdict against the current content. Polish measurements of byte-identical output keep their values, but the Polish stage is owed again. These checks are tamper evidence, not proof: a hand-written record that copies the current values, or a packet pointed at an older copy of the spec, is not detected.
|
|
@@ -89,6 +89,44 @@ or when it was a page-scoped run that did not complete, in which case the
|
|
|
89
89
|
pages are counted from the full QA that run extended, so its unpaid pages stay
|
|
90
90
|
owed. A blocked or pending full QA owes a full run.
|
|
91
91
|
|
|
92
|
+
A change is read against the build each stage last checked, counting every
|
|
93
|
+
rebuild since, not just the latest. So a copy edit that `record build` calls
|
|
94
|
+
`page_only` still owes the full stages when an earlier rebuild since then was
|
|
95
|
+
commerce-affecting. The report names that build and that rebuild, and gives a
|
|
96
|
+
date for a QA that completed.
|
|
97
|
+
|
|
98
|
+
Page-only changes start after one full QA on 1.62.0 or later, or after a
|
|
99
|
+
baseline. A build recorded before 1.62.0 has no markup hashes to compare with,
|
|
100
|
+
so the first change on it reads as commerce-affecting.
|
|
101
|
+
|
|
102
|
+
### Accepting a funnel from before 1.62.0
|
|
103
|
+
|
|
104
|
+
A funnel handed off before 1.62.0 owes the full Polish and QA on its first
|
|
105
|
+
change, whatever the change. To start from where it is instead, a named person
|
|
106
|
+
can accept it:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
campaigns-os record build --packet <packet>
|
|
110
|
+
campaigns-os record baseline --packet <packet> --accepted-by "<named person>" --reason "<why>"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
This tests nothing and places no orders. Polish and QA read `baseline_accepted`,
|
|
114
|
+
which is never `completed`. What they held moves to history, so its purchase
|
|
115
|
+
proof stops counting. After that:
|
|
116
|
+
|
|
117
|
+
- A page-only change owes QA on that page only. A passing `qa run --page` leaves
|
|
118
|
+
QA at `baseline_accepted`, because it proved those pages and not the funnel.
|
|
119
|
+
- A commerce-affecting change owes the full Polish and QA.
|
|
120
|
+
- A CampaignSpec or brief change owes the stages again.
|
|
121
|
+
- Doctor warns `qa.baseline_accepted`, and `doctor --ci`, readback and `next` all
|
|
122
|
+
say QA rests on a baseline and no purchase has been tested since.
|
|
123
|
+
- A completed full `qa run` ends it. A blocked one leaves the baseline in place.
|
|
124
|
+
|
|
125
|
+
A funnel gets one baseline. The command refuses a funnel whose last QA was
|
|
126
|
+
blocked, one where no QA ever completed, one whose records have markup hashes,
|
|
127
|
+
and one where no full QA is owed. For the first two, run a full `qa run`. Once it
|
|
128
|
+
passes, page-only changes work without a baseline.
|
|
129
|
+
|
|
92
130
|
The PR comment opens with the kind it found and what that owes, for example
|
|
93
131
|
"Change found: page-only. It owes QA on `upsell1`; Polish is advisory." A
|
|
94
132
|
commerce-affecting change names its reasons.
|
|
@@ -116,6 +116,37 @@ Generator checklist:
|
|
|
116
116
|
These checks are the `CampaignCurrencies` validator rule, which
|
|
117
117
|
`campaigns-os doctor` runs with the rest of the spec rules.
|
|
118
118
|
|
|
119
|
+
A page package row can also carry its price in each currency, as `prices[]`:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"ref_id": 101, "qty": 1, "price": "49.99", "price_retail": "79.99",
|
|
124
|
+
"prices": [
|
|
125
|
+
{ "currency": "USD", "price": "49.99", "price_retail": "79.99" },
|
|
126
|
+
{ "currency": "EUR", "price": "46.99", "price_retail": "74.99" }
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- `price` and `price_retail` stay the default currency's. Each `prices[]`
|
|
132
|
+
entry has a `currency` code and a `price`, plus `price_retail` and, for a
|
|
133
|
+
subscription package, `price_recurring` when the Campaigns App has them.
|
|
134
|
+
- The values come from the Campaigns API read in each currency
|
|
135
|
+
(`GET /campaigns/?currency=EUR`). That read returns only the packages the
|
|
136
|
+
Campaigns App has a price for in that currency, so a package with no EUR
|
|
137
|
+
price is missing from the campaign for a shopper paying in EUR. Map Builder
|
|
138
|
+
fills `prices[]` when it refreshes from the API; set prices in the
|
|
139
|
+
Campaigns App, not in the spec. `campaigns-os spec derive` refreshes `price`
|
|
140
|
+
and `price_retail` from one default-currency read and leaves `prices[]` as
|
|
141
|
+
it was, so after changing any price, refresh the Map in Map Builder: a
|
|
142
|
+
default-currency change otherwise leaves `price` and the default currency's
|
|
143
|
+
`prices[]` entry disagreeing.
|
|
144
|
+
- `prices[]` is optional. A row without it says nothing about other
|
|
145
|
+
currencies and is not checked. A row with it should list every currency in
|
|
146
|
+
`available_currencies` and `campaign.currency`; the `PackageCurrencyPrices`
|
|
147
|
+
rule warns on a missing currency, a malformed entry and a currency listed
|
|
148
|
+
twice.
|
|
149
|
+
|
|
119
150
|
## Order Bump Notes
|
|
120
151
|
|
|
121
152
|
An order bump is still a checkout package/offer surface. It should be encoded as
|
package/docs/effects.md
CHANGED
|
@@ -98,6 +98,14 @@ are on every row for those three commands. The effect test runs each row with
|
|
|
98
98
|
the one condition whose `--proxy-base` is the loopback receiver, so the fetch
|
|
99
99
|
and the write are observed rather than taken on trust.
|
|
100
100
|
|
|
101
|
+
`spec fetch` sends the same request and writes the same file, only when the
|
|
102
|
+
packet's copy is missing, and it never replaces one. It also writes the target's
|
|
103
|
+
`.gitignore` when the managed block is missing or still lists `fetched-specs/`.
|
|
104
|
+
|
|
105
|
+
`record baseline` writes only the Assembly Report and sends nothing. It is tier C
|
|
106
|
+
because it replaces the live Polish and QA records, so any purchase proof they
|
|
107
|
+
held stops counting. History keeps them.
|
|
108
|
+
|
|
101
109
|
### The live campaign read
|
|
102
110
|
|
|
103
111
|
`doctor --packet` (with or without `--write` / `--no-write`) and every `qa run`
|
package/docs/local-setup.md
CHANGED
|
@@ -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.3.0 && npm install --save-dev --save-exact @nextcommerce/campaigns-os@1.
|
|
6
|
+
npm init -y && npm install --save-exact next-campaign-page-kit@0.3.0 && npm install --save-dev --save-exact @nextcommerce/campaigns-os@1.66.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.
|
|
29
|
+
Supported surface at generation time: `1.66.0`
|
|
30
30
|
|
|
31
31
|
## Forward compatibility
|
|
32
32
|
|
|
@@ -421,10 +421,11 @@ can load with `?currency=GBP` and still take a USD order:
|
|
|
421
421
|
price and recurring-price claims are not compared with the run-currency
|
|
422
422
|
totals: they count as unresolved, and the commercial report records
|
|
423
423
|
`unresolved_money_claims_reason: "static_source_not_in_run_currency"`. A
|
|
424
|
-
correctly built multi-currency page gets no `price-claim-mismatch` warning
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
424
|
+
correctly built multi-currency page gets no `price-claim-mismatch` warning.
|
|
425
|
+
The commercial status reads `incomplete` instead of `pass`, and a
|
|
426
|
+
`commercial-parity:incomplete:static_source_not_in_run_currency` warn row says
|
|
427
|
+
what was not compared. With `--currency` set to the Map's own currency, the
|
|
428
|
+
claims are compared as before.
|
|
428
429
|
|
|
429
430
|
Either blocker makes the verdict `blocked` and the command exit `4`. Without
|
|
430
431
|
`--currency` neither row is written and expected totals use
|
|
@@ -480,16 +481,49 @@ claims per document, 256 claims across the run, 1 MiB per price-preview
|
|
|
480
481
|
response, 256 calculate scenarios, four concurrent proxy requests, and 20
|
|
481
482
|
seconds per request. A limit, missing key, malformed response,
|
|
482
483
|
or unavailable page records incomplete commercial evidence; it never invents
|
|
483
|
-
a mismatch.
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
`
|
|
484
|
+
a mismatch. QA dispositions remain `ready`, `ready_with_exceptions`, or
|
|
485
|
+
`blocked` — `ready_with_waivers` is the doctor/`next` checkpoint-readiness term.
|
|
486
|
+
|
|
487
|
+
`commercial.status = "incomplete"` makes the verdict at least
|
|
488
|
+
`ready_with_exceptions`. It stays warn-only: it never makes a run `blocked` and
|
|
489
|
+
never changes the exit code. Each cause writes one warn row in family `pricing`,
|
|
490
|
+
`commercial-parity:incomplete:<code>`, for example
|
|
491
|
+
`commercial-parity:incomplete:missing_campaigns_api_key`. The row's `actual` says
|
|
492
|
+
what was not compared and how to fix it, so an agent reading only `exceptions[]`
|
|
493
|
+
sees the gap. The committed sidecar keeps the id, which names the cause. The report
|
|
494
|
+
repeats the causes as `commercial.incomplete_reasons`, and `qa run` prints them under
|
|
495
|
+
its commercial line. A cause an earlier one explains writes no second row, so a
|
|
496
|
+
missing key does not also report every page as unpriced.
|
|
497
|
+
|
|
498
|
+
Read the row for what to do, not the code: the set of codes is not a contract and
|
|
499
|
+
can change. Not every cause has a repair. A store coupon, two bound prices on one
|
|
500
|
+
page, a run in another currency and a funnel over the check's limits only say what
|
|
501
|
+
this run did not compare. A `commercial-currency:<RUN>:<ANSWERED>` blocker and a
|
|
502
|
+
`commercial_parity.recurring_claim_absent` warning are their own rows and add no
|
|
503
|
+
`commercial-parity:incomplete:` row.
|
|
504
|
+
|
|
505
|
+
Bound prices are read two ways, because the two bindings mean different things in
|
|
506
|
+
the source. `bundle.<id>.price` is compared as authored in the source HTML, against
|
|
507
|
+
the page's calculated total. `package.price` and `package.<ref>.price` are compared
|
|
508
|
+
as rendered, because what the source holds there is the template's placeholder (a
|
|
509
|
+
starter upsell ships `$9.99`). Under `qa run --browser`, QA reads each visible node
|
|
510
|
+
once the SDK has written it, which it knows by the `display-visible` class the SDK
|
|
511
|
+
adds, and compares it with calculate's `unit_price` for that package's row on the
|
|
512
|
+
page: one unit, after offers. So a page printing `package.price`, which is the list
|
|
513
|
+
price, beside a live offer gets a `price-claim-mismatch` warning. The package is the
|
|
514
|
+
id in the path, or else the nearest `data-next-package-id`, `data-next-package` or
|
|
515
|
+
`data-package-id`, which is how the SDK scopes it. The source is only read to learn
|
|
516
|
+
that these claims exist, so a run without `--browser` reports them as
|
|
517
|
+
`package_price_not_rendered`. `verdict.commercial.package_price_claims` counts them.
|
|
518
|
+
Under a `--currency` that is not the Map's they stay unresolved, like the source
|
|
519
|
+
prices above, because the browser pass does not load pages in the run currency.
|
|
487
520
|
|
|
488
521
|
Only contract-governed claims are compared, and only against `Exact` normalized
|
|
489
522
|
truth. Proven differences emit warn-severity `pricing` assertions named
|
|
490
523
|
`price-claim-mismatch`, `cadence-disclosure-mismatch`, or
|
|
491
|
-
`voucher-not-applied`. Decorative, ambiguous, stale,
|
|
492
|
-
|
|
524
|
+
`voucher-not-applied`. Decorative claims are ignored, and ambiguous, stale,
|
|
525
|
+
unresolved, or malformed claims never read as a mismatch: they leave the proof
|
|
526
|
+
incomplete, as above. A page showing a voucher the normalized plan cannot price
|
|
493
527
|
(no calculated pair for its code, for example a live voucher on an upsell)
|
|
494
528
|
leaves that page's price claims unresolved: the plan's only Exact truth for
|
|
495
529
|
the page is then the list price, which a voucher-priced page is expected to
|
package/docs/readback.md
CHANGED
|
@@ -525,13 +525,20 @@ the build a stage reviewed are on that stage instead. An owed stage carries:
|
|
|
525
525
|
`stages.assembly.build_changes` plus the drift on disk. `kind` is `unknown`
|
|
526
526
|
when that history does not reach the reviewed build (reason
|
|
527
527
|
`change_history_unavailable`) or the drift cannot be read. `null` on a stage
|
|
528
|
-
that is not owed.
|
|
528
|
+
that is not owed. It also carries `commerce_rebuild` when a recorded rebuild
|
|
529
|
+
since that build was not page-only: `{ "recorded_at", "build_fingerprint",
|
|
530
|
+
"kind", "latest_change_kind" }`.
|
|
529
531
|
- Polish only, `advisory`: `true` when the change is `page_only` and no brief or
|
|
530
532
|
spec change requires Polish. Advisory Polish is reported; `next`, `doctor
|
|
531
533
|
--ci` and the QA gate do not require it.
|
|
532
534
|
- QA only, `scope` and `owed_pages`: `pages` with the changed pages when the
|
|
533
535
|
change is `page_only` (`qa run --page` pays it), `full` otherwise, `null`
|
|
534
536
|
when QA is not owed.
|
|
537
|
+
- QA only, `reviewed_at`: when the completed QA the change is read from was
|
|
538
|
+
checked. Absent when QA did not complete.
|
|
539
|
+
- QA only, `baseline`: `{ "accepted_by", "accepted_at", "reason",
|
|
540
|
+
"build_fingerprint" }` while QA rests on a baseline (`record baseline`), which
|
|
541
|
+
is not a test. Present on a QA that is current as well as one that is owed.
|
|
535
542
|
|
|
536
543
|
If recording removed a
|
|
537
544
|
stage binding entirely, that stage reads unknown unless its recorded status is
|
|
@@ -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.3`, validated by `schemas/campaigns-os-runtime-recipe.v1.schema.json` (`Campaigns OS Runtime Recipe v1`). Supported surface at generation time: `1.
|
|
11
|
+
Recipe kind `campaigns-os-node-v1`, revision `1.0.3`, validated by `schemas/campaigns-os-runtime-recipe.v1.schema.json` (`Campaigns OS Runtime Recipe v1`). Supported surface at generation time: `1.66.0`.
|
|
12
12
|
|
|
13
13
|
## What this is
|
|
14
14
|
|
|
@@ -81,7 +81,7 @@ Not redundant with the install step. Because install runs with lifecycle scripts
|
|
|
81
81
|
|
|
82
82
|
The complete input set, enumerated explicitly rather than globbed. The compiler's configured include globs are NOT the input set: two root modules enter the compilation transitively through imports from the entry module and are emitted, so a fingerprint derived from the globs would cover 36 of the 38 compiled sources and miss one of the larger emitted surfaces. Test fixtures and the package's own tests are not inputs; nothing under them is emitted. A checker resolves the compiler's actual file list and asserts it equals this enumeration, so this list cannot rot silently.
|
|
83
83
|
|
|
84
|
-
Fingerprint algorithm `sha256`, over
|
|
84
|
+
Fingerprint algorithm `sha256`, over 51 enumerated files:
|
|
85
85
|
|
|
86
86
|
- `campaign-spec/analytics-vendors.ts`
|
|
87
87
|
- `campaign-spec/analytics-vocabulary.ts`
|
|
@@ -109,6 +109,7 @@ Fingerprint algorithm `sha256`, over 50 enumerated files:
|
|
|
109
109
|
- `campaign-spec/rules/index.ts`
|
|
110
110
|
- `campaign-spec/rules/offer-ref-integrity.ts`
|
|
111
111
|
- `campaign-spec/rules/one-checkout-per-path.ts`
|
|
112
|
+
- `campaign-spec/rules/package-currency-prices.ts`
|
|
112
113
|
- `campaign-spec/rules/package-pricing-sanity.ts`
|
|
113
114
|
- `campaign-spec/rules/page-count.ts`
|
|
114
115
|
- `campaign-spec/rules/page-id-uniqueness.ts`
|
|
@@ -140,7 +141,7 @@ The output directory is a build product. It is untracked and git-ignored, no com
|
|
|
140
141
|
|
|
141
142
|
Directory `campaign-spec/dist`. Committed: `false`. Type entry `campaign-spec/dist/index.d.ts`.
|
|
142
143
|
|
|
143
|
-
Expected inventory is derived, never listed twice. For every enumerated input under module_source_root whose path ends in .ts, the build is expected to emit campaign-spec/dist/<path relative to module_source_root, with .ts replaced> once per entry in emitted_extensions. The expected inventory is exactly that set: nothing missing, nothing extra. Emitted extensions: `.js`, `.d.ts`. At this revision that derivation yields
|
|
144
|
+
Expected inventory is derived, never listed twice. For every enumerated input under module_source_root whose path ends in .ts, the build is expected to emit campaign-spec/dist/<path relative to module_source_root, with .ts replaced> once per entry in emitted_extensions. The expected inventory is exactly that set: nothing missing, nothing extra. Emitted extensions: `.js`, `.d.ts`. At this revision that derivation yields 94 files.
|
|
144
145
|
|
|
145
146
|
### Mandatory checks
|
|
146
147
|
|
package/docs/skills-revision.md
CHANGED
|
@@ -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.
|
|
19
|
+
"bundle_revision": "1.66.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.
|
|
28
|
+
with the prefix**. `1.66.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.
|
|
40
|
+
Bundle revision: 1.66.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.
|
|
51
|
+
npx --no-install campaigns-os tooling status --skills-revision 1.66.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.
|
|
93
|
+
"requested": "1.66.0+skills.1",
|
|
94
94
|
"spelling": "bundle",
|
|
95
|
-
"on_disk": "1.
|
|
95
|
+
"on_disk": "1.66.0+skills.1",
|
|
96
96
|
"on_disk_skill": null,
|
|
97
|
-
"message": "match (1.
|
|
97
|
+
"message": "match (1.66.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.
|
|
105
|
-
Skills revision: mismatch: loaded 1.39.0+skills.1, on disk 1.
|
|
106
|
-
Skills revision: unchecked (on disk 1.
|
|
104
|
+
Skills revision: match (1.66.0+skills.1)
|
|
105
|
+
Skills revision: mismatch: loaded 1.39.0+skills.1, on disk 1.66.0+skills.1 — start a fresh session
|
|
106
|
+
Skills revision: unchecked (on disk 1.66.0+skills.1)
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
`unchecked` is the state when the flag is absent. It is not an error — an
|
|
@@ -14,7 +14,7 @@ implementation detail, however stable it looks.
|
|
|
14
14
|
| Surface | Contract | Change discipline |
|
|
15
15
|
|---|---|---|
|
|
16
16
|
| `schemas/*.schema.json` (all of them) | The portable contract catalog: CampaignSpec, Design Source Package, Build Packet, Build Context, Assembly Report, Doctor Output, sidecar-bundle conformance, Run Record, Workflow Finding, Build Brief, Source-HTML Manifest, Tooling Orientation, Release Ledger, QA Verdict, the QA Verdict sidecar projection, Runtime Recipe, and the legacy-migration inventory/plan/receipt trio. | Hashed. Any content change requires updating the recorded hash **and** bumping `surface_version` in the same PR. A shape change that alters meaning gets a new schema-version const — one version identifier must never cover two shapes (the 2026-08 assembly-report drift is the incident this rule encodes). Additions to an open `v0` schema are expected and consumers must tolerate unknown fields; the security-sensitive legacy-migration schemas are closed, so additions there require a new lineage. 1.28.0 (RL entry `surface_version: 1.28.0`, breaking) removed the two required Build Packet booleans `qa.test_orders_allowed` and `qa.sandbox_test_card_confirmed` (nothing read them; test orders run from `--test-order <mode>` alone), added `local-serve` to the `deploy.target` enum, and added the optional `remit_result` / `remit_base_kind` fields to the Run Record. 1.30.0 (additive) added the optional `data_layer` record to the QA Verdict's `test_orders[]` entries — the order's `dl_purchase` reading (#325). 1.33.0 (additive) added the optional `qa_verdict_publish` block to the Run Record — which verdict was posted to the QA portal, by `qa run` or `qa publish`, and what the portal answered, in the `remit_result` vocabulary (#328). 1.55.0 (breaking) adds the optional publish decision `reason`, record `closed_by` closer, QA observation `verdict_run_id`, `build_fingerprint`, and `stale`, lifecycle `stages[].wait_ms` and `stages[].finding_codes`, and run-level `needs_input_count` and `failure_count` while removing the unused `agent_usage` field from the same v0 Run Record shape. 1.56.0 (breaking) adds `schemas/campaign-spec.v5.schema.json` (CampaignSpec `5.0`: the `checkout_step` page type, and `checkout` meaning the page that takes payment; 4.2/4.3 specs still validate against the v4 schema and are upgraded on read) and the `success_url` and `step_number` keys to the closed `page_kit.frontmatter` object of the Build Packet and Build Context v0 schemas, so a validator holding the 1.55.0 schemas refuses a packet that carries them (campaigns-os#641). |
|
|
17
|
-
| CLI commands: `start`, `prepare-build`, `build`, `polish`, `checkpoint`, `page-kit`, `spec`, `doctor`, `bundle`, `next`, `theme`, `tooling`, `install-skills`, `install-agent-context`, `validate-assembly-report`, `telemetry`, `standardize`, `qa`, `findings`, `run-record`, `run`, `sdk`, `demo`, `login`, `logout` | Scriptable entry points. The founding list (1.0.0) was the argv surface Campaigns Agent's remit fixture pins; 1.25.0 adds the partner entry path the public guides instruct — `install-skills` (skill install), `tooling status` (preflight), `next --packet` (the runtime cursor) — plus `theme`, `install-agent-context`, `validate-assembly-report`, and `telemetry`, since consent is part of the partner contract. `validate-build-packet`, formerly an undocumented and unsupported alias of `doctor`, was removed in 1.25.0+agent.7 (RL-0036) and now gets the unknown-command error; use `doctor`. `standardization-report`, a supported but redundant second spelling of `standardize` — same flags, same output, same exit codes — was removed in 1.27.0 and now gets the same unknown-command error; use `standardize`. Removing a supported command is a breaking change and is why that release moved the minor. `bundle check` validates the canonical JSON readback set and makes QA required only under `--require-qa`. `polish capture` owns page-load evidence production. `checkpoint waive` currently accepts five registered gates: `page_kit.store_profile`, `page_kit.sdk_version`, `polish.hidden_eager_media`, `built_output.upsell_selector_scope`, and `source_html.producer_provenance` (per page, with `--page <page_id>`). `page-kit sync` (added in 1.29.0) writes the CampaignSpec's Store Profile fields and SDK pin into the target's `_data/campaigns.json` entry — the repair the first two gates name — and is the only `page-kit` subcommand. `spec derive` (added in 1.31.0) is the reverse write for the repo-derived field class (#432): the target's SDK pin, page routes and analytics ids into the packet's local CampaignSpec;
|
|
17
|
+
| CLI commands: `start`, `prepare-build`, `build`, `polish`, `checkpoint`, `page-kit`, `spec`, `doctor`, `bundle`, `next`, `theme`, `tooling`, `install-skills`, `install-agent-context`, `validate-assembly-report`, `telemetry`, `standardize`, `qa`, `findings`, `run-record`, `run`, `sdk`, `demo`, `login`, `logout` | Scriptable entry points. The founding list (1.0.0) was the argv surface Campaigns Agent's remit fixture pins; 1.25.0 adds the partner entry path the public guides instruct — `install-skills` (skill install), `tooling status` (preflight), `next --packet` (the runtime cursor) — plus `theme`, `install-agent-context`, `validate-assembly-report`, and `telemetry`, since consent is part of the partner contract. `validate-build-packet`, formerly an undocumented and unsupported alias of `doctor`, was removed in 1.25.0+agent.7 (RL-0036) and now gets the unknown-command error; use `doctor`. `standardization-report`, a supported but redundant second spelling of `standardize` — same flags, same output, same exit codes — was removed in 1.27.0 and now gets the same unknown-command error; use `standardize`. Removing a supported command is a breaking change and is why that release moved the minor. `bundle check` validates the canonical JSON readback set and makes QA required only under `--require-qa`. `polish capture` owns page-load evidence production. `checkpoint waive` currently accepts five registered gates: `page_kit.store_profile`, `page_kit.sdk_version`, `polish.hidden_eager_media`, `built_output.upsell_selector_scope`, and `source_html.producer_provenance` (per page, with `--page <page_id>`). `page-kit sync` (added in 1.29.0) writes the CampaignSpec's Store Profile fields and SDK pin into the target's `_data/campaigns.json` entry — the repair the first two gates name — and is the only `page-kit` subcommand. `spec derive` (added in 1.31.0) is the reverse write for the repo-derived field class (#432): the target's SDK pin, page routes and analytics ids into the packet's local CampaignSpec; the `page_kit.sdk_version.repo_newer` advisory names it; its `--write-map` flag (1.33.0+agent.2) also records the derived pin in the saved Map's Build hints field through the proxy Worker, never moving a Map pin backwards. `record baseline` (added in 1.66.0) accepts a handed-off funnel from before markup hashes as the starting point for page-only changes, one time per funnel, without testing it. `spec fetch` (added in 1.64.0) puts a saved-Map packet's CampaignSpec back on a checkout that lacks it, never replacing a copy on disk. `qa publish` (added in 1.33.0) posts an already-stored verdict to the QA portal without a re-run or an order, refusing a stale `spec_hash` or a verdict its Run Record already records as published (#328). Within Polish, only the broader Source Freshness waiver remains on its existing report lane; theme and QA decisions also retain their existing lanes. | Any change to this list — adding, renaming, or removing a command — bumps `surface_version` in the same PR, and since 1.25.0 `check-supported-surface.mjs --base` enforces that (before, only hashed and named entries owed a bump, so `checkpoint` landed unbumped). Subcommands, registered gates, and flags may grow freely beneath a listed command. Removing a listed command from dispatch fails the gate outright. Do not infer support for an unregistered checkpoint from the top-level command. |
|
|
18
18
|
| `sdk storage-check` and `docs/sdk-storage-compatibility.md` | Read-only AST compatibility report for explicitly scoped tracked campaign HTML/JS before an SDK bump; consumes the supplied SDK-owned manifest and records its SHA-256 and verified or unverified local Git provenance. Incompatible and unknown findings exit 2; a clean scan is static source evidence only. | Additive supported CLI and named documentation; no automatic merchant integration repair. |
|
|
19
19
|
| `sdk repin` | Rewrites the version segment of semver-pinned Campaign Cart loader.js / campaign-cart.css URLs in a static repo (no `_data/campaigns.json`) to a target version, default the SDK support policy's `preferred_minimum`. It previews by default. `--apply` writes the files and the change record `.campaign-runtime/sdk-repin.json` (`campaigns-os-sdk-repin/v0`). `@latest`, `@main`, commit and prerelease refs, and pins at or above the target, are reported and never rewritten. A page-kit repo is refused with exit 2. Usage is in `docs/sdk-storage-compatibility.md`. | Additive supported subcommand under the listed `sdk` command. It never changes anything in a URL except the version segment. |
|
|
20
20
|
| `bin/campaigns-os.mjs` (`campaigns-os`) | The CLI entry itself. | Declared in `package.json` `bin`; the gate fails if it disappears. |
|
package/package.json
CHANGED
|
@@ -254,6 +254,21 @@
|
|
|
254
254
|
"name": { "type": ["string", "null"] },
|
|
255
255
|
"price": { "type": ["string", "number", "null"] },
|
|
256
256
|
"price_retail": { "type": ["string", "number", "null"] },
|
|
257
|
+
"prices": {
|
|
258
|
+
"type": ["array", "null"],
|
|
259
|
+
"description": "The package's price in each currency the Campaigns App prices it in, one entry per currency, read from the Campaigns API (GET /campaigns/?currency=<code>), which returns only the packages priced in the requested currency. price and price_retail stay the default currency's. Optional: a row without prices (absent or null) says nothing about other currencies. A row with prices should list every currency in campaign.available_currencies and campaign.currency; the PackageCurrencyPrices rule warns when one is missing, since the campaign leaves the package out in that currency.",
|
|
260
|
+
"items": {
|
|
261
|
+
"type": "object",
|
|
262
|
+
"additionalProperties": true,
|
|
263
|
+
"required": ["currency", "price"],
|
|
264
|
+
"properties": {
|
|
265
|
+
"currency": { "type": "string", "description": "Three-letter upper-case ISO 4217 code." },
|
|
266
|
+
"price": { "type": ["string", "number"] },
|
|
267
|
+
"price_retail": { "type": ["string", "number", "null"] },
|
|
268
|
+
"price_recurring": { "type": ["string", "number", "null"] }
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
},
|
|
257
272
|
"product_name": { "type": ["string", "null"] },
|
|
258
273
|
"product_sku": { "type": ["string", "null"] },
|
|
259
274
|
"product_variant_name": { "type": ["string", "null"] },
|
|
@@ -254,6 +254,21 @@
|
|
|
254
254
|
"name": { "type": ["string", "null"] },
|
|
255
255
|
"price": { "type": ["string", "number", "null"] },
|
|
256
256
|
"price_retail": { "type": ["string", "number", "null"] },
|
|
257
|
+
"prices": {
|
|
258
|
+
"type": ["array", "null"],
|
|
259
|
+
"description": "The package's price in each currency the Campaigns App prices it in, one entry per currency, read from the Campaigns API (GET /campaigns/?currency=<code>), which returns only the packages priced in the requested currency. price and price_retail stay the default currency's. Optional: a row without prices (absent or null) says nothing about other currencies. A row with prices should list every currency in campaign.available_currencies and campaign.currency; the PackageCurrencyPrices rule warns when one is missing, since the campaign leaves the package out in that currency.",
|
|
260
|
+
"items": {
|
|
261
|
+
"type": "object",
|
|
262
|
+
"additionalProperties": true,
|
|
263
|
+
"required": ["currency", "price"],
|
|
264
|
+
"properties": {
|
|
265
|
+
"currency": { "type": "string", "description": "Three-letter upper-case ISO 4217 code." },
|
|
266
|
+
"price": { "type": ["string", "number"] },
|
|
267
|
+
"price_retail": { "type": ["string", "number", "null"] },
|
|
268
|
+
"price_recurring": { "type": ["string", "number", "null"] }
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
},
|
|
257
272
|
"product_name": { "type": ["string", "null"] },
|
|
258
273
|
"product_sku": { "type": ["string", "null"] },
|
|
259
274
|
"product_variant_name": { "type": ["string", "null"] },
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"disposition": {
|
|
95
95
|
"type": "string",
|
|
96
96
|
"enum": ["ready", "ready_with_exceptions", "blocked"],
|
|
97
|
-
"description": "Derived from the assertions (and the commercial section's status): any unwaived failing blocker => blocked; any warn/manual_review/waived assertion
|
|
97
|
+
"description": "Derived from the assertions (and the commercial section's status): any unwaived failing blocker => blocked; any warn/manual_review/waived assertion, a proven commercial mismatch or incomplete commercial coverage => ready_with_exceptions; otherwise ready. Waived is never clean. ready_with_waivers is the doctor/next checkpoint term, never a verdict disposition."
|
|
98
98
|
},
|
|
99
99
|
"assertions": {
|
|
100
100
|
"type": "array",
|
|
@@ -417,7 +417,7 @@
|
|
|
417
417
|
"status": {
|
|
418
418
|
"type": "string",
|
|
419
419
|
"enum": ["pass", "mismatch", "incomplete", "not_applicable", "not_run"],
|
|
420
|
-
"description": "mismatch
|
|
420
|
+
"description": "mismatch and incomplete each keep the verdict at ready_with_exceptions at least, and neither blocks it. incomplete also writes a warn commercial-parity:incomplete:<code> assertion for each named cause, and never invents a mismatch."
|
|
421
421
|
},
|
|
422
422
|
"coverage_complete": { "type": "boolean" },
|
|
423
423
|
"findings": {
|
|
@@ -436,6 +436,21 @@
|
|
|
436
436
|
}
|
|
437
437
|
},
|
|
438
438
|
"description": "Sanitized capture/proxy/limit issue codes with counts. An issue records incomplete evidence; it never invents a mismatch."
|
|
439
|
+
},
|
|
440
|
+
"incomplete_reasons": {
|
|
441
|
+
"type": "array",
|
|
442
|
+
"items": {
|
|
443
|
+
"type": "object",
|
|
444
|
+
"additionalProperties": true,
|
|
445
|
+
"required": ["code", "summary", "fix"],
|
|
446
|
+
"properties": {
|
|
447
|
+
"code": { "type": "string", "minLength": 1 },
|
|
448
|
+
"count": { "type": "integer" },
|
|
449
|
+
"summary": { "type": "string", "description": "What this run did not compare, and why." },
|
|
450
|
+
"fix": { "type": "string", "description": "What to do about it. Some causes have nothing to repair and say so." }
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
"description": "Present when status is incomplete: one entry per named cause, the same causes the commercial-parity:incomplete:<code> assertions carry. Empty when every cause already has a row of its own. Absent on verdicts emitted before the field existed."
|
|
439
454
|
}
|
|
440
455
|
},
|
|
441
456
|
"description": "The compact commercial-parity report: counters over checked pages, scenarios, and claims, plus findings and issues. Counter fields are additive and not individually contracted here."
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: campaign-lifecycle-orientation
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.52
|
|
4
4
|
description: Orient a reader to the Campaigns OS lifecycle artifacts a run has already emitted, without advancing any stage or changing any state.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.66.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.66.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: campaign-readback-classification
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.52
|
|
4
4
|
description: Classify a selected campaign from the readback projection's v2 fields and write a read-only handoff without turning diagnosis into permission.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.66.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.66.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|