@natjswenson/press 0.5.0 → 0.6.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 CHANGED
@@ -5,6 +5,38 @@ All notable changes to the **press** skill are documented here.
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
6
6
  this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.6.0] - 2026-08-01
9
+
10
+ ### Changed
11
+
12
+ - **A consumer's pin, its region receipt and the current release are now kept
13
+ equal.** Previously propagation acted only when generated *values* moved, so
14
+ a repo could show three different version numbers at once — natejswenson.io
15
+ read pin 0.2.0, receipt 0.1.0, latest 0.5.1 — and there was no way to tell a
16
+ healthy consumer from a stale one without knowing the rule. Every release now
17
+ opens a PR in every consumer.
18
+
19
+ What keeps that readable is that the PR says which kind it is: a **brand
20
+ change** (`press vX — BRAND VALUES CHANGED`) is reviewed as a design change,
21
+ while a **version-only adoption** (`adopt press vX`) is a one-line diff where
22
+ nothing renders differently. `propagate --json` reports the two separately as
23
+ `brand` and `stale`.
24
+
25
+ The trade is deliberate: a few no-op pull requests per release, in exchange
26
+ for state anyone can read at a glance.
27
+
28
+ ## [0.5.1] - 2026-08-01
29
+
30
+ ### Fixed
31
+
32
+ - **Propagation targeted the default branch, not the integration branch.** Every
33
+ consumer runs `feature → dev → main`, so a PR opened straight into `main`
34
+ would violate its own flow. Worse, the default branch can legitimately lag:
35
+ the first real fan-out reported `budget` and `local-fitness` as *missing* only
36
+ because their migration was still sitting on `dev`. It now checks out `dev`
37
+ when the remote has one and falls back to the default branch otherwise.
38
+ - The clone was `--depth=1`, which cannot see other branches. Full clone now.
39
+
8
40
  ## [0.5.0] - 2026-08-01
9
41
 
10
42
  The last hand-maintained copy of the brand is gone.
@@ -216,6 +248,8 @@ source of truth and a CI drift gate.
216
248
  rasterised cards, whose eyebrow legitimately runs at `.16em`. Scoped rather
217
249
  than waived, and caught by linting the real shipped corpus.
218
250
 
251
+ [0.6.0]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.6.0
252
+ [0.5.1]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.5.1
219
253
  [0.5.0]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.5.0
220
254
  [0.4.1]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.4.1
221
255
  [0.4.0]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.4.0
package/SKILL.md CHANGED
@@ -2,7 +2,7 @@
2
2
  name: press
3
3
  description: The one brand system for everything produced in Claude — design tokens, the visual laws, the run-presentation contract, and the universal voice core. Use when composing or restyling any artifact (report, résumé, card, cover, PDF, HTML page, chart), when asked about brand colors, fonts, the accent law or "the PRESS look", when adding a new skill that renders anything, or when a brand value needs to change everywhere at once. Also handles "check the brand is in sync", "why do my colors differ", and onboarding a new consumer repo.
4
4
  user_invocable: true
5
- version: 0.5.0
5
+ version: 0.6.0
6
6
  ---
7
7
 
8
8
  # /press — the brand system
@@ -108,13 +108,18 @@ node bin/press.js propagate --repo ../budget # re-emit + bump its pi
108
108
  what moved. The caller turns that into a pull request, so nothing lands
109
109
  unreviewed — but nobody has to *remember*.
110
110
 
111
- Two rules keep it from becoming noise or a false negative:
111
+ **One version per repo.** A consumer's CI pin, its region receipt and the
112
+ current release are kept equal, so "is this repo up to date?" is answerable by
113
+ looking. The earlier rule — only act when values move — left three divergent
114
+ version numbers per repo and no way to tell a healthy consumer from a stale one.
112
115
 
113
- - **Content decides, not the version receipt.** A region written by 0.1.0 that
114
- is still byte-correct under 0.3.0 is *current*. Opening a PR for it would be
115
- noise.
116
- - **A stale pin alone is not "behind".** It changes no shipped artifact, so it
117
- is bumped silently and never triggers a PR on its own.
116
+ Every release therefore opens a PR in every consumer. What keeps that readable
117
+ is that the PR says which kind it is:
118
+
119
+ | Kind | Title | Review |
120
+ |---|---|---|
121
+ | **Brand values moved** | `press vX — BRAND VALUES CHANGED` | read the diff as a design change |
122
+ | **Version only** | `adopt press vX` | one line; nothing renders differently |
118
123
 
119
124
  `.github/workflows/press-propagate.yml` runs this on every release and weekly,
120
125
  opening a PR in each consumer repo whose bytes actually moved.
package/lib/propagate.mjs CHANGED
@@ -52,19 +52,35 @@ export function propagate({ tokens, targets, root, version, dryRun = false }) {
52
52
  }
53
53
  const body = emitBody(tokens, target.emitter, target.params ?? {});
54
54
  const after = spliceRegion(before, target.region, target.syntax, body, version);
55
- // Compare the region bodies, not whole files: the start marker carries the
56
- // emitting version, so a no-op release would otherwise look like a change
57
- // and open a pull request that alters nothing anyone can see.
58
- const changed = found.body.replace(/\s+$/, '') !== body.replace(/\s+$/, '');
55
+ // Two different kinds of "changed", kept apart on purpose.
56
+ //
57
+ // brand the generated VALUES moved something renders differently and
58
+ // the diff must be reviewed as a design change.
59
+ // version only the marker's recorded version moved. Nothing renders
60
+ // differently; the repo is adopting a newer press.
61
+ //
62
+ // Both are written. Skipping the write when only the version moved is what
63
+ // produced three divergent version numbers per repo — pin, region receipt
64
+ // and current release all disagreeing, with no way to tell a healthy
65
+ // consumer from a stale one at a glance.
66
+ const brandChanged = found.body.replace(/\s+$/, '') !== body.replace(/\s+$/, '');
67
+ const versionChanged = found.version !== version;
68
+ const changed = brandChanged || versionChanged;
59
69
  if (changed && !dryRun) writeFileSync(path, after, 'utf8');
60
70
  regions.push({
61
71
  id: target.id,
62
72
  path: target.path,
63
- // `changed` is carried as a boolean, never re-derived from the status
64
- // string: matching /updated$/ silently missed "would update", so a
65
- // dry run reported "nothing to do" while showing a changed region.
73
+ // Carried as booleans, never re-derived from the status string: matching
74
+ // /updated$/ once silently missed "would update", so a dry run reported
75
+ // "nothing to do" while showing a changed region.
66
76
  changed,
67
- status: changed ? (dryRun ? 'would update' : 'updated') : 'current',
77
+ brandChanged,
78
+ versionChanged,
79
+ status: brandChanged
80
+ ? (dryRun ? 'would change brand' : 'brand updated')
81
+ : versionChanged
82
+ ? (dryRun ? 'would adopt' : 'adopted')
83
+ : 'current',
68
84
  wroteBy: found.version,
69
85
  });
70
86
  }
@@ -76,9 +92,15 @@ export function propagate({ tokens, targets, root, version, dryRun = false }) {
76
92
  version,
77
93
  regions,
78
94
  pins,
79
- // A repo is behind if its bytes moved. A pin bump alone is not worth a pull
80
- // request it changes no shipped artifact.
81
- changed: regions.some((r) => r.changed),
95
+ // A repo needs a pull request if anything moved at all values or version
96
+ // — so that its pin, its region receipt and the current release stay one
97
+ // readable number. Letting the receipt lag "because the body did not
98
+ // change" produced three divergent versions per repo and no way to tell a
99
+ // healthy consumer from a stale one at a glance.
100
+ changed: regions.some((r) => r.changed) || pins.some((p) => p.status !== 'current'),
101
+ // The subset a human must actually look at: these change what renders.
102
+ // Everything else is routine adoption and reviews in seconds.
103
+ brand: regions.filter((r) => r.brandChanged).map((r) => r.id),
82
104
  stale: regions.filter((r) => r.changed).map((r) => r.id),
83
105
  missing: regions.filter((r) => r.status === 'missing').map((r) => r.id),
84
106
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@natjswenson/press",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "One brand system — tokens, laws, run presentation and voice core — generated into every consumer with a CI drift gate",
5
5
  "license": "MIT",
6
6
  "author": "Nate Swenson",