@mriekki/lean 0.3.1 → 0.3.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/README.md CHANGED
@@ -6,12 +6,7 @@ Canonical engineering remains in the private platform repositories. Before publi
6
6
 
7
7
  ## Current `greenfield-poc` profile
8
8
 
9
- The current profile bundles:
10
-
11
- - Lean Agentic Core `1.11.1` from commit `e3a8a994d1e95923d629af7640d3b3a0a4f098d4`, distribution `greenfield-workspace`;
12
- - Lean POC Deployment Kit commit `4c23140576b39608a3ef285cad4abe53dccb9c47`, distribution `greenfield-poc`.
13
-
14
- Core `1.11.1` tightens `/lean-update` for Bootstrap-generated package-managed consumers: it keeps `POC_BOOTSTRAP.json`, umbrella/component baselines, exact registry integrity, and generated package-verification surfaces coherent while retaining `/core-sync` as the compatibility/router path for component-only Core consumers.
9
+ The profile bundles Lean Agentic Core's `greenfield-workspace` distribution and the Lean POC Deployment Kit's `greenfield-poc` distribution. The package version and exact canonical component commits are recorded in [`assets/components.json`](./assets/components.json), which is the source of truth for each release.
15
10
 
16
11
  ## Developer experience
17
12
 
@@ -38,7 +33,7 @@ npx @mriekki/lean@latest status
38
33
  npx @mriekki/lean@latest check
39
34
  ```
40
35
 
41
- The umbrella package writes `LEAN_BASELINE.json` while preserving component-native `CORE_BASELINE.json` and `DEPLOYMENT_BASELINE.json` ledgers. Updates refuse managed drift/conflicts before modifying any component. Core retired paths require explicit ownership transfer with `--accept-retired core:<path>`; unchanged Deployment Kit retirements may be removed under its existing ownership contract.
36
+ The umbrella package writes `LEAN_BASELINE.json` while preserving component-native `CORE_BASELINE.json` and `DEPLOYMENT_BASELINE.json` ledgers. Updates refuse managed drift/conflicts before modifying any component. An unchanged retired managed path can be removed safely; a locally modified retired Core path stops the update until the operator explicitly transfers ownership with `--accept-retired core:<path>`.
42
37
 
43
38
  ## Safety and ownership
44
39
 
@@ -58,16 +53,16 @@ The package currently starts with the `greenfield-poc` consumer profile. Existin
58
53
  For normal releases in the Lean Platform VS Code workspace, use:
59
54
 
60
55
  ```text
61
- /release-lean [version | patch | minor | major]
56
+ /release-lean
62
57
  ```
63
58
 
64
- The repo-local skill resolves accepted component commits, prepares the version/pin/descriptor update, runs local verification, creates the release PR, waits for the package-PR merge gate, verifies post-merge `main` CI, then stops once at the exact npm publication gate.
59
+ The repo-local skill resolves accepted component commits, automatically selects the package version from the observed consumer-visible changes, prepares the version/pin/descriptor update, runs local verification, creates the release PR, waits for the package-PR merge gate, verifies post-merge `main` CI, then stops once at the exact npm publication gate. Explicit bump or exact-version arguments remain available only as unusual overrides.
65
60
 
66
61
  After publication approval it triggers `.github/workflows/npm-publish.yml`. The workflow reconstructs the package from the exact private component pins, runs the deterministic release preparation, publishes the exact tarball directly with npm Trusted Publishing (OIDC), and automatically verifies the registry identity plus a neutral-directory `npx` smoke check.
67
62
 
68
63
  The normal flow does not use staged npm publication or a long-lived npm publish token. An existing stage-only trusted publisher must be migrated once to allow direct `npm publish` from `npm-publish.yml`; `/release-lean` guides that bounded one-time setup.
69
64
 
70
- Before merging a release PR, verify the local npm maintainer login and trusted-publisher relationship; the release skill explains npm's browser/2FA reauthorization if `npm trust list` returns `EOTP`. GitHub Actions publication uses OIDC, not a manually managed npm token. See `docs/RELEASE_PROCESS.md` for the operator setup.
65
+ Routine releases do not require local npm authentication: GitHub Actions publishes through OIDC. npm login and trusted-publisher inspection are only needed for initial setup, migration, or troubleshooting a trust error. See `docs/RELEASE_PROCESS.md` for details.
71
66
 
72
67
  The underlying release-candidate command remains available for diagnostics:
73
68
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "package": "@mriekki/lean",
4
- "version": "0.3.1",
4
+ "version": "0.3.2",
5
5
  "defaultProfile": "greenfield-poc",
6
6
  "profiles": {
7
7
  "greenfield-poc": {
@@ -14,8 +14,8 @@
14
14
  "components": {
15
15
  "core": {
16
16
  "repository": "mriekki/lean-agentic-core",
17
- "commit": "009572aa4c61ad9a45ec5473b6affc984d6faac0",
18
- "version": "1.12.0",
17
+ "commit": "bcc3836b50713504aea1dc47b8ad26bf483315dd",
18
+ "version": "1.12.1",
19
19
  "distribution": "greenfield-workspace",
20
20
  "descriptor": "assets/core/distribution.json",
21
21
  "sourceRoot": "assets/generated/core",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "package": "@mriekki/lean",
4
- "version": "0.3.1",
4
+ "version": "0.3.2",
5
5
  "components": {
6
6
  "core": {
7
7
  "repository": "mriekki/lean-agentic-core",
8
- "commit": "009572aa4c61ad9a45ec5473b6affc984d6faac0",
9
- "version": "1.12.0",
8
+ "commit": "bcc3836b50713504aea1dc47b8ad26bf483315dd",
9
+ "version": "1.12.1",
10
10
  "distribution": "greenfield-workspace",
11
11
  "files": [
12
12
  {
@@ -82,7 +82,7 @@
82
82
  {
83
83
  "source": "core/skills/lean-update/SKILL.md",
84
84
  "destination": ".github/skills/lean-update/SKILL.md",
85
- "sha256": "8b4bce53b109a5959e862924b0a4afd931233a5094a1aef43c2f3672199108bb"
85
+ "sha256": "50bfa181bb9a6abc1d0bb6533ef0b29fe729a8a96924a34a7796c0cbb80061fc"
86
86
  },
87
87
  {
88
88
  "source": "core/skills/core-sync/SKILL.md",
@@ -109,9 +109,11 @@ Stop on unsafe states such as:
109
109
  - `LOCAL_MISSING`;
110
110
  - `UNTRACKED_CONFLICT`;
111
111
  - path collisions/conflicts;
112
- - unaccepted Core `RETIRED_PATH`.
112
+ - Core `RETIRED_DRIFT` when the retired file no longer matches its recorded managed hash.
113
113
 
114
- Core retired paths are an ownership-transfer decision. Never auto-add `--accept-retired core:<path>` merely to make the update pass. Explain the retired path and require a separate explicit ownership decision before re-previewing it.
114
+ A package-managed retired path that is unchanged from its recorded baseline may be classified as `REMOVE` and deleted safely; an already-missing retired path may be classified as `RETIRED_MISSING`. These states do not require a separate ownership decision because the baseline proves there is no consumer edit to preserve.
115
+
116
+ A modified retired Core path is different: never auto-add `--accept-retired core:<path>` merely to make the update pass. Explain that the file differs from its recorded Core-managed hash and require a separate explicit ownership-transfer decision before re-previewing it. When that exact transfer is accepted, the package updater preserves the file as consumer-owned content and drops it from the new Core baseline.
115
117
 
116
118
  For a Bootstrap-generated consumer, also preview the companion generated-consumer changes:
117
119
 
@@ -145,6 +147,7 @@ Choosing option 1 authorizes only:
145
147
 
146
148
  - creation/switch to one repository-appropriate short-lived update branch when needed;
147
149
  - exact target package apply;
150
+ - removal of unchanged retired package-managed files that the reviewed preview classified as `REMOVE`;
148
151
  - when `POC_BOOTSTRAP.json` is present, update of its exact `generation.lean.version` and `generation.lean.integrity` to the reviewed target while preserving package/profile;
149
152
  - migration of a recognized Bootstrap-generated package-verification block from duplicated current literals to the accepted manifest-driven form, only when the existing block still exactly matches the current approved metadata;
150
153
  - package/baseline/generated-consumer verification;
@@ -168,7 +171,7 @@ Apply using the same exact package version that was previewed, for example:
168
171
  npx --yes @mriekki/lean@<target-version> update --apply
169
172
  ```
170
173
 
171
- If explicit Core retired-path ownership transfer was separately approved, pass only the exact reviewed paths using the package's `--accept-retired core:<path>` contract. Never widen that set implicitly.
174
+ If explicit Core retired-path ownership transfer was separately approved, pass only the exact reviewed modified paths using the package's `--accept-retired core:<path>` contract. Never widen that set implicitly. Unchanged retired managed paths do not need this flag; the package updater removes them according to the reviewed `REMOVE` plan.
172
175
 
173
176
  For a Bootstrap-generated consumer, complete the reviewed companion metadata changes in the same bounded update before declaring success. Preserve every unrelated `POC_BOOTSTRAP.json` field. Never change package identity or profile as an incidental version update.
174
177
 
@@ -195,11 +198,12 @@ Confirm:
195
198
  - `LEAN_BASELINE.json` records the exact target package/version/profile;
196
199
  - component baseline commits/distributions match the umbrella baseline;
197
200
  - package check passes for the whole profile;
201
+ - unchanged retired managed files shown as `REMOVE` in the preview are actually absent after apply, while any explicitly transferred modified retired Core file remains present and is no longer Core-managed;
198
202
  - when `POC_BOOTSTRAP.json` exists, its package/version/profile agree with `LEAN_BASELINE.json` and its integrity equals the exact reviewed target registry integrity;
199
203
  - when the repository contains `tools/lean-package/check-managed-baselines.py`, that generated baseline checker passes after all companion metadata changes;
200
204
  - generated package-verification surfaces no longer contradict the approved manifest metadata;
201
205
  - consumer-owned content was not overwritten;
202
- - only intended package-managed changes, reviewed generated-consumer companion changes, and reviewed ownership transfers occurred.
206
+ - only intended package-managed changes, reviewed generated-consumer companion changes, reviewed removals, and reviewed ownership transfers occurred.
203
207
 
204
208
  If generated CI still embeds a different package version/integrity after the update, report `GENERATED LEAN VERIFICATION DRIFT` and stop before delivery.
205
209
 
@@ -213,7 +217,7 @@ Present the meaningful diff for human review. After acceptance, recommend `/deli
213
217
  - uses a different package version for apply than the reviewed preview;
214
218
  - performs a downgrade/rollback as an ordinary update;
215
219
  - overwrites unexplained managed drift;
216
- - auto-accepts Core retired-path ownership transfer;
220
+ - auto-accepts Core retired-path ownership transfer for a modified retired file;
217
221
  - silently repairs a pre-existing mismatch between `POC_BOOTSTRAP.json` and `LEAN_BASELINE.json`;
218
222
  - changes a generated consumer's package identity/profile or regenerates the repository as an incidental version update;
219
223
  - guesses at locally modified/unrecognized Bootstrap-generated verification logic;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mriekki/lean",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Public consumer distribution and updater for Lean engineering assets.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/engine.mjs CHANGED
@@ -3,7 +3,7 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { readJson } from './package-data.mjs';
5
5
 
6
- const CORE_SAFE = new Set(['SAME', 'ADD', 'CORE_UPDATE', 'UNTRACKED_MATCH', 'BASELINE_STALE']);
6
+ const CORE_SAFE = new Set(['SAME', 'ADD', 'CORE_UPDATE', 'UNTRACKED_MATCH', 'BASELINE_STALE', 'REMOVE', 'RETIRED_MISSING']);
7
7
  const DEPLOYMENT_SAFE = new Set(['SAME', 'ADD', 'KIT_UPDATE', 'UNTRACKED_MATCH', 'BASELINE_STALE', 'REMOVE', 'RETIRED_MISSING']);
8
8
 
9
9
  export function safeRelative(value, label = 'path') {
@@ -123,22 +123,24 @@ export function planComponent(packageData, componentId, targetRoot, acceptedReti
123
123
  for (const [retiredPath, item] of [...baseMap.entries()].sort(([a], [b]) => a.localeCompare(b))) {
124
124
  if (desired.has(retiredPath)) continue;
125
125
  const dst = resolveUnder(targetRoot, retiredPath, 'retired destination');
126
- if (componentId === 'core') {
127
- retired.push({ path: retiredPath, item, state: acceptedRetired.has(retiredPath) ? 'RETIRED_ACCEPTED' : 'RETIRED_PATH' });
128
- continue;
129
- }
130
126
  if (!fs.existsSync(dst)) {
131
127
  retired.push({ path: retiredPath, item, state: 'RETIRED_MISSING' });
128
+ continue;
129
+ }
130
+ const stat = fs.lstatSync(dst);
131
+ const currentHash = stat.isFile() && !stat.isSymbolicLink() ? sha256File(dst) : null;
132
+ if (currentHash === item.sha256) {
133
+ retired.push({ path: retiredPath, item, currentHash, state: 'REMOVE' });
134
+ } else if (componentId === 'core' && acceptedRetired.has(retiredPath)) {
135
+ retired.push({ path: retiredPath, item, currentHash, state: 'RETIRED_ACCEPTED' });
132
136
  } else {
133
- const stat = fs.lstatSync(dst);
134
- const currentHash = stat.isFile() && !stat.isSymbolicLink() ? sha256File(dst) : null;
135
- retired.push({ path: retiredPath, item, currentHash, state: currentHash === item.sha256 ? 'REMOVE' : 'RETIRED_DRIFT' });
137
+ retired.push({ path: retiredPath, item, currentHash, state: 'RETIRED_DRIFT' });
136
138
  }
137
139
  }
138
140
  const safe = componentId === 'core' ? CORE_SAFE : DEPLOYMENT_SAFE;
139
141
  const unsafe = rows.filter((row) => !safe.has(row.state)).map((row) => ({ state: row.state, path: row.path }));
140
142
  for (const item of retired) {
141
- const isSafe = componentId === 'core' ? item.state === 'RETIRED_ACCEPTED' : safe.has(item.state);
143
+ const isSafe = item.state === 'RETIRED_ACCEPTED' || safe.has(item.state);
142
144
  if (!isSafe) unsafe.push({ state: item.state, path: item.path });
143
145
  }
144
146
  return {
@@ -202,10 +204,8 @@ export function applyProfile(packageData, profilePlan, targetRoot, consumer, con
202
204
  fs.mkdirSync(path.dirname(dst), { recursive: true });
203
205
  fs.copyFileSync(row.bundlePath, dst);
204
206
  }
205
- if (plan.componentId === 'deployment') {
206
- for (const retired of plan.retired) {
207
- if (retired.state === 'REMOVE') fs.unlinkSync(resolveUnder(targetRoot, retired.path, 'retired destination'));
208
- }
207
+ for (const retired of plan.retired) {
208
+ if (retired.state === 'REMOVE') fs.unlinkSync(resolveUnder(targetRoot, retired.path, 'retired destination'));
209
209
  }
210
210
  }
211
211
  for (const plan of profilePlan.plans) {