@nextcommerce/campaigns-os 1.43.1 → 1.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -2
- package/CHANGELOG.md +1099 -5103
- package/README.md +34 -13
- package/agents/claude/CLAUDE.md +1 -1
- package/agents/codex/AGENTS.md +1 -1
- package/agents/copilot/copilot-instructions.md +1 -1
- package/agents/cursor/campaigns-os.mdc +1 -1
- package/campaign-spec/dist/rules/analytics-contract-shape.d.ts +2 -2
- package/campaign-spec/dist/rules/analytics-contract-shape.js +2 -2
- package/campaign-spec/dist/rules/store-profile-shape.d.ts +5 -1
- package/campaign-spec/dist/rules/store-profile-shape.js +8 -10
- package/campaign-spec/dist/types.d.ts +2 -2
- package/contracts/agent-relevant-change-policy.v1.json +5 -0
- package/contracts/archive/CHANGELOG.2026-09-30.md +5111 -0
- package/contracts/archive/release-ledger.2026-09-30.json +5068 -0
- package/contracts/effects.v1.json +1184 -121
- package/contracts/orientation-reason-codes.v1.json +7 -0
- package/contracts/release-ledger.json +2190 -5260
- package/contracts/supported-surface.json +7 -4
- package/contracts/template-slot-manifest.shared-content-core.v0.json +403 -0
- package/docs/brand-theme-bridge.md +81 -0
- package/docs/build-packet.md +222 -23
- package/docs/campaigns-os-build-flow.md +4 -3
- package/docs/design-source-package.md +162 -15
- package/docs/effects.md +66 -12
- package/docs/gateway-login.md +3 -0
- package/docs/local-setup.md +1 -1
- package/docs/orientation-contract-reference.md +42 -2
- package/docs/polish-evidence.md +74 -0
- package/docs/progress-snapshots.md +10 -6
- package/docs/qa-and-test-orders.md +230 -20
- package/docs/release-ledger-authoring-guide.md +70 -8
- package/docs/runtime-readiness.md +1 -1
- package/docs/sdk-storage-compatibility.md +1 -1
- package/docs/skills-revision.md +10 -10
- package/docs/supported-surface.md +2 -2
- package/docs/versioning.md +4 -1
- package/package.json +1 -1
- package/schemas/campaigns-os-release-ledger.v1.schema.json +32 -2
- package/schemas/campaigns-os-tooling-orientation.v1.schema.json +1 -0
- package/skills/campaign-lifecycle-orientation/SKILL.md +16 -5
- package/skills/campaign-readback-classification/SKILL.md +3 -3
- package/skills/campaign-run-evidence/SKILL.md +7 -6
- package/skills/contribution-intake/SKILL.md +3 -3
- package/skills/next-campaigns-build/SKILL.md +7 -6
- package/skills/next-campaigns-os/SKILL.md +7 -7
- package/skills/next-campaigns-os/references/session-intake.md +9 -3
- package/skills/next-campaigns-os-setup/SKILL.md +5 -5
- package/skills/next-campaigns-polish/SKILL.md +28 -9
- package/skills/next-campaigns-qa/SKILL.md +7 -4
- package/skills.json +10 -10
- package/src/brand-theme.mjs +320 -20
- package/src/build-brief.mjs +6 -4
- package/src/built-script-syntax.mjs +480 -0
- package/src/built-site-scope.mjs +16 -4
- package/src/campaigns-api-key.mjs +99 -0
- package/src/cli-helpers.mjs +118 -0
- package/src/cli.mjs +1530 -7580
- package/src/commercial-parity.mjs +48 -2
- package/src/design-source-package.mjs +1 -1
- package/src/design-source-publication.mjs +898 -0
- package/src/deviation.mjs +13 -1
- package/src/diagnostic.mjs +6 -2
- package/src/directory-lock.mjs +270 -0
- package/src/doctor/checks.mjs +4654 -0
- package/src/doctor/inspect.mjs +678 -0
- package/src/doctor/next-step.mjs +731 -0
- package/src/doctor/source-provenance.mjs +184 -0
- package/src/install-invocation.mjs +29 -0
- package/src/invocation.mjs +183 -0
- package/src/live-campaign-refs.mjs +466 -0
- package/src/login.mjs +2 -2
- package/src/page-kit-store-profile.mjs +69 -12
- package/src/page-kit-sync.mjs +31 -12
- package/src/private-template-source.mjs +1 -1
- package/src/progress-node.mjs +9 -36
- package/src/proof-policy.mjs +1 -1
- package/src/qa-analytics-correctness.mjs +3 -0
- package/src/qa-binding-evidence.mjs +76 -11
- package/src/qa-browser.mjs +1316 -105
- package/src/qa-build-scope.mjs +47 -0
- package/src/qa-commercial-parity.mjs +48 -5
- package/src/qa-node.mjs +339 -19
- package/src/qa-test-order-topology.mjs +148 -0
- package/src/sdk-markup.mjs +72 -8
- package/src/source-html-intake.mjs +117 -1
- package/src/source-html-manifest.mjs +9 -2
- package/src/stage-ledger.mjs +28 -0
- package/src/stage-record.mjs +551 -0
- package/src/target-lock.mjs +54 -0
- package/src/template-brand-contract.mjs +17 -1
- package/src/upsell-selector-scope.mjs +112 -2
package/docs/effects.md
CHANGED
|
@@ -38,8 +38,10 @@ that already understands those hints needs no translation layer.
|
|
|
38
38
|
**`readOnlyHint` counts the command-lifecycle journal.** A journal append is a
|
|
39
39
|
write like any other, so every `readOnlyHint: true` row is an invocation the
|
|
40
40
|
CLI exempts from lifecycle capture (the converse does not hold: `demo` and the
|
|
41
|
-
`--no-write` forms skip the journal but still write other declared files
|
|
42
|
-
`
|
|
41
|
+
`--no-write` forms skip the journal but still write other declared files, and
|
|
42
|
+
`doctor` inspection and `doctor --no-write` skip it and write nothing but may
|
|
43
|
+
send the one live campaign read): `help`, `readback`,
|
|
44
|
+
`run status`, `doctor --no-live-refs` inspection, `sdk storage-check`,
|
|
43
45
|
`tooling diagnose`, a refused invocation, `run-record --no-write`, and every
|
|
44
46
|
`--dry-run` form on the commands that implement the flag. Everything else
|
|
45
47
|
appends an entry when a journal is selected — an active run session,
|
|
@@ -81,6 +83,46 @@ writes your **home** directory, not the campaign. `telemetry on` writes your
|
|
|
81
83
|
**machine** config. `run-record` writes beside the **working directory**, not the
|
|
82
84
|
target repo. A row that said "writes the target" would be wrong about all three.
|
|
83
85
|
|
|
86
|
+
### The Map Builder fetch
|
|
87
|
+
|
|
88
|
+
`start`, `prepare-build` and `build` take their CampaignSpec from `--spec` or
|
|
89
|
+
from `--map-id`. With `--map-id` (and no `--cached-spec`) the invocation sends
|
|
90
|
+
`{proxy-base}/api/spec/{map-id}` and writes what comes back to
|
|
91
|
+
`{target}/.campaign-runtime/fetched-specs/<map-id>.json`, replacing any earlier
|
|
92
|
+
copy of that Map; the intake then reads the spec from that file. Both effects
|
|
93
|
+
are on every row for those three commands. The effect test runs each row with
|
|
94
|
+
`--spec` under four conditions and with `--map-id` under `persisted_consent`,
|
|
95
|
+
the one condition whose `--proxy-base` is the loopback receiver, so the fetch
|
|
96
|
+
and the write are observed rather than taken on trust.
|
|
97
|
+
|
|
98
|
+
### The live campaign read
|
|
99
|
+
|
|
100
|
+
`doctor --packet` (with or without `--write` / `--no-write`) and every `qa run`
|
|
101
|
+
form check each page's shipping and package refs against the live campaign.
|
|
102
|
+
Doctor reads when the packet's built `_site/<route>/` exists; QA reads when it
|
|
103
|
+
has read at least one served page. Both need a public Campaigns API key from
|
|
104
|
+
the packet, its local CampaignSpec or the declared campaign-key env var. The
|
|
105
|
+
read is one `GET {proxy-base}/api/campaign` with the key in `X-Campaign-Key`,
|
|
106
|
+
plus `?ref_id=<id>` when the CampaignSpec's `campaign.ref_id` names the
|
|
107
|
+
campaign. No store or Admin credential is used. The proxy's answer is an
|
|
108
|
+
envelope whose `data` field holds the campaign. A failed read, an `ok: false`
|
|
109
|
+
envelope, one with an `error` and no campaign, or several campaigns with no
|
|
110
|
+
`campaign.ref_id` to pick one is recorded as `not_run` with its reason, never
|
|
111
|
+
as a pass. `--no-live-refs` on `doctor` or `qa run` skips only this read and
|
|
112
|
+
records `not_run` with reason `disabled`; every other declared write and send
|
|
113
|
+
is unchanged. Each form has its own row, including `doctor --write
|
|
114
|
+
--no-live-refs` and `qa run --no-live-refs` with each `qa run` modifier.
|
|
115
|
+
`doctor --no-write --no-live-refs` has the effects of the `doctor
|
|
116
|
+
--no-live-refs` row.
|
|
117
|
+
|
|
118
|
+
Only `doctor` and `qa run` make this read. The other commands that run doctor
|
|
119
|
+
internally make no request and record `derived.live_campaign_refs` as
|
|
120
|
+
`not_run` with reason `not_read`, with no warning: `start`, `prepare-build`
|
|
121
|
+
and `build` (the intake alias for prepare-build + doctor), `theme waive`,
|
|
122
|
+
`checkpoint`, `findings harvest`, `run-record`, `next`, and the doctor
|
|
123
|
+
sidecar refresh `qa run` makes after it records the QA stage (the verdict
|
|
124
|
+
itself carries QA's own read).
|
|
125
|
+
|
|
84
126
|
## How to read a row
|
|
85
127
|
|
|
86
128
|
```jsonc
|
|
@@ -108,7 +150,7 @@ There is **one row per command and per effect-changing flag combination**. The
|
|
|
108
150
|
flags that change what the invocation does to the world are listed once, in
|
|
109
151
|
`vocabulary.effect_changing_flags`: `--browser`, `--built`, `--dry-run`,
|
|
110
152
|
`--emit-packet`, `--example`, `--force`, `--from-store`, `--list`,
|
|
111
|
-
`--no-post-verdict`, `--no-probe`, `--no-remit`, `--no-run-session`,
|
|
153
|
+
`--no-live-refs`, `--no-post-verdict`, `--no-probe`, `--no-remit`, `--no-run-session`,
|
|
112
154
|
`--no-write`, `--republish`, `--test-order`, `--write`, `--write-map`. Flags
|
|
113
155
|
that only change the output shape (`--json`, `--report`) deliberately do not.
|
|
114
156
|
|
|
@@ -134,7 +176,9 @@ a flag the command rejects up front. It writes nothing, journals nothing, and is
|
|
|
134
176
|
the row to read when you want to know what a typo costs. The one exception is
|
|
135
177
|
declared on the rows it belongs to: `start`, `prepare-build`, `build`,
|
|
136
178
|
`run start` and `run end` close out a **stale** run session at the root they are
|
|
137
|
-
about to act on *before* argv is refused.
|
|
179
|
+
about to act on *before* argv is refused. Commands that implement `--dry-run`
|
|
180
|
+
skip this closeout whenever that flag is present, including a valued flag that
|
|
181
|
+
will be refused: `run end --dry-run yes` writes, sends, and deletes nothing.
|
|
138
182
|
|
|
139
183
|
A refusal is decided by argv alone. When file content or state on disk decides
|
|
140
184
|
the outcome, the command has reached a handler failure and journals it.
|
|
@@ -143,9 +187,13 @@ For intake, run-record, built-site QA, and `next`, argv-only checks run before
|
|
|
143
187
|
their handler reads the target; invalid values are refused without a journal
|
|
144
188
|
entry. For `start`, `prepare-build`, and `build`, bare, empty, and whitespace-only
|
|
145
189
|
values of `--spec`, `--map-id`, `--source`, `--target`, `--source-kind`,
|
|
146
|
-
`--proxy-base`, `--wrapper-policy`, `--design-manifest`,
|
|
147
|
-
`--
|
|
148
|
-
|
|
190
|
+
`--proxy-base`, `--wrapper-policy`, `--design-manifest`, `--order-path-depth`,
|
|
191
|
+
`--template-family`, `--allow-uncertified-template`, `--theme-policy`, and
|
|
192
|
+
`--brief` are refused before local spec reads, Map fetches, or cache writes on
|
|
193
|
+
the `--spec`, `--map-id`, and `--map-id --cached-spec` paths. So is a
|
|
194
|
+
`--theme-policy` outside `inspect_only`, `auto`, and `off`. Whether a named
|
|
195
|
+
template family is certified, and whether a named brief can be read, depend on
|
|
196
|
+
file content: those checks still run in the handler and are journaled.
|
|
149
197
|
The operator-facing `run-record` and `run end` commands refuse bare, empty, or
|
|
150
198
|
whitespace-only values for every value-taking inherited run-record flag before
|
|
151
199
|
packet work. The five agent
|
|
@@ -189,6 +237,12 @@ flag without a value is refused with "Missing value for --<flag>". If a named
|
|
|
189
237
|
packet yields neither a Map ID nor a valid local-spec identity after checkpoint
|
|
190
238
|
preflight reads the packet, spec, and report, the requirement is a journaled
|
|
191
239
|
handler failure. A conflicting local/Map identity is also a handler failure.
|
|
240
|
+
When `qa run` selects `--legacy-api-test-order`, a missing, bare, empty, or
|
|
241
|
+
unusable `--cart` and an unknown legacy mode are argv-only refusals before QA
|
|
242
|
+
input resolution. They append no lifecycle entry. Accepted modes remain
|
|
243
|
+
`accept`, `decline`, and `both` (case-insensitive); browser `--test-order` still
|
|
244
|
+
takes precedence and does not require the legacy cart. API credentials are
|
|
245
|
+
still checked only inside the legacy handler and failures there are journaled.
|
|
192
246
|
The nested run-record refusal scope in session closeout guards against future
|
|
193
247
|
changes. No internal closeout can currently create a refusal before its
|
|
194
248
|
invoking command journals.
|
|
@@ -205,7 +259,7 @@ sha256) before and after while a loopback `node:http` receiver counts requests.
|
|
|
205
259
|
| `ambient_session` | An active ambient run session opened by `run start` at the target. |
|
|
206
260
|
| `stale_session` | A run session idle past the 12 h TTL, at the target and at the working directory. |
|
|
207
261
|
| `lifecycle_log` | `CAMPAIGNS_OS_LIFECYCLE_LOG` names a journal outside the runtime directory. |
|
|
208
|
-
| `persisted_consent` | Run Telemetry consent **persisted on the machine for the loopback receiver's scope**, a synthetic campaign key in the environment, no run session, and `--proxy-base <loopback>` wherever the command takes it. |
|
|
262
|
+
| `persisted_consent` | Run Telemetry consent **persisted on the machine for the loopback receiver's scope**, a synthetic campaign key in the environment, no run session, and `--proxy-base <loopback>` wherever the command takes it. `start`, `prepare-build` and `build` name their spec by `--map-id` here, and the receiver serves it. |
|
|
209
263
|
|
|
210
264
|
Five conditions rather than one, because the CLI's effects are not a function of
|
|
211
265
|
argv alone: an ambient session redirects the journal and is itself touched by
|
|
@@ -307,10 +361,10 @@ loopback receiver only stands in for (`{base-url}`, the login gateway) matched
|
|
|
307
361
|
refuses the contradiction rather than letting the test find it.
|
|
308
362
|
|
|
309
363
|
A `full` row may still carry an individual effect the offline fixture cannot
|
|
310
|
-
reach — the
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
364
|
+
reach — the `codex` and `agents` destinations of `install-skills`. Each such
|
|
365
|
+
entry has an empty `observed_in` **and** a `not_observed_reason`, and
|
|
366
|
+
`check-effects.mjs` refuses one without the reason. What it may not be is
|
|
367
|
+
silent.
|
|
314
368
|
|
|
315
369
|
## The rule
|
|
316
370
|
|
package/docs/gateway-login.md
CHANGED
|
@@ -17,6 +17,9 @@ campaigns-os login --store example
|
|
|
17
17
|
There is no discovery or guess from the current project. Noninteractive calls
|
|
18
18
|
must supply `--store`. URLs, paths and unrelated hosts are refused before any
|
|
19
19
|
request. Login uses the fixed `https://mcp.nextcommerce.com` gateway.
|
|
20
|
+
It asks for the `https://mcp.nextcommerce.com/mcp` resource and the
|
|
21
|
+
`campaigns.read` capability. A login saved before 1.43.2+agent.3, which named
|
|
22
|
+
the earlier `/campaigns` resource, is refused; sign in again.
|
|
20
23
|
|
|
21
24
|
Open the displayed device page in one browser tab and enter the displayed code.
|
|
22
25
|
Keep that tab: if installation is needed, follow its Install Campaigns link,
|
package/docs/local-setup.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
For a new campaign, choose its working folder and run this from that folder:
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
npm install --save-dev --save-exact @nextcommerce/campaigns-os@1.
|
|
6
|
+
npm install --save-dev --save-exact @nextcommerce/campaigns-os@1.46.0 next-campaign-page-kit@0.2.0 && npx --no-install campaigns-os tooling setup --target . --platform claude
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Review the release source/provenance before installation as described in
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
contracts/orientation-reason-codes.v1.json
|
|
10
10
|
contracts/orientation-limits.v1.json
|
|
11
11
|
contracts/supported-surface.json
|
|
12
|
+
contracts/release-ledger.json (baseline_floor only)
|
|
12
13
|
contracts/fixtures/orientation/canonicalization/v1.json
|
|
13
14
|
Regenerate: node ./scripts/generate-orientation-reference.mjs --write
|
|
14
15
|
CI runs the same script with --check, so a stale copy of this file fails the build.
|
|
@@ -25,7 +26,7 @@ Ledger schema id: `campaigns-os-release-ledger/v1`
|
|
|
25
26
|
Change policy version: `1.0.0`
|
|
26
27
|
Reason-code vocabulary version: `1.0.0`
|
|
27
28
|
Limits version: `1.0.0`
|
|
28
|
-
Supported surface at generation time: `1.
|
|
29
|
+
Supported surface at generation time: `1.46.0`
|
|
29
30
|
|
|
30
31
|
## Forward compatibility
|
|
31
32
|
|
|
@@ -60,6 +61,43 @@ position, so a consumer reading history in array order reads it in `sequence` or
|
|
|
60
61
|
its number to `sequence`; the two diverge legitimately, because when concurrent pull requests
|
|
61
62
|
land the later one restamps its `sequence` to follow the earlier while keeping the id it was
|
|
62
63
|
written with. Order by `sequence`, identify by `id`, and do not infer one from the other.
|
|
64
|
+
After a baseline rotation the file starts at `baseline_floor.last_archived_sequence + 1`, not at 1;
|
|
65
|
+
entries are never renumbered, so position plus that offset is still the sequence.
|
|
66
|
+
|
|
67
|
+
## Baseline rotation
|
|
68
|
+
|
|
69
|
+
The ledger and the changelog are bounded as whole files, so they are rotated rather than allowed to
|
|
70
|
+
outgrow the limits. A rotation moves every entry up to a reviewed cut, and the changelog from the first section
|
|
71
|
+
those entries link to the end of the file (unlinked sections in that range included), byte-for-byte and in order
|
|
72
|
+
into a dated archive pair under `contracts/archive/`, and declares the cut as
|
|
73
|
+
`baseline_floor` in `contracts/release-ledger.json`. Archived entries keep their `sequence`, `entry_sha256` and
|
|
74
|
+
`changelog_sha256`; each section hash verifies against the archive changelog. The floor names the archive
|
|
75
|
+
files with their SHA-256, the last archived entry and its sequence, the first kept entry, the live entry that
|
|
76
|
+
recorded the rotation, and the reason code to refuse with. The floor only moves forward, only with a new
|
|
77
|
+
rotation entry, and an archive file never changes once merged; the next rotation writes a new dated pair.
|
|
78
|
+
|
|
79
|
+
Rotation changes no mandatory read. The reading order in `AGENTS.md` is unchanged, and `source_bytes` measures
|
|
80
|
+
exactly its data files (steps 1 to 8):
|
|
81
|
+
|
|
82
|
+
- `contracts/supported-surface.json`
|
|
83
|
+
- `contracts/release-ledger.json`
|
|
84
|
+
- `CHANGELOG.md`
|
|
85
|
+
- `contracts/orientation-limits.v1.json`
|
|
86
|
+
- `contracts/orientation-reason-codes.v1.json`
|
|
87
|
+
- `schemas/campaigns-os-tooling-orientation.v1.schema.json`
|
|
88
|
+
- `schemas/campaigns-os-release-ledger.v1.schema.json`
|
|
89
|
+
- `contracts/agent-relevant-change-policy.v1.json`
|
|
90
|
+
|
|
91
|
+
The archive files are not among them: they are optional history reads and count against no limit, and their
|
|
92
|
+
sections and entries are not in `section_count` or `ledger_entries`.
|
|
93
|
+
|
|
94
|
+
Current floor: last archived entry `RL-0124` (sequence 124), first kept entry
|
|
95
|
+
`RL-0125`, recorded by `RL-0190`. Archives, oldest rotation first:
|
|
96
|
+
|
|
97
|
+
- [`contracts/archive/release-ledger.2026-09-30.json`](../contracts/archive/release-ledger.2026-09-30.json) and [`contracts/archive/CHANGELOG.2026-09-30.md`](../contracts/archive/CHANGELOG.2026-09-30.md)
|
|
98
|
+
|
|
99
|
+
A consumer whose reviewed baseline's newest ledger entry is older than `RL-0124` refuses with
|
|
100
|
+
`baseline_below_floor`. Adopt a newer reviewed baseline whose ledger reaches the floor's last_archived_id; every commit at or after the floor's first_kept_id qualifies. Do not orient on the partial live window, and do not stitch the archive in as a substitute: the archive holds the history for reference, not for this read.
|
|
63
101
|
|
|
64
102
|
## Release-ledger digest canonicalization
|
|
65
103
|
|
|
@@ -120,6 +158,7 @@ consumer's parser tests.
|
|
|
120
158
|
|---|---|---|---|---|---|
|
|
121
159
|
| `ahead_of_upstream` | `refused` | campaigns-agent | `TP-D4-ahead-of-upstream` | The checkout carries commits the upstream default branch does not, so its contracts are not a published generation. | Push or drop the local commits, or run against a managed generation at the published OID. |
|
|
122
160
|
| `already_current` | `current` | campaigns-agent | `TP-B3-already-current` | The verified target equals the active generation; nothing changed and no restart is required. | None. |
|
|
161
|
+
| `baseline_below_floor` | `refused` | campaigns-os | `A1-baseline-below-floor` | The target ledger declares a baseline_floor, and the reviewed baseline's newest ledger entry is older than the floor's last archived entry. Part of the baseline-to-target window was rotated into the archive files the floor names, which are not mandatory orientation reads, so the window cannot be read from the live ledger and changelog. | Adopt a newer reviewed baseline whose ledger reaches the floor's last_archived_id; every commit at or after the floor's first_kept_id qualifies. Do not orient on the partial live window, and do not stitch the archive in as a substitute: the archive holds the history for reference, not for this read. |
|
|
123
162
|
| `checkout_acquisition_failed` | `refused` | campaigns-agent | `TP-D1-acquisition-failed` | Managed acquisition did not complete: interrupted clone, remote validation failure, or a broken linked-worktree backpointer. Partial state is quarantined, never exposed as ready. | Rerun acquisition. If it fails repeatedly, inspect the quarantined partial named in the diagnostic and confirm remote reachability. |
|
|
124
163
|
| `checkout_already_exists` | `refused` | campaigns-agent | `TP-D1-checkout-already-exists` | The managed root reserved for acquisition appeared concurrently and is not an empty reservation this run owns. | Rerun so the existing root is validated as a managed store, or remove the unexpected directory after confirming it holds no needed state. |
|
|
125
164
|
| `checkout_missing` | `refused` | campaigns-agent | `TP-D1-checkout-missing` | No checkout exists at the configured location and the mode does not authorize acquiring one. | Supply the operator checkout at the configured path, or switch to managed mode so a generation can be acquired. |
|
|
@@ -205,7 +244,7 @@ nowhere in the schemas, or in the schemas and not here, is a generation failure.
|
|
|
205
244
|
| `$defs.baseline.properties.kind` | `legacy_commit`, `supported_surface` |
|
|
206
245
|
| `$defs.change_class` | `schema`, `hashed_surface`, `named_surface`, `cli_surface`, `skill`, `package_export`, `compatibility_policy`, `documentation`, `workflow`, `generated_runtime` |
|
|
207
246
|
| `$defs.disposition` | `current`, `orientation_available`, `updated`, `restart_required`, `recovered_interrupted_update`, `legacy_baseline`, `freshness_unknown`, `refused` |
|
|
208
|
-
| `$defs.reason_code` | `already_current`, `ahead_of_upstream`, `checkout_acquisition_failed`, `checkout_already_exists`, `checkout_missing`, `checkout_mutation_not_authorized`, `detached_head`, `dirty_checkout`, `diverged_history`, `evidence_budget_exceeded`, `fetch_failed`, `git_environment_unsafe`, `history_incomplete`, `missing_upstream`, `orientation_contract_missing`, `orientation_in_progress`, `orientation_incomplete`, `orientation_rendered`, `orientation_too_large`, `pointer_race`, `runtime_commit_mismatch`, `runtime_refresh_failed`, `runtime_refresh_required`, `surface_incompatible`, `transaction_incomplete`, `transaction_reconciled`, `wrong_remote` |
|
|
247
|
+
| `$defs.reason_code` | `already_current`, `ahead_of_upstream`, `baseline_below_floor`, `checkout_acquisition_failed`, `checkout_already_exists`, `checkout_missing`, `checkout_mutation_not_authorized`, `detached_head`, `dirty_checkout`, `diverged_history`, `evidence_budget_exceeded`, `fetch_failed`, `git_environment_unsafe`, `history_incomplete`, `missing_upstream`, `orientation_contract_missing`, `orientation_in_progress`, `orientation_incomplete`, `orientation_rendered`, `orientation_too_large`, `pointer_race`, `runtime_commit_mismatch`, `runtime_refresh_failed`, `runtime_refresh_required`, `surface_incompatible`, `transaction_incomplete`, `transaction_reconciled`, `wrong_remote` |
|
|
209
248
|
|
|
210
249
|
### `schemas/campaigns-os-release-ledger.v1.schema.json`
|
|
211
250
|
|
|
@@ -225,6 +264,7 @@ so a renamed command fails here as well as at the supported-surface gate.
|
|
|
225
264
|
- `campaigns-os prepare-build`
|
|
226
265
|
- `campaigns-os build`
|
|
227
266
|
- `campaigns-os polish`
|
|
267
|
+
- `campaigns-os record`
|
|
228
268
|
- `campaigns-os checkpoint`
|
|
229
269
|
- `campaigns-os page-kit`
|
|
230
270
|
- `campaigns-os spec`
|
package/docs/polish-evidence.md
CHANGED
|
@@ -21,6 +21,11 @@ Three layers must all be satisfied:
|
|
|
21
21
|
`campaigns-os polish capture` from the current packet, report, served build,
|
|
22
22
|
mapped routes, and fixed desktop/mobile viewports.
|
|
23
23
|
|
|
24
|
+
Record all three with `campaigns-os record polish` (§7) rather than editing
|
|
25
|
+
the report by hand: it stamps the stage-record fields from doctor's current
|
|
26
|
+
state, keeps the captured `page_load`, and writes nothing unless this gate would
|
|
27
|
+
pass on the result.
|
|
28
|
+
|
|
24
29
|
## 1. Stage-record requirements
|
|
25
30
|
|
|
26
31
|
The gate only applies once assembly is complete
|
|
@@ -500,3 +505,72 @@ current value.
|
|
|
500
505
|
Polish evidence certifies the polish pass only — it is not QA and does not
|
|
501
506
|
certify launch readiness (`docs/qa-and-test-orders.md` owns the QA proof
|
|
502
507
|
stack).
|
|
508
|
+
|
|
509
|
+
## 7. Recording with `record polish`
|
|
510
|
+
|
|
511
|
+
```bash
|
|
512
|
+
campaigns-os record polish --packet <campaign-runtime.build.json> --evidence <polish-evidence.json> [--report <json>] [--dry-run] [--json]
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Run it after `campaigns-os polish capture`, against the build `campaigns-os
|
|
516
|
+
record build` recorded. The `--evidence` file is a JSON object with these keys;
|
|
517
|
+
any other key is refused:
|
|
518
|
+
|
|
519
|
+
| Key | Required | Written to |
|
|
520
|
+
|---|---|---|
|
|
521
|
+
| `status` | no (default `completed`) | `stages.polish.status`; `completed`, `completed_with_warnings`, `blocked` or `skipped` |
|
|
522
|
+
| `evidence` | for `completed*` | `stages.polish.evidence`, the seven categories of §2. Leave out `visual_review.page_load`: the value `polish capture` recorded is kept, and a file that carries one is refused. Without it, a `blocked` or `skipped` record keeps the evidence already on the report. |
|
|
523
|
+
| `blockers` | for `blocked` only | `stages.polish.blockers`: a non-empty array of `{"code", "message"}` objects. |
|
|
524
|
+
| `skip_reason` | for `skipped` only | `stages.polish.skip_reason`: a non-empty string. |
|
|
525
|
+
| `repair_loop_defect` | no | `report.theme.repair_loop_defect`: `null`, or the first brand-layer repair-loop defect as an object (`code`, `message`, `path`, `detail`). A non-null defect needs a recorded `report.theme`. |
|
|
526
|
+
|
|
527
|
+
The command itself stamps `performed_by: "next-campaigns-polish"`,
|
|
528
|
+
`source_build_fingerprint` (doctor's `derived.build_output_fingerprint.value`,
|
|
529
|
+
which must equal the recorded `stages.assembly.build_fingerprint`),
|
|
530
|
+
`source_package_material_fingerprint` when the report fingerprints a Design
|
|
531
|
+
Source Package, and `completed_at` (not for `blocked`). It then validates the
|
|
532
|
+
report it would write against
|
|
533
|
+
`schemas/campaign-runtime-assembly-report.v0.schema.json` and doctor's report
|
|
534
|
+
checks and, for a `completed*` status, evaluates this gate and the hidden
|
|
535
|
+
eager-media checkpoint over it exactly as doctor does. A `blocked` or `skipped`
|
|
536
|
+
record keeps this gate blocked, so `next` stays at Polish and QA stays blocked. Any failure is printed by field or gate code
|
|
537
|
+
(for example `repair_loop_defect must be null or an object ... (got string)`, or
|
|
538
|
+
`polish.evidence_incomplete` with its per-field problems), the command exits
|
|
539
|
+
non-zero, and nothing is written. `--dry-run` runs every check and writes
|
|
540
|
+
nothing. Like `record setup` and `record build`, it refuses a report that is not
|
|
541
|
+
bound to the packet (`docs/build-packet.md`, "Recording stage completion") and
|
|
542
|
+
output that changes while it records.
|
|
543
|
+
|
|
544
|
+
A complete file:
|
|
545
|
+
|
|
546
|
+
```json
|
|
547
|
+
{
|
|
548
|
+
"status": "completed",
|
|
549
|
+
"evidence": {
|
|
550
|
+
"visual_review": {
|
|
551
|
+
"screenshots": ["qa-output/polish/landing-desktop.png", "qa-output/polish/landing-mobile.png"]
|
|
552
|
+
},
|
|
553
|
+
"brand_review": {
|
|
554
|
+
"logo_checked": true,
|
|
555
|
+
"favicon": { "status": "confirmed_non_template" },
|
|
556
|
+
"colors": ["#1f4d3a"],
|
|
557
|
+
"brand_bleed": { "cleared": true }
|
|
558
|
+
},
|
|
559
|
+
"checkout_review": {
|
|
560
|
+
"field_labels": "initial field labels and placeholders are legible on desktop and mobile",
|
|
561
|
+
"phone_alignment": "checked",
|
|
562
|
+
"payment_display": "checked",
|
|
563
|
+
"bump_compare_price_rule": "no equal or no-discount compare price renders"
|
|
564
|
+
},
|
|
565
|
+
"template_residue_review": {
|
|
566
|
+
"next_blue": "not found",
|
|
567
|
+
"starter_favicon": { "status": "confirmed_non_template" },
|
|
568
|
+
"lorem": "not found"
|
|
569
|
+
},
|
|
570
|
+
"commerce_flow_review": "direct-entry package selection reviewed",
|
|
571
|
+
"issues": [],
|
|
572
|
+
"commands": ["next-campaigns-polish", "campaigns-os polish capture"]
|
|
573
|
+
},
|
|
574
|
+
"repair_loop_defect": null
|
|
575
|
+
}
|
|
576
|
+
```
|
|
@@ -59,12 +59,16 @@ across them. A progress stream is independent of a run-session ID.
|
|
|
59
59
|
|
|
60
60
|
Sanitized immutable snapshots are written under the target repository's
|
|
61
61
|
`.campaign-runtime/progress/` before any request. Allocation uses an exclusive
|
|
62
|
-
local lock with a process owner
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
local lock with a process owner; the lock directory and its owner record are
|
|
63
|
+
published together in one rename, so a lock never exists without its owner.
|
|
64
|
+
Dead owners are recovered through an exclusive recovery claim and an atomic
|
|
65
|
+
rename; a live process is never evicted. A lock directory with no owner record
|
|
66
|
+
(left by an older release) is never taken over: capture refuses it after about
|
|
67
|
+
a second. If one is found, or if recovery itself is interrupted, capture fails
|
|
68
|
+
closed and the warning names the affected `.allocation-lock` directory: stop
|
|
69
|
+
all Campaigns OS writers for that target, then remove that directory before
|
|
70
|
+
retrying `next`. Do not remove a lock while a writer is active. Do not run an
|
|
71
|
+
older Campaigns OS release against the same target at the same time.
|
|
68
72
|
|
|
69
73
|
An unchanged projection reuses its ID, timestamp and sequence. Identity
|
|
70
74
|
changes start a new stream. Each local scope retains at most 32 snapshots and
|