create-pmos 0.51.0 → 0.52.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/package.json +1 -1
- package/payload/KIT-VERSION +1 -1
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/state/discoveries.md +1 -1
- package/workspace/package-lock.json +1 -1
- package/workspace/package.json +2 -1
- package/workspace/run-tests.js +61 -1
- package/workspace/test-files.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
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. Includes `workflow-sweep`, which audits the state every board derives from and separates real defects from work you simply have not done. `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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Eval calibration log"
|
|
4
4
|
description: "Append-only record of every PM override of an evaluator verdict."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-05T21:40:45Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Health budgets"
|
|
4
4
|
description: "Closed registry of health-budget anchors (D59), edited only by the PM."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-05T21:40:45Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Discoveries and friction log"
|
|
4
4
|
description: "Append-only record of what runs discovered and where the process rubbed."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-05T21:40:45Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
package/workspace/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmos-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
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",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"test:panels": "node run-tests.js panels",
|
|
20
20
|
"test:offline": "node offline-tarball.test.js",
|
|
21
21
|
"test:browser": "node run-tests.js browser",
|
|
22
|
+
"test:changed": "node run-tests.js all --changed",
|
|
22
23
|
"test:list-check": "node run-tests.js --check"
|
|
23
24
|
},
|
|
24
25
|
"engines": {
|
package/workspace/run-tests.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
/* run-tests.js — run the workspace suites side by side.
|
|
4
4
|
*
|
|
5
|
-
* node run-tests.js <group> [--jobs=N] [--shard=I/N] [--list] [--verbose]
|
|
5
|
+
* node run-tests.js <group> [--jobs=N] [--shard=I/N] [--changed[=REF]] [--list] [--verbose]
|
|
6
6
|
* node run-tests.js --check
|
|
7
7
|
*
|
|
8
8
|
* Groups come from test-files.json: unit (test.js + modules), modules, panels, ui, browser
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
* file in exactly one shard), and --check fails when a suite on disk is in no list, so a new suite
|
|
18
18
|
* that nobody wired in is a red build rather than a suite that runs nowhere.
|
|
19
19
|
*
|
|
20
|
+
* --changed is for the LOCAL inner loop only. It narrows the group to the suites a diff against REF
|
|
21
|
+
* (default origin/main) can reach, and runs the whole group whenever it cannot tell. CI never passes
|
|
22
|
+
* it: Workspace CI runs every suite on every PR, so a suite --changed leaves out still runs before
|
|
23
|
+
* the merge. See changedSuites() for the rule.
|
|
24
|
+
*
|
|
20
25
|
* Output: a suite's own output is buffered and printed whole when it finishes, so lines from two
|
|
21
26
|
* suites never interleave. A passing suite prints its last lines; --verbose prints all of it.
|
|
22
27
|
*/
|
|
@@ -65,6 +70,53 @@ function shardOf(list, spec) {
|
|
|
65
70
|
return out[i - 1];
|
|
66
71
|
}
|
|
67
72
|
|
|
73
|
+
// Files every page or every suite loads. A change to one can break any suite, so --changed runs
|
|
74
|
+
// the whole group. A changed file no suite can be traced to is treated the same way (see below).
|
|
75
|
+
const WIDE = new Set([
|
|
76
|
+
'server.js', 'test-run.js', 'run-tests.js', 'test-files.json', 'package.json', 'package-lock.json',
|
|
77
|
+
'public/pm/app.js', 'public/pm/state.js', 'public/pm/dom.js', 'public/pm/data.js',
|
|
78
|
+
'public/pm/tokens.css', 'public/pm/fixture.js', 'public/pm/mobile.css', 'lib/find-browser.js',
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
/* The suites in `list` that the diff against `ref` can reach. A suite is selected when its own file
|
|
82
|
+
* changed, when its source names a changed file (`panel-tabs` in pm-tabs-test.js), or when its file
|
|
83
|
+
* name carries the changed file's stem with any panel-/view-/tab- prefix removed (view-memory.js ->
|
|
84
|
+
* pm-r29-memory-test.js). It errs toward running MORE: a WIDE file, or a changed workspace file that
|
|
85
|
+
* no suite can be traced to, selects the whole list. Returns { files, suites, why }. */
|
|
86
|
+
function changedSuites(list, ref) {
|
|
87
|
+
const git = (...a) => {
|
|
88
|
+
const r = require('child_process').spawnSync('git', a, { cwd: DIR, encoding: 'utf8' });
|
|
89
|
+
return r.status === 0 ? r.stdout : null;
|
|
90
|
+
};
|
|
91
|
+
const top = (git('rev-parse', '--show-toplevel') || '').trim();
|
|
92
|
+
if (!top) return { files: [], suites: list, why: 'not a git checkout, so every suite runs' };
|
|
93
|
+
const base = (git('merge-base', 'HEAD', ref) || '').trim();
|
|
94
|
+
if (!base) return { files: [], suites: list, why: `no merge base with ${ref} (fetch it first), so every suite runs` };
|
|
95
|
+
const prefix = path.relative(top, DIR).split(path.sep).join('/') + '/';
|
|
96
|
+
const names = [
|
|
97
|
+
git('diff', '--name-only', base), // committed on this branch and uncommitted changes
|
|
98
|
+
git('ls-files', '--others', '--exclude-standard', '--full-name', DIR),
|
|
99
|
+
].join('\n').split('\n').map((f) => f.trim()).filter(Boolean);
|
|
100
|
+
const files = [...new Set(names)].filter((f) => f.startsWith(prefix)).map((f) => f.slice(prefix.length));
|
|
101
|
+
if (!files.length) return { files, suites: [], why: `nothing under workspace/ differs from ${ref}` };
|
|
102
|
+
const wide = files.filter((f) => WIDE.has(f));
|
|
103
|
+
if (wide.length) return { files, suites: list, why: `${wide.join(', ')} changed, and every suite depends on it` };
|
|
104
|
+
const source = new Map(list.map((f) => [f, fs.existsSync(path.join(DIR, f)) ? fs.readFileSync(path.join(DIR, f), 'utf8') : '']));
|
|
105
|
+
const picked = new Set();
|
|
106
|
+
const untraced = [];
|
|
107
|
+
for (const f of files) {
|
|
108
|
+
if (source.has(f)) { picked.add(f); continue; }
|
|
109
|
+
if (/^(guide|docs)\/|\.md$/.test(f)) continue; // prose: no suite asserts on it except guide.test.js, a unit suite
|
|
110
|
+
const stem = path.basename(f).replace(/\.[^.]+$/, '');
|
|
111
|
+
const key = stem.replace(/^(panel|view|tab)-/, '');
|
|
112
|
+
const hits = list.filter((s) => source.get(s).includes(stem) || s.includes(`-${key}-`) || s.includes(`-${key}.`));
|
|
113
|
+
if (!hits.length) untraced.push(f);
|
|
114
|
+
for (const h of hits) picked.add(h);
|
|
115
|
+
}
|
|
116
|
+
if (untraced.length) return { files, suites: list, why: `no suite names ${untraced.slice(0, 3).join(', ')}${untraced.length > 3 ? ' …' : ''}, so every suite runs` };
|
|
117
|
+
return { files, suites: list.filter((f) => picked.has(f)), why: `${files.length} changed file(s) reach these suites` };
|
|
118
|
+
}
|
|
119
|
+
|
|
68
120
|
function check() {
|
|
69
121
|
const listed = new Set(GROUPS.all);
|
|
70
122
|
const problems = [];
|
|
@@ -114,6 +166,14 @@ async function main() {
|
|
|
114
166
|
let list = GROUPS[group];
|
|
115
167
|
const shard = flag('shard');
|
|
116
168
|
list = shard ? shardOf(list, shard) : bySlowest(list);
|
|
169
|
+
const changed = flag('changed');
|
|
170
|
+
if (changed) {
|
|
171
|
+
const ref = changed === true ? 'origin/main' : changed;
|
|
172
|
+
const pick = changedSuites(list, ref);
|
|
173
|
+
console.log(`run-tests: --changed against ${ref}: ${pick.why}. ${pick.suites.length} of ${list.length} suites run here; CI runs all ${list.length}.`);
|
|
174
|
+
list = pick.suites;
|
|
175
|
+
if (!list.length) return;
|
|
176
|
+
}
|
|
117
177
|
if (flag('list')) { console.log(list.join('\n')); return; }
|
|
118
178
|
const cpus = typeof os.availableParallelism === 'function' ? os.availableParallelism() : os.cpus().length;
|
|
119
179
|
// A browser suite runs a server, a tmux daemon and a Chromium of its own, and several of them assert
|