@natjswenson/press 0.5.1 → 0.6.1

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,42 @@ 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.1] - 2026-08-01
9
+
10
+ ### Fixed
11
+
12
+ - **Every propagate push failed on missing git credentials, and the job still
13
+ reported success.** `gh` authenticates its own API calls from `GH_TOKEN`, so
14
+ cloning and PR listing worked, but a plain `git push` has no credential helper
15
+ and fails with `could not read Username for 'https://github.com'`. All four
16
+ consumers failed this way on the 0.6.0 release. `gh auth setup-git` now wires
17
+ gh in as git's credential helper.
18
+ - **A failed fan-out now fails the job.** Failures were a warning, on the
19
+ reasoning that the release had already happened and the weekly run would
20
+ retry. That was wrong: a fan-out that silently does nothing is worse than one
21
+ that never ran, because the green tick claims the brand reached everywhere
22
+ when it reached nowhere.
23
+
24
+ ## [0.6.0] - 2026-08-01
25
+
26
+ ### Changed
27
+
28
+ - **A consumer's pin, its region receipt and the current release are now kept
29
+ equal.** Previously propagation acted only when generated *values* moved, so
30
+ a repo could show three different version numbers at once — natejswenson.io
31
+ read pin 0.2.0, receipt 0.1.0, latest 0.5.1 — and there was no way to tell a
32
+ healthy consumer from a stale one without knowing the rule. Every release now
33
+ opens a PR in every consumer.
34
+
35
+ What keeps that readable is that the PR says which kind it is: a **brand
36
+ change** (`press vX — BRAND VALUES CHANGED`) is reviewed as a design change,
37
+ while a **version-only adoption** (`adopt press vX`) is a one-line diff where
38
+ nothing renders differently. `propagate --json` reports the two separately as
39
+ `brand` and `stale`.
40
+
41
+ The trade is deliberate: a few no-op pull requests per release, in exchange
42
+ for state anyone can read at a glance.
43
+
8
44
  ## [0.5.1] - 2026-08-01
9
45
 
10
46
  ### Fixed
@@ -228,6 +264,8 @@ source of truth and a CI drift gate.
228
264
  rasterised cards, whose eyebrow legitimately runs at `.16em`. Scoped rather
229
265
  than waived, and caught by linting the real shipped corpus.
230
266
 
267
+ [0.6.1]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.6.1
268
+ [0.6.0]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.6.0
231
269
  [0.5.1]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.5.1
232
270
  [0.5.0]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.5.0
233
271
  [0.4.1]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.4.1
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.1
5
+ version: 0.6.1
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.1",
3
+ "version": "0.6.1",
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",