@nextcommerce/campaigns-os 1.60.0 → 1.61.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 +126 -0
- package/README.md +5 -5
- package/agents/claude/CLAUDE.md +15 -0
- package/agents/codex/AGENTS.md +15 -0
- package/agents/copilot/copilot-instructions.md +15 -0
- package/agents/cursor/campaigns-os.mdc +15 -0
- package/compatibility.json +1 -1
- package/contracts/brand-theme-target-tokens.next-core.v0.json +95 -25
- package/contracts/campaign-cart-sdk-support-policy.v0.json +2 -2
- package/contracts/commerce-surface-catalog.json +27 -26
- package/contracts/release-ledger.json +768 -0
- package/contracts/supported-surface.json +4 -4
- package/contracts/template-brand-contract.shared-commerce.v0.json +1 -1
- package/docs/access-model.md +20 -0
- package/docs/brand-theme-bridge.md +62 -3
- package/docs/build-packet.md +28 -1
- package/docs/campaign-build-brief.md +16 -0
- package/docs/campaigns-os-build-flow.md +61 -2
- package/docs/campaignspec-authoring-examples.md +116 -0
- package/docs/design-source-package.md +46 -2
- package/docs/entry-points.md +188 -0
- package/docs/local-setup.md +1 -1
- package/docs/orientation-contract-reference.md +1 -1
- package/docs/polish-evidence.md +40 -2
- package/docs/qa-and-test-orders.md +50 -2
- package/docs/quickstart.md +438 -0
- package/docs/readback.md +119 -6
- package/docs/runtime-readiness.md +1 -1
- package/docs/sdk-storage-compatibility.md +3 -1
- package/docs/skills-revision.md +10 -10
- package/docs/source-adapters.md +213 -0
- package/package.json +10 -3
- package/schemas/campaign-design-source-package.v0.schema.json +8 -0
- package/schemas/campaigns-os-readback.v2.schema.json +177 -1
- package/schemas/source-html-manifest.v0.schema.json +3 -0
- 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 +4 -4
- package/skills/next-campaigns-os/SKILL.md +4 -3
- package/skills/next-campaigns-os-setup/SKILL.md +3 -3
- package/skills/next-campaigns-polish/SKILL.md +6 -9
- package/skills/next-campaigns-qa/SKILL.md +3 -3
- package/skills/next-campaigns-sdk-bump/SKILL.md +117 -24
- package/skills.json +11 -11
- package/src/brand-theme.mjs +413 -8
- package/src/brief-summary.mjs +6 -2
- package/src/browser-launch.mjs +2 -1
- package/src/build-brief.mjs +9 -2
- package/src/built-site-scope.mjs +2 -2
- package/src/cli.mjs +52 -13
- package/src/design-file-reference.mjs +93 -0
- package/src/design-source-package.mjs +55 -1
- package/src/doctor/checks.mjs +8 -74
- package/src/doctor/ci.mjs +112 -0
- package/src/doctor/inspect.mjs +18 -0
- package/src/doctor/source-provenance.mjs +74 -0
- package/src/evidence-debt.mjs +119 -0
- package/src/parity/compare.mjs +316 -0
- package/src/parity/design-access.mjs +54 -0
- package/src/parity/element-parity-record.mjs +569 -0
- package/src/parity/probe.mjs +205 -0
- package/src/parity/snapshot.mjs +110 -0
- package/src/png-header.mjs +87 -0
- package/src/polish-browser.mjs +186 -9
- package/src/polish-captures-record.mjs +52 -0
- package/src/polish-deadline.mjs +14 -0
- package/src/polish-gate.mjs +25 -6
- package/src/polish-node.mjs +81 -7
- package/src/polish-visual-fixtures.mjs +74 -0
- package/src/polish-visual.mjs +606 -0
- package/src/qa-analytics-correctness.mjs +14 -2
- package/src/qa-analytics-parity.mjs +94 -5
- package/src/qa-node.mjs +39 -6
- package/src/qc-accept.mjs +148 -6
- package/src/qc-check-registry.mjs +37 -1
- package/src/qc-results.mjs +404 -16
- package/src/readback.mjs +57 -13
- package/src/sdk-attribute-index.mjs +11 -11
- package/src/sdk-markup.mjs +77 -2
- package/src/sdk-storage-compatibility.mjs +94 -4
- package/src/source-html-manifest.mjs +7 -0
- package/src/source-html-server.mjs +129 -0
- package/src/stage-record.mjs +45 -7
- package/src/theme-gate.mjs +2 -2
- package/src/visual-diff.mjs +296 -0
- package/src/visual-reference.mjs +126 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
# Quickstart
|
|
2
|
+
|
|
3
|
+
Read [activation, access, and evidence](activation-and-evidence.md) before
|
|
4
|
+
interpreting an installed toolkit, saved Map, demo, or preview as campaign proof.
|
|
5
|
+
|
|
6
|
+
This path is optimized for a developer using Claude Code or another AI coding tool with a prepared campaign design.
|
|
7
|
+
|
|
8
|
+
For Claude Code, [local campaign setup](local-setup.md) combines the pinned
|
|
9
|
+
dependency install, skills, connected project instructions and QA browser in
|
|
10
|
+
one setup entry. It preserves authored source and project instructions. The
|
|
11
|
+
detailed/manual paths below remain supported.
|
|
12
|
+
|
|
13
|
+
## Install without a clone
|
|
14
|
+
|
|
15
|
+
Requirements: Node `>=20.19.0` and npm 10 or 11 (Node 22 ships npm 10). The
|
|
16
|
+
primary way to run Campaigns OS is pinned as a devDependency of the campaign
|
|
17
|
+
folder — a page-kit project — and run through `npx --no-install campaigns-os …`
|
|
18
|
+
from that folder. Nothing is cloned, nothing goes on PATH, and the pin is
|
|
19
|
+
committed in `package.json`, so CI and the deploy host install the same package
|
|
20
|
+
bytes. `--no-install` is part of the command: `campaigns-os` is only the bin
|
|
21
|
+
name of `@nextcommerce/campaigns-os`, so in a folder without the pinned copy a
|
|
22
|
+
plain `npx campaigns-os` looks that name up on the registry and, with no
|
|
23
|
+
terminal to ask, installs what it finds. With the flag, npx fails instead.
|
|
24
|
+
|
|
25
|
+
Do the steps in this order:
|
|
26
|
+
|
|
27
|
+
1. **Orient.** Read [`AGENTS.md`](../AGENTS.md), `contracts/supported-surface.json`,
|
|
28
|
+
`contracts/release-ledger.json` and `CHANGELOG.md` on GitHub at one commit.
|
|
29
|
+
Follow its canonical reading order. That is a read of declarative data,
|
|
30
|
+
not a run of toolkit code. Keep the commit sha.
|
|
31
|
+
2. **Pin the toolkit, preflight, and install skills** at the exact reviewed release version.
|
|
32
|
+
3. **Start** at the exact reviewed release version.
|
|
33
|
+
|
|
34
|
+
New campaign folder:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
mkdir "<route>" && cd "<route>"
|
|
38
|
+
npm init -y && npm i --save-exact next-campaign-page-kit@0.3.0
|
|
39
|
+
npx campaign-init --non-interactive --template <family> --slug "<route>" --name "<campaign name>"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Existing page-kit campaign: `cd` into it (its `package.json` declares
|
|
43
|
+
`next-campaign-page-kit`). Then, in the campaign folder:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install --save-dev --save-exact @nextcommerce/campaigns-os@<version>
|
|
47
|
+
npx --no-install campaigns-os tooling status --platform claude
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`<version>` is the exact release you reviewed. The newest published release is
|
|
51
|
+
npm's `latest` (`npm view @nextcommerce/campaigns-os version`), and
|
|
52
|
+
`contracts/release-ledger.json` and `CHANGELOG.md` list every release. Verify
|
|
53
|
+
its tag/provenance against the source commit; do not use a floating dist-tag. Commit both `package.json` and `package-lock.json`. An unreleased
|
|
54
|
+
reviewed commit may instead be pinned with
|
|
55
|
+
`npm install --save-dev --save-exact "github:NextCommerceCo/campaigns-os#<full-sha>"`.
|
|
56
|
+
The package install runs its own lifecycle build; it is separate from the
|
|
57
|
+
checkout-only runtime preparation recipe and makes no claim under that recipe.
|
|
58
|
+
Diagnostics and corrected global invocation rendering require 1.35.0 or later;
|
|
59
|
+
`demo` requires 1.37.0 or later.
|
|
60
|
+
|
|
61
|
+
For an optional visual walkthrough,
|
|
62
|
+
`npx --no-install campaigns-os demo --target ./apollo-sample`
|
|
63
|
+
writes an offline sample. Open the printed local
|
|
64
|
+
`landing/index.html` file. This offline sample has no live commerce or campaign
|
|
65
|
+
proof. Preserve sample edits and create a separate new Page Kit folder for the
|
|
66
|
+
real campaign. See [offline sample preview](demo-preview.md).
|
|
67
|
+
|
|
68
|
+
Global use is also supported, with an exact release:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm install -g @nextcommerce/campaigns-os@<version>
|
|
72
|
+
campaigns-os tooling status --platform claude
|
|
73
|
+
campaigns-os install-skills --platform claude
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Global Git-source installation is not the supported path: npm's nested build
|
|
77
|
+
can inherit global mode. Global registry installations ship without Chromium;
|
|
78
|
+
run `campaigns-os qa install-browser` once. If optional Playwright was omitted,
|
|
79
|
+
ordinary commands still work and browser commands explain the missing package.
|
|
80
|
+
When another install shadows the global binary, status prints an explicit
|
|
81
|
+
invocation of the inspected copy. In a campaign folder,
|
|
82
|
+
`npx --no-install campaigns-os` selects the project-local dependency ahead of
|
|
83
|
+
the global binary on PATH.
|
|
84
|
+
|
|
85
|
+
`tooling status` is the preflight for "am I current?". It names the install
|
|
86
|
+
mode and checks package identity, CLI entrypoint, and installed Campaigns OS
|
|
87
|
+
skills. It also reports local gateway login metadata across saved store bindings,
|
|
88
|
+
without a `--store` flag or a remote validity check. No credential values are
|
|
89
|
+
shown; unavailable storage is reported without guessing that the user is logged
|
|
90
|
+
out. See [gateway login](gateway-login.md).
|
|
91
|
+
|
|
92
|
+
- From a campaign folder it reports `Install mode: package install
|
|
93
|
+
(node_modules), pinned at <version> @ <sha>`; git freshness is
|
|
94
|
+
`not_applicable` because the pinned commit is the freshness answer, and
|
|
95
|
+
there is no npm dist-tag to compare against. An npm release reports its version even when a source commit is not derivable.
|
|
96
|
+
Commands are spelled `npx --no-install campaigns-os <command>`; if a
|
|
97
|
+
different install of the toolkit is on PATH, it says so and points you back
|
|
98
|
+
to `npx`.
|
|
99
|
+
- From a git checkout it reports `Install mode: git checkout at <path>` plus
|
|
100
|
+
branch, upstream, ahead/behind, and whether the tree is dirty.
|
|
101
|
+
|
|
102
|
+
Exit code 2 from `tooling status` means attention is required — on a fresh
|
|
103
|
+
profile that is the not-yet-installed skills — and the output prints the exact
|
|
104
|
+
refresh command for the mode you ran it in. Pass the same `--platform` to
|
|
105
|
+
status that you install skills for; without it, status checks every agent
|
|
106
|
+
profile and stays at exit 2 until each is installed.
|
|
107
|
+
|
|
108
|
+
Everything the toolkit prints for you to run (`next`, gate remediations, the
|
|
109
|
+
browser-missing hints) is spelled for the install it came from: `npx
|
|
110
|
+
--no-install campaigns-os …` from a campaign folder (releases before 1.41.2
|
|
111
|
+
print it without `--no-install`; add the flag when you copy one), bare
|
|
112
|
+
`campaigns-os …` from a global installation whose binary matches PATH, and the
|
|
113
|
+
documented checkout translation below.
|
|
114
|
+
|
|
115
|
+
## Contributor / local checkout
|
|
116
|
+
|
|
117
|
+
To change the toolkit, run `npm run check`, or work against an unpushed commit,
|
|
118
|
+
use a checkout:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
git clone https://github.com/NextCommerceCo/campaigns-os.git
|
|
122
|
+
cd campaigns-os
|
|
123
|
+
npm install
|
|
124
|
+
npm run campaigns-os -- --help
|
|
125
|
+
npm run campaigns-os -- tooling status
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Every `npm run campaigns-os -- <command>` example in this repository is the
|
|
129
|
+
checkout form. From a campaign folder the same command is `npx --no-install
|
|
130
|
+
campaigns-os <command>`; the arguments are identical. `npm run
|
|
131
|
+
qa:install-browser`, a checkout script, is `npx --no-install campaigns-os qa
|
|
132
|
+
install-browser` from a campaign folder (`npx playwright install chromium` is
|
|
133
|
+
equivalent only when it resolves to the toolkit's own Playwright; prefer
|
|
134
|
+
`qa install-browser` on pins that have it). A fresh `git pull`
|
|
135
|
+
does not refresh copied agent skills in either mode; `tooling status` tells
|
|
136
|
+
you when they are stale.
|
|
137
|
+
|
|
138
|
+
## Install Skills
|
|
139
|
+
|
|
140
|
+
After installing or updating the CLI, refresh the Campaigns OS skills in Claude Code:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npx --no-install campaigns-os install-skills --platform claude
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
This syncs bundled `skills/*` directories from the installed package into `~/.claude/skills/<skill-name>/` (`--platform codex` writes `~/.codex/skills`), replacing same-name folders, and reports which skills were created, updated, or unchanged. It lists each `SKILL.md` it wrote under `Read now`: read those files now in the same session, because a running agent does not load skills installed after it started (a new session loads them on its own). Preview changes without writing files:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
npx --no-install campaigns-os install-skills --dry-run
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Use `--platform` for other local agent profiles:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
npx --no-install campaigns-os install-skills --platform codex
|
|
156
|
+
npx --no-install campaigns-os install-skills --platform agents
|
|
157
|
+
npx --no-install campaigns-os install-skills --platform all --dry-run
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
If `tooling status` reports stale skills, run the refresh command it prints —
|
|
161
|
+
it names each stale platform, through the same prefix — and read the
|
|
162
|
+
`SKILL.md` files it lists under `Read now` in the running session so the new
|
|
163
|
+
instructions are loaded (new sessions load them on their own). The build after `start` is
|
|
164
|
+
agent-driven — `next` names the skill for each stage — so install skills before
|
|
165
|
+
the first `start`.
|
|
166
|
+
|
|
167
|
+
From a local checkout, `./skills.sh status` previews every known local target
|
|
168
|
+
and `./skills.sh install codex` refreshes Codex skills. Use `--target <dir>` to
|
|
169
|
+
sync into another skills directory for testing or a managed profile.
|
|
170
|
+
|
|
171
|
+
## Inputs
|
|
172
|
+
|
|
173
|
+
You need:
|
|
174
|
+
|
|
175
|
+
- A Campaigns App campaign with shipping methods, payment methods, an API key, and Offers for its price tiers. The Offers bring in the campaign's products (packages in the API); see the setup note below.
|
|
176
|
+
- CampaignSpec JSON exported from a saved Map or authored by the coding agent through the [local-spec entry](build-packet.md#local-spec-entry), including `campaign.store_url` for page-kit `campaigns.json`. Local-spec identity requires a reviewed 1.43.0-or-later release.
|
|
177
|
+
- Prepared HTML/CSS/assets for the campaign pages.
|
|
178
|
+
- A target `next-campaign-page-kit` repo or local directory.
|
|
179
|
+
- A starter template family decision, usually `olympus` unless `demeter` or `shop-single-step` better matches the campaign shape.
|
|
180
|
+
|
|
181
|
+
If the checkout should have an exit-intent offer or typed promo-code box,
|
|
182
|
+
configure it in Campaign Map Builder before export, or declare it in the local
|
|
183
|
+
spec using verified campaign Offer values, so the checkout page carries
|
|
184
|
+
the mapped `exit_intent.offer_ref_id` / `exit_intent.offer_code` or
|
|
185
|
+
`promo_code_input.offer_ref_id` / `promo_code_input.offer_code`.
|
|
186
|
+
|
|
187
|
+
Campaigns API keys are public, browser-side, domain-allowlisted keys. If your CampaignSpec includes `campaign.campaigns_api_key`, `doctor` uses it directly and does not require a `CAMPAIGNS_API_KEY` shell env var.
|
|
188
|
+
|
|
189
|
+
The Store Profile is campaign metadata entered by the operator or derived from the store with `spec derive --from-store` (see "Create The Packet" below), not Campaigns API data. Before the target is scaffolded, `doctor` requires only `campaign.store_url`; `store_name`, `store_terms`, `store_privacy`, `store_contact`, `store_returns`, `store_shipping`, `store_phone`, and `store_phone_tel` are optional storefront/legal metadata used by templates when present. Once the target's `campaigns.json` entry exists, `page_kit.store_profile` checks every one of those fields the CampaignSpec provides against the target: a field the spec carries that the target lacks, or carries with a different value, blocks (the spec is the authority; run `npx --no-install campaigns-os page-kit sync --packet <campaign-runtime.build.json>` to write the spec's values into the target entry, or fix the spec, then re-run `doctor`), a field present only in the target warns as `target_only` — unless the value is starter demo residue (a placeholder storefront URL or phone number), which blocks as `demo_residue` whatever the spec says — and a field absent from both is clean. An optional field (any of the eight besides `store_url`) the spec sets to `""` says the merchant has none: `page-kit sync` blanks a recognised starter demo value (the placeholder storefront URLs and phone number; the starter's demo store name is not recognised and stays a `target_only` warning) and doctor reads the blank field as `intentionally_empty`, while a real, non-demo target value is left as it is (still a `target_only` warning, and sync reports the `""` as not applied; remove the value by hand if the merchant has none); an absent or null field still means "not provided". `campaign.store_url` stays required: `store_url: ""` still fails doctor's `spec.store_profile` check. So a spec that fills all nine fields makes all nine required after scaffold. A discrepancy the spec cannot yet resolve can be recorded with `campaigns-os checkpoint waive --packet <campaign-runtime.build.json> --gate page_kit.store_profile --reason "<why>" --waived-by "<named human>" --review-condition "<trigger>"` (or `--expires-at <ISO timestamp>` instead of the review condition; see [docs/build-packet.md](./build-packet.md), "Page Kit Store Profile checkpoint").
|
|
190
|
+
|
|
191
|
+
In the Campaigns App dashboard ([Campaigns App docs](https://docs.nextcommerce.com/docs/apps/campaigns-app), which name these screens and tabs), setup takes two steps: **Create Campaign** (name, currency, shipping, payments), then **Add Offer** on the campaign's **Offers** tab. Picking store products in the Offer's condition adds each selected variant to the campaign as one product at its store price when the Offer is saved, and later Offers reuse it. Pages reference those products by package ID, which the campaign's **Products** tab shows on each row. Offers set the customer's final price: Buy 1, Buy 2 and Buy 3 are separate Offers over the same products. Do not create separate `1x` / `2x` / `3x` products to express tier pricing, and do not rely on Retail Price/Quantity fields unless the campaign already uses that legacy setting (**Enable Package Retail Price & Quantity**). A campaign set up over the Admin API still creates its packages first and points Offers at their package IDs.
|
|
192
|
+
|
|
193
|
+
For synthetic or AI-generated evaluation campaigns, create or reuse a known test
|
|
194
|
+
store/API key before checkout work. A purely static source page can validate the
|
|
195
|
+
landing-page assembly path, but SDK checkout, package, shipping, payment, and
|
|
196
|
+
receipt surfaces need Campaigns App data; without it, checkout can remain in a
|
|
197
|
+
loading state. Record the test store/key choice in the Build Packet or
|
|
198
|
+
CampaignSpec so QA knows whether runtime checkout proof is expected or blocked.
|
|
199
|
+
|
|
200
|
+
For partial campaign work, map only the pages being built with
|
|
201
|
+
`source_html.pages[].path` and give intentionally untouched CampaignSpec pages a
|
|
202
|
+
clear `skip_reason`. Doctor will surface those pages under `derived.scope`,
|
|
203
|
+
label mapped routes as previewable, and keep checkout launch/test-order proof
|
|
204
|
+
blocked when runtime pages are out of scope.
|
|
205
|
+
|
|
206
|
+
`source_html.pages[].path` is the source/provenance path. Use
|
|
207
|
+
`source_html.pages[].page_kit` for the Page Kit target file, public route, CPK
|
|
208
|
+
`page_type`, and frontmatter projection. This matters for mixed sources where a
|
|
209
|
+
producer path such as `checkout/index.html` should still target
|
|
210
|
+
`src/<slug>/checkout.html` unless the CampaignSpec route requires a permalink.
|
|
211
|
+
|
|
212
|
+
## Prepare Raw HTML Source
|
|
213
|
+
|
|
214
|
+
**Standalone HTML mockups: keep them whole, set
|
|
215
|
+
`wrapper_policy: preserve_document_wrappers`.** Put it in the source-html
|
|
216
|
+
manifest at `<source>/.campaigns-os/source-html-manifest.json` (or pass
|
|
217
|
+
`--wrapper-policy preserve_document_wrappers` to `start` / `prepare-build`).
|
|
218
|
+
Source screenshot proof must be of the standalone document, so a page kept whole
|
|
219
|
+
needs no conversion, and doctor reports its document wrappers as a warning that
|
|
220
|
+
names the decision. For pages without a Figma `design_source`, no exporter is
|
|
221
|
+
required: for hand-written HTML, write the manifest yourself. When any active
|
|
222
|
+
page's `design_source` is Figma, the manifest must pass the Figma provenance
|
|
223
|
+
gate (`source_html.producer_provenance`), which needs the exporter's handoff
|
|
224
|
+
manifest. A minimal one (schema:
|
|
225
|
+
`schemas/source-html-manifest.v0.schema.json`):
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"schema_version": "source-html-manifest/v0",
|
|
230
|
+
"wrapper_policy": "preserve_document_wrappers",
|
|
231
|
+
"pages": [{ "page_id": "landing", "path": "landing.html" }]
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Decide this before capturing screenshots or computing `source_hash`; the rest
|
|
236
|
+
of this section is the conversion for source that is not kept whole.
|
|
237
|
+
|
|
238
|
+
Otherwise, `html_funnel` source files should be page-kit-ready source, not full browser
|
|
239
|
+
documents copied verbatim from an AI tool. This is not a wholesale Liquid
|
|
240
|
+
rewrite. Use Liquid only for page-kit helpers such as `campaign_link`,
|
|
241
|
+
`campaign_asset`, and `campaign_include`.
|
|
242
|
+
|
|
243
|
+
The conversion process used in prior builds is:
|
|
244
|
+
|
|
245
|
+
- Strip document-level wrappers: `<!doctype>`, `<html>`, `<head>`, and `<body>`.
|
|
246
|
+
- Add page frontmatter for title, layout, route/meta values, and any source mapping notes.
|
|
247
|
+
- Move shared CSS into the campaign asset tree or an include when it is reused; inline page-specific CSS only when the target page-kit style allows it.
|
|
248
|
+
- Move local images/fonts/assets into the campaign asset tree and root paths with `campaign_asset` when needed. Page Kit publishes `src/<slug>/assets/config.js` as `/<slug>/config.js` and `src/<slug>/assets/products/foo.png` as `/<slug>/products/foo.png`; raw `/assets/...` paths from AI/exported HTML are source paths, not built campaign URLs.
|
|
249
|
+
- Check `context.source.asset_crawl` in `.campaign-runtime/build-context.json` before rewriting assets. `prepare-build` crawls source HTML plus referenced local CSS, records raw refs, resolved source files, missing/out-of-root assets, and `pagekit_asset_path` hints so assembly does not have to rediscover `/assets/...` path behavior page by page.
|
|
250
|
+
- Replace internal links/CTAs with CampaignSpec routes, usually via `campaign_link`.
|
|
251
|
+
- Keep source landing/presell composition and copy intact when it is a real design.
|
|
252
|
+
- Use starter-template SDK contracts for checkout, upsell, downsell, receipt, payment, totals, and submit controls.
|
|
253
|
+
- Run page-kit build and inspect `_site/<slug>/` before handing off to polish.
|
|
254
|
+
|
|
255
|
+
Doctor checks this preparation deterministically: unstripped document wrappers
|
|
256
|
+
and leftover/broken frontmatter block with `source_html.prep.*` error codes,
|
|
257
|
+
and source-file internal links warn. See the "Source preparation check" section
|
|
258
|
+
in [docs/source-adapters.md](./source-adapters.md) for each code and its fix.
|
|
259
|
+
If the source is meant to stay a full browser document (raw HTML handed over
|
|
260
|
+
as-is, or pages whose `source_screenshot` proof must be of the standalone
|
|
261
|
+
document), do not strip it: record the decision instead with
|
|
262
|
+
`--wrapper-policy preserve_document_wrappers` on `start` / `prepare-build`, or
|
|
263
|
+
`wrapper_policy` in the source-html manifest, and the wrapper finding becomes a
|
|
264
|
+
warning that names the decision. Choose before capturing screenshots or
|
|
265
|
+
computing `source_hash`; the "Selecting the wrapper policy at intake" section of
|
|
266
|
+
[docs/source-adapters.md](./source-adapters.md) gives the order of operations.
|
|
267
|
+
|
|
268
|
+
## Create The Packet
|
|
269
|
+
|
|
270
|
+
> **Heads up — `start` turns on run telemetry, and remit is ON by default.**
|
|
271
|
+
> `start` (and `prepare-build`) opens an ambient Run Session in the target repo.
|
|
272
|
+
> When that session's Run Record is assembled — by a ready `qa run`, by
|
|
273
|
+
> `campaigns-os run end`, or by the automatic closeout of a stale session — it is
|
|
274
|
+
> **remitted to the canonical Next Commerce endpoint,
|
|
275
|
+
> `https://campaign-map.nextcommerce.com`**, without asking again.
|
|
276
|
+
>
|
|
277
|
+
> What is sent: the Run Record (the run's own system signal and workflow
|
|
278
|
+
> findings — stage timings, findings, tool/environment shape), plus the packet's
|
|
279
|
+
> Campaigns API key in the `X-Campaign-Key` header so the record lands in your
|
|
280
|
+
> tenant scope and you can read it back with `campaigns-os telemetry list
|
|
281
|
+
> --packet <json>`. Campaigns API keys are public, browser-side,
|
|
282
|
+
> domain-allowlisted keys by design, so this is attribution, not a secret.
|
|
283
|
+
>
|
|
284
|
+
> Why: those run records are what the toolkit learns from — they are how
|
|
285
|
+
> templates, gates, and guidance get fixed.
|
|
286
|
+
>
|
|
287
|
+
> Three ways out, any of which is enough:
|
|
288
|
+
>
|
|
289
|
+
> - `npx --no-install campaigns-os telemetry off` — machine-level, sticks.
|
|
290
|
+
> - `CAMPAIGNS_OS_TELEMETRY=off` — per shell or per CI job.
|
|
291
|
+
> - `--no-remit` on the remitting command (`qa run`, `run-record`, `run end`).
|
|
292
|
+
>
|
|
293
|
+
> Consent gates remit only. Capture is always local, so opting out costs you
|
|
294
|
+
> nothing locally: sessions, journals and Run Records are still written and
|
|
295
|
+
> the Run Record's `remit_state` reads `skipped`. Turning consent off at the
|
|
296
|
+
> machine or environment level (`telemetry off`, `CAMPAIGNS_OS_TELEMETRY=off`)
|
|
297
|
+
> also makes saved-Map `qa run` default to a local-only verdict unless the campaign is
|
|
298
|
+
> portal-managed or `--post-verdict` is passed; `--no-remit` does not — it
|
|
299
|
+
> skips this command's remit only, and the verdict still publishes. Whatever
|
|
300
|
+
> the consent, a run whose base URL is a local address (`localhost` or any
|
|
301
|
+
> `*.localhost` name, any `127.x.x.x`, `0.0.0.0`, `[::1]` or an IPv4-mapped
|
|
302
|
+
> loopback) keeps its verdict local by default; pass
|
|
303
|
+
> `--post-verdict`, or run `qa publish` afterwards, to send it. To keep
|
|
304
|
+
> one saved-Map verdict local, pass `--no-post-verdict` (or `--local-only`) to `qa run`.
|
|
305
|
+
> A `local_spec_id` packet always keeps its QA verdict and progress local,
|
|
306
|
+
> including with `--post-verdict`; `qa publish` refuses it. Run Record remit
|
|
307
|
+
> still follows the consent controls above.
|
|
308
|
+
> `--no-run-session` on `start` skips opening the session altogether. Full
|
|
309
|
+
> contract: [Run Telemetry](./workflow-findings-sidecar.md).
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
mkdir -p source
|
|
313
|
+
npx --no-install campaigns-os start --map-id <map-id> --target . --source ./source --template-family <family>
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
`--map-id <id>` starts from a map saved in Campaign Map Builder (add
|
|
317
|
+
`--proxy-base <origin>` when the map was saved on a non-production map store);
|
|
318
|
+
`--spec <campaignspec.json>` starts from a local saved-Map export or an
|
|
319
|
+
agent-authored local CampaignSpec instead. One of the two is required.
|
|
320
|
+
`--target .` is the campaign folder, the
|
|
321
|
+
page-kit project that pins the toolkit; with `--spec` it must already be a
|
|
322
|
+
directory (`Target repo is not a directory` otherwise). `--source` is always
|
|
323
|
+
required: the folder of prepared HTML/CSS/assets for the pages you are
|
|
324
|
+
building, with a source manifest carrying desktop and mobile screenshot proof
|
|
325
|
+
for each designed page — see [Design Source Package](./design-source-package.md).
|
|
326
|
+
It must exist even when every page is template stock; those pages are declared
|
|
327
|
+
out of source scope (manifest `skip_reason`, or CampaignSpec
|
|
328
|
+
`build_scope.mode: "partial"`), never left out of `--source`.
|
|
329
|
+
|
|
330
|
+
`start` creates the packet, context, report, doctor output, and target-repo agent context. It does not edit campaign pages, deploy, run QA, or place test orders.
|
|
331
|
+
|
|
332
|
+
`start` finishes by running doctor, and on a fresh target doctor's first verdict
|
|
333
|
+
is normally `BLOCKED` with a list of what to supply — missing screenshot proof,
|
|
334
|
+
demo values to replace, a scaffold to run. That list is the intake checklist,
|
|
335
|
+
not a failed install; work through it and re-run. The demo values are the
|
|
336
|
+
store profile and SDK pin `campaign-init` seeded into `_data/campaigns.json`
|
|
337
|
+
after a fresh scaffold; doctor's required actions print the command that
|
|
338
|
+
replaces them from the CampaignSpec — `npx --no-install campaigns-os page-kit
|
|
339
|
+
sync --packet campaign-runtime.build.json` (`--dry-run` to see the diff first)
|
|
340
|
+
— and after it `page_kit.store_profile` and `page_kit.sdk_version` pass without a waiver.
|
|
341
|
+
|
|
342
|
+
The other direction exists too. Once the campaign is configured, the repo is
|
|
343
|
+
the authority for the SDK pin, the page routes and the analytics ids, and
|
|
344
|
+
`npx --no-install campaigns-os spec derive --packet campaign-runtime.build.json` writes
|
|
345
|
+
those into the local CampaignSpec with a field-by-field diff (`--dry-run`
|
|
346
|
+
first). After a bump in `_data/campaigns.json`, that is the one command that
|
|
347
|
+
brings the spec back in line; doctor's `page_kit.sdk_version.repo_newer`
|
|
348
|
+
warning names it. Add `--write-map` and the pin is also recorded in the saved
|
|
349
|
+
Map's Build hints field, so the Map and its next export stop reading stale.
|
|
350
|
+
The store profile (`campaign.store_*`) comes from the store
|
|
351
|
+
itself: the 1.38.0 candidate uses gateway login for `--from-store <subdomain>`
|
|
352
|
+
within the admitted owned-store private pilot. Existing direct callers must add
|
|
353
|
+
`--store-token-source env:<VAR>` explicitly; there is no implicit environment
|
|
354
|
+
fallback. See [gateway login and migration](gateway-login.md). A successful read
|
|
355
|
+
writes the available store name,
|
|
356
|
+
primary domain, phone and policy-page URLs into the spec too, for `page-kit
|
|
357
|
+
sync` to carry into the repo.
|
|
358
|
+
|
|
359
|
+
It also runs brand-theme discovery in inspect-only mode. When source tokens are
|
|
360
|
+
available, the build context records `context.theme` and the target repo gets
|
|
361
|
+
`.campaign-runtime/theme/theme-report.json`. It does not write
|
|
362
|
+
`brand-theme.css` by default.
|
|
363
|
+
|
|
364
|
+
To inspect or generate the optional commerce-page brand bridge:
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
npx --no-install campaigns-os theme inspect --packet ./campaign-runtime.build.json --json
|
|
368
|
+
npx --no-install campaigns-os theme generate --packet ./campaign-runtime.build.json --json
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Use `--theme-policy auto` on `start` / `prepare-build` only when you want
|
|
372
|
+
Campaigns OS to write `brand-theme.css` automatically from high-confidence
|
|
373
|
+
source tokens. Generated CSS is root-variable-only and must be loaded after
|
|
374
|
+
`next-core.css` on checkout, upsell, downsell, and receipt pages.
|
|
375
|
+
|
|
376
|
+
## Continue In Your AI Tool
|
|
377
|
+
|
|
378
|
+
Run `next` after `start` and after every stage, and do what it prints — the
|
|
379
|
+
commands are already spelled for this install:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
npx --no-install campaigns-os next --packet ./campaign-runtime.build.json --json
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
When it names the setup stage, that is:
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
npx --no-install campaigns-os next setup --packet ./campaign-runtime.build.json
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
If doctor says setup is not required, run:
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
npx --no-install campaigns-os next build --packet ./campaign-runtime.build.json
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Paste the generated handoff into your AI tool. From here the build is
|
|
398
|
+
agent-driven: each `next` names the Campaigns OS skill for the stage
|
|
399
|
+
(`next-campaigns-os-setup`, `next-campaigns-build`, `next-campaigns-polish`,
|
|
400
|
+
`next-campaigns-qa`) and the exact commands, which is why the skills are
|
|
401
|
+
installed before the first `start`.
|
|
402
|
+
|
|
403
|
+
## Gates
|
|
404
|
+
|
|
405
|
+
Build is not launch readiness. A complete run still needs:
|
|
406
|
+
|
|
407
|
+
- page-kit build
|
|
408
|
+
- starter-template/SDK lint from the target repo, for example `npm run lint:sdk`,
|
|
409
|
+
`npm run lint:sdk:promoted`, or `npm run lint:sdk:ci` when those scripts are
|
|
410
|
+
available. There is no separate `campaign-lint` package in the current flow.
|
|
411
|
+
- Campaigns OS Playwright browser install
|
|
412
|
+
- formal polish pass against the served current build
|
|
413
|
+
- mandatory package-owned `polish capture` evidence before Polish becomes
|
|
414
|
+
terminal or deploy/QA begins
|
|
415
|
+
- preview deploy, or a local serve of the built `_site/` output with the packet's
|
|
416
|
+
`deploy.target` set to `local-serve` (`qa policy set --deploy-target local-serve
|
|
417
|
+
--preview-url http://localhost:<port>/<slug>/`); localhost on any port is a
|
|
418
|
+
Development domain, so no SDK origin allowlist entry is needed there
|
|
419
|
+
- Node/npm QA with a saved Map ID or local-spec Build Packet, and the tested URL
|
|
420
|
+
- typed-card test-order proof via `--test-order common` (global test cards bypass the gateway and move no money, but each leaves a real store order record; unless the operator has already said test orders are fine for this campaign, ask once, up front, before placing test orders, then proceed; there is no permission flag)
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
npx --no-install campaigns-os qa install-browser
|
|
424
|
+
npx --no-install campaigns-os polish capture --packet ./campaign-runtime.build.json --base-url <served-current-build-url>
|
|
425
|
+
npx --no-install campaigns-os qa resolve --packet ./campaign-runtime.build.json
|
|
426
|
+
npx --no-install campaigns-os qa run --packet ./campaign-runtime.build.json --base-url https://preview.example.com/campaign/ --browser --test-order common
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
`npx --no-install campaigns-os qa install-browser` (`npm run qa:install-browser` from a checkout; `npx playwright install chromium` only when it resolves to the toolkit's Playwright)
|
|
430
|
+
is a one-time local setup step after install/update. It installs the Chromium
|
|
431
|
+
binary used by package-owned polish capture and QA.
|
|
432
|
+
Run it before `polish capture`, `--browser`, or `--test-order`; the CLI will tell
|
|
433
|
+
you to run it if the browser binary is missing. The capture producer must point
|
|
434
|
+
at the served current build and complete before Polish hands off to deploy/QA.
|
|
435
|
+
|
|
436
|
+
After the browser is installed, `npm run smoke:polish-capture` is an optional
|
|
437
|
+
real-Chromium package smoke. It needs permission to open a loopback HTTP
|
|
438
|
+
listener and is deliberately excluded from `npm run check` and CI.
|
package/docs/readback.md
CHANGED
|
@@ -8,7 +8,9 @@ most once, plus two fixed Git metadata files (the nearest `.git` entry at the
|
|
|
8
8
|
target or an ancestor, and that Git directory's `logs/HEAD` reflog) for the
|
|
9
9
|
freshness comparison, and the CampaignSpec and normalized Campaign Build Brief
|
|
10
10
|
the Build Packet names, whose current content decides each stage's effective
|
|
11
|
-
status.
|
|
11
|
+
status. It also reads and hashes regular files in the packet's built output
|
|
12
|
+
at `_site/<public_route_slug>/` to assess build-bound evidence debt. Symlinks
|
|
13
|
+
are skipped by the build fingerprint machinery.
|
|
12
14
|
|
|
13
15
|
The safety contract is the point of the command. The readback **writes nothing**
|
|
14
16
|
under the target — not even a lifecycle journal entry, which every other command
|
|
@@ -21,7 +23,7 @@ For the same reason the readback resolves **no run session** and sweeps no stale
|
|
|
21
23
|
one. Its `--packet` names the Build Packet to *project*, not a Build Packet to
|
|
22
24
|
act on, so an active run session — here, at the target, or bound to some other
|
|
23
25
|
packet entirely — never changes what this command reads or what it exits with.
|
|
24
|
-
|
|
26
|
+
The only reader of a `--packet` file is the
|
|
25
27
|
readback's own 32 MiB-bounded one, which refuses an oversized packet as an
|
|
26
28
|
`unreadable` artifact row rather than loading it.
|
|
27
29
|
|
|
@@ -41,8 +43,8 @@ campaigns-os readback --example [--json]
|
|
|
41
43
|
The default output is the rendered human view. `--json` emits the same
|
|
42
44
|
projection as one `campaigns-os-readback/v2` object on stdout so a caller can
|
|
43
45
|
gate on it; `schemas/campaigns-os-readback.v2.schema.json` is that object's
|
|
44
|
-
shape,
|
|
45
|
-
path: the JSON serializes what the text view already computes and adds no
|
|
46
|
+
shape, including the additive `evidence_debt` field. The two modes share one
|
|
47
|
+
computation path: the JSON serializes what the text view already computes and adds no
|
|
46
48
|
interpretation the text view does not also carry.
|
|
47
49
|
|
|
48
50
|
## Exit codes
|
|
@@ -118,6 +120,15 @@ STAGES [assembly report; report status: prepared; effective: prepared]
|
|
|
118
120
|
deploy pending
|
|
119
121
|
qa pending
|
|
120
122
|
|
|
123
|
+
EVIDENCE DEBT [the readback's own projection layer: recorded build bindings versus built output]
|
|
124
|
+
polish: unknown — evidence currency is unknown (build_output_missing); absence of proof is not no debt
|
|
125
|
+
recorded: unknown; current: unknown
|
|
126
|
+
qa: unknown — evidence currency is unknown (build_output_missing); absence of proof is not no debt
|
|
127
|
+
recorded: unknown; current: unknown
|
|
128
|
+
changes against assembly manifest: unknown (build_output_missing)
|
|
129
|
+
recorded: unknown; current: unknown
|
|
130
|
+
Page changes use the assembly baseline; older stage manifests are not reconstructed.
|
|
131
|
+
|
|
121
132
|
BUILD CONTEXT [build context; source adapter: html_funnel, status: prepared]
|
|
122
133
|
|
|
123
134
|
DOCTOR [doctor output; status: ready_with_warnings]
|
|
@@ -182,6 +193,23 @@ STAGES [assembly report; report status: completed; effective: prepared]
|
|
|
182
193
|
},
|
|
183
194
|
"skip_cascades": [{ "blocked_by": "polish.evidence_incomplete", "families": ["meta-tags"] }],
|
|
184
195
|
"divergences": [{ "stage": "polish", "assertion_ids": ["polish.evidence_incomplete"] }],
|
|
196
|
+
"evidence_debt": {
|
|
197
|
+
"polish": {
|
|
198
|
+
"status": "unknown", "matches_current_build": null,
|
|
199
|
+
"recorded_build_fingerprint": null, "current_build_fingerprint": null,
|
|
200
|
+
"reason": "build_output_missing"
|
|
201
|
+
},
|
|
202
|
+
"qa": {
|
|
203
|
+
"status": "unknown", "matches_current_build": null,
|
|
204
|
+
"recorded_build_fingerprint": null, "current_build_fingerprint": null,
|
|
205
|
+
"reason": "build_output_missing"
|
|
206
|
+
},
|
|
207
|
+
"changes": {
|
|
208
|
+
"status": "unknown", "reason": "build_output_missing",
|
|
209
|
+
"recorded_build_fingerprint": null, "current_build_fingerprint": null,
|
|
210
|
+
"changed_pages": [], "unmapped_files": [], "extra": [], "missing": [], "changed": []
|
|
211
|
+
}
|
|
212
|
+
},
|
|
185
213
|
"clean": false
|
|
186
214
|
}
|
|
187
215
|
```
|
|
@@ -397,6 +425,86 @@ in first-seen order:
|
|
|
397
425
|
Empty when no QA verdict loaded or none of its assertions were skipped. This
|
|
398
426
|
grouping is the readback's own projection layer.
|
|
399
427
|
|
|
428
|
+
### `evidence_debt`
|
|
429
|
+
|
|
430
|
+
An additive top-level object in `campaigns-os-readback/v2`, emitted in JSON and
|
|
431
|
+
as an **EVIDENCE DEBT** text section. This assessment reads built bytes; it uses
|
|
432
|
+
no timestamps or reflog entries, starts no process, and writes nothing. It
|
|
433
|
+
does not rerun doctor, Polish, or QA and does not reinterpret their verdicts.
|
|
434
|
+
|
|
435
|
+
`polish` and `qa` each carry:
|
|
436
|
+
|
|
437
|
+
- `status`: the closed set `current`, `owed`, `unknown`;
|
|
438
|
+
- `matches_current_build`: fingerprint equality only (`true` or `false`), or `null`
|
|
439
|
+
when either fingerprint is absent;
|
|
440
|
+
- `recorded_build_fingerprint`: the stage's build binding, or `null`;
|
|
441
|
+
- `current_build_fingerprint`: the fingerprint computed from current built
|
|
442
|
+
output with `computeBuildFingerprint`, or `null`;
|
|
443
|
+
- `reason`: `null` for `current`; `build_fingerprint_changed`,
|
|
444
|
+
`stage_required_by_build`, `stage_required_by_brief`,
|
|
445
|
+
`stage_required_by_spec`, or `stage_required` for `owed`; or an explicit
|
|
446
|
+
reason for `unknown`.
|
|
447
|
+
|
|
448
|
+
Polish uses `stages.polish.source_build_fingerprint`, falling back to
|
|
449
|
+
`stages.polish.evidence.source_build_fingerprint`, with the same precedence as
|
|
450
|
+
the Polish gate. QA uses `stages.qa.evidence.qc_build_fingerprint`, falling back
|
|
451
|
+
to `source_build_fingerprint`, and the same build-binding comparison as `next`.
|
|
452
|
+
It does not require uncommitted capture files to be present.
|
|
453
|
+
|
|
454
|
+
`current` means the stage's binding equals the bytes on disk and the assembly
|
|
455
|
+
fingerprint is available and the stage is not recorded as `required`. `owed`
|
|
456
|
+
means that binding differs from the current build, or the lifecycle recorded
|
|
457
|
+
the stage as `required`, even without a stage fingerprint. Its `required_by`
|
|
458
|
+
selects `stage_required_by_build`, `stage_required_by_brief`, or
|
|
459
|
+
`stage_required_by_spec`; an absent or unrecognized source gives
|
|
460
|
+
`stage_required`. Both comparisons work without a manifest, including reports recorded
|
|
461
|
+
before manifests existed. **`unknown` never means no debt.** Missing or unreadable
|
|
462
|
+
built output or a missing assembly fingerprint still makes the affected stage
|
|
463
|
+
explicitly `unknown`; a missing stage fingerprint does too unless the stage is
|
|
464
|
+
`required`. The unknown stage reason codes are
|
|
465
|
+
`packet_unavailable`, `build_output_location_unknown`, `build_output_missing`,
|
|
466
|
+
`build_output_unreadable`, `assembly_build_fingerprint_missing`, and
|
|
467
|
+
`stage_build_fingerprint_missing`.
|
|
468
|
+
When both stage and disk fingerprints are available, `matches_current_build`
|
|
469
|
+
reports their equality even if a missing assembly fingerprint makes status unknown.
|
|
470
|
+
`matches_current_build: true` together with `status: owed` means the lifecycle
|
|
471
|
+
requires the stage again over matching evidence. Consumers decide whether
|
|
472
|
+
evidence is owed from `status`, never from `matches_current_build`.
|
|
473
|
+
|
|
474
|
+
`changes` compares current output to **the retained assembly manifest**, using
|
|
475
|
+
the existing file drift machinery. It carries:
|
|
476
|
+
|
|
477
|
+
- `status`: `known` or `unknown`, with `reason` (`null` when known);
|
|
478
|
+
- `recorded_build_fingerprint` and `current_build_fingerprint`: the assembly
|
|
479
|
+
manifest baseline and the disk fingerprint;
|
|
480
|
+
- `extra`, `missing`, `changed`: complete file path lists from the manifest
|
|
481
|
+
comparison, without the human drift summary's truncation;
|
|
482
|
+
- `changed_pages`: `{ "page_id": "landing", "built_path": "landing/index.html" }`
|
|
483
|
+
entries for added, removed, or modified HTML in the built-site page map;
|
|
484
|
+
- `unmapped_files`: every differing path outside that map, including shared
|
|
485
|
+
CSS, scripts and assets. These changes still make build-bound evidence owed.
|
|
486
|
+
|
|
487
|
+
Paths are relative to `_site/<public_route_slug>/` and sorted; page IDs use
|
|
488
|
+
`resolveBuiltSiteScope`'s route-derived identities (nested routes use hyphens).
|
|
489
|
+
Removed HTML uses that same route/ID projection. Empty lists with
|
|
490
|
+
`changes.status: unknown` do not establish that no pages changed.
|
|
491
|
+
A missing or unusable manifest makes only `changes.status` unknown, with
|
|
492
|
+
`build_manifest_unavailable` as its reason. A manifest is usable only if its
|
|
493
|
+
entries hash to the assembly's recorded fingerprint.
|
|
494
|
+
If the built page map cannot be resolved (for example, output contains assets
|
|
495
|
+
but no HTML), page changes are unknown with `build_output_pages_unresolved`;
|
|
496
|
+
stage bindings still follow the fingerprint comparison.
|
|
497
|
+
|
|
498
|
+
The assembly baseline is explicit because it need not equal an older stage's
|
|
499
|
+
binding. If a later `record build` replaced that manifest, readback can still
|
|
500
|
+
report older Polish/QA bindings as owed, but cannot reconstruct which pages
|
|
501
|
+
changed from those older builds. An empty **known** list then means only that
|
|
502
|
+
no files differ from the retained assembly manifest. If recording removed a
|
|
503
|
+
stage binding entirely, that stage reads unknown unless its recorded status is
|
|
504
|
+
`required`, which reports owed. `current` is a build-binding
|
|
505
|
+
assessment only; input currency, stage completion, waivers and verdicts remain
|
|
506
|
+
separate.
|
|
507
|
+
|
|
400
508
|
### `divergences`
|
|
401
509
|
|
|
402
510
|
Where two artifacts record different states for the same stage: the assembly
|
|
@@ -413,7 +521,7 @@ failure lines up with a completed stage. This too is the readback's own layer.
|
|
|
413
521
|
|
|
414
522
|
## `clean`
|
|
415
523
|
|
|
416
|
-
`clean` is true if and only if **all
|
|
524
|
+
`clean` is true if and only if **all six** of the following hold:
|
|
417
525
|
|
|
418
526
|
1. **Every artifact the readback found is loaded and recognized.** Formally: no
|
|
419
527
|
artifact is in state `unreadable` or `unrecognized`. An `absent` artifact
|
|
@@ -439,6 +547,11 @@ failure lines up with a completed stage. This too is the readback's own layer.
|
|
|
439
547
|
failed to check.
|
|
440
548
|
4. **`divergences` is empty.**
|
|
441
549
|
5. **`doctor.error_count` is zero.**
|
|
550
|
+
6. **Neither `evidence_debt.polish.status` nor `evidence_debt.qa.status` is
|
|
551
|
+
`owed`.** Built output that moved since recorded evidence cannot read clean.
|
|
552
|
+
An `unknown` status does not independently make this integrity flag false;
|
|
553
|
+
it remains unknown, never a claim that no evidence is owed. Callers must
|
|
554
|
+
inspect `evidence_debt` even when `clean` is true.
|
|
442
555
|
|
|
443
556
|
Doctor warnings — of either group — do not affect `clean`. Neither does a
|
|
444
557
|
blocked QA verdict, a blocked assembly report, or a non-empty `skip_cascades`.
|
|
@@ -449,7 +562,7 @@ blocked QA verdict, a blocked assembly report, or a non-empty `skip_cascades`.
|
|
|
449
562
|
campaign. It means: the readback read every artifact that was there, understood
|
|
450
563
|
all of them, can show — for every artifact that recorded an age — that none of
|
|
451
564
|
them is older than the checkout, found no contradiction between them, and saw no
|
|
452
|
-
doctor error. Campaigns OS remains the
|
|
565
|
+
doctor error, and found no known build-bound evidence debt. Campaigns OS remains the
|
|
453
566
|
lifecycle and verdict authority; the readback never reinterprets a verdict.
|
|
454
567
|
|
|
455
568
|
The practical consequence for a caller: `clean: true` says the artifacts are
|
|
@@ -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.61.0`.
|
|
12
12
|
|
|
13
13
|
## What this is
|
|
14
14
|
|