@nextcommerce/campaigns-os 1.37.3 → 1.41.2
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 +114 -10
- package/CHANGELOG.md +530 -0
- package/README.md +38 -27
- package/contracts/agent-relevant-change-policy.v1.json +11 -1
- package/contracts/effects.v1.json +4794 -0
- package/contracts/release-ledger.json +789 -0
- package/contracts/supported-surface.json +25 -5
- package/docs/build-packet.md +27 -16
- package/docs/demo-preview.md +1 -1
- package/docs/diagnostics.md +7 -4
- package/docs/effects.md +281 -0
- package/docs/gateway-login.md +113 -0
- package/docs/orientation-contract-reference.md +4 -1
- package/docs/progress-snapshots.md +3 -3
- package/docs/qa-and-test-orders.md +3 -3
- package/docs/readback.md +523 -0
- package/docs/runtime-readiness.md +1 -1
- package/docs/sdk-storage-compatibility.md +1 -1
- package/docs/skills-revision.md +364 -0
- package/docs/supported-surface.md +11 -3
- package/docs/versioning.md +8 -4
- package/package.json +8 -3
- package/schemas/campaign-runtime-build-packet.v0.schema.json +5 -0
- package/schemas/campaigns-os-effects.v1.schema.json +211 -0
- package/schemas/campaigns-os-readback.v2.schema.json +267 -0
- package/skills/campaign-lifecycle-orientation/SKILL.md +174 -0
- package/skills/campaign-readback-classification/SKILL.md +230 -0
- package/skills/campaign-run-evidence/SKILL.md +140 -0
- package/skills/contribution-intake/SKILL.md +85 -0
- package/skills/next-campaigns-build/SKILL.md +33 -12
- package/skills/next-campaigns-os/SKILL.md +45 -21
- package/skills/next-campaigns-os-setup/SKILL.md +35 -14
- package/skills/next-campaigns-polish/SKILL.md +43 -17
- package/skills/next-campaigns-qa/SKILL.md +48 -24
- package/skills.json +39 -6
- package/src/admin-transport.mjs +123 -0
- package/src/cli.mjs +991 -200
- package/src/credential-store.mjs +183 -0
- package/src/deviation.mjs +3 -2
- package/src/diagnostic.mjs +4 -1
- package/src/gate-actions.mjs +2 -2
- package/src/install-mode.mjs +17 -9
- package/src/lifecycle.mjs +95 -0
- package/src/login.mjs +152 -0
- package/src/package-install-fixture.mjs +3 -2
- package/src/qa-node.mjs +56 -19
- package/src/qa-publish.mjs +108 -2
- package/src/readback.mjs +1936 -0
- package/src/remit.mjs +17 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,536 @@
|
|
|
2
2
|
|
|
3
3
|
Notable supported-surface changes are recorded here.
|
|
4
4
|
|
|
5
|
+
## [1.41.2] - 2026-09-23
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Every command Campaigns OS prints for a project-local install is spelled
|
|
10
|
+
`npx --no-install campaigns-os …`, and so is every command in the bundled
|
|
11
|
+
skills, the README, `AGENTS.md` and the docs. 1.41.1 made this change only
|
|
12
|
+
for the revision check in the skill header. `campaigns-os` is only the bin
|
|
13
|
+
name of `@nextcommerce/campaigns-os`. In a folder where the package is not
|
|
14
|
+
installed (another folder, or one where `npm install` has not run yet), a
|
|
15
|
+
plain `npx campaigns-os …` looks the bin name up as a registry package and,
|
|
16
|
+
with no terminal to ask, installs whatever it finds and runs it. With
|
|
17
|
+
`--no-install`, npx runs the pinned copy or stops with an error.
|
|
18
|
+
- For a `node_modules` install, `tooling status` reports
|
|
19
|
+
`cli.invocation_prefix` as `npx --no-install campaigns-os` and
|
|
20
|
+
`cli.invocation` as `npx --no-install campaigns-os <command>`.
|
|
21
|
+
- Every command spelled with that prefix follows: `next` text and `--json`,
|
|
22
|
+
doctor required actions, gate and checkpoint remediations, the
|
|
23
|
+
skill-refresh and gateway login actions of `tooling status`, and the
|
|
24
|
+
browser-missing hints.
|
|
25
|
+
- The PATH warnings of `tooling status` and its action for a stale project
|
|
26
|
+
pin name the same spelling.
|
|
27
|
+
- A checkout, a global install and an npx cache keep their spellings.
|
|
28
|
+
- Run-session deviation tracking reads the command word through the new
|
|
29
|
+
prefix, and still through the old one in sessions recorded by earlier
|
|
30
|
+
versions.
|
|
31
|
+
|
|
32
|
+
Commands printed or documented by earlier releases lack the flag; add
|
|
33
|
+
`--no-install` after `npx` when you reuse one.
|
|
34
|
+
- Skills bundle revision `1.41.2+skills.1`. Every skill's version advances by
|
|
35
|
+
one patch.
|
|
36
|
+
|
|
37
|
+
## [1.41.1] - 2026-09-23
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- `tooling status` without `--platform` or `--target` checks skill freshness
|
|
42
|
+
only on the platform directories where Campaigns OS skills are installed. A
|
|
43
|
+
directory counts when a skill sits under one of the bundled names or under a
|
|
44
|
+
retired name when it is our own copy. Before this, a Claude Code only install (the
|
|
45
|
+
documented path) was read as stale for Codex and the shared directory, so
|
|
46
|
+
the revision check the skills ask for exited 2 and printed an action to
|
|
47
|
+
install skills for every platform. Now:
|
|
48
|
+
- A `Ready:` line names the skipped platforms.
|
|
49
|
+
- The refresh action names each stale installed platform (`install-skills
|
|
50
|
+
--platform claude`), or `--platform all` when all three are installed and
|
|
51
|
+
stale.
|
|
52
|
+
- When no platform has Campaigns OS skills, the action asks for an install
|
|
53
|
+
on the harness in use (`install-skills --platform claude`, with `codex` and
|
|
54
|
+
`agents` named as the alternatives)
|
|
55
|
+
rather than on all three.
|
|
56
|
+
- `--platform all` still checks every platform.
|
|
57
|
+
- `--json` adds `skills.scope` (`requested`, `installed_platforms` or
|
|
58
|
+
`no_platform_installed`) and `skills.not_installed_platforms`.
|
|
59
|
+
- `tooling diagnose` forwards `--platform` only when one is given, and its
|
|
60
|
+
export reports the unnamed scope as `platform: installed`.
|
|
61
|
+
- The gateway login hint uses the printed invocation prefix.
|
|
62
|
+
- Every bundled skill header now tells the agent to run the check as `npx
|
|
63
|
+
--no-install campaigns-os tooling status --skills-revision <revision>` from
|
|
64
|
+
the campaign's Page Kit folder. There it runs the project's pinned copy, and
|
|
65
|
+
it never installs one. The header change fixes two problems:
|
|
66
|
+
- A bare `campaigns-os` resolves through PATH. On a machine with an older
|
|
67
|
+
global install, a copy from before 1.40.0 answers instead. That copy
|
|
68
|
+
ignores `--skills-revision`, prints no `Skills revision:` line, and lists
|
|
69
|
+
an `install-skills --platform all` action. Following it replaces five of
|
|
70
|
+
the nine bundled skills with older text. The revision comparison cannot
|
|
71
|
+
see that result; only the pinned copy's freshness check reports it.
|
|
72
|
+
- `campaigns-os` is only the bin name of `@nextcommerce/campaigns-os`.
|
|
73
|
+
Outside a pinned folder, a plain `npx campaigns-os` looks the bin name up
|
|
74
|
+
as a registry package, and with no terminal to ask, it would install
|
|
75
|
+
whatever it found.
|
|
76
|
+
|
|
77
|
+
The header also says that output with no `Skills revision:` line (no
|
|
78
|
+
`revision_check` under `--json`) did not come from the pinned copy, and that
|
|
79
|
+
none of its actions should be followed. `docs/skills-revision.md`, the
|
|
80
|
+
README, the quickstart and `docs/diagnostics.md` describe the new
|
|
81
|
+
behaviour.
|
|
82
|
+
- Refusals that happen before a command's first effect are tagged, so they
|
|
83
|
+
write no lifecycle journal entry (campaigns-os#465). This covers:
|
|
84
|
+
- `theme waive` without `--reason`, or with a waiver attribution it rejects
|
|
85
|
+
(a missing or placeholder `--waived-by`, or a bad `--expires-at`).
|
|
86
|
+
- `qa waive` without `--assertion`, with an assertion outside the waiver
|
|
87
|
+
lane, or without `--reason`.
|
|
88
|
+
- `qa policy set` with a removed flag, a string flag given no value, a
|
|
89
|
+
non-boolean `--allowed-domains-confirmed`, or an unsupported
|
|
90
|
+
`--order-path-depth`.
|
|
91
|
+
|
|
92
|
+
Every other plain throw in `src/cli.mjs` and `src/qa-node.mjs` was reviewed
|
|
93
|
+
against the rule in `docs/effects.md` and left as a journaled handler
|
|
94
|
+
failure. Those throws follow a read of the target (spec, source, report,
|
|
95
|
+
session state or built site), an effect, or a request, or they are internal
|
|
96
|
+
defect checks. Each newly tagged site has a refusal-table row in
|
|
97
|
+
`src/lifecycle-effects.test.mjs`. Each touched handler has a positive
|
|
98
|
+
control: the same invocation, when it passes every refusal and then fails,
|
|
99
|
+
still appends exactly one entry. No effects row changes.
|
|
100
|
+
- Skills bundle revision `1.41.1+skills.1`. Every skill's version advances by
|
|
101
|
+
one patch.
|
|
102
|
+
|
|
103
|
+
## [1.41.0] - 2026-09-23
|
|
104
|
+
|
|
105
|
+
### Added
|
|
106
|
+
|
|
107
|
+
- `tooling status` reports the pin checks (ADR 0002, campaigns-os#466): one
|
|
108
|
+
executable per project. The project pin — the first exact
|
|
109
|
+
`@nextcommerce/campaigns-os` spec (`x.y.z`, `=x.y.z` or `vx.y.z`) in
|
|
110
|
+
`devDependencies`, then `dependencies`, of each `package.json` walking up from
|
|
111
|
+
the working directory, through manifests that name nothing, to the workspace
|
|
112
|
+
root — comes first; a range counts only
|
|
113
|
+
when no exact spec exists on that walk, and `peerDependencies` /
|
|
114
|
+
`optionalDependencies` are never a pin. The Build Packet's recorded kernel
|
|
115
|
+
version comes second (the project's `campaign-runtime.build.json`, or
|
|
116
|
+
`--packet <path>`). `--json` carries `pin: { source, version, running,
|
|
117
|
+
status, range, packet_version, packet_version_ignored, project_version,
|
|
118
|
+
project_manifest, project_key, forced, message }` and the text view a `Pin:` line under the
|
|
119
|
+
skills revision line. The line names, for every status, the key and manifest
|
|
120
|
+
of each project version or range it quotes (`devDependencies in
|
|
121
|
+
<project>/package.json`), the nearest manifest when there is no project pin,
|
|
122
|
+
and the packet file of each packet version it quotes; every action names the
|
|
123
|
+
manifest and key to change; a packet value with an `=` or `v` prefix is
|
|
124
|
+
named as ignored (`packet_version_ignored`), not as absent. An installed
|
|
125
|
+
package's own manifest (`node_modules/<name>` or `node_modules/@<scope>/<name>`)
|
|
126
|
+
is never the project, so a run from inside an install resolves the enclosing
|
|
127
|
+
project, while a project whose own path passes through a `node_modules`
|
|
128
|
+
directory still resolves its own manifest; a leading BOM is accepted, and an
|
|
129
|
+
unreadable or malformed ancestor manifest ends the walk with a warning.
|
|
130
|
+
`pin.status` is `match`; `stale_pin` (the pin is not the
|
|
131
|
+
running version); `conflicting_pin` (both sources present and different); or
|
|
132
|
+
`unpinned` (neither present — a range or tag is not a pin and is reported
|
|
133
|
+
under `range`). `stale_pin` and `conflicting_pin` exit 2 with an action
|
|
134
|
+
naming the file to change; `unpinned` exits 0 and is always reported.
|
|
135
|
+
- `tooling status --force`: a bare flag that overrides `stale_pin` and
|
|
136
|
+
`conflicting_pin`, so the command exits as the rest of the status dictates.
|
|
137
|
+
The override is reported as `pin.forced: true` and recorded on the
|
|
138
|
+
command-lifecycle journal entry through `argv_shape`. `--force true` is
|
|
139
|
+
refused. Declared as its own row in `contracts/effects.v1.json`
|
|
140
|
+
(`effects: tooling status --force`, 92 rows): it changes the exit status only
|
|
141
|
+
and writes nothing the plain row does not.
|
|
142
|
+
- Build Packet: optional top-level `campaigns_os_version` (a bare `x.y.z` version) in
|
|
143
|
+
`schemas/campaign-runtime-build-packet.v0.schema.json`, stamped by
|
|
144
|
+
`prepare-build` with the version that prepared the packet. Additive: the
|
|
145
|
+
packet schema stays `campaign-runtime-build-packet/v0`, and packets without
|
|
146
|
+
the field stay valid (they are no packet pin source).
|
|
147
|
+
|
|
148
|
+
### Changed
|
|
149
|
+
|
|
150
|
+
- `docs/skills-revision.md`: the "Not yet built" section is replaced by the pin
|
|
151
|
+
check as built — sources and precedence, the four statuses, exit codes,
|
|
152
|
+
`--force`, and JSON and text output from real runs. The `tooling status` help
|
|
153
|
+
line gains `[--packet <campaign-runtime.build.json>] [--force]`.
|
|
154
|
+
- `tooling status` refuses `--no-force` up front (`--force` is bare and off by
|
|
155
|
+
default), journaling nothing, where the shared parser had let it pass as a
|
|
156
|
+
no-op; and an empty or whitespace-only project spec is absent, never a
|
|
157
|
+
`range`.
|
|
158
|
+
- Skills: `bundle_revision` moves to `1.41.0+skills.1` with the package
|
|
159
|
+
version, and every bundled skill's `Bundle revision:` header and its
|
|
160
|
+
`--skills-revision` instruction follow (each skill version patch-bumped).
|
|
161
|
+
- `contracts/supported-surface.json`: `surface_version` 1.41.0, with the
|
|
162
|
+
sha256 of the hashed `contracts/effects.v1.json` and
|
|
163
|
+
`schemas/campaign-runtime-build-packet.v0.schema.json` entries recomputed. No
|
|
164
|
+
entry, command, export or bin moved.
|
|
165
|
+
- `package.json` and `package-lock.json`: version 1.41.0; no dependency moved.
|
|
166
|
+
- `docs/orientation-contract-reference.md` and `docs/runtime-readiness.md`:
|
|
167
|
+
regenerated for surface version 1.41.0.
|
|
168
|
+
|
|
169
|
+
## [1.40.0] - 2026-09-22
|
|
170
|
+
|
|
171
|
+
### Added
|
|
172
|
+
|
|
173
|
+
- `contracts/effects.v1.json`: the declared effect of every supported
|
|
174
|
+
invocation — 91 rows, one per command, per subcommand and per effect-changing
|
|
175
|
+
flag, stating what the invocation **writes** (with location tokens, so a write
|
|
176
|
+
to your home directory or your machine config is not mistaken for a write to
|
|
177
|
+
the campaign) and what it **sends**, alongside the four MCP-style annotations
|
|
178
|
+
(`readOnlyHint`, `destructiveHint`, `openWorldHint`, `idempotentHint`) and an
|
|
179
|
+
effect tier (`none` < `B` writes < `A` sends < `C` destructive). One row is
|
|
180
|
+
not a command: `{"command": "*refused*"}` declares what an invocation refused
|
|
181
|
+
before its handler runs costs. Its shape is published as
|
|
182
|
+
`schemas/campaigns-os-effects.v1.schema.json` and its prose as
|
|
183
|
+
`docs/effects.md`.
|
|
184
|
+
- Every row is proved by a case in `src/effects.test.mjs`, which runs the real
|
|
185
|
+
CLI in a disposable target under five conditions — no run session, an active
|
|
186
|
+
ambient session, a session idle past the 12 h TTL,
|
|
187
|
+
`CAMPAIGNS_OS_LIFECYCLE_LOG`, and **Run Telemetry consent persisted for a
|
|
188
|
+
loopback receiver's scope** — snapshotting the whole tree (paths plus sha256)
|
|
189
|
+
before and after while a loopback receiver counts requests. The assertion runs
|
|
190
|
+
both ways: nothing the row does not declare may change in any condition, and
|
|
191
|
+
every declared effect whose `observed_in` names a condition must be seen in
|
|
192
|
+
it. The fifth condition is the one that does not take the row's word for
|
|
193
|
+
whether consent is on — under the other four, consent is switched on only for
|
|
194
|
+
rows that declare a consent-gated send, so a send nobody declared ran with
|
|
195
|
+
consent off and left no trace. It is also what pins the send declarations of
|
|
196
|
+
`next`, its five stage forms and the three `qa run` rows, each of which POSTs
|
|
197
|
+
under persisted consent: the stage progress observation to
|
|
198
|
+
`{proxy-base}/api/progress`, and for `qa run` the verdict to
|
|
199
|
+
`{proxy-base}/api/qa/verdicts`, on blocked attempts included. 79 rows are
|
|
200
|
+
proved end to end; 12 whose command cannot execute past its preflight offline
|
|
201
|
+
(`login`, `logout`, `page-kit parity`, `polish capture`,
|
|
202
|
+
`qa install-browser`, `qa parity`, `qa parity --no-post-verdict`,
|
|
203
|
+
`qa resolve`, `qa run --browser`, `spec derive --from-store`,
|
|
204
|
+
`spec derive --write-map`, `telemetry list`) carry `test_scope: "preflight"`
|
|
205
|
+
and a `preflight` allowance
|
|
206
|
+
— the exact paths the refusal may write and the exact request paths it may
|
|
207
|
+
contact — so a home-directory write or an undeclared endpoint fails the row
|
|
208
|
+
even when the row declares that path or destination for its success path.
|
|
209
|
+
- `npm run check:effects` (`scripts/check-effects.mjs`, in `npm run check` and
|
|
210
|
+
`npm run check:contracts`): every command on the supported CLI surface, every
|
|
211
|
+
subcommand **any** help block teaches **and every effect-changing flag a help
|
|
212
|
+
usage line carries** (`vocabulary.effect_changing_flags`) has a row. "Any help
|
|
213
|
+
block" is the point: `campaigns-os qa` prints its own from `src/qa-node.mjs`,
|
|
214
|
+
and a scan that read only `src/cli.mjs` never required a row for the three
|
|
215
|
+
subcommands documented there alone — `qa parity`, `qa waive` and
|
|
216
|
+
`qa install-browser`, all three of which the QA skill tells an agent to run.
|
|
217
|
+
Every module that owns a usage block is now scanned, and a test derives that
|
|
218
|
+
list from the source so a command that grows its own help cannot leave the
|
|
219
|
+
scan quietly. Beyond that: every row names
|
|
220
|
+
the test case the per-row generator gives it and has argv in the test's
|
|
221
|
+
invocation table; every effect the offline fixture cannot reach states why;
|
|
222
|
+
every preflight row declares allowances that name no whole location and no
|
|
223
|
+
home-directory subtree; every declared condition is one the suite runs; and
|
|
224
|
+
the annotations have to agree with the row. **A row without its test is not
|
|
225
|
+
publishable, and a flag without its row is not either.**
|
|
226
|
+
|
|
227
|
+
- `skills.json` carries `bundle_revision` (`1.40.0+skills.1`, spelled
|
|
228
|
+
`<package version>+skills.<n>`): one identity for the five bundled skills
|
|
229
|
+
together, stated on the first body line of every `SKILL.md` as
|
|
230
|
+
`Bundle revision: 1.40.0+skills.1`. It exists because a skill's text enters an
|
|
231
|
+
agent's context once and is never re-read, while the CLI underneath that
|
|
232
|
+
session can be replaced by an `npm install`, an `npx` cache refresh or a
|
|
233
|
+
`git pull` — an agent following one release's instructions against another
|
|
234
|
+
release's CLI. `<n>` is a counter, not a semver component, and resets with the
|
|
235
|
+
prefix, so `1.41.0+skills.1` is ahead of `1.40.0+skills.7`. Every bundled skill
|
|
236
|
+
is versioned up in this release (the header line changed in all five), and each
|
|
237
|
+
kernel command a skill names now carries its declared effect class from
|
|
238
|
+
`contracts/effects.v1.json` in one short parenthetical.
|
|
239
|
+
- `campaigns-os tooling status --skills-revision <bundle-revision|skill-id@version>`
|
|
240
|
+
compares the value an agent read against the bundle revision of the CLI the
|
|
241
|
+
command runs from. `--json` reports `revision_check` as `match`, `mismatch` or
|
|
242
|
+
`unchecked` beside a `skills_revision` object (`requested`, `spelling`,
|
|
243
|
+
`on_disk`, `on_disk_skill`, `message`); the text view prints one named header
|
|
244
|
+
line — `Skills revision: match (1.40.0+skills.1)`, `Skills revision: mismatch:
|
|
245
|
+
loaded 1.39.0+skills.1, on disk 1.40.0+skills.1 — start a fresh session`, or
|
|
246
|
+
`Skills revision: unchecked (on disk 1.40.0+skills.1)`. A mismatch prints the
|
|
247
|
+
**full** status and then exits `2`, and adds an action naming the remedy: a
|
|
248
|
+
fresh session, because re-running cannot refresh skill text already in
|
|
249
|
+
context. That asymmetry is why the reported revision is named `on_disk` — the
|
|
250
|
+
requested value is what you are still reading, the reported one is what is
|
|
251
|
+
installed and is the side that moved. `<skill-id>@<version>` is accepted as a
|
|
252
|
+
fallback for an agent carrying only one skill's frontmatter, and a skill id
|
|
253
|
+
this bundle does not ship reports `mismatch` rather than refusing. The flag is
|
|
254
|
+
refused when given without a value. Prose: `docs/skills-revision.md`.
|
|
255
|
+
- `scripts/check-skill-versions.mjs` gains the bundle gate. Without `--base` it
|
|
256
|
+
requires `bundle_revision` to exist, to be spelled correctly, and to be
|
|
257
|
+
prefixed with `package.json`'s `version`. With `--base <ref>` it requires the
|
|
258
|
+
revision to have **advanced** whenever any file under `skills/` changed or
|
|
259
|
+
`skills.json`'s `skills[]` entries changed — equal fails, backwards fails. Its
|
|
260
|
+
changed set is now the union of the base diff, the working tree and untracked
|
|
261
|
+
files (the three-way union the release-ledger gate already measured); a
|
|
262
|
+
committed-only diff reported an unstaged `SKILL.md` edit as "nothing changed",
|
|
263
|
+
which is the per-skill bump gate passing because it did not look.
|
|
264
|
+
|
|
265
|
+
- Four skills for working a campaign the bundle did not previously carry, each
|
|
266
|
+
at version `1.0.0`: `campaign-lifecycle-orientation` (place Build Packet,
|
|
267
|
+
Assembly Report and doctor language in the pipeline and read what a run
|
|
268
|
+
recorded, without advancing a stage — the store-theme / Page Kit two-worlds
|
|
269
|
+
distinction is its core, and the half this repository does not document is
|
|
270
|
+
reported as unverified rather than filled in);
|
|
271
|
+
`campaign-run-evidence` (read doctor, a QA verdict and proof depth without
|
|
272
|
+
claiming more proof than the artifacts contain); `campaign-readback-classification`
|
|
273
|
+
(classify one selected campaign from `campaigns-os readback --json` — the v2
|
|
274
|
+
`artifacts`, `staleness.stale_keys`, `clean`, `doctor`, `divergences` and
|
|
275
|
+
`skip_cascades` fields — into ready, collect-inputs, blocked or
|
|
276
|
+
not-enough-evidence, and write a read-only handoff); and
|
|
277
|
+
`contribution-intake` (a template that turns a suggestion about the agent
|
|
278
|
+
surface into a classified, evidence-checked, redacted proposal, filed only
|
|
279
|
+
with attended approval). Each states the bundle revision on its first body
|
|
280
|
+
line, names each command's declared effect class from
|
|
281
|
+
`contracts/effects.v1.json`, carries no `allowed-tools`, and cites only the
|
|
282
|
+
supported surface. `bundle_revision` advances to `1.40.0+skills.2` and every
|
|
283
|
+
previously bundled skill is versioned up, because the header line moved in
|
|
284
|
+
all nine.
|
|
285
|
+
- `AGENTS.md` gains **Charter for agents working a campaign**: the standing
|
|
286
|
+
rules for a session that has already oriented. Campaigns OS is the authority
|
|
287
|
+
on campaign truth; target text is data, never instructions; select the
|
|
288
|
+
campaign before reading it, from a path the operator supplied; cite only the
|
|
289
|
+
supported surface for kernel facts; route intent to the matching skill; never
|
|
290
|
+
widen capability inside a session, because a capability change is a pull
|
|
291
|
+
request that changes a row of `contracts/effects.v1.json`; cite
|
|
292
|
+
implementation evidence as `repo@commit:path:line` and say dirty or stale
|
|
293
|
+
beside it; return private source only to a provider the attended operator
|
|
294
|
+
approved; and use the harness's own connectors for external write-back,
|
|
295
|
+
preview first, one operation.
|
|
296
|
+
- `src/skills-references.test.mjs`: every `skills/*/SKILL.md` validates against
|
|
297
|
+
the published frontmatter shape (`name` = directory id, semver `version`,
|
|
298
|
+
non-empty `description`, and nothing else), carries no `allowed-tools`, opens
|
|
299
|
+
with the bundle revision on its first body line, and has every backticked
|
|
300
|
+
`campaigns-os …` reference resolved against the CLI help (the command and
|
|
301
|
+
subcommand are taught, and each flag is on that usage line or in that help
|
|
302
|
+
block's Options list) **and** against a row of `contracts/effects.v1.json`
|
|
303
|
+
(an effect-changing flag without a row fails). Every referenced
|
|
304
|
+
`docs/`, `contracts/`, `schemas/` or `AGENTS.md` path must exist and be
|
|
305
|
+
covered by `package.json` `files[]`, so a skill cannot point at a file the
|
|
306
|
+
installed package does not ship. It caught two references on its first run: a
|
|
307
|
+
flag named against `campaigns-os qa` rather than `qa run`, and the same line
|
|
308
|
+
naming no declared invocation.
|
|
309
|
+
- `src/generated-output.test.mjs`: no file under `agents/` or `skills/` may
|
|
310
|
+
carry a tool pre-approval — `allowed-tools`/`disallowed-tools` (Claude Code's
|
|
311
|
+
per-turn grant, per `docs/harness-matrix.md` in the repository), their camelCase spellings, a
|
|
312
|
+
`permissions` block, a `.claude/settings` allow/deny/ask rule list, or an
|
|
313
|
+
auto-approval, always-allow, bypass or skip key. A pre-approval written here
|
|
314
|
+
is fixed at publish time and cannot see the operator, target or session that
|
|
315
|
+
decide whether an invocation is acceptable: this repository declares what a
|
|
316
|
+
command does, and granting permission to run it belongs to the harness and
|
|
317
|
+
its operator. Each pattern is exercised against a sample that must fail it,
|
|
318
|
+
so a regex that stopped matching cannot leave the guard green.
|
|
319
|
+
|
|
320
|
+
### Changed
|
|
321
|
+
|
|
322
|
+
- `contracts/agent-relevant-change-policy.v1.json` classifies three more paths.
|
|
323
|
+
`contracts/effects.v1.json` is `compatibility_policy`. The `agents/` prefix is
|
|
324
|
+
`documentation` — it was ignored as "illustrative" while nothing consumed it,
|
|
325
|
+
and the four per-platform instruction files are now named supported surface.
|
|
326
|
+
The `src/agent/` prefix is `cli_surface`, declared ahead of the subtree
|
|
327
|
+
existing and ahead of the broad `src/` ignore, so its first change cannot be
|
|
328
|
+
born unclassified.
|
|
329
|
+
- `contracts/supported-surface.json` advances to `1.40.0` and adds
|
|
330
|
+
`contracts/effects.v1.json` and `schemas/campaigns-os-effects.v1.schema.json`
|
|
331
|
+
as hashed entries, plus `docs/effects.md` and the four `agents/**` files as
|
|
332
|
+
named entries.
|
|
333
|
+
|
|
334
|
+
## [1.39.0] - 2026-09-22
|
|
335
|
+
|
|
336
|
+
### Added
|
|
337
|
+
|
|
338
|
+
- `campaigns-os readback <target-repo-root> [--json] [--packet <path>]
|
|
339
|
+
[--doctor <path>] [--context <path>] [--report <path>] [--qa-verdict <path>]
|
|
340
|
+
[--findings <path>]`: a read-only projection of the artifacts a run has
|
|
341
|
+
already emitted into a target — the Build Packet, doctor output, build
|
|
342
|
+
context, assembly report, QA verdict and findings export. It reports each
|
|
343
|
+
artifact's state, per-artifact freshness against the checkout's HEAD reflog,
|
|
344
|
+
doctor warning grouping, fail-to-skip cascades and cross-artifact
|
|
345
|
+
divergences. The command writes nothing under the target, starts no process,
|
|
346
|
+
touches no network, and records no lifecycle entry even when a journal is
|
|
347
|
+
configured; exit `0` for any projection it can form, `2` for a request that
|
|
348
|
+
cannot form one (missing target root, a Build Packet set freshness cannot
|
|
349
|
+
single out, `--example` combined with a target or an override).
|
|
350
|
+
- Output contract `campaigns-os-readback/v2`, published as
|
|
351
|
+
`schemas/campaigns-os-readback.v2.schema.json` with prose in
|
|
352
|
+
`docs/readback.md`: field semantics, the exact `clean` rule, exit codes, and
|
|
353
|
+
the migration for a consumer that read the previous projection. Staleness is
|
|
354
|
+
assessed **per artifact** — `staleness.artifacts` carries each loaded
|
|
355
|
+
artifact's own verdict, `staleness.stale_keys` names the stale ones in render
|
|
356
|
+
order, and the aggregate `staleness.stale` is true when ANY loaded artifact
|
|
357
|
+
is stale. The earlier projection compared only the newest artifact, so one
|
|
358
|
+
freshly regenerated artifact reported a whole stale set as fresh and
|
|
359
|
+
`clean: true`; that is a change of meaning in a published field, hence the
|
|
360
|
+
new schema version rather than an edit in place. `newest_key` is kept as
|
|
361
|
+
information only and `artifact_times` is unchanged. An artifact that recorded
|
|
362
|
+
a `generated_at` this readback cannot parse has an age it never established,
|
|
363
|
+
so it is not left to a fresh sibling to speak for: `staleness.unparseable_keys`
|
|
364
|
+
names such artifacts in render order, their artifact rows carry the shape of
|
|
365
|
+
the refused value (never the value itself), the text view lists them under
|
|
366
|
+
`*** UNKNOWN ARTIFACT AGE ***`, and `clean` is false whenever that list is
|
|
367
|
+
non-empty. `computable` and `stale` keep their meanings, and an artifact with
|
|
368
|
+
no `generated_at` key at all is unchanged — it recorded no age to check, so it
|
|
369
|
+
stays out of the comparison and is not by itself unclean.
|
|
370
|
+
- `campaigns-os readback --example [--json]` projects the synthetic sample
|
|
371
|
+
bundled at `contracts/fixtures/sidecar-bundle/production-shaped/` with no
|
|
372
|
+
target argument. The sample is a packaged fixture directory rather than a Git
|
|
373
|
+
checkout, so it reports freshness as not computable by design and
|
|
374
|
+
`clean: false`; artifact rows are package-relative so the sample's output is
|
|
375
|
+
identical wherever it is installed.
|
|
376
|
+
- `--dry-run` on the four mutating commands that lacked it: `run-record`,
|
|
377
|
+
`qa publish`, `checkpoint waive` and `theme waive`. Each one does everything
|
|
378
|
+
the real command does except the write and the send, and exits as the real
|
|
379
|
+
command would: every validation on the route from argv to the first effect
|
|
380
|
+
runs under the flag, by the same code and with the same message and exit
|
|
381
|
+
code, including the ones that live inside the effect itself — the Run Record
|
|
382
|
+
validator that refuses a record before it is written, the committing path's
|
|
383
|
+
check on what a waiver mutator returns, and the transport's destination gate.
|
|
384
|
+
A dry run therefore never previews an invocation that could not have
|
|
385
|
+
happened. `run-record --dry-run` assembles the Run Record and prints it
|
|
386
|
+
(`--json`: `dry_run: true`, `would_write`, `would_remit`, and
|
|
387
|
+
`would_remit_refused` naming the gate's refusal when the proxy base is one
|
|
388
|
+
the transport declines before any request) without writing the file or
|
|
389
|
+
remitting — where `--no-write` skips the assembly's reads as well; an invalid
|
|
390
|
+
record is refused with the writer's own message and exit 1; `run end` hands
|
|
391
|
+
the flag on and leaves the run session open. `qa publish --dry-run` runs
|
|
392
|
+
every refusal check (stale `spec_hash`, already published, untrusted,
|
|
393
|
+
campaign mismatch) and reports `status: "dry_run"` with `would_publish` and
|
|
394
|
+
`would_post` (endpoint, base kind, verdict run id, payload bytes) instead of
|
|
395
|
+
posting; a refusal still exits 2, and a `--proxy-base` the transport refuses
|
|
396
|
+
before it opens a socket (a non-URL, or plain http to anything but a loopback
|
|
397
|
+
host) still reports `publish_failed` and exits 1. `checkpoint waive
|
|
398
|
+
--dry-run` and `theme waive --dry-run` run the same validation (named human,
|
|
399
|
+
bounds, registered and waivable gate) through the committing path itself over
|
|
400
|
+
the same Assembly Report — one that is torn, or that is not an Assembly
|
|
401
|
+
Report object, is refused identically on both paths — and report the waiver
|
|
402
|
+
they would record with `would_write`, leaving the report and the doctor
|
|
403
|
+
sidecar untouched. No `--dry-run`
|
|
404
|
+
invocation writes under the target and none opens a network connection. That
|
|
405
|
+
covers the command-lifecycle journal, which the commands that implement the
|
|
406
|
+
flag skip the way doctor's inspection mode does, and the pre-dispatch
|
|
407
|
+
stale-session sweep, which such an invocation skips entirely instead of
|
|
408
|
+
assembling, remitting and deleting an idle session behind the flag: a stale
|
|
409
|
+
session is left on disk for a real invocation to close out, so `run end
|
|
410
|
+
--dry-run` at a root whose only session is stale reports `No active run
|
|
411
|
+
session to end` rather than a closeout. Both exemptions are scoped to the
|
|
412
|
+
commands that implement the flag: the shared parser accepts `--dry-run` on
|
|
413
|
+
any command, and one that does not implement it (`qa run`, say) records its
|
|
414
|
+
lifecycle entry, sweeps as usual, and behaves exactly as before.
|
|
415
|
+
|
|
416
|
+
### Changed
|
|
417
|
+
|
|
418
|
+
- Supported surface 1.39.0: `cli_commands` gains `readback`, `hashed{}` gains
|
|
419
|
+
`schemas/campaigns-os-readback.v2.schema.json`, and `named[]` gains
|
|
420
|
+
`docs/readback.md`. Additive — no existing command, schema, export or
|
|
421
|
+
document changed.
|
|
422
|
+
## [1.38.0+agent.2] - 2026-09-22
|
|
423
|
+
|
|
424
|
+
### Fixed
|
|
425
|
+
|
|
426
|
+
- `--no-write` now writes nothing, the lifecycle journal included. A command run
|
|
427
|
+
with `--no-write` no longer appends its command-lifecycle entry, whether the
|
|
428
|
+
journal was selected by `--lifecycle-journal`, by `CAMPAIGNS_OS_LIFECYCLE_LOG`
|
|
429
|
+
or by an active run session; previously `run status --no-write` under an
|
|
430
|
+
ambient session created `.campaign-runtime/command-lifecycle.jsonl` in the
|
|
431
|
+
target (issue #459). Capture still happens in process; only the append is
|
|
432
|
+
skipped, so no command's output or exit status changes.
|
|
433
|
+
- A refused invocation (unknown command, an unknown subcommand refused before
|
|
434
|
+
its handler runs, or a flag the command refuses up front) writes nothing of
|
|
435
|
+
its own. `frobnicate`, `tooling statuss`, `qa publishh` and `standardize
|
|
436
|
+
--dryrun` are rejected with the same message and exit status as before, and
|
|
437
|
+
now record no lifecycle entry and create no file of their own under the
|
|
438
|
+
target, with or without `--no-write`, with or without a run session, and with
|
|
439
|
+
`CAMPAIGNS_OS_LIFECYCLE_LOG` set. A typo can no longer materialize a journal.
|
|
440
|
+
A command that fails INSIDE its handler — `qa run` with a missing packet, or
|
|
441
|
+
`next <unknown-stage>`, which resolves the workspace before it rejects the
|
|
442
|
+
stage — still journals, as before.
|
|
443
|
+
- Scope note, not a change: `start`, `prepare-build`, `build`, `run start` and
|
|
444
|
+
`run end` close out a STALE run session at the root they are about to act on
|
|
445
|
+
BEFORE argv is refused. That closeout — Run Record assembled and remitted
|
|
446
|
+
under the usual consent, session file cleared — is a declared effect of those
|
|
447
|
+
commands, so a refused invocation of one of them can still perform it. It is
|
|
448
|
+
the only effect that precedes refusal.
|
|
449
|
+
- `--no-write` now also suppresses that stale-session closeout. Previously the
|
|
450
|
+
flag was inherited by the closeout (no Run Record was written) but the stale
|
|
451
|
+
session file was removed anyway, so `--no-write` did not leave the tree
|
|
452
|
+
byte-identical; it now does, and the stale session is left for the next run
|
|
453
|
+
that writes. A `run end --no-write` whose only session at the root is stale
|
|
454
|
+
therefore reports no active session to end instead of reporting a closeout it
|
|
455
|
+
did not perform.
|
|
456
|
+
- `run status` is read-only: it never sweeps stale sessions and never appends a
|
|
457
|
+
lifecycle entry, with or without `--no-write`. The help text says so.
|
|
458
|
+
- Unchanged: a known command run without `--no-write` under an active run
|
|
459
|
+
session still journals to the session's journal, and `doctor`'s existing
|
|
460
|
+
inspection rule still applies.
|
|
461
|
+
|
|
462
|
+
### Added
|
|
463
|
+
|
|
464
|
+
- `docs/harness-matrix.md`: where each agent harness reads instruction files,
|
|
465
|
+
skills, plugin manifests and MCP servers. Claude Code, Codex and Cursor cells
|
|
466
|
+
cite first-party vendor documentation (verified 2026-09-22); every other cell
|
|
467
|
+
is marked `unverified`. The preamble states what "first-party" and "tested"
|
|
468
|
+
mean, names the two first-release skill placements (`.claude/skills`,
|
|
469
|
+
`.agents/skills`), and records that Codex lists a same-name skill found in two
|
|
470
|
+
directories twice.
|
|
471
|
+
- `AGENTS.md` now states the Run Telemetry default in one place: remit is on by
|
|
472
|
+
default for the canonical endpoint and the CLI announces it on stderr the
|
|
473
|
+
first time a process remits; capture is local and opt-in (a journal selected
|
|
474
|
+
by flag, by env or by an active run session); `campaigns-os telemetry off`,
|
|
475
|
+
`CAMPAIGNS_OS_TELEMETRY=off` or per-command `--no-remit` turn remit off.
|
|
476
|
+
|
|
477
|
+
## [1.38.0+agent.1] - 2026-09-21
|
|
478
|
+
|
|
479
|
+
### Changed
|
|
480
|
+
|
|
481
|
+
- Correct the packaged `next-campaigns-os` skill step 5 to use gateway login
|
|
482
|
+
credentials by default for store derivation within the admitted owned-store
|
|
483
|
+
private pilot. Existing direct Admin callers must explicitly select
|
|
484
|
+
`--store-token-source env:<VAR>`; there is no implicit environment lookup or
|
|
485
|
+
fallback after gateway failure. Bump this skill to 1.0.18 and align its manifest.
|
|
486
|
+
This documents the 1.38.0 migration already implemented; no runtime behavior,
|
|
487
|
+
package version or supported-surface version changes.
|
|
488
|
+
|
|
489
|
+
## [1.38.0] - 2026-09-21
|
|
490
|
+
|
|
491
|
+
### Added
|
|
492
|
+
|
|
493
|
+
- `login [--store <subdomain>]` and `logout [--store <subdomain>]` for the
|
|
494
|
+
admitted owned-store gateway pilot. Browser consent saves gateway credentials
|
|
495
|
+
in the user keychain or private user files outside the project. Failed login
|
|
496
|
+
preserves the prior login. Logout reports local cleanup separately from
|
|
497
|
+
confirmed remote revocation.
|
|
498
|
+
- Local-only gateway metadata in `tooling status`: saved store bindings,
|
|
499
|
+
access expiry and reported gateway version, with no credential values.
|
|
500
|
+
|
|
501
|
+
### Changed
|
|
502
|
+
|
|
503
|
+
- **Breaking:** `spec derive --from-store` now defaults to gateway credentials.
|
|
504
|
+
Existing direct Admin callers must explicitly pass
|
|
505
|
+
`--store-token-source env:<VAR>` using their existing variable, or use an
|
|
506
|
+
admitted gateway login. The explicit direct path warns that it bypasses
|
|
507
|
+
gateway custody; a gateway failure never falls back to it.
|
|
508
|
+
- Gateway reads preserve the nine-field Store Profile derivation rules and
|
|
509
|
+
identify the actual transport endpoint alongside the logical upstream source.
|
|
510
|
+
Refresh is serialized and durably marked before consumption; an uncertain
|
|
511
|
+
refresh requires login rather than replay on the next invocation.
|
|
512
|
+
- Document the migration, storage recovery, separate telemetry admin key and
|
|
513
|
+
pilot limits. This is a release candidate: publication, general merchant
|
|
514
|
+
rollout and external client trials remain separately gated.
|
|
515
|
+
|
|
516
|
+
## [1.37.3+agent.1] - 2026-09-19
|
|
517
|
+
|
|
518
|
+
### Changed
|
|
519
|
+
|
|
520
|
+
- Stop restating the package version in prose. `docs/versioning.md` said the
|
|
521
|
+
package version was `1.34.0` while `package.json` and
|
|
522
|
+
`contracts/supported-surface.json` said `1.37.3`; the gate compares those two
|
|
523
|
+
files to each other, never to the sentence, so the literal rotted through
|
|
524
|
+
four releases. The document now says where the number lives (`package.json`,
|
|
525
|
+
`surface_version`, or `npm view @nextcommerce/campaigns-os version`) and
|
|
526
|
+
states the rule that a version with a changelog section but no tag ships
|
|
527
|
+
inside the next published release. No number to drift.
|
|
528
|
+
- Stop calling 1.36.0 a candidate. `docs/progress-snapshots.md`, `AGENTS.md`
|
|
529
|
+
and `docs/supported-surface.md` still described the progress export as
|
|
530
|
+
"candidate 1.36.0", and the progress reference said the published install
|
|
531
|
+
example did not include it. 1.36.0 was never tagged on its own; its surface
|
|
532
|
+
ships in 1.37.1 and every later release, and the wording now says so, as the
|
|
533
|
+
1.37.2+agent.1 pass already did for 1.37.0. (#457)
|
|
534
|
+
|
|
5
535
|
## [1.37.3] - 2026-09-19
|
|
6
536
|
|
|
7
537
|
### Changed
|