create-pmos 0.66.0 → 0.67.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 +1 -1
- package/workspace/run-tests.js +39 -2
- package/workspace/test-timings.json +172 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.67.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-07T02:01:39Z"}
|
|
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-07T02:01:39Z"}
|
|
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-07T02:01:39Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
package/workspace/package.json
CHANGED
package/workspace/run-tests.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
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] [--changed[=REF]] [--list] [--verbose]
|
|
5
|
+
* node run-tests.js <group> [--jobs=N] [--shard=I/N] [--changed[=REF]] [--timings-out=FILE] [--list] [--verbose]
|
|
6
6
|
* node run-tests.js --check
|
|
7
|
+
* node run-tests.js --merge-timings=DIR
|
|
7
8
|
*
|
|
8
9
|
* Groups come from test-files.json: unit (test.js + modules), modules, panels, ui, browser
|
|
9
10
|
* (panels + ui), all. The npm scripts call this file, so `npm test` and CI run the same lists.
|
|
@@ -31,6 +32,14 @@
|
|
|
31
32
|
* test-files.json, plus `helpers` (a -test.js file that is a helper, not a suite, with the reason, as
|
|
32
33
|
* in ELSEWHERE). This file merges every fragment into the lists before it does anything else, so
|
|
33
34
|
* --check, --shard and every group see one list. A fragment that is not valid fails every command.
|
|
35
|
+
*
|
|
36
|
+
* Timings. --shard balances the shards by each suite's time, and the shards only finish together when
|
|
37
|
+
* those times are the CI runner's. test-timings.json holds times measured on CI; the `seconds` keys in
|
|
38
|
+
* test-files.json and the fragments are the fallback for a suite it does not name yet, and a suite in
|
|
39
|
+
* neither counts as 10. The `seconds` numbers were measured on a 4-CPU sandbox and were off by up to
|
|
40
|
+
* 4x on the 2-CPU runners, which left one shard running 280s while two others finished in 130s.
|
|
41
|
+
* Every CI job writes its times with --timings-out and uploads them; --merge-timings=DIR folds a
|
|
42
|
+
* downloaded set into test-timings.json. A stale time never skips a suite: it only moves it.
|
|
34
43
|
*/
|
|
35
44
|
const fs = require('fs');
|
|
36
45
|
const os = require('os');
|
|
@@ -98,7 +107,10 @@ const args = process.argv.slice(2);
|
|
|
98
107
|
const flag = (name) => { const a = args.find((x) => x === `--${name}` || x.startsWith(`--${name}=`)); return a === undefined ? undefined : (a.includes('=') ? a.slice(a.indexOf('=') + 1) : true); };
|
|
99
108
|
const group = args.find((a) => !a.startsWith('--'));
|
|
100
109
|
|
|
101
|
-
const
|
|
110
|
+
const TIMINGS_FILE = path.join(DIR, 'test-timings.json');
|
|
111
|
+
let TIMINGS = {};
|
|
112
|
+
try { TIMINGS = JSON.parse(fs.readFileSync(TIMINGS_FILE, 'utf8')).seconds || {}; } catch { /* no CI timings yet: the seconds keys decide */ }
|
|
113
|
+
const secs = (f) => Number(TIMINGS[f]) || Number((FILES.seconds || {})[f]) || 10;
|
|
102
114
|
// Slowest first, so a long suite never starts last and holds the run open on its own.
|
|
103
115
|
const bySlowest = (list) => list.map((f, k) => ({ f, k })).sort((a, b) => secs(b.f) - secs(a.f) || a.k - b.k).map((x) => x.f);
|
|
104
116
|
|
|
@@ -155,6 +167,7 @@ function changedSuites(list, ref) {
|
|
|
155
167
|
for (const f of files) {
|
|
156
168
|
if (source.has(f)) { picked.add(f); continue; }
|
|
157
169
|
if (/^(guide|docs)\/|\.md$/.test(f)) continue; // prose: no suite asserts on it except guide.test.js, a unit suite
|
|
170
|
+
if (f === 'test-timings.json') continue; // run order only: no suite reads it
|
|
158
171
|
const stem = path.basename(f).replace(/\.[^.]+$/, '');
|
|
159
172
|
const key = stem.replace(/^(panel|view|tab)-/, '');
|
|
160
173
|
const hits = list.filter((s) => source.get(s).includes(stem) || s.includes(`-${key}-`) || s.includes(`-${key}.`));
|
|
@@ -207,8 +220,30 @@ function runOne(file, verbose) {
|
|
|
207
220
|
});
|
|
208
221
|
}
|
|
209
222
|
|
|
223
|
+
// Fold the --timings-out files a CI run uploaded (one per job, in DIR) into test-timings.json. A suite
|
|
224
|
+
// the run did not reach keeps its old time; a time under one second is recorded as one.
|
|
225
|
+
function mergeTimings(dir) {
|
|
226
|
+
const old = fs.existsSync(TIMINGS_FILE) ? JSON.parse(fs.readFileSync(TIMINGS_FILE, 'utf8')) : {};
|
|
227
|
+
const seconds = { ...(old.seconds || {}) };
|
|
228
|
+
let n = 0;
|
|
229
|
+
for (const name of fs.readdirSync(dir, { recursive: true }).filter((x) => String(x).endsWith('.json'))) {
|
|
230
|
+
for (const [f, v] of Object.entries(JSON.parse(fs.readFileSync(path.join(dir, String(name)), 'utf8')))) {
|
|
231
|
+
if (typeof v === 'number' && GROUPS.all.includes(f)) { seconds[f] = Math.max(1, Math.round(v)); n++; }
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const sorted = Object.fromEntries(Object.keys(seconds).filter((f) => GROUPS.all.includes(f)).sort().map((f) => [f, seconds[f]]));
|
|
235
|
+
const out = {
|
|
236
|
+
_comment: old._comment || 'Wall time of each suite on the Workspace CI runners, in seconds. run-tests.js --shard balances the shards with these numbers. Refresh: download the timings-* artifacts of a green Workspace CI run into one directory, then run `node run-tests.js --merge-timings=DIR`.',
|
|
237
|
+
measured: new Date().toISOString().slice(0, 10),
|
|
238
|
+
seconds: sorted,
|
|
239
|
+
};
|
|
240
|
+
fs.writeFileSync(TIMINGS_FILE, `${JSON.stringify(out, null, 2)}\n`);
|
|
241
|
+
console.log(`run-tests: ${n} times merged into test-timings.json (${Object.keys(sorted).length} suites).`);
|
|
242
|
+
}
|
|
243
|
+
|
|
210
244
|
async function main() {
|
|
211
245
|
if (flag('check')) return check();
|
|
246
|
+
if (flag('merge-timings')) return mergeTimings(flag('merge-timings'));
|
|
212
247
|
if (fragProblems.length) { for (const p of fragProblems) console.error(`✗ ${p}`); process.exitCode = 2; return; }
|
|
213
248
|
if (!group || !GROUPS[group]) {
|
|
214
249
|
console.error(`usage: node run-tests.js <${Object.keys(GROUPS).join('|')}> [--jobs=N] [--shard=I/N] [--list] [--verbose]\n node run-tests.js --check`);
|
|
@@ -250,6 +285,8 @@ async function main() {
|
|
|
250
285
|
|
|
251
286
|
const failed = results.filter((r) => !r.ok);
|
|
252
287
|
const slowest = [...results].sort((a, b) => b.secs - a.secs).slice(0, 5).map((r) => `${r.file} ${r.secs}s`).join(', ');
|
|
288
|
+
const timingsOut = flag('timings-out');
|
|
289
|
+
if (timingsOut && timingsOut !== true) fs.writeFileSync(timingsOut, JSON.stringify(Object.fromEntries(results.map((r) => [r.file, r.secs])), null, 2));
|
|
253
290
|
console.log(`\nrun-tests: ${results.length - failed.length}/${results.length} suites passed in ${((Date.now() - t0) / 1000).toFixed(1)}s wall. Slowest: ${slowest}.`);
|
|
254
291
|
// exitCode, never process.exit(): stdout to a pipe is asynchronous, and exiting here cut the CI log
|
|
255
292
|
// off mid-suite, before the summary that names what failed.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Wall time of each suite on the Workspace CI runners, in seconds. run-tests.js --shard balances the shards with these numbers. Refresh: download the timings-* artifacts of a green Workspace CI run into one directory, then run `node run-tests.js --merge-timings=DIR`.",
|
|
3
|
+
"measured": "2026-10-07",
|
|
4
|
+
"seconds": {
|
|
5
|
+
"agenda.test.js": 1,
|
|
6
|
+
"auto-sync.test.js": 2,
|
|
7
|
+
"board-cli.test.js": 3,
|
|
8
|
+
"capture.test.js": 1,
|
|
9
|
+
"claims.test.js": 2,
|
|
10
|
+
"cuts.test.js": 1,
|
|
11
|
+
"d86-seams.test.js": 1,
|
|
12
|
+
"day-run.test.js": 21,
|
|
13
|
+
"doc-category-corrections.test.js": 1,
|
|
14
|
+
"doc-category.test.js": 1,
|
|
15
|
+
"doc-library.test.js": 1,
|
|
16
|
+
"doc-sync.test.js": 1,
|
|
17
|
+
"docs-cli.test.js": 5,
|
|
18
|
+
"external-linking.test.js": 1,
|
|
19
|
+
"factory-join.test.js": 1,
|
|
20
|
+
"first-paint-board.test.js": 8,
|
|
21
|
+
"first-paint-count.test.js": 23,
|
|
22
|
+
"first-paint-initiatives.test.js": 12,
|
|
23
|
+
"first-paint-reports.test.js": 8,
|
|
24
|
+
"first-paint-today.test.js": 26,
|
|
25
|
+
"fixture-parity.test.mjs": 1,
|
|
26
|
+
"focus.test.js": 1,
|
|
27
|
+
"glossary.test.js": 1,
|
|
28
|
+
"goals.test.js": 1,
|
|
29
|
+
"grant-mint.test.js": 1,
|
|
30
|
+
"guide.test.js": 1,
|
|
31
|
+
"handoff.test.js": 1,
|
|
32
|
+
"health.test.js": 1,
|
|
33
|
+
"initiative-cli.test.js": 8,
|
|
34
|
+
"initiative-needs.test.js": 2,
|
|
35
|
+
"initiative-surface.test.js": 3,
|
|
36
|
+
"initiative-writer.test.js": 1,
|
|
37
|
+
"issues.test.js": 1,
|
|
38
|
+
"item-cli.test.js": 7,
|
|
39
|
+
"item-spans.test.js": 1,
|
|
40
|
+
"jev-categorize.test.js": 1,
|
|
41
|
+
"jev-claims.test.js": 1,
|
|
42
|
+
"jev-route.test.js": 1,
|
|
43
|
+
"jev-status.test.js": 1,
|
|
44
|
+
"jira-wbs.test.js": 1,
|
|
45
|
+
"kg.test.js": 2,
|
|
46
|
+
"kit-update.test.js": 1,
|
|
47
|
+
"launcher.test.js": 1,
|
|
48
|
+
"learn.test.js": 4,
|
|
49
|
+
"live-notes.test.js": 5,
|
|
50
|
+
"markdown.test.mjs": 1,
|
|
51
|
+
"measurements.test.js": 1,
|
|
52
|
+
"next-step-actions.test.js": 11,
|
|
53
|
+
"no-network.test.js": 1,
|
|
54
|
+
"notes-listen.test.js": 2,
|
|
55
|
+
"notes.test.js": 5,
|
|
56
|
+
"online-mode.test.js": 1,
|
|
57
|
+
"people.test.js": 2,
|
|
58
|
+
"phone-access.test.js": 1,
|
|
59
|
+
"phone-command.test.js": 4,
|
|
60
|
+
"placements.test.js": 1,
|
|
61
|
+
"plan-items-stage-parity.test.js": 1,
|
|
62
|
+
"plan-items.test.js": 1,
|
|
63
|
+
"plan-routes.test.js": 11,
|
|
64
|
+
"plan-tree.test.js": 6,
|
|
65
|
+
"pm-board-dest-phone-test.js": 14,
|
|
66
|
+
"pm-board-dest-test.js": 27,
|
|
67
|
+
"pm-breakdown-phone-test.js": 33,
|
|
68
|
+
"pm-breakdown-test.js": 68,
|
|
69
|
+
"pm-breakdown-timeline-test.js": 25,
|
|
70
|
+
"pm-cuts-test.js": 16,
|
|
71
|
+
"pm-detail-test.js": 18,
|
|
72
|
+
"pm-doc-category-test.js": 6,
|
|
73
|
+
"pm-docs-test.js": 37,
|
|
74
|
+
"pm-frame-phone-test.js": 14,
|
|
75
|
+
"pm-frame-test.js": 21,
|
|
76
|
+
"pm-initiatives-dest-phone-test.js": 9,
|
|
77
|
+
"pm-initiatives-dest-test.js": 28,
|
|
78
|
+
"pm-initiatives-test.js": 19,
|
|
79
|
+
"pm-initiatives-timeline-test.js": 39,
|
|
80
|
+
"pm-jira-test.js": 6,
|
|
81
|
+
"pm-lines-needs.test.js": 1,
|
|
82
|
+
"pm-live-notes-test.js": 25,
|
|
83
|
+
"pm-modes-test.js": 11,
|
|
84
|
+
"pm-nonce-test.js": 14,
|
|
85
|
+
"pm-notes-test.js": 40,
|
|
86
|
+
"pm-palette-test.js": 29,
|
|
87
|
+
"pm-people-test.js": 12,
|
|
88
|
+
"pm-r1-test.js": 10,
|
|
89
|
+
"pm-r10-test.js": 1,
|
|
90
|
+
"pm-r11-test.js": 2,
|
|
91
|
+
"pm-r12-test.js": 17,
|
|
92
|
+
"pm-r13-test.js": 12,
|
|
93
|
+
"pm-r14-test.js": 7,
|
|
94
|
+
"pm-r15-test.js": 7,
|
|
95
|
+
"pm-r16-test.js": 16,
|
|
96
|
+
"pm-r17-test.js": 7,
|
|
97
|
+
"pm-r18-test.js": 11,
|
|
98
|
+
"pm-r19-test.js": 20,
|
|
99
|
+
"pm-r2-test.js": 7,
|
|
100
|
+
"pm-r20-test.js": 19,
|
|
101
|
+
"pm-r21-test.js": 29,
|
|
102
|
+
"pm-r22-test.js": 2,
|
|
103
|
+
"pm-r23-test.js": 17,
|
|
104
|
+
"pm-r24-test.js": 15,
|
|
105
|
+
"pm-r25-test.js": 1,
|
|
106
|
+
"pm-r26-test.js": 1,
|
|
107
|
+
"pm-r27-test.js": 26,
|
|
108
|
+
"pm-r28-test.js": 1,
|
|
109
|
+
"pm-r29-filespad-test.js": 1,
|
|
110
|
+
"pm-r29-knowledge-test.js": 1,
|
|
111
|
+
"pm-r29-memory-test.js": 1,
|
|
112
|
+
"pm-r29-newsession-alignment-test.js": 1,
|
|
113
|
+
"pm-r29-portfolio-test.js": 1,
|
|
114
|
+
"pm-r29-reports-addrepo-test.js": 1,
|
|
115
|
+
"pm-r29-skills-scheduled-test.js": 1,
|
|
116
|
+
"pm-r29-today-test.js": 12,
|
|
117
|
+
"pm-r29-validate-guide-test.js": 1,
|
|
118
|
+
"pm-r3-test.js": 11,
|
|
119
|
+
"pm-r30-test.js": 13,
|
|
120
|
+
"pm-r4-test.js": 4,
|
|
121
|
+
"pm-r5-test.js": 6,
|
|
122
|
+
"pm-r6-test.js": 5,
|
|
123
|
+
"pm-r7-test.js": 1,
|
|
124
|
+
"pm-r8-test.js": 1,
|
|
125
|
+
"pm-r9-test.js": 10,
|
|
126
|
+
"pm-report-slides-test.js": 6,
|
|
127
|
+
"pm-reports-phone-test.js": 9,
|
|
128
|
+
"pm-reports-test.js": 20,
|
|
129
|
+
"pm-repos-test.js": 10,
|
|
130
|
+
"pm-seams-test.js": 16,
|
|
131
|
+
"pm-shell-test.js": 6,
|
|
132
|
+
"pm-tabs-test.js": 55,
|
|
133
|
+
"pm-term-test.js": 38,
|
|
134
|
+
"pm-today-goals-test.js": 30,
|
|
135
|
+
"pm-today-phone-test.js": 27,
|
|
136
|
+
"pm-today-test.js": 60,
|
|
137
|
+
"pm-updates-test.js": 16,
|
|
138
|
+
"proactive-prepare.test.js": 12,
|
|
139
|
+
"proactive.test.js": 17,
|
|
140
|
+
"profiles.test.js": 1,
|
|
141
|
+
"public-host.test.js": 1,
|
|
142
|
+
"r25-guards.test.js": 9,
|
|
143
|
+
"r3-display.test.js": 4,
|
|
144
|
+
"replaced-tests.test.js": 17,
|
|
145
|
+
"repo-scan.test.js": 1,
|
|
146
|
+
"repo-watch.test.js": 1,
|
|
147
|
+
"report-cli.test.js": 2,
|
|
148
|
+
"report-draft.test.js": 4,
|
|
149
|
+
"report-needs.test.js": 1,
|
|
150
|
+
"report-slides-data.test.js": 1,
|
|
151
|
+
"routes-board.test.js": 1,
|
|
152
|
+
"routes-capture.test.js": 1,
|
|
153
|
+
"routes-initiatives.test.js": 3,
|
|
154
|
+
"routes-reports.test.js": 1,
|
|
155
|
+
"routes-timeline.test.js": 1,
|
|
156
|
+
"routes-today.test.js": 6,
|
|
157
|
+
"scan-consistency.test.js": 1,
|
|
158
|
+
"schedule-cli.test.js": 3,
|
|
159
|
+
"session-flag.test.js": 1,
|
|
160
|
+
"start-policy.test.js": 1,
|
|
161
|
+
"state-isolation.test.js": 37,
|
|
162
|
+
"status-facts-panel.test.js": 1,
|
|
163
|
+
"story-artifacts.test.js": 1,
|
|
164
|
+
"sync.test.js": 4,
|
|
165
|
+
"test.js": 65,
|
|
166
|
+
"today.test.js": 5,
|
|
167
|
+
"tracker-refresh.test.js": 1,
|
|
168
|
+
"typesafe-validation.test.js": 1,
|
|
169
|
+
"ui-test.js": 43,
|
|
170
|
+
"work-check-needs.test.js": 1
|
|
171
|
+
}
|
|
172
|
+
}
|