@nextcommerce/campaigns-os 1.63.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/campaign-spec/dist/types.d.ts +1 -1
  3. package/compatibility.json +1 -1
  4. package/contracts/effects.v1.json +129 -1
  5. package/contracts/release-ledger.json +549 -0
  6. package/contracts/supported-surface.json +3 -3
  7. package/docs/build-packet.md +31 -5
  8. package/docs/campaigns-os-build-flow.md +38 -0
  9. package/docs/campaignspec-authoring-examples.md +3 -2
  10. package/docs/effects.md +8 -0
  11. package/docs/local-setup.md +1 -1
  12. package/docs/orientation-contract-reference.md +1 -1
  13. package/docs/qa-and-test-orders.md +44 -10
  14. package/docs/readback.md +8 -1
  15. package/docs/runtime-readiness.md +1 -1
  16. package/docs/skills-revision.md +10 -10
  17. package/docs/supported-surface.md +1 -1
  18. package/package.json +1 -1
  19. package/schemas/campaigns-os-qa-verdict.v0.schema.json +17 -2
  20. package/skills/campaign-lifecycle-orientation/SKILL.md +3 -3
  21. package/skills/campaign-readback-classification/SKILL.md +3 -3
  22. package/skills/campaign-run-evidence/SKILL.md +3 -3
  23. package/skills/contribution-intake/SKILL.md +3 -3
  24. package/skills/next-campaigns-build/SKILL.md +3 -3
  25. package/skills/next-campaigns-os/SKILL.md +3 -3
  26. package/skills/next-campaigns-os-setup/SKILL.md +3 -3
  27. package/skills/next-campaigns-polish/SKILL.md +3 -3
  28. package/skills/next-campaigns-qa/SKILL.md +4 -4
  29. package/skills/next-campaigns-sdk-bump/SKILL.md +3 -3
  30. package/skills.json +11 -11
  31. package/src/change-scope.mjs +25 -6
  32. package/src/cli.mjs +120 -7
  33. package/src/commercial-journey.mjs +3 -0
  34. package/src/commercial-parity.mjs +157 -7
  35. package/src/doctor/checks.mjs +28 -3
  36. package/src/doctor/ci.mjs +36 -1
  37. package/src/evidence-debt.mjs +9 -1
  38. package/src/input-currency.mjs +4 -2
  39. package/src/input-refresh.mjs +1 -1
  40. package/src/invocation.mjs +3 -2
  41. package/src/orchestration-stage-contract.mjs +3 -1
  42. package/src/polish-gate.mjs +10 -0
  43. package/src/qa-browser.mjs +46 -0
  44. package/src/qa-commercial-parity.mjs +173 -18
  45. package/src/qa-node.mjs +10 -5
  46. package/src/qa-verdict.mjs +3 -1
  47. package/src/readback.mjs +1 -0
  48. package/src/runtime-state-ignore.mjs +44 -19
  49. package/src/stage-ledger.mjs +35 -9
  50. package/src/stage-record.mjs +93 -14
@@ -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/`, `fetched-specs/`, `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. The readback bundle (`build-context.json`, `assembly-report.json`, `doctor-output.json`, `qa-verdict.json`), `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.
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, a missing record is the
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 unknown (full Polish and
1584
- QA) until `record build` records one.
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. KV is the source of truth; the cache file is a debug/inspection artifact, not a performance optimization.
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.
@@ -138,8 +138,9 @@ A page package row can also carry its price in each currency, as `prices[]`:
138
138
  fills `prices[]` when it refreshes from the API; set prices in the
139
139
  Campaigns App, not in the spec. `campaigns-os spec derive` refreshes `price`
140
140
  and `price_retail` from one default-currency read and leaves `prices[]` as
141
- it was, so after changing a non-default price, refresh the Map in Map
142
- Builder.
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.
143
144
  - `prices[]` is optional. A row without it says nothing about other
144
145
  currencies and is not checked. A row with it should list every currency in
145
146
  `available_currencies` and `campaign.currency`; the `PackageCurrencyPrices`
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`
@@ -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.63.0 && npx --no-install campaigns-os tooling setup --target . --platform claude
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.63.0`
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
- and the commercial status reads `incomplete` instead of `pass`. With
426
- `--currency` set to the Map's own currency, the claims are compared as
427
- before.
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. `commercial.status = "incomplete"` preserves the disposition
484
- derived from the ordinary QA assertions; it does not create an exception by
485
- itself. QA dispositions remain `ready`, `ready_with_exceptions`, or `blocked` —
486
- `ready_with_waivers` is the doctor/`next` checkpoint-readiness term.
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, unresolved, or malformed
492
- claims remain silent. A page showing a voucher the normalized plan cannot price
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.63.0`.
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
 
@@ -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.63.0+skills.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.63.0+skills.1` is therefore ahead of `1.40.0+skills.7`.
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.63.0+skills.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.63.0+skills.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.63.0+skills.1",
93
+ "requested": "1.66.0+skills.1",
94
94
  "spelling": "bundle",
95
- "on_disk": "1.63.0+skills.1",
95
+ "on_disk": "1.66.0+skills.1",
96
96
  "on_disk_skill": null,
97
- "message": "match (1.63.0+skills.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.63.0+skills.1)
105
- Skills revision: mismatch: loaded 1.39.0+skills.1, on disk 1.63.0+skills.1 — start a fresh session
106
- Skills revision: unchecked (on disk 1.63.0+skills.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; it is the only `spec` subcommand, and 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. `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. |
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextcommerce/campaigns-os",
3
- "version": "1.63.0",
3
+ "version": "1.66.0",
4
4
  "description": "Toolkit for agent-assisted NEXT campaign builds.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -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 or a proven commercial mismatch => ready_with_exceptions; otherwise ready. Waived is never clean. ready_with_waivers is the doctor/next checkpoint term, never a verdict disposition."
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 keeps the verdict at ready_with_exceptions; incomplete preserves the disposition derived from ordinary assertions and never invents an exception."
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.49
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.63.0+skills.1
8
- Run `npx --no-install campaigns-os tooling status --skills-revision 1.63.0+skills.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.49
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.63.0+skills.1
8
- Run `npx --no-install campaigns-os tooling status --skills-revision 1.63.0+skills.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-run-evidence
3
- version: 1.0.49
3
+ version: 1.0.52
4
4
  description: Interpret existing Campaigns OS doctor, QA and proof-depth evidence without claiming more proof than the artifacts contain.
5
5
  ---
6
6
 
7
- Bundle revision: 1.63.0+skills.1
8
- Run `npx --no-install campaigns-os tooling status --skills-revision 1.63.0+skills.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: contribution-intake
3
- version: 1.0.49
3
+ version: 1.0.52
4
4
  description: Turn a suggestion about the agent surface into a classified, evidence-checked proposal and, only with attended approval, one issue on this repository's tracker.
5
5
  ---
6
6
 
7
- Bundle revision: 1.63.0+skills.1
8
- Run `npx --no-install campaigns-os tooling status --skills-revision 1.63.0+skills.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: next-campaigns-build
3
- version: 1.0.54
3
+ version: 1.0.57
4
4
  description: Assemble a NEXT campaign from a doctor-cleared Build Packet, CampaignSpec/API values, prepared HTML/assets, page-kit, and starter-template contracts.
5
5
  ---
6
6
 
7
- Bundle revision: 1.63.0+skills.1
8
- Run `npx --no-install campaigns-os tooling status --skills-revision 1.63.0+skills.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: next-campaigns-os
3
- version: 1.0.69
3
+ version: 1.0.72
4
4
  description: Coordinate Campaigns OS lifecycle workflows from CampaignSpec, Build Packet, starter-template contracts, stage reports, deploy evidence, and QA proof depth.
5
5
  ---
6
6
 
7
- Bundle revision: 1.63.0+skills.1
8
- Run `npx --no-install campaigns-os tooling status --skills-revision 1.63.0+skills.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: next-campaigns-os-setup
3
- version: 2.0.52
3
+ version: 2.0.55
4
4
  description: Bootstrap or prepare a target page-kit campaign repo from a doctor-cleared Campaigns OS Build Packet before full build wiring. Formerly installed as next-campaigns-setup; renamed 2026-08 to stop colliding with the published NextCommerceCo/skills scaffolder of that name.
5
5
  ---
6
6
 
7
- Bundle revision: 1.63.0+skills.1
8
- Run `npx --no-install campaigns-os tooling status --skills-revision 1.63.0+skills.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: next-campaigns-polish
3
- version: 1.1.54
3
+ version: 1.1.57
4
4
  description: Run the visual/runtime polish pass after build and before QA for a Campaigns OS campaign.
5
5
  ---
6
6
 
7
- Bundle revision: 1.63.0+skills.1
8
- Run `npx --no-install campaigns-os tooling status --skills-revision 1.63.0+skills.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: next-campaigns-qa
3
- version: 1.3.53
3
+ version: 1.3.56
4
4
  description: Run spec-aware QA from a saved Map or local-spec Build Packet and tested campaign URL after build, polish, and deploy/local evidence exist, including Playwright typed-card test-order proof.
5
5
  ---
6
6
 
7
- Bundle revision: 1.63.0+skills.1
8
- Run `npx --no-install campaigns-os tooling status --skills-revision 1.63.0+skills.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
@@ -75,7 +75,7 @@ Inputs:
75
75
  Rules:
76
76
 
77
77
  - Use the public Node/npm `campaigns-os qa` commands for campaign QA runs.
78
- - Canonical `qa run` automatically runs commercial parity for enabled pages with package rows when their source evidence is available. It reuses each page's read-only source fetch and the existing read-only `POST /api/price-preview`; neither creates carts, orders, or transactions. The lane is warn-first: proven price, recurring-cadence, and voucher mismatches become warn-severity `pricing` assertions, with compact coverage and findings at top-level `verdict.commercial`. Omitted or otherwise unprovable claims emit no mismatch assertion and do not change an otherwise-ready disposition; incomplete coverage remains visible in `verdict.commercial`.
78
+ - Canonical `qa run` automatically runs commercial parity for enabled pages with package rows when their source evidence is available. It reuses each page's read-only source fetch and the existing read-only `POST /api/price-preview`; neither creates carts, orders, or transactions. The lane is warn-first: proven price, recurring-cadence, and voucher mismatches become warn-severity `pricing` assertions, with compact coverage and findings at top-level `verdict.commercial`. Unprovable claims never read as a mismatch. Incomplete coverage (for example a spec with no `campaign.campaigns_api_key`, so no scenario ran) keeps the run at `ready_with_exceptions` at least, never `blocked`, with a warn row `commercial-parity:incomplete:<code>` in `exceptions[]` that names the cause and the fix. Report it to the operator, and do not call a run whose commercial status is `incomplete` fully proven.
79
79
  - Use `qa parity` for migration cells that carry a parity fixture; select the fixture scenario and drive that offer through the candidate funnel.
80
80
  - Parity capture blocking proof is the voucher-adjusted persisted line from typed-card order readback. Browser totals and client state do not replace the persisted-line voucher guard.
81
81
  - Read client purchase values per event. A whole-cart `dl_purchase` must not mask or supply an offer-level upsell purchase expectation.
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: next-campaigns-sdk-bump
3
- version: 1.0.9
3
+ version: 1.0.12
4
4
  description: Bump a live campaign's Campaign Cart SDK from one 0.4.x release to a later 0.4.x on a page-kit or static repo. Use for any request to bump, upgrade or move a campaign to SDK 0.4.x, including when the repository has its own BUMP.md. Find every pin, check the release diff and storage against the campaign's own code, prove on a preview with approved test orders, and report what is not proven.
5
5
  ---
6
6
 
7
- Bundle revision: 1.63.0+skills.1
8
- Run `npx --no-install campaigns-os tooling status --skills-revision 1.63.0+skills.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