create-pmos 0.10.0 → 0.10.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/README.md +18 -0
- package/package.json +1 -1
- package/payload/KIT-VERSION +1 -1
- package/workspace/lib/layout.js +16 -0
- package/workspace/package.json +7 -3
- package/workspace/public/app.js +7 -1
- package/workspace/server.js +16 -2
package/README.md
CHANGED
|
@@ -333,6 +333,24 @@ Refreshes the skills, concepts, and scripts only. Your records — `state/`, `pl
|
|
|
333
333
|
`okf/product/` — are yours, and an update never touches them. `pmos/KIT-VERSION` always says
|
|
334
334
|
exactly what you're running.
|
|
335
335
|
|
|
336
|
+
## What's new in 0.10.1
|
|
337
|
+
|
|
338
|
+
**Two fixes found by installing 0.10.0 and using it, rather than by reading the code.**
|
|
339
|
+
|
|
340
|
+
- **Upgrades will keep reaching you.** The launcher decides whether to reinstall the workspace by
|
|
341
|
+
comparing the app version it bundles against the one you have. 0.10.0 shipped the *kit* at 0.10.0
|
|
342
|
+
but the bundled app still labelled **0.9.1** — so the next release would have compared equal,
|
|
343
|
+
decided you were current, and **silently skipped the upgrade**. Which is precisely the failure
|
|
344
|
+
0.9.1 existed to fix. The two versions are now checked against each other by the test suite, so it
|
|
345
|
+
cannot drift again unnoticed.
|
|
346
|
+
- **Alignment no longer offers to edit its own notes.** The change-context digest quotes every change
|
|
347
|
+
you make, so it matched the "quotes text this change altered" rule on the very first save in any
|
|
348
|
+
repository — and topped the list of documents to realign. A generated file proposing to realign
|
|
349
|
+
itself. Machine-written state is now excluded from alignment entirely.
|
|
350
|
+
|
|
351
|
+
If you installed 0.10.0, run `npx create-pmos@latest workspace` once. Nothing was damaged by either
|
|
352
|
+
bug; the first would have cost you a future upgrade, and the second a confusing first suggestion.
|
|
353
|
+
|
|
336
354
|
## What's new in 0.10.0
|
|
337
355
|
|
|
338
356
|
**Editing a document now does something for the rest of them.**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Scaffold the PMOS portable kit — run the PMOS product-management workflow (initiative → lane-fit spec → contract → build → three gates, with work-type lanes for features, research, maintenance, incidents, and content) entirely inside your own repo, with all state as git-versioned files. `create-pmos workspace` also installs + launches the local Terminal Workspace GUI. No database, no network, no telemetry.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
package/payload/KIT-VERSION
CHANGED
package/workspace/lib/layout.js
CHANGED
|
@@ -163,9 +163,25 @@
|
|
|
163
163
|
return { layouts, currentId: DEFAULT_ID };
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
/* What a GET /api/layouts response entitles the client to DO. Split out as a pure function
|
|
167
|
+
because the original inline condition conflated two very different states — "this workspace
|
|
168
|
+
has never stored a layout" and "I could not read what it stored" — and answered both by
|
|
169
|
+
migrating and then WRITING, silently replacing a real stored document whenever a read failed.
|
|
170
|
+
A read failure must never author a write.
|
|
171
|
+
|
|
172
|
+
'migrate' — the server affirmatively says nothing is stored (first run here): R8 applies.
|
|
173
|
+
'use' — a stored document came back: adopt it.
|
|
174
|
+
'keep' — the answer is unusable (transport error, non-JSON, !ok, or no `stored` flag).
|
|
175
|
+
Hold the boot defaults in memory and WRITE NOTHING. */
|
|
176
|
+
function loadAction(doc) {
|
|
177
|
+
if (!doc || doc.ok !== true || typeof doc.stored !== 'boolean') return 'keep';
|
|
178
|
+
return doc.stored ? 'use' : 'migrate';
|
|
179
|
+
}
|
|
180
|
+
|
|
166
181
|
return {
|
|
167
182
|
SIDE, READER, SPLIT, FOLD_KEYS, STOCK_IDS, DEFAULT_ID,
|
|
168
183
|
SIDE_MIN, SIDE_MAX, READ_MIN, READ_MAX, BOTTOM_H,
|
|
169
184
|
isStock, stockLayout, stockLayouts, layout, sanitize, gridFor, hasEscape, migrateLegacy,
|
|
185
|
+
loadAction,
|
|
170
186
|
};
|
|
171
187
|
});
|
package/workspace/package.json
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmos-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "PMOS Terminal Workspace — local companion hosting real PTYs beside the library and gates (D62)",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"bin": {
|
|
7
|
+
"bin": {
|
|
8
|
+
"pmos": "bin/pmos.js"
|
|
9
|
+
},
|
|
8
10
|
"scripts": {
|
|
9
11
|
"start": "node server.js",
|
|
10
12
|
"test": "node test.js",
|
|
11
13
|
"test:ui": "node ui-test.js",
|
|
12
14
|
"doctor": "node doctor.js"
|
|
13
15
|
},
|
|
14
|
-
"engines": {
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
},
|
|
15
19
|
"dependencies": {
|
|
16
20
|
"@xterm/addon-fit": "^0.11.0",
|
|
17
21
|
"@xterm/addon-web-links": "^0.12.0",
|
package/workspace/public/app.js
CHANGED
|
@@ -1059,7 +1059,13 @@ async function loadLayouts() {
|
|
|
1059
1059
|
const r = await fetch('/api/layouts?ws=' + encodeURIComponent(state.wsId || ''));
|
|
1060
1060
|
doc = await r.json();
|
|
1061
1061
|
} catch { doc = null; }
|
|
1062
|
-
|
|
1062
|
+
// A read failure must never author a write. loadAction() is the one place that decides, and it
|
|
1063
|
+
// only says 'migrate' when the SERVER affirmatively reports nothing stored — never because the
|
|
1064
|
+
// answer was unreadable. Anything unusable is 'keep': show the boot defaults, persist nothing,
|
|
1065
|
+
// and leave whatever is on disk exactly as it is until a load succeeds.
|
|
1066
|
+
const act = LAY.loadAction(doc);
|
|
1067
|
+
if (act === 'keep') return;
|
|
1068
|
+
if (act === 'migrate') {
|
|
1063
1069
|
// R8 — a workspace with nothing stored inherits whatever the PM had already tuned by hand
|
|
1064
1070
|
// in the pre-feature client, so the upgrade never costs them their rail width.
|
|
1065
1071
|
const legacy = {};
|
package/workspace/server.js
CHANGED
|
@@ -225,6 +225,9 @@ function wsList() {
|
|
|
225
225
|
}));
|
|
226
226
|
}
|
|
227
227
|
const SKIP_DIRS = new Set(['node_modules', 'dist', 'build', 'vendor', 'target', 'coverage', '.git', 'tmp', 'out']);
|
|
228
|
+
// The directories stateDirFor() resolves to. Their contents are machine-written runtime and curated
|
|
229
|
+
// records — never documents alignment should offer to edit.
|
|
230
|
+
const STATE_DIR_NAMES = new Set(['state', '.pmos']);
|
|
228
231
|
const PLAIN_MAX_DEPTH = 3; // the curated (knowledge) view stays shallow
|
|
229
232
|
const TREE_MAX_DEPTH = 6; // the folder lens is a real explorer, but bounded on huge/slow repos
|
|
230
233
|
const TREE_MAX_NODES = 4000; // …and node-budgeted so a giant repo (or Google Drive) never hangs
|
|
@@ -317,6 +320,11 @@ function markdownDocs(root, limit = 800) {
|
|
|
317
320
|
if (e.name.startsWith('.') || SKIP_DIRS.has(e.name)) continue;
|
|
318
321
|
const full = path.join(dir, e.name);
|
|
319
322
|
const r = rel ? rel + '/' + e.name : e.name;
|
|
323
|
+
// Machine-written state is not a document the PM maintains, and alignment must never propose
|
|
324
|
+
// realigning it. change-context.md in particular QUOTES every change, so it matches the
|
|
325
|
+
// quoted-text rule on the first save in any repository and would top the candidate list —
|
|
326
|
+
// a generated file suggesting itself, found by installing the published artifact and using it.
|
|
327
|
+
if (STATE_DIR_NAMES.has(e.name)) continue;
|
|
320
328
|
if (e.isDirectory()) walk(full, r, depth + 1);
|
|
321
329
|
else if (preview.previewKind(e.name) === 'markdown') out.push(r);
|
|
322
330
|
}
|
|
@@ -1510,11 +1518,17 @@ const LAYOUT_MAX = 40_000;
|
|
|
1510
1518
|
function layoutPath(w) { return w ? path.join(LAYOUT_DIR, w.id + '.json') : null; }
|
|
1511
1519
|
// Always returns a usable document: an absent, empty, truncated or hand-mangled file yields the
|
|
1512
1520
|
// four stock layouts rather than an error, because the alternative is a window the PM cannot use.
|
|
1521
|
+
// `stored` is the client's ONLY safe signal that R8's one-time migration should run. sanitize()
|
|
1522
|
+
// always yields the four stock layouts, so the response body alone cannot distinguish "this
|
|
1523
|
+
// workspace has never stored anything" from "I could not read what it stored" — and the client
|
|
1524
|
+
// answers those two cases very differently (one writes, one must not). A file that exists but is
|
|
1525
|
+
// corrupt still counts as stored: this workspace has been through the layout system, so replaying
|
|
1526
|
+
// the pre-feature localStorage over it would be a second loss, not a recovery.
|
|
1513
1527
|
function readLayouts(w) {
|
|
1514
1528
|
const f = layoutPath(w);
|
|
1515
1529
|
let raw = null;
|
|
1516
1530
|
if (f) { try { raw = fs.readFileSync(f, 'utf8'); } catch { raw = null; } }
|
|
1517
|
-
return layoutModel.sanitize(raw);
|
|
1531
|
+
return Object.assign(layoutModel.sanitize(raw), { stored: raw !== null });
|
|
1518
1532
|
}
|
|
1519
1533
|
function writeLayouts(w, doc) {
|
|
1520
1534
|
const f = layoutPath(w);
|
|
@@ -2122,7 +2136,7 @@ async function handleRequest(req, res) {
|
|
|
2122
2136
|
const w = getWs(url.searchParams.get('ws'));
|
|
2123
2137
|
if (!w) return json(res, { ok: false, error: 'no workspace' }, 404);
|
|
2124
2138
|
const doc = readLayouts(w);
|
|
2125
|
-
return json(res, { ok: true, layouts: doc.layouts, currentId: doc.currentId });
|
|
2139
|
+
return json(res, { ok: true, layouts: doc.layouts, currentId: doc.currentId, stored: doc.stored });
|
|
2126
2140
|
}
|
|
2127
2141
|
if (p === '/api/doc') {
|
|
2128
2142
|
const w = getWs(url.searchParams.get('ws'));
|